feat: add scheduled scans (cron-like recurring scans)

- New `scheduled_scans` table with daily/weekly/monthly frequencies
- asyncio background scheduler loop checks for due schedules every 60s
- 6 REST endpoints: CRUD + toggle enabled + run-now
- `scheduled_scan_id` FK added to scans table; migrated automatically
- Frontend: Schedules page (list + create form), Schedules nav link,
  "Scheduled" badge on ScanDetails when scan was triggered by a schedule

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 10:48:43 +01:00
parent ef5a27097d
commit 836c8474eb
9 changed files with 1666 additions and 10 deletions

View File

@@ -1,9 +1,10 @@
import { Fragment, useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useParams, useNavigate, Link } from 'react-router-dom';
import {
ArrowLeft,
PlaneTakeoff,
Calendar,
CalendarClock,
Users,
Armchair,
Clock,
@@ -221,6 +222,16 @@ export default function ScanDetails() {
<h1 className="text-xl font-semibold text-on-surface">
{scan.origin} {scan.country}
</h1>
{scan.scheduled_scan_id != null && (
<Link
to={`/schedules`}
className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-primary-container text-on-primary-container hover:opacity-80 transition-opacity"
title={`Scheduled scan #${scan.scheduled_scan_id}`}
>
<CalendarClock size={11} aria-hidden="true" />
Scheduled
</Link>
)}
</div>
<StatusChip status={scan.status as ScanStatus} />
</div>