/** * @file users.js * @description `/api/users` — server mode's per-user view for an admin or * analyst (members are refused by STAFF_ONLY_PREFIXES in server/lib/auth.js): * `GET /` lists every account with its workspaces, sessions and $ spent in the * last 7 and 30 days (from `token_usage_hourly`, sessions owned through their * workspace — server/lib/scope.js); `GET /audit` the newest audit entries * (server/lib/audit.js). Read-only: accounts are managed from the host's shell. * @author Nguyễn Ngọc Trí Vĩ */ const { Router } = require("express"); const { db, stmts } = require("../db"); const { listUsers } = require("../lib/auth"); const { sessionWhere } = require("../lib/scope"); const { listAudit } = require("../lib/audit"); const router = Router(); const USAGE_COLS = [ "input_tokens", "output_tokens", "cache_read_tokens", "cache_write_tokens", "cache_write_1h_tokens", "web_search_requests", "web_fetch_requests", "code_execution_requests", ]; /** $ and tokens a member's sessions spent since `sinceHour` (UTC `YYYY-MM-DDTHH`). */ function usageSince(userId, sinceHour, rules) { const { calculateCost } = require("./pricing"); const w = sessionWhere({ user: { id: userId, role: "member" } }, "s.id", "s.lane_id"); const rows = db .prepare( `SELECT h.model, h.speed, h.inference_geo, h.service_tier, ${USAGE_COLS.map((c) => `SUM(h.${c}) AS ${c}`).join(", ")} FROM token_usage_hourly h JOIN sessions s ON s.id = h.session_id WHERE h.hour >= ? AND ${w.where} GROUP BY h.model, h.speed, h.inference_geo, h.service_tier` ) .all(sinceHour, ...w.params); const tokens = rows.reduce( (n, r) => n + r.input_tokens + r.output_tokens + r.cache_read_tokens + r.cache_write_tokens, 0 ); return { cost: rows.length ? calculateCost(rows, rules).total_cost : 0, tokens }; } const hourAgo = (days) => new Date(Date.now() - days * 864e5).toISOString().slice(0, 13); router.get("/", (_req, res) => { const rules = stmts.listPricing.all(); const users = listUsers().map((u) => { const w = sessionWhere({ user: { id: u.id, role: "member" } }); return { ...u, workspaces: db.prepare("SELECT COUNT(*) AS n FROM lanes WHERE owner_id = ?").get(u.id).n, sessions: db.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE ${w.where}`).get(...w.params) .n, last7d: usageSince(u.id, hourAgo(7), rules), last30d: usageSince(u.id, hourAgo(30), rules), }; }); res.json({ users }); }); router.get("/audit", (req, res) => { res.json({ entries: listAudit(req.query.limit) }); }); module.exports = router;