PDO::ERRMODE_EXCEPTION]); $pdo->exec(" CREATE TABLE IF NOT EXISTS admin.cost_tracking ( id SERIAL PRIMARY KEY, provider VARCHAR(50), resource_type VARCHAR(50), resource_id VARCHAR(255), cost_per_hour FLOAT, hours_running FLOAT DEFAULT 0, total_cost FLOAT DEFAULT 0, status VARCHAR(50) DEFAULT 'running', started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, stopped_at TIMESTAMP ); CREATE TABLE IF NOT EXISTS admin.cost_daily ( id SERIAL PRIMARY KEY, date DATE UNIQUE, huawei_cost FLOAT DEFAULT 0, scaleway_cost FLOAT DEFAULT 0, other_cost FLOAT DEFAULT 0, total_cost FLOAT DEFAULT 0, servers_used INTEGER DEFAULT 0, emails_sent INTEGER DEFAULT 0, cost_per_email FLOAT DEFAULT 0 ); CREATE TABLE IF NOT EXISTS admin.cost_alerts ( id SERIAL PRIMARY KEY, alert_type VARCHAR(50), threshold FLOAT, current_value FLOAT, triggered BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS admin.budget_limits ( id SERIAL PRIMARY KEY, period VARCHAR(20) DEFAULT 'daily', provider VARCHAR(50) DEFAULT 'all', limit_amount FLOAT, current_spend FLOAT DEFAULT 0, is_active BOOLEAN DEFAULT true ); "); class CostTracker { private $pdo; // Huawei T6 pricing (approximate) private $pricing = [ 'huawei' => [ 't6.small.1' => 0.02, 't6.medium.2' => 0.05, 't6.large.4' => 0.10, 'eip' => 0.01 ], 'scaleway' => [ 'DEV1-S' => 0.01, 'DEV1-M' => 0.02, 'DEV1-L' => 0.04, 'GP1-S' => 0.05 ] ]; public function __construct($pdo) { $this->pdo = $pdo; $this->initBudgets(); } private function initBudgets() { $budgets = [ ['daily', 'all', 100], ['daily', 'huawei', 75], ['daily', 'scaleway', 25], ['monthly', 'all', 2000] ]; foreach ($budgets as $b) { $this->pdo->prepare("INSERT INTO admin.budget_limits (period, provider, limit_amount) VALUES (?, ?, ?) ON CONFLICT DO NOTHING") ->execute($b); } } public function trackResource($provider, $resourceType, $resourceId, $flavor = 't6.medium.2') { $costPerHour = $this->pricing[$provider][$flavor] ?? 0.05; $this->pdo->prepare("INSERT INTO admin.cost_tracking (provider, resource_type, resource_id, cost_per_hour) VALUES (?, ?, ?, ?)") ->execute([$provider, $resourceType, $resourceId, $costPerHour]); return ['success' => true, 'cost_per_hour' => $costPerHour]; } public function stopTracking($resourceId) { $this->pdo->exec("UPDATE admin.cost_tracking SET status = 'stopped', stopped_at = NOW(), hours_running = EXTRACT(EPOCH FROM (NOW() - started_at)) / 3600, total_cost = cost_per_hour * EXTRACT(EPOCH FROM (NOW() - started_at)) / 3600 WHERE resource_id = '$resourceId' AND status = 'running'"); return ['success' => true]; } public function updateDailyCosts() { $today = date('Y-m-d'); // Calculate today's costs $huawei = $this->pdo->query("SELECT COALESCE(SUM( cost_per_hour * EXTRACT(EPOCH FROM (COALESCE(stopped_at, NOW()) - started_at)) / 3600 ), 0) FROM admin.cost_tracking WHERE provider = 'huawei' AND DATE(started_at) = '$today'")->fetchColumn(); $scaleway = $this->pdo->query("SELECT COALESCE(SUM( cost_per_hour * EXTRACT(EPOCH FROM (COALESCE(stopped_at, NOW()) - started_at)) / 3600 ), 0) FROM admin.cost_tracking WHERE provider = 'scaleway' AND DATE(started_at) = '$today'")->fetchColumn(); $servers = $this->pdo->query("SELECT COUNT(DISTINCT resource_id) FROM admin.cost_tracking WHERE DATE(started_at) = '$today'")->fetchColumn(); $emails = $this->pdo->query("SELECT COALESCE(SUM(total_sent), 0) FROM admin.campaigns WHERE DATE(started_at) = '$today'")->fetchColumn(); $total = $huawei + $scaleway; $costPerEmail = $emails > 0 ? $total / $emails : 0; $this->pdo->prepare("INSERT INTO admin.cost_daily (date, huawei_cost, scaleway_cost, total_cost, servers_used, emails_sent, cost_per_email) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT (date) DO UPDATE SET huawei_cost = ?, scaleway_cost = ?, total_cost = ?, servers_used = ?, emails_sent = ?, cost_per_email = ?") ->execute([$today, $huawei, $scaleway, $total, $servers, $emails, $costPerEmail, $huawei, $scaleway, $total, $servers, $emails, $costPerEmail]); // Check budgets $this->checkBudgets($total); return [ 'date' => $today, 'huawei' => round($huawei, 2), 'scaleway' => round($scaleway, 2), 'total' => round($total, 2), 'servers' => $servers, 'emails' => $emails, 'cost_per_email' => round($costPerEmail, 6) ]; } private function checkBudgets($dailySpend) { $budgets = $this->pdo->query("SELECT * FROM admin.budget_limits WHERE is_active = true AND period = 'daily'")->fetchAll(PDO::FETCH_ASSOC); foreach ($budgets as $budget) { if ($dailySpend >= $budget['limit_amount'] * 0.8) { // 80% warning $this->createAlert('budget_warning', $budget['limit_amount'], $dailySpend); } if ($dailySpend >= $budget['limit_amount']) { // Over budget - could trigger server shutdowns $this->createAlert('budget_exceeded', $budget['limit_amount'], $dailySpend); } } } private function createAlert($type, $threshold, $value) { $this->pdo->prepare("INSERT INTO admin.cost_alerts (alert_type, threshold, current_value, triggered) VALUES (?, ?, ?, true)") ->execute([$type, $threshold, $value]); } public function getStats() { $today = date('Y-m-d'); $month = date('Y-m'); return [ 'today' => $this->pdo->query("SELECT * FROM admin.cost_daily WHERE date = '$today'")->fetch(PDO::FETCH_ASSOC) ?: ['total_cost' => 0], 'this_month' => $this->pdo->query("SELECT SUM(total_cost) as total, SUM(emails_sent) as emails FROM admin.cost_daily WHERE date >= '$month-01'")->fetch(PDO::FETCH_ASSOC), 'running_resources' => $this->pdo->query("SELECT provider, COUNT(*) as count FROM admin.cost_tracking WHERE status = 'running' GROUP BY provider")->fetchAll(PDO::FETCH_ASSOC), 'budgets' => $this->pdo->query("SELECT * FROM admin.budget_limits WHERE is_active = true")->fetchAll(PDO::FETCH_ASSOC), 'recent_alerts' => $this->pdo->query("SELECT * FROM admin.cost_alerts ORDER BY created_at DESC LIMIT 10")->fetchAll(PDO::FETCH_ASSOC) ]; } public function getHistory($days = 30) { return $this->pdo->query("SELECT * FROM admin.cost_daily ORDER BY date DESC LIMIT $days")->fetchAll(PDO::FETCH_ASSOC); } } $tracker = new CostTracker($pdo); $action = $_POST['action'] ?? $_GET['action'] ?? ''; switch ($action) { case 'track': echo json_encode($tracker->trackResource($_POST['provider'], $_POST['resource_type'], $_POST['resource_id'], $_POST['flavor'] ?? 't6.medium.2')); break; case 'stop': echo json_encode($tracker->stopTracking($_POST['resource_id'])); break; case 'update': echo json_encode($tracker->updateDailyCosts()); break; case 'history': echo json_encode(['history' => $tracker->getHistory($_GET['days'] ?? 30)]); break; case 'stats': echo json_encode($tracker->getStats()); break; case 'set_budget': $pdo->prepare("UPDATE admin.budget_limits SET limit_amount = ? WHERE period = ? AND provider = ?") ->execute([$_POST['limit'], $_POST['period'], $_POST['provider']]); echo json_encode(['success' => true]); break; default: echo json_encode(['name' => 'Cost Tracker', 'actions' => ['track','stop','update','history','stats','set_budget']]); }