forked from mirrors/misskey
feat: 管理画面のジョブキューページにresume/pauseボタンを用意 (#17436)
* feat: 管理画面のジョブキューページにresume/pauseボタンを用意 * fix review
This commit is contained in:
parent
43534d6213
commit
9f2e806c20
12 changed files with 303 additions and 3 deletions
|
|
@ -1,7 +1,7 @@
|
|||
## Unreleased
|
||||
|
||||
### General
|
||||
-
|
||||
- Feat: ジョブキュー管理画面からキューの一時停止/再開ができるように
|
||||
|
||||
### Client
|
||||
-
|
||||
|
|
|
|||
|
|
@ -769,6 +769,18 @@ export class QueueService {
|
|||
await queue.promoteJobs();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async queuePause(queueType: typeof QUEUE_TYPES[number]) {
|
||||
const queue = this.getQueue(queueType);
|
||||
await queue.pause();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async queueResume(queueType: typeof QUEUE_TYPES[number]) {
|
||||
const queue = this.getQueue(queueType);
|
||||
await queue.resume();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async queueRetryJob(queueType: typeof QUEUE_TYPES[number], jobId: string) {
|
||||
const queue = this.getQueue(queueType);
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ export * as 'admin/queue/remove-job' from './endpoints/admin/queue/remove-job.js
|
|||
export * as 'admin/queue/show-job' from './endpoints/admin/queue/show-job.js';
|
||||
export * as 'admin/queue/show-job-logs' from './endpoints/admin/queue/show-job-logs.js';
|
||||
export * as 'admin/queue/promote-jobs' from './endpoints/admin/queue/promote-jobs.js';
|
||||
export * as 'admin/queue/pause' from './endpoints/admin/queue/pause.js';
|
||||
export * as 'admin/queue/resume' from './endpoints/admin/queue/resume.js';
|
||||
export * as 'admin/queue/jobs' from './endpoints/admin/queue/jobs.js';
|
||||
export * as 'admin/queue/stats' from './endpoints/admin/queue/stats.js';
|
||||
export * as 'admin/queue/queues' from './endpoints/admin/queue/queues.js';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
kind: 'write:admin:queue',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
queue: { type: 'string', enum: QUEUE_TYPES },
|
||||
},
|
||||
required: ['queue'],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
private moderationLogService: ModerationLogService,
|
||||
private queueService: QueueService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
await this.queueService.queuePause(ps.queue);
|
||||
|
||||
this.moderationLogService.log(me, 'pauseQueue');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
kind: 'write:admin:queue',
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
queue: { type: 'string', enum: QUEUE_TYPES },
|
||||
},
|
||||
required: ['queue'],
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||
constructor(
|
||||
private moderationLogService: ModerationLogService,
|
||||
private queueService: QueueService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
await this.queueService.queueResume(ps.queue);
|
||||
|
||||
this.moderationLogService.log(me, 'resumeQueue');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -94,6 +94,8 @@ export const moderationLogTypes = [
|
|||
'deleteRole',
|
||||
'clearQueue',
|
||||
'promoteQueue',
|
||||
'pauseQueue',
|
||||
'resumeQueue',
|
||||
'deleteDriveFile',
|
||||
'deleteNote',
|
||||
'createGlobalAnnouncement',
|
||||
|
|
@ -199,6 +201,8 @@ export type ModerationLogPayloads = {
|
|||
};
|
||||
clearQueue: Record<string, never>;
|
||||
promoteQueue: Record<string, never>;
|
||||
pauseQueue: Record<string, never>;
|
||||
resumeQueue: Record<string, never>;
|
||||
deleteDriveFile: {
|
||||
fileId: string;
|
||||
fileUserId: string | null;
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div class="_buttons">
|
||||
<MkButton rounded @click="promoteAllJobs"><i class="ti ti-player-track-next"></i> Promote all jobs</MkButton>
|
||||
<!-- <MkButton rounded @click="createJob"><i class="ti ti-plus"></i> Add job</MkButton> -->
|
||||
<!-- <MkButton v-if="queueInfo.isPaused" rounded @click="resumeQueue"><i class="ti ti-player-play"></i> Resume queue</MkButton> -->
|
||||
<!-- <MkButton v-else rounded danger @click="pauseQueue"><i class="ti ti-player-pause"></i> Pause queue</MkButton> -->
|
||||
<MkButton v-if="queueInfo.isPaused" rounded @click="resumeQueue"><i class="ti ti-player-play"></i> Resume queue</MkButton>
|
||||
<MkButton v-else rounded danger @click="pauseQueue"><i class="ti ti-player-pause"></i> Pause queue</MkButton>
|
||||
<MkButton rounded danger @click="clearQueue"><i class="ti ti-trash"></i> Empty queue</MkButton>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -292,6 +292,30 @@ async function promoteAllJobs() {
|
|||
fetchJobs();
|
||||
}
|
||||
|
||||
async function pauseQueue() {
|
||||
if (tab.value === '-') return;
|
||||
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
title: i18n.ts.areYouSure,
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
await os.apiWithDialog('admin/queue/pause', { queue: tab.value });
|
||||
|
||||
fetchCurrentQueue();
|
||||
fetchJobs();
|
||||
}
|
||||
|
||||
async function resumeQueue() {
|
||||
if (tab.value === '-') return;
|
||||
|
||||
await os.apiWithDialog('admin/queue/resume', { queue: tab.value });
|
||||
|
||||
fetchCurrentQueue();
|
||||
fetchJobs();
|
||||
}
|
||||
|
||||
async function removeJobs() {
|
||||
if (tab.value === '-' || jobState.value === 'latest') return;
|
||||
|
||||
|
|
|
|||
|
|
@ -280,6 +280,9 @@ type AdminQueueJobsRequest = operations['admin___queue___jobs']['requestBody']['
|
|||
// @public (undocumented)
|
||||
type AdminQueueJobsResponse = operations['admin___queue___jobs']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminQueuePauseRequest = operations['admin___queue___pause']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminQueuePromoteJobsRequest = operations['admin___queue___promote-jobs']['requestBody']['content']['application/json'];
|
||||
|
||||
|
|
@ -295,6 +298,9 @@ type AdminQueueQueueStatsResponse = operations['admin___queue___queue-stats']['r
|
|||
// @public (undocumented)
|
||||
type AdminQueueRemoveJobRequest = operations['admin___queue___remove-job']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminQueueResumeRequest = operations['admin___queue___resume']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminQueueRetryJobRequest = operations['admin___queue___retry-job']['requestBody']['content']['application/json'];
|
||||
|
||||
|
|
@ -1604,11 +1610,13 @@ declare namespace entities {
|
|||
AdminQueueInboxDelayedResponse,
|
||||
AdminQueueJobsRequest,
|
||||
AdminQueueJobsResponse,
|
||||
AdminQueuePauseRequest,
|
||||
AdminQueuePromoteJobsRequest,
|
||||
AdminQueueQueueStatsRequest,
|
||||
AdminQueueQueueStatsResponse,
|
||||
AdminQueueQueuesResponse,
|
||||
AdminQueueRemoveJobRequest,
|
||||
AdminQueueResumeRequest,
|
||||
AdminQueueRetryJobRequest,
|
||||
AdminQueueShowJobRequest,
|
||||
AdminQueueShowJobResponse,
|
||||
|
|
|
|||
|
|
@ -647,6 +647,17 @@ declare module '../api.js' {
|
|||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:queue*
|
||||
*/
|
||||
request<E extends 'admin/queue/pause', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
|
|
@ -691,6 +702,17 @@ declare module '../api.js' {
|
|||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:queue*
|
||||
*/
|
||||
request<E extends 'admin/queue/resume', P extends Endpoints[E]['req']>(
|
||||
endpoint: E,
|
||||
params: P,
|
||||
credential?: string | null,
|
||||
): Promise<SwitchCaseResponseType<E, P>>;
|
||||
|
||||
/**
|
||||
* No description provided.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -80,11 +80,13 @@ import type {
|
|||
AdminQueueInboxDelayedResponse,
|
||||
AdminQueueJobsRequest,
|
||||
AdminQueueJobsResponse,
|
||||
AdminQueuePauseRequest,
|
||||
AdminQueuePromoteJobsRequest,
|
||||
AdminQueueQueueStatsRequest,
|
||||
AdminQueueQueueStatsResponse,
|
||||
AdminQueueQueuesResponse,
|
||||
AdminQueueRemoveJobRequest,
|
||||
AdminQueueResumeRequest,
|
||||
AdminQueueRetryJobRequest,
|
||||
AdminQueueShowJobRequest,
|
||||
AdminQueueShowJobResponse,
|
||||
|
|
@ -722,10 +724,12 @@ export type Endpoints = {
|
|||
'admin/queue/deliver-delayed': { req: EmptyRequest; res: AdminQueueDeliverDelayedResponse };
|
||||
'admin/queue/inbox-delayed': { req: EmptyRequest; res: AdminQueueInboxDelayedResponse };
|
||||
'admin/queue/jobs': { req: AdminQueueJobsRequest; res: AdminQueueJobsResponse };
|
||||
'admin/queue/pause': { req: AdminQueuePauseRequest; res: EmptyResponse };
|
||||
'admin/queue/promote-jobs': { req: AdminQueuePromoteJobsRequest; res: EmptyResponse };
|
||||
'admin/queue/queue-stats': { req: AdminQueueQueueStatsRequest; res: AdminQueueQueueStatsResponse };
|
||||
'admin/queue/queues': { req: EmptyRequest; res: AdminQueueQueuesResponse };
|
||||
'admin/queue/remove-job': { req: AdminQueueRemoveJobRequest; res: EmptyResponse };
|
||||
'admin/queue/resume': { req: AdminQueueResumeRequest; res: EmptyResponse };
|
||||
'admin/queue/retry-job': { req: AdminQueueRetryJobRequest; res: EmptyResponse };
|
||||
'admin/queue/show-job': { req: AdminQueueShowJobRequest; res: AdminQueueShowJobResponse };
|
||||
'admin/queue/show-job-logs': { req: AdminQueueShowJobLogsRequest; res: AdminQueueShowJobLogsResponse };
|
||||
|
|
|
|||
|
|
@ -83,11 +83,13 @@ export type AdminQueueDeliverDelayedResponse = operations['admin___queue___deliv
|
|||
export type AdminQueueInboxDelayedResponse = operations['admin___queue___inbox-delayed']['responses']['200']['content']['application/json'];
|
||||
export type AdminQueueJobsRequest = operations['admin___queue___jobs']['requestBody']['content']['application/json'];
|
||||
export type AdminQueueJobsResponse = operations['admin___queue___jobs']['responses']['200']['content']['application/json'];
|
||||
export type AdminQueuePauseRequest = operations['admin___queue___pause']['requestBody']['content']['application/json'];
|
||||
export type AdminQueuePromoteJobsRequest = operations['admin___queue___promote-jobs']['requestBody']['content']['application/json'];
|
||||
export type AdminQueueQueueStatsRequest = operations['admin___queue___queue-stats']['requestBody']['content']['application/json'];
|
||||
export type AdminQueueQueueStatsResponse = operations['admin___queue___queue-stats']['responses']['200']['content']['application/json'];
|
||||
export type AdminQueueQueuesResponse = operations['admin___queue___queues']['responses']['200']['content']['application/json'];
|
||||
export type AdminQueueRemoveJobRequest = operations['admin___queue___remove-job']['requestBody']['content']['application/json'];
|
||||
export type AdminQueueResumeRequest = operations['admin___queue___resume']['requestBody']['content']['application/json'];
|
||||
export type AdminQueueRetryJobRequest = operations['admin___queue___retry-job']['requestBody']['content']['application/json'];
|
||||
export type AdminQueueShowJobRequest = operations['admin___queue___show-job']['requestBody']['content']['application/json'];
|
||||
export type AdminQueueShowJobResponse = operations['admin___queue___show-job']['responses']['200']['content']['application/json'];
|
||||
|
|
|
|||
|
|
@ -530,6 +530,15 @@ export type paths = {
|
|||
*/
|
||||
post: operations['admin___queue___jobs'];
|
||||
};
|
||||
'/admin/queue/pause': {
|
||||
/**
|
||||
* admin/queue/pause
|
||||
* @description No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:queue*
|
||||
*/
|
||||
post: operations['admin___queue___pause'];
|
||||
};
|
||||
'/admin/queue/promote-jobs': {
|
||||
/**
|
||||
* admin/queue/promote-jobs
|
||||
|
|
@ -566,6 +575,15 @@ export type paths = {
|
|||
*/
|
||||
post: operations['admin___queue___remove-job'];
|
||||
};
|
||||
'/admin/queue/resume': {
|
||||
/**
|
||||
* admin/queue/resume
|
||||
* @description No description provided.
|
||||
*
|
||||
* **Credential required**: *Yes* / **Permission**: *write:admin:queue*
|
||||
*/
|
||||
post: operations['admin___queue___resume'];
|
||||
};
|
||||
'/admin/queue/retry-job': {
|
||||
/**
|
||||
* admin/queue/retry-job
|
||||
|
|
@ -9903,6 +9921,69 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
admin___queue___pause: {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
/** @enum {string} */
|
||||
queue: 'system' | 'endedPollNotification' | 'postScheduledNote' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver';
|
||||
};
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description OK (without any results) */
|
||||
204: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
};
|
||||
/** @description Client error */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Authentication error */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden error */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description I'm Ai */
|
||||
418: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Internal server error */
|
||||
500: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
'admin___queue___promote-jobs': {
|
||||
requestBody: {
|
||||
content: {
|
||||
|
|
@ -10197,6 +10278,69 @@ export interface operations {
|
|||
};
|
||||
};
|
||||
};
|
||||
admin___queue___resume: {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
/** @enum {string} */
|
||||
queue: 'system' | 'endedPollNotification' | 'postScheduledNote' | 'deliver' | 'inbox' | 'db' | 'relationship' | 'objectStorage' | 'userWebhookDeliver' | 'systemWebhookDeliver';
|
||||
};
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description OK (without any results) */
|
||||
204: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
};
|
||||
/** @description Client error */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Authentication error */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Forbidden error */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description I'm Ai */
|
||||
418: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
/** @description Internal server error */
|
||||
500: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
'application/json': components['schemas']['Error'];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
'admin___queue___retry-job': {
|
||||
requestBody: {
|
||||
content: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue