/** * @fileoverview Per-tenant usage metering counters. * @module store/usage */ import { getStore, persist } from './db.js'; import { getTenant, resolveLimits } from './tenants.js'; import { createDebugger } from '../debug/logger.js'; const log = createDebugger('billing'); /** * @returns {string} YYYY-MM period key */ function currentPeriod() { const d = new Date(); return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}`; } /** * @param {string} tenantId * @returns {{ period: string, timestamps: number, verifications: number, statusChecks: number, batchTimestamps: number, overage: object }} */ function emptyUsage(tenantId) { return { tenantId, period: currentPeriod(), timestamps: 0, verifications: 0, statusChecks: 0, batchTimestamps: 0, overage: { timestamps: 0, verifications: 0 }, }; } /** * Get usage for tenant, rolling period if month changed. * @param {string} tenantId * @returns {ReturnType} */ export function getUsage(tenantId) { const store = getStore(); let usage = store.usage[tenantId]; const period = currentPeriod(); if (!usage || usage.period !== period) { usage = emptyUsage(tenantId); store.usage[tenantId] = usage; } return usage; } /** * Public summary for /auth/me. * @param {string} tenantId * @returns {object} */ export function getUsageSummary(tenantId) { const usage = getUsage(tenantId); const tenant = getTenant(tenantId); const limits = tenant ? resolveLimits(tenant) : null; return { period: usage.period, timestamps: usage.timestamps, verifications: usage.verifications, statusChecks: usage.statusChecks, batchTimestamps: usage.batchTimestamps, limits: limits ? { timestamps: limits.timestamps, verifications: limits.verifications, batch: limits.batch, batchMaxItems: limits.batchMaxItems, } : null, }; } /** * Increment a usage metric. * * @param {string} tenantId * @param {string} metric - e.g. `timestamps`, `verifications`, `statusChecks`, `batchTimestamps`, `overage.timestamps` * @param {number} [amount=1] * @returns {void} */ export function incrementUsage(tenantId, metric, amount = 1) { const usage = getUsage(tenantId); if (metric.startsWith('overage.')) { const key = metric.slice('overage.'.length); usage.overage[key] = (usage.overage[key] ?? 0) + amount; } else { usage[metric] = (usage[metric] ?? 0) + amount; } getStore().usage[tenantId] = usage; persist(); log.debug('usage incremented', { tenantId, metric, amount, value: usage[metric] ?? usage.overage }); }