neuralpin/notifyli

Websocket communication app

Maintainers

Package info

github.com/ulisesrendon/notifyli

Type:project

pkg:composer/neuralpin/notifyli

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.3 2026-03-06 22:57 UTC

This package is auto-updated.

Last update: 2026-03-09 14:30:39 UTC


README

A WebSocket-based real-time messaging system that enables users to communicate in private and public channels. Built-in Redis broker allows external systems to send notifications to specific users in their designated channels.

Overview

Primary Goal: Enable real-time user-to-user communication in private/public channels via WebSocket.

Secondary Feature: Redis-based notification broker for sending automated messages from external systems to connected users.

Key Architecture

  • User Identification: Each user must identify themselves with a user_id and connect to a specific room (channel)
  • Private Channels: Users typically connect to personal /user/{user_id} rooms to receive notifications
  • Public Channels: Users can join shared rooms for group communication
  • External Integration: Third-party systems orchestrate notifications via Redis broker, targeting specific users and channels

Important: The UI MUST initialize with a user_id and room before the WebSocket connection is established. This allows the system to:

  1. Route direct notifications to the correct user
  2. Allow external systems to target specific users for automated notifications
  3. Maintain user state in Redis for offline notification handling

Example Implementation Files

This repository includes three example implementations that demonstrate how to integrate the system:

  • server.php - Example WebSocket server initialization. Demonstrates how to configure and start the messaging server with your environment settings.

  • notification-bridge.php - Example notification bridge implementation. Shows how to listen to Redis pub/sub and forward notifications to connected users via WebSocket.

  • public/index.php - Example client UI implementation. Demonstrates user authentication with user_id, room selection, and real-time message handling.

These are reference implementations. Adapt them to your specific infrastructure and requirements.

Cloud Production Deployment

This guide covers deploying all three components in a production cloud environment.

Prerequisites

  • Cloud server (Ubuntu 20.04+ recommended) with root access
  • PHP 8.2 or higher installed
  • Redis server installed and running
  • Nginx web server
  • Domain name with DNS configured
  • SSL certificates (Let's Encrypt recommended)

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                    Nginx (Port 80/443)                  │
│  ┌──────────────────────┬───────────────────────────┐  │
│  │   Web UI (PHP-FPM)   │   WebSocket Proxy (7000)  │  │
│  │  public/index.php    │   → server.php daemon     │  │
│  └──────────────────────┴───────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                              │
                              ├──►  Redis Pub/Sub
                              │
                 ┌────────────┴────────────┐
                 │ notification-bridge.php │
                 │  (Notification Bridge)  │
                 └─────────────────────────┘

Step 1: Server Preparation

# Update system
sudo apt update && sudo apt upgrade -y

# Install dependencies
sudo apt install -y nginx php8.2-fpm php8.2-cli php8.2-redis php8.2-mbstring \
    php8.2-xml php8.2-curl redis-server git composer

# Enable Redis
sudo systemctl enable redis-server
sudo systemctl start redis-server

# Verify Redis is running
redis-cli ping
# Expected: PONG

Step 2: Deploy Application Code

# Create application directory
sudo mkdir -p /var/www/notifyli
sudo chown -R www-data:www-data /var/www/notifyli

# Clone or copy your application
cd /var/www/notifyli
# ... upload your code here ...

# Install PHP dependencies
composer install --no-dev --optimize-autoloader

# Set proper permissions
sudo chown -R www-data:www-data /var/www/notifyli
sudo chmod -R 755 /var/www/notifyli

Step 3: Configure Environment

Create /var/www/notifyli/.env:

# WebSocket Server Configuration
APP_WS_SERVER_PROTOCOL=wss  # Use wss for secure WebSocket
APP_WS_SERVER_DOMAIN=your-domain.com
APP_PORT=7000

# Redis Configuration
REDIS_ENABLED=true
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=

# Notification Bridge Configuration
BOT_NAME=NotificationBot
BOT_ROOM=1

Step 4: Configure Nginx (Web UI + WebSocket Proxy)

Create /etc/nginx/sites-available/notifyli.conf:

# WebSocket upgrade handling
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

# HTTP to HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name your-domain.com;
    
    return 301 https://$server_name$request_uri;
}

