diff --git a/src/Console/Commands/WatchStats.php b/src/Console/Commands/WatchStats.php new file mode 100644 index 0000000..3e850b5 --- /dev/null +++ b/src/Console/Commands/WatchStats.php @@ -0,0 +1,105 @@ +option('interval')); + + // Restore cursor visibility on Ctrl+C / kill so the terminal isn't + // left in a broken state if the user interrupts mid-render. + if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) { + pcntl_async_signals(true); + $restore = function () { + $this->output->write("\033[?25h"); + $this->newLine(); + exit(0); + }; + pcntl_signal(SIGINT, $restore); + pcntl_signal(SIGTERM, $restore); + } + + $this->output->write("\033[?25l"); // hide cursor for cleaner refresh + + try { + while (true) { + $this->output->write("\033[2J\033[H"); // clear screen + home cursor + + $this->line('WebSocket Server — Live Stats'); + $this->line('' . now()->format('Y-m-d H:i:s') . ' — refreshing every ' . $interval . 's (Ctrl+C to exit)'); + $this->newLine(); + + $this->renderStats(); + + sleep($interval); + } + } finally { + $this->output->write("\033[?25h"); // always restore cursor + } + + return 0; // @phpstan-ignore-line + } + + /** + * Identical to ServerInfo::renderStats — duplicated rather than shared via + * trait so that the two commands can evolve independently if needed (e.g. + * the live view may eventually want sparkline-style deltas that the + * one-shot view doesn't). + */ + 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('Live Stats'); + $this->components->twoColumnDetail('Total connections', "{$totalConnections}"); + $this->components->twoColumnDetail('Authenticated users', "{$uniqueUsers}"); + $this->components->twoColumnDetail('Active channels', '' . count($channels) . ''); + + if (count($channelData) > 0) { + $this->newLine(); + $this->table(['Channel', 'Connections'], $channelData); + } else { + $this->newLine(); + $this->line(' No active channels.'); + } + } +} diff --git a/src/WebSocketsServiceProvider.php b/src/WebSocketsServiceProvider.php index f771d6a..1600de6 100644 --- a/src/WebSocketsServiceProvider.php +++ b/src/WebSocketsServiceProvider.php @@ -61,6 +61,7 @@ class WebSocketsServiceProvider extends ServiceProvider Console\Commands\RestartHard::class, Console\Commands\SteerServer::class, Console\Commands\ServerInfo::class, + Console\Commands\WatchStats::class, ]); }