feat: add conversion_jobs repository
Also configures the MariaDB pool with timezone: 'auto', since the default 'local' mode sends dates without timezone conversion and silently broke expires_at comparisons whenever the app host and DB server clocks differ (caught by the findExpiredJobs test). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
function toCamelJob(row) {
|
||||
if (!row) return null;
|
||||
return {
|
||||
id: row.id,
|
||||
status: row.status,
|
||||
family: row.family,
|
||||
sourceFormat: row.source_format,
|
||||
targetFormat: row.target_format,
|
||||
originalFilename: row.original_filename,
|
||||
inputPath: row.input_path,
|
||||
outputPath: row.output_path,
|
||||
inputMimeType: row.input_mime_type,
|
||||
outputMimeType: row.output_mime_type,
|
||||
errorMessage: row.error_message,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
expiresAt: row.expires_at,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createJob(pool, job) {
|
||||
await pool.query(
|
||||
`INSERT INTO conversion_jobs
|
||||
(id, status, family, source_format, target_format, original_filename, input_path, input_mime_type, expires_at)
|
||||
VALUES (?, 'pending', ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
job.id,
|
||||
job.family,
|
||||
job.sourceFormat,
|
||||
job.targetFormat,
|
||||
job.originalFilename,
|
||||
job.inputPath,
|
||||
job.inputMimeType,
|
||||
job.expiresAt,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
export async function getJobById(pool, id) {
|
||||
const rows = await pool.query(
|
||||
`SELECT id, status, family, source_format, target_format, original_filename,
|
||||
input_path, output_path, input_mime_type, output_mime_type,
|
||||
error_message, created_at, updated_at, expires_at
|
||||
FROM conversion_jobs WHERE id = ?`,
|
||||
[id]
|
||||
);
|
||||
return toCamelJob(rows[0]);
|
||||
}
|
||||
|
||||
export async function getJobErrorLog(pool, id) {
|
||||
const rows = await pool.query('SELECT error_log FROM conversion_jobs WHERE id = ?', [id]);
|
||||
return rows[0]?.error_log ?? null;
|
||||
}
|
||||
|
||||
export async function markProcessing(pool, id) {
|
||||
await pool.query("UPDATE conversion_jobs SET status = 'processing' WHERE id = ?", [id]);
|
||||
}
|
||||
|
||||
export async function markDone(pool, id, { outputPath, outputMimeType }) {
|
||||
await pool.query(
|
||||
"UPDATE conversion_jobs SET status = 'done', output_path = ?, output_mime_type = ? WHERE id = ?",
|
||||
[outputPath, outputMimeType, id]
|
||||
);
|
||||
}
|
||||
|
||||
export async function markFailed(pool, id, { errorMessage, errorLog }) {
|
||||
await pool.query(
|
||||
"UPDATE conversion_jobs SET status = 'failed', error_message = ?, error_log = ? WHERE id = ?",
|
||||
[errorMessage, errorLog, id]
|
||||
);
|
||||
}
|
||||
|
||||
export async function findPendingJobs(pool, limit) {
|
||||
const rows = await pool.query(
|
||||
`SELECT id, status, family, source_format, target_format, original_filename,
|
||||
input_path, output_path, input_mime_type, output_mime_type,
|
||||
error_message, created_at, updated_at, expires_at
|
||||
FROM conversion_jobs WHERE status = 'pending' ORDER BY created_at ASC LIMIT ?`,
|
||||
[limit]
|
||||
);
|
||||
return rows.map(toCamelJob);
|
||||
}
|
||||
|
||||
export async function findExpiredJobs(pool) {
|
||||
const rows = await pool.query(
|
||||
`SELECT id, status, family, source_format, target_format, original_filename,
|
||||
input_path, output_path, input_mime_type, output_mime_type,
|
||||
error_message, created_at, updated_at, expires_at
|
||||
FROM conversion_jobs WHERE expires_at < NOW()`
|
||||
);
|
||||
return rows.map(toCamelJob);
|
||||
}
|
||||
|
||||
export async function deleteJob(pool, id) {
|
||||
await pool.query('DELETE FROM conversion_jobs WHERE id = ?', [id]);
|
||||
}
|
||||
Reference in New Issue
Block a user