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: "直近のリクエスト受信"
|
latest-request-received-at: "直近のリクエスト受信"
|
||||||
remove-all-following: "フォローを全解除"
|
remove-all-following: "フォローを全解除"
|
||||||
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
|
||||||
|
block: "ブロック"
|
||||||
lookup: "照会"
|
lookup: "照会"
|
||||||
instances: "インスタンス"
|
instances: "インスタンス"
|
||||||
instance-not-registered: "そのインスタンスは登録されていません"
|
instance-not-registered: "そのインスタンスは登録されていません"
|
||||||
|
@ -1398,6 +1399,11 @@ admin/views/federation.vue:
|
||||||
followingDesc: "フォローが多い順"
|
followingDesc: "フォローが多い順"
|
||||||
followersAsc: "フォロワーが少ない順"
|
followersAsc: "フォロワーが少ない順"
|
||||||
followersDesc: "フォロワーが多い順"
|
followersDesc: "フォロワーが多い順"
|
||||||
|
state: "状態"
|
||||||
|
states:
|
||||||
|
all: "すべて"
|
||||||
|
blocked: "ブロック"
|
||||||
|
result-is-truncated: "上位{n}件を表示しています。"
|
||||||
|
|
||||||
desktop/views/pages/welcome.vue:
|
desktop/views/pages/welcome.vue:
|
||||||
about: "詳しく..."
|
about: "詳しく..."
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
<ui-input :value="instance.latestRequestReceivedAt" type="text" readonly>
|
<ui-input :value="instance.latestRequestReceivedAt" type="text" readonly>
|
||||||
<span>{{ $t('latest-request-received-at') }}</span>
|
<span>{{ $t('latest-request-received-at') }}</span>
|
||||||
</ui-input>
|
</ui-input>
|
||||||
|
<ui-switch v-model="instance.isBlocked" @change="updateInstance()">{{ $t('block') }}</ui-switch>
|
||||||
<section>
|
<section>
|
||||||
<ui-button @click="removeAllFollowing()"><fa :icon="faMinusCircle"/> {{ $t('remove-all-following') }}</ui-button>
|
<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>
|
<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.followersAsc') }}</option>
|
||||||
<option value="+followers">{{ $t('sorts.followersDesc') }}</option>
|
<option value="+followers">{{ $t('sorts.followersDesc') }}</option>
|
||||||
</ui-select>
|
</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>
|
</ui-horizon-group>
|
||||||
|
|
||||||
<div class="instances">
|
<div class="instances">
|
||||||
|
@ -84,6 +90,8 @@
|
||||||
<span>{{ instance.latestStatus }}</span>
|
<span>{{ instance.latestStatus }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ui-info v-if="instances.length == limit">{{ $t('result-is-truncated', { n: limit }) }}</ui-info>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
</div>
|
</div>
|
||||||
|
@ -102,6 +110,7 @@ export default Vue.extend({
|
||||||
instance: null,
|
instance: null,
|
||||||
target: null,
|
target: null,
|
||||||
sort: '+caughtAt',
|
sort: '+caughtAt',
|
||||||
|
state: 'all',
|
||||||
limit: 50,
|
limit: 50,
|
||||||
instances: [],
|
instances: [],
|
||||||
faGlobe, faTerminal, faSearch, faMinusCircle
|
faGlobe, faTerminal, faSearch, faMinusCircle
|
||||||
|
@ -110,7 +119,10 @@ export default Vue.extend({
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
sort() {
|
sort() {
|
||||||
this.instances = [];
|
this.fetchInstances();
|
||||||
|
},
|
||||||
|
|
||||||
|
state() {
|
||||||
this.fetchInstances();
|
this.fetchInstances();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -137,9 +149,11 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchInstances() {
|
fetchInstances() {
|
||||||
|
this.instances = [];
|
||||||
this.$root.api('federation/instances', {
|
this.$root.api('federation/instances', {
|
||||||
|
state: this.state,
|
||||||
sort: this.sort,
|
sort: this.sort,
|
||||||
limit: 50
|
limit: this.limit
|
||||||
}).then(instances => {
|
}).then(instances => {
|
||||||
this.instances = instances;
|
this.instances = instances;
|
||||||
});
|
});
|
||||||
|
@ -154,7 +168,14 @@ export default Vue.extend({
|
||||||
splash: true
|
splash: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
updateInstance() {
|
||||||
|
this.$root.api('admin/federation/update-instance', {
|
||||||
|
host: this.instance.host,
|
||||||
|
isBlocked: this.instance.isBlocked,
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -57,4 +57,9 @@ export interface IInstance {
|
||||||
* 直近のリクエスト受信日時
|
* 直近のリクエスト受信日時
|
||||||
*/
|
*/
|
||||||
latestRequestReceivedAt?: Date;
|
latestRequestReceivedAt?: Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* このインスタンスをブロックしているか
|
||||||
|
*/
|
||||||
|
isBlocked: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
||||||
return;
|
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;
|
user = await User.findOne({ usernameLower: username, host: host.toLowerCase() }) as IRemoteUser;
|
||||||
} else {
|
} else {
|
||||||
// アクティビティ内のホストの検証
|
// アクティビティ内のホストの検証
|
||||||
|
@ -57,6 +66,15 @@ export default async (job: bq.Job, done: any): Promise<void> => {
|
||||||
return;
|
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({
|
user = await User.findOne({
|
||||||
host: { $ne: null },
|
host: { $ne: null },
|
||||||
'publicKey.id': signature.keyId
|
'publicKey.id': signature.keyId
|
||||||
|
|
|
@ -4,11 +4,13 @@ import { URL } from 'url';
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
import { lookup, IRunOptions } from 'lookup-dns-cache';
|
||||||
import * as promiseAny from 'promise-any';
|
import * as promiseAny from 'promise-any';
|
||||||
|
import { toUnicode } from 'punycode';
|
||||||
|
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import { ILocalUser } from '../../models/user';
|
import { ILocalUser } from '../../models/user';
|
||||||
import { publishApLogStream } from '../../services/stream';
|
import { publishApLogStream } from '../../services/stream';
|
||||||
import { apLogger } from './logger';
|
import { apLogger } from './logger';
|
||||||
|
import Instance from '../../models/instance';
|
||||||
|
|
||||||
export const logger = apLogger.createSubLogger('deliver');
|
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);
|
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 data = JSON.stringify(object);
|
||||||
|
|
||||||
const sha256 = crypto.createHash('sha256');
|
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,
|
requireCredential: false,
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
|
state: {
|
||||||
|
validator: $.str.optional,
|
||||||
|
},
|
||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
validator: $.num.optional.range(1, 100),
|
validator: $.num.optional.range(1, 100),
|
||||||
default: 30
|
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
|
const instances = await Instance
|
||||||
.find({}, {
|
.find(q, {
|
||||||
limit: ps.limit,
|
limit: ps.limit,
|
||||||
sort: sort,
|
sort: sort,
|
||||||
skip: ps.offset
|
skip: ps.offset
|
||||||
|
|
Loading…
Reference in a new issue