feat(scheduler): admin-tunable maintenance re-validation batch size
Expose the maintenance job's rolling re-validation batch (videos re-checked per run) as an admin control on the Scheduler dashboard, stored in app_state (migration 0018; NULL = the env/config default). The job reads the effective value each run via a state helper, clamped to a sane range. Trilingual.
This commit is contained in:
parent
264a3c74af
commit
dd83718304
11 changed files with 183 additions and 232 deletions
|
|
@ -212,6 +212,60 @@ function JobRow({
|
|||
);
|
||||
}
|
||||
|
||||
function MaintenanceSettings({
|
||||
m,
|
||||
onSave,
|
||||
saving,
|
||||
}: {
|
||||
m: SchedulerStatus["maintenance"];
|
||||
onSave: (batch: number) => void;
|
||||
saving: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [val, setVal] = useState(String(m.revalidate_batch));
|
||||
useEffect(() => setVal(String(m.revalidate_batch)), [m.revalidate_batch]);
|
||||
|
||||
function save() {
|
||||
const n = parseInt(val, 10);
|
||||
if (Number.isFinite(n) && n >= m.min && n <= m.max && n !== m.revalidate_batch) onSave(n);
|
||||
}
|
||||
const dirty = String(m.revalidate_batch) !== val;
|
||||
|
||||
return (
|
||||
<div className="glass-card rounded-xl p-4">
|
||||
<div className="flex items-center gap-2 text-sm flex-wrap">
|
||||
<Database className="w-4 h-4 text-muted shrink-0" />
|
||||
<Tooltip hint={t("scheduler.maintenance.batchHint")}>
|
||||
<span className="underline decoration-dotted decoration-muted/40 underline-offset-4 cursor-help">
|
||||
{t("scheduler.maintenance.batchLabel")}
|
||||
</span>
|
||||
</Tooltip>
|
||||
<span className="flex-1" />
|
||||
<input
|
||||
type="number"
|
||||
min={m.min}
|
||||
max={m.max}
|
||||
step={1000}
|
||||
value={val}
|
||||
onChange={(e) => setVal(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && save()}
|
||||
className="w-28 bg-card border border-border rounded px-2 py-1 text-sm tabular-nums outline-none focus:border-accent"
|
||||
/>
|
||||
<button
|
||||
onClick={save}
|
||||
disabled={!dirty || saving}
|
||||
className="glass-card glass-hover px-3 py-1.5 rounded-lg text-sm disabled:opacity-40 transition"
|
||||
>
|
||||
{t("common.save")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-[11px] text-muted mt-1.5">
|
||||
{t("scheduler.maintenance.batchNote", { default: m.revalidate_batch_default.toLocaleString() })}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Stat({
|
||||
icon: Icon,
|
||||
label,
|
||||
|
|
@ -285,6 +339,10 @@ export default function Scheduler() {
|
|||
qc.invalidateQueries({ queryKey: ["scheduler"] });
|
||||
},
|
||||
});
|
||||
const batchMut = useMutation({
|
||||
mutationFn: (v: number) => api.updateMaintenanceBatch(v),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ["scheduler"] }),
|
||||
});
|
||||
|
||||
if (q.isLoading && !data)
|
||||
return <div className="p-8 text-muted">{t("scheduler.loading")}</div>;
|
||||
|
|
@ -432,6 +490,16 @@ export default function Scheduler() {
|
|||
<div className="text-[11px] text-muted mt-1.5">{t("scheduler.quotaNote")}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Maintenance settings */}
|
||||
<div>
|
||||
<div className="text-xs uppercase tracking-wide text-muted mb-2">{t("scheduler.maintenance.title")}</div>
|
||||
<MaintenanceSettings
|
||||
m={data.maintenance}
|
||||
onSave={(batch) => batchMut.mutate(batch)}
|
||||
saving={batchMut.isPending}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue