- Add last_confirm_sent_at to subscriptions, with a migration for
existing databases. registerSubscription now sends at most one
confirmation email per 10 minutes per subscription, so a page-refresh
or client retry storm can't spam an inbox. The cooldown resets when
the address changes or a tombstoned row is reactivated (fresh key),
so genuinely new addresses always get an email immediately.
- Restore getCronExpression to the documented daily (17:00 UTC) and
weekly (Monday 17:00 UTC) schedules with the 6-field cron syntax the
cron package expects, fixing the pre-existing reschedule tests that the
'run every minute' testing hack had broken.
- Add confirm-email-cooldown.test.ts covering first send, refresh storms,
frequency changes, email changes, reactivation, and the 10-minute constant.
(cherry picked from commit 051c1e88fb)
Add DB migration (hour, minute, day_of_week, timezone columns) with
idempotent ALTER TABLE upgrade path for existing databases.
Extend getCronExpression with optional dayOfWeek parameter; weekly
defaults to Monday (1) when not specified.
Worker createJob now passes stored schedule fields to cron expression
and uses the subscription's IANA timezone instead of hardcoded 'UTC'.
PUT /subscription/email accepts optional hour (0-23), minute (0-59),
dayOfWeek (1-7, only for weekly), timezone (IANA). GET response
includes the new fields. Omitted fields fall back to defaults (17:00
UTC).
Closes mailship-200
Resolve 21 pre-existing @typescript-eslint/no-unused-vars errors across
5 files (actions.ts, alert.ts, digest.ts, env.ts, worker/email.ts) that
were blocking the script/checks gate. All removals are unused imports
and unused variable assignments with no runtime impact.