File transfer
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Docker Stats Collector — queries Wings REST API for per-server network stats.
|
||||
*
|
||||
* @author Ball Studios <https://git.balls.studio>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Addons\AdvancedAdmin\Services\Network;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Pterodactyl\Models\Node;
|
||||
use Pterodactyl\Models\Server;
|
||||
|
||||
class DockerStatsCollector
|
||||
{
|
||||
/**
|
||||
* Collect network stats for all running servers on a node.
|
||||
* Returns array of ['server_id' => int, 'rx_bytes' => int, 'tx_bytes' => int]
|
||||
*/
|
||||
public function collectForNode(Node $node): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$servers = Server::query()
|
||||
->where('node_id', $node->id)
|
||||
->whereNotNull('uuid')
|
||||
->get();
|
||||
|
||||
foreach ($servers as $server) {
|
||||
try {
|
||||
$stats = $this->fetchStats($node, $server->uuid);
|
||||
if ($stats !== null) {
|
||||
$results[] = [
|
||||
'server_id' => $server->id,
|
||||
'node_id' => $node->id,
|
||||
'rx_bytes' => $stats['network']['rx_bytes'] ?? 0,
|
||||
'tx_bytes' => $stats['network']['tx_bytes'] ?? 0,
|
||||
];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning("[AdvancedAdmin] Failed to collect stats for server {$server->uuid}: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch stats from Wings for a single server UUID.
|
||||
* Uses the Wings Application API (node-level auth token).
|
||||
*
|
||||
* SSRF Prevention: URL is constructed only from trusted Node model data,
|
||||
* never from user input.
|
||||
*/
|
||||
private function fetchStats(Node $node, string $serverUuid): ?array
|
||||
{
|
||||
// Validate UUID format before use
|
||||
if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $serverUuid)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$scheme = $node->scheme;
|
||||
$fqdn = $node->fqdn;
|
||||
$port = $node->daemonListen;
|
||||
|
||||
// Allowlist: only known node FQDNs are used (from DB, not from user input)
|
||||
$url = "{$scheme}://{$fqdn}:{$port}/api/servers/{$serverUuid}/resources";
|
||||
|
||||
$response = Http::withToken($node->daemon_token_id)
|
||||
->withHeaders(['Authorization' => "Bearer {$node->daemon_token_id}"])
|
||||
->timeout(5)
|
||||
->get($url);
|
||||
|
||||
if (!$response->successful()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $response->json('resources');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user