diff --git a/composer.json b/composer.json
index d80f32ce60..4ebd5c7a32 100644
--- a/composer.json
+++ b/composer.json
@@ -25,11 +25,13 @@
"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/broadcasting": "5.7.*",
+ "illuminate/support": "5.7.*",
"symfony/http-kernel": "~4.0",
+ "pusher/pusher-php-server": "~3.0",
"symfony/psr-http-message-bridge": "^1.1"
},
"require-dev": {
diff --git a/config/websockets.php b/config/websockets.php
index 85bdf28a32..8e1d003c54 100644
--- a/config/websockets.php
+++ b/config/websockets.php
@@ -1,5 +1,6 @@
[
[
+ 'name' => env('APP_NAME'),
'app_id' => env('WEBSOCKETS_APP_ID'),
'app_key' => env('WEBSOCKETS_APP_KEY'),
'app_secret' => env('WEBSOCKETS_APP_SECRET')
@@ -58,4 +60,20 @@
* `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
new file mode 100644
index 0000000000..1fc817f407
--- /dev/null
+++ b/resources/views/console.blade.php
@@ -0,0 +1,176 @@
+
+
+
+ WebSockets Console
+
+
+
+
+
+
+
+
+
+
+
+
+
Events
+
+
+
+ Type |
+ Socket |
+ Details |
+ Time |
+
+
+
+
+ @{{ log.type }} |
+ @{{ log.socketId }} |
+ @{{ log.details }} |
+ @{{ log.time }} |
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ClientProviders/Client.php b/src/ClientProviders/Client.php
index cd3f3c706d..688a3ef63d 100644
--- a/src/ClientProviders/Client.php
+++ b/src/ClientProviders/Client.php
@@ -16,6 +16,9 @@ class Client
/** @var string */
public $appSecret;
+ /** @var string|null */
+ public $name;
+
public static function findByAppId(int $appId)
{
return app(ClientProvider::class)->findByAppId($appId);
@@ -26,7 +29,7 @@ public static function findByAppKey(string $appKey): ?Client
return app(ClientProvider::class)->findByAppKey($appKey);
}
- public function __construct($appId, string $appKey, string $appSecret)
+ public function __construct($appId, string $appKey, string $appSecret, ?string $name)
{
if (!is_numeric($appId)) {
throw InvalidClient::appIdIsNotNumeric($appId);
@@ -45,6 +48,8 @@ public function __construct($appId, string $appKey, string $appSecret)
$this->appKey = $appKey;
$this->appSecret = $appSecret;
+
+ $this->name = $name;
}
diff --git a/src/ClientProviders/ClientProvider.php b/src/ClientProviders/ClientProvider.php
index f363f537de..4f67e0caf7 100644
--- a/src/ClientProviders/ClientProvider.php
+++ b/src/ClientProviders/ClientProvider.php
@@ -8,4 +8,6 @@ interface ClientProvider
public function findByAppId(int $appId): ?Client;
public function findByAppKey(string $appKey): ?Client;
+
+ public function all(): array;
}
\ No newline at end of file
diff --git a/src/ClientProviders/ConfigClientProvider.php b/src/ClientProviders/ConfigClientProvider.php
index 6cf63d1679..6a4653ebb7 100644
--- a/src/ClientProviders/ConfigClientProvider.php
+++ b/src/ClientProviders/ConfigClientProvider.php
@@ -24,6 +24,15 @@ public function findByAppKey(string $appKey): ?Client
return $this->instanciate($clientAttributes);
}
+ public function all(): array
+ {
+ return $this->allClients()
+ ->map(function ($client) {
+ return $this->instanciate($client);
+ })
+ ->toArray();
+ }
+
protected function allClients(): Collection
{
return collect(config('websockets.clients'));
@@ -38,7 +47,8 @@ protected function instanciate(?array $clientAttributes): ?Client
return new Client(
$clientAttributes['app_id'],
$clientAttributes['app_key'],
- $clientAttributes['app_secret']
+ $clientAttributes['app_secret'],
+ $clientAttributes['name'] ?? null
);
}
}
\ 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 0000000000..2dce0f7d3e
--- /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/SendMessage.php b/src/Http/Controllers/SendMessage.php
new file mode 100644
index 0000000000..ff9b39bf44
--- /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/Controllers/ShowConsole.php b/src/Http/Controllers/ShowConsole.php
new file mode 100644
index 0000000000..e8fa6cefe6
--- /dev/null
+++ b/src/Http/Controllers/ShowConsole.php
@@ -0,0 +1,16 @@
+ $clients->all()
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/src/Http/Middleware/Authorize.php b/src/Http/Middleware/Authorize.php
new file mode 100644
index 0000000000..a2d80c4ce1
--- /dev/null
+++ b/src/Http/Middleware/Authorize.php
@@ -0,0 +1,13 @@
+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 d84ea03cdc..0eb98649c2 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 @@ public function subscribe(ConnectionInterface $connection, stdClass $payload)
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 0000000000..8a325e07e4
--- /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/Pusher/Exceptions/InvalidConnectionException.php b/src/LaravelEcho/Pusher/Exceptions/InvalidConnectionException.php
new file mode 100644
index 0000000000..143270caf1
--- /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 76e856fe35..c154d279ed 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/Message.php b/src/LaravelEcho/WebSocket/Message.php
index ad089d2506..5617f59c4a 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 @@ public function __construct(stdClass $payload, ConnectionInterface $connection,
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 f48027fbd8..b81127ec9e 100644
--- a/src/LaravelEcho/WebSocket/PusherServer.php
+++ b/src/LaravelEcho/WebSocket/PusherServer.php
@@ -2,14 +2,15 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\WebSocket;
-use BeyondCode\LaravelWebSockets\ClientProviders\Client;
-use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\PusherException;
-use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\UnknownAppKey;
+use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard;
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;
class PusherServer extends WebSocketController
{
@@ -49,7 +50,6 @@ function onError(ConnectionInterface $connection, Exception $exception)
$exception->getPayload()
));
}
- dump($exception);
}
protected function verifyConnection(ConnectionInterface $connection)
@@ -61,7 +61,7 @@ protected function verifyConnection(ConnectionInterface $connection)
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;
@@ -69,6 +69,8 @@ protected function verifyConnection(ConnectionInterface $connection)
protected function establishConnection(ConnectionInterface $connection)
{
+ Dashboard::connection($connection);
+
$connection->send(json_encode([
'event' => 'pusher:connection_established',
'data' => json_encode([
diff --git a/src/LaravelWebSocketsServiceProvider.php b/src/LaravelWebSocketsServiceProvider.php
index c96fe8ba2d..8d92ab0950 100644
--- a/src/LaravelWebSocketsServiceProvider.php
+++ b/src/LaravelWebSocketsServiceProvider.php
@@ -2,6 +2,8 @@
namespace BeyondCode\LaravelWebSockets;
+use Illuminate\Support\Facades\Gate;
+use Illuminate\Support\Facades\Route;
use BeyondCode\LaravelWebSockets\ClientProviders\ClientProvider;
use Illuminate\Support\ServiceProvider;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
@@ -10,15 +12,39 @@ class LaravelWebSocketsServiceProvider extends ServiceProvider
{
public function boot()
{
+ Route::middlewareGroup('websockets', config('websockets.dashboard.middleware', []));
+
$this->publishes([
__DIR__.'/../config/websockets.php' => base_path('config/websockets.php'),
], 'config');
+ $this->registerRoutes();
+
+ $this->registerDashboardGate();
+
+ $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.dashboard.path'),
+ 'middleware' => 'websockets'
+ ];
+ }
+
public function register()
{
$this->mergeConfigFrom(__DIR__.'/../config/websockets.php', 'websockets');
@@ -35,4 +61,11 @@ public function register()
return app(config('websockets.client_provider'));
});
}
+
+ protected function registerDashboardGate()
+ {
+ Gate::define('viewWebSocketDashboard', function ($user = null) {
+ return app()->environment('local');
+ });
+ }
}