Add opt-in Daily Digest (daily email of last 24h of library recaps)

Multi-mode, off by default. Each new recap is synthesized into a 1-2
paragraph overview via the relay (operator-absorbed) and cached onto the
session JSON; a daily 08:00 scan emails opted-in users their fresh
recaps, deduped by a per-user watermark that never skips a failed or
over-cap recap. One-click tokenized unsubscribe; settings-modal toggle;
admin test trigger. Bumps to 0.2.158.
This commit is contained in:
Keysat
2026-06-15 19:50:48 -05:00
parent 962423ca10
commit b4fa5d7be8
14 changed files with 1144 additions and 17 deletions
+56
View File
@@ -279,4 +279,60 @@ export function setupAccountRoutes(app) {
}
},
);
// ── Daily Digest opt-in ────────────────────────────────────────────
// Opt-in (off by default) daily email of the last ~24h of library
// recaps. The relay-owned subscription tier is unrelated — any
// signed-in user may toggle this. GET reads current state; POST
// {enabled:bool} flips it. Enabling stamps last_digest_at to "now"
// so the first digest covers only recaps added AFTER opt-in, never
// the user's whole backlog (the scan picks createdAt > watermark).
app.get("/api/account/digest", requireUser, (req, res) => {
if (!req.user || !req.user.id) {
return res.status(401).json({ error: "auth_required" });
}
try {
const row = getDb()
.prepare("SELECT digest_enabled, last_digest_at FROM users WHERE id = ?")
.get(req.user.id);
res.json({
enabled: !!row?.digest_enabled,
last_digest_at: row?.last_digest_at ?? null,
});
} catch (err) {
console.error("[account] digest read failed:", err);
res.status(500).json({ error: "internal_error" });
}
});
app.post("/api/account/digest", requireUser, (req, res) => {
if (!req.user || !req.user.id) {
return res.status(401).json({ error: "auth_required" });
}
const enabled = req.body?.enabled;
if (typeof enabled !== "boolean") {
return res.status(400).json({ error: "enabled_must_be_boolean" });
}
try {
if (enabled) {
// Start the watermark at opt-in so the first send isn't a backlog dump.
getDb()
.prepare(
"UPDATE users SET digest_enabled = 1, last_digest_at = ? WHERE id = ?",
)
.run(Date.now(), req.user.id);
} else {
getDb()
.prepare("UPDATE users SET digest_enabled = 0 WHERE id = ?")
.run(req.user.id);
}
console.log(
`[account] digest ${enabled ? "enabled" : "disabled"} for user ${req.user.id}`,
);
res.json({ ok: true, enabled });
} catch (err) {
console.error("[account] digest toggle failed:", err);
res.status(500).json({ error: "internal_error" });
}
});
}