-- simple-cost-dashboard — schéma SQLite -- -- Une "collection" = un relevé de coût pour un tenant sur une période donnée. -- granularity='daily' -> alimenté par le cron (1 jour = 1 ligne) -- granularity='monthly' -> alimenté par l'import historique (1 mois = 1 ligne), -- utilisé pour les données antérieures à la mise en -- place de la collecte journalière. -- -- Le montant est figé en EUR au moment de la collecte (avec le taux utilisé), -- pour que l'historique affiché ne bouge pas quand le taux de change évolue. CREATE TABLE IF NOT EXISTS collections ( id INTEGER PRIMARY KEY AUTOINCREMENT, provider TEXT NOT NULL CHECK(provider IN ('aws', 'azure')), tenant_name TEXT NOT NULL, granularity TEXT NOT NULL CHECK(granularity IN ('daily', 'monthly')), period_start DATE NOT NULL, -- inclusif period_end DATE NOT NULL, -- exclusif collected_at TIMESTAMP NOT NULL, status TEXT NOT NULL CHECK(status IN ('ok', 'error')), error_message TEXT, total_amount REAL, -- en EUR (figé) currency TEXT, -- 'EUR' original_total REAL, -- montant avant conversion original_currency TEXT, -- devise de facturation d'origine exchange_rate REAL, -- taux appliqué au moment de la collecte UNIQUE(provider, tenant_name, granularity, period_start) ); CREATE INDEX IF NOT EXISTS idx_collections_lookup ON collections(provider, tenant_name, granularity, period_start); CREATE TABLE IF NOT EXISTS service_costs ( id INTEGER PRIMARY KEY AUTOINCREMENT, collection_id INTEGER NOT NULL REFERENCES collections(id) ON DELETE CASCADE, service_name TEXT NOT NULL, amount REAL NOT NULL -- en EUR (figé) ); CREATE INDEX IF NOT EXISTS idx_service_costs_collection ON service_costs(collection_id); -- Détail par ressource (bucket S3, instance EC2, fonction Lambda, VM Azure...). -- Cliché glissant sur les 14 derniers jours, réécrit à chaque collecte — pas -- un historique jour par jour comme `collections` : AWS Cost Explorer ne -- fournit le détail par ressource que sur cette fenêtre (limite dure de -- l'API, indépendante de notre code), donc conserver plus d'historique -- n'apporterait rien. Pour rester cohérent entre providers, Azure (qui n'a -- pas cette limite) utilise la même fenêtre de 14 jours. CREATE TABLE IF NOT EXISTS resource_costs ( id INTEGER PRIMARY KEY AUTOINCREMENT, provider TEXT NOT NULL CHECK(provider IN ('aws', 'azure')), tenant_name TEXT NOT NULL, service_name TEXT NOT NULL, resource_id TEXT NOT NULL, -- ARN AWS / resource ID Azure complet resource_name TEXT NOT NULL, -- dernier segment, pour l'affichage period_start DATE NOT NULL, period_end DATE NOT NULL, amount REAL NOT NULL, -- en EUR (figé), somme sur la fenêtre UNIQUE(provider, tenant_name, service_name, resource_id) ); CREATE INDEX IF NOT EXISTS idx_resource_costs_lookup ON resource_costs(provider, tenant_name, service_name); -- Statut de la collecte resource-level, séparé de `collections` : elle peut -- échouer pour une raison différente (ex: "Resource IDs" non activé côté -- AWS Cost Explorer) sans que la collecte quotidienne normale soit en cause. CREATE TABLE IF NOT EXISTS resource_collection_status ( provider TEXT NOT NULL CHECK(provider IN ('aws', 'azure')), tenant_name TEXT NOT NULL, status TEXT NOT NULL CHECK(status IN ('ok', 'unavailable', 'error')), message TEXT, collected_at TIMESTAMP NOT NULL, PRIMARY KEY (provider, tenant_name) ); -- Historique des exécutions de tâches de collecte (page "Collecte" du -- dashboard) : une ligne par lancement de collect.py (run_type='daily') ou -- backfill_historical.py (run_type='backfill'). Le détail des tenants en -- erreur n'est pas dupliqué ici : il se relit dans `collections` -- (status='error') sur la fenêtre [started_at, finished_at] de l'exécution. -- acknowledged_at reste NULL tant qu'une erreur n'a pas été acquittée -- manuellement depuis le dashboard. CREATE TABLE IF NOT EXISTS collection_runs ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_type TEXT NOT NULL CHECK(run_type IN ('daily', 'backfill')), started_at TIMESTAMP NOT NULL, finished_at TIMESTAMP, tenants_ok INTEGER NOT NULL DEFAULT 0, tenants_error INTEGER NOT NULL DEFAULT 0, acknowledged_at TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_collection_runs_started ON collection_runs(started_at);