ses sender

This commit is contained in:
2026-04-29 17:14:50 -05:00
parent 479df311ba
commit 3f0e770d21
4 changed files with 381 additions and 66 deletions

View File

@@ -1,23 +1,15 @@
import { Router } from 'express';
import { requireAuth, requireSuperAdmin } from '../middleware/auth.js';
import { computeMonthlyBilling, listBillingEvents, PRICE_PER_INBOX } from '../services/billing.js';
import { getDomainVolumeForMonth, currentYm } from '../services/ses-events.js';
export const billingRouter = Router();
billingRouter.use(requireAuth);
billingRouter.use(requireSuperAdmin);
/**
* GET /api/billing/summary
* Optional query params:
* ?domain=foo.com -> restrict to one domain
*
* Returns:
* { price_per_inbox: 5,
* months: [
* { domain, ym, year, month, inbox_count, amount_usd, inbox_emails: [...] },
* ...
* ]
* }
* GET /api/billing/summary?domain=foo.com
* Inbox-count based monthly summary ($5 per inbox per month).
*/
billingRouter.get('/summary', async (req, res) => {
const domain = req.query.domain ? String(req.query.domain).toLowerCase() : undefined;
@@ -29,14 +21,8 @@ billingRouter.get('/summary', async (req, res) => {
});
/**
* GET /api/billing/events
* Raw event log. Useful for "Show me the create/delete history".
*
* Optional query params:
* ?domain=foo.com
* ?from=2026-01-01T00:00:00Z
* ?to=2026-02-01T00:00:00Z
* ?limit=200 (1..5000)
* GET /api/billing/events?domain=foo.com
* Raw mailbox lifecycle event log (created/deleted).
*/
billingRouter.get('/events', async (req, res) => {
const events = await listBillingEvents({
@@ -47,3 +33,34 @@ billingRouter.get('/events', async (req, res) => {
});
res.json(events);
});
/**
* GET /api/billing/volume?domain=foo.com&ym=2026-04
* SES outbound volume for a domain in a specific month.
*
* Returns per-inbox + domain totals with send count, total bytes,
* bounce count and complaint count.
*
* Defaults to the current month if ym is omitted.
* Domain is required because volume is always per-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();
// Validate ym format
if (!/^\d{4}-\d{2}$/.test(ym)) {
res.status(400).json({ error: 'ym must be in YYYY-MM format' });
return;
}
const volume = await getDomainVolumeForMonth(domain, ym);
res.json(volume);
});