import Database from 'better-sqlite3'; import { summarize, UsageEntry, UsageRepo, UsageSummary } from '../usage'; export class SqliteUsageRepo implements UsageRepo { constructor(private db: Database.Database) { this.db.exec(` CREATE TABLE IF NOT EXISTS usage_entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id TEXT NOT NULL, endpoint_id TEXT NOT NULL, cents INTEGER NOT NULL, metadata_bytes INTEGER NOT NULL, attachment_bytes INTEGER NOT NULL, timestamp_ms INTEGER NOT NULL ) `); } record(entry: UsageEntry): void { this.db .prepare( `INSERT INTO usage_entries (customer_id, endpoint_id, cents, metadata_bytes, attachment_bytes, timestamp_ms) VALUES (?, ?, ?, ?, ?, ?)`, ) .run( entry.customerId, entry.endpointId, entry.cents, entry.metadataBytes, entry.attachmentBytes, entry.timestamp.getTime(), ); } listFor(customerId: string, since?: Date): UsageEntry[] { const rows = ( since ? this.db .prepare( 'SELECT * FROM usage_entries WHERE customer_id = ? AND timestamp_ms >= ? ORDER BY timestamp_ms', ) .all(customerId, since.getTime()) : this.db .prepare('SELECT * FROM usage_entries WHERE customer_id = ? ORDER BY timestamp_ms') .all(customerId) ) as Record[]; return rows.map((r) => ({ customerId: r.customer_id as string, endpointId: r.endpoint_id as string, cents: r.cents as number, metadataBytes: r.metadata_bytes as number, attachmentBytes: r.attachment_bytes as number, timestamp: new Date(r.timestamp_ms as number), })); } summaryFor(customerId: string, since?: Date): UsageSummary { return summarize(customerId, this.listFor(customerId, since)); } }