Skip to content

feature(monitor): Scheduled tab UI + cron expression support#185

Open
jamby77 wants to merge 1 commit into
feature/monitor-scheduled-capturesfrom
feature/monitor-scheduled-captures-ui
Open

feature(monitor): Scheduled tab UI + cron expression support#185
jamby77 wants to merge 1 commit into
feature/monitor-scheduled-capturesfrom
feature/monitor-scheduled-captures-ui

Conversation

@jamby77
Copy link
Copy Markdown
Collaborator

@jamby77 jamby77 commented May 12, 2026

Summary

PR 20 of the MONITOR stack. Adds the Pro+ Scheduled tab on `/monitor` and brings cron support to the scheduler.

Backend

  • Nullable `cron_expression` column on `scheduled_captures` (sqlite + postgres + memory). `CHECK` constraints enforce exactly one of `intervalSeconds` or `cronExpression` and the `>=10s` floor stays in place when interval is used
  • Idempotent migrations bring PR 19 deployments forward (`PRAGMA table_info` add-column on sqlite, `ADD COLUMN IF NOT EXISTS` on postgres)
  • `StoredScheduledCapture` / `ScheduledCapturePatch` carry optional `cronExpression`
  • `CaptureScheduler`: picks `CronJob` (via `SchedulerRegistry.addCronJob`) when `cronExpression` is set, `setInterval` otherwise. Distinct prefixes (`monitor-schedule-interval-` / `monitor-schedule-cron-`) keep timer maps clean. Cron expressions are validated server-side via `new CronJob(...)` in a try/throw
  • `POST /monitor/schedules` accepts either `intervalSeconds` or `cronExpression` and rejects when both / neither are supplied

Frontend

  • New Scheduled tab on `/monitor`, gated by `Feature.MONITOR_SCHEDULED_CAPTURES`
  • `SchedulesTable`: created at, status badge, cadence (`every 5m` / `cron: */2 * * * *`), duration, last fire timestamp, last session id, last skip reason, delete action
  • `CreateScheduleModal`: amount + unit interval picker by default; Advanced (cron expression) toggle reveals the cron field; capture duration in seconds; success/error states; invalidates the schedules query on save
  • `monitorApi.listSchedules / createSchedule / deleteSchedule`

Tests

  • `capture-scheduler.spec`: cron register/unregister, validation for missing/conflicting/invalid specs
  • `monitor.controller.spec`: BadRequest when both interval and cron are missing; cronExpression forwarding

How to verify

Requires `MONITOR_DEV_PREVIEW=true VITE_MONITOR_DEV_PREVIEW=true` and a Pro+ license loaded.

  1. `pnpm dev`. Browser: `/monitor` → click Scheduled tab.
  2. Click New schedule → enter `5` and `minutes`, duration `5` → Create. Row appears with cadence `every 5m`. Wait 5 min → `lastFiredAt` populates and a new session shows in the Sessions tab.
  3. Open the modal again → toggle Advanced → enter `*/2 * * * *` → Create. Row appears with cadence `cron: */2 * * * *`. Fires on the next even minute.
  4. Click Delete → row disappears within one poll.

Test plan

  • `pnpm exec tsc --noEmit` (apps/api + apps/web) → clean
  • `pnpm exec eslint` on touched files → clean
  • `pnpm exec jest --testPathPatterns="capture-scheduler|monitor.controller|storage"` → 151 tests pass
  • `pnpm exec vitest run` (apps/web) → 187 tests pass
  • Live verify the interval + cron paths against a real Valkey on a Pro+ license

@jamby77 jamby77 force-pushed the feature/monitor-scheduled-captures branch from 467dd68 to 3879ec1 Compare May 12, 2026 11:18
@jamby77 jamby77 force-pushed the feature/monitor-scheduled-captures-ui branch from 69af567 to b5b888f Compare May 12, 2026 11:18
@jamby77 jamby77 force-pushed the feature/monitor-scheduled-captures branch from 3879ec1 to 4f51b75 Compare May 12, 2026 12:45
@jamby77 jamby77 force-pushed the feature/monitor-scheduled-captures-ui branch from b5b888f to 3ccd9e4 Compare May 12, 2026 12:45
@jamby77 jamby77 force-pushed the feature/monitor-scheduled-captures branch from 4f51b75 to 98938d0 Compare May 12, 2026 12:50
@jamby77 jamby77 force-pushed the feature/monitor-scheduled-captures-ui branch from 3ccd9e4 to 043808e Compare May 12, 2026 12:50
@jamby77 jamby77 force-pushed the feature/monitor-scheduled-captures branch from 98938d0 to 2e08240 Compare May 13, 2026 12:42
Backend:
- Add nullable cron_expression column to scheduled_captures (sqlite +
  postgres + memory). CHECK constraint enforces exactly one of
  intervalSeconds or cronExpression
- Idempotent migrations bring PR 19 deployments forward
- StoredScheduledCapture / ScheduledCapturePatch carry optional
  cronExpression
- CaptureScheduler picks cron CronJob via SchedulerRegistry.addCronJob
  for cron rows; setInterval-backed timer for interval rows
- POST /monitor/schedules accepts either intervalSeconds or
  cronExpression and rejects when both or neither are supplied

Frontend:
- New Scheduled tab on /monitor (gated by MONITOR_SCHEDULED_CAPTURES)
  with SchedulesTable showing cadence, duration, last fire info, and
  delete action
- CreateScheduleModal: amount + unit interval picker by default;
  Advanced toggle reveals a cron expression field; both pass through
  monitorApi.createSchedule

Tests:
- capture-scheduler.spec: cron register/unregister, validation
  rejection for missing/conflicting/invalid specs
- monitor.controller.spec: BadRequest when both interval and cron are
  missing; cronExpression forwarding to the scheduler
@jamby77 jamby77 force-pushed the feature/monitor-scheduled-captures-ui branch from 043808e to 0100f82 Compare May 13, 2026 12:42
jamby77 added a commit that referenced this pull request May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant