From f8008ea95ec22865f41768a287f4cd7a96a92a23 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sat, 24 Nov 2018 00:06:28 +0100 Subject: [PATCH 1/5] wip --- config/websockets.php | 5 ++ resources/views/console.blade.php | 83 +++++++++++++++++++ src/Http/Controllers/AuthenticateConsole.php | 14 ++++ src/Http/Controllers/ShowConsole.php | 13 +++ src/Http/routes.php | 4 + .../Pusher/Channels/ChannelManager.php | 4 + .../Pusher/Channels/LoggingChannel.php | 24 ++++++ .../Exceptions/InvalidConnectionException.php | 12 +++ ...nAppKey.php => UnknownAppKeyException.php} | 2 +- src/LaravelEcho/WebSocket/ConsoleServer.php | 36 ++++++++ src/LaravelEcho/WebSocket/PusherServer.php | 11 ++- src/LaravelWebSocketsServiceProvider.php | 25 ++++++ src/Router.php | 2 + 13 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 resources/views/console.blade.php create mode 100644 src/Http/Controllers/AuthenticateConsole.php create mode 100644 src/Http/Controllers/ShowConsole.php create mode 100644 src/Http/routes.php create mode 100644 src/LaravelEcho/Pusher/Channels/LoggingChannel.php create mode 100644 src/LaravelEcho/Pusher/Exceptions/InvalidConnectionException.php rename src/LaravelEcho/Pusher/Exceptions/{UnknownAppKey.php => UnknownAppKeyException.php} (81%) create mode 100644 src/LaravelEcho/WebSocket/ConsoleServer.php diff --git a/config/websockets.php b/config/websockets.php index 85bdf28..6ca0917 100644 --- a/config/websockets.php +++ b/config/websockets.php @@ -4,6 +4,11 @@ use BeyondCode\LaravelWebSockets\ClientProviders\ConfigClientProvider; return [ + /* + * Path for the Websockets debug console + */ + 'path' => '/websockets', + /* * TODO: add the laravel style comment here */ diff --git a/resources/views/console.blade.php b/resources/views/console.blade.php new file mode 100644 index 0000000..a9bb95f --- /dev/null +++ b/resources/views/console.blade.php @@ -0,0 +1,83 @@ + + + + WebSockets Console + + + + + + +
+
+
+
+ WebSockets Console +
+
+ + +
+
+ + +
+ +
+
+ +
+
+
+

Events

+ + + + + + + + + + + +
TypeSocketDetailsTime
+
+
+
+ + + \ No newline at end of file diff --git a/src/Http/Controllers/AuthenticateConsole.php b/src/Http/Controllers/AuthenticateConsole.php new file mode 100644 index 0000000..12e3cd0 --- /dev/null +++ b/src/Http/Controllers/AuthenticateConsole.php @@ -0,0 +1,14 @@ +validAuthenticationResponse($request, []); + } +} \ No newline at end of file diff --git a/src/Http/Controllers/ShowConsole.php b/src/Http/Controllers/ShowConsole.php new file mode 100644 index 0000000..ebeb262 --- /dev/null +++ b/src/Http/Controllers/ShowConsole.php @@ -0,0 +1,13 @@ +verifySignature($connection, $payload); + + $this->verifyAdministrator($connection); + + parent::subscribe($connection, $payload); + } + + protected function verifyAdministrator(ConnectionInterface $connection) + { + throw_unless($connection->isAdmin, new InvalidConnectionException()); + } +} \ No newline at end of file diff --git a/src/LaravelEcho/Pusher/Exceptions/InvalidConnectionException.php b/src/LaravelEcho/Pusher/Exceptions/InvalidConnectionException.php new file mode 100644 index 0000000..143270c --- /dev/null +++ b/src/LaravelEcho/Pusher/Exceptions/InvalidConnectionException.php @@ -0,0 +1,12 @@ +message = 'Invalid Connection'; + $this->code = 4009; + } +} \ No newline at end of file diff --git a/src/LaravelEcho/Pusher/Exceptions/UnknownAppKey.php b/src/LaravelEcho/Pusher/Exceptions/UnknownAppKeyException.php similarity index 81% rename from src/LaravelEcho/Pusher/Exceptions/UnknownAppKey.php rename to src/LaravelEcho/Pusher/Exceptions/UnknownAppKeyException.php index 76e856f..c154d27 100644 --- a/src/LaravelEcho/Pusher/Exceptions/UnknownAppKey.php +++ b/src/LaravelEcho/Pusher/Exceptions/UnknownAppKeyException.php @@ -2,7 +2,7 @@ namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions; -class UnknownAppKey extends PusherException +class UnknownAppKeyException extends PusherException { public function __construct(string $appKey) { diff --git a/src/LaravelEcho/WebSocket/ConsoleServer.php b/src/LaravelEcho/WebSocket/ConsoleServer.php new file mode 100644 index 0000000..34beb5a --- /dev/null +++ b/src/LaravelEcho/WebSocket/ConsoleServer.php @@ -0,0 +1,36 @@ +generateSocketId($connection); + + $this->verifyConnection($connection); + + $this->establishConnection($connection); + + // TODO check connection signature + $connection->isAdmin = true; + } + + public function log(string $appId, string $type, string $details) + { + $channelId = "private-logger-{$type}"; + + $channel = $this->channelManager->find($appId, $channelId); + + optional($channel)->broadcast([ + 'event' => $type, + 'channel' => $channelId, + 'data' => [ + 'type' => $type, + 'details' => $details + ] + ]); + } +} \ No newline at end of file diff --git a/src/LaravelEcho/WebSocket/PusherServer.php b/src/LaravelEcho/WebSocket/PusherServer.php index f48027f..9cab45d 100644 --- a/src/LaravelEcho/WebSocket/PusherServer.php +++ b/src/LaravelEcho/WebSocket/PusherServer.php @@ -10,15 +10,22 @@ use Ratchet\ConnectionInterface; use Ratchet\RFC6455\Messaging\MessageInterface; use BeyondCode\LaravelWebSockets\WebSocketController; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager; +use BeyondCode\LaravelWebsockets\LaravelEcho\Pusher\Exceptions\PusherException; +use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKeyException; class PusherServer extends WebSocketController { /** @var \BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager */ protected $channelManager; - public function __construct(ChannelManager $channelManager) + /** @var ConsoleServer|null */ + protected $consoleServer; + + public function __construct(ChannelManager $channelManager, ConsoleServer $consoleServer = null) { $this->channelManager = $channelManager; + + $this->consoleServer = $consoleServer; } function onOpen(ConnectionInterface $connection) @@ -28,6 +35,8 @@ class PusherServer extends WebSocketController $this->verifyConnection($connection); $this->establishConnection($connection); + + $this->consoleServer->log($connection->appId, 'new_connection', ''); } public function onMessage(ConnectionInterface $connection, MessageInterface $message) diff --git a/src/LaravelWebSocketsServiceProvider.php b/src/LaravelWebSocketsServiceProvider.php index c96fe8b..b980814 100644 --- a/src/LaravelWebSocketsServiceProvider.php +++ b/src/LaravelWebSocketsServiceProvider.php @@ -2,8 +2,10 @@ namespace BeyondCode\LaravelWebSockets; +use Illuminate\Support\Facades\Route; use BeyondCode\LaravelWebSockets\ClientProviders\ClientProvider; use Illuminate\Support\ServiceProvider; +use BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket\ConsoleServer; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager; class LaravelWebSocketsServiceProvider extends ServiceProvider @@ -14,11 +16,30 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider __DIR__.'/../config/websockets.php' => base_path('config/websockets.php'), ], 'config'); + $this->registerRoutes(); + + $this->loadViewsFrom(__DIR__.'/../resources/views/', 'websockets'); + $this->commands([ Console\StartWebSocketServer::class, ]); } + protected function registerRoutes() + { + Route::group($this->routeConfiguration(), function () { + $this->loadRoutesFrom(__DIR__.'/Http/routes.php'); + }); + } + + protected function routeConfiguration() + { + return [ + 'namespace' => 'BeyondCode\LaravelWebSockets\Http\Controllers', + 'prefix' => config('websockets.path'), + ]; + } + public function register() { $this->mergeConfigFrom(__DIR__.'/../config/websockets.php', 'websockets'); @@ -34,5 +55,9 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider $this->app->singleton(ClientProvider::class, function() { return app(config('websockets.client_provider')); }); + + $this->app->singleton(ConsoleServer::class, function() { + return new ConsoleServer(new ChannelManager()); + }); } } diff --git a/src/Router.php b/src/Router.php index 3f07418..2f3147e 100644 --- a/src/Router.php +++ b/src/Router.php @@ -67,6 +67,8 @@ class Router { $this->get('/app/{appKey}', LaravelEcho\WebSocket\PusherServer::class); + $this->get('/console/app/{appKey}', LaravelEcho\WebSocket\ConsoleServer::class); + $this->get('/apps/{appId}/channels', LaravelEcho\Http\Controllers\FetchChannels::class); $this->get('/apps/{appId}/channels/{channelName}', LaravelEcho\Http\Controllers\FetchChannel::class); $this->get('/apps/{appId}/channels/{channelName}/users', LaravelEcho\Http\Controllers\FetchUsers::class); From e6b22dc36fb9cf62a8c5cc376779725a67476869 Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sat, 24 Nov 2018 00:15:18 +0100 Subject: [PATCH 2/5] wip --- .../Pusher/Channels/ChannelManager.php | 4 ---- .../Pusher/Channels/LoggingChannel.php | 24 ------------------- 2 files changed, 28 deletions(-) delete mode 100644 src/LaravelEcho/Pusher/Channels/LoggingChannel.php diff --git a/src/LaravelEcho/Pusher/Channels/ChannelManager.php b/src/LaravelEcho/Pusher/Channels/ChannelManager.php index 6230311..2b2c46b 100644 --- a/src/LaravelEcho/Pusher/Channels/ChannelManager.php +++ b/src/LaravelEcho/Pusher/Channels/ChannelManager.php @@ -30,10 +30,6 @@ class ChannelManager protected function detectChannelClass($channelId): string { - if (starts_with($channelId, 'logging-')) { - return LoggingChannel::class; - } - if (starts_with($channelId, 'private-')) { return PrivateChannel::class; } diff --git a/src/LaravelEcho/Pusher/Channels/LoggingChannel.php b/src/LaravelEcho/Pusher/Channels/LoggingChannel.php deleted file mode 100644 index f09f23c..0000000 --- a/src/LaravelEcho/Pusher/Channels/LoggingChannel.php +++ /dev/null @@ -1,24 +0,0 @@ -verifySignature($connection, $payload); - - $this->verifyAdministrator($connection); - - parent::subscribe($connection, $payload); - } - - protected function verifyAdministrator(ConnectionInterface $connection) - { - throw_unless($connection->isAdmin, new InvalidConnectionException()); - } -} \ No newline at end of file From 92362ba918173e26cbb2a8e9798965de72e260ad Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sat, 24 Nov 2018 13:58:56 +0100 Subject: [PATCH 3/5] wip --- src/LaravelEcho/WebSocket/PusherServer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LaravelEcho/WebSocket/PusherServer.php b/src/LaravelEcho/WebSocket/PusherServer.php index 9cab45d..1889fe5 100644 --- a/src/LaravelEcho/WebSocket/PusherServer.php +++ b/src/LaravelEcho/WebSocket/PusherServer.php @@ -9,6 +9,7 @@ use Exception; use Ratchet\ConnectionInterface; use Ratchet\RFC6455\Messaging\MessageInterface; use BeyondCode\LaravelWebSockets\WebSocketController; +use BeyondCode\LaravelWebSockets\ClientProviders\Client; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager; use BeyondCode\LaravelWebsockets\LaravelEcho\Pusher\Exceptions\PusherException; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKeyException; @@ -70,7 +71,7 @@ class PusherServer extends WebSocketController parse_str($request->getUri()->getQuery(), $queryParameters); if (! $client = Client::findByAppKey($queryParameters['appKey'])) { - throw new UnknownAppKey($queryParameters['appKey']); + throw new UnknownAppKeyException($queryParameters['appKey']); } $connection->client = $client; From 8faa7bf3f13808fc3e15c950390f9887ad600c3a Mon Sep 17 00:00:00 2001 From: Marcel Pociot Date: Sat, 24 Nov 2018 23:52:55 +0100 Subject: [PATCH 4/5] wip --- composer.json | 8 ++--- config/websockets.php | 23 ++++++++++--- resources/views/console.blade.php | 24 ++++++------- src/ClientProviders/Client.php | 7 +++- src/ClientProviders/ClientProvider.php | 2 ++ src/ClientProviders/ConfigClientProvider.php | 12 ++++++- src/Http/Controllers/AuthenticateConsole.php | 2 +- src/Http/Controllers/ShowConsole.php | 7 ++-- src/Http/Middleware/Authorize.php | 13 +++++++ src/LaravelEcho/WebSocket/ConsoleServer.php | 36 -------------------- src/LaravelEcho/WebSocket/PusherServer.php | 29 +++++++++------- src/LaravelWebSocketsServiceProvider.php | 16 ++++++--- src/Router.php | 2 -- 13 files changed, 99 insertions(+), 82 deletions(-) create mode 100644 src/Http/Middleware/Authorize.php delete mode 100644 src/LaravelEcho/WebSocket/ConsoleServer.php diff --git a/composer.json b/composer.json index d80f32c..38b4c71 100644 --- a/composer.json +++ b/composer.json @@ -25,10 +25,10 @@ "php": "^7.1", "ext-json": "*", "cboden/ratchet": "^0.4.1", - "illuminate/console": "5.6.*|5.7.*", - "illuminate/http": "5.6.*|5.7.*", - "illuminate/routing": "5.6.*|5.7.*", - "illuminate/support": "5.6.*|5.7.*", + "illuminate/console": "5.7.*", + "illuminate/http": "5.7.*", + "illuminate/routing": "5.7.*", + "illuminate/support": "5.7.*", "symfony/http-kernel": "~4.0", "symfony/psr-http-message-bridge": "^1.1" }, diff --git a/config/websockets.php b/config/websockets.php index 6ca0917..8e1d003 100644 --- a/config/websockets.php +++ b/config/websockets.php @@ -1,14 +1,10 @@ '/websockets', - /* * TODO: add the laravel style comment here */ @@ -49,6 +45,7 @@ return [ */ 'clients' => [ [ + 'name' => env('APP_NAME'), 'app_id' => env('WEBSOCKETS_APP_ID'), 'app_key' => env('WEBSOCKETS_APP_KEY'), 'app_secret' => env('WEBSOCKETS_APP_SECRET') @@ -63,4 +60,20 @@ return [ * `ClientProvier` interface. */ 'client_provider' => ConfigClientProvider::class, + + 'dashboard' => [ + + /* + * Path for the Websockets debug console + */ + 'path' => '/websockets', + + /* + * Middleware that will be applied to the dashboard routes. + */ + 'middleware' => [ + Authorize::class, + ], + + ] ]; \ No newline at end of file diff --git a/resources/views/console.blade.php b/resources/views/console.blade.php index a9bb95f..e3366f4 100644 --- a/resources/views/console.blade.php +++ b/resources/views/console.blade.php @@ -18,12 +18,11 @@ WebSockets Console
- - -
-
- - +
@@ -51,9 +50,7 @@ + -
-
-
-
- WebSockets Console +
+
+
-
- -
- -
-
- + + + + + +
+
-
-

Events

+
+
+

Event Creator

+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+

Events

@@ -42,38 +64,113 @@ + + + + + +
@{{ log.type }}@{{ log.socketId }}@{{ log.details }}@{{ log.time }}
\ No newline at end of file diff --git a/src/Http/Controllers/SendMessage.php b/src/Http/Controllers/SendMessage.php new file mode 100644 index 0000000..ff9b39b --- /dev/null +++ b/src/Http/Controllers/SendMessage.php @@ -0,0 +1,21 @@ +key, $request->secret, + $request->appId, config('broadcasting.connections.pusher.options', []) + ); + + return (new PusherBroadcaster($pusher)) + ->broadcast([$request->channel], $request->event, json_decode($request->data, true)); + } +} \ No newline at end of file diff --git a/src/Http/routes.php b/src/Http/routes.php index 82d1f1a..b41fb75 100644 --- a/src/Http/routes.php +++ b/src/Http/routes.php @@ -1,4 +1,5 @@ verifySignature($request); foreach ($request->json()->get('channels', []) as $channelId) { + Dashboard::apiMessage($request->appId, $channelId, $request->json()->get('name'), $request->json()->get('data')); + $channel = $this->channelManager->find($request->appId, $channelId); optional($channel)->broadcastToEveryoneExcept([ diff --git a/src/LaravelEcho/Pusher/Channels/Channel.php b/src/LaravelEcho/Pusher/Channels/Channel.php index d84ea03..0eb9864 100644 --- a/src/LaravelEcho/Pusher/Channels/Channel.php +++ b/src/LaravelEcho/Pusher/Channels/Channel.php @@ -2,6 +2,7 @@ namespace BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels; +use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\InvalidSignatureException; use Illuminate\Support\Collection; use Ratchet\ConnectionInterface; @@ -56,11 +57,21 @@ class Channel public function unsubscribe(ConnectionInterface $connection) { unset($this->subscriptions[$connection->socketId]); + + if (! $this->hasConnections()) { + Dashboard::vacated($connection, $this->channelId); + } } protected function saveConnection(ConnectionInterface $connection) { + if (! $this->hasConnections()) { + Dashboard::occupied($connection, $this->channelId); + } + $this->subscriptions[$connection->socketId] = $connection; + + Dashboard::subscribed($connection, $this->channelId); } public function broadcast($payload) diff --git a/src/LaravelEcho/Pusher/Dashboard.php b/src/LaravelEcho/Pusher/Dashboard.php new file mode 100644 index 0000000..8a325e0 --- /dev/null +++ b/src/LaravelEcho/Pusher/Dashboard.php @@ -0,0 +1,100 @@ +httpRequest; + + self::log($connection->client->appId, self::TYPE_CONNECTION, [ + 'details' => "Origin: {$request->getUri()->getScheme()}://{$request->getUri()->getHost()}", + 'socketId' => $connection->socketId, + ]); + } + + public static function disconnection(ConnectionInterface $connection) + { + self::log($connection->client->appId, self::TYPE_DISCONNECTION, [ + 'socketId' => $connection->socketId + ]); + } + + public static function vacated(ConnectionInterface $connection, string $channelId) + { + self::log($connection->client->appId, self::TYPE_VACATED, [ + 'details' => "Channel: {$channelId}" + ]); + } + + public static function occupied(ConnectionInterface $connection, string $channelId) + { + self::log($connection->client->appId, self::TYPE_OCCUPIED, [ + 'details' => "Channel: {$channelId}" + ]); + } + + public static function subscribed(ConnectionInterface $connection, string $channelId) + { + self::log($connection->client->appId, self::TYPE_SUBSCRIBED, [ + 'socketId' => $connection->socketId, + 'details' => "Channel: {$channelId}" + ]); + } + + public static function clientMessage(ConnectionInterface $connection, stdClass $payload) + { + self::log($connection->client->appId, self::TYPE_CLIENT_MESSAGE, [ + 'details' => "Channel: {$payload->channel}, Event: {$payload->event}", + 'socketId' => $connection->socketId, + 'data' => json_encode($payload) + ]); + } + + public static function apiMessage($appId, string $channel, string $event, string $payload) + { + self::log($appId, self::TYPE_API_MESSAGE, [ + 'details' => "Channel: {$channel}, Event: {$event}", + 'data' => $payload + ]); + } + + public static function log($appId, string $type, array $attributes = []) + { + $channelId = self::LOG_CHANNEL_PREFIX . $type; + + $channel = app(ChannelManager::class)->find($appId, $channelId); + + optional($channel)->broadcast([ + 'event' => 'log_message', + 'channel' => $channelId, + 'data' => [ + 'type' => $type, + 'time' => strftime("%H:%M:%S") + ] + $attributes + ]); + } + +} \ No newline at end of file diff --git a/src/LaravelEcho/WebSocket/Message.php b/src/LaravelEcho/WebSocket/Message.php index ad089d2..5617f59 100644 --- a/src/LaravelEcho/WebSocket/Message.php +++ b/src/LaravelEcho/WebSocket/Message.php @@ -3,6 +3,7 @@ namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket; use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager; +use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard; use Ratchet\ConnectionInterface; use stdClass; @@ -29,6 +30,8 @@ class Message implements RespondableMessage public function respond() { if (starts_with($this->payload->event, 'client-')) { + Dashboard::clientMessage($this->connection, $this->payload); + $channel = $this->channelManager->find($this->connection->client->appId, $this->payload->channel); optional($channel)->broadcast($this->payload); diff --git a/src/LaravelEcho/WebSocket/PusherServer.php b/src/LaravelEcho/WebSocket/PusherServer.php index b1024c1..b81127e 100644 --- a/src/LaravelEcho/WebSocket/PusherServer.php +++ b/src/LaravelEcho/WebSocket/PusherServer.php @@ -2,6 +2,7 @@ namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket; +use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard; use Exception; use Ratchet\ConnectionInterface; use Ratchet\RFC6455\Messaging\MessageInterface; @@ -68,6 +69,8 @@ class PusherServer extends WebSocketController protected function establishConnection(ConnectionInterface $connection) { + Dashboard::connection($connection); + $connection->send(json_encode([ 'event' => 'pusher:connection_established', 'data' => json_encode([ @@ -83,20 +86,4 @@ class PusherServer extends WebSocketController $connection->socketId = $socketId; } - - public function log(string $appId, string $type, string $details) - { - $channelId = "private-logger-{$type}"; - - $channel = $this->channelManager->find($appId, $channelId); - - optional($channel)->broadcast([ - 'event' => $type, - 'channel' => $channelId, - 'data' => [ - 'type' => $type, - 'details' => $details - ] - ]); - } } \ No newline at end of file