# Main HTTPS server
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name your-domain.com;

    # SSL Configuration (Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Logs
    access_log /var/log/nginx/notifyli-access.log;
    error_log /var/log/nginx/notifyli-error.log;

    # Document root for web UI
    root /var/www/notifyli/public;
    index index.php index.html;

    # Serve static files and PHP
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # PHP-FPM Configuration (Web UI Client)
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        
        # Increase timeouts for long-polling if needed
        fastcgi_read_timeout 300;
    }

    # WebSocket Proxy to server.php daemon
    location /ws {
        proxy_pass http://127.0.0.1:7000;
        proxy_http_version 1.1;
        
        # WebSocket upgrade headers
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        
        # Standard proxy headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Timeouts for long-lived connections
        proxy_connect_timeout 7d;
        proxy_send_timeout 7d;
        proxy_read_timeout 7d;
    }

    # Deny access to sensitive files
    location ~ /\. {
        deny all;
    }

    location ~ /\.env {
        deny all;
    }
}

Enable the site:

# Enable site
sudo ln -s /etc/nginx/sites-available/notifyli.conf /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Reload nginx
sudo systemctl reload nginx

Step 5: SSL Certificate (Let's Encrypt)

# Install certbot
sudo apt install -y certbot python3-certbot-nginx

# Obtain certificate (interactive)
sudo certbot --nginx -d your-domain.com

# Auto-renewal is configured automatically
# Test renewal
sudo certbot renew --dry-run

Step 6: Configure Firewall

# Allow necessary ports
sudo ufw allow 22/tcp      # SSH
sudo ufw allow 80/tcp      # HTTP
sudo ufw allow 443/tcp     # HTTPS
sudo ufw allow 7000/tcp    # WebSocket (if direct access needed)

# Enable firewall
sudo ufw enable
sudo ufw status

Step 7: Create Log Directories

# Create log directory
sudo mkdir -p /var/log/notifyli

# Set permissions
sudo chown -R www-data:www-data /var/log/notifyli
sudo chmod 755 /var/log/notifyli

Step 8: Configure Supervisor

Install Supervisor:

sudo apt install -y supervisor

Create /etc/supervisor/conf.d/notifyli.conf:

[program:notifyli-websocket]
command=/usr/bin/php -q /var/www/notifyli/server.php
directory=/var/www/notifyli
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/notifyli/websocket-server.log
stderr_logfile=/var/log/notifyli/websocket-server-error.log
stopasgroup=true
killasgroup=true

[program:notifyli-notification-bridge]
command=/usr/bin/php -q /var/www/notifyli/notification-bridge.php
directory=/var/www/notifyli
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/notifyli/notification-bridge.log
stderr_logfile=/var/log/notifyli/notification-bridge-error.log
stopasgroup=true
killasgroup=true

Step 9: Enable and Start Services

# Reload supervisor
sudo supervisorctl reread
sudo supervisorctl update

# Start programs
sudo supervisorctl start notifyli-websocket
sudo supervisorctl start notifyli-notification-bridge

# Check status
sudo supervisorctl status

Step 10: Verify Deployment

# Check WebSocket server is listening
sudo netstat -tulpn | grep :7000

# Check logs
sudo supervisorctl tail -f notifyli-websocket
sudo supervisorctl tail -f notifyli-notification-bridge

# Or check log files
sudo tail -f /var/log/notifyli/websocket-server.log
sudo tail -f /var/log/notifyli/notification-bridge.log

# Test Redis connection
redis-cli ping

# Monitor Redis pub/sub
redis-cli SUBSCRIBE notifyli:messages

Managing Services

# Start services
sudo supervisorctl start notifyli-websocket notifyli-notification-bridge

# Stop services
sudo supervisorctl stop notifyli-websocket notifyli-notification-bridge

# Restart services
sudo supervisorctl restart notifyli-websocket notifyli-notification-bridge

# View logs
sudo tail -n 100 /var/log/notifyli/websocket-server.log
sudo tail -n 100 /var/log/notifyli/notification-bridge.log

# Follow logs in real-time
sudo supervisorctl tail -f notifyli-websocket
sudo supervisorctl tail -f notifyli-notification-bridge

Maintenance & Monitoring

Daily Restart (Required)

Create a cron job to restart services daily at 3 AM to mitigate PHP daemon memory growth over time:

sudo crontab -e

Add:

# Restart Notifyli services daily at 3 AM
0 3 * * * supervisorctl restart notifyli-websocket notifyli-notification-bridge

Log Rotation

Create /etc/logrotate.d/notifyli:

/var/log/notifyli/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0644 www-data www-data
    sharedscripts
    postrotate
        supervisorctl restart notifyli-websocket > /dev/null 2>&1 || true
        supervisorctl restart notifyli-notification-bridge > /dev/null 2>&1 || true
    endscript
}

Monitoring

# Check service health
supervisorctl status notifyli-websocket
supervisorctl status notifyli-notification-bridge

