Socket in Laravel with Reverb

WhoAmI => notes.sohag.pro/author
Search for a command to run...

WhoAmI => notes.sohag.pro/author
Insightful
In today's digitally driven landscape, the security of your online assets is paramount. Whether you're running a personal blog, an e-commerce site, or a web application, safeguarding sensitive information and establishing trust with your users is non...
I had a solid list of reasons my life wasn't moving faster, until a grainy old lecture pointed out the one name missing from it

The finale isn't a victory lap. It's the story of the control I shipped that did nothing, the footgun still sitting in my demo, and the handful of things I'd keep exactly as they are.

How do you hold a large payment for a second pair of eyes without ever letting the unapproved money touch a balance, and how do you stream that decision to the outside world without standing up a broker?

How do you show the total under a parent account when the whole system refuses to store a balance? A recursive query, a trigger that refuses to draw a circle, and a rule about what actually has to sum to zero.

Every time I wanted to change an FX rate I had to edit a file on the server and restart the app. So I moved rates and markup into a live admin API, and then audited it hard enough to find the bug that quietly undid the whole thing.

Documentation : https://laravel.com/docs/reverb
// Laravel 11
php artisan install:broadcasting
Documentation: https://laravel.com/docs/11.x/broadcasting#client-side-installation
npm install --save-dev laravel-echo pusher-js
BROADCAST_DRIVER=reverb
REVERB_APP_ID=22..88
REVERB_APP_KEY=eql...x52m
REVERB_APP_SECRET=v3kdi...76dc
REVERB_HOST="sohag.pro"
REVERB_PORT=443
REVERB_SCHEME=https
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
with ShouldBroadcast
<?php
namespace App\Events;
use App\Models\Car;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class TestEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(public Car $car)
{
//
}
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new Channel('car.' . $this->car->id),
];
}
}
$car = Car::find(7);
TestEvent::dispatch($car);
in resources/js/bootstrap.js
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,
wssPort: import.meta.env.VITE_REVERB_PORT,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
const [isSubscribed, setIsSubscribed] = useState(false);
useEffect(() => {
if (!isSubscribed && car && car.id) {
Echo.channel(`car.${car.id}`).listen("TestEvent", (e) => {
console.log(e.car);
setCar(e.car);
setLatestBid(e.car.highest_bid.amount);
toast.success("New Bid placed of $" + parseInt(e.car.highest_bid.amount));
});
setIsSubscribed(true); // Set subscribed flag to true
}
// Cleanup subscription on component unmount
return () => {
if (isSubscribed && car && car.id) {
Echo.leave(`car.${car.id}`);
setIsSubscribed(false); // Reset subscribed flag
}
};
}, [isSubscribed]);
const receiverName = '{{ $receiver->name }}';
const senderName = '{{ auth()->user()->name }}';
const receiverId = {{ $receiver->id }};
const senderId = {{ auth()->id() }};
const messagesDiv = document.getElementById('messages');
window.addEventListener('load', () => {
window.Echo.private(`chat.{{ auth()->id() }}.{{ $receiver->id }}`)
.listen('TestEvent', (e) => {
console.log(e);
let pushMessage = e.message.message;
let image = e.message.image;
let time = e.message.created_at;
// format as 12 hour time format with date and time
time = new Date(time).toLocaleString('en-US', {
timeZone: '{{ config('app.timezone') }}',
hour: 'numeric',
minute: 'numeric',
hour12: true,
month: 'short',
day: 'numeric'
});
document.getElementById('messages').innerHTML += generateBlock(pushMessage, image, receiverName,
time);
// messages div scroll to bottom
messagesDiv.scrollTop = messagesDiv.scrollHeight;
});
window.Echo.private(`chat.{{ $receiver->id }}.{{ auth()->id() }}`)
.listen('TestEvent', (e) => {
console.log(e);
let pushMessage = e.message.message;
let image = e.message.image;
let time = e.message.created_at;
// format as 12 hour time format with date and time
time = new Date(time).toLocaleString('en-US', {
timeZone: '{{ config('app.timezone') }}',
hour: 'numeric',
minute: 'numeric',
hour12: true,
month: 'short',
day: 'numeric'
});
document.getElementById('messages').innerHTML += generateBlock(pushMessage, image, senderName,
time, true);
// messages div scroll to bottom
messagesDiv.scrollTop = messagesDiv.scrollHeight;
});
// messages div scroll to bottom
messagesDiv.scrollTop = messagesDiv.scrollHeight;
})
how to setup supervisor? https://notes.sohag.pro/supervisor-setup-for-laravel-application
Worker conf:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/laravel/artisan reverb:start
user=www-data
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/html/laravel/storage/logs/reverb.log
stopwaitsecs=3600
server {
root /var/www/html/laravel/public;
index index.php;
server_name example.com;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location /app {
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Scheme $scheme;
proxy_set_header SERVER_PORT $server_port;
proxy_set_header REMOTE_ADDR $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://0.0.0.0:8080;
}
location ~ /\.(?!well-known).* {
deny all;
}
}