pipeline: equal-width columns + per-column clear/stop button
All checks were successful
Build and Push Docker Image / build (push) Successful in 39s
All checks were successful
Build and Push Docker Image / build (push) Successful in 39s
Extract a ColumnShell component so all four columns share the same flex-1
basis-0 width (no more 24/16/18/16 rem mix) and the same header layout
(title + count + optional action button on the right).
Per-column actions:
- Review: 'Skip all' → POST /api/review/skip-all (new endpoint, sets all
pending non-noop plans to skipped in one update)
- Queued: 'Clear' → POST /api/execute/clear (existing; cancels pending jobs)
- Processing: 'Stop' → POST /api/execute/stop (new; SIGTERMs the running
ffmpeg via a tracked Bun.spawn handle, runJob's catch path
marks the job error and cleans up)
- Done: 'Clear' → POST /api/execute/clear-completed (existing)
All destructive actions confirm before firing.
This commit is contained in:
@@ -20,6 +20,8 @@ const app = new Hono();
|
||||
// ─── Sequential local queue ──────────────────────────────────────────────────
|
||||
|
||||
let queueRunning = false;
|
||||
let runningProc: ReturnType<typeof Bun.spawn> | null = null;
|
||||
let runningJobId: number | null = null;
|
||||
|
||||
function emitQueueStatus(
|
||||
status: "running" | "paused" | "sleeping" | "idle",
|
||||
@@ -255,6 +257,23 @@ app.post("/clear-completed", (c) => {
|
||||
return c.json({ ok: true, cleared: result.changes });
|
||||
});
|
||||
|
||||
// ─── Stop running job ─────────────────────────────────────────────────────────
|
||||
|
||||
app.post("/stop", (c) => {
|
||||
if (!runningProc || runningJobId == null) {
|
||||
return c.json({ ok: false, error: "No job is currently running" }, 409);
|
||||
}
|
||||
const stoppedId = runningJobId;
|
||||
try {
|
||||
runningProc.kill("SIGTERM");
|
||||
} catch (err) {
|
||||
logError(`Failed to kill job ${stoppedId}:`, err);
|
||||
return c.json({ ok: false, error: String(err) }, 500);
|
||||
}
|
||||
// runJob's catch path will mark the job error and clean up runningProc.
|
||||
return c.json({ ok: true, stopped: stoppedId });
|
||||
});
|
||||
|
||||
// ─── SSE ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
app.get("/events", (c) => {
|
||||
@@ -356,6 +375,8 @@ async function runJob(job: Job): Promise<void> {
|
||||
|
||||
try {
|
||||
const proc = Bun.spawn(["sh", "-c", job.command], { stdout: "pipe", stderr: "pipe" });
|
||||
runningProc = proc;
|
||||
runningJobId = job.id;
|
||||
const readStream = async (readable: ReadableStream<Uint8Array>, prefix = "") => {
|
||||
const reader = readable.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
@@ -422,6 +443,9 @@ async function runJob(job: Job): Promise<void> {
|
||||
.run(fullOutput, job.id);
|
||||
emitJobUpdate(job.id, "error", fullOutput);
|
||||
db.prepare("UPDATE review_plans SET status = 'error' WHERE item_id = ?").run(job.item_id);
|
||||
} finally {
|
||||
runningProc = null;
|
||||
runningJobId = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user