Merge S7 (a1): newsletter — THE VYNDR REPORT
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> # Conflicts: # BUILD-STATE.md # CLAUDE.md
This commit is contained in:
@@ -233,6 +233,32 @@ router.post('/ledger/settle', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/internal/newsletter/send (Session S7, a1) — assemble today's
|
||||
* VYNDR REPORT from the pipeline (snapshot signals + streak lens + the
|
||||
* ledger record) and send it as a Listmonk campaign to the opted-in list.
|
||||
*
|
||||
* DELIBERATELY UNSCHEDULED: nothing calls this on a timer. The operator
|
||||
* (or a future n8n cron, once Kev arms it) triggers the send. Env-gated —
|
||||
* without LISTMONK_* config it's a calm no-op; an empty report (zero
|
||||
* signals AND zero streaks) refuses to send.
|
||||
*
|
||||
* Body (optional): { sports?: string[] } — defaults to ['mlb', 'wnba'].
|
||||
*/
|
||||
router.post('/newsletter/send', async (req, res) => {
|
||||
const newsletter = require('../services/newsletterService');
|
||||
const body = (req.body && typeof req.body === 'object') ? req.body : {};
|
||||
const sports = Array.isArray(body.sports) && body.sports.length > 0 ? body.sports : undefined;
|
||||
try {
|
||||
const result = await newsletter.sendDailyReport({ sports });
|
||||
return res.json(result);
|
||||
} catch (err) {
|
||||
const message = err && err.message ? err.message : String(err);
|
||||
console.error('[internal/newsletter/send] failed:', message);
|
||||
return res.status(500).json({ ok: false, error: message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/outcomes/:sport', async (req, res) => {
|
||||
const outcomes = require('../services/outcomeService');
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user