Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

12 — API Mapping (Scheduler Module)

Honest statement first: the scheduler has no monitoring API surface. Its public surface is three CRUD-ish endpoints on the repeatable-job registry; everything else the console needs (run history, logs, manual trigger, DLQ) is (proposed) and would be new endpoints. The scheduler's real work happens through cron → BullMQ repeatables → queue internals, not REST. Wire contract per 00-shared/07 (base /api/v1, Bearer JWT, envelopes).


E1 — List repeatable jobs (implemented)

EndpointGET /api/v1/scheduler (scheduler.controller.ts:23-28)
GuardJwtAuthGuard + @Permissions('scheduler.read') (scheduler.controller.ts:18,24)
Requestnone
Responseenvelope data: {queue, name, pattern, tz}[] — iterates the 10 queues' getRepeatableJobs() (scheduler.service.ts:152-186), tz defaults 'UTC' (:181)
NotesNo status/last-run fields — console status badges are impossible from this endpoint alone (07 C1, 09 B2)

E2 — Create custom repeatable job (implemented)

EndpointPOST /api/v1/scheduler (scheduler.controller.ts:30-35)
Guardscheduler.create (:31)
BodyCreateScheduleDto — queue (11 whitelisted, dto:4-16), jobName, 5-field pattern (dto:20-24), optional payload/tz (dto:18-41)
Side effectqueue.add(jobName, payload, {repeat: {pattern, tz}, removeOnComplete {1h/100}, removeOnFail {7d}}) (scheduler.service.ts:198-208)
Errors400 invalid cron/queue; 401; 403; duplicate skipped silently (scheduler.service.ts:128-133)

E3 — Remove repeatable job (implemented)

EndpointDELETE /api/v1/scheduler?queue=&name=&pattern= (scheduler.controller.ts:37-46)
Guardscheduler.delete (:38)
Side effectremoveRepeatable(jobName, {pattern, tz: 'UTC'}) (scheduler.service.ts:188-196); unknown queue → error from getQueue (:210-226)

E4 — Run history (proposed)

GET /scheduler/runs?queue=&name= — last N runs (startedAt, status, duration, error). No source today: BullMQ job state holds the data (attemptsMade, finishedOn, failedReason per dlq.setup.ts:8-21), retention window bounded by removeOnComplete {age:3600, count:100} and removeOnFail {age: 7d} (scheduler.service.ts:145-146), failed jobs 14 d global (bullmq.module.ts:63-64). Backs S2 + S1 badges.

E5 — Job logs (proposed)

GET /scheduler/jobs/:queue/:name/logs?level=&tail= — nestsjs-pino worker logs (AGENTS.md stack) surfaced by jobId; no log-retrieval endpoint today. Backs S4.

E6 — Manual trigger (proposed)

POST /scheduler/:queue/:name/trigger — re-enqueues the child job exactly as the job class would (jobs/*.job.ts pattern: check-overdue, send-daily-digest, send-payment-reminder, generate-attendance-report). Must preserve correlationId so IdempotencyService dedup holds (idempotency.service.ts:11-20). Backs S5.

E7 — DLQ listing (proposed)

GET /scheduler/dlq — records written by setupDlqListener when attemptsMade >= (opts.attempts ?? 3) (dlq.setup.ts:8-9): originalQueue, originalJobId, originalJobName, data, failedReason, attemptsMade, failedAt (:12-21). Backs S6.

E8 — DLQ replay / delete (proposed)

POST /scheduler/dlq/:id/retry (re-add to original queue, preserving correlationId/tenantId), DELETE /scheduler/dlq/:id.

Non-REST surface (the actual work)

TriggerMechanismTargetSource
10 default schedulesBullMQ repeatable queue.add(..., {repeat:{pattern,tz:'UTC'}})10 queuesscheduler.service.ts:48-147
Fan-out child jobsdirect queue.add from job classessee table belowjobs/*.job.ts
Event-driven jobseventQueueMap route → QueueBridgeemails/push/in-app/audit-write/attendance-process/invoice-generateevent-queue-map.ts:6-43, queue-bridge.service.ts:40-75
Failure pathretry 3× exp. 5 s → DLQper queue → dlqbullmq.module.ts:60-65, dlq.setup.ts:5-27
RepeatableCron (UTC)Child job → queue
overdue-scan0 6 * * *check-overdueinvoice-generate (scheduler.service.ts:49-55, overdue-scan.job.ts:12-19)
daily-digest0 9 * * *send-daily-digestemails (:56-62, daily-digest.job.ts:12-25)
dashboard-rebuild*/5 * * * *(payload-only) → cache-rebuild (:63-69)
biometric-sync*/15 * * * *biometric-sync (:70-76)
audit-flush*/1 * * * *audit-write (:77-83)
retention-archive0 2 * * 0tenant-purge (:84-90)
fee-reminder0 8 * * *per-invoice send-payment-reminderpayment-reminder (:91-97, fee-reminder.job.ts:16-47)
attendance-report-daily0 7 * * *generate-attendance-reportreport-generate (:98-104, attendance-report.job.ts:13-28)
admission-reminder-scan0 8 * * *admission-reminder (:105-111)
admission-expiry-scan0 2 * * *admission-expiry (:112-118)

Client contract summary

  • Reads: E1 today; E4-E8 gated on (proposed) endpoints — console builds against them as stubs behind a repository interface.
  • Tenant: scheduler registry is platform-level (tenantId: 'system' trigger payloads, scheduler.service.ts:139) — console is superadmin surface, not tenant-scoped.
  • 401 → reauth; 403 → hide module; 429 → backoff poll (api tier, 00-shared/07 §4); 5xx → keep-last-good.
  • Rate budget: 1 req/min poll = 60 req/h per console tab — fine.