# Monitor resource usage
top -p $(pgrep -f "server.php")
top -p $(pgrep -f "notification-bridge.php")

# Check Redis memory usage
redis-cli INFO memory

# Monitor active connections
redis-cli CLIENT LIST

Integration Guide

Client UI Integration (Example: public/index.php)

The example UI demonstrates key integration points:

Required Fields:

  • user_id: Unique identifier for the user (required for targeted notifications)
  • room: Channel identifier (e.g., "lobby", "/user/123")
  • name: Display name for messages

WebSocket Connection:

const ws = new WebSocket('wss://your-domain.com/ws');

ws.onopen = () => {
    // Send initial authentication/join message
    ws.send(JSON.stringify({
        type: 'usermsg',
        user_id: userId,
        room: roomId,
        name: userName,
        message: 'joined'
    }));
};

ws.onmessage = (event) => {
    const data = JSON.parse(event.data);
    
    // Handle direct notifications
    if (data.type === 'direct_message') {
        showNotification(data.message, data.name);
    } else {
        // Handle regular chat messages
        displayMessage(data.message, data.name);
    }
};

Message Types:

Regular chat message (user to user):

{
  "type": "usermsg",
  "user_id": 123,
  "room": "lobby",
  "message": "Hello!",
  "name": "Alice"
}

Direct notification (system to user):

{
  "type": "direct_message",
  "user_id": 123,
  "message": "Order shipped!",
  "from": "OrderSystem"
}

External System Integration (Publishing Notifications)

External applications can send notifications to connected users via Redis pub/sub.

PHP Integration:

<?php
require 'vendor/autoload.php';

use Neuralpin\Notifyli\Services\RedisMessageBroker;

// Initialize broker
$broker = new RedisMessageBroker(
    enabled: true,
    host: '127.0.0.1',
    port: 6379,
    password: null
);

// Send notification to specific user
$broker->publish(
    userId: 123,
    room: '/user/123',  // Private notification channel
    message: 'Your order #12345 has been shipped!',
    from: 'OrderSystem'
);

// Check if user is currently connected
if ($broker->isUserConnected(123, '/user/123')) {
    echo "User is online - notification delivered\n";
} else {
    echo "User is offline - notification queued\n";
}

Direct Redis Integration (Any Language):

# Using redis-cli
redis-cli PUBLISH notifyli:messages '{"user_id":123,"room":"/user/123","message":"Hello!","from":"System"}'
# Python example
import redis
import json

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

notification = {
    'user_id': 123,
    'room': '/user/123',
    'message': 'Your order has been shipped!',
    'from': 'OrderSystem',
}

r.publish('notifyli:messages', json.dumps(notification))
// Node.js example
const redis = require('redis');
const publisher = redis.createClient();

await publisher.connect();

const notification = {
  user_id: 123,
  room: '/user/123',
  message: 'Your order has been shipped!',
  from: 'OrderSystem'
};

await publisher.publish('notifyli:messages', JSON.stringify(notification));

Message Format:

Field Type Required Description
user_id int Target user ID
room string Target channel (e.g., "/user/123" for private)
message string Notification content
from string Sender identifier (default: "system")
timestamp int Unix timestamp

External Redis Check by client_id

If your external system is connected to the same Redis instance, you can detect active users by searching client_id inside the room hashes (notifyli:connections:{room}).

Single room check:

redis-cli HGETALL notifyli:connections:/user/123 | grep '"client_id":"abc123"'

All rooms check (returns matching room key, user field, and payload):

redis-cli EVAL "
local out = {}
local cursor = '0'
repeat
    local scan = redis.call('SCAN', cursor, 'MATCH', 'notifyli:connections:*', 'COUNT', 100)
    cursor = scan[1]
    for _, key in ipairs(scan[2]) do
        local entries = redis.call('HGETALL', key)
        for i = 1, #entries, 2 do
            local field = entries[i]
            local value = entries[i + 1]
            if string.find(value, '\"client_id\":\"abc123\"', 1, true) then
                table.insert(out, key)
                table.insert(out, field)
                table.insert(out, value)
            end
        end
    end
until cursor == '0'
return out
" 0

If this returns data, the user session with that client_id is currently connected.

Testing Notifications (console/send-notification.php)

For development and testing:

# Send test notification
php console/send-notification.php -u 123 -r "/user/123" -m "Test message" -f "TestBot"

# Interactive mode (prompts for details)
php console/send-notification.php -u 123

# Options:
#   -u, --user    User ID (required)
#   -r, --room    Room/channel (default: "1")
#   -m, --message Message content
#   -f, --from    Sender name (default: "Test Bot")
#   -h, --help    Show help

