enhance: コントロールパネルでジョブキューをクリアできるように

This commit is contained in:
syuilo 2025-04-16 16:47:03 +09:00
parent f2aeafaddb
commit eda2f587a3
8 changed files with 71 additions and 17 deletions

View File

@ -7,6 +7,7 @@
- Feat: チャットウィジェットを追加
- Feat: デッキにチャットカラムを追加
- Enhance: Unicode絵文字をslugから入力する際に`:ok:`のように最後の`:`を入力したあとにUnicode絵文字に変換できるように
- Enhance: コントロールパネルでジョブキューをクリアできるように
- Enhance: テーマでページヘッダーの色を変更できるように
- Enhance: デザインのブラッシュアップ
- Fix: ログアウトした際に処理が終了しない問題を修正

View File

@ -38,6 +38,18 @@ import type {
import type httpSignature from '@peertube/http-signature';
import type * as Bull from 'bullmq';
export const QUEUE_TYPES = [
'system',
'endedPollNotification',
'deliver',
'inbox',
'db',
'relationship',
'objectStorage',
'userWebhookDeliver',
'systemWebhookDeliver',
] as const;
@Injectable()
export class QueueService {
constructor(
@ -529,15 +541,35 @@ export class QueueService {
}
@bindThis
public destroy() {
this.deliverQueue.once('cleaned', (jobs, status) => {
//deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`);
});
this.deliverQueue.clean(0, 0, 'delayed');
private getQueue(type: typeof QUEUE_TYPES[number]) {
switch (type) {
case 'system': return this.systemQueue;
case 'endedPollNotification': return this.endedPollNotificationQueue;
case 'deliver': return this.deliverQueue;
case 'inbox': return this.inboxQueue;
case 'db': return this.dbQueue;
case 'relationship': return this.relationshipQueue;
case 'objectStorage': return this.objectStorageQueue;
case 'userWebhookDeliver': return this.userWebhookDeliverQueue;
case 'systemWebhookDeliver': return this.systemWebhookDeliverQueue;
default: throw new Error(`Unrecognized queue type: ${type}`);
}
}
this.inboxQueue.once('cleaned', (jobs, status) => {
//inboxLogger.succ(`Cleaned ${jobs.length} ${status} jobs`);
});
this.inboxQueue.clean(0, 0, 'delayed');
@bindThis
public clearQueue(queueType: typeof QUEUE_TYPES[number], state: '*' | 'completed' | 'wait' | 'active' | 'paused' | 'prioritized' | 'delayed' | 'failed') {
const queue = this.getQueue(queueType);
if (state === '*') {
queue.clean(0, 0, 'completed');
queue.clean(0, 0, 'wait');
queue.clean(0, 0, 'active');
queue.clean(0, 0, 'paused');
queue.clean(0, 0, 'prioritized');
queue.clean(0, 0, 'delayed');
queue.clean(0, 0, 'failed');
} else {
queue.clean(0, 0, state);
}
}
}

View File

@ -6,7 +6,7 @@
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { QueueService } from '@/core/QueueService.js';
import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js';
export const meta = {
tags: ['admin'],
@ -18,8 +18,11 @@ export const meta = {
export const paramDef = {
type: 'object',
properties: {},
required: [],
properties: {
type: { type: 'string', enum: QUEUE_TYPES },
state: { type: 'string', enum: ['*', 'wait', 'delayed'] },
},
required: ['type', 'state'],
} as const;
@Injectable()
@ -29,7 +32,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queueService: QueueService,
) {
super(meta, paramDef, async (ps, me) => {
this.queueService.destroy();
this.queueService.clearQueue(ps.type, ps.state);
this.moderationLogService.log(me, 'clearQueue');
});

View File

@ -10,14 +10,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<XQueue v-if="tab === 'deliver'" domain="deliver"/>
<XQueue v-else-if="tab === 'inbox'" domain="inbox"/>
<br>
<MkButton @click="promoteAllQueues"><i class="ti ti-reload"></i> {{ i18n.ts.retryAllQueuesNow }}</MkButton>
<div class="_buttons">
<MkButton @click="promoteAllQueues"><i class="ti ti-reload"></i> {{ i18n.ts.retryAllQueuesNow }}</MkButton>
<MkButton danger @click="clear"><i class="ti ti-trash"></i> {{ i18n.ts.clearQueue }}</MkButton>
</div>
</MkSpacer>
</MkStickyContainer>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import * as config from '@@/js/config.js';
import XQueue from './queue.chart.vue';
import XHeader from './_header_.vue';
import type { Ref } from 'vue';
@ -38,7 +40,7 @@ function clear() {
}).then(({ canceled }) => {
if (canceled) return;
os.apiWithDialog('admin/queue/clear');
os.apiWithDialog('admin/queue/clear', { type: tab.value, state: '*' });
});
}

View File

@ -257,6 +257,9 @@ type AdminMetaResponse = operations['admin___meta']['responses']['200']['content
// @public (undocumented)
type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json'];
// @public (undocumented)
type AdminQueueClearRequest = operations['admin___queue___clear']['requestBody']['content']['application/json'];
// @public (undocumented)
type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json'];
@ -1525,6 +1528,7 @@ declare namespace entities {
AdminInviteListResponse,
AdminMetaResponse,
AdminPromoCreateRequest,
AdminQueueClearRequest,
AdminQueueDeliverDelayedResponse,
AdminQueueInboxDelayedResponse,
AdminQueuePromoteRequest,

View File

@ -75,6 +75,7 @@ import type {
AdminInviteListResponse,
AdminMetaResponse,
AdminPromoCreateRequest,
AdminQueueClearRequest,
AdminQueueDeliverDelayedResponse,
AdminQueueInboxDelayedResponse,
AdminQueuePromoteRequest,
@ -690,7 +691,7 @@ export type Endpoints = {
'admin/invite/list': { req: AdminInviteListRequest; res: AdminInviteListResponse };
'admin/meta': { req: EmptyRequest; res: AdminMetaResponse };
'admin/promo/create': { req: AdminPromoCreateRequest; res: EmptyResponse };
'admin/queue/clear': { req: EmptyRequest; res: EmptyResponse };
'admin/queue/clear': { req: AdminQueueClearRequest; res: EmptyResponse };
'admin/queue/deliver-delayed': { req: EmptyRequest; res: AdminQueueDeliverDelayedResponse };
'admin/queue/inbox-delayed': { req: EmptyRequest; res: AdminQueueInboxDelayedResponse };
'admin/queue/promote': { req: AdminQueuePromoteRequest; res: EmptyResponse };

View File

@ -78,6 +78,7 @@ export type AdminInviteListRequest = operations['admin___invite___list']['reques
export type AdminInviteListResponse = operations['admin___invite___list']['responses']['200']['content']['application/json'];
export type AdminMetaResponse = operations['admin___meta']['responses']['200']['content']['application/json'];
export type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json'];
export type AdminQueueClearRequest = operations['admin___queue___clear']['requestBody']['content']['application/json'];
export type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json'];
export type AdminQueueInboxDelayedResponse = operations['admin___queue___inbox-delayed']['responses']['200']['content']['application/json'];
export type AdminQueuePromoteRequest = operations['admin___queue___promote']['requestBody']['content']['application/json'];

View File

@ -8805,6 +8805,16 @@ export type operations = {
* **Credential required**: *Yes* / **Permission**: *write:admin:queue*
*/
admin___queue___clear: {
requestBody: {
content: {
'application/json': {
/** @enum {string} */
type: 'system' | 'endedPollNotification' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver';
/** @enum {string} */
state: '*' | 'wait' | 'delayed';
};
};
};
responses: {
/** @description OK (without any results) */
204: {