checkAndEnforceBandwidthLimit(); } public function getMonthlyTrafficProperty() { $startOfMonth = Carbon::now()->startOfMonth(); // Get the earliest and latest statistics for this month $firstStat = $this->server->statistics() ->where('DateTime', '>=', $startOfMonth) ->orderBy('DateTime', 'asc') ->first(); $latestStat = $this->server->statistics() ->where('DateTime', '>=', $startOfMonth) ->orderBy('DateTime', 'desc') ->first(); if (!$firstStat || !$latestStat) { return 0; } // Calculate total traffic (in + out) for the month $netinDiff = max(0, $latestStat->netin - $firstStat->netin); $netoutDiff = max(0, $latestStat->netout - $firstStat->netout); $totalBytes = $netinDiff + $netoutDiff; // Convert to GiB return round($totalBytes / (1024 * 1024 * 1024), 2); } public function getBandwidthThreshold95Property() { // Cache for 15 minutes to avoid recalculating for every server return Cache::remember('bandwidth_threshold_95_' . Carbon::now()->format('Y-m'), 15, function () { $startOfMonth = Carbon::now()->startOfMonth(); // Get all servers $allServers = ServerModel::all(); if ($allServers->isEmpty()) { return 10; // Minimum threshold of 10 GiB } $totalTraffic = 0; $serverCount = 0; foreach ($allServers as $server) { // Get the earliest and latest statistics for this month for each server $firstStat = ServerStatistics::where('server_id', $server->id) ->where('DateTime', '>=', $startOfMonth) ->orderBy('DateTime', 'asc') ->first(); $latestStat = ServerStatistics::where('server_id', $server->id) ->where('DateTime', '>=', $startOfMonth) ->orderBy('DateTime', 'desc') ->first(); if ($firstStat && $latestStat) { // Calculate total traffic (in + out) for the month $netinDiff = max(0, $latestStat->netin - $firstStat->netin); $netoutDiff = max(0, $latestStat->netout - $firstStat->netout); $totalBytes = $netinDiff + $netoutDiff; // Convert to GiB and add to total $totalTraffic += $totalBytes / (1024 * 1024 * 1024); $serverCount++; } } if ($serverCount === 0) { return 10; // Minimum threshold of 10 GiB } // Calculate average bandwidth across all servers $averageBandwidth = $totalTraffic / $serverCount; // Calculate 95% of average $threshold95Percent = $averageBandwidth * 0.95; // Return the maximum of 95% average or 10 GiB minimum return round(max($threshold95Percent, 10), 2); }); } protected function checkAndEnforceBandwidthLimit() { $monthlyTraffic = $this->monthlyTraffic; $threshold = $this->bandwidthThreshold95; // Check if bandwidth is exceeded if ($monthlyTraffic > $threshold) { // Throttle the server to 1 MiB/s $this->throttleServerBandwidth(); } } protected function throttleServerBandwidth() { try { $proxmox = new \Paymenter\Extensions\Servers\Proxmox\Proxmox; // Get current VM config $config = $proxmox->request('/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config')->json()['data']; $updates = []; // Update all network interfaces to rate limit of 1 MiB/s foreach ($config as $key => $value) { if (preg_match('/^net\d+$/', $key)) { // Parse existing network config $parts = explode(',', $value); $newParts = []; $hasRate = false; foreach ($parts as $part) { if (strpos($part, 'rate=') === 0) { // Replace existing rate $newParts[] = 'rate=1'; $hasRate = true; } else { $newParts[] = $part; } } // Add rate if not present if (!$hasRate) { $newParts[] = 'rate=1'; } $updates[$key] = implode(',', $newParts); } } // Apply the updates if any network interfaces were found if (!empty($updates)) { $proxmox->request('/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config', 'put', $updates); } } catch (\Throwable $e) { // Log the error but don't fail the page load \Log::error('Failed to throttle bandwidth for server ' . $this->server->id, [ 'error' => $e->getMessage(), 'server_id' => $this->server->id, ]); } } public function render() { return view('proxmox::server'); } public function placeholder(array $params = []) { return view('proxmox::serverplaceholder', $params); } }