77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
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));
|
|
});
|