53 lines
1.9 KiB
TypeScript
53 lines
1.9 KiB
TypeScript
/**
|
|
* Lets `digest-service.ts` know when each user's next digest is due.
|
|
* `digest_schedule` holds, for each user and cadence they receive, the start
|
|
* of the period their next digest covers; it is due once a full period has
|
|
* passed.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
import type { DatabaseSync } from "node:sqlite";
|
|
|
|
const DAY_MS = 86_400_000;
|
|
// A frozen copy of digest-service's periods: a migration must keep doing
|
|
// what it did when it shipped, whatever the service later becomes.
|
|
const PERIOD_DAYS = { weekly: 7, monthly: 30 } as const;
|
|
|
|
/** Spreads `userId` over `[0, days)` days, the same way each run. */
|
|
function offsetOf(userId: string, days: number): number {
|
|
let hash = 2166136261;
|
|
for (const ch of userId) hash = Math.imul(hash ^ ch.charCodeAt(0), 16777619);
|
|
return ((hash >>> 0) % (days * 24)) * 3_600_000;
|
|
}
|
|
|
|
/**
|
|
* Run once by `runMigrations`. Every user present gets both cadences, with a
|
|
* first period that ends at some hour of the coming week or month, picked
|
|
* from their id: the digests start trickling out after the deploy instead of
|
|
* all leaving a week later. Users created later are scheduled by the service
|
|
* when it first sees them.
|
|
*/
|
|
export function up(db: DatabaseSync): void {
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS digest_schedule (
|
|
user_id TEXT NOT NULL,
|
|
cadence TEXT NOT NULL,
|
|
window_start TEXT NOT NULL,
|
|
PRIMARY KEY (user_id, cadence),
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
);
|
|
`);
|
|
const users = db.prepare(`SELECT id FROM users;`).all() as { id: string }[];
|
|
const insert = db.prepare(
|
|
`INSERT OR IGNORE INTO digest_schedule (user_id, cadence, window_start) VALUES (?, ?, ?);`,
|
|
);
|
|
const now = Date.now();
|
|
for (const { id } of users) {
|
|
for (const [cadence, days] of Object.entries(PERIOD_DAYS)) {
|
|
const start = now - days * DAY_MS + offsetOf(id, days);
|
|
insert.run(id, cadence, new Date(start).toISOString());
|
|
}
|
|
}
|
|
}
|