feat(recoverable job): add pause/resume capability
This commit is contained in:
@@ -543,6 +543,19 @@ export type JobOutcome =
|
||||
* Cadence + last-run bookkeeping. `interval_ms` / `next_run_at` are
|
||||
* `undefined` on on-demand jobs (serde skips `Option::None`).
|
||||
*/
|
||||
/**
|
||||
* Enough info about a paused recoverable run for the admin panel to
|
||||
* render "Resume (scanned/total)" on the job row without opening the
|
||||
* drawer. Absent when no `Paused` row exists for this job. `total`
|
||||
* is absent when the tenant didn't seed a countable subject —
|
||||
* fallback UI is just "Resume".
|
||||
*/
|
||||
export interface PausedRunBrief {
|
||||
id: string;
|
||||
scanned: number;
|
||||
total?: number;
|
||||
}
|
||||
|
||||
export interface JobSummary {
|
||||
name: string;
|
||||
interval_ms?: number;
|
||||
@@ -560,6 +573,10 @@ export interface JobSummary {
|
||||
* row-expand until this flag was added).
|
||||
*/
|
||||
recoverable: boolean;
|
||||
/** Populated iff a `Paused` row exists in `jobs.recoverable_runs`
|
||||
* for this job. Distinct from `running` — a paused run is
|
||||
* resumable via the same trigger endpoint. */
|
||||
paused_run?: PausedRunBrief;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -616,14 +616,37 @@
|
||||
{/if}
|
||||
</td>
|
||||
<td class="jobs-panel__actions">
|
||||
<button
|
||||
class="jobs-panel__btn jobs-panel__btn--small"
|
||||
disabled={busyKeys.has(`trigger:${job.name}`)}
|
||||
onclick={() => onTrigger(job.name)}
|
||||
>
|
||||
{t('admin.jobs.run', 'Run')}
|
||||
</button>
|
||||
{#if supportsDeep(job.name)}
|
||||
{#if job.paused_run}
|
||||
{@const p = job.paused_run}
|
||||
{@const label =
|
||||
p.total && p.total > 0
|
||||
? t(
|
||||
'admin.jobs.resume_progress',
|
||||
{ scanned: p.scanned, total: p.total },
|
||||
'Resume ({{scanned}}/{{total}})'
|
||||
)
|
||||
: t('admin.jobs.resume', 'Resume')}
|
||||
<button
|
||||
class="jobs-panel__btn jobs-panel__btn--small jobs-panel__btn--primary"
|
||||
disabled={busyKeys.has(`trigger:${job.name}`)}
|
||||
onclick={() => onTrigger(job.name)}
|
||||
title={t(
|
||||
'admin.jobs.resume_title',
|
||||
'Continue the paused run from its last checkpoint.'
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="jobs-panel__btn jobs-panel__btn--small"
|
||||
disabled={busyKeys.has(`trigger:${job.name}`)}
|
||||
onclick={() => onTrigger(job.name)}
|
||||
>
|
||||
{t('admin.jobs.run', 'Run')}
|
||||
</button>
|
||||
{/if}
|
||||
{#if supportsDeep(job.name) && !job.paused_run}
|
||||
<button
|
||||
class="jobs-panel__btn jobs-panel__btn--small"
|
||||
disabled={busyKeys.has(`trigger:${job.name}:deep`)}
|
||||
@@ -633,13 +656,33 @@
|
||||
</button>
|
||||
{/if}
|
||||
{#if isRunning(job) && canExpand}
|
||||
<button
|
||||
class="jobs-panel__btn jobs-panel__btn--small jobs-panel__btn--danger"
|
||||
disabled={busyKeys.has(`cancel:${job.name}`)}
|
||||
onclick={() => onCancel(job.name)}
|
||||
>
|
||||
{t('admin.jobs.cancel', 'Cancel')}
|
||||
</button>
|
||||
{#if isRecoverable(job)}
|
||||
<!-- Recoverable jobs: the "cancel" endpoint just
|
||||
flips CancelRequested → handler yields at the
|
||||
next batch boundary → status=Paused (resumable
|
||||
with a fresh Resume click, cursor preserved).
|
||||
Label it "Pause" so admins know it's not
|
||||
destructive. -->
|
||||
<button
|
||||
class="jobs-panel__btn jobs-panel__btn--small"
|
||||
disabled={busyKeys.has(`cancel:${job.name}`)}
|
||||
onclick={() => onCancel(job.name)}
|
||||
title={t(
|
||||
'admin.jobs.pause_title',
|
||||
'Signal a graceful pause at the next batch boundary. Run row stays as `Paused` — Resume picks up from the checkpoint.'
|
||||
)}
|
||||
>
|
||||
{t('admin.jobs.pause', 'Pause')}
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="jobs-panel__btn jobs-panel__btn--small jobs-panel__btn--danger"
|
||||
disabled={busyKeys.has(`cancel:${job.name}`)}
|
||||
onclick={() => onCancel(job.name)}
|
||||
>
|
||||
{t('admin.jobs.cancel', 'Cancel')}
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user