Production Operations

Service Management

# Start services
sudo supervisorctl start notifyli-websocket notifyli-notification-bridge

# Stop services
sudo supervisorctl stop notifyli-websocket notifyli-notification-bridge

# Restart services
sudo supervisorctl restart notifyli-websocket notifyli-notification-bridge

# Check service status
sudo supervisorctl status notifyli-websocket
sudo supervisorctl status notifyli-notification-bridge

Monitoring

Check Service Health:

# Check if services are running
supervisorctl status notifyli-websocket
supervisorctl status notifyli-notification-bridge

# View service logs
sudo tail -n 100 /var/log/notifyli/websocket-server.log
sudo tail -n 100 /var/log/notifyli/notification-bridge.log

# Follow logs in real-time
sudo supervisorctl tail -f notifyli-websocket
sudo supervisorctl tail -f notifyli-notification-bridge

Monitor Redis:

# Check Redis connection
redis-cli ping
# Expected: PONG

# View connected users in a room
redis-cli HGETALL notifyli:connections:1

# Monitor pub/sub messages in real-time
redis-cli SUBSCRIBE notifyli:messages

# Check Redis memory usage
redis-cli INFO memory

# List active client connections
redis-cli CLIENT LIST

Monitor WebSocket Server:

# Check if port 7000 is listening
sudo netstat -tulpn | grep :7000
# Or
sudo ss -tulpn | grep :7000

# Check process resource usage
top -p $(pgrep -f "server.php")
ps aux | grep "server.php"

Monitor Nginx:

# Check nginx status
sudo systemctl status nginx

# View access logs
sudo tail -f /var/log/nginx/notifyli-access.log

# View error logs
sudo tail -f /var/log/nginx/notifyli-error.log

# Test configuration
sudo nginx -t

# Reload nginx configuration
sudo systemctl reload nginx

Troubleshooting

WebSocket Server Won't Start:

# Check for port conflicts
sudo lsof -i :7000

# Check logs for errors
sudo tail -n 50 /var/log/notifyli/websocket-server-error.log

# Verify Redis is running
systemctl status redis-server

# Check application logs
sudo tail -f /var/log/notifyli/websocket-server-error.log

Notification Bridge Won't Connect:

# Ensure WebSocket server is running first
supervisorctl status notifyli-websocket

# Check bridge logs
sudo tail -n 50 /var/log/notifyli/notification-bridge-error.log

# Verify Redis connectivity
redis-cli ping

# Check application logs
sudo tail -f /var/log/notifyli/notification-bridge-error.log

Notifications Not Delivered:

# 1. Verify user is connected
redis-cli HGETALL notifyli:connections:1

# 2. Test Redis pub/sub manually
redis-cli PUBLISH notifyli:messages '{"user_id":123,"room":"1","message":"Test","from":"Manual"}'

# 3. Check bridge is subscribed
sudo grep "subscribed" /var/log/notifyli/notification-bridge.log

# 4. Verify correct user_id and room
# User must have connected with matching user_id and room

High Memory Usage:

# Check process memory
ps aux | grep -E "server.php|notification-bridge.php"

# Restart services
sudo supervisorctl restart notifyli-websocket notifyli-notification-bridge

# Monitor Redis memory
redis-cli INFO memory

# Clear old connections if needed (caution: clears all)
redis-cli FLUSHDB

SSL Certificate Issues:

# Check certificate expiry
sudo certbot certificates

# Renew certificates
sudo certbot renew

# Test certificate renewal
sudo certbot renew --dry-run

# Restart nginx after renewal
sudo systemctl reload nginx

Performance Tuning

PHP-FPM Optimization (/etc/php/8.2/fpm/pool.d/www.conf):

pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500

Nginx Optimization:

# Add to http block in /etc/nginx/nginx.conf
worker_processes auto;
worker_connections 4096;
keepalive_timeout 65;

# Enable gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;

Redis Optimization (/etc/redis/redis.conf):

# Increase max connections
maxclients 10000

# Set max memory limit
maxmemory 256mb
maxmemory-policy allkeys-lru

# Persistence (adjust based on needs)
save 900 1
save 300 10
save 60 10000

System Limits (/etc/security/limits.conf):

www-data soft nofile 65536
www-data hard nofile 65536

Backup & Recovery

Backup Redis Data:

# Manual backup
redis-cli SAVE
sudo cp /var/lib/redis/dump.rdb /backup/redis-$(date +%Y%m%d).rdb

