feat: create job with data dto

This commit is contained in:
izzy
2026-01-12 12:32:55 +00:00
parent d4ad523eb3
commit b49ee6d90d
11 changed files with 323 additions and 3 deletions
+15 -1
View File
@@ -1,9 +1,10 @@
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Put, Query } from '@nestjs/common';
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Endpoint, HistoryBuilder } from 'src/decorators';
import { AuthDto } from 'src/dtos/auth.dto';
import {
QueueDeleteDto,
QueueJobCreateDto,
QueueJobResponseDto,
QueueJobSearchDto,
QueueNameParamDto,
@@ -19,6 +20,19 @@ import { QueueService } from 'src/services/queue.service';
export class QueueController {
constructor(private service: QueueService) {}
@Post('job')
@Authenticated({ permission: Permission.JobCreate, admin: true })
@HttpCode(HttpStatus.NO_CONTENT)
@Endpoint({
summary: 'Create a manual job',
description:
'Run a specific job. Most jobs are queued automatically, but this endpoint allows for manual creation of a handful of jobs, including various cleanup tasks, as well as creating a new database backup.',
history: new HistoryBuilder().added('v2.5.0').alpha('v2.5.0'),
})
queueJob(@Body() dto: QueueJobCreateDto): Promise<void> {
return this.service.createJob(dto);
}
@Get()
@Authenticated({ permission: Permission.QueueRead, admin: true })
@Endpoint({
+69 -1
View File
@@ -1,7 +1,75 @@
import { ApiProperty } from '@nestjs/swagger';
import { ClassConstructor, Transform, Type } from 'class-transformer';
import { Equals, IsBoolean, IsDefined, IsOptional, ValidateNested } from 'class-validator';
import { HistoryBuilder, Property } from 'src/decorators';
import { JobName, QueueCommand, QueueJobStatus, QueueName } from 'src/enum';
import { ValidateBoolean, ValidateEnum } from 'src/validation';
import { transformToOneOf, ValidateBoolean, ValidateEnum } from 'src/validation';
class BaseJobData {
@IsOptional()
@IsBoolean()
force?: boolean;
}
class BaseJob {
@ValidateNested()
@Type(() => BaseJobData)
data!: BaseJobData;
}
class JobTagCleanup extends BaseJob {
@Equals(JobName.TagCleanup)
name!: JobName.TagCleanup;
}
class JobPersonCleanup extends BaseJob {
@Equals(JobName.PersonCleanup)
name!: JobName.PersonCleanup;
}
class JobUserDeleteCheck extends BaseJob {
@Equals(JobName.UserDeleteCheck)
name!: JobName.UserDeleteCheck;
}
class JobMemoryCleanup extends BaseJob {
@Equals(JobName.MemoryCleanup)
name!: JobName.MemoryCleanup;
}
class JobMemoryGenerate extends BaseJob {
@Equals(JobName.MemoryGenerate)
name!: JobName.MemoryGenerate;
}
class JobDatabaseBackup extends BaseJob {
@Equals(JobName.DatabaseBackup)
name!: JobName.DatabaseBackup;
}
const JOB_MAP: Record<string, ClassConstructor<object>> = {
[JobName.TagCleanup]: JobTagCleanup,
[JobName.PersonCleanup]: JobPersonCleanup,
[JobName.UserDeleteCheck]: JobUserDeleteCheck,
[JobName.MemoryCleanup]: JobMemoryCleanup,
[JobName.MemoryGenerate]: JobMemoryGenerate,
[JobName.DatabaseBackup]: JobDatabaseBackup,
};
export class QueueJobCreateDto {
@ValidateNested()
@Transform(transformToOneOf(JOB_MAP))
@IsDefined({
message: `job.name must be one of ${Object.keys(JOB_MAP)}`,
})
job!:
| JobTagCleanup
| JobPersonCleanup
| JobUserDeleteCheck
| JobMemoryCleanup
| JobMemoryGenerate
| JobDatabaseBackup;
}
export class QueueNameParamDto {
@ValidateEnum({ enum: QueueName, name: 'QueueName' })
+5
View File
@@ -12,6 +12,7 @@ import {
import {
QueueCommandDto,
QueueDeleteDto,
QueueJobCreateDto,
QueueJobResponseDto,
QueueJobSearchDto,
QueueResponseDto,
@@ -100,6 +101,10 @@ export class QueueService extends BaseService {
this.services = services;
}
createJob(dto: QueueJobCreateDto): Promise<void> {
return this.jobRepository.queue(dto.job);
}
async runCommandLegacy(name: QueueName, dto: QueueCommandDto): Promise<QueueResponseLegacyDto> {
this.logger.debug(`Handling command: queue=${name},command=${dto.command},force=${dto.force}`);
+6 -1
View File
@@ -7,7 +7,7 @@ import {
applyDecorators,
} from '@nestjs/common';
import { ApiProperty, ApiPropertyOptions } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { ClassConstructor, Transform, TransformFnParams, plainToInstance } from 'class-transformer';
import {
IsArray,
IsBoolean,
@@ -417,3 +417,8 @@ export function IsIPRange(options: IsIPRangeOptions, validationOptions?: Validat
validationOptions,
);
}
export const transformToOneOf =
<T extends Record<string, ClassConstructor<object>>>(map: T) =>
({ value }: TransformFnParams) =>
value && typeof value === 'object' && map[value.name] ? plainToInstance(map[value.name], value) : null;