### Start Redis Sentinel Setup Source: https://github.com/centrifugal/centrifugo/blob/master/misc/redis_sentinel/readme.md Execute this script to start the local Redis Sentinel and Redis nodes. Ensure you have the necessary setup files in place. ```bash bash start.sh ``` -------------------------------- ### Run Clickhouse Cluster Setup Source: https://github.com/centrifugal/centrifugo/blob/master/misc/clickhouse_cluster/readme.md Use these commands to generate configuration and start the Clickhouse cluster. ```bash make config ``` ```bash docker compose up ``` -------------------------------- ### Generate Example Configuration (JSON) Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html Generates a configuration example string in JSON format based on the provided documentation object and its level. Handles complex types, arrays, and default values. ```javascript function generateExample(doc, format = 'json') { const parts = doc.field.split('.'); let example = ''; if (format === 'json') { const indent = ' '.repeat(Math.max(0, doc.level - 1)); const fieldName = doc.name; if (doc.is_complex_type) { if (doc.default === '[]') { example = `${indent}"${fieldName}": []`; } else { example = `${indent}"${fieldName}": {}`; } } else { let exampleValue = doc.default; if (doc.type.startsWith('[]')) { exampleValue = exampleValue || '[]'; } else if (doc.type === 'string' || doc.type === 'PEMData') { if (exampleValue === undefined || exampleValue === null) { exampleValue = '""'; } else if (exampleValue === '') { exampleValue = '""'; } else if (!exampleValue.startsWith('"')) { exampleValue = `"${exampleValue}"`; } } else if (doc.type === 'bool') { exampleValue = exampleValue || 'false'; } else if (doc.type === 'int' || doc.type.includes('int')) { exampleValue = exampleValue || '0'; } else { exampleValue = exampleValue || '""'; } example = `${indent}"${fieldName}": ${exampleValue}`; } } else if (format === 'yaml') { const indent = ' '.repeat(Math.max(0, doc.level - 1)); const fieldName = doc.name; if (doc.is_complex_type) { example = `${indent}${fieldName}:`; } else { let exampleValue = doc.default || ''; if (doc.type.startsWith('[]')) { exampleValue = exampleValue || '[]'; } else if (doc.type === 'string') { exampleValue = exampleValue || '""'; } else if (doc.type === 'bool') { exampleValue = exampleValue || 'false'; } else if (doc.type === 'int' || doc.type.includes('int')) { exampleValue = exampleValue || '0'; } example = `${indent}${fieldName}: ${exampleValue}`; } } return example; } ``` -------------------------------- ### Install RPM Development Tools Source: https://github.com/centrifugal/centrifugo/blob/master/misc/rpm/README.md Installs the necessary tools for RPM development on your system. ```bash yum -y install rpmdevtools ``` -------------------------------- ### Start Local Redis Instances Source: https://github.com/centrifugal/centrifugo/blob/master/misc/redis_instances/readme.md Use this script to create N isolated Redis master instances starting from port 6000. Redis nodes will run until the script is interrupted. This is intended for development and benchmarking. ```bash bash start_instances.sh 3 ``` -------------------------------- ### Render All Configuration Documentation Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html Initializes the configuration documentation by rendering cards for all configuration items and setting up event listeners for copy and show example buttons. ```javascript function renderConfigDocs() { const container = document.getElementById('config-content'); container.innerHTML = ''; configData.forEach(doc => renderConfigCard(doc, container)); // Setup copy buttons document.querySelectorAll('.copy-btn').forEach(btn => { btn.addEventListener('click', function() { copyToClipboard(this.dataset.copy, this); }); }); document.querySelectorAll('.copy-example').forEach(btn => { btn.addEventListener('click', function() { copyToClipboard(this.dataset.example, this); }); }); // Setup show config example buttons document.querySelectorAll('.show-config-example').forEach(btn => { btn.addEventListener('click', function() { const field = this.dataset.field; const fieldId = generateFieldId(field); const exampleDiv = document.getElementById(fieldId + '-example'); if (exampleDiv.classList.contains('hidden')) { const jsonConfig = generateFullContextConfig(field, 'json'); const yamlConfig = generateFullContextConfig(field, 'yaml'); exampleDiv.innerHTML = `
';
}
document.getElementById('theme-toggle').addEventListener('click', function() {
state.darkMode = !state.darkMode;
localStorage.setItem('darkMode', state.darkMode);
document.documentElement.setAttribute('data-theme', state.darkMode ? 'dark' : 'light');
this.innerHTML = state.darkMode ? '' : '';
});
}
```
--------------------------------
### Running k6 Benchmark Script
Source: https://github.com/centrifugal/centrifugo/blob/master/misc/benchmarking/k6/readme.md
Execute the k6 benchmark script using the 'k6 run' command. Ensure the 'benchmark.js' file is in the current directory.
```bash
k6 run benchmark.js
```
--------------------------------
### Serve Static Files with Centrifugo CLI
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Use the `serve` command to run a development server for static files. Specify the directory and port if needed.
```bash
centrifugo serve
```
```bash
centrifugo serve --dir=./public --port=8080
```
--------------------------------
### Configure TLS
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Enable TLS for HTTP server and gRPC API. Specify certificate and key paths, or use autocert for automatic certificate management.
```json
{
"http_server": {
"tls": {
"enabled": true,
"cert_pem": "/path/to/cert.pem",
"key_pem": "/path/to/key.pem"
},
"tls_autocert": {
"enabled": true,
"host_whitelist": ["example.com"],
"cache_dir": "/tmp/certs",
"email": "admin@example.com"
}
},
"grpc_api": {
"enabled": true,
"tls": {
"enabled": true,
"cert_pem": "/path/to/grpc-cert.pem",
"key_pem": "/path/to/grpc-key.pem"
}
}
}
```
--------------------------------
### JavaScript: Copy Configuration to Clipboard
Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html
Attaches event listeners to copy configuration snippets to the clipboard and format them as JSON or YAML.
```javascript
const copyBtn = exampleDiv.querySelector('.copy-example');
const exampleContent = exampleDiv.querySelector('.example-content');
const formatBtns = exampleDiv.querySelectorAll('.format-btn');
let currentFormat = 'json';
if (copyBtn) {
copyBtn.addEventListener('click', function() {
const textToCopy = exampleContent.dataset[currentFormat].replace(/\\n/g, '\n').replace(/"/g, '"');
copyToClipboard(textToCopy, this);
});
}
formatBtns.forEach(btn => {
btn.addEventListener('click', function() {
const format = this.dataset.format;
currentFormat = format;
formatBtns.forEach(b => {
b.classList.remove('btn-primary', 'active');
b.classList.add('btn-outline-secondary');
});
this.classList.remove('btn-outline-secondary');
this.classList.add('btn-primary', 'active');
if (format === 'json') {
exampleContent.innerHTML = jsonConfig.html;
} else {
exampleContent.innerHTML = yamlConfig.html;
}
});
});
```
--------------------------------
### Get Node Information (Centrifugo API)
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Retrieves runtime information about all running Centrifugo nodes, including client and channel counts, metrics, and process statistics.
```bash
curl -X POST http://localhost:8000/api/info \
-H "Authorization: apikey my-api-key" \
-H "Content-Type: application/json" \
-d '{}'
```
--------------------------------
### JavaScript: Generate Full Context JSON
Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html
Builds a map of configuration documentation by field path and generates the JSON representation for a given target field, including highlighting.
```javascript
function generateFullContextJSON(targetField) {
// Build a map of all docs by field path for quick lookup
const docsMap = new Map();
function buildMap(docs) {
docs.forEach(doc => {
docsMap.set(doc.field, doc);
if (doc.children && doc.children.length > 0) {
buildMap(doc.children);
}
});
}
buildMap(configData);
const targetDoc = docsMap.get(targetField);
if (!targetDoc) return { html: 'Field not found', plain: '' };
// Parse the field path - handle array notation
const cleanField = targetField.replace(/\\\[\\\]/g, '');
const pathParts = cleanField.split('.');
const rootSection = pathParts[0];
// Find the root section doc
const rootDoc = configData.find(d => d.name === rootSection);
if (!rootDoc) return { html: 'Root section not found', plain: '' };
let jsonLines = [];
let plainLines = [];
function addLine(text, highlight = false) {
if (highlight) {
jsonLines.push(`${text}`);
} else {
jsonLines.push(text);
}
plainLines.push(text);
}
function getFieldValue(doc, showArrayStructure = false) {
if (doc.is_complex_type) {
if (doc.default === '[]') {
return showArrayStructure ? '[{}]' : '[]';
}
return '{}';
}
let val = doc.default;
if (doc.type.startsWith('[]')) {
// Array type - check if default is actually an array string or a plain value
if (!val || val === '[]') {
return '[]';
}
// If default doesn't start with '[', it's a plain value that should be wrapped in array
if (!va
```
--------------------------------
### Get Environment Variable Name
Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html
Constructs the expected environment variable name for a given configuration field. It handles nested fields, arrays, and complex types, prefixing with 'CENTRIFUGO_' and converting to uppercase.
```javascript
function getEnvVar(doc) {
if (doc.field.includes('[]')) {
const hasNamespacedArray = doc.field.match(/\bnamespaces\[\]/) || doc.field.match(/\bproxies\[\]/) || doc.field.match(/\bconsumers\[\]/);
if (!doc.field.endsWith('.name') && hasNamespacedArray && (!doc.is_complex_type || doc.default === '[]')) {
let field = doc.field.replace(/\\\[\\\]/g, '_');
let envVar = 'CENTRIFUGO_' + field.toUpperCase().replace(/\./g, '_');
return envVar;
}
return '';
} else if (!doc.is_complex_type) {
return 'CENTRIFUGO_' + doc.field.toUpperCase().replace(/\./g, '_');
} else if (doc.field.includes('namespaces') || doc.field.includes('proxies') || doc.field.includes('consumers') || doc.default === '[]') {
return 'CENTRIFUGO_' + doc.field.toUpperCase().replace(/\./g, '_');
}
return '';
}
```
--------------------------------
### Get Channel Presence Statistics via HTTP API
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Obtain compact presence statistics, such as the number of clients and unique users in a channel, without retrieving full presence data, by using the `/api/presence_stats` endpoint.
```bash
curl -X POST http://localhost:8000/api/presence_stats \
-H "Authorization: apikey my-api-key" \
-H "Content-Type: application/json" \
-d '{"channel": "chat:room1"}'
```
--------------------------------
### Render Configuration Card
Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html
Creates and appends a configuration card element to the DOM for a given configuration documentation object. It includes the field name, type, and action buttons like a copy link.
```javascript
function renderConfigCard(doc, parent) {
const fieldId = generateFieldId(doc.field);
const card = document.createElement('div');
card.className = 'config-card';
card.id = fieldId;
card.dataset.field = doc.field;
card.dataset.level = doc.level;
const header = document.createElement('div');
header.className = 'config-header';
const titleDiv = document.createElement('div');
titleDiv.className = 'config-title';
const heading = document.createElement(`h${Math.min(doc.level, 6)}`);
const typeIndicator = doc.is_complex_type ? (doc.default === '[]' ? '[array]' : '{object}') : doc.type;
heading.innerHTML = ` ${doc.field} ${typeIndicator} `;
titleDiv.appendChild(heading);
const actions = document.createElement('div');
actions.className = 'config-actions';
actions.innerHTML = ` `;
header.appendChild(titleDiv);
header.appendChild(actions);
card.appendChild(header);
parent.appendChild(card);
}
```
--------------------------------
### Backend Handler for Centrifugo Connect Proxy
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Example backend handler (using Flask) to process connect requests proxied by Centrifugo. It should validate session information and return a JSON response with user details or a disconnect status.
```python
# Backend handler for /centrifugo/connect (Django/Flask/FastAPI example)
# Centrifugo POST body: {"client":"...","transport":"websocket","protocol":"json","encoding":"json","data":{...}}
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/centrifugo/connect", methods=["POST"])
def connect():
# Validate session cookie, extract user
session_token = request.cookies.get("session")
user_id = validate_session(session_token) # your auth logic
if not user_id:
return jsonify({"disconnect": {"code": 4001, "reason": "Unauthorized"}}), 200
return jsonify({
"result": {
"user": user_id,
"expire_at": int(time.time()) + 3600,
"info": {"ip": request.remote_addr}
}
})
```
--------------------------------
### Minimal Centrifugo Configuration File
Source: https://context7.com/centrifugal/centrifugo/llms.txt
A sample JSON configuration file for Centrifugo, demonstrating settings for client authentication, API keys, Redis engine, gRPC API, admin UI, and channel namespaces.
```json
{
"client": {
"token": {
"hmac_secret_key": "my-secret-key"
},
"allowed_origins": ["http://localhost:3000"]
},
"http_api": {
"key": "my-api-key"
},
"engine": {
"type": "redis",
"redis": {
"address": "localhost:6379"
}
},
"grpc_api": {
"enabled": true,
"port": 10000
},
"admin": {
"enabled": true
},
"prometheus": {
"enabled": true
},
"health": {
"enabled": true
},
"channel": {
"namespaces": [
{
"name": "chat",
"presence": true,
"join_leave": true,
"history_size": 100,
"history_ttl": "5m",
"force_recovery": true,
"allow_subscribe_for_client": true,
"allow_publish_for_subscriber": true
}
]
}
}
```
--------------------------------
### JavaScript: Generate Full Context Configuration
Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html
Generates the full configuration in JSON or YAML format for a specified target field. Includes HTML highlighting for JSON.
```javascript
function generateFullContextConfig(targetField, format = 'json') {
const jsonResult = generateFullContextJSON(targetField);
if (format === 'yaml') {
const yamlPlain = jsonToYAML(jsonResult.plain);
// Apply highlighting to YAML
let yamlHtml = yamlPlain;
if (jsonResult.html.includes('highlight-line')) {
// Extract the highlighted field name from the last part of the path
const highlightMatch = jsonResult.html.match(/\s\*("\w+"):/);
if (highlightMatch) {
const fieldName = highlightMatch[1];
const lines = yamlPlain.split('\n');
// Find the last occurrence of this field (most nested one)
let lastMatchIndex = -1;
lines.forEach((line, idx) => {
if (line.match(new RegExp(`^\\s*${fieldName}:\\s`))) {
lastMatchIndex = idx;
}
});
if (lastMatchIndex >= 0) {
yamlHtml = lines.map((line, idx) => idx === lastMatchIndex ? `${line}` : line ).join('\n');
}
}
}
return { html: yamlHtml, plain: yamlPlain };
}
return jsonResult;
}
```
--------------------------------
### Initialize Search Functionality
Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html
Sets up event listeners for a search input field and a clear button. It manages search term state and triggers search operations.
```javascript
function initSearch() {
const searchInput = document.getElementById('search-input');
const searchClear = document.getElementById('search-clear');
searchInput.addEventListener('input', function() {
state.searchTerm = this.value.toLowerCase();
searchClear.classList.toggle('visible', this.value.length > 0);
performSearch();
});
searchClear.addEventListener('click', function() {
searchInput.value = '';
state.searchTerm = '';
this.classList.remove('visible');
performSearch();
});
}
```
--------------------------------
### Render Configuration Card
Source: https://github.com/centrifugal/centrifugo/blob/master/internal/cli/configdoc/template.html
Renders a card for a single configuration option, displaying its default value, environment variable, and description. Handles complex types and nested children.
```javascript
function renderConfigCard(doc, parent) {
const card = document.createElement('div');
card.className = 'config-card';
const fieldId = generateFieldId(doc.field);
card.id = fieldId;
// Header with field name and type
const header = document.createElement('div');
header.className = 'header';
header.innerHTML = `
${doc.field}
${doc.type}
`;
// ld(actions); card.appendChild(header); // Meta information
let metaHTML = '';
// Only show defaults for non-complex types
if (!doc.is_complex_type) {
const isStringType = doc.type === 'string' || doc.type === 'PEMData' || doc.type === 'Duration';
const isBoolType = doc.type === 'bool';
let shouldShow = false;
let displayDefault = doc.default;
if (isStringType) {
// Always show string defaults (including empty strings) with quotes
shouldShow = true;
displayDefault = `"${doc.default || ''}"`;
} else if (isBoolType) {
// Show boolean defaults, use 'false' if not set
shouldShow = true;
displayDefault = doc.default || 'false';
} else if (doc.default && doc.default !== '') {
// For other types (int, etc.), only show if there's a non-empty default
shouldShow = true;
displayDefault = doc.default;
}
if (shouldShow) {
metaHTML += ` `;
}
}
const envVar = getEnvVar(doc);
if (envVar) {
const escapedEnvVar = envVar.replace(//g, '>');
metaHTML += ` `;
}
// Only add meta div if there's content
if (metaHTML) {
const meta = document.createElement('div');
meta.className = 'meta-info';
meta.innerHTML = metaHTML;
card.appendChild(meta);
}
// Description
const desc = document.createElement('div');
desc.className = 'description';
desc.textContent = doc.comment || 'No documentation available.';
card.appendChild(desc);
// Config example button
const exampleBtnContainer = document.createElement('div');
exampleBtnContainer.className = 'example-btn-container';
exampleBtnContainer.innerHTML = ` `;
card.appendChild(exampleBtnContainer);
// Example configuration (hidden by default)
const exampleDiv = document.createElement('div');
exampleDiv.className = 'example-config hidden';
exampleDiv.id = fieldId + '-example';
card.appendChild(exampleDiv);
parent.appendChild(card);
// Handle children
if (doc.children && doc.children.length > 0) {
const expandIndicator = document.createElement('div');
expandIndicator.className = 'expand-indicator';
expandIndicator.innerHTML = ` ${doc.children.length} nested element${doc.children.length > 1 ? 's' : ''} `;
card.appendChild(expandIndicator);
const nestedContent = document.createElement('div');
nestedContent.className = 'nested-content';
nestedContent.id = fieldId + '-nested';
doc.children.forEach(child => renderConfigCard(child, nestedContent));
card.appendChild(nestedContent);
expandIndicator.addEventListener('click', function() {
const isExpanded = nestedContent.classList.contains('open');
if (isExpanded) {
nestedContent.classList.remove('open');
expandIndicator.classList.remove('expanded');
state.expandedSections.delete(fieldId);
} else {
nestedContent.classList.add('open');
expandIndicator.classList.add('expanded');
state.expandedSections.add(fieldId);
}
saveExpandedState();
});
}
}
```
--------------------------------
### Initialize Centrifuge Client and Connect
Source: https://github.com/centrifugal/centrifugo/blob/master/internal/devpage/index.html
Sets up the Centrifuge client with multiple transport endpoints and event handlers for connection status. It also defines functions to fetch connection and subscription tokens.
```javascript
window.addEventListener('load', function () {
const statusEl = document.getElementById('status');
const logEl = document.getElementById('log');
function updateStatus(text, state) {
statusEl.textContent = text;
statusEl.className = `status ${state}`;
}
function log(message, isError) {
const entry = document.createElement('div');
entry.className = `log-entry${isError ? ' error' : ''}`;
const timestamp = new Date().toLocaleTimeString();
entry.innerHTML = `[${timestamp}] ${message}`;
logEl.insertBefore(entry, logEl.firstChild);
}
// Detect protocol and construct transport endpoints
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const httpProtocol = window.location.protocol;
const host = window.location.host;
const transports = [
{ 'transport': 'websocket', 'endpoint': `${wsProtocol}//${host}/connection/websocket` },
{ 'transport': 'http_stream', 'endpoint': `${httpProtocol}//${host}/connection/http_stream` },
{ 'transport': 'sse', 'endpoint': `${httpProtocol}//${host}/connection/sse` }
];
log('Initializing connection with transports: websocket, http_stream, sse');
const CHANNEL = 'dev';
function getConnectionToken(ctx) {
return fetch('/dev/connection_token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
if (!res.ok) {
throw new Error('Failed to get connection token');
}
return res.json();
})
.then(data => {
log('Received connection token');
return data.token;
})
.catch(err => {
log(`Error getting connection token: ${err.message}`, true);
throw err;
});
}
function getSubscriptionToken(ctx) {
return fetch('/dev/subscription_token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ channel: ctx.channel })
})
.then(res => {
if (!res.ok) {
throw new Error('Failed to get subscription token');
}
return res.json();
})
.then(data => {
log(`Received subscription token for channel: ${ctx.channel}`);
return data.token;
})
.catch(err => {
log(`Error getting subscription token: ${err.message}`, true);
throw err;
});
}
const centrifuge = new Centrifuge(transports, {
debug: true,
getToken: getConnectionToken,
// Workaround to reliably use HTTP/2 Extended CONNECT in Chrome upon reconnections
// (when no HTTP/2 connection in pool exists Chrome selects HTTP/1.1 for WebSocket).
getData: function () {
return fetch(`${httpProtocol}//${host}/connection/init`, {method: 'GET'}).then(function() { return null; });
}
});
centrifuge.on('connecting', function (ctx) {
updateStatus('Connecting to server...', 'connecting');
log(`Connecting: ${ctx.reason}`);
});
centrifuge.on('connected', function (ctx) {
updateStatus(`Connected (Client ID: ${ctx.client}, Transport: ${ctx.transport})`, 'connected');
log(`Connected: ${JSON.stringify(ctx)}`);
});
centrifuge.on('disconnected', function (ctx) {
updateStatus('Disconnected', 'disconnected');
log(`Disconnected: ${ctx.reason}${ctx.reconnect ? ', will try to reconnect' : ', will not reconnect'}`);
});
centrifuge.on('error', function (ctx) {
log(`Error: ${JSON.stringify(ctx)}`, true);
});
// Subscribe to channel
const sub = centrifuge.newSubscription(CHANNEL, { getToken: getSubscriptionToken });
sub.on('publication', function (ctx) {
log(`Publication received: ${JSON.stringify(ctx)}`);
});
sub.on('subscribed', function (ctx) {
log(`Subscribed to channel: ${JSON.stringify(ctx)}`);
});
sub.on('subscribing', function (ctx) {
log(`Subscribing to channel "${CHANNEL}": ${ctx.reason}`);
});
sub.on('unsubscribed', function (ctx) {
log(`Unsubscribed from channel "${CHANNEL}": ${ctx.reason}`);
});
sub.on('error', function (ctx) {
log(`Subscription error in channel "${CHANNEL}": ${JSON.stringify(ctx)}`, true);
});
sub.subscribe();
log('Starting connection...');
centrifuge.connect();
});
```
--------------------------------
### Implement Subscribe Proxy Backend Logic
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Implement a backend endpoint to handle subscribe requests proxied by Centrifugo. Check user permissions for the requested channel and return a forbidden error or an empty result to allow subscription.
```python
@app.route("/centrifugo/subscribe", methods=["POST"])
def subscribe():
body = request.json
channel = body.get("channel", "")
user = body.get("user", "")
# Check user permission for the channel
if not has_access(user, channel):
return jsonify({"disconnect": {"code": 4003, "reason": "Forbidden"}}), 200
return jsonify({"result": {}})
```
--------------------------------
### Configure Client Transports
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Configure various client transports like WebSocket, SSE, and gRPC. Each transport can have specific options and handler prefixes.
```json
{
"websocket": {
"compression": true,
"compression_level": 1,
"write_timeout": "1000ms",
"message_size_limit": 65536,
"handler_prefix": "/connection/websocket"
},
"sse": {
"enabled": true,
"handler_prefix": "/connection/sse"
},
"http_stream": {
"enabled": true,
"handler_prefix": "/connection/http_stream"
},
"uni_sse": {
"enabled": true,
"handler_prefix": "/connection/uni_sse"
},
"uni_websocket": {
"enabled": true,
"handler_prefix": "/connection/uni_websocket"
},
"uni_grpc": {
"enabled": true,
"port": 11000
}
}
```
--------------------------------
### Configure NATS Broker
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Enable NATS as a PUB/SUB broker for at-most-once message delivery. Configure the NATS URL, prefix, and wildcard settings.
```json
{
"broker": {
"enabled": true,
"type": "nats",
"nats": {
"url": "nats://localhost:4222",
"prefix": "centrifugo",
"allow_wildcards": false
}
}
}
```
--------------------------------
### Create Local Redis Cluster
Source: https://github.com/centrifugal/centrifugo/blob/master/misc/redis_cluster/readme.md
Use this script to create a local Redis cluster with a specified number of nodes and replicas. The cluster is intended for development and benchmarking and stores data in the 'cluster_data' directory. Redis runs without RDB and AOF persistence by default.
```bash
bash create_cluster.sh 3 0
```
--------------------------------
### Connect to Redis Sentinel
Source: https://github.com/centrifugal/centrifugo/blob/master/misc/redis_sentinel/readme.md
Use redis-cli to connect to the Sentinel instance running on port 26379. This is the first step before issuing Sentinel commands.
```bash
redis-cli -p 26379
```
--------------------------------
### Implement Publish Proxy Backend Logic
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Create a backend endpoint to handle publish requests proxied by Centrifugo. Validate message content and optionally modify it before returning a response. The backend can return an error or a result object.
```python
@app.route("/centrifugo/publish", methods=["POST"])
def publish():
body = request.json
# body: {"client":"...", "user":"alice", "channel":"chat:room1", "data":{...}}
if len(body.get("data", {}).get("text", "")) > 500:
return jsonify({"error": {"code": 1000, "message": "Message too long"}}), 200
# Optionally modify data before storing/delivering
return jsonify({"result": {"skip_history": False}})
```
--------------------------------
### Generate Minimal Centrifugo Configuration
Source: https://context7.com/centrifugal/centrifugo/llms.txt
Use the `genconfig` command to create a basic configuration file for Centrifugo. This file can then be customized.
```bash
centrifugo genconfig
```