From 20ee57931f4ddbb29fcff93e4a7dc3b27a43eb9e Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 6 Feb 2019 15:24:59 +0900
Subject: [PATCH] Resolve #4165

---
 locales/ja-JP.yml                             |  5 +++
 src/client/app/admin/views/index.vue          |  9 +++-
 src/client/app/admin/views/queue.vue          | 43 +++++++++++++++++++
 src/queue/index.ts                            |  6 +++
 src/server/api/endpoints/admin/queue/clear.ts | 15 +++++++
 5 files changed, 76 insertions(+), 2 deletions(-)
 create mode 100644 src/client/app/admin/views/queue.vue
 create mode 100644 src/server/api/endpoints/admin/queue/clear.ts

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 43f6651b24..77bcad3ff5 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1129,6 +1129,7 @@ admin/views/index.vue:
   announcements: "お知らせ"
   hashtags: "ハッシュタグ"
   abuse: "スパム報告"
+  queue: "ジョブキュー"
   back-to-misskey: "Misskeyに戻る"
 
 admin/views/dashboard.vue:
@@ -1140,6 +1141,10 @@ admin/views/dashboard.vue:
   this-instance: "このインスタンス"
   federated: "連合"
 
+admin/views/queue.vue:
+  operation: "操作"
+  remove-all-jobs: "すべてのジョブをクリア"
+
 admin/views/abuse.vue:
   title: "スパム報告"
   target: "対象"
diff --git a/src/client/app/admin/views/index.vue b/src/client/app/admin/views/index.vue
index aa831e8bf6..9de1da0d09 100644
--- a/src/client/app/admin/views/index.vue
+++ b/src/client/app/admin/views/index.vue
@@ -20,6 +20,7 @@
 		<ul>
 			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
 			<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
+			<li @click="nav('queue')" :class="{ active: page == 'queue' }"><fa :icon="faTasks" fixed-width/>{{ $t('queue') }}</li>
 			<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
 			<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
 			<li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>{{ $t('@.drive') }}</li>
@@ -40,6 +41,7 @@
 		<div class="page">
 			<div v-if="page == 'dashboard'"><x-dashboard/></div>
 			<div v-if="page == 'instance'"><x-instance/></div>
+			<div v-if="page == 'queue'"><x-queue/></div>
 			<div v-if="page == 'moderators'"><x-moderators/></div>
 			<div v-if="page == 'users'"><x-users/></div>
 			<div v-if="page == 'emoji'"><x-emoji/></div>
@@ -58,6 +60,7 @@ import i18n from '../../i18n';
 import { version } from '../../config';
 import XDashboard from "./dashboard.vue";
 import XInstance from "./instance.vue";
+import XQueue from "./queue.vue";
 import XModerators from "./moderators.vue";
 import XEmoji from "./emoji.vue";
 import XAnnouncements from "./announcements.vue";
@@ -65,7 +68,7 @@ import XHashtags from "./hashtags.vue";
 import XUsers from "./users.vue";
 import XDrive from "./drive.vue";
 import XAbuse from "./abuse.vue";
-import { faHeadset, faArrowLeft, faShareAlt, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
+import { faHeadset, faArrowLeft, faShareAlt, faExclamationCircle, faTasks } from '@fortawesome/free-solid-svg-icons';
 import { faGrin } from '@fortawesome/free-regular-svg-icons';
 
 // Detect the user agent
@@ -77,6 +80,7 @@ export default Vue.extend({
 	components: {
 		XDashboard,
 		XInstance,
+		XQueue,
 		XModerators,
 		XEmoji,
 		XAnnouncements,
@@ -98,7 +102,8 @@ export default Vue.extend({
 			faArrowLeft,
 			faHeadset,
 			faShareAlt,
-			faExclamationCircle
+			faExclamationCircle,
+			faTasks
 		};
 	},
 	methods: {
diff --git a/src/client/app/admin/views/queue.vue b/src/client/app/admin/views/queue.vue
new file mode 100644
index 0000000000..e26b86e3ef
--- /dev/null
+++ b/src/client/app/admin/views/queue.vue
@@ -0,0 +1,43 @@
+<template>
+<div>
+	<ui-card>
+		<div slot="title">{{ $t('operation') }}</div>
+		<section>
+			<ui-button @click="removeAllJobs">{{ $t('remove-all-jobs') }}</ui-button>
+		</section>
+	</ui-card>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../i18n';
+
+export default Vue.extend({
+	i18n: i18n('admin/views/queue.vue'),
+
+	data() {
+		return {
+		};
+	},
+
+	methods: {
+		async removeAllJobs() {
+			const process = async () => {
+				await this.$root.api('admin/queue/clear');
+				this.$root.dialog({
+					type: 'success',
+					splash: true
+				});
+			};
+
+			await process().catch(e => {
+				this.$root.dialog({
+					type: 'error',
+					text: e.toString()
+				});
+			});
+		},
+	}
+});
+</script>
diff --git a/src/queue/index.ts b/src/queue/index.ts
index 8105999b60..da1c178a00 100644
--- a/src/queue/index.ts
+++ b/src/queue/index.ts
@@ -87,3 +87,9 @@ export default function() {
 
 	return queue;
 }
+
+export function destroy() {
+	queue.destroy().then(n => {
+		queueLogger.succ(`All job removed (${n} jobs)`);
+	});
+}
diff --git a/src/server/api/endpoints/admin/queue/clear.ts b/src/server/api/endpoints/admin/queue/clear.ts
new file mode 100644
index 0000000000..4da8d2c72d
--- /dev/null
+++ b/src/server/api/endpoints/admin/queue/clear.ts
@@ -0,0 +1,15 @@
+import define from '../../../define';
+import { destroy } from '../../../../../queue';
+
+export const meta = {
+	requireCredential: true,
+	requireModerator: true,
+
+	params: {}
+};
+
+export default define(meta, (ps) => new Promise(async (res, rej) => {
+	destroy();
+
+	res();
+}));