import { Router } from 'express'; import { requireAuth, requireSuperAdmin } from '../middleware/auth.js'; import { computeMonthlyBilling, listBillingEvents, PRICE_PER_INBOX } from '../services/billing.js'; import { getDomainVolumeForMonth, getVolumeOverview, currentYm, previousYm, } from '../services/ses-events.js'; export const billingRouter = Router(); billingRouter.use(requireAuth); billingRouter.use(requireSuperAdmin); billingRouter.get('/summary', async (req, res) => { const domain = req.query.domain ? String(req.query.domain).toLowerCase() : undefined; const months = await computeMonthlyBilling({ domain }); res.json({ price_per_inbox: PRICE_PER_INBOX, months }); }); billingRouter.get('/events', async (req, res) => { const events = await listBillingEvents({ domain: req.query.domain ? String(req.query.domain).toLowerCase() : undefined, fromIso: req.query.from ? String(req.query.from) : undefined, toIso: req.query.to ? String(req.query.to) : undefined, limit: req.query.limit ? Number(req.query.limit) : 500, }); res.json(events); }); /** * GET /api/billing/volume?domain=foo.com&ym=2026-04 * Per-inbox drilldown for a single domain. */ billingRouter.get('/volume', async (req, res) => { const domain = req.query.domain ? String(req.query.domain).toLowerCase() : ''; if (!domain) { res.status(400).json({ error: 'domain query parameter is required' }); return; } const ym = req.query.ym ? String(req.query.ym) : currentYm(); if (!/^\d{4}-\d{2}$/.test(ym)) { res.status(400).json({ error: 'ym must be in YYYY-MM format' }); return; } res.json(await getDomainVolumeForMonth(domain, ym)); }); /** * GET /api/billing/volume-overview?ym=2026-04 * Cross-domain overview for super admin. One DynamoDB scan, aggregates * per domain. ym defaults to the current month. * * Returns: * { * ym: 'YYYY-MM', * total_send_count, total_bounce_count, total_complaint_count, total_inbox_count, * rows: [{ domain, send_count, bytes_total, bounce_count, complaint_count, inbox_count }, ...] * } */ billingRouter.get('/volume-overview', async (req, res) => { // Limit to current/previous month per product decision — no point in // accepting arbitrary ym values from the client right now. const ym = req.query.ym ? String(req.query.ym) : currentYm(); const allowed = new Set([currentYm(), previousYm()]); if (!allowed.has(ym)) { res.status(400).json({ error: `ym must be one of: ${[...allowed].join(', ')}`, }); return; } res.json(await getVolumeOverview(ym)); });