enhance(sensitive-flag):センシティブフラグの機能の強化 (MisskeyIO#936)

This commit is contained in:
まっちゃてぃー。 2025-03-18 03:22:08 +09:00 committed by GitHub
parent 7a94724098
commit abdaa18666
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 197 additions and 20 deletions

View file

@ -0,0 +1,13 @@
export class SensitiveFlag1739335129758 {
name = 'SensitiveFlag1739335129758'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "drive_file" ADD "isSensitiveByModerator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`CREATE INDEX "IDX_e779d1afdfa44dc3d64213cd2e" ON "drive_file" ("isSensitiveByModerator") `);
}
async down(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_e779d1afdfa44dc3d64213cd2e"`);
await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "isSensitiveByModerator"`);
}
}

View file

@ -44,6 +44,7 @@ import { correctFilename } from '@/misc/correct-filename.js';
import { isMimeImage } from '@/misc/is-mime-image.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { LoggerService } from '@/core/LoggerService.js';
import { NotificationService } from '@/core/NotificationService.js';
type AddFileArgs = {
/** User who wish to add file */
@ -129,6 +130,7 @@ export class DriveService {
private driveChart: DriveChart,
private perUserDriveChart: PerUserDriveChart,
private instanceChart: InstanceChart,
private notificationService: NotificationService,
) {
const logger = this.loggerService.getLogger('drive', 'blue');
this.registerLogger = logger.createSubLogger('register', 'yellow');
@ -664,13 +666,15 @@ export class DriveService {
@bindThis
public async updateFile(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) {
const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw;
const isModerator = await this.roleService.isModerator(updater);
if (values.name != null && !this.driveFileEntityService.validateFileName(values.name)) {
throw new DriveService.InvalidFileNameError();
}
if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive && alwaysMarkNsfw && !values.isSensitive) {
throw new DriveService.CannotUnmarkSensitiveError();
if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive && !values.isSensitive) {
if (alwaysMarkNsfw) throw new DriveService.CannotUnmarkSensitiveError();
if (file.isSensitiveByModerator && (file.userId === updater.id)) throw new DriveService.CannotUnmarkSensitiveError();
}
if (values.folderId != null) {
@ -684,6 +688,10 @@ export class DriveService {
}
}
if (isModerator && file.userId !== updater.id) {
values.isSensitiveByModerator = values.isSensitive;
}
await this.driveFilesRepository.update(file.id, values);
const fileObj = await this.driveFileEntityService.pack(file.id, updater, { self: true });
@ -693,7 +701,7 @@ export class DriveService {
this.globalEventService.publishDriveStream(file.userId, 'fileUpdated', fileObj);
}
if (await this.roleService.isModerator(updater) && (file.userId !== updater.id)) {
if (isModerator && (file.userId !== updater.id)) {
if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive) {
const user = file.userId ? await this.usersRepository.findOneByOrFail({ id: file.userId }) : null;
if (values.isSensitive) {
@ -703,6 +711,11 @@ export class DriveService {
fileUserUsername: user?.username ?? null,
fileUserHost: user?.host ?? null,
});
if (file.userId) {
this.notificationService.createNotification(file.userId, 'sensitiveFlagAssigned', {
fileId: file.id,
});
}
} else {
this.moderationLogService.log(updater, 'unmarkSensitiveDriveFile', {
fileId: file.id,

View file

@ -210,6 +210,9 @@ export class DriveFileEntityService {
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
...(opts.detail ? {
isSensitiveByModerator: file.isSensitiveByModerator,
} : {}),
blurhash: file.blurhash,
properties: opts.self ? file.properties : this.getPublicProperties(file),
url: opts.self ? file.url : this.getPublicUrl(file),
@ -246,6 +249,9 @@ export class DriveFileEntityService {
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
...(opts.detail ? {
isSensitiveByModerator: file.isSensitiveByModerator,
} : {}),
blurhash: file.blurhash,
properties: opts.self ? file.properties : this.getPublicProperties(file),
url: opts.self ? file.url : this.getPublicUrl(file),

View file

@ -183,6 +183,9 @@ export class NotificationEntityService implements OnModuleInit {
header: notification.customHeader,
icon: notification.customIcon,
} : {}),
...(notification.type === 'sensitiveFlagAssigned' ? {
fileId: notification.fileId,
} : {}),
});
}

View file

@ -162,6 +162,12 @@ export class MiDriveFile {
})
public isSensitive: boolean;
@Index()
@Column('boolean', {
default: false,
})
public isSensitiveByModerator: boolean;
@Index()
@Column('boolean', {
default: false,

View file

@ -93,6 +93,11 @@ export type MiNotification = {
id: string;
createdAt: string;
draftId: MiScheduledNote['id'];
} | {
type: 'sensitiveFlagAssigned'
id: string;
fileId: string;
createdAt: string;
} | {
type: 'app';
id: string;

View file

@ -42,6 +42,10 @@ export const packedDriveFileSchema = {
type: 'boolean',
optional: false, nullable: false,
},
isSensitiveByModerator: {
type: 'boolean',
optional: true, nullable: true,
},
blurhash: {
type: 'string',
optional: false, nullable: true,

View file

@ -309,8 +309,8 @@ export const packedNotificationSchema = {
type: 'object',
ref: 'NoteDraft',
optional: false, nullable: false,
}
}
},
},
}, {
type: 'object',
properties: {
@ -324,8 +324,8 @@ export const packedNotificationSchema = {
type: 'object',
ref: 'Note',
optional: false, nullable: false,
}
}
},
},
}, {
type: 'object',
properties: {
@ -339,8 +339,21 @@ export const packedNotificationSchema = {
type: 'object',
ref: 'NoteDraft',
optional: false, nullable: false,
}
}
},
},
}, {
type: 'object',
properties: {
...baseSchema.properties,
type: {
type: 'string',
optional: false, nullable: false,
enum: ['sensitiveFlagAssigned'],
},
fileId: {
optional: false, nullable: false,
},
},
}, {
type: 'object',
properties: {

View file

@ -51,6 +51,12 @@ export const meta = {
code: 'RESTRICTED_BY_ROLE',
id: '7f59dccb-f465-75ab-5cf4-3ce44e3282f7',
},
restrictedByModerator: {
message: 'The isSensitive specified by the administrator cannot be changed.',
code: 'RESTRICTED_BY_ADMINISTRATOR',
id: '20e6c501-e579-400d-97e4-1c7efc286f35',
},
},
res: {
type: 'object',
@ -105,7 +111,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} else if (e instanceof DriveService.NoSuchFolderError) {
throw new ApiError(meta.errors.noSuchFolder);
} else if (e instanceof DriveService.CannotUnmarkSensitiveError) {
throw new ApiError(meta.errors.restrictedByRole);
if (file.isSensitiveByModerator) {
throw new ApiError(meta.errors.restrictedByModerator);
} else {
throw new ApiError(meta.errors.restrictedByRole);
}
} else {
throw e;
}

View file

@ -26,6 +26,7 @@ import type { MiNote } from '@/models/Note.js';
* noteScheduled - 稿
* scheduledNotePosted - 稿稿
* scheduledNoteError - 稿
* sensitiveFlagAssigned -
* app -
* test -
*/
@ -45,6 +46,7 @@ export const notificationTypes = [
'noteScheduled',
'scheduledNotePosted',
'scheduledNoteError',
'sensitiveFlagAssigned',
'app',
'test',
] as const;

View file

@ -95,6 +95,7 @@ describe('NoteCreateService', () => {
folderId: null,
folder: null,
isSensitive: false,
isSensitiveByModerator: false,
maybeSensitive: false,
maybePorn: false,
isLink: false,