summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-04-02 19:16:18 +0900
committersyuilo <4439005+syuilo@users.noreply.github.com>2025-04-02 19:16:18 +0900
commite4ceab5f6cf02975ba524f886bfb62d08789bd77 (patch)
treee0c6575ee536a17d198c2a9d941b74e214999f49 /packages
parent🎨 (diff)
parentUpdate CHANGELOG.md (diff)
downloadsharkey-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')
-rw-r--r--packages/backend/migration/1736230492103-addAntennaHideNotesInSensitiveChannel.js16
-rw-r--r--packages/backend/package.json1
-rw-r--r--packages/backend/src/config.ts17
-rw-r--r--packages/backend/src/core/AntennaService.ts2
-rw-r--r--packages/backend/src/core/NoteCreateService.ts5
-rw-r--r--packages/backend/src/core/entities/AntennaEntityService.ts1
-rw-r--r--packages/backend/src/core/entities/MetaEntityService.ts1
-rw-r--r--packages/backend/src/models/Antenna.ts5
-rw-r--r--packages/backend/src/models/json-schema/antenna.ts5
-rw-r--r--packages/backend/src/models/json-schema/meta.ts32
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/notes.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/update.ts2
-rw-r--r--packages/backend/test/e2e/antennas.ts39
-rw-r--r--packages/frontend/package.json1
-rw-r--r--packages/frontend/src/boot/common.ts37
-rw-r--r--packages/frontend/src/components/MkAntennaEditor.vue4
-rw-r--r--packages/misskey-js/src/autogen/types.ts19
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;
};
};
};