diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 27fc640a8c..57298f1f35 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1381,6 +1381,8 @@ admin/views/federation.vue:
status: "ステータス"
latest-request-sent-at: "直近のリクエスト送信"
latest-request-received-at: "直近のリクエスト受信"
+ remove-all-following: "フォローを全解除"
+ remove-all-following-info: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。"
lookup: "照会"
instances: "インスタンス"
instance-not-registered: "そのインスタンスは登録されていません"
diff --git a/src/client/app/admin/views/federation.vue b/src/client/app/admin/views/federation.vue
index 2e403829a0..8e55722a2f 100644
--- a/src/client/app/admin/views/federation.vue
+++ b/src/client/app/admin/views/federation.vue
@@ -39,6 +39,10 @@
{{ $t('latest-request-received-at') }}
+
+ {{ $t('remove-all-following') }}
+ {{ $t('remove-all-following-info', { host: instance.host }) }}
+
@@ -138,6 +142,17 @@ export default Vue.extend({
}).then(instances => {
this.instances = instances;
});
+ },
+
+ removeAllFollowing() {
+ this.$root.api('admin/federation/remove-all-following', {
+ host: this.instance.host
+ }).then(() => {
+ this.$root.dialog({
+ type: 'success',
+ splash: true
+ });
+ });
}
}
});
diff --git a/src/server/api/endpoints/admin/federation/remove-all-following.ts b/src/server/api/endpoints/admin/federation/remove-all-following.ts
new file mode 100644
index 0000000000..1021965b1e
--- /dev/null
+++ b/src/server/api/endpoints/admin/federation/remove-all-following.ts
@@ -0,0 +1,41 @@
+import $ from 'cafy';
+import define from '../../../define';
+import Instance from '../../../../../models/instance';
+import Following from '../../../../../models/following';
+import User from '../../../../../models/user';
+import deleteFollowing from '../../../../../services/following/delete';
+
+export const meta = {
+ requireCredential: true,
+ requireModerator: true,
+
+ params: {
+ host: {
+ validator: $.str
+ }
+ }
+};
+
+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');
+ }
+
+ const followings = await Following.find({
+ '_follower.host': { $ne: null }
+ });
+
+ const pairs = await Promise.all(followings.map(f => Promise.all([
+ User.findOne({ _id: f.followerId }),
+ User.findOne({ _id: f.followeeId })
+ ])));
+
+ for (const pair of pairs) {
+ deleteFollowing(pair[0], pair[1]);
+ }
+
+ res();
+}));