diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 57298f1f3..119fe791b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1383,6 +1383,7 @@ admin/views/federation.vue: latest-request-received-at: "直近のリクエスト受信" remove-all-following: "フォローを全解除" remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。" + block: "ブロック" lookup: "照会" instances: "インスタンス" instance-not-registered: "そのインスタンスは登録されていません" @@ -1398,6 +1399,11 @@ admin/views/federation.vue: followingDesc: "フォローが多い順" followersAsc: "フォロワーが少ない順" followersDesc: "フォロワーが多い順" + state: "状態" + states: + all: "すべて" + blocked: "ブロック" + result-is-truncated: "上位{n}件を表示しています。" desktop/views/pages/welcome.vue: about: "詳しく..." diff --git a/src/client/app/admin/views/federation.vue b/src/client/app/admin/views/federation.vue index 754a70f52..80b9e9541 100644 --- a/src/client/app/admin/views/federation.vue +++ b/src/client/app/admin/views/federation.vue @@ -39,6 +39,7 @@ {{ $t('latest-request-received-at') }} + {{ $t('block') }}
{{ $t('remove-all-following') }} {{ $t('remove-all-following-info', { host: instance.host }) }} @@ -64,6 +65,11 @@ + + {{ $t('state') }} + + +
@@ -84,6 +90,8 @@ {{ instance.latestStatus }}
+ + {{ $t('result-is-truncated', { n: limit }) }}
@@ -102,6 +110,7 @@ export default Vue.extend({ instance: null, target: null, sort: '+caughtAt', + state: 'all', limit: 50, instances: [], faGlobe, faTerminal, faSearch, faMinusCircle @@ -110,7 +119,10 @@ export default Vue.extend({ watch: { sort() { - this.instances = []; + this.fetchInstances(); + }, + + state() { this.fetchInstances(); }, }, @@ -137,9 +149,11 @@ export default Vue.extend({ }, fetchInstances() { + this.instances = []; this.$root.api('federation/instances', { + state: this.state, sort: this.sort, - limit: 50 + limit: this.limit }).then(instances => { this.instances = instances; }); @@ -154,7 +168,14 @@ export default Vue.extend({ splash: true }); }); - } + }, + + updateInstance() { + this.$root.api('admin/federation/update-instance', { + host: this.instance.host, + isBlocked: this.instance.isBlocked, + }); + }, } }); diff --git a/src/models/instance.ts b/src/models/instance.ts index 904ed95dc..242e80f30 100644 --- a/src/models/instance.ts +++ b/src/models/instance.ts @@ -57,4 +57,9 @@ export interface IInstance { * 直近のリクエスト受信日時 */ latestRequestReceivedAt?: Date; + + /** + * このインスタンスをブロックしているか + */ + isBlocked: boolean; } diff --git a/src/queue/processors/http/process-inbox.ts b/src/queue/processors/http/process-inbox.ts index d88f00a09..583e25513 100644 --- a/src/queue/processors/http/process-inbox.ts +++ b/src/queue/processors/http/process-inbox.ts @@ -45,6 +45,15 @@ export default async (job: bq.Job, done: any): Promise => { return; } + // ブロックしてたら中断 + // TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく + const instance = await Instance.findOne({ host: host.toLowerCase() }); + if (instance && instance.isBlocked) { + logger.warn(`Blocked request: ${host}`); + done(); + return; + } + user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser; } else { // アクティビティ内のホストの検証 @@ -57,6 +66,15 @@ export default async (job: bq.Job, done: any): Promise => { return; } + // ブロックしてたら中断 + // TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく + const instance = await Instance.findOne({ host: host.toLowerCase() }); + if (instance && instance.isBlocked) { + logger.warn(`Blocked request: ${host}`); + done(); + return; + } + user = await User.findOne({ host: { $ne: null }, 'publicKey.id': signature.keyId diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index 7264bef24..df8eced13 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -4,11 +4,13 @@ import { URL } from 'url'; import * as crypto from 'crypto'; import { lookup, IRunOptions } from 'lookup-dns-cache'; import * as promiseAny from 'promise-any'; +import { toUnicode } from 'punycode'; import config from '../../config'; import { ILocalUser } from '../../models/user'; import { publishApLogStream } from '../../services/stream'; import { apLogger } from './logger'; +import Instance from '../../models/instance'; export const logger = apLogger.createSubLogger('deliver'); @@ -19,6 +21,11 @@ export default (user: ILocalUser, url: string, object: any) => new Promise(async const { protocol, host, hostname, port, pathname, search } = new URL(url); + // ブロックしてたら中断 + // TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく + const instance = await Instance.findOne({ host: toUnicode(host) }); + if (instance && instance.isBlocked) return; + const data = JSON.stringify(object); const sha256 = crypto.createHash('sha256'); diff --git a/src/server/api/endpoints/admin/federation/update-instance.ts b/src/server/api/endpoints/admin/federation/update-instance.ts new file mode 100644 index 000000000..de40480a4 --- /dev/null +++ b/src/server/api/endpoints/admin/federation/update-instance.ts @@ -0,0 +1,34 @@ +import $ from 'cafy'; +import define from '../../../define'; +import Instance from '../../../../../models/instance'; + +export const meta = { + requireCredential: true, + requireModerator: true, + + params: { + host: { + validator: $.str + }, + + isBlocked: { + validator: $.bool + }, + } +}; + +export default define(meta, (ps, me) => new Promise(async (res, rej) => { + const instance = await Instance.findOne({ host: ps.host }); + + if (instance == null) { + return rej('instance not found'); + } + + Instance.update({ host: ps.host }, { + $set: { + isBlocked: ps.isBlocked + } + }); + + res(); +})); diff --git a/src/server/api/endpoints/federation/instances.ts b/src/server/api/endpoints/federation/instances.ts index 723cbe8fd..ce0d10af2 100644 --- a/src/server/api/endpoints/federation/instances.ts +++ b/src/server/api/endpoints/federation/instances.ts @@ -6,6 +6,10 @@ export const meta = { requireCredential: false, params: { + state: { + validator: $.str.optional, + }, + limit: { validator: $.num.optional.range(1, 100), default: 30 @@ -73,8 +77,14 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }; } + const q = {} as any; + + if (ps.state === 'blocked') { + q.isBlocked = true; + } + const instances = await Instance - .find({}, { + .find(q, { limit: ps.limit, sort: sort, skip: ps.offset