Skip to content
This repository was archived by the owner on Feb 7, 2024. It is now read-only.

Add Debug Dashboard #1

Merged
merged 5 commits into from
Nov 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
18 changes: 18 additions & 0 deletions config/websockets.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use BeyondCode\LaravelWebSockets\Http\Middleware\Authorize;
use BeyondCode\LaravelWebSockets\ClientProviders\ConfigClientProvider;

return [
Expand Down Expand Up @@ -44,6 +45,7 @@
*/
'clients' => [
[
'name' => env('APP_NAME'),
'app_id' => env('WEBSOCKETS_APP_ID'),
'app_key' => env('WEBSOCKETS_APP_KEY'),
'app_secret' => env('WEBSOCKETS_APP_SECRET')
Expand All @@ -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,
],

]
];
176 changes: 176 additions & 0 deletions resources/views/console.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>WebSockets Console</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://js.pusher.com/4.3/pusher.min.js"></script>
</head>

<body>
<div class="container" id="app">
<div class="card col-xs-12">
<div class="card-header">
<form id="connect" class="form-inline" role="form">
<label class="my-1 mr-2" for="client">Client:</label>
<select class="form-control form-control-sm mr-2" name="client" id="client" v-model="client">
<option v-for="client in clients" :value="client">@{{ client.name }}</option>
</select>
<label class="my-1 mr-2" for="client">Port:</label>
<input class="form-control form-control-sm mr-2" v-model="port" placeholder="Port">
<button v-if="! connected" type="submit" @click.prevent="connect" class="mr-2 btn btn-sm btn-primary">Connect</button>
<button v-if="connected" type="submit" @click.prevent="disconnect" class="btn btn-sm btn-danger">Disconnect</button>
</form>
<div id="status"></div>
</div>
<div class="card-body">
<div v-if="connected">
<h4>Event Creator</h4>
<form>
<div class="row">
<div class="col">
<input type="text" class="form-control" v-model="form.channel" placeholder="Channel">
</div>
<div class="col">
<input type="text" class="form-control" v-model="form.event" placeholder="Event">
</div>
</div>
<div class="row mt-3">
<div class="col">
<div class="form-group">
<textarea placeholder="Data" v-model="form.data" class="form-control" id="data" rows="3"></textarea>
</div>
</div>
</div>
<div class="row text-right">
<div class="col">
<button type="submit" @click.prevent="sendEvent" class="btn btn-sm btn-primary">Send event</button>
</div>
</div>
</form>
</div>
<h4>Events</h4>
<table id="events" class="table table-striped table-hover">
<thead>
<tr>
<th>Type</th>
<th>Socket</th>
<th>Details</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<tr v-for="log in logs.slice().reverse()">
<td><span class="badge" :class="getBadgeClass(log)">@{{ log.type }}</span></td>
<td>@{{ log.socketId }}</td>
<td>@{{ log.details }}</td>
<td>@{{ log.time }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',

data: {
connected: false,
pusher: null,
port: 6001,
client: null,
clients: {!! json_encode($clients) !!},
form: {
channel: null,
event: null,
data: null
},
logs: [],
},

methods: {
connect() {
this.pusher = new Pusher(this.client.appKey, {
wsHost: window.location.hostname,
wsPort: this.port,
authEndpoint: '{{ config('websockets.dashboard.path') }}/auth',
enabledTransports: ['ws', 'flash']
});

this.pusher.connection.bind('state_change', states => {
$('div#status').text("Channels current state is " + states.current);
});

this.pusher.connection.bind('connected', () => {
this.connected = true;
});

this.pusher.connection.bind('disconnected', () => {
this.connected = false;
this.logs = [];
});

this.subscribeToChannel('disconnection');

this.subscribeToChannel('connection');

this.subscribeToChannel('vacated');

this.subscribeToChannel('occupied');

this.subscribeToChannel('subscribed');

this.subscribeToChannel('client_message');

this.subscribeToChannel('api_message');
},

disconnect() {
this.pusher.disconnect();
},

subscribeToChannel(channel) {
this.pusher.subscribe('{{ \BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard::LOG_CHANNEL_PREFIX }}'+channel)
.bind('log_message', (data) => {
this.logs.push(data);
});
},

getBadgeClass(log) {
if (log.type === 'occupied' || log.type === 'connection') {
return 'badge-primary';
}
if (log.type === 'vacated') {
return 'badge-warning';
}
if (log.type === 'disconnection') {
return 'badge-error';
}
if (log.type === 'api_message') {
return 'badge-info';
}
return 'badge-secondary';
},

sendEvent() {
$.post('{{ config('websockets.dashboard.path') }}/event', {
key: this.client.appKey,
secret: this.client.appSecret,
appId: this.client.appId,
channel: this.form.channel,
event: this.form.event,
data: this.form.data,
}).fail(e => {
alert('Error sending event.');
});
}
}
});
</script>
</body>
</html>
7 changes: 6 additions & 1 deletion src/ClientProviders/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -45,6 +48,8 @@ public function __construct($appId, string $appKey, string $appSecret)
$this->appKey = $appKey;

