mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-14 22:33:12 -06:00
Implements the ByteTinker-Bold distribution-agreement billing math and surfaces it on a standalone admin-only route. No UI (the API figure is the deliverable). Server-side only. Contract math (lib/billing.js, config-driven; defaults ARE the agreement): - ASD (per device/day) = min(1.0, online_seconds / (hours*3600)) # 28800 default - BillableScreens (per month) = round-half-up( Sum ASD / days_in_month ) - Flat tier (not marginal): 1-499 $1.50 / 500-999 $1.25 / 1000+ $1.00; cost = screens*rate. Single global rate card for now (per-tenant is a future concern; noted in code). Data foundation: - New durable rollup device_usage_daily(device_id, day 'YYYY-MM-DD', online_seconds), index on day. status_log (3d) / telemetry (24h) can't back a billing month. - Accumulated INCREMENTALLY off the heartbeat tick from the live connection map (same source as devices_connected) - never reconstructed from logs. Each tick credits every connected device's today-row (min(86400, +elapsed)), chunked + transactional (non-blocking); per-tick credit capped (accrualCapSeconds) as a stall/restart guard. - Retention ~400d, pruned via chunked-prune (pruneUsageDaily in runMaintenance). API: GET /api/billing/usage?month=YYYY-MM (default current), requirePlatformAdmin, mounted SEPARATELY from /api/status (billing is revenue data + a heavier aggregate; must not touch the hot status path). Reads the rollup only. MTD figure averages over COMPLETED days only (today shown in `daily` but excluded until it completes); is_final + billable_screens_final appear once the month completes. Tests (12): ASD math; billable round-half-up; flat tier/cost boundaries; accumulator (accrues by interval, caps at 86400/day, disconnected doesn't accrue); report MTD-excludes- today + final-month is_final; retention prune; endpoint authz (admin 200 / non-admin 403 / anon 401) + billing absent from /api/status. Suite 301/301. First-full-month caveat + formula in docs/billing.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
24 lines
918 B
JavaScript
24 lines
918 B
JavaScript
'use strict';
|
||
|
||
// #146 BILLING — admin-gated Usage Report (ByteTinker–Bold agreement §4.1 system-of-record
|
||
// / §4.2 verification). DELIBERATELY a standalone route, separate from routes/status.js:
|
||
// /api/status is the hot, constantly-polled path; this is a heavier admin-only aggregate
|
||
// and must not touch it. Reads the daily rollup ONLY (cheap — no raw-log scans).
|
||
|
||
const express = require('express');
|
||
const router = express.Router();
|
||
const { requirePlatformAdmin } = require('../middleware/auth');
|
||
const billing = require('../lib/billing');
|
||
|
||
// GET /api/billing/usage?month=YYYY-MM (default: current month)
|
||
// Admin/platform-role gated with the SAME authz as other admin endpoints.
|
||
router.get('/usage', requirePlatformAdmin, (req, res) => {
|
||
try {
|
||
res.json(billing.buildUsageReport(req.query.month));
|
||
} catch (e) {
|
||
res.status(400).json({ error: e.message });
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|