From 38f6f871a8875c6b7947c5caad3bf79ddb7fc163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=82=8F=E3=82=8F=E3=82=8F=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sun, 9 Mar 2025 12:29:20 +0900 Subject: [PATCH 01/16] build(docker): fix docker build (MisskeyIO#941) --- Dockerfile | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3d91b9f8c..913d1cb38 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,8 @@ # syntax = docker/dockerfile:1.4 -ARG NODE_VERSION=22 - # build assets & compile TypeScript -FROM --platform=$BUILDPLATFORM node:${NODE_VERSION} AS native-builder +FROM --platform=$BUILDPLATFORM node:22 AS native-builder RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ @@ -17,7 +15,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ WORKDIR /misskey COPY --link pnpm-lock.yaml ./ -RUN npm install -g pnpm +RUN npm install -g pnpm@9 RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ pnpm fetch --ignore-scripts @@ -30,7 +28,7 @@ COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"] COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"] COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"] -RUN pnpm i --frozen-lockfile --aggregate-output --offline \ +RUN pnpm i --frozen-lockfile --aggregate-output --prefer-offline \ && pnpm rebuild -r COPY --link . ./ @@ -39,7 +37,7 @@ RUN NODE_ENV=production pnpm build # build native dependencies for target platform -FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder +FROM --platform=$TARGETPLATFORM node:22 AS target-builder RUN apt-get update \ && apt-get install -yqq --no-install-recommends \ @@ -48,7 +46,7 @@ RUN apt-get update \ WORKDIR /misskey COPY --link pnpm-lock.yaml ./ -RUN npm install -g pnpm +RUN npm install -g pnpm@9 RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ pnpm fetch --ignore-scripts @@ -59,10 +57,10 @@ COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"] COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"] COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"] -RUN pnpm i --frozen-lockfile --aggregate-output --offline \ +RUN pnpm i --frozen-lockfile --aggregate-output --prefer-offline \ && pnpm rebuild -r -FROM --platform=$TARGETPLATFORM node:${NODE_VERSION}-slim AS runner +FROM --platform=$TARGETPLATFORM node:22-slim AS runner ARG UID="991" ARG GID="991" @@ -81,7 +79,7 @@ RUN apt-get update \ WORKDIR /misskey COPY --chown=misskey:misskey pnpm-lock.yaml ./ -RUN npm install -g pnpm +RUN npm install -g pnpm@9 COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules From d13ec4c5fdbf851923c156657e383071c951da3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 17:31:13 +0900 Subject: [PATCH 02/16] chore(deps): bump actions/setup-node from 4.2.0 to 4.3.0 (MisskeyIO#943) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4.2.0...v4.3.0) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/api-misskey-js.yml | 2 +- .github/workflows/lint.yml | 6 +++--- .github/workflows/test-backend.yml | 4 ++-- .github/workflows/test-frontend.yml | 2 +- .github/workflows/test-misskey-js.yml | 2 +- .github/workflows/test-production.yml | 2 +- .github/workflows/validate-api-json.yml | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml index 1b3eabc3c..eda561f91 100644 --- a/.github/workflows/api-misskey-js.yml +++ b/.github/workflows/api-misskey-js.yml @@ -26,7 +26,7 @@ jobs: run_install: false - name: Setup Node.js - uses: actions/setup-node@v4.2.0 + uses: actions/setup-node@v4.3.0 with: node-version-file: '.node-version' cache: 'pnpm' diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 71600a5a4..a42921607 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,7 +31,7 @@ jobs: - uses: pnpm/action-setup@v4 with: run_install: false - - uses: actions/setup-node@v4.2.0 + - uses: actions/setup-node@v4.3.0 with: node-version-file: '.node-version' cache: 'pnpm' @@ -56,7 +56,7 @@ jobs: - uses: pnpm/action-setup@v4 with: run_install: false - - uses: actions/setup-node@v4.2.0 + - uses: actions/setup-node@v4.3.0 with: node-version-file: '.node-version' cache: 'pnpm' @@ -80,7 +80,7 @@ jobs: - uses: pnpm/action-setup@v4 with: run_install: false - - uses: actions/setup-node@v4.2.0 + - uses: actions/setup-node@v4.3.0 with: node-version-file: '.node-version' cache: 'pnpm' diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 98752b9e5..78db5e083 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -59,7 +59,7 @@ jobs: - name: Install FFmpeg uses: FedericoCarboni/setup-ffmpeg@v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.2.0 + uses: actions/setup-node@v4.3.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' @@ -118,7 +118,7 @@ jobs: with: run_install: false - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.2.0 + uses: actions/setup-node@v4.3.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml index 68f96f7b2..05ab63e87 100644 --- a/.github/workflows/test-frontend.yml +++ b/.github/workflows/test-frontend.yml @@ -39,7 +39,7 @@ jobs: with: run_install: false - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.2.0 + uses: actions/setup-node@v4.3.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml index 82ce1e726..8de9d79f9 100644 --- a/.github/workflows/test-misskey-js.yml +++ b/.github/workflows/test-misskey-js.yml @@ -38,7 +38,7 @@ jobs: run_install: false - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.2.0 + uses: actions/setup-node@v4.3.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml index dc05e766a..d6bed2107 100644 --- a/.github/workflows/test-production.yml +++ b/.github/workflows/test-production.yml @@ -29,7 +29,7 @@ jobs: with: run_install: false - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.2.0 + uses: actions/setup-node@v4.3.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' diff --git a/.github/workflows/validate-api-json.yml b/.github/workflows/validate-api-json.yml index ba3679894..42b6ee08a 100644 --- a/.github/workflows/validate-api-json.yml +++ b/.github/workflows/validate-api-json.yml @@ -30,7 +30,7 @@ jobs: with: run_install: false - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.2.0 + uses: actions/setup-node@v4.3.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' From 7a947240981e757cc4cb45a77ef48d5e3f9e835c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A6=E3=81=83?= =?UTF-8?q?=E3=83=BC=E3=80=82?= <56515516+mattyatea@users.noreply.github.com> Date: Tue, 18 Mar 2025 03:21:18 +0900 Subject: [PATCH 03/16] =?UTF-8?q?fix(backend/DriveService):=20variation?= =?UTF-8?q?=E3=81=8C=E4=BB=98=E3=81=84=E3=81=9FMIME=E3=82=92=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E3=81=AB=E3=83=91=E3=83=BC=E3=82=B9=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(MisskeyIO#942)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/DriveService.ts | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index a68fc25ab..a1d06a1d8 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -146,8 +146,10 @@ export class DriveService { */ @bindThis private async save(file: MiDriveFile, path: string, name: string, type: string, hash: string, size: number): Promise { - // thunbnail, webpublic を必要なら生成 - const alts = await this.generateAlts(path, type, !file.uri); + const fileType = type.split(';')[0]; + + // thunbnail, webpublic を必要なら生成 + const alts = await this.generateAlts(path, fileType, !file.uri); const meta = await this.metaService.fetch(); @@ -156,17 +158,17 @@ export class DriveService { let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) ?? ['']); if (ext === '') { - if (type === 'image/jpeg') ext = '.jpg'; - if (type === 'image/png') ext = '.png'; - if (type === 'image/webp') ext = '.webp'; - if (type === 'image/avif') ext = '.avif'; - if (type === 'image/apng') ext = '.apng'; - if (type === 'image/vnd.mozilla.apng') ext = '.apng'; + if (fileType === 'image/jpeg') ext = '.jpg'; + if (fileType === 'image/png') ext = '.png'; + if (fileType === 'image/webp') ext = '.webp'; + if (fileType === 'image/avif') ext = '.avif'; + if (fileType === 'image/apng') ext = '.apng'; + if (fileType === 'image/vnd.mozilla.apng') ext = '.apng'; } // 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、 // 許可されているファイル形式でしかURLに拡張子をつけない - if (!FILE_TYPE_BROWSERSAFE.includes(type)) { + if (!FILE_TYPE_BROWSERSAFE.includes(fileType)) { ext = ''; } @@ -373,8 +375,10 @@ export class DriveService { */ @bindThis private async upload(key: string, stream: fs.ReadStream | Buffer, type: string, ext?: string | null, filename?: string) { - if (type === 'image/apng') type = 'image/png'; - if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream'; + const fileType = type.split(';')[0]; + + if (fileType === 'image/apng') type = 'image/png'; + if (!FILE_TYPE_BROWSERSAFE.includes(fileType)) type = 'application/octet-stream'; const meta = await this.metaService.fetch(); From abdaa186663f802ab60d72ef918ac56ac292ba7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A6=E3=81=83?= =?UTF-8?q?=E3=83=BC=E3=80=82?= <56515516+mattyatea@users.noreply.github.com> Date: Tue, 18 Mar 2025 03:22:08 +0900 Subject: [PATCH 04/16] =?UTF-8?q?enhance(sensitive-flag):=E3=82=BB?= =?UTF-8?q?=E3=83=B3=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=83=95=E3=83=A9?= =?UTF-8?q?=E3=82=B0=E3=81=AE=E6=A9=9F=E8=83=BD=E3=81=AE=E5=BC=B7=E5=8C=96?= =?UTF-8?q?=20(MisskeyIO#936)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 13 +++++ locales/ja-JP.yml | 3 + .../migration/1739335129758-sensitiveFlag.js | 13 +++++ packages/backend/src/core/DriveService.ts | 19 ++++++- .../core/entities/DriveFileEntityService.ts | 6 ++ .../entities/NotificationEntityService.ts | 3 + packages/backend/src/models/DriveFile.ts | 6 ++ packages/backend/src/models/Notification.ts | 5 ++ .../src/models/json-schema/drive-file.ts | 4 ++ .../src/models/json-schema/notification.ts | 25 +++++++-- .../api/endpoints/drive/files/update.ts | 12 +++- packages/backend/src/types.ts | 2 + .../backend/test/unit/NoteCreateService.ts | 1 + .../src/components/MkNotification.vue | 55 +++++++++++++++++++ packages/frontend/src/const.ts | 1 + .../frontend/src/pages/drive.file.info.vue | 17 ++++-- packages/frontend/src/pages/note.vue | 15 +++++ packages/misskey-js/src/autogen/types.ts | 17 ++++-- 18 files changed, 197 insertions(+), 20 deletions(-) create mode 100644 packages/backend/migration/1739335129758-sensitiveFlag.js diff --git a/locales/index.d.ts b/locales/index.d.ts index 269480fb0..4bc906b3c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5362,6 +5362,15 @@ export interface Locale extends ILocale { * {x}に投稿されます */ "willBePostedAt": ParameterizedString<"x">; + /** + * 管理者によって、ドライブのファイルがセンシティブとして設定されました。 + * 詳細については、[NSFWガイドライン](https://go.misskey.io/media-guideline)を確認してください。 + */ + "sensitiveByModerator": string; + /** + * この情報は他のユーザーには公開されません。 + */ + "thisInfoIsNotVisibleOtherUser": string; "_bubbleGame": { /** * 遊び方 @@ -9747,6 +9756,10 @@ export interface Locale extends ILocale { * 通知の履歴をリセットする */ "flushNotification": string; + /** + * ドライブのファイルがセンシティブとして設定されました + */ + "sensitiveFlagAssigned": string; "_types": { /** * すべて diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 60507f0b1..1d1dc7857 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1334,6 +1334,8 @@ scheduled: "予約済み" unschedule: "予約を解除" setScheduledTime: "予約日時を設定" willBePostedAt: "{x}に投稿されます" +sensitiveByModerator: "管理者によって、ドライブのファイルがセンシティブとして設定されました。\n詳細については、[NSFWガイドライン](https://go.misskey.io/media-guideline)を確認してください。" +thisInfoIsNotVisibleOtherUser: "この情報は他のユーザーには公開されません。" _bubbleGame: howToPlay: "遊び方" @@ -2562,6 +2564,7 @@ _notification: renotedBySomeUsers: "{n}人がリノートしました" followedBySomeUsers: "{n}人にフォローされました" flushNotification: "通知の履歴をリセットする" + sensitiveFlagAssigned: "ドライブのファイルがセンシティブとして設定されました" _types: all: "すべて" diff --git a/packages/backend/migration/1739335129758-sensitiveFlag.js b/packages/backend/migration/1739335129758-sensitiveFlag.js new file mode 100644 index 000000000..b3ca6df6c --- /dev/null +++ b/packages/backend/migration/1739335129758-sensitiveFlag.js @@ -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"`); + } +} diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index a1d06a1d8..1a744f1b4 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -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, 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, diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 90e13153b..eb894326a 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -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), diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index bd8f9a1cf..b793d515c 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -183,6 +183,9 @@ export class NotificationEntityService implements OnModuleInit { header: notification.customHeader, icon: notification.customIcon, } : {}), + ...(notification.type === 'sensitiveFlagAssigned' ? { + fileId: notification.fileId, + } : {}), }); } diff --git a/packages/backend/src/models/DriveFile.ts b/packages/backend/src/models/DriveFile.ts index 079e9cd9d..6973e4d9d 100644 --- a/packages/backend/src/models/DriveFile.ts +++ b/packages/backend/src/models/DriveFile.ts @@ -162,6 +162,12 @@ export class MiDriveFile { }) public isSensitive: boolean; + @Index() + @Column('boolean', { + default: false, + }) + public isSensitiveByModerator: boolean; + @Index() @Column('boolean', { default: false, diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts index 4747b51b5..ef783d211 100644 --- a/packages/backend/src/models/Notification.ts +++ b/packages/backend/src/models/Notification.ts @@ -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; diff --git a/packages/backend/src/models/json-schema/drive-file.ts b/packages/backend/src/models/json-schema/drive-file.ts index ca88cc0e3..3cc98058a 100644 --- a/packages/backend/src/models/json-schema/drive-file.ts +++ b/packages/backend/src/models/json-schema/drive-file.ts @@ -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, diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts index e68240897..160fcae42 100644 --- a/packages/backend/src/models/json-schema/notification.ts +++ b/packages/backend/src/models/json-schema/notification.ts @@ -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: { 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 42c03204a..1a03827ec 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -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 { // 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; } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 945eb27b5..f6f8db812 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -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; diff --git a/packages/backend/test/unit/NoteCreateService.ts b/packages/backend/test/unit/NoteCreateService.ts index 6010fcee8..e1a1396b2 100644 --- a/packages/backend/test/unit/NoteCreateService.ts +++ b/packages/backend/test/unit/NoteCreateService.ts @@ -95,6 +95,7 @@ describe('NoteCreateService', () => { folderId: null, folder: null, isSensitive: false, + isSensitiveByModerator: false, maybeSensitive: false, maybePorn: false, isLink: false, diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 87418961b..ce14517ac 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -8,6 +8,14 @@ SPDX-License-Identifier: AGPL-3.0-only
+
+
+ +
+
@@ -71,6 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.tsx._notification.reactedBySomeUsers({ n: getActualReactedUsersCount(notification) }) }} {{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }} {{ notification.header }} + {{ i18n.ts._notification.sensitiveFlagAssigned }}
@@ -159,6 +168,10 @@ SPDX-License-Identifier: AGPL-3.0-only
+ + + {{ i18n.ts.thisInfoIsNotVisibleOtherUser }} + @@ -341,6 +354,12 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification) pointer-events: none; } +.t_sensitiveFlagAssigned { + padding: 3px; + background: var(--eventOther); + pointer-events: none; +} + .tail { flex: 1; min-width: 0; @@ -430,6 +449,42 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification) color: #fff; } +.iconFrame { + position: relative; + width: 100%; + height: 100%; + padding: 4px; + border-radius: 100%; + box-sizing: border-box; + pointer-events: none; + user-select: none; + filter: drop-shadow(0px 2px 2px #00000044); + box-shadow: 0 1px 0px #ffffff88 inset; + overflow: clip; + background: linear-gradient(0deg, #703827, #d37566); +} + +.iconImg { + width: calc(100% - 12px); + height: calc(100% - 12px); + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + filter: drop-shadow(0px 1px 2px #000000aa); +} + +.iconInner { + position: relative; + width: 100%; + height: 100%; + border-radius: 100%; + box-shadow: 0 1px 0px #ffffff88 inset; + background: linear-gradient(0deg, #d37566, #703827); +} + @container (max-width: 600px) { .root { padding: 16px; diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index e84958a69..6283391be 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -70,6 +70,7 @@ export const notificationTypes = [ 'noteScheduled', 'scheduledNotePosted', 'scheduledNoteError', + 'sensitiveFlagAssigned', 'app', ] as const; export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue index 8077edff5..bd690e1e7 100644 --- a/packages/frontend/src/pages/drive.file.info.vue +++ b/packages/frontend/src/pages/drive.file.info.vue @@ -6,6 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only