feat: add websockets:info command with live stats
This commit is contained in:
parent
488f068140
commit
781e329601
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
namespace BlaxSoftware\LaravelWebSockets\Console\Commands;
|
||||
|
||||
use BlaxSoftware\LaravelWebSockets\Services\WebsocketService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class ServerInfo extends Command
|
||||
{
|
||||
protected $signature = 'websockets:info
|
||||
{--live : Live-updating display (like htop). Press Ctrl+C to exit.}
|
||||
{--interval=2 : Refresh interval in seconds for --live mode.}';
|
||||
|
||||
protected $description = 'Show WebSocket server connection info, URL, and frontend secrets.';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if ($this->option('live')) {
|
||||
return $this->liveLoop();
|
||||
}
|
||||
|
||||
$this->renderInfo();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function renderInfo(): void
|
||||
{
|
||||
$this->renderConnectionDetails();
|
||||
$this->newLine();
|
||||
$this->renderFrontendSecrets();
|
||||
$this->newLine();
|
||||
$this->renderStats();
|
||||
}
|
||||
|
||||
protected function renderConnectionDetails(): void
|
||||
{
|
||||
$app = config('websockets.apps.0', []);
|
||||
$port = config('websockets.port', config('websockets.dashboard.port', 6001));
|
||||
$host = $app['host'] ?? env('PUSHER_HOST', '127.0.0.1');
|
||||
$scheme = env('PUSHER_SCHEME', 'ws');
|
||||
|
||||
$appId = $app['id'] ?? env('PUSHER_APP_ID', '—');
|
||||
$appPath = $app['path'] ?? env('PUSHER_APP_PATH', '');
|
||||
|
||||
$internalUrl = "{$scheme}://0.0.0.0:{$port}/app/{$appId}";
|
||||
$externalUrl = "{$scheme}://{$host}:{$port}/app/{$appId}";
|
||||
|
||||
if ($appPath) {
|
||||
$internalUrl .= "/{$appPath}";
|
||||
$externalUrl .= "/{$appPath}";
|
||||
}
|
||||
|
||||
// Detect Traefik setup via APP_TRAEFIK env
|
||||
$traefikHost = env('APP_TRAEFIK');
|
||||
$traefikUrl = null;
|
||||
if ($traefikHost) {
|
||||
$traefikScheme = $scheme === 'ws' ? 'ws' : 'wss';
|
||||
$traefikWsHost = "ws-{$traefikHost}";
|
||||
$traefikUrl = "{$traefikScheme}://{$traefikWsHost}/app/{$appId}";
|
||||
if ($appPath) {
|
||||
$traefikUrl .= "/{$appPath}";
|
||||
}
|
||||
}
|
||||
|
||||
$this->components->twoColumnDetail('<fg=cyan;options=bold>WebSocket Server</>');
|
||||
$this->components->twoColumnDetail('Internal URL', "<fg=green>{$internalUrl}</>");
|
||||
$this->components->twoColumnDetail('External URL', "<fg=green>{$externalUrl}</>");
|
||||
|
||||
if ($traefikUrl) {
|
||||
$this->components->twoColumnDetail('Traefik URL', "<fg=yellow>{$traefikUrl}</>");
|
||||
}
|
||||
|
||||
$this->components->twoColumnDetail('Port', (string) $port);
|
||||
$this->components->twoColumnDetail('Broadcast socket', config('websockets.broadcast_socket_enabled') ? '<fg=green>enabled</>' : '<fg=red>disabled</>');
|
||||
|
||||
if (config('websockets.broadcast_socket_enabled')) {
|
||||
$socketPath = config('websockets.broadcast_socket', '/tmp/laravel-websockets-broadcast.sock');
|
||||
$socketAvailable = ws_available();
|
||||
$this->components->twoColumnDetail(
|
||||
'Socket path',
|
||||
$socketPath . ' ' . ($socketAvailable ? '<fg=green>(listening)</>' : '<fg=red>(not listening)</>')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function renderFrontendSecrets(): void
|
||||
{
|
||||
$app = config('websockets.apps.0', []);
|
||||
$port = config('websockets.port', config('websockets.dashboard.port', 6001));
|
||||
|
||||
$key = $app['key'] ?? env('PUSHER_APP_KEY', '—');
|
||||
$host = $app['host'] ?? env('PUSHER_HOST', '127.0.0.1');
|
||||
$scheme = env('PUSHER_SCHEME', 'ws');
|
||||
$cluster = env('PUSHER_APP_CLUSTER', 'mt1');
|
||||
|
||||
$this->components->twoColumnDetail('<fg=cyan;options=bold>Frontend Connection Secrets</>');
|
||||
$this->components->twoColumnDetail('PUSHER_APP_KEY', "<fg=yellow>{$key}</>");
|
||||
$this->components->twoColumnDetail('PUSHER_HOST', $host);
|
||||
$this->components->twoColumnDetail('PUSHER_PORT', (string) $port);
|
||||
$this->components->twoColumnDetail('PUSHER_SCHEME', $scheme);
|
||||
$this->components->twoColumnDetail('PUSHER_APP_CLUSTER', $cluster);
|
||||
|
||||
$traefikHost = env('APP_TRAEFIK');
|
||||
if ($traefikHost) {
|
||||
$this->components->twoColumnDetail('Traefik WS host', "<fg=yellow>ws-{$traefikHost}</>");
|
||||
}
|
||||
}
|
||||
|
||||
protected function renderStats(): void
|
||||
{
|
||||
$channels = WebsocketService::getActiveChannels();
|
||||
$authedUsers = WebsocketService::getAuthedUsers();
|
||||
|
||||
$totalConnections = 0;
|
||||
$channelData = [];
|
||||
|
||||
foreach ($channels as $channel) {
|
||||
$connections = WebsocketService::getChannelConnections($channel);
|
||||
$count = count($connections);
|
||||
$totalConnections += $count;
|
||||
$channelData[] = [$channel, $count];
|
||||
}
|
||||
|
||||
$uniqueUsers = count(array_unique(array_values($authedUsers)));
|
||||
|
||||
$this->components->twoColumnDetail('<fg=cyan;options=bold>Live Stats</>');
|
||||
$this->components->twoColumnDetail('Total connections', "<fg=white;options=bold>{$totalConnections}</>");
|
||||
$this->components->twoColumnDetail('Authenticated users', "<fg=white;options=bold>{$uniqueUsers}</>");
|
||||
$this->components->twoColumnDetail('Active channels', '<fg=white;options=bold>' . count($channels) . '</>');
|
||||
|
||||
if (count($channelData) > 0) {
|
||||
$this->newLine();
|
||||
$this->table(['Channel', 'Connections'], $channelData);
|
||||
} else {
|
||||
$this->newLine();
|
||||
$this->line(' <fg=gray>No active channels.</>');
|
||||
}
|
||||
}
|
||||
|
||||
protected function liveLoop(): int
|
||||
{
|
||||
$interval = max(1, (int) $this->option('interval'));
|
||||
|
||||
$this->info("Live mode — refreshing every {$interval}s. Press Ctrl+C to exit.");
|
||||
$this->newLine();
|
||||
|
||||
while (true) {
|
||||
// Clear screen
|
||||
$this->output->write("\033[2J\033[H");
|
||||
|
||||
$this->line('<fg=cyan;options=bold> WebSocket Server — Live Stats</>');
|
||||
$this->line(' <fg=gray>' . now()->format('Y-m-d H:i:s') . ' — refreshing every ' . $interval . 's (Ctrl+C to exit)</>');
|
||||
$this->newLine();
|
||||
|
||||
$this->renderStats();
|
||||
|
||||
sleep($interval);
|
||||
}
|
||||
|
||||
return 0; // @phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ class WebSocketsServiceProvider extends ServiceProvider
|
|||
Console\Commands\StartServer::class,
|
||||
Console\Commands\RestartServer::class,
|
||||
Console\Commands\SteerServer::class,
|
||||
Console\Commands\ServerInfo::class,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue