runCleanup previously had no try/catch around each expired job, so one job's markCleaned/delete failure would abort the whole pass, leaving later expired jobs' files undeleted for that run. Each iteration is now wrapped in a try/catch that logs and continues; the returned count only reflects jobs that actually completed the delete+markCleaned sequence. markDone now guards outputPath/outputMimeType/outputSizeBytes/ conversionDurationSeconds with `?? null`, matching the guard createJob already has on `quality` — Prisma treats `undefined` in a data object as "leave the column alone" rather than binding NULL like the old raw SQL did. Currently unreachable in practice since the worker always passes real values, but keeps the repository defensive and consistent. Added a cleanup.test.js case that monkey-patches prisma.conversionJob.update to reject for one job's cleanedAt update, asserting a later expired job in the same batch still gets cleaned. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
93 lines
2.5 KiB
JavaScript
93 lines
2.5 KiB
JavaScript
const jobSelect = {
|
|
id: true,
|
|
uuid: true,
|
|
status: true,
|
|
family: true,
|
|
sourceFormat: true,
|
|
targetFormat: true,
|
|
originalFilename: true,
|
|
inputPath: true,
|
|
outputPath: true,
|
|
inputMimeType: true,
|
|
outputMimeType: true,
|
|
inputSizeBytes: true,
|
|
outputSizeBytes: true,
|
|
quality: true,
|
|
conversionDurationSeconds: true,
|
|
errorMessage: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
expiresAt: true,
|
|
cleanedAt: true,
|
|
};
|
|
|
|
export async function createJob(prisma, job) {
|
|
await prisma.conversionJob.create({
|
|
data: {
|
|
uuid: job.uuid,
|
|
family: job.family,
|
|
sourceFormat: job.sourceFormat,
|
|
targetFormat: job.targetFormat,
|
|
originalFilename: job.originalFilename,
|
|
inputPath: job.inputPath,
|
|
inputMimeType: job.inputMimeType,
|
|
inputSizeBytes: job.inputSizeBytes,
|
|
expiresAt: job.expiresAt,
|
|
quality: job.quality ?? null,
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function getJobByUuid(prisma, uuid) {
|
|
return prisma.conversionJob.findUnique({ where: { uuid }, select: jobSelect });
|
|
}
|
|
|
|
export async function getJobErrorLog(prisma, id) {
|
|
const job = await prisma.conversionJob.findUnique({ where: { id }, select: { errorLog: true } });
|
|
return job?.errorLog ?? null;
|
|
}
|
|
|
|
export async function markProcessing(prisma, id) {
|
|
await prisma.conversionJob.update({ where: { id }, data: { status: 'processing' } });
|
|
}
|
|
|
|
export async function markDone(prisma, id, { outputPath, outputMimeType, outputSizeBytes, conversionDurationSeconds }) {
|
|
await prisma.conversionJob.update({
|
|
where: { id },
|
|
data: {
|
|
status: 'done',
|
|
outputPath: outputPath ?? null,
|
|
outputMimeType: outputMimeType ?? null,
|
|
outputSizeBytes: outputSizeBytes ?? null,
|
|
conversionDurationSeconds: conversionDurationSeconds ?? null,
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function markFailed(prisma, id, { errorMessage, errorLog }) {
|
|
await prisma.conversionJob.update({
|
|
where: { id },
|
|
data: { status: 'failed', errorMessage, errorLog },
|
|
});
|
|
}
|
|
|
|
export async function findPendingJobs(prisma, limit) {
|
|
return prisma.conversionJob.findMany({
|
|
where: { status: 'pending' },
|
|
orderBy: { createdAt: 'asc' },
|
|
take: limit,
|
|
select: jobSelect,
|
|
});
|
|
}
|
|
|
|
export async function findExpiredJobs(prisma) {
|
|
return prisma.conversionJob.findMany({
|
|
where: { expiresAt: { lt: new Date() }, cleanedAt: null },
|
|
select: jobSelect,
|
|
});
|
|
}
|
|
|
|
export async function markCleaned(prisma, id) {
|
|
await prisma.conversionJob.update({ where: { id }, data: { cleanedAt: new Date() } });
|
|
}
|