Implement instance blocking (#4182)
* Implement instance blocking * Add missing text * Delete unnecessary file * Covert Punycode to Unicode
This commit is contained in:
parent
5a28632af7
commit
e6612f610c
7 changed files with 105 additions and 4 deletions
|
@ -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: "詳しく..."
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<ui-input :value="instance.latestRequestReceivedAt" type="text" readonly>
|
||||
<span>{{ $t('latest-request-received-at') }}</span>
|
||||
</ui-input>
|
||||
<ui-switch v-model="instance.isBlocked" @change="updateInstance()">{{ $t('block') }}</ui-switch>
|
||||
<section>
|
||||
<ui-button @click="removeAllFollowing()"><fa :icon="faMinusCircle"/> {{ $t('remove-all-following') }}</ui-button>
|
||||
<ui-info warn>{{ $t('remove-all-following-info', { host: instance.host }) }}</ui-info>
|
||||
|
@ -64,6 +65,11 @@
|
|||
<option value="-followers">{{ $t('sorts.followersAsc') }}</option>
|
||||
<option value="+followers">{{ $t('sorts.followersDesc') }}</option>
|
||||
</ui-select>
|
||||
<ui-select v-model="state">
|
||||
<span slot="label">{{ $t('state') }}</span>
|
||||
<option value="all">{{ $t('states.all') }}</option>
|
||||
<option value="blocked">{{ $t('states.blocked') }}</option>
|
||||
</ui-select>
|
||||
</ui-horizon-group>
|
||||
|
||||
<div class="instances">
|
||||
|
@ -84,6 +90,8 @@
|
|||
<span>{{ instance.latestStatus }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ui-info v-if="instances.length == limit">{{ $t('result-is-truncated', { n: limit }) }}</ui-info>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
|
@ -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,
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -57,4 +57,9 @@ export interface IInstance {
|
|||
* 直近のリクエスト受信日時
|
||||
*/
|
||||
latestRequestReceivedAt?: Date;
|
||||
|
||||
/**
|
||||
* このインスタンスをブロックしているか
|
||||
*/
|
||||
isBlocked: boolean;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
|||
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<void> => {
|
|||
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
|
||||
|
|
|
@ -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');
|
||||
|
|
34
src/server/api/endpoints/admin/federation/update-instance.ts
Normal file
34
src/server/api/endpoints/admin/federation/update-instance.ts
Normal file
|
@ -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();
|
||||
}));
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue