The legacy ?secret=... route stays alive through v1.4.27 so existing
Withings subscriptions keep delivering while users re-subscribe. The
release-gate cannot tell when "legacy form usage has trended to zero"
because the route only emits a fire-and-forget Wide Event warning and
the warning text omits the destination URL anyone watching the access
log would need.
This pass adds:
- An in-memory `withings.webhook.legacy_form_total` counter scoped to
the process (the right granularity for "since cold-start" usage).
- A `getLegacyFormTotal()` accessor + a test-only reset helper so the
counter is observable without a backchannel.
- A `counters.withingsWebhookLegacyFormTotal` field on the existing
`/api/admin/status` payload — the natural surface for ops counters
alongside the user / measurement / worker blocks.
- The warning text now spells out `/api/withings/webhook/[token]` and
the "re-subscribe with the path-segment token form" instruction.
- Two new assertions on the webhook test (counter increments on the
query path, stays at zero on the header path, warning contains the
migration URL) and one new assertion on the admin-status test.
Two new pg-boss queues join the existing reminder-worker orchestrator:
withings-activity-sync — cron '0 * * * *' (hourly at :00)
withings-sleep-sync — cron '15 * * * *' (hourly at :15, offset)
Each queue's handler dispatches per-user: when the payload carries
`userId` (webhook-triggered) only that user syncs; when `userId` is
absent (cron-triggered) the worker iterates every WithingsConnection.
Per-user failures log as warnings so one parked-at-reauth connection
doesn't starve the rest on a cron tick.
The webhook handler now reads the `appli` form / JSON field and
dispatches:
appli=16 → enqueue withings-activity-sync (W17b)
appli=44 → enqueue withings-sleep-sync (W17c)
appli=1/2/4 or missing → legacy syncUserMeasurements (unchanged)
When pg-boss is unavailable (dev server without the worker process)
the handler falls back to inline sync so dev iteration isn't blocked
on a separate worker process.
The 15-minute cadence offset between activity (:00) and sleep (:15)
keeps the two crons out of lockstep with the existing measure cron at
:00, smoothing the load against the Withings 120-req/min/app cap.
Adds vitest unit tests covering the four zero-coverage webhook /
monitoring surfaces external systems hit. Each test mocks at the module
boundary (per existing project pattern in src/app/api/auth/login/
__tests__/native-token.test.ts) and asserts on actual call arguments
— no test theatre.
Per-file line coverage:
- src/lib/auth/audit.ts 100.00%
- src/app/api/withings/webhook/route.ts 98.11%
- src/app/api/integrations/moodlog/webhook/route.ts 95.23%
- src/app/api/telegram/webhook/route.ts 95.12%
Test count: +61 (494 -> 555).