add reverb container, unread attribute in notification pivot table and private channel and event for new register

This commit is contained in:
2026-01-07 16:50:51 +01:00
parent 6eaac7a745
commit 2790026475
33 changed files with 1415 additions and 15 deletions
+12 -5
View File
@@ -37,11 +37,18 @@ SESSION_PATH=/
SESSION_DOMAIN=localhost
SESSION_SECURE_COOKIE=false
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=local
REVERB_APP_KEY=local
REVERB_APP_SECRET=local
REVERB_HOST=reverb
REVERB_PORT=8080
REVERB_SCHEME=http
PUSHER_APP_CLUSTER="mt1"
CACHE_STORE=file
QUEUE_CONNECTION=sync
CACHE_STORE=database
# CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
@@ -68,6 +75,6 @@ AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
SANCTUM_STATEFUL_DOMAINS=localhost,127.0.0.1,localhost:8000
SANCTUM_STATEFUL_DOMAINS=localhost,127.0.0.1,localhost:8000,localhost:5173
TYPESENSE_CONNECTION=typesense
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserCreated implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
/**
* Create a new event instance.
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new privateChannel('admins-private'),
];
}
public function broadcastAs()
{
return 'user.created';
}
}
+5
View File
@@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Events\UserCreated;
use App\Models\Notification;
use App\Models\User;
use Illuminate\Http\JsonResponse;
@@ -51,6 +52,10 @@ class UserController extends Controller
$admins->pluck('id')->toArray()
);
Log::info('Broadcasting UserCreated event', ['user_id' => $user->id]);
broadcast(new UserCreated($user));
Log::info('Event broadcasted');
return response()->json(['message' => 'User created successfully']);
}
+3 -1
View File
@@ -17,6 +17,8 @@ class Notification extends Model
'notification_user',
'notification_id',
'user_id'
)->withTimestamps();
)
->withPivot('unread')
->withTimestamps();
}
}
+4 -2
View File
@@ -61,7 +61,9 @@ class User extends Authenticatable
Notification::class,
'notification_user',
'user_id',
'notification_id'
)->withTimestamps();
'notification_id',
)
->withPivot('unread')
->withTimestamps();
}
}
+1
View File
@@ -24,5 +24,6 @@ class AppServiceProvider extends ServiceProvider
Route::middleware('api')->prefix('api')->group(base_path('routes/auth.php'));
Route::middleware('api')->prefix('api')->group(base_path('routes/events.php'));
Route::middleware('api')->prefix('api')->group(base_path('routes/notifications.php'));
Route::middleware('api')->prefix('api')->group(base_path('routes/channels.php'));
}
}
@@ -0,0 +1,16 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
public function boot(): void
{
Broadcast::routes(['middleware' => ['web', 'auth:sanctum']]);
require base_path('routes/channels.php');
}
}
+1
View File
@@ -9,6 +9,7 @@ return Application::configure(basePath: dirname(__DIR__))
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
channels: __DIR__.'/../routes/channels.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
Regular → Executable
View File
+1
View File
@@ -2,5 +2,6 @@
return [
App\Providers\AppServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\GateServiceProvider::class,
];
+3 -2
View File
@@ -11,10 +11,11 @@
"require": {
"php": "^8.2",
"laravel/framework": "^12.0",
"laravel/reverb": "^1.0",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.10.1",
"typesense/typesense-php": "*",
"propaganistas/laravel-phone": "^6.0"
"propaganistas/laravel-phone": "^6.0",
"typesense/typesense-php": "*"
},
"require-dev": {
"fakerphp/faker": "^1.23",
Generated
+1006 -1
View File
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "reverb", "pusher", "ably", "redis", "log", "null"
|
*/
'default' => env('BROADCAST_CONNECTION', 'null'),
/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over WebSockets. Samples of
| each available type of connection are provided inside this array.
|
*/
'connections' => [
'reverb' => [
'driver' => 'reverb',
'key' => env('REVERB_APP_KEY'),
'secret' => env('REVERB_APP_SECRET'),
'app_id' => env('REVERB_APP_ID'),
'options' => [
'host' => env('REVERB_HOST'),
'port' => env('REVERB_PORT', 443),
'scheme' => env('REVERB_SCHEME', 'https'),
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
],
'client_options' => [
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
],
],
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
'port' => env('PUSHER_PORT', 443),
'scheme' => env('PUSHER_SCHEME', 'https'),
'encrypted' => true,
'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
],
'client_options' => [
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
],
],
'ably' => [
'driver' => 'ably',
'key' => env('ABLY_KEY'),
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
],
];
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('notification_user', function (Blueprint $table) {
if (!Schema::hasColumn('notification_user', 'unread')) {
$table->boolean('unread')->default(true);
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('notification_user', function (Blueprint $table) {
if (Schema::hasColumn('notification_user', 'unread')) {
$table->dropColumn('unread');
}
});
}
};
+16
View File
@@ -6,6 +6,8 @@ services:
context: .
dockerfile: ./php/Dockerfile
container_name: app-php
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ../:/var/www/app
environment:
@@ -46,6 +48,20 @@ services:
- typesense-data:/data
command: '--data-dir /data --api-key= --enable-cors'
reverb:
build:
context: .
dockerfile: ./php/Dockerfile
container_name: app-reverb
command: php artisan reverb:start --host=0.0.0.0 --port=8080
volumes:
- ../:/var/www/app
ports:
- "8080:8080"
depends_on:
- php
- mariadb
volumes:
dbdata:
typesense-data:
+1 -1
View File
@@ -17,4 +17,4 @@ chmod -R 775 storage bootstrap/cache
composer install --no-interaction --prefer-dist
exec php-fpm
exec "$@"
+1 -1
View File
@@ -2,7 +2,7 @@ FROM php:8.4-fpm
RUN apt-get update && apt-get install -y \
git unzip libzip-dev libpng-dev libonig-dev \
&& docker-php-ext-install pdo_mysql zip
&& docker-php-ext-install pdo_mysql zip pcntl
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
+159
View File
@@ -8,7 +8,9 @@
"@tailwindcss/vite": "^4.0.0",
"axios": "^1.11.0",
"concurrently": "^9.0.1",
"laravel-echo": "^2.2.7",
"laravel-vite-plugin": "^2.0.0",
"pusher-js": "^8.4.0",
"tailwindcss": "^4.0.0",
"vite": "^7.0.7"
}
@@ -826,6 +828,14 @@
"win32"
]
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/@tailwindcss/node": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz",
@@ -1282,6 +1292,25 @@
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -1324,6 +1353,32 @@
"dev": true,
"license": "MIT"
},
"node_modules/engine.io-client": {
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1",
"xmlhttprequest-ssl": "~2.1.1"
}
},
"node_modules/engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/enhanced-resolve": {
"version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
@@ -1661,6 +1716,20 @@
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/laravel-echo": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-2.2.7.tgz",
"integrity": "sha512-MgD3ZFXqH5OOVdRjxNHPyQ0ijRr5+nLr7MtyF2XP+kRfhl+Qaa7qVzbtCn1HMgXuTn4SWH6ivn4qWVLlvRl8kg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20"
},
"peerDependencies": {
"pusher-js": "*",
"socket.io-client": "*"
}
},
"node_modules/laravel-vite-plugin": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-2.0.1.tgz",
@@ -1986,6 +2055,14 @@
"node": ">= 18"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -2061,6 +2138,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/pusher-js": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.4.0.tgz",
"integrity": "sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"tweetnacl": "^1.0.3"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -2136,6 +2223,38 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/socket.io-client": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -2262,6 +2381,13 @@
"dev": true,
"license": "0BSD"
},
"node_modules/tweetnacl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
"dev": true,
"license": "Unlicense"
},
"node_modules/vite": {
"version": "7.1.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz",
@@ -2379,6 +2505,39 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+2
View File
@@ -10,7 +10,9 @@
"@tailwindcss/vite": "^4.0.0",
"axios": "^1.11.0",
"concurrently": "^9.0.1",
"laravel-echo": "^2.2.7",
"laravel-vite-plugin": "^2.0.0",
"pusher-js": "^8.4.0",
"tailwindcss": "^4.0.0",
"vite": "^7.0.7"
}
+1 -1
View File
@@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="logoIcone.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Comité des fêtes de Beaupont</title>
<script type="module" crossorigin src="/assets/index-4OUq_vxg.js"></script>
<script type="module" crossorigin src="/assets/index-CeLi4uhf.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DBz-GVvs.css">
</head>
<body>
+14
View File
@@ -0,0 +1,14 @@
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'reverb',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: import.meta.env.VITE_REVERB_HOST,
wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
+10
View File
@@ -0,0 +1,10 @@
<?php
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('admins-private', function ($user) {
return in_array($user->role, [1, 2]);
});
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
View File
View File
View File
View File
Regular → Executable
View File
+1 -1
View File
@@ -1,4 +1,4 @@
headers {
Accept: application/json
X-XSRF-TOKEN: eyJpdiI6IkdOazgycU5wUThPNWU4RTBTQ1JaRWc9PSIsInZhbHVlIjoidFZQQXh1WStoQ3pwTXpsQnBFNklLb1puRGpEMHp4aS9lM2hXYTN2SmtLN28zU3V3cEFpclR6dXhMMmN6eDBFcFB3VHdGcG8xN1d2a2RMa05DNTFBcTR6T0JNUFllUWtBanY5clMwb1JpL3Vjb211YW5YRk5tTG8yLzFRWkQ4SEIiLCJtYWMiOiJmYjRhMjczNDY2NzZkNmViYzg5ODJhMDViZjc3NTRmZjU5OWUxZGRlOTRlNGVjZGJhZDAyODY3NTAyNDNmZWM0IiwidGFnIjoiIn0=
X-XSRF-TOKEN: eyJpdiI6IjZ3OWo1dG43ZFZrWkI0akozSkZOUnc9PSIsInZhbHVlIjoieE1kNTBPSGlReXlhOHY0MlF6MDUxa2VDc3c3ajdhVUFEUGdOZDNOcUJoZGdwaFBWQUZ2a2g3a1FOdnRwTnVNcW44V095bGlIaEkwZEZiUnIzWElVV29nNmZPZVNpSzlXU2FJMS9TOERqRWtOVStxVTEvVWZLQVFpQ2VzTlA1bkEiLCJtYWMiOiJiZjNkODFkMjk2YTZiNDU4ODlkMTAxNzU0ZTdlOTdmODU3YjEyNzI1ODk1ZjM0ZDVhN2U2YThhZmMwYWY4NjZhIiwidGFnIjoiIn0=
}