From 1fa1d316969de15d6aaef1dbf0a0b95aec30e377 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 16 Oct 2023 10:45:22 +0900 Subject: perf(backend): createdAtをidから取得するように & 無駄なDateインスタンスの生成を避けるように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/endpoints/admin/ad/create.ts | 3 +-- .../backend/src/server/api/endpoints/admin/announcements/create.ts | 1 - .../backend/src/server/api/endpoints/admin/announcements/list.ts | 4 +++- packages/backend/src/server/api/endpoints/admin/drive/show-file.ts | 4 +++- packages/backend/src/server/api/endpoints/admin/emoji/copy.ts | 2 +- packages/backend/src/server/api/endpoints/admin/get-user-ips.ts | 7 +++++-- packages/backend/src/server/api/endpoints/admin/invite/create.ts | 3 +-- packages/backend/src/server/api/endpoints/admin/roles/users.ts | 4 +++- packages/backend/src/server/api/endpoints/admin/show-user.ts | 4 +++- 9 files changed, 20 insertions(+), 12 deletions(-) (limited to 'packages/backend/src/server/api/endpoints/admin') diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index e48dffecf4..17f792639b 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -44,8 +44,7 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const ad = await this.adsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), expiresAt: new Date(ps.expiresAt), startsAt: new Date(ps.startsAt), dayOfWeek: ps.dayOfWeek, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 262b36b9a4..253a29cf5a 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -71,7 +71,6 @@ export default class extends Endpoint { // eslint- ) { super(meta, paramDef, async (ps, me) => { const { raw, packed } = await this.announcementService.create({ - createdAt: new Date(), updatedAt: null, title: ps.title, text: ps.text, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index c82e702eef..fefc379c00 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -9,6 +9,7 @@ import type { MiAnnouncement } from '@/models/Announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['admin'], @@ -81,6 +82,7 @@ export default class extends Endpoint { // eslint- private announcementReadsRepository: AnnouncementReadsRepository, private queryService: QueryService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); @@ -102,7 +104,7 @@ export default class extends Endpoint { // eslint- return announcements.map(announcement => ({ id: announcement.id, - createdAt: announcement.createdAt.toISOString(), + createdAt: this.idService.parse(announcement.id).date.toISOString(), updatedAt: announcement.updatedAt?.toISOString() ?? null, title: announcement.title, text: announcement.text, diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 7fb5342f8d..4e5320007e 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -8,6 +8,7 @@ import type { DriveFilesRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; +import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -163,6 +164,7 @@ export default class extends Endpoint { // eslint- private usersRepository: UsersRepository, private roleService: RoleService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const file = ps.fileId ? await this.driveFilesRepository.findOneBy({ id: ps.fileId }) : await this.driveFilesRepository.findOne({ @@ -212,7 +214,7 @@ export default class extends Endpoint { // eslint- type: file.type, name: file.name, md5: file.md5, - createdAt: file.createdAt.toISOString(), + createdAt: this.idService.parse(file.id).date.toISOString(), requestIp: iAmModerator ? file.requestIp : null, requestHeaders: iAmModerator && !ownerIsModerator ? file.requestHeaders : null, }; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index c5f986ff02..a65e4e7624 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -79,7 +79,7 @@ export default class extends Endpoint { // eslint- } const copied = await this.emojisRepository.insert({ - id: this.idService.genId(), + id: this.idService.gen(), updatedAt: new Date(), name: emoji.name, host: null, diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index cf94c998fa..7b807e848b 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserIpsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['admin'], @@ -28,17 +29,19 @@ export default class extends Endpoint { // eslint- constructor( @Inject(DI.userIpsRepository) private userIpsRepository: UserIpsRepository, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const ips = await this.userIpsRepository.find({ where: { userId: ps.userId }, - order: { createdAt: 'DESC' }, + order: { id: 'DESC' }, take: 30, }); return ips.map(x => ({ ip: x.ip, - createdAt: x.createdAt.toISOString(), + createdAt: this.idService.parse(x.id).date.toISOString(), })); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts index 2cc5ab6e35..4a22fd4824 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts @@ -72,8 +72,7 @@ export default class extends Endpoint { // eslint- for (let i = 0; i < ps.count; i++) { ticketsPromises.push(this.registrationTicketsRepository.insert({ - id: this.idService.genId(), - createdAt: new Date(), + id: this.idService.gen(), expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null, code: generateInviteCode(), }).then(x => this.registrationTicketsRepository.findOneByOrFail(x.identifiers[0]))); diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts index ef5627bc9a..b7f9aa0495 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts @@ -10,6 +10,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -49,6 +50,7 @@ export default class extends Endpoint { // eslint- private queryService: QueryService, private userEntityService: UserEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const role = await this.rolesRepository.findOneBy({ @@ -74,7 +76,7 @@ export default class extends Endpoint { // eslint- return await Promise.all(assigns.map(async assign => ({ id: assign.id, - createdAt: assign.createdAt, + createdAt: this.idService.parse(assign.id).date.toISOString(), user: await this.userEntityService.pack(assign.user!, me, { detail: true }), expiresAt: assign.expiresAt, }))); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 0731413d05..f550c4fd28 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -9,6 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['admin'], @@ -44,6 +45,7 @@ export default class extends Endpoint { // eslint- private roleService: RoleService, private roleEntityService: RoleEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ @@ -92,7 +94,7 @@ export default class extends Endpoint { // eslint- policies: await this.roleService.getUserPolicies(user.id), roles: await this.roleEntityService.packMany(roles, me), roleAssigns: roleAssigns.map(a => ({ - createdAt: a.createdAt.toISOString(), + createdAt: this.idService.parse(a.id).date.toISOString(), expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null, roleId: a.roleId, })), -- cgit v1.2.3-freya From 24437a04d49bb720ac632959270e072f072707c6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 16 Oct 2023 12:00:56 +0900 Subject: fix of 1fa1d31696 --- packages/backend/src/core/AccountMoveService.ts | 6 ++---- packages/backend/src/server/api/endpoints/admin/invite/list.ts | 4 ++-- packages/backend/src/server/api/endpoints/admin/show-users.ts | 4 ++-- packages/backend/src/server/api/endpoints/drive/files.ts | 4 ++-- packages/backend/src/server/api/endpoints/hashtags/users.ts | 4 ++-- packages/backend/src/server/api/endpoints/users.ts | 4 ++-- 6 files changed, 12 insertions(+), 14 deletions(-) (limited to 'packages/backend/src/server/api/endpoints/admin') diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index ed2891113b..350aa6ba24 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -180,7 +180,7 @@ export class AccountMoveService { { muteeId: dst.id, expiresAt: IsNull() }, ).then(mutings => mutings.map(muting => muting.muterId)); - const newMutings: Map = new Map(); + const newMutings: Map = new Map(); // 重複しないようにIDを生成 const genId = (): string => { @@ -194,7 +194,6 @@ export class AccountMoveService { if (existingMutingsMuterUserIds.includes(muting.muterId)) continue; // skip if already muted indefinitely newMutings.set(genId(), { ...muting, - createdAt: new Date(), muteeId: dst.id, }); } @@ -228,7 +227,7 @@ export class AccountMoveService { }, }).then(memberships => memberships.map(membership => membership.userListId)); - const newMemberships: Map = new Map(); + const newMemberships: Map = new Map(); // 重複しないようにIDを生成 const genId = (): string => { @@ -241,7 +240,6 @@ export class AccountMoveService { for (const membership of oldMemberships) { if (existingUserListIds.includes(membership.userListId)) continue; // skip if dst exists in this user's list newMemberships.set(genId(), { - createdAt: new Date(), userId: dst.id, userListId: membership.userListId, userListUserId: membership.userListUserId, diff --git a/packages/backend/src/server/api/endpoints/admin/invite/list.ts b/packages/backend/src/server/api/endpoints/admin/invite/list.ts index a20a51121a..f25d3fcb33 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite/list.ts @@ -56,8 +56,8 @@ export default class extends Endpoint { // eslint- } switch (ps.sort) { - case '+createdAt': query.orderBy('ticket.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('ticket.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('ticket.id', 'DESC'); break; + case '-createdAt': query.orderBy('ticket.id', 'ASC'); break; case '+usedAt': query.orderBy('ticket.usedAt', 'DESC', 'NULLS LAST'); break; case '-usedAt': query.orderBy('ticket.usedAt', 'ASC', 'NULLS FIRST'); break; default: query.orderBy('ticket.id', 'DESC'); break; diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index e89e1a1490..fc810987d2 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -99,8 +99,8 @@ export default class extends Endpoint { // eslint- switch (ps.sort) { case '+follower': query.orderBy('user.followersCount', 'DESC'); break; case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('user.id', 'DESC'); break; + case '-createdAt': query.orderBy('user.id', 'ASC'); break; case '+updatedAt': query.orderBy('user.updatedAt', 'DESC', 'NULLS LAST'); break; case '-updatedAt': query.orderBy('user.updatedAt', 'ASC', 'NULLS FIRST'); break; case '+lastActiveDate': query.orderBy('user.lastActiveDate', 'DESC', 'NULLS LAST'); break; diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 6f3a62977f..b7e9d12e94 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -69,8 +69,8 @@ export default class extends Endpoint { // eslint- } switch (ps.sort) { - case '+createdAt': query.orderBy('file.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('file.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('file.id', 'DESC'); break; + case '-createdAt': query.orderBy('file.id', 'ASC'); break; case '+name': query.orderBy('file.name', 'DESC'); break; case '-name': query.orderBy('file.name', 'ASC'); break; case '+size': query.orderBy('file.size', 'DESC'); break; diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 1cef76d3d2..50aea79943 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -66,8 +66,8 @@ export default class extends Endpoint { // eslint- switch (ps.sort) { case '+follower': query.orderBy('user.followersCount', 'DESC'); break; case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('user.id', 'DESC'); break; + case '-createdAt': query.orderBy('user.id', 'ASC'); break; case '+updatedAt': query.orderBy('user.updatedAt', 'DESC'); break; case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break; } diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 21c585f1ad..8dc5841314 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -74,8 +74,8 @@ export default class extends Endpoint { // eslint- switch (ps.sort) { case '+follower': query.orderBy('user.followersCount', 'DESC'); break; case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+createdAt': query.orderBy('user.id', 'DESC'); break; + case '-createdAt': query.orderBy('user.id', 'ASC'); break; case '+updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'DESC'); break; case '-updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'ASC'); break; default: query.orderBy('user.id', 'ASC'); break; -- cgit v1.2.3-freya From 5efd01ba70b37868f625e0acfc5c1c22d5df775c Mon Sep 17 00:00:00 2001 From: まっちゃてぃー <56515516+mattyatea@users.noreply.github.com> Date: Mon, 16 Oct 2023 20:11:27 +0900 Subject: feat: サーバーサイレンス機能を追加 (#12031) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat : サーバーサイレンスを追加 * Update CHANGELOG.md * Update CHANGELOG.md * Update locale * Update instance-info.vue * update misskey-js.api.md * lint fix * migration fix * 既存のものを使うように * fix * 色々直した * Update packages/frontend/src/pages/admin/instance-block.vue * Update packages/frontend/src/pages/admin/instance-block.vue * Update packages/frontend/src/components/MkInstanceCardMini.vue * Update packages/backend/src/core/entities/InstanceEntityService.ts * Update packages/backend/src/core/entities/InstanceEntityService.ts * Update packages/backend/src/core/entities/InstanceEntityService.ts * Update packages/backend/src/core/UserFollowingService.ts * Update packages/backend/src/core/UserFollowingService.ts * fix: サイレンスされてるサーバーからの投稿は全部ホームにする * fix: undefinedでfalseを返すようにした --------- Co-authored-by: syuilo --- CHANGELOG.md | 1 + locales/en-US.yml | 10 +++- locales/index.d.ts | 3 ++ locales/ja-JP.yml | 7 ++- .../migration/1697247230117-InstanceSilence.js | 16 +++++++ packages/backend/src/core/NoteCreateService.ts | 8 ++++ packages/backend/src/core/UserFollowingService.ts | 11 +++-- packages/backend/src/core/UtilityService.ts | 6 +++ .../src/core/entities/InstanceEntityService.ts | 4 +- packages/backend/src/models/Meta.ts | 5 ++ .../src/models/json-schema/federation-instance.ts | 5 ++ .../backend/src/server/api/endpoints/admin/meta.ts | 11 +++++ .../src/server/api/endpoints/admin/update-meta.ts | 56 +++++++++++++++------- .../server/api/endpoints/federation/instances.ts | 18 +++++++ .../frontend/src/components/MkInstanceCardMini.vue | 8 +++- packages/frontend/src/pages/about.federation.vue | 3 ++ .../frontend/src/pages/admin/instance-block.vue | 25 ++++++++-- packages/frontend/src/pages/instance-info.vue | 16 +++++-- packages/misskey-js/etc/misskey-js.api.md | 4 +- packages/misskey-js/src/entities.ts | 2 + 20 files changed, 184 insertions(+), 35 deletions(-) create mode 100644 packages/backend/migration/1697247230117-InstanceSilence.js (limited to 'packages/backend/src/server/api/endpoints/admin') diff --git a/CHANGELOG.md b/CHANGELOG.md index ee14d361f9..22593d4862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ - Enhance: トレンドハッシュタグ取得時のパフォーマンスを大幅に向上 - Enhance: WebSocket接続が多い場合のパフォーマンスを向上 - Enhance: 不要なPostgreSQLのインデックスを削除しパフォーマンスを向上 +- Feat: サーバーサイレンス機能が追加されました - Fix: 連合なしアンケートに投票をするとUpdateがリモートに配信されてしまうのを修正 - Fix: nodeinfoにおいてCORS用のヘッダーが設定されていないのを修正 - Fix: 同じ種類のTLのストリーミングを複数接続できない問題を修正 diff --git a/locales/en-US.yml b/locales/en-US.yml index 66825eaa7f..a2873181fe 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -195,6 +195,7 @@ perHour: "Per Hour" perDay: "Per Day" stopActivityDelivery: "Stop sending activities" blockThisInstance: "Block this instance" +silenceThisInstance: "Silence this instance" operations: "Operations" software: "Software" version: "Version" @@ -213,6 +214,13 @@ clearQueueConfirmText: "Any undelivered notes remaining in the queue will not be clearCachedFiles: "Clear cache" clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?" blockedInstances: "Blocked Instances" +silencedInstances: "Silenced Instances" +silencedInstancesDescription: "List the hostnames of the instances that you want to\ + \ silence. Accounts in the listed instances are treated as \"Silenced\", can only make follow requests, and cannot mention local accounts if not followed. This will not affect the blocked instances." +hiddenTags: "Hidden Hashtags" +hiddenTagsDescription: "List the hashtags (without the #) of the hashtags you wish\ + \ to hide from trending and explore. Hidden hashtags are still discoverable via\ + \ other means. Blocked instances are not affected even if listed here." blockedInstancesDescription: "List the hostnames of the instances that you want to block separated by linebreaks. Listed instances will no longer be able to communicate with this instance." muteAndBlock: "Mutes and Blocks" mutedUsers: "Muted users" @@ -794,7 +802,7 @@ active: "Active" offline: "Offline" notRecommended: "Not recommended" botProtection: "Bot Protection" -instanceBlocking: "Blocked Instances" +instanceBlocking: "Blocked/Silenced Instances" selectAccount: "Select account" switchAccount: "Switch account" enabled: "Enabled" diff --git a/locales/index.d.ts b/locales/index.d.ts index 2494c1709b..483c470be8 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -198,6 +198,7 @@ export interface Locale { "perDay": string; "stopActivityDelivery": string; "blockThisInstance": string; + "silenceThisInstance": string; "operations": string; "software": string; "version": string; @@ -217,6 +218,8 @@ export interface Locale { "clearCachedFilesConfirm": string; "blockedInstances": string; "blockedInstancesDescription": string; + "silencedInstances": string; + "silencedInstancesDescription": string; "muteAndBlock": string; "mutedUsers": string; "blockedUsers": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9adc4381a7..725d1e7a87 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -195,6 +195,7 @@ perHour: "1時間ごと" perDay: "1日ごと" stopActivityDelivery: "アクティビティの配送を停止" blockThisInstance: "このサーバーをブロック" +silenceThisInstance: "サーバーをサイレンス" operations: "操作" software: "ソフトウェア" version: "バージョン" @@ -213,7 +214,9 @@ clearQueueConfirmText: "未配達の投稿は配送されなくなります。 clearCachedFiles: "キャッシュをクリア" clearCachedFilesConfirm: "キャッシュされたリモートファイルをすべて削除しますか?" blockedInstances: "ブロックしたサーバー" -blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このサーバーとやり取りできなくなります。サブドメインもブロックされます。" +blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。" +silencedInstances: "サイレンスしたサーバー" +silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなります。ブロックしたインスタンスには影響しません。" muteAndBlock: "ミュートとブロック" mutedUsers: "ミュートしたユーザー" blockedUsers: "ブロックしたユーザー" @@ -794,7 +797,7 @@ active: "アクティブ" offline: "オフライン" notRecommended: "非推奨" botProtection: "Botプロテクション" -instanceBlocking: "サーバーブロック" +instanceBlocking: "サーバーブロック・サイレンス" selectAccount: "アカウントを選択" switchAccount: "アカウントを切り替え" enabled: "有効" diff --git a/packages/backend/migration/1697247230117-InstanceSilence.js b/packages/backend/migration/1697247230117-InstanceSilence.js new file mode 100644 index 0000000000..5fdbca3b27 --- /dev/null +++ b/packages/backend/migration/1697247230117-InstanceSilence.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class InstanceSilence1697247230117 { + name = 'InstanceSilence1697247230117' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "silencedHosts" character varying(1024) array NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "silencedHosts"`); + } +} diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 400f1ec98c..a308e1aaa8 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -56,6 +56,7 @@ import { SearchService } from '@/core/SearchService.js'; import { FeaturedService } from '@/core/FeaturedService.js'; import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { nyaize } from '@/misc/nyaize.js'; +import { UtilityService } from '@/core/UtilityService.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -215,6 +216,7 @@ export class NoteCreateService implements OnApplicationShutdown { private perUserNotesChart: PerUserNotesChart, private activeUsersChart: ActiveUsersChart, private instanceChart: InstanceChart, + private utilityService: UtilityService, ) { } @bindThis @@ -259,6 +261,12 @@ export class NoteCreateService implements OnApplicationShutdown { } } + const inSilencedInstance = this.utilityService.isSilencedHost((await this.metaService.fetch()).silencedHosts, user.host); + + if (data.visibility === 'public' && inSilencedInstance && user.host !== null) { + data.visibility = 'home'; + } + if (data.renote) { switch (data.renote.visibility) { case 'public': diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index f6d0c3a6d5..87484f0383 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common'; +import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { IsNull } from 'typeorm'; import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; @@ -28,6 +28,7 @@ import { MetaService } from '@/core/MetaService.js'; import { CacheService } from '@/core/CacheService.js'; import type { Config } from '@/config.js'; import { AccountMoveService } from '@/core/AccountMoveService.js'; +import { UtilityService } from '@/core/UtilityService.js'; import Logger from '../logger.js'; const logger = new Logger('following/create'); @@ -71,6 +72,7 @@ export class UserFollowingService implements OnModuleInit { private instancesRepository: InstancesRepository, private cacheService: CacheService, + private utilityService: UtilityService, private userEntityService: UserEntityService, private idService: IdService, private queueService: QueueService, @@ -118,15 +120,16 @@ export class UserFollowingService implements OnModuleInit { } const followeeProfile = await this.userProfilesRepository.findOneByOrFail({ userId: followee.id }); - // フォロー対象が鍵アカウントである or // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or - // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである + // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである or + // フォロワーがローカルユーザーであり、フォロー対象がサイレンスされているサーバーである // 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく if ( followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || - (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') + (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') || + (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower) && this.utilityService.isSilencedHost((await this.metaService.fetch()).silencedHosts, follower.host)) ) { let autoAccept = false; diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index d2d2776bd2..b95e41167b 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -35,6 +35,12 @@ export class UtilityService { return blockedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); } + @bindThis + public isSilencedHost(silencedHosts: string[] | undefined, host: string | null): boolean { + if (!silencedHosts || host == null) return false; + return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); + } + @bindThis public extractDbHost(uri: string): string { const url = new URL(uri); diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index 0e27e9df7f..9afe87eab7 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -3,9 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import type { Packed } from '@/misc/json-schema.js'; -import type { } from '@/models/Blocking.js'; import type { MiInstance } from '@/models/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { bindThis } from '@/decorators.js'; @@ -43,6 +42,7 @@ export class InstanceEntityService { description: instance.description, maintainerName: instance.maintainerName, maintainerEmail: instance.maintainerEmail, + isSilenced: this.utilityService.isSilencedHost(meta.silencedHosts, instance.host), iconUrl: instance.iconUrl, faviconUrl: instance.faviconUrl, themeColor: instance.themeColor, diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index d2bd0c26e9..23ae513ede 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -76,6 +76,11 @@ export class MiMeta { }) public sensitiveWords: string[]; + @Column('varchar', { + length: 1024, array: true, default: '{}', + }) + public silencedHosts: string[]; + @Column('varchar', { length: 1024, nullable: true, diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts index ac07519f16..4ad84d02ff 100644 --- a/packages/backend/src/models/json-schema/federation-instance.ts +++ b/packages/backend/src/models/json-schema/federation-instance.ts @@ -93,6 +93,11 @@ export const packedFederationInstanceSchema = { type: 'string', optional: false, nullable: true, }, + isSilenced: { + type: "boolean", + optional: false, + nullable: false, + }, infoUpdatedAt: { type: 'string', optional: false, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 5a74456ab0..f294934344 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -105,6 +105,16 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + silencedHosts: { + type: "array", + optional: true, + nullable: false, + items: { + type: "string", + optional: false, + nullable: false, + }, + }, pinnedUsers: { type: 'array', optional: false, nullable: false, @@ -367,6 +377,7 @@ export default class extends Endpoint { // eslint- pinnedUsers: instance.pinnedUsers, hiddenTags: instance.hiddenTags, blockedHosts: instance.blockedHosts, + silencedHosts: instance.silencedHosts, sensitiveWords: instance.sensitiveWords, preservedUsernames: instance.preservedUsernames, hcaptchaSecretKey: instance.hcaptchaSecretKey, 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 7db25e659f..72c4936c13 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -20,18 +20,26 @@ export const paramDef = { type: 'object', properties: { disableRegistration: { type: 'boolean', nullable: true }, - pinnedUsers: { type: 'array', nullable: true, items: { - type: 'string', - } }, - hiddenTags: { type: 'array', nullable: true, items: { - type: 'string', - } }, - blockedHosts: { type: 'array', nullable: true, items: { - type: 'string', - } }, - sensitiveWords: { type: 'array', nullable: true, items: { - type: 'string', - } }, + pinnedUsers: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, + hiddenTags: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, + blockedHosts: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, + sensitiveWords: { + type: 'array', nullable: true, items: { + type: 'string', + }, + }, themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' }, mascotImageUrl: { type: 'string', nullable: true }, bannerUrl: { type: 'string', nullable: true }, @@ -67,9 +75,11 @@ export const paramDef = { proxyAccountId: { type: 'string', format: 'misskey:id', nullable: true }, maintainerName: { type: 'string', nullable: true }, maintainerEmail: { type: 'string', nullable: true }, - langs: { type: 'array', items: { - type: 'string', - } }, + langs: { + type: 'array', items: { + type: 'string', + }, + }, summalyProxy: { type: 'string', nullable: true }, deeplAuthKey: { type: 'string', nullable: true }, deeplIsPro: { type: 'boolean' }, @@ -115,6 +125,13 @@ export const paramDef = { perUserHomeTimelineCacheMax: { type: 'integer' }, perUserListTimelineCacheMax: { type: 'integer' }, notesPerOneAd: { type: 'integer' }, + silencedHosts: { + type: 'array', + nullable: true, + items: { + type: 'string', + }, + }, }, required: [], } as const; @@ -147,7 +164,14 @@ export default class extends Endpoint { // eslint- if (Array.isArray(ps.sensitiveWords)) { set.sensitiveWords = ps.sensitiveWords.filter(Boolean); } - + if (Array.isArray(ps.silencedHosts)) { + let lastValue = ''; + set.silencedHosts = ps.silencedHosts.sort().filter((h) => { + const lv = lastValue; + lastValue = h; + return h !== '' && h !== lv && !set.blockedHosts?.includes(h); + }); + } if (ps.themeColor !== undefined) { set.themeColor = ps.themeColor; } diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index be73e5dbb8..c8beefa9c7 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -36,6 +36,7 @@ export const paramDef = { blocked: { type: 'boolean', nullable: true }, notResponding: { type: 'boolean', nullable: true }, suspended: { type: 'boolean', nullable: true }, + silenced: { type: "boolean", nullable: true }, federating: { type: 'boolean', nullable: true }, subscribing: { type: 'boolean', nullable: true }, publishing: { type: 'boolean', nullable: true }, @@ -102,6 +103,23 @@ export default class extends Endpoint { // eslint- } } + if (typeof ps.silenced === "boolean") { + const meta = await this.metaService.fetch(true); + + if (ps.silenced) { + if (meta.silencedHosts.length === 0) { + return []; + } + query.andWhere("instance.host IN (:...silences)", { + silences: meta.silencedHosts, + }); + } else if (meta.silencedHosts.length > 0) { + query.andWhere("instance.host NOT IN (:...silences)", { + silences: meta.silencedHosts, + }); + } + } + if (typeof ps.federating === 'boolean') { if (ps.federating) { query.andWhere('((instance.followingCount > 0) OR (instance.followersCount > 0))'); diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index de726e3aa4..e384b7a0bc 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only -->