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

View File

@ -38,6 +38,18 @@ import type {
import type httpSignature from '@peertube/http-signature'; import type httpSignature from '@peertube/http-signature';
import type * as Bull from 'bullmq'; import type * as Bull from 'bullmq';
export const QUEUE_TYPES = [
'system',
'endedPollNotification',
'deliver',
'inbox',
'db',
'relationship',
'objectStorage',
'userWebhookDeliver',
'systemWebhookDeliver',
] as const;
@Injectable() @Injectable()
export class QueueService { export class QueueService {
constructor( constructor(
@ -529,15 +541,35 @@ export class QueueService {
} }
@bindThis @bindThis
public destroy() { private getQueue(type: typeof QUEUE_TYPES[number]) {
this.deliverQueue.once('cleaned', (jobs, status) => { switch (type) {
//deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); case 'system': return this.systemQueue;
}); case 'endedPollNotification': return this.endedPollNotificationQueue;
this.deliverQueue.clean(0, 0, 'delayed'); 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) => { @bindThis
//inboxLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); public clearQueue(queueType: typeof QUEUE_TYPES[number], state: '*' | 'completed' | 'wait' | 'active' | 'paused' | 'prioritized' | 'delayed' | 'failed') {
}); const queue = this.getQueue(queueType);
this.inboxQueue.clean(0, 0, 'delayed');
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 { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js';
import { QueueService } from '@/core/QueueService.js'; import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js';
export const meta = { export const meta = {
tags: ['admin'], tags: ['admin'],
@ -18,8 +18,11 @@ export const meta = {
export const paramDef = { export const paramDef = {
type: 'object', type: 'object',
properties: {}, properties: {
required: [], type: { type: 'string', enum: QUEUE_TYPES },
state: { type: 'string', enum: ['*', 'wait', 'delayed'] },
},
required: ['type', 'state'],
} as const; } as const;
@Injectable() @Injectable()
@ -29,7 +32,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queueService: QueueService, private queueService: QueueService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
this.queueService.destroy(); this.queueService.clearQueue(ps.type, ps.state);
this.moderationLogService.log(me, 'clearQueue'); 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-if="tab === 'deliver'" domain="deliver"/>
<XQueue v-else-if="tab === 'inbox'" domain="inbox"/> <XQueue v-else-if="tab === 'inbox'" domain="inbox"/>
<br> <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> </MkSpacer>
</MkStickyContainer> </MkStickyContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import * as config from '@@/js/config.js';
import XQueue from './queue.chart.vue'; import XQueue from './queue.chart.vue';
import XHeader from './_header_.vue'; import XHeader from './_header_.vue';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
@ -38,7 +40,7 @@ function clear() {
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) return; 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) // @public (undocumented)
type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json']; type AdminPromoCreateRequest = operations['admin___promo___create']['requestBody']['content']['application/json'];
// @public (undocumented)
type AdminQueueClearRequest = operations['admin___queue___clear']['requestBody']['content']['application/json'];
// @public (undocumented) // @public (undocumented)
type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json']; type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliver-delayed']['responses']['200']['content']['application/json'];
@ -1525,6 +1528,7 @@ declare namespace entities {
AdminInviteListResponse, AdminInviteListResponse,
AdminMetaResponse, AdminMetaResponse,
AdminPromoCreateRequest, AdminPromoCreateRequest,
AdminQueueClearRequest,
AdminQueueDeliverDelayedResponse, AdminQueueDeliverDelayedResponse,
AdminQueueInboxDelayedResponse, AdminQueueInboxDelayedResponse,
AdminQueuePromoteRequest, AdminQueuePromoteRequest,

View File

@ -75,6 +75,7 @@ import type {
AdminInviteListResponse, AdminInviteListResponse,
AdminMetaResponse, AdminMetaResponse,
AdminPromoCreateRequest, AdminPromoCreateRequest,
AdminQueueClearRequest,
AdminQueueDeliverDelayedResponse, AdminQueueDeliverDelayedResponse,
AdminQueueInboxDelayedResponse, AdminQueueInboxDelayedResponse,
AdminQueuePromoteRequest, AdminQueuePromoteRequest,
@ -690,7 +691,7 @@ export type Endpoints = {
'admin/invite/list': { req: AdminInviteListRequest; res: AdminInviteListResponse }; 'admin/invite/list': { req: AdminInviteListRequest; res: AdminInviteListResponse };
'admin/meta': { req: EmptyRequest; res: AdminMetaResponse }; 'admin/meta': { req: EmptyRequest; res: AdminMetaResponse };
'admin/promo/create': { req: AdminPromoCreateRequest; res: EmptyResponse }; '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/deliver-delayed': { req: EmptyRequest; res: AdminQueueDeliverDelayedResponse };
'admin/queue/inbox-delayed': { req: EmptyRequest; res: AdminQueueInboxDelayedResponse }; 'admin/queue/inbox-delayed': { req: EmptyRequest; res: AdminQueueInboxDelayedResponse };
'admin/queue/promote': { req: AdminQueuePromoteRequest; res: EmptyResponse }; '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 AdminInviteListResponse = operations['admin___invite___list']['responses']['200']['content']['application/json'];
export type AdminMetaResponse = operations['admin___meta']['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 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 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 AdminQueueInboxDelayedResponse = operations['admin___queue___inbox-delayed']['responses']['200']['content']['application/json'];
export type AdminQueuePromoteRequest = operations['admin___queue___promote']['requestBody']['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* * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
*/ */
admin___queue___clear: { 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: { responses: {
/** @description OK (without any results) */ /** @description OK (without any results) */
204: { 204: {