meita / realtime-notifications
Fast, reliable realtime notifications over SSE backed by JSON/TXT files in storage (per user_id).
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/meita/realtime-notifications
Requires
- php: ^8.1
- illuminate/contracts: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/routing: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.5|^11.0
README
Author: Mohamed A. Eita (maa1987@hotmail.com)
File-backed, reliable realtime notifications over Server-Sent Events (SSE). Notifications are stored per user_id as JSON/TXT under storage, moved to inflight/ when streamed, and deleted after ACK.
Works standalone in pure PHP or plugged into Laravel (10/11/12). Comes with a Laravel bridge (routes + provider) and a plain PHP adapter.
Features
- SSE push (
event: meita.notification) with auto ACK support - Storage as files: JSON (structured) or TXT (plain)
- Per user folders:
meita/notifications/{pending|inflight}/{userId}/... - Keyword filtering for JSON (
?keywords=order,urgent) - Lease handling: pending → inflight until ACK; reclaim stale inflight files
- Auth via signed token (or Laravel auth/session when using the bridge)
Requirements
- PHP
^8.1 - Optional Laravel bridge: Laravel
^10|^11|^12
Install
composer require meita/realtime-notifications
For a local path repo (adjust the path as needed):
{
"repositories": [
{ "type": "path", "url": "../realtime-notifications", "options": { "symlink": true } }
],
"require": { "meita/realtime-notifications": "*" }
}
Storage layout
storage/app/meita/notifications/
pending/{userId}/
inflight/{userId}/
Flow: push → pending → stream moves to inflight → ACK deletes.
Usage with Laravel
- Install the package (auto-discovery registers the provider and facade).
- Optional publish:
php artisan vendor:publish --tag=meita-notifications-config php artisan vendor:publish --tag=meita-notifications-assets --force
Push from PHP
use Meita\RealtimeNotifications\Facades\MeitaNotifications; MeitaNotifications::push(42, 'Order created successfully', [ 'title' => 'Orders', 'type' => 'success', 'keywords' => ['order', 'payments'], 'data' => ['order_id' => 999], 'format' => 'json', // or 'txt' 'ttl_seconds' => 3600, ]);
Generate a token (for SSE/ACK)
$token = MeitaNotifications::tokenForUser(42);
Endpoints (provided by the bridge)
- Stream (SSE):
GET /api/meita/notifications/stream/{userId}?meita_token=... - ACK:
POST /api/meita/notifications/ack/{userId}?meita_token=...
Browser (vanilla JS)
<script> const userId = 42; const token = 'PUT_TOKEN_HERE'; const streamUrl = `/api/meita/notifications/stream/${userId}?meita_token=${encodeURIComponent(token)}`; const ackUrl = `/api/meita/notifications/ack/${userId}?meita_token=${encodeURIComponent(token)}`; const es = new EventSource(streamUrl); es.addEventListener('meita.notification', async (event) => { const n = JSON.parse(event.data); console.log('NOTIF', n); await fetch(ackUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file: n.file, id: n.id }), }); }); </script>
Using the shipped JS client
php artisan vendor:publish --tag=meita-notifications-assets --force
<script type="module"> import { MeitaNotificationsClient } from '/vendor/meita/meita-notifications.js'; const userId = 42; const token = 'PUT_TOKEN_HERE'; const client = new MeitaNotificationsClient({ userId, streamUrl: `/api/meita/notifications/stream/${userId}?meita_token=${encodeURIComponent(token)}`, ackUrl: `/api/meita/notifications/ack/${userId}?meita_token=${encodeURIComponent(token)}`, onNotification: async (n) => console.log('NOTIF', n), onError: (e) => console.error('SSE error', e), }); client.connect(); </script>
Usage in pure PHP (or any framework)
Use the core manager with the local filesystem adapter:
use Meita\RealtimeNotifications\Adapters\LocalFilesystemAdapter; use Meita\RealtimeNotifications\MeitaNotificationsManager; $config = [ 'disk_root' => __DIR__.'/storage', // where files are stored 'base_dir' => 'meita/notifications', 'secret_key' => 'base64:your-key-or-plain-string', 'auth' => ['token_ttl_seconds' => 3600], ]; $manager = new MeitaNotificationsManager( $config, new LocalFilesystemAdapter($config['disk_root']) ); // push $ref = $manager->push('user-123', 'Hello standalone', ['keywords' => ['demo']]); // token (sign/verify yourself in your HTTP layer) $token = $manager->tokenForUser('user-123');
Build your own HTTP endpoints (any framework) mirroring the Laravel controllers:
- SSE stream: emit
event: meita.notificationwith JSON payload fromreserveNext(...), move file to inflight,flush(), heartbeat: ping. - ACK: delete by
fileoridusing$manager->store()->ackMany(...).
Configuration (ENV for Laravel bridge)
MEITA_NOTIFICATIONS_DISK=local
MEITA_NOTIFICATIONS_BASE_DIR=meita/notifications
MEITA_NOTIFICATIONS_SECRET=base64:...or key...
MEITA_NOTIFICATIONS_AUTH_MODE=token_or_auth # token_or_auth | token | auth | none
MEITA_NOTIFICATIONS_TOKEN_TTL=3600
MEITA_NOTIFICATIONS_TOKEN_PARAM=meita_token
MEITA_NOTIFICATIONS_SSE_RETRY_MS=2000
MEITA_NOTIFICATIONS_POLL_MS=500
MEITA_NOTIFICATIONS_HEARTBEAT_SECONDS=15
MEITA_NOTIFICATIONS_LEASE_TTL_SECONDS=30
MEITA_NOTIFICATIONS_RECLAIM_ON_STREAM_START=true
Tests
composer install
composer test
Tests are under tests/Feature/MeitaRealtimeNotificationsTest.php.
License
MIT