clusev/app/Services/WgStatus.php

81 lines
2.9 KiB
PHP

<?php
namespace App\Services;
/**
* Reads + validates the host-written status file run/wg-status.json (bind-mounted to
* storage/app/restart-signal). Single source for the WireGuard page + the /wg-status.json route.
* Contains NO secrets — the collector never writes a private key.
*/
class WgStatus
{
/** Status older than this (seconds) means the host collector is offline. */
private const STALE_AFTER = 20;
/** A peer with a handshake within this many seconds is considered online. */
private const ONLINE_WITHIN = 180;
private function path(): string
{
return storage_path('app/restart-signal/wg-status.json');
}
/**
* @return array{configured:bool, at:int, stale:bool, gate?:array, wg_quick?:string, server?:array, peers?:array}
*/
public function read(): array
{
$path = $this->path();
$data = is_file($path) ? json_decode((string) @file_get_contents($path), true) : null;
if (! is_array($data)) {
return ['configured' => false, 'at' => 0, 'stale' => true];
}
$at = (int) ($data['at'] ?? 0);
$stale = $at < (time() - self::STALE_AFTER);
if (empty($data['configured'])) {
return ['configured' => false, 'at' => $at, 'stale' => $stale];
}
$peers = [];
foreach ((array) ($data['peers'] ?? []) as $p) {
if (! is_array($p)) {
continue;
}
$hs = (int) ($p['latest_handshake'] ?? 0);
$peers[] = [
'name' => (string) ($p['name'] ?? ''),
'pubkey' => (string) ($p['pubkey'] ?? ''),
'endpoint' => (string) ($p['endpoint'] ?? ''),
'latest_handshake' => $hs,
'rx' => (int) ($p['rx'] ?? 0),
'tx' => (int) ($p['tx'] ?? 0),
'online' => $hs > (time() - self::ONLINE_WITHIN),
];
}
return [
'configured' => true,
'at' => $at,
'stale' => $stale,
'gate' => [
'marker' => (bool) ($data['gate']['marker'] ?? false),
'chain' => (bool) ($data['gate']['chain'] ?? false),
'ssh' => (bool) ($data['gate']['ssh'] ?? false),
],
'wg_quick' => (string) ($data['wg_quick'] ?? 'inactive'),
'server' => [
'subnet' => (string) ($data['server']['subnet'] ?? ''),
'server_ip' => (string) ($data['server']['server_ip'] ?? ''),
'port' => (int) ($data['server']['port'] ?? 0),
'endpoint' => (string) ($data['server']['endpoint'] ?? ''),
// Not rendered by the page, but part of the /wg-status.json response contract (API consumers).
'pubkey' => (string) ($data['server']['pubkey'] ?? ''),
],
'peers' => $peers,
];
}
}