# Automated daily backup (cron)
0 4 * * * redis-cli SAVE && cp /var/lib/redis/dump.rdb /backup/redis-$(date +\%Y\%m\%d).rdb

Backup Application:

# Backup application directory
sudo tar -czf /backup/notifyli-$(date +%Y%m%d).tar.gz /var/www/notifyli

API Reference

PHP API (for External Systems)

use Neuralpin\Notifyli\Services\RedisMessageBroker;

$broker = new RedisMessageBroker(
    enabled: true,
    host: '127.0.0.1',
    port: 6379,
    password: null
);

// Publish notification
$broker->publish(userId: 123, room: '/user/123', message: 'Hello', from: 'System');

// Check user connection
$isConnected = $broker->isUserConnected(123, '/user/123');

// Get connection details
$info = $broker->getUserConnection(123, '/user/123');

WebSocket Protocol

Client → Server (user sends message):

{
  "type": "usermsg",
  "user_id": 123,
  "room": "lobby",
  "message": "Hello everyone!",
  "name": "Alice"
}

Server → Client (broadcast or notification):

{
  "message": "Hello everyone!",
  "name": "Alice",
  "timestamp": 1234567890,
  "type": "direct_message"
}

Redis Commands

# View all users in room "1"
redis-cli HGETALL notifyli:connections:1

# Subscribe to messages
redis-cli SUBSCRIBE notifyli:messages

# Publish test message
redis-cli PUBLISH notifyli:messages '{"user_id":123,"room":"1","message":"Test","from":"CLI"}'

# Monitor all Redis commands (debug mode)
redis-cli MONITOR

Project Structure

notifyli/
├── public/
│   ├── index.php          # Example Web UI client
│   ├── main.js            # Client-side WebSocket logic
│   └── main.css           # Styling
├── src/
│   ├── MessagingServer.php              # Main WebSocket server
│   ├── Adapters/
│   │   └── SocketAdapter.php            # Socket handling
│   ├── Clients/
│   │   └── ReusableWebSocketClient.php  # WebSocket client for notification bridge
│   ├── Contracts/
│   │   └── SocketInterface.php          # Interface definitions
│   └── Services/
│       ├── RedisMessageBroker.php       # Redis pub/sub interface
│       ├── RedisStateManager.php        # Connection state tracking
│       ├── RoomManager.php              # Room/channel management
│       └── WebSocketFrameHandler.php    # Protocol handling
├── console/
│   └── send-notification.php   # CLI tool for testing
├── tests/
│   ├── Unit/              # Unit tests
│   └── Integration/       # Integration tests
├── server.php             # Example WebSocket server initialization
├── notification-bridge.php # Example notification bridge implementation
└── composer.json          # PHP dependencies

Architecture Components

1. WebSocket Server (server.php)

  • Handles persistent connections from clients
  • Routes messages between users in same room
  • Tracks connections in Redis for notification routing
  • Non-blocking I/O for high concurrency

2. Notification Bridge (notification-bridge.php)

  • Subscribes to Redis pub/sub channel notifyli:messages
  • Receives notifications from external systems
  • Verifies user is connected before forwarding
  • Maintains persistent WebSocket connection to server
  • Auto-reconnects on connection failure

3. Web UI Client (public/index.php)

  • Authenticates user with user_id and room
  • Establishes WebSocket connection
  • Sends/receives real-time messages
  • Displays notifications from external systems
  • Persists settings in localStorage

4. Redis State Manager

  • Stores active user connections
  • Key format: notifyli:connections:{room} (Hash)
  • Fields: user_{id} → JSON (name, connected_at, client_id)
  • TTL: Removed on disconnect

5. Redis Message Broker

  • Pub/sub channel: notifyli:messages
  • Message routing logic
  • Connection state queries
  • Used by external systems to send notifications

Performance Characteristics

  • Concurrent Connections: 1000+ per server (tested)
  • Message Latency: <10ms (local network)
  • Redis Overhead: ~1ms per pub/sub message
  • Memory: ~2-5KB per WebSocket connection
  • Keepalive Interval: 25 seconds (configurable)
  • Connection Timeout: 7 days (nginx proxy)

Security Considerations

  • SSL/TLS encryption for WebSocket connections (WSS)
  • Deny access to .env and sensitive files in nginx
  • Run services as www-data (limited privileges)
  • Supervisor-managed daemons with auto-restart
  • Firewall rules (UFW) to restrict access
  • Redis should bind to 127.0.0.1 (not exposed publicly)
  • Rotate logs to prevent disk exhaustion
  • SSL certificates auto-renewed (Let's Encrypt)

License

This project is licensed under GPL-3.0-or-later.