/** * Usage metering service for SentryAgent.ai AgentIdP. * Provides daily usage summaries and active agent counts per tenant. */ import { Pool } from 'pg'; /** * Daily usage summary for a tenant. */ export interface IUsageSummary { /** The tenant (organization) UUID. */ tenantId: string; /** Date string in YYYY-MM-DD format. */ date: string; /** Number of API calls made on the given date. */ apiCalls: number; /** Number of active (non-decommissioned) agents for the tenant. */ agentCount: number; } /** * Service for retrieving per-tenant usage data. * Reads from the `usage_events` and `agents` tables. */ export class UsageService { /** * @param pool - PostgreSQL connection pool. */ constructor(private readonly pool: Pool) {} /** * Returns the daily usage summary for a tenant on a given date. * If no usage row exists for the date, apiCalls defaults to 0. * * @param tenantId - The tenant UUID. * @param date - Date string in 'YYYY-MM-DD' format. * @returns A resolved IUsageSummary with api_calls and agent count. */ async getDailyUsage(tenantId: string, date: string): Promise { const usageResult = await this.pool.query<{ count: string }>( `SELECT COALESCE(SUM(count), 0) AS count FROM usage_events WHERE tenant_id = $1 AND date = $2 AND metric_type = 'api_calls'`, [tenantId, date], ); const agentCount = await this.getActiveAgentCount(tenantId); const apiCalls = parseInt(usageResult.rows[0]?.count ?? '0', 10); return { tenantId, date, apiCalls, agentCount }; } /** * Returns the number of non-decommissioned agents for a tenant. * * @param tenantId - The tenant UUID. * @returns The count of active agents (status != 'decommissioned'). */ async getActiveAgentCount(tenantId: string): Promise { const result = await this.pool.query<{ count: string }>( `SELECT COUNT(*) AS count FROM agents WHERE organization_id = $1 AND status != 'decommissioned'`, [tenantId], ); return parseInt(result.rows[0]?.count ?? '0', 10); } }