diff --git a/apps/client-server/src/app/post/post-manager.service.ts b/apps/client-server/src/app/post/post-manager.service.ts index 44a316b40..259c841bb 100644 --- a/apps/client-server/src/app/post/post-manager.service.ts +++ b/apps/client-server/src/app/post/post-manager.service.ts @@ -1,7 +1,7 @@ /* eslint-disable no-param-reassign */ import { Loaded, wrap } from '@mikro-orm/core'; import { InjectRepository } from '@mikro-orm/nestjs'; -import { BadRequestException, Injectable } from '@nestjs/common'; +import { Inject, Injectable, forwardRef } from '@nestjs/common'; import { Logger } from '@postybirb/logger'; import { EntityId, @@ -64,6 +64,7 @@ export class PostManagerService { private readonly postRepository: PostyBirbRepository, @InjectRepository(WebsitePostRecord) private readonly websitePostRecordRepository: PostyBirbRepository, + @Inject(forwardRef(() => PostService)) private readonly postService: PostService, private readonly websiteRegistry: WebsiteRegistryService, private readonly resizerService: PostFileResizerService, @@ -79,7 +80,7 @@ export class PostManagerService { if (!IsTestEnvironment()) { const nextToPost = await this.postService.getNext(); if (nextToPost && this.currentPost?.id !== nextToPost.id) { - this.logger.info(`Found next post to post: ${nextToPost.id}`); + this.logger.info(`Found next to post: ${nextToPost.id}`); this.startPost(nextToPost); } } @@ -115,7 +116,7 @@ export class PostManagerService { this.logger.withMetadata(entity.toJSON()).info(`Initializing post`); this.currentPost = entity; await this.postRepository.update(entity.id, { - state: PostRecordState.PROCESSING, + state: PostRecordState.RUNNING, }); // Ensure parent (submission) is loaded @@ -150,11 +151,6 @@ export class PostManagerService { } private async finishPost(entity: LoadedPostRecord) { - if (this.currentPost) { - this.currentPost = null; - this.cancelToken = null; - } - this.currentPost = null; this.cancelToken = null; @@ -612,7 +608,7 @@ export class PostManagerService { } if (result?.errors?.length) { - throw new BadRequestException('Submission contains validation errors'); + throw new Error('Submission contains validation errors'); } } } diff --git a/apps/client-server/src/app/post/post.service.ts b/apps/client-server/src/app/post/post.service.ts index f2efbc7e5..38c0187b9 100644 --- a/apps/client-server/src/app/post/post.service.ts +++ b/apps/client-server/src/app/post/post.service.ts @@ -1,7 +1,8 @@ import { InjectRepository } from '@mikro-orm/nestjs'; -import { Injectable, Optional } from '@nestjs/common'; +import { Inject, Injectable, Optional, forwardRef } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; import { SubmissionId } from '@postybirb/types'; +import { Cron as CronGenerator } from 'croner'; import { uniq } from 'lodash'; import { PostyBirbService } from '../common/service/postybirb-service'; import { PostRecord, Submission } from '../database/entities'; @@ -10,6 +11,7 @@ import { IsTestEnvironment } from '../utils/test.util'; import { WSGateway } from '../web-socket/web-socket-gateway'; import { WebsiteOptionsService } from '../website-options/website-options.service'; import { QueuePostRecordRequestDto } from './dtos/queue-post-record.dto'; +import { PostManagerService } from './post-manager.service'; /** * Handles enqueue and dequeue of post records. @@ -18,6 +20,8 @@ import { QueuePostRecordRequestDto } from './dtos/queue-post-record.dto'; @Injectable() export class PostService extends PostyBirbService { constructor( + @Inject(forwardRef(() => PostManagerService)) + private readonly postManagerService: PostManagerService, @InjectRepository(PostRecord) repository: PostyBirbRepository, @InjectRepository(Submission) @@ -46,6 +50,17 @@ export class PostService extends PostyBirbService { new Date(b.schedule.scheduledFor).getTime() ); // Sort by oldest first. this.enqueue({ ids: sorted.map((s) => s.id) }); + + sorted + .filter((s) => s.schedule.cron) + .forEach((s) => { + const next = CronGenerator(s.schedule.cron).nextRun()?.toISOString(); + if (next) { + // eslint-disable-next-line no-param-reassign + s.schedule.scheduledFor = next; + this.submissionRepository.persistAndFlush(s); + } + }); } } @@ -61,6 +76,7 @@ export class PostService extends PostyBirbService { }); // Filter out any already queued that are not in a completed state. + // It may be better to move completed to a separate table to avoid this check. const unqueued = uniq( request.ids.filter( (id) => !existing.some((e) => e.parent.id === id && !e.completedAt) @@ -81,6 +97,11 @@ export class PostService extends PostyBirbService { } } + if (created.length > 0) { + // Attempt to start the post manager if it is not already running. + this.postManagerService.startPost(await this.getNext()); + } + return created; } @@ -99,6 +120,8 @@ export class PostService extends PostyBirbService { const incomplete = existing.filter( (e: PostRecord) => e.completedAt !== undefined ); + + request.ids.forEach(this.postManagerService.cancelIfRunning); await this.repository.removeAndFlush(incomplete); } diff --git a/libs/types/src/enums/post-record-state.enum.ts b/libs/types/src/enums/post-record-state.enum.ts index 126725a67..f877e9736 100644 --- a/libs/types/src/enums/post-record-state.enum.ts +++ b/libs/types/src/enums/post-record-state.enum.ts @@ -4,7 +4,7 @@ */ export enum PostRecordState { PENDING = 'PENDING', - PROCESSING = 'PROCESSING', + RUNNING = 'RUNNING', DONE = 'DONE', FAILED = 'FAILED', }