diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-04-02 19:16:18 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-04-02 19:16:18 +0900 |
| commit | e4ceab5f6cf02975ba524f886bfb62d08789bd77 (patch) | |
| tree | e0c6575ee536a17d198c2a9d941b74e214999f49 /packages | |
| parent | 🎨 (diff) | |
| parent | Update CHANGELOG.md (diff) | |
| download | sharkey-e4ceab5f6cf02975ba524f886bfb62d08789bd77.tar.gz sharkey-e4ceab5f6cf02975ba524f886bfb62d08789bd77.tar.bz2 sharkey-e4ceab5f6cf02975ba524f886bfb62d08789bd77.zip | |
Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop
Diffstat (limited to 'packages')
18 files changed, 187 insertions, 5 deletions
diff --git a/packages/backend/migration/1736230492103-addAntennaHideNotesInSensitiveChannel.js b/packages/backend/migration/1736230492103-addAntennaHideNotesInSensitiveChannel.js new file mode 100644 index 0000000000..74225de96a --- /dev/null +++ b/packages/backend/migration/1736230492103-addAntennaHideNotesInSensitiveChannel.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class AddAntennaHideNotesInSensitiveChannel1736230492103 { + name = 'AddAntennaHideNotesInSensitiveChannel1736230492103' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "antenna" ADD "hideNotesInSensitiveChannel" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "hideNotesInSensitiveChannel"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index bcaa6357ce..d7705b2b9e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -186,6 +186,7 @@ "devDependencies": { "@jest/globals": "29.7.0", "@nestjs/platform-express": "10.4.15", + "@sentry/vue": "9.8.0", "@simplewebauthn/types": "12.0.0", "@swc/jest": "0.2.37", "@types/accepts": "1.3.7", diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 32ea700748..646fa07911 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -7,7 +7,8 @@ import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; import * as yaml from 'js-yaml'; -import * as Sentry from '@sentry/node'; +import type * as Sentry from '@sentry/node'; +import type * as SentryVue from '@sentry/vue'; import type { RedisOptions } from 'ioredis'; type RedisOptionsSource = Partial<RedisOptions> & { @@ -62,7 +63,12 @@ type Source = { scope?: 'local' | 'global' | string[]; }; sentryForBackend?: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; }; - sentryForFrontend?: { options: Partial<Sentry.NodeOptions> }; + sentryForFrontend?: { + options: Partial<SentryVue.BrowserOptions> & { dsn: string }; + vueIntegration?: SentryVue.VueIntegrationOptions | null; + browserTracingIntegration?: Parameters<typeof SentryVue.browserTracingIntegration>[0] | null; + replayIntegration?: Parameters<typeof SentryVue.replayIntegration>[0] | null; + }; publishTarballInsteadOfProvideRepositoryUrl?: boolean; @@ -198,7 +204,12 @@ export type Config = { redisForTimelines: RedisOptions & RedisOptionsSource; redisForReactions: RedisOptions & RedisOptionsSource; sentryForBackend: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; } | undefined; - sentryForFrontend: { options: Partial<Sentry.NodeOptions> } | undefined; + sentryForFrontend: { + options: Partial<SentryVue.BrowserOptions> & { dsn: string }; + vueIntegration?: SentryVue.VueIntegrationOptions | null; + browserTracingIntegration?: Parameters<typeof SentryVue.browserTracingIntegration>[0] | null; + replayIntegration?: Parameters<typeof SentryVue.replayIntegration>[0] | null; + } | undefined; perChannelMaxNoteCacheCount: number; perUserNotificationsMaxCount: number; deactivateAntennaThreshold: number; diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index e827ffa68c..5ad5bcf72a 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -114,6 +114,8 @@ export class AntennaService implements OnApplicationShutdown { if (note.visibility === 'specified') return false; if (note.visibility === 'followers') return false; + if (antenna.hideNotesInSensitiveChannel && note.channel?.isSensitive) return false; + if (antenna.excludeBots && noteUser.isBot) return false; if (antenna.localOnly && noteUser.host != null) return false; diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 8f416f398c..1ddb2b173d 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -532,7 +532,10 @@ export class NoteCreateService implements OnApplicationShutdown { this.pushToTl(note, user); - this.antennaService.addNoteToAntennas(note, user); + this.antennaService.addNoteToAntennas({ + ...note, + channel: data.channel ?? null, + }, user); if (data.reply) { this.saveReply(data.reply, note); diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index e770028af3..e81c1e8db4 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -41,6 +41,7 @@ export class AntennaEntityService { excludeBots: antenna.excludeBots, withReplies: antenna.withReplies, withFile: antenna.withFile, + hideNotesInSensitiveChannel: antenna.hideNotesInSensitiveChannel, isActive: antenna.isActive, hasUnreadNote: false, // TODO notify: false, // 後方互換性のため diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts index 08717bd066..02783dc450 100644 --- a/packages/backend/src/core/entities/MetaEntityService.ts +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -127,6 +127,7 @@ export class MetaEntityService { policies: { ...DEFAULT_POLICIES, ...instance.policies }, + sentryForFrontend: this.config.sentryForFrontend ?? null, mediaProxy: this.config.mediaProxy, enableUrlPreview: instance.urlPreviewEnabled, noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local', diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts index 33e6f48189..0d92c5cade 100644 --- a/packages/backend/src/models/Antenna.ts +++ b/packages/backend/src/models/Antenna.ts @@ -100,4 +100,9 @@ export class MiAntenna { default: false, }) public localOnly: boolean; + + @Column('boolean', { + default: false, + }) + public hideNotesInSensitiveChannel: boolean; } diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts index b5b9a5b42c..2bdaca80d0 100644 --- a/packages/backend/src/models/json-schema/antenna.ts +++ b/packages/backend/src/models/json-schema/antenna.ts @@ -100,5 +100,10 @@ export const packedAntennaSchema = { optional: false, nullable: false, default: false, }, + hideNotesInSensitiveChannel: { + type: 'boolean', + optional: false, nullable: false, + default: false, + }, }, } as const; diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts index 1e25c355ca..2cd7620af0 100644 --- a/packages/backend/src/models/json-schema/meta.ts +++ b/packages/backend/src/models/json-schema/meta.ts @@ -211,6 +211,38 @@ export const packedMetaLiteSchema = { type: 'boolean', optional: false, nullable: false, }, + sentryForFrontend: { + type: 'object', + optional: false, nullable: true, + properties: { + options: { + type: 'object', + optional: false, nullable: false, + properties: { + dsn: { + type: 'string', + optional: false, nullable: false, + }, + }, + additionalProperties: true, + }, + vueIntegration: { + type: 'object', + optional: true, nullable: true, + additionalProperties: true, + }, + browserTracingIntegration: { + type: 'object', + optional: true, nullable: true, + additionalProperties: true, + }, + replayIntegration: { + type: 'object', + optional: true, nullable: true, + additionalProperties: true, + }, + }, + }, mediaProxy: { type: 'string', optional: false, nullable: false, diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index e0c8ddcc84..9b34b52b16 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -73,6 +73,7 @@ export const paramDef = { excludeBots: { type: 'boolean' }, withReplies: { type: 'boolean' }, withFile: { type: 'boolean' }, + hideNotesInSensitiveChannel: { type: 'boolean' }, }, required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile'], } as const; @@ -133,6 +134,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- excludeBots: ps.excludeBots, withReplies: ps.withReplies, withFile: ps.withFile, + hideNotesInSensitiveChannel: ps.hideNotesInSensitiveChannel, }); this.globalEventService.publishInternalEvent('antennaCreated', antenna); diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 4b8543c2d1..a44eb6720b 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -108,6 +108,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('reply.user', 'replyUser') .leftJoinAndSelect('renote.user', 'renoteUser'); + // NOTE: センシティブ除外の設定はこのエンドポイントでは無視する。 + // https://github.com/misskey-dev/misskey/pull/15346#discussion_r1929950255 + this.queryService.generateVisibilityQuery(query, me); this.queryService.generateMutedUserQueryForNotes(query, me); this.queryService.generateBlockedUserQueryForNotes(query, me); diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 10f26b1912..c5ddefebf7 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -72,6 +72,7 @@ export const paramDef = { excludeBots: { type: 'boolean' }, withReplies: { type: 'boolean' }, withFile: { type: 'boolean' }, + hideNotesInSensitiveChannel: { type: 'boolean' }, }, required: ['antennaId'], } as const; @@ -129,6 +130,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- excludeBots: ps.excludeBots, withReplies: ps.withReplies, withFile: ps.withFile, + hideNotesInSensitiveChannel: ps.hideNotesInSensitiveChannel, isActive: true, lastUsedAt: new Date(), }); diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts index a544db955a..eb9583ee01 100644 --- a/packages/backend/test/e2e/antennas.ts +++ b/packages/backend/test/e2e/antennas.ts @@ -146,6 +146,7 @@ describe('アンテナ', () => { caseSensitive: false, createdAt: new Date(response.createdAt).toISOString(), excludeKeywords: [['']], + hideNotesInSensitiveChannel: false, hasUnreadNote: false, isActive: true, keywords: [['keyword']], @@ -217,6 +218,8 @@ describe('アンテナ', () => { { parameters: () => ({ withReplies: true }) }, { parameters: () => ({ withFile: false }) }, { parameters: () => ({ withFile: true }) }, + { parameters: () => ({ hideNotesInSensitiveChannel: false }) }, + { parameters: () => ({ hideNotesInSensitiveChannel: true }) }, ]; test.each(antennaParamPattern)('を作成できること($#)', async ({ parameters }) => { const response = await successfulApiCall({ @@ -626,6 +629,42 @@ describe('アンテナ', () => { assert.deepStrictEqual(response, expected); }); + test('が取得できること(センシティブチャンネルのノートを除く)', async () => { + const keyword = 'キーワード'; + const antenna = await successfulApiCall({ + endpoint: 'antennas/create', + parameters: { ...defaultParam, keywords: [[keyword]], hideNotesInSensitiveChannel: true }, + user: alice, + }); + const nonSensitiveChannel = await successfulApiCall({ + endpoint: 'channels/create', + parameters: { name: 'test', isSensitive: false }, + user: alice, + }); + const sensitiveChannel = await successfulApiCall({ + endpoint: 'channels/create', + parameters: { name: 'test', isSensitive: true }, + user: alice, + }); + + const noteInLocal = await post(bob, { text: `test ${keyword}` }); + const noteInNonSensitiveChannel = await post(bob, { text: `test ${keyword}`, channelId: nonSensitiveChannel.id }); + await post(bob, { text: `test ${keyword}`, channelId: sensitiveChannel.id }); + + const response = await successfulApiCall({ + endpoint: 'antennas/notes', + parameters: { antennaId: antenna.id }, + user: alice, + }); + // 最後に投稿したものが先頭に来る。 + const expected = [ + noteInNonSensitiveChannel, + noteInLocal, + ]; + assert.deepStrictEqual(response, expected); + }); + + test.skip('が取得でき、日付指定のPaginationに一貫性があること', async () => { }); test.each([ { label: 'ID指定', offsetBy: 'id' }, diff --git a/packages/frontend/package.json b/packages/frontend/package.json index fe0abb173b..01dcf09d47 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -25,6 +25,7 @@ "@rollup/plugin-json": "6.1.0", "@rollup/plugin-replace": "6.0.2", "@rollup/pluginutils": "5.1.4", + "@sentry/vue": "9.8.0", "@syuilo/aiscript": "0.19.0", "@tabler/icons-webfont": "3.31.0", "@twemoji/parser": "15.1.1", diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 7a88b938dd..c8098b6cf8 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -5,7 +5,7 @@ import { computed, watch, version as vueVersion } from 'vue'; import { compareVersions } from 'compare-versions'; -import { version, lang, updateLocale, locale } from '@@/js/config.js'; +import { version, lang, updateLocale, locale, apiUrl } from '@@/js/config.js'; import defaultLightTheme from '@@/themes/l-light.json5'; import defaultDarkTheme from '@@/themes/d-green-lime.json5'; import type { App } from 'vue'; @@ -291,6 +291,41 @@ export async function common(createVue: () => Promise<App<Element>>) { return root; })(); + if (instance.sentryForFrontend) { + const Sentry = await import('@sentry/vue'); + Sentry.init({ + app, + integrations: [ + ...(instance.sentryForFrontend.vueIntegration !== undefined ? [ + Sentry.vueIntegration(instance.sentryForFrontend.vueIntegration ?? undefined), + ] : []), + ...(instance.sentryForFrontend.browserTracingIntegration !== undefined ? [ + Sentry.browserTracingIntegration(instance.sentryForFrontend.browserTracingIntegration ?? undefined), + ] : []), + ...(instance.sentryForFrontend.replayIntegration !== undefined ? [ + Sentry.replayIntegration(instance.sentryForFrontend.replayIntegration ?? undefined), + ] : []), + ], + + // Set tracesSampleRate to 1.0 to capture 100% + tracesSampleRate: 1.0, + + // Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled + ...(instance.sentryForFrontend.browserTracingIntegration !== undefined ? { + tracePropagationTargets: [apiUrl], + } : {}), + + // Capture Replay for 10% of all sessions, + // plus for 100% of sessions with an error + ...(instance.sentryForFrontend.replayIntegration !== undefined ? { + replaysSessionSampleRate: 0.1, + replaysOnErrorSampleRate: 1.0, + } : {}), + + ...instance.sentryForFrontend.options, + }); + } + app.mount(rootEl); // boot.jsのやつを解除 diff --git a/packages/frontend/src/components/MkAntennaEditor.vue b/packages/frontend/src/components/MkAntennaEditor.vue index ac71618ee2..e1c8200b73 100644 --- a/packages/frontend/src/components/MkAntennaEditor.vue +++ b/packages/frontend/src/components/MkAntennaEditor.vue @@ -39,6 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> <MkSwitch v-model="caseSensitive">{{ i18n.ts.caseSensitive }}</MkSwitch> <MkSwitch v-model="withFile">{{ i18n.ts.withFileAntenna }}</MkSwitch> + <MkSwitch v-model="hideNotesInSensitiveChannel">{{ i18n.ts.hideNotesInSensitiveChannel }}</MkSwitch> </div> <div :class="$style.actions"> <div class="_buttons"> @@ -86,6 +87,7 @@ const initialAntenna = deepMerge<PartialAllowedAntenna>(props.antenna ?? {}, { caseSensitive: false, localOnly: false, withFile: false, + hideNotesInSensitiveChannel: false, isActive: true, hasUnreadNote: false, notify: false, @@ -108,6 +110,7 @@ const localOnly = ref<boolean>(initialAntenna.localOnly); const excludeBots = ref<boolean>(initialAntenna.excludeBots); const withReplies = ref<boolean>(initialAntenna.withReplies); const withFile = ref<boolean>(initialAntenna.withFile); +const hideNotesInSensitiveChannel = ref<boolean>(initialAntenna.hideNotesInSensitiveChannel); const userLists = ref<Misskey.entities.UserList[] | null>(null); watch(() => src.value, async () => { @@ -124,6 +127,7 @@ async function saveAntenna() { excludeBots: excludeBots.value, withReplies: withReplies.value, withFile: withFile.value, + hideNotesInSensitiveChannel: hideNotesInSensitiveChannel.value, caseSensitive: caseSensitive.value, localOnly: localOnly.value, users: users.value.trim().split('\n').map(x => x.trim()), diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index d46df2423a..037b09660c 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4898,6 +4898,8 @@ export type components = { hasUnreadNote: boolean; /** @default false */ notify: boolean; + /** @default false */ + hideNotesInSensitiveChannel: boolean; }; Clip: { /** @@ -5309,6 +5311,21 @@ export type components = { enableEmail: boolean; enableServiceWorker: boolean; translatorAvailable: boolean; + sentryForFrontend: ({ + options: { + dsn: string; + [key: string]: unknown; + }; + vueIntegration?: { + [key: string]: unknown; + } | null; + browserTracingIntegration?: { + [key: string]: unknown; + } | null; + replayIntegration?: { + [key: string]: unknown; + } | null; + }) | null; mediaProxy: string; enableUrlPreview: boolean; backgroundImageUrl: string | null; @@ -11290,6 +11307,7 @@ export type operations = { excludeBots?: boolean; withReplies: boolean; withFile: boolean; + hideNotesInSensitiveChannel?: boolean; }; }; }; @@ -11571,6 +11589,7 @@ export type operations = { excludeBots?: boolean; withReplies?: boolean; withFile?: boolean; + hideNotesInSensitiveChannel?: boolean; }; }; }; |