diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 13ab6755e6..69c48a5e64 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2184,3 +2184,5 @@ _moderationLogTypes: resetPassword: "パスワードをリセット" suspendRemoteInstance: "リモートサーバーを停止" unsuspendRemoteInstance: "リモートサーバーを再開" + markSensitiveDriveFile: "ファイルをセンシティブ付与" + unmarkSensitiveDriveFile: "ファイルをセンシティブ解除" diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 2ff062142c..0409a4f53b 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -649,6 +649,57 @@ export class DriveService { return file; } + @bindThis + public async update(file: MiDriveFile, values: Partial, updater: MiUser) { + const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw; + + if (values.name && !this.driveFileEntityService.validateFileName(file.name)) { + throw new Error('invalid filename'); + } + + if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive && alwaysMarkNsfw && !values.isSensitive) { + throw new Error('cannot unmark nsfw'); + } + + if (values.folderId != null) { + const folder = await this.driveFoldersRepository.findOneBy({ + id: values.folderId, + userId: file.userId!, + }); + + if (folder == null) { + throw new Error('folder-not-found'); + } + } + + await this.driveFilesRepository.update(file.id, values); + + const fileObj = await this.driveFileEntityService.pack(file, { self: true }); + + // Publish fileUpdated event + if (file.userId) { + this.globalEventService.publishDriveStream(file.userId, 'fileUpdated', fileObj); + } + + if (await this.roleService.isModerator(updater) && (file.userId !== updater.id)) { + if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive) { + if (values.isSensitive) { + this.moderationLogService.log(updater, 'markSensitiveDriveFile', { + fileId: file.id, + fileUserId: file.userId, + }); + } else { + this.moderationLogService.log(updater, 'unmarkSensitiveDriveFile', { + fileId: file.id, + fileUserId: file.userId, + }); + } + } + } + + return fileObj; + } + @bindThis public async deleteFile(file: MiDriveFile, isExpired = false, deleter?: MiUser) { if (file.storedInternal) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index d26ed63474..7db88e152c 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -4,12 +4,11 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/_.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; +import { DriveService } from '@/core/DriveService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -77,16 +76,11 @@ export default class extends Endpoint { // eslint- @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - @Inject(DI.driveFoldersRepository) - private driveFoldersRepository: DriveFoldersRepository, - - private driveFileEntityService: DriveFileEntityService, + private driveService: DriveService, private roleService: RoleService, - private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); - const alwaysMarkNsfw = (await this.roleService.getUserPolicies(me.id)).alwaysMarkNsfw; if (file == null) { throw new ApiError(meta.errors.noSuchFile); } @@ -95,47 +89,7 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.accessDenied); } - if (ps.name) file.name = ps.name; - if (!this.driveFileEntityService.validateFileName(file.name)) { - throw new ApiError(meta.errors.invalidFileName); - } - - if (ps.comment !== undefined) file.comment = ps.comment; - - if (ps.isSensitive !== undefined && ps.isSensitive !== file.isSensitive && alwaysMarkNsfw && !ps.isSensitive) { - throw new ApiError(meta.errors.restrictedByRole); - } - - if (ps.isSensitive !== undefined) file.isSensitive = ps.isSensitive; - - if (ps.folderId !== undefined) { - if (ps.folderId === null) { - file.folderId = null; - } else { - const folder = await this.driveFoldersRepository.findOneBy({ - id: ps.folderId, - userId: me.id, - }); - - if (folder == null) { - throw new ApiError(meta.errors.noSuchFolder); - } - - file.folderId = folder.id; - } - } - - await this.driveFilesRepository.update(file.id, { - name: file.name, - comment: file.comment, - folderId: file.folderId, - isSensitive: file.isSensitive, - }); - - const fileObj = await this.driveFileEntityService.pack(file, { self: true }); - - // Publish fileUpdated event - this.globalEventService.publishDriveStream(me.id, 'fileUpdated', fileObj); + const fileObj = await this.driveService.update(file, ps, me); return fileObj; }); diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index ea78bb919a..16654edd88 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -52,6 +52,8 @@ export const moderationLogTypes = [ 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', + 'markSensitiveDriveFile', + 'unmarkSensitiveDriveFile', ] as const; export type ModerationLogPayloads = { @@ -152,4 +154,12 @@ export type ModerationLogPayloads = { id: string; host: string; }; + markSensitiveDriveFile: { + fileId: string; + fileUserId: string | null; + }; + unmarkSensitiveDriveFile: { + fileId: string; + fileUserId: string | null; + }; }; diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index d8b6aa44d8..b3806754a8 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2595,10 +2595,16 @@ type ModerationLog = { } | { type: 'unsuspendRemoteInstance'; info: ModerationLogPayloads['unsuspendRemoteInstance']; +} | { + type: 'markSensitiveDriveFile'; + info: ModerationLogPayloads['markSensitiveDriveFile']; +} | { + type: 'unmarkSensitiveDriveFile'; + info: ModerationLogPayloads['unmarkSensitiveDriveFile']; }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index 462ad16cc8..63137dcc83 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -70,6 +70,8 @@ export const moderationLogTypes = [ 'resetPassword', 'suspendRemoteInstance', 'unsuspendRemoteInstance', + 'markSensitiveDriveFile', + 'unmarkSensitiveDriveFile', ] as const; export type ModerationLogPayloads = { @@ -170,4 +172,12 @@ export type ModerationLogPayloads = { id: string; host: string; }; + markSensitiveDriveFile: { + fileId: string; + fileUserId: string | null; + }; + unmarkSensitiveDriveFile: { + fileId: string; + fileUserId: string | null; + }; }; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index e6a97f0209..f377f1a5ed 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -646,4 +646,10 @@ export type ModerationLog = { } | { type: 'unsuspendRemoteInstance'; info: ModerationLogPayloads['unsuspendRemoteInstance']; +} | { + type: 'markSensitiveDriveFile'; + info: ModerationLogPayloads['markSensitiveDriveFile']; +} | { + type: 'unmarkSensitiveDriveFile'; + info: ModerationLogPayloads['unmarkSensitiveDriveFile']; });