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:
@@ -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" });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user