$this->appSecret = $appSecret;

$this->name = $name;
}


Expand Down
2 changes: 2 additions & 0 deletions src/ClientProviders/ClientProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ interface ClientProvider
public function findByAppId(int $appId): ?Client;

public function findByAppKey(string $appKey): ?Client;

public function all(): array;
}
12 changes: 11 additions & 1 deletion src/ClientProviders/ConfigClientProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
Expand All @@ -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
);
}
}
14 changes: 14 additions & 0 deletions src/Http/Controllers/AuthenticateConsole.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace BeyondCode\LaravelWebsockets\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Contracts\Broadcasting\Broadcaster;

class AuthenticateConsole
{
public function __invoke(Request $request, Broadcaster $broadcaster)
{
return $broadcaster->validAuthenticationResponse($request, []);
}
}
21 changes: 21 additions & 0 deletions src/Http/Controllers/SendMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace BeyondCode\LaravelWebsockets\Http\Controllers;

use Pusher\Pusher;
use Illuminate\Http\Request;
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;

class SendMessage
{
public function __invoke(Request $request)
{
$pusher = new Pusher(
$request->key, $request->secret,
$request->appId, config('broadcasting.connections.pusher.options', [])
);

return (new PusherBroadcaster($pusher))
->broadcast([$request->channel], $request->event, json_decode($request->data, true));
}
}
16 changes: 16 additions & 0 deletions src/Http/Controllers/ShowConsole.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace BeyondCode\LaravelWebsockets\Http\Controllers;

use Illuminate\Http\Request;
use BeyondCode\LaravelWebSockets\ClientProviders\ClientProvider;

class ShowConsole
{
public function __invoke(Request $request, ClientProvider $clients)
{
return view('websockets::console', [
'clients' => $clients->all()
]);
}
}
13 changes: 13 additions & 0 deletions src/Http/Middleware/Authorize.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace BeyondCode\LaravelWebsockets\Http\Middleware;

use Illuminate\Support\Facades\Gate;

class Authorize
{
public function handle($request, $next)
{
return Gate::check('viewWebSocketDashboard') ? $next($request) : abort(403);
}
}
5 changes: 5 additions & 0 deletions src/Http/routes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

Route::get('/', 'ShowConsole');
Route::post('/auth', 'AuthenticateConsole');
Route::post('/event', 'SendMessage');
3 changes: 3 additions & 0 deletions src/LaravelEcho/Http/Controllers/TriggerEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers;

use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Dashboard;
use Illuminate\Http\Request;

class TriggerEvent extends EchoController
Expand All @@ -11,6 +12,8 @@ public function __invoke(Request $request)
$this->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([
Expand Down
Loading