npm package: @intellias/laravel-echo-server
Description: Laravel Echo Node JS Server for Socket.io
Author: Nazarii Shershin
Latest version: 1.1.4

# Laravel Echo Server

NodeJs server for Laravel Echo broadcasting with Socket.io.

> **Note:** This is a customized fork maintained by Intellias for the Intems project. This version uses `.env` file for configuration instead of `laravel-echo-server.json`.

## System Requirements

The following are required to function properly.

*   Laravel 12.0+
*   Node 18.0+
*   Redis 3+

Additional information on broadcasting with Laravel can be found on the
official docs: <https://laravel.com/docs/master/broadcasting>

## Getting Started

Install npm package globally with the following command:

```shell
npm install -g @intellias/laravel-echo-server
```

Or install locally in your project:

```shell
npm install @intellias/laravel-echo-server
```

### Configuration

This fork uses environment variables (`.env` file) for configuration. Copy the `.env.example` file to `.env` and configure your settings:

```shell
cp .env.example .env
```

#### Environment Variables

| Variable | Default              | Description |
| :------- |:---------------------| :---------- |
| `PORT` | `6001`               | The port that the socket.io server should run on |
| `HOST` | `null`               | The host of the socket.io server |
| `PROTOCOL` | `http`               | Protocol to use (`http` or `https`) |
| `SSL_ENABLED` | `false`              | Enable SSL/TLS (alternative to setting `PROTOCOL=https`) |
| `SSL_CERT_PATH` | ``                   | Path to SSL certificate file (for HTTPS) |
| `SSL_KEY_PATH` | ``                   | Path to SSL key file (for HTTPS) |
| `SSL_CERT_CHAIN_PATH` | ``                   | Path to SSL certificate chain file (for HTTPS) |
| `SSL_PASSPHRASE` | ``                   | SSL certificate passphrase (if required) |
| `AUTH_HOST` | `http://localhost`   | The host of the server that authenticates private and presence channels |
| `AUTH_ENDPOINT` | `/broadcasting/auth` | The route that authenticates private channels |
| `REDIS_HOST` | `localhost`          | Redis server host |
| `REDIS_PORT` | `6379`               | Redis server port |
| `REDIS_PASSWORD` | ``                   | Redis server password |
| `REDIS_KEY_PREFIX` | ``                   | Prefix for Redis keys |
| `REDIS_TLS` | `false`              | Enable TLS connection to Redis |
| `USE_AZURE_MANAGED_IDENTITY` | `false`              | Use Azure Managed Identity for Redis authentication |
| `REDIS_USERNAME` | ``                   | Client ID for User-assigned Managed Identity (used as username for Azure Redis AAD auth) |
| `REDIS_TOKEN_REFRESH_MINUTES` | `30`                 | Token refresh interval in minutes (when using Managed Identity) |
| `DEBUG` | `false`              | Enable debug/dev mode |
| `DEV_MODE` | `false`              | Enable development mode with additional logging |

#### Example `.env` file

```dotenv
# Server Configuration
PORT=6001
HOST=laravel-echo-server
PROTOCOL=http

# SSL Configuration (for HTTPS)
# Set SSL_ENABLED=true or PROTOCOL=https to enable SSL
SSL_ENABLED=false
# Use Let's Encrypt: sudo certbot certonly --standalone -d yourdomain.com
SSL_CERT_PATH=/etc/letsencrypt/live/yourdomain.com/fullchain.pem
SSL_KEY_PATH=/etc/letsencrypt/live/yourdomain.com/privkey.pem
SSL_CERT_CHAIN_PATH=
SSL_PASSPHRASE=

# Laravel Auth
AUTH_HOST=http://localhost
AUTH_ENDPOINT=/broadcasting/auth

# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_KEY_PREFIX=
REDIS_TLS=false

# Azure Managed Identity
USE_AZURE_MANAGED_IDENTITY=false

# Debug
DEBUG=false
DEV_MODE=false
```

### Running with HTTPS

To enable HTTPS with Let's Encrypt:

1. Install Certbot:
```shell
sudo apt install certbot
```

2. Generate certificate:
```shell
sudo certbot certonly --standalone -d yourdomain.com
```

3. Update your `.env` file:
```dotenv
SSL_ENABLED=true
SSL_CERT_PATH=/etc/letsencrypt/live/yourdomain.com/fullchain.pem
SSL_KEY_PATH=/etc/letsencrypt/live/yourdomain.com/privkey.pem
```

4. Start the server:
```shell
laravel-echo-server start
```

> **Note:** Let's Encrypt certificates expire every 90 days. Set up auto-renewal with `sudo certbot renew --dry-run`

### Azure Managed Identity Authentication

This server supports Azure Managed Identity for authenticating with Azure Cache for Redis. This allows passwordless authentication when running on Azure services like Azure Kubernetes Service (AKS), Azure Container Instances, or Azure VMs.

#### Prerequisites

1. Azure Cache for Redis with Azure AD authentication enabled
2. A Managed Identity (System-assigned or User-assigned) with the appropriate Redis Data Owner/Contributor role
3. The `@azure/identity` package (included as a dependency)

#### Configuration

Set the following environment variables:

```dotenv
# Enable Azure Managed Identity
USE_AZURE_MANAGED_IDENTITY=true

# Redis Configuration
REDIS_HOST=your-redis.redis.cache.windows.net
REDIS_PORT=6380
REDIS_TLS=true

# Optional: For User-assigned Managed Identity, specify the Client ID
REDIS_USERNAME=your-managed-identity-client-id

# Optional: Token refresh interval in minutes (default: 45)
REDIS_TOKEN_REFRESH_MINUTES=30
```

