diff --git a/locales/en-US.yml b/locales/en-US.yml index 730e7450dc..2726641a6e 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -745,6 +745,8 @@ thisIsExperimentalFeature: "This is an experimental feature. Its functionality i developer: "Developer" makeExplorable: "Make account visible in \"Explore\"" makeExplorableDescription: "If you turn this off, your account will not show up in the \"Explore\" section." +makeIndexable: "Make public notes indexable" +makeIndexableDescription: "Allow note search to index your public notes." showGapBetweenNotesInTimeline: "Show a gap between posts on the timeline" duplicate: "Duplicate" left: "Left" diff --git a/locales/index.d.ts b/locales/index.d.ts index d4934b77a5..dfdf3119b6 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -748,6 +748,8 @@ export interface Locale { "developer": string; "makeExplorable": string; "makeExplorableDescription": string; + "makeIndexable": string; + "makeIndexableDescription": string; "showGapBetweenNotesInTimeline": string; "duplicate": string; "left": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 36521c8817..fb2a64b399 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -745,6 +745,8 @@ thisIsExperimentalFeature: "これは実験的な機能です。仕様が変更 developer: "開発者" makeExplorable: "アカウントを見つけやすくする" makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らなくなります。" +makeIndexable: "公開ノートをインデックス化" +makeIndexableDescription: "ノート検索で公開ノートにインデックスを付けられるようにする。" showGapBetweenNotesInTimeline: "タイムラインのノートを離して表示" duplicate: "複製" left: "左" diff --git a/packages/backend/migration/1699376974000-isIndexable.js b/packages/backend/migration/1699376974000-isIndexable.js new file mode 100644 index 0000000000..ae826924a8 --- /dev/null +++ b/packages/backend/migration/1699376974000-isIndexable.js @@ -0,0 +1,11 @@ +export class IsIndexable1699376974000 { + name = 'IsIndexable1699376974000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" ADD "isIndexable" boolean NOT NULL DEFAULT true`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isIndexable"`); + } +} diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 40d1db600d..3e7bba9e1e 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -223,8 +223,7 @@ export class NoteCreateService implements OnApplicationShutdown { username: MiUser['username']; host: MiUser['host']; isBot: MiUser['isBot']; - isCat: MiUser['isCat']; - speakAsCat: MiUser['speakAsCat']; + isIndexable: MiUser['isIndexable']; }, data: Option, silent = false): Promise { // チャンネル外にリプライしたら対象のスコープに合わせる // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) @@ -482,6 +481,7 @@ export class NoteCreateService implements OnApplicationShutdown { username: MiUser['username']; host: MiUser['host']; isBot: MiUser['isBot']; + isIndexable: MiUser['isIndexable']; }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { const meta = await this.metaService.fetch(); @@ -712,7 +712,7 @@ export class NoteCreateService implements OnApplicationShutdown { } // Register to search database - this.index(note); + if (user.isIndexable) this.index(note); } @bindThis diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index e88ab053e6..23ce2ab7a9 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -224,6 +224,7 @@ export class NoteEditService implements OnApplicationShutdown { username: MiUser['username']; host: MiUser['host']; isBot: MiUser['isBot']; + isIndexable: MiUser['isIndexable']; }, editid: MiNote['id'], data: Option, silent = false): Promise { if (!editid) { throw new Error('fail'); @@ -498,6 +499,7 @@ export class NoteEditService implements OnApplicationShutdown { username: MiUser['username']; host: MiUser['host']; isBot: MiUser['isBot']; + isIndexable: MiUser['isIndexable']; }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { // Register host if (this.userEntityService.isRemoteUser(user)) { @@ -686,7 +688,7 @@ export class NoteEditService implements OnApplicationShutdown { } // Register to search database - this.index(note); + if (user.isIndexable) this.index(note); } @bindThis diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 8facc2536c..fb32eab203 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -506,6 +506,7 @@ export class ApRendererService { discoverable: user.isExplorable, publicKey: this.renderKey(user, keypair, '#main-key'), isCat: user.isCat, + isIndexable: user.isIndexable, speakAsCat: user.speakAsCat, attachment: attachment.length ? attachment : undefined, }; diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index cfb380a322..02bacacc6b 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -310,6 +310,7 @@ export class ApPersonService implements OnModuleInit { backgroundId: null, lastFetchedAt: new Date(), name: truncate(person.name, nameLength), + isIndexable: person.isIndexable ?? true, isLocked: person.manuallyApprovesFollowers, movedToUri: person.movedTo, movedAt: person.movedTo ? new Date() : null, @@ -476,6 +477,7 @@ export class ApPersonService implements OnModuleInit { isBot: getApType(object) === 'Service' || getApType(object) === 'Application', isCat: (person as any).isCat === true, speakAsCat: (person as any).speakAsCat != null ? (person as any).speakAsCat === true : (person as any).isCat === true, + isIndexable: person.isIndexable ?? true, isLocked: person.manuallyApprovesFollowers, movedToUri: person.movedTo ?? null, alsoKnownAs: person.alsoKnownAs ?? null, diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index 62d7ef93f2..02667214d3 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -184,6 +184,7 @@ export interface IActor extends IObject { }; 'vcard:bday'?: string; 'vcard:Address'?: string; + isIndexable?: boolean; listenbrainz?: string; backgroundUrl?: string; } diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index e40ff27c70..06ce60c7c2 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -399,6 +399,7 @@ export class UserEntityService implements OnModuleInit { }))) : [], isBot: user.isBot, isCat: user.isCat, + isIndexable: user.isIndexable, isSilenced: user.isSilenced || this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote), speakAsCat: user.speakAsCat ?? false, approved: user.approved, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index c4bc98fc10..bff2b142e2 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -188,6 +188,12 @@ export class MiUser { }) public isSilenced: boolean; + @Column('boolean', { + default: true, + comment: 'Whether the User\'s notes get indexed.', + }) + public isIndexable: boolean; + @Column('boolean', { default: false, comment: 'Whether the User is locked.', diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index e1b6261897..aa293d583b 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -79,6 +79,10 @@ export const packedUserLiteSchema = { type: 'boolean', nullable: false, optional: false, }, + isIndexable: { + type: 'boolean', + nullable: false, optional: false, + }, isBot: { type: 'boolean', nullable: false, optional: true, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 373614f786..aa4dd05113 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -177,6 +177,7 @@ export const paramDef = { autoAcceptFollowed: { type: 'boolean' }, noCrawle: { type: 'boolean' }, preventAiLearning: { type: 'boolean' }, + isIndexable: { type: 'boolean' }, isBot: { type: 'boolean' }, isCat: { type: 'boolean' }, speakAsCat: { type: 'boolean' }, @@ -278,6 +279,7 @@ export default class extends Endpoint { // eslint- if (typeof ps.isExplorable === 'boolean') updates.isExplorable = ps.isExplorable; if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; if (typeof ps.publicReactions === 'boolean') profileUpdates.publicReactions = ps.publicReactions; + if (typeof ps.isIndexable === 'boolean') updates.isIndexable = ps.isIndexable; if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 1ec8aee0a0..e7414acd26 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -33,6 +33,10 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.preventAiLearning }}{{ i18n.ts.beta }} + + {{ i18n.ts.makeIndexable }} + + {{ i18n.ts.makeExplorable }} @@ -82,6 +86,7 @@ let autoAcceptFollowed = $ref($i.autoAcceptFollowed); let noCrawle = $ref($i.noCrawle); let preventAiLearning = $ref($i.preventAiLearning); let isExplorable = $ref($i.isExplorable); +let isIndexable = $ref($i.isIndexable); let hideOnlineStatus = $ref($i.hideOnlineStatus); let publicReactions = $ref($i.publicReactions); let ffVisibility = $ref($i.ffVisibility); @@ -98,6 +103,7 @@ function save() { noCrawle: !!noCrawle, preventAiLearning: !!preventAiLearning, isExplorable: !!isExplorable, + isIndexable: !!isIndexable, hideOnlineStatus: !!hideOnlineStatus, publicReactions: !!publicReactions, ffVisibility: ffVisibility, diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 9c01446fa2..dee1aed40b 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -37,6 +37,7 @@ export type UserLite = { }; isCat?: boolean; isBot?: boolean; + isIndexable?: boolean; }; export type UserDetailed = UserLite & { @@ -65,6 +66,7 @@ export type UserDetailed = UserLite & { speakAsCat: boolean; isFollowed: boolean; isFollowing: boolean; + isIndexable: boolean; isLocked: boolean; isModerator: boolean; isMuted: boolean;