From f59bf35615165bbefcbccfa2b0b21fd822d5ecfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sat, 4 Jul 2026 16:26:22 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9D=A1=E4=BB=B6=E3=81=AB=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E3=81=97=E3=81=9FURL=E3=83=97=E3=83=AC=E3=83=93?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=81=AE=E3=82=B5=E3=83=A0=E3=83=8D=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=82=92=E9=9A=A0=E3=81=99=E3=81=93=E3=81=A8=E3=81=8C?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1763?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * URLプレビューのサムネイルを隠す機能を追加 (MisskeyIO#214) * Update Changelog * fix * Update Changelog * fix * fix: もうセンシティブと判定されている場合はそれをそのまま通すように * fix: unnecessary import * fix lint --------- Co-authored-by: CyberRex --- CHANGELOG.md | 2 ++ locales/ja-JP.yml | 2 ++ .../1782581064131-urlPreviewSensitiveList.js | 16 ++++++++++++++++ packages/backend/src/models/Meta.ts | 5 +++++ .../src/server/api/endpoints/admin/meta.ts | 9 +++++++++ .../server/api/endpoints/admin/update-meta.ts | 10 ++++++++++ .../backend/src/server/web/UrlPreviewService.ts | 6 ++++++ packages/frontend/src/pages/admin/settings.vue | 9 +++++++++ packages/i18n/src/autogen/locale.ts | 8 ++++++++ packages/misskey-js/src/autogen/types.ts | 2 ++ 10 files changed, 69 insertions(+) create mode 100644 packages/backend/migration/1782581064131-urlPreviewSensitiveList.js diff --git a/CHANGELOG.md b/CHANGELOG.md index bad2db5ce2..a7dd8eefba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ ### General - Feat: コントロールパネルから二要素認証を解除できるように +- Feat: 条件に一致したURLプレビューのサムネイルを隠すことができるように + (Based on https://github.com/MisskeyIO/misskey/pull/214) ### Client - Fix: チャットでIMEの変換を確定するEnterでメッセージが送信されてしまうことがある問題を修正 diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 965330ee82..2f6c912044 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1419,6 +1419,8 @@ addToEmojiPalette: "絵文字パレットに追加" emojiPaletteAlreadyAddedConfirm: "この絵文字はすでにこの絵文字パレットに含まれています。追加しなおしますか?" append: "末尾に追加" prepend: "先頭に追加" +urlPreviewSensitiveList: "サムネイルの表示を制限するURL" +urlPreviewSensitiveListDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。スラッシュで囲むと正規表現になります。一致した場合、サムネイルが表示されなくなります。" _imageEditing: _vars: diff --git a/packages/backend/migration/1782581064131-urlPreviewSensitiveList.js b/packages/backend/migration/1782581064131-urlPreviewSensitiveList.js new file mode 100644 index 0000000000..8c0bdedeb9 --- /dev/null +++ b/packages/backend/migration/1782581064131-urlPreviewSensitiveList.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class UrlPreviewSensitiveList1782581064131 { + name = 'UrlPreviewSensitiveList1782581064131' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "urlPreviewSensitiveList" character varying(3072) array NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "urlPreviewSensitiveList"`); + } +} diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index d4214c0e5a..307b3f7a5d 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -672,6 +672,11 @@ export class MiMeta { }) public urlPreviewUserAgent: string | null; + @Column('varchar', { + length: 3072, array: true, default: '{}', + }) + public urlPreviewSensitiveList: string[]; + @Column('varchar', { length: 128, default: 'none', diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 956f991d32..849a05c334 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -544,6 +544,14 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + urlPreviewSensitiveList: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, federation: { type: 'string', enum: ['all', 'specified', 'none'], @@ -760,6 +768,7 @@ export default class extends Endpoint { // eslint- urlPreviewRequireContentLength: instance.urlPreviewRequireContentLength, urlPreviewUserAgent: instance.urlPreviewUserAgent, urlPreviewSummaryProxyUrl: instance.urlPreviewSummaryProxyUrl, + urlPreviewSensitiveList: instance.urlPreviewSensitiveList, federation: instance.federation, federationHosts: instance.federationHosts, deliverSuspendedSoftware: instance.deliverSuspendedSoftware, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index def6a0bda7..4a7df410bc 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -189,6 +189,12 @@ export const paramDef = { urlPreviewRequireContentLength: { type: 'boolean' }, urlPreviewUserAgent: { type: 'string', nullable: true }, urlPreviewSummaryProxyUrl: { type: 'string', nullable: true }, + urlPreviewSensitiveList: { + type: 'array', nullable: true, + items: { + type: 'string', + } + }, federation: { type: 'string', enum: ['all', 'none', 'specified'], @@ -734,6 +740,10 @@ export default class extends Endpoint { // eslint- set.urlPreviewSummaryProxyUrl = value === '' ? null : value; } + if (Array.isArray(ps.urlPreviewSensitiveList)) { + set.urlPreviewSensitiveList = ps.urlPreviewSensitiveList.filter(Boolean); + } + if (ps.federation !== undefined) { set.federation = ps.federation; } diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 886e876c40..e5ba8c596c 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -11,6 +11,7 @@ import { HttpRequestService } from '@/core/HttpRequestService.js'; import type Logger from '@/logger.js'; import { query } from '@/misc/prelude/url.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { ApiError } from '@/server/api/error.js'; import { MiMeta } from '@/models/Meta.js'; @@ -29,6 +30,7 @@ export class UrlPreviewService { private meta: MiMeta, private httpRequestService: HttpRequestService, + private utilityService: UtilityService, private loggerService: LoggerService, ) { this.logger = this.loggerService.getLogger('url-preview'); @@ -95,6 +97,10 @@ export class UrlPreviewService { summary.icon = this.wrap(summary.icon); summary.thumbnail = this.wrap(summary.thumbnail); + if (summary.sensitive !== true) { + summary.sensitive = this.utilityService.isKeyWordIncluded(summary.url, this.meta.urlPreviewSensitiveList); + } + // Cache 1day reply.header('Cache-Control', 'max-age=86400, immutable'); diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 99d4455939..31825f17ab 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -240,6 +240,13 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + + + + @@ -465,6 +472,7 @@ const urlPreviewForm = useForm({ urlPreviewRequireContentLength: meta.urlPreviewRequireContentLength, urlPreviewUserAgent: meta.urlPreviewUserAgent ?? '', urlPreviewSummaryProxyUrl: meta.urlPreviewSummaryProxyUrl ?? '', + urlPreviewSensitiveList: meta.urlPreviewSensitiveList.join('\n'), }, async (state) => { await os.apiWithDialog('admin/update-meta', { urlPreviewEnabled: state.urlPreviewEnabled, @@ -474,6 +482,7 @@ const urlPreviewForm = useForm({ urlPreviewRequireContentLength: state.urlPreviewRequireContentLength, urlPreviewUserAgent: state.urlPreviewUserAgent, urlPreviewSummaryProxyUrl: state.urlPreviewSummaryProxyUrl, + urlPreviewSensitiveList: state.urlPreviewSensitiveList.split('\n'), }); fetchInstance(true); }); diff --git a/packages/i18n/src/autogen/locale.ts b/packages/i18n/src/autogen/locale.ts index 367c9bb8f3..5103f3845f 100644 --- a/packages/i18n/src/autogen/locale.ts +++ b/packages/i18n/src/autogen/locale.ts @@ -5691,6 +5691,14 @@ export interface Locale extends ILocale { * 先頭に追加 */ "prepend": string; + /** + * サムネイルの表示を制限するURL + */ + "urlPreviewSensitiveList": string; + /** + * スペースで区切るとAND指定になり、改行で区切るとOR指定になります。スラッシュで囲むと正規表現になります。一致した場合、サムネイルが表示されなくなります。 + */ + "urlPreviewSensitiveListDescription": string; "_imageEditing": { "_vars": { /** diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index efb20fd43c..0f5db3e205 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -9557,6 +9557,7 @@ export interface operations { urlPreviewRequireContentLength: boolean; urlPreviewUserAgent: string | null; urlPreviewSummaryProxyUrl: string | null; + urlPreviewSensitiveList: string[]; /** @enum {string} */ federation: 'all' | 'specified' | 'none'; federationHosts: string[]; @@ -13076,6 +13077,7 @@ export interface operations { urlPreviewRequireContentLength?: boolean; urlPreviewUserAgent?: string | null; urlPreviewSummaryProxyUrl?: string | null; + urlPreviewSensitiveList?: string[] | null; /** @enum {string} */ federation?: 'all' | 'none' | 'specified'; federationHosts?: string[];