#### How It Works

1. When `USE_AZURE_MANAGED_IDENTITY=true`, the server uses `DefaultAzureCredential` from the `@azure/identity` package to obtain an access token
2. The token is used as the password for Redis authentication
3. Tokens are automatically refreshed before expiration (default: every 45 minutes)
4. On token refresh, the Redis connection is automatically re-authenticated

#### Running on Azure

When deploying to Azure:

1. **Azure Kubernetes Service (AKS)**: Enable workload identity and configure the pod to use a managed identity
2. **Azure Container Instances**: Assign a managed identity to your container group
3. **Azure VMs**: Use system-assigned or user-assigned managed identity

Example Kubernetes deployment with workload identity:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: laravel-echo-server
spec:
  template:
    metadata:
      labels:
        azure.workload.identity/use: "true"
    spec:
      serviceAccountName: your-service-account
      containers:
      - name: laravel-echo-server
        image: your-registry/laravel-echo-server
        env:
        - name: USE_AZURE_MANAGED_IDENTITY
          value: "true"
        - name: REDIS_HOST
          value: "your-redis.redis.cache.windows.net"
        - name: REDIS_PORT
          value: "6380"
        - name: REDIS_TLS
          value: "true"
```

### Run The Server

In your project root directory, run:

```shell
laravel-echo-server start
```

### Stop The Server

In your project root directory, run:

```shell
laravel-echo-server stop
```

### Running with Docker

A Dockerfile is included for containerized deployments:

```shell
# Build the Docker image
docker build -t laravel-echo-server .

# Run the container
docker run -p 6001:6001 laravel-echo-server
```

You can pass environment variables directly:

```shell
docker run -p 6001:6001 \
  -e AUTH_HOST=http://your-app \
  -e REDIS_HOST=your-redis-host \
  -e REDIS_PORT=6379 \
  laravel-echo-server
```

Or mount your `.env` file:

```shell
docker run -p 6001:6001 -v /path/to/.env:/app/.env laravel-echo-server
```

## Subscribers

The Laravel Echo Server subscribes to incoming events with two methods: Redis & Http.

### Redis

Your core application can use Redis to publish events to channels. The Laravel Echo Server will subscribe to those channels and broadcast those messages via socket.io.

### Http

Using Http, you can also publish events to the Laravel Echo Server in the same fashion you would with Redis by submitting a `channel` and `message` to the broadcast endpoint.

**Request Endpoint**

```http
POST http://app.dev:6001/apps/your-app-id/events?auth_key=skti68i...
```

**Request Body**

```json
{
  "channel": "channel-name",
  "name": "event-name",
  "data": {
      "key": "value"
  },
  "socket_id": "h3nAdb134tbvqwrg"
}
```

**channel** - The name of the channel to broadcast an event to. For private or presence channels prepend `private-` or `presence-`.
**channels** - Instead of a single channel, you can broadcast to an array of channels with 1 request.
**name** - A string that represents the event key within your app.
**data** - Data you would like to broadcast to channel.
**socket_id (optional)** - The socket id of the user that initiated the event. When present, the server will only "broadcast to others".

### Pusher Compatibility

The HTTP subscriber is compatible with the Laravel Pusher subscriber. Just configure the host and port for your Socket.IO server and set the app id and key in config/broadcasting.php. Secret is not required.

```php
'pusher' => [
    'driver' => 'pusher',
    'key' => env('PUSHER_KEY'),
    'secret' => null,
    'app_id' => env('PUSHER_APP_ID'),
    'options' => [
        'host' => 'localhost',
        'port' => 6001,
        'scheme' => 'http'
    ],
],
```

## HTTP API

The HTTP API exposes endpoints that allow you to gather information about your running server and channels.

**Status**
Get total number of clients, uptime of the server, and memory usage.

```http
GET /apps/:APP_ID/status
```

**Channels**
List of all channels.

```http
GET /apps/:APP_ID/channels
```

**Channel**
Get information about a particular channel.

```http
GET /apps/:APP_ID/channels/:CHANNEL_NAME
```

**Channel Users**
List of users on a channel.

```http
GET /apps/:APP_ID/channels/:CHANNEL_NAME/users
```

## Proxy Configuration

If you want to run the Echo server behind a reverse proxy, here are sample configurations:

### NginX Proxy Config

```nginx
location /socket.io {
    proxy_pass http://laravel-echo-server:6001;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}
```

### Apache Proxy Config

```apache
RewriteCond %{REQUEST_URI}  ^/socket.io            [NC]
RewriteCond %{QUERY_STRING} transport=websocket    [NC]
RewriteRule /(.*)           ws://localhost:6001/$1 [P,L]

ProxyPass        /socket.io http://localhost:6001/socket.io
ProxyPassReverse /socket.io http://localhost:6001/socket.io
```

## Presence Channels

When users join a presence channel, their presence channel authentication data is stored using Redis.

While presence channels contain a list of users, there will be instances where a user joins a presence channel multiple times. For example, this would occur when opening multiple browser tabs. In this situation "joining" and "leaving" events are only emitted to the first and last instance of the user.

## Client Side Configuration

See the official Laravel documentation for more information: <https://laravel.com/docs/master/broadcasting#introduction>

### Tips

#### Socket.io client library

You can include the socket.io client library from your running server. For example, if your server is running at `app.dev:6001` you should be able to add a script tag to your html like so:

```html
<script src="//app.dev:6001/socket.io/socket.io.js"></script>
```

_Note: When using the socket.io client library from your running server, remember to check that the `io` global variable is defined before subscribing to events._

## License

MIT