initial commit

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-12-27 03:39:14 +01:00
commit 74d5e0a94c
Se han modificado 37 ficheros con 6509 adiciones y 0 borrados

330
docs/API.md Archivo normal
Ver fichero

@@ -0,0 +1,330 @@
# API Documentation
## Core Classes
### Server
Main server class that orchestrates all components.
```javascript
const Server = require('./core/server');
const server = new Server(config);
await server.initialize();
await server.start();
```
#### Methods
- `async initialize()` - Initialize server components
- `async start()` - Start all servers
- `async stop()` - Stop all servers gracefully
- `getStatus()` - Get server status
### ConfigManager
Singleton configuration manager.
```javascript
const ConfigManager = require('./core/config-manager');
const config = ConfigManager.getInstance();
await config.load('./config.yaml');
const port = config.get('network.c2s.port', 5222);
```
#### Methods
- `async load(configPath)` - Load configuration
- `get(key, defaultValue)` - Get configuration value
- `set(key, value)` - Set configuration value
- `has(key)` - Check if key exists
- `getAll()` - Get all configuration
### SessionManager
Manages active client sessions.
```javascript
const session = sessionManager.createSession({
id: 'session-id',
jid: 'user@domain/resource',
type: 'c2s'
});
```
#### Methods
- `createSession(options)` - Create new session
- `getSession(sessionId)` - Get session by ID
- `getSessionByJid(jid)` - Get session by full JID
- `getSessionsByBareJid(bareJid)` - Get all sessions for bare JID
- `getBestSessionForBareJid(bareJid)` - Get best session (highest priority)
- `updateSession(sessionId, updates)` - Update session
- `authenticateSession(sessionId, jid, resource)` - Authenticate session
- `closeSession(sessionId, reason)` - Close session
- `getSessionCount()` - Get total session count
- `getAllSessions()` - Get all sessions
### StanzaRouter
Routes XMPP stanzas to their destinations.
```javascript
stanzaRouter.route(stanza, session);
```
#### Methods
- `route(stanza, session)` - Route a stanza
- `addFilter(filter)` - Add routing filter
- `removeFilter(filter)` - Remove routing filter
- `sendError(stanza, session, errorType)` - Send error response
#### Events
- `message` - Message stanza routed
- `presence` - Presence stanza routed
- `iq` - IQ stanza routed
- `stanza:routed` - Any stanza routed
- `stanza:recipient-unavailable` - Recipient not found
### HostManager
Manages virtual hosts.
```javascript
const host = hostManager.addHost({
domain: 'example.com',
enabled: true,
modules: ['roster', 'disco']
});
```
#### Methods
- `async initialize()` - Initialize host manager
- `addHost(hostConfig)` - Add virtual host
- `removeHost(domain)` - Remove virtual host
- `getHost(domain)` - Get host by domain
- `hasHost(domain)` - Check if host exists
- `getHosts()` - Get all host domains
- `getEnabledHosts()` - Get enabled host domains
- `isLocalJid(jid)` - Check if JID is local
### ModuleManager
Manages server modules.
```javascript
await moduleManager.loadModuleForHost('localhost', 'disco');
```
#### Methods
- `async initialize()` - Initialize module manager
- `registerModuleDefinition(name, definition)` - Register module
- `async loadGlobalModule(moduleName)` - Load global module
- `async loadModuleForHost(host, moduleName)` - Load module for host
- `async loadModulesForAllHosts()` - Load modules for all hosts
- `async unloadModule(host, moduleName)` - Unload module
- `async reloadModule(host, moduleName)` - Reload module
- `getModule(host, moduleName)` - Get module instance
- `getLoadedModules(host)` - Get loaded module names
- `isModuleLoaded(host, moduleName)` - Check if module loaded
### StorageManager
Manages data persistence.
```javascript
const store = storageManager.getStore('localhost', 'roster');
await store.set('user@localhost', rosterData);
```
#### Methods
- `async initialize()` - Initialize storage
- `getStore(host, name)` - Get storage store
- `async shutdown()` - Shutdown storage
### Store
Key-value store interface.
```javascript
const store = storageManager.getStore(host, name);
await store.set('key', value);
const value = await store.get('key');
await store.delete('key');
```
#### Methods
- `async get(key)` - Get value
- `async set(key, value)` - Set value
- `async delete(key)` - Delete value
- `async has(key)` - Check if key exists
- `async keys()` - Get all keys
- `async values()` - Get all values
- `async entries()` - Get all entries
- `async clear()` - Clear all data
- `async size()` - Get size
- `async find(predicate)` - Find values matching predicate
- `async findOne(predicate)` - Find first value matching predicate
## Network Servers
### C2SServer
Client-to-Server connection handler.
```javascript
const c2s = new C2SServer(config, {
sessionManager,
stanzaRouter,
moduleManager
});
await c2s.start();
```
#### Events
- `session:authenticated` - Client authenticated
### BOSHServer
HTTP binding (BOSH) server.
```javascript
const bosh = new BOSHServer(config, {
sessionManager,
stanzaRouter
});
await bosh.start();
```
### WebSocketServer
WebSocket XMPP server.
```javascript
const ws = new WebSocketServer(config, {
sessionManager,
stanzaRouter
});
await ws.start();
```
### S2SServer
Server-to-Server federation.
```javascript
const s2s = new S2SServer(config, {
sessionManager,
stanzaRouter
});
await s2s.start();
```
### ComponentServer
External component handler.
```javascript
const component = new ComponentServer(config, {
stanzaRouter
});
await component.start();
```
## Utilities
### Logger
Winston-based logger.
```javascript
const Logger = require('./utils/logger');
const logger = Logger.createLogger('mymodule');
logger.info('Information message');
logger.warn('Warning message');
logger.error('Error message', error);
logger.debug('Debug message');
```
## Data Types
### Session Object
```javascript
{
id: 'session-123',
jid: 'user@domain/resource',
bareJid: 'user@domain',
resource: 'resource',
authenticated: true,
type: 'c2s',
priority: 0,
presence: <presence/>,
createdAt: 1234567890,
lastActivity: 1234567890,
features: Set(['carbons', 'csi']),
metadata: {}
}
```
### Stanza Object (ltx)
```javascript
const ltx = require('ltx');
const message = new ltx.Element('message', {
to: 'user@domain',
from: 'sender@domain',
type: 'chat'
}).c('body').t('Hello!');
```
## Module API
See [MODULE_DEVELOPMENT.md](MODULE_DEVELOPMENT.md) for detailed module API documentation.
## Error Handling
All async operations should be wrapped in try-catch:
```javascript
try {
await server.start();
} catch (error) {
logger.error('Failed to start server:', error);
process.exit(1);
}
```
## Events
The server uses EventEmitter for event-driven architecture:
```javascript
sessionManager.on('session:authenticated', (session) => {
console.log('User authenticated:', session.jid);
});
stanzaRouter.on('message', (stanza, session) => {
console.log('Message received');
});
```
## Configuration Schema
See [CONFIGURATION.md](CONFIGURATION.md) for complete configuration schema.

283
docs/ARCHITECTURE.md Archivo normal
Ver fichero

@@ -0,0 +1,283 @@
# System Architecture
## Overview Diagram
```
┌─────────────────────────────────────────────────────────────────────┐
│ XMPP Clients │
│ (Gajim, Pidgin, Conversations, Web Clients, Bots, etc.) │
└────┬─────────────┬──────────────┬──────────────┬────────────────────┘
│ │ │ │
│ TCP/TLS │ HTTP │ WebSocket │ Components
│ :5222 │ :5280 │ :5281 │ :5347
│ │ │ │
┌────▼─────────────▼──────────────▼──────────────▼────────────────────┐
│ Network Layer │
├────────────────────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌───────────┐ ┌────────────┐ ┌─────────────────┐│
│ │ C2S │ │ BOSH │ │ WebSocket │ │ Component ││
│ │ Server │ │ Server │ │ Server │ │ Server ││
│ └────┬─────┘ └─────┬─────┘ └──────┬─────┘ └────────┬────────┘│
└───────┼──────────────┼────────────────┼─────────────────┼─────────┘
│ │ │ │
└──────────────┴────────────────┴─────────────────┘
┌─────────────────────────────▼─────────────────────────────────────┐
│ XMPP Stream Handler │
│ • Stream Negotiation • SASL Auth • Resource Binding │
└─────────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────▼─────────────────────────────────────┐
│ Session Manager │
│ • Session Creation • JID Mapping • Authentication │
│ • Priority Handling • Presence • Cleanup │
└─────────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────▼─────────────────────────────────────┐
│ Stanza Router │
│ • Message Routing • Presence Broadcasting • IQ Handling │
│ • Filtering • Event Hooks • Error Handling │
└──────┬──────────────────────┬──────────────────────┬──────────────┘
│ │ │
│ │ │
┌──────▼──────┐ ┌─────────▼────────┐ ┌────────▼──────────┐
│ Module │ │ Host │ │ Storage │
│ Manager │ │ Manager │ │ Manager │
│ │ │ │ │ │
│ • Dynamic │ │ • Virtual Hosts │ │ • Backends │
│ Loading │ │ • Multi-domain │ │ • Per-host │
│ • Hooks │ │ • Config │ │ • Abstraction │
└──────┬──────┘ └─────────┬────────┘ └────────┬─────────┘
│ │ │
└─────────────────────┴──────────────────────┘
┌────────────────┴────────────────┐
│ │
┌───────────▼──────────┐ ┌──────────▼──────────┐
│ Modules (Plugins) │ │ Storage Backends │
├──────────────────────┤ ├─────────────────────┤
│ • mod_roster │ │ • Memory │
│ • mod_disco │ │ • File │
│ • mod_presence │ │ • PostgreSQL │
│ • mod_message │ │ • MongoDB │
│ • mod_mam │ │ • Redis Cache │
│ • mod_muc │ └─────────────────────┘
│ • mod_pubsub │
│ • ... (extensible) │
└──────────────────────┘
```
## Component Interaction Flow
### Client Connection Flow
```
Client C2S Server XMPP Stream Session Mgr
│ │ │ │
│─── TCP Connect ────────> │ │
│ │ │ │
│ │─── Create Stream ───>│ │
│ │ │ │
│<──── Stream Start ─────│<────────────────────│ │
│ │ │ │
│─── SASL Auth ─────────>│─────────────────────>│ │
│ │ │ │
│<──── Auth Success ─────│<────────────────────│ │
│ │ │ │
│─── Bind Resource ─────>│─────────────────────>│─── Create ──────>│
│ │ │ Session │
│ │ │ │
│<──── Bound JID ────────│<────────────────────│<─── Authenticate ─│
│ │ │ │
│─── Send Presence ─────>│ │ │
│ │ │ │
● ● ● ●
Connected and Ready
```
### Message Routing Flow
```
Sender Stanza Router Session Mgr Recipient
│ │ │ │
│─── Send Message ──────>│ │ │
│ │ │ │
│ │─── Get Session ────>│ │
│ │ by JID │ │
│ │<──── Session ───────│ │
│ │ │ │
│ │───────── Route Message ──────────────>│
│ │ │ │
│ │<────── Delivered ───────────────────│
│ │ │ │
│<──── Receipt (opt) ────│ │ │
```
### Module Hook Flow
```
Event Stanza Router Module Manager Module
│ │ │ │
│─── Stanza Arrives ────>│ │ │
│ │ │ │
│ │─── Emit Event ─────>│ │
│ │ (e.g., message) │ │
│ │ │ │
│ │ │─── Hook ─────>│
│ │ │ Called │
│ │ │ │
│ │ │<── Process ───│
│ │ │ Stanza │
│ │ │ │
│ │<─── Continue ───────│ │
│ │ or Block │ │
```
## Data Flow Layers
```
┌──────────────────────────────────────────────────────────┐
│ Application Layer │
│ (User Logic, Business Rules, Module Code) │
└────────────────────────┬─────────────────────────────────┘
┌────────────────────────▼─────────────────────────────────┐
│ XMPP Protocol Layer │
│ (Stanza Processing, Routing, Presence, Roster) │
└────────────────────────┬─────────────────────────────────┘
┌────────────────────────▼─────────────────────────────────┐
│ Session Layer │
│ (Authentication, Session Management, JID Mapping) │
└────────────────────────┬─────────────────────────────────┘
┌────────────────────────▼─────────────────────────────────┐
│ Transport Layer │
│ (TCP, TLS, HTTP, WebSocket) │
└──────────────────────────────────────────────────────────┘
```
## Storage Architecture
```
┌─────────────────────────────────────────────────────────┐
│ Application Code │
└────────────────────────┬────────────────────────────────┘
┌────────────────────────▼────────────────────────────────┐
│ Storage Manager (Abstraction) │
│ • getStore(host, name) │
│ • Per-host isolation │
└────────────────────────┬────────────────────────────────┘
┌────────────┴────────────┐
│ │
┌───────────▼──────────┐ ┌─────────▼─────────┐
│ Store Interface │ │ Store Interface │
│ (localhost:roster) │ │ (example.com:mam) │
└───────────┬──────────┘ └─────────┬─────────┘
│ │
┌───────────▼────────────────────────▼─────────┐
│ Storage Backend │
│ ┌─────────┐ ┌──────────┐ ┌────────────┐ │
│ │ Memory │ │ File │ │ Database │ │
│ └─────────┘ └──────────┘ └────────────┘ │
└──────────────────────────────────────────────┘
```
## Security Layers
```
┌─────────────────────────────────────────────┐
│ Connection Security │
│ • TLS/SSL Encryption │
│ • Certificate Validation │
└──────────────────┬──────────────────────────┘
┌──────────────────▼──────────────────────────┐
│ Authentication Security │
│ • SASL Mechanisms │
│ • Credential Validation │
│ • Session Tokens │
└──────────────────┬──────────────────────────┘
┌──────────────────▼──────────────────────────┐
│ Application Security │
│ • Rate Limiting │
│ • Connection Limits │
│ • Input Validation │
│ • ACL/Permissions │
└─────────────────────────────────────────────┘
```
## Event-Driven Architecture
```
┌─────────────┐
│ Events │
└──────┬──────┘
┌────────────────────┼────────────────────┐
│ │ │
┌─────▼──────┐ ┌────────▼────────┐ ┌──────▼──────┐
│ Session │ │ Stanza │ │ Module │
│ Events │ │ Events │ │ Events │
│ │ │ │ │ │
│ • created │ │ • message │ │ • loaded │
│ • auth │ │ • presence │ │ • unloaded │
│ • closed │ │ • iq │ │ • error │
└────────────┘ └─────────────────┘ └─────────────┘
│ │ │
└────────────────────┼────────────────────┘
┌──────▼──────┐
│ Listeners │
│ (Modules) │
└─────────────┘
```
## Deployment Architecture
### Single Instance
```
┌────────────────────────────────────┐
│ Load Balancer / Proxy │
│ (Nginx, HAProxy) │
└────────────┬───────────────────────┘
┌────────────▼───────────────────────┐
│ Prosody Node.js Instance │
│ ┌──────────────────────────────┐ │
│ │ Network Servers │ │
│ │ (C2S, BOSH, WebSocket) │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Core Components │ │
│ └──────────────────────────────┘ │
└────────────┬───────────────────────┘
┌────────────▼───────────────────────┐
│ Database │
│ (PostgreSQL, MongoDB) │
└─────────────────────────────────────┘
```
### Clustered (Future)
```
┌────────────────────────────────────┐
│ Load Balancer (HAProxy) │
└──────┬──────────┬──────────┬───────┘
│ │ │
┌──────▼─────┐ ┌──▼──────┐ ┌▼────────┐
│ Instance 1 │ │Instance 2│ │Instance3│
└──────┬─────┘ └──┬──────┘ └┬────────┘
│ │ │
└──────────┼──────────┘
┌──────────▼──────────┐
│ Shared Database │
│ Redis Cache │
└─────────────────────┘
```

415
docs/DEPLOYMENT.md Archivo normal
Ver fichero

@@ -0,0 +1,415 @@
# Deployment Guide
## Production Deployment
### System Requirements
- Node.js 18+ LTS
- 2GB RAM minimum
- 10GB disk space
- Linux (Ubuntu 20.04+ recommended)
### Installation
#### 1. Install Node.js
```bash
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
```
#### 2. Create User
```bash
sudo useradd -r -s /bin/false prosody-nodejs
sudo mkdir -p /opt/prosody-nodejs
sudo chown prosody-nodejs:prosody-nodejs /opt/prosody-nodejs
```
#### 3. Deploy Application
```bash
cd /opt/prosody-nodejs
sudo -u prosody-nodejs git clone https://github.com/yourusername/prosody-nodejs.git .
sudo -u prosody-nodejs npm install --production
```
#### 4. Configuration
```bash
sudo -u prosody-nodejs cp .env.example .env
sudo -u prosody-nodejs nano .env
```
```bash
NODE_ENV=production
SERVER_HOST=your-domain.com
SERVER_PORT=5222
TLS_ENABLED=true
TLS_CERT_PATH=/etc/letsencrypt/live/your-domain.com/fullchain.pem
TLS_KEY_PATH=/etc/letsencrypt/live/your-domain.com/privkey.pem
STORAGE_TYPE=database
LOG_LEVEL=info
```
### TLS Certificates
#### Using Let's Encrypt
```bash
sudo apt-get install certbot
# Get certificate
sudo certbot certonly --standalone -d your-domain.com
# Auto-renewal
sudo crontab -e
# Add: 0 3 * * * certbot renew --quiet
```
### SystemD Service
Create `/etc/systemd/system/prosody-nodejs.service`:
```ini
[Unit]
Description=Prosody Node.js XMPP Server
After=network.target
[Service]
Type=simple
User=prosody-nodejs
Group=prosody-nodejs
WorkingDirectory=/opt/prosody-nodejs
Environment=NODE_ENV=production
ExecStart=/usr/bin/node src/index.js
Restart=always
RestartSec=10
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=prosody-nodejs
[Install]
WantedBy=multi-user.target
```
Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable prosody-nodejs
sudo systemctl start prosody-nodejs
sudo systemctl status prosody-nodejs
```
### Firewall
```bash
sudo ufw allow 5222/tcp # C2S
sudo ufw allow 5269/tcp # S2S
sudo ufw allow 5280/tcp # BOSH
sudo ufw allow 5281/tcp # WebSocket
```
### Reverse Proxy (Nginx)
#### BOSH
Create `/etc/nginx/sites-available/prosody-bosh`:
```nginx
server {
listen 443 ssl http2;
server_name xmpp.your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location /http-bind {
proxy_pass http://localhost:5280/http-bind;
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;
proxy_buffering off;
}
}
```
#### WebSocket
```nginx
server {
listen 443 ssl http2;
server_name ws.your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location /xmpp-websocket {
proxy_pass http://localhost:5281/xmpp-websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
Enable:
```bash
sudo ln -s /etc/nginx/sites-available/prosody-bosh /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```
## Database Setup
### PostgreSQL
```bash
sudo apt-get install postgresql
sudo -u postgres psql
CREATE DATABASE prosody_nodejs;
CREATE USER prosody_nodejs WITH PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE prosody_nodejs TO prosody_nodejs;
```
Update configuration:
```yaml
storage:
type: database
options:
dialect: postgres
host: localhost
database: prosody_nodejs
username: prosody_nodejs
password: password
```
### MongoDB
```bash
sudo apt-get install mongodb
mongo
use prosody_nodejs
db.createUser({
user: "prosody_nodejs",
pwd: "password",
roles: ["readWrite"]
})
```
## Monitoring
### PM2 (Alternative to SystemD)
```bash
sudo npm install -g pm2
pm2 start src/index.js --name prosody-nodejs
pm2 save
pm2 startup
```
### Logs
```bash
# SystemD
sudo journalctl -u prosody-nodejs -f
# PM2
pm2 logs prosody-nodejs
# Application logs
tail -f /opt/prosody-nodejs/logs/prosody-nodejs.log
```
### Metrics
Install monitoring:
```bash
npm install prometheus-client
```
Configure metrics endpoint:
```javascript
// In server setup
const client = require('prom-client');
const collectDefaultMetrics = client.collectDefaultMetrics;
collectDefaultMetrics();
```
## Backup
### Configuration
```bash
# Backup
sudo tar -czf prosody-backup-$(date +%Y%m%d).tar.gz \
/opt/prosody-nodejs/config \
/opt/prosody-nodejs/.env \
/opt/prosody-nodejs/data
# Restore
sudo tar -xzf prosody-backup-20231215.tar.gz -C /
```
### Database
```bash
# PostgreSQL
pg_dump prosody_nodejs > backup.sql
psql prosody_nodejs < backup.sql
# MongoDB
mongodump --db prosody_nodejs --out backup/
mongorestore --db prosody_nodejs backup/prosody_nodejs
```
## Scaling
### Clustering
Deploy multiple instances behind load balancer:
```bash
# Instance 1
SERVER_PORT=5222 npm start
# Instance 2
SERVER_PORT=5223 npm start
```
HAProxy configuration:
```
frontend xmpp
bind *:5222
mode tcp
default_backend xmpp_servers
backend xmpp_servers
mode tcp
balance leastconn
server server1 127.0.0.1:5222 check
server server2 127.0.0.1:5223 check
```
### Database Connection Pooling
```yaml
storage:
options:
pool:
min: 2
max: 10
acquireTimeout: 30000
```
## Security Hardening
### Fail2Ban
Create `/etc/fail2ban/filter.d/prosody-nodejs.conf`:
```ini
[Definition]
failregex = Authentication failed for <HOST>
ignoreregex =
```
Create `/etc/fail2ban/jail.d/prosody-nodejs.conf`:
```ini
[prosody-nodejs]
enabled = true
port = 5222
filter = prosody-nodejs
logpath = /opt/prosody-nodejs/logs/prosody-nodejs.log
maxretry = 5
bantime = 3600
```
Restart:
```bash
sudo systemctl restart fail2ban
```
### AppArmor
Create profile for additional security.
### Regular Updates
```bash
cd /opt/prosody-nodejs
sudo -u prosody-nodejs git pull
sudo -u prosody-nodejs npm install --production
sudo systemctl restart prosody-nodejs
```
## Performance Tuning
### Node.js
```bash
# Increase memory limit
NODE_OPTIONS="--max-old-space-size=4096" npm start
```
### System
```bash
# Increase file descriptors
sudo nano /etc/security/limits.conf
prosody-nodejs soft nofile 65536
prosody-nodejs hard nofile 65536
```
### Database
- Enable connection pooling
- Add indexes on frequently queried fields
- Regular VACUUM (PostgreSQL)
## Troubleshooting
### Check Status
```bash
sudo systemctl status prosody-nodejs
```
### View Logs
```bash
sudo journalctl -u prosody-nodejs -n 100 --no-pager
```
### Test Connection
```bash
telnet localhost 5222
```
### Debug Mode
```bash
LOG_LEVEL=debug sudo systemctl restart prosody-nodejs
```
## Support
- Documentation: [https://github.com/yourusername/prosody-nodejs/docs](docs/)
- Issues: [https://github.com/yourusername/prosody-nodejs/issues](issues)
- Community: XMPP chat at prosody-nodejs@conference.example.com

326
docs/MODULE_DEVELOPMENT.md Archivo normal
Ver fichero

@@ -0,0 +1,326 @@
# Module Development Guide
## Introduction
Modules are the primary way to extend the Prosody Node.js server. They can add new features, handle stanzas, store data, and integrate with external services.
## Module Structure
A basic module consists of a JavaScript file that exports an object with specific properties:
```javascript
module.exports = {
name: 'example',
version: '1.0.0',
description: 'An example module',
author: 'Your Name',
dependencies: [], // Other modules this depends on
load(module) {
// Called when module is loaded
},
unload(module) {
// Called when module is unloaded
}
};
```
## Module API
The `module` object passed to `load()` provides access to the server API:
### Properties
- `module.name` - Module name
- `module.host` - Virtual host this module is loaded for
- `module.api` - API object with helper functions
### API Object
```javascript
module.api = {
config, // Configuration manager
hostManager, // Host manager
sessionManager, // Session manager
stanzaRouter, // Stanza router
storageManager, // Storage manager
events, // Event emitter
logger, // Logger instance
// Helper methods
getConfig(key, defaultValue),
getGlobalConfig(key, defaultValue),
getHostConfig(key, defaultValue),
require(moduleName) // Load another module
};
```
## Examples
### 1. Simple Echo Module
```javascript
// modules/mod_echo.js
module.exports = {
name: 'echo',
version: '1.0.0',
description: 'Echoes messages back to sender',
load(module) {
const { logger, stanzaRouter } = module.api;
logger.info('Echo module loaded');
module.hook('message', (stanza, session) => {
if (stanza.attrs.type === 'chat') {
const body = stanza.getChildText('body');
if (body) {
// Create echo response
const response = stanza.clone();
response.attrs.from = stanza.attrs.to;
response.attrs.to = stanza.attrs.from;
session.send(response);
logger.debug(`Echoed message to ${stanza.attrs.from}`);
}
}
});
}
};
```
### 2. Message Logger Module
```javascript
// modules/mod_message_logger.js
module.exports = {
name: 'message_logger',
version: '1.0.0',
description: 'Logs all messages to storage',
async load(module) {
const { logger, stanzaRouter, storageManager } = module.api;
const store = storageManager.getStore(module.host, 'message_log');
logger.info('Message logger module loaded');
stanzaRouter.on('message', async (stanza, session) => {
try {
const logEntry = {
timestamp: Date.now(),
from: stanza.attrs.from,
to: stanza.attrs.to,
type: stanza.attrs.type,
body: stanza.getChildText('body')
};
const key = `msg_${Date.now()}_${Math.random()}`;
await store.set(key, logEntry);
logger.debug('Message logged');
} catch (error) {
logger.error('Failed to log message:', error);
}
});
}
};
```
### 3. User Statistics Module
```javascript
// modules/mod_user_stats.js
module.exports = {
name: 'user_stats',
version: '1.0.0',
description: 'Tracks user statistics',
load(module) {
const { logger, sessionManager, storageManager } = module.api;
const store = storageManager.getStore(module.host, 'user_stats');
const stats = {
totalMessages: 0,
totalPresence: 0,
onlineUsers: new Set()
};
// Track sessions
sessionManager.on('session:authenticated', (session) => {
stats.onlineUsers.add(session.bareJid);
logger.info(`User online: ${session.bareJid} (total: ${stats.onlineUsers.size})`);
});
sessionManager.on('session:closed', (session) => {
stats.onlineUsers.delete(session.bareJid);
logger.info(`User offline: ${session.bareJid} (total: ${stats.onlineUsers.size})`);
});
// Track stanzas
module.on('message', () => {
stats.totalMessages++;
});
module.on('presence', () => {
stats.totalPresence++;
});
// Expose stats via IQ
module.hook('iq', (stanza, session) => {
if (stanza.attrs.type === 'get') {
const query = stanza.getChild('query');
if (query && query.attrs.xmlns === 'http://example.com/stats') {
const response = new ltx.Element('iq', {
type: 'result',
id: stanza.attrs.id,
to: stanza.attrs.from
}).c('query', { xmlns: 'http://example.com/stats' })
.c('stat', { name: 'messages' }).t(stats.totalMessages.toString()).up()
.c('stat', { name: 'presence' }).t(stats.totalPresence.toString()).up()
.c('stat', { name: 'online' }).t(stats.onlineUsers.size.toString());
session.send(response);
return true; // Handled
}
}
});
}
};
```
### 4. Rate Limiting Module
```javascript
// modules/mod_rate_limit.js
const { RateLimiterMemory } = require('rate-limiter-flexible');
module.exports = {
name: 'rate_limit',
version: '1.0.0',
description: 'Rate limits stanzas per user',
load(module) {
const { logger, stanzaRouter, config } = module.api;
const limiter = new RateLimiterMemory({
points: module.api.getConfig('points', 10),
duration: module.api.getConfig('duration', 1)
});
// Add filter to stanza router
stanzaRouter.addFilter(async (stanza, session) => {
if (!session || !session.bareJid) return true;
try {
await limiter.consume(session.bareJid);
return true; // Allow stanza
} catch (error) {
logger.warn(`Rate limit exceeded for ${session.bareJid}`);
return false; // Block stanza
}
});
logger.info('Rate limiting enabled');
}
};
```
## Hooks and Events
### Stanza Events
- `message` - Message stanza received
- `presence` - Presence stanza received
- `iq` - IQ stanza received
- `iq:get` - IQ get request
- `iq:set` - IQ set request
- `iq:result` - IQ result
- `iq:error` - IQ error
### Session Events
- `session:created` - New session created
- `session:authenticated` - Session authenticated
- `session:closed` - Session closed
### Server Events
- `server:started` - Server started
- `server:stopped` - Server stopped
## Storage
Modules can store persistent data:
```javascript
const store = storageManager.getStore(module.host, 'mydata');
// Store data
await store.set('key', { data: 'value' });
// Retrieve data
const data = await store.get('key');
// Delete data
await store.delete('key');
// Find data
const results = await store.find(item => item.type === 'important');
```
## Configuration
Modules can access configuration:
```javascript
// Module-specific config from moduleConfig section
const enabled = module.api.getConfig('enabled', true);
// Global config
const domain = module.api.getGlobalConfig('server.domain');
// Host-specific config
const modules = module.api.getHostConfig('modules', []);
```
## Best Practices
1. **Error Handling**: Always use try-catch blocks
2. **Logging**: Log important events and errors
3. **Cleanup**: Implement proper cleanup in `unload()`
4. **Performance**: Avoid blocking operations
5. **Testing**: Write tests for your modules
6. **Documentation**: Document module configuration
## Testing
```javascript
// test/mod_example.test.js
const ModuleManager = require('../src/core/module-manager');
describe('Example Module', () => {
it('should load successfully', async () => {
// Test module loading
});
it('should handle messages', async () => {
// Test message handling
});
});
```
## Publishing
To share your module:
1. Create a repository
2. Add proper documentation
3. Include example configuration
4. Publish to npm (optional)
## Resources
- [XMPP RFCs](https://xmpp.org/rfcs/)
- [XMPP Extensions (XEPs)](https://xmpp.org/extensions/)
- [Module Examples](examples/modules/)

283
docs/QUICKSTART.md Archivo normal
Ver fichero

@@ -0,0 +1,283 @@
# Quick Start Guide
## Installation
### Prerequisites
- Node.js 18 or higher
- npm or yarn
### Step 1: Install Dependencies
```bash
cd prosody-nodejs
npm install
```
### Step 2: Create Environment File
```bash
cp .env.example .env
```
Edit `.env`:
```bash
SERVER_HOST=localhost
SERVER_PORT=5222
LOG_LEVEL=info
STORAGE_TYPE=memory
```
### Step 3: Start Server
```bash
# Development mode (with auto-reload)
npm run dev
# Or production mode
npm start
```
You should see:
```
14:23:45 INFO [main] Starting Prosody Node.js XMPP Server...
14:23:45 INFO [config] Configuration loaded successfully
14:23:45 INFO [server] Initializing server components...
14:23:45 INFO [c2s] C2S TLS server listening on 0.0.0.0:5222
14:23:45 INFO [bosh] BOSH server listening on port 5280
14:23:45 INFO [websocket] WebSocket server listening on port 5281
14:23:45 INFO [main] Prosody Node.js is ready to accept connections
```
## Testing with a Client
### Using Pidgin
1. Download [Pidgin](https://pidgin.im/)
2. Add account:
- Protocol: XMPP
- Username: test
- Domain: localhost
- Password: password
3. Advanced tab:
- Connect port: 5222
- Connect server: localhost
4. Connect!
### Using Gajim
1. Download [Gajim](https://gajim.org/)
2. Add account:
- JID: test@localhost
- Password: password
3. Connect!
### Using Node.js Client
```javascript
const { Client } = require('@xmpp/client');
const client = new Client({
service: 'xmpp://localhost:5222',
domain: 'localhost',
username: 'test',
password: 'password'
});
client.on('online', (address) => {
console.log('Connected as', address.toString());
// Send presence
client.send('<presence/>');
});
client.on('stanza', (stanza) => {
console.log('Received:', stanza.toString());
});
client.start().catch(console.error);
```
## Basic Configuration
Edit `config/default.yaml`:
```yaml
server:
domain: localhost
network:
c2s:
enabled: true
port: 5222
virtualHosts:
- domain: localhost
enabled: true
modules:
- roster
- saslauth
- disco
- presence
- message
```
## Adding Virtual Hosts
```yaml
virtualHosts:
- domain: example.com
enabled: true
modules:
- roster
- saslauth
- disco
- domain: conference.example.com
enabled: true
modules:
- muc
```
## Enabling Features
### Message Archive Management (MAM)
```yaml
virtualHosts:
- domain: localhost
modules:
- mam
moduleConfig:
mam:
maxArchiveSize: 10000
```
### Multi-User Chat (MUC)
```yaml
virtualHosts:
- domain: conference.localhost
modules:
- muc
moduleConfig:
muc:
persistentRooms: true
```
### HTTP File Upload
```yaml
modules:
global:
- http_files
moduleConfig:
fileSharing:
enabled: true
maxFileSize: 10485760
uploadPath: ./uploads
```
## Security Setup
### Enable TLS
1. Generate certificates:
```bash
mkdir -p certs
cd certs
# Self-signed certificate (development only)
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes
```
2. Configure:
```yaml
network:
c2s:
tls:
enabled: true
required: false
```
```bash
TLS_CERT_PATH=./certs/server.crt
TLS_KEY_PATH=./certs/server.key
```
### Enable Rate Limiting
```yaml
rateLimit:
enabled: true
maxPointsPerSecond: 10
blockDuration: 60
```
### Set Connection Limits
```yaml
security:
maxConnectionsPerIP: 5
connectionTimeout: 60000
```
## Monitoring
### View Logs
```bash
tail -f logs/prosody-nodejs.log
```
### Enable Debug Logging
```bash
LOG_LEVEL=debug npm start
```
## Next Steps
- Read [Configuration Guide](CONFIGURATION.md)
- Learn about [Module Development](MODULE_DEVELOPMENT.md)
- Check [API Documentation](API.md)
- See [Examples](../examples/)
## Common Issues
### Port Already in Use
```
Error: listen EADDRINUSE: address already in use :::5222
```
Solution: Change port in configuration or stop other XMPP server.
### Permission Denied
```
Error: listen EACCES: permission denied 0.0.0.0:5222
```
Solution: Use port > 1024 or run with sudo (not recommended).
### TLS Certificate Error
```
TLS client error: unable to verify certificate
```
Solution: Use proper certificates or configure client to accept self-signed.
## Getting Help
- Documentation: [docs/](.)
- GitHub Issues: [Report a bug](https://github.com/yourusername/prosody-nodejs/issues)
- XMPP Chat: prosody-nodejs@conference.example.com

262
docs/STRUCTURE.md Archivo normal
Ver fichero

@@ -0,0 +1,262 @@
# Project Structure
```
prosody-nodejs/
├── config/
│ └── default.yaml # Default server configuration
├── src/
│ ├── index.js # Main entry point
│ ├── core/ # Core server components
│ │ ├── server.js # Main server orchestrator
│ │ ├── config-manager.js # Configuration management
│ │ ├── session-manager.js # Session management
│ │ ├── stanza-router.js # Stanza routing
│ │ ├── host-manager.js # Virtual host management
│ │ ├── module-manager.js # Module system
│ │ ├── xmpp-stream.js # XMPP stream handler
│ │ ├── c2s-server.js # Client-to-Server
│ │ ├── s2s-server.js # Server-to-Server
│ │ ├── bosh-server.js # BOSH HTTP binding
│ │ ├── websocket-server.js # WebSocket support
│ │ └── component-server.js # Component protocol
│ ├── modules/ # Server modules
│ │ ├── mod_roster.js # Roster management
│ │ ├── mod_disco.js # Service discovery
│ │ ├── mod_presence.js # Presence handling
│ │ ├── mod_message.js # Message handling
│ │ ├── mod_mam.js # Message archive
│ │ ├── mod_muc.js # Multi-user chat
│ │ └── ...
│ ├── storage/ # Storage backends
│ │ ├── storage-manager.js
│ │ └── storage/
│ │ ├── memory.js # In-memory storage
│ │ ├── file.js # File-based storage
│ │ └── database.js # Database storage
│ └── utils/ # Utilities
│ ├── logger.js # Logging
│ ├── jid.js # JID parsing
│ └── xml.js # XML utilities
├── docs/ # Documentation
│ ├── QUICKSTART.md # Quick start guide
│ ├── API.md # API documentation
│ ├── MODULE_DEVELOPMENT.md # Module dev guide
│ └── DEPLOYMENT.md # Deployment guide
├── examples/ # Example code
│ ├── echo-bot.js # Echo bot example
│ ├── simple-client.js # Client example
│ └── modules/ # Example modules
│ └── mod_welcome.js
├── test/ # Tests
│ ├── core/
│ ├── modules/
│ └── integration/
├── logs/ # Log files (created at runtime)
├── data/ # Data storage (created at runtime)
├── certs/ # TLS certificates
├── uploads/ # File uploads
├── .env.example # Example environment variables
├── .gitignore # Git ignore file
├── package.json # NPM package configuration
├── setup.sh # Setup script
└── README.md # Main README
## File Descriptions
### Core Files
#### src/index.js
Main entry point. Initializes and starts the server.
#### src/core/server.js
Main server class that orchestrates all components:
- Initializes managers
- Starts network servers
- Handles lifecycle
#### src/core/config-manager.js
Singleton configuration manager:
- Loads YAML configuration
- Applies environment variables
- Provides configuration access
#### src/core/session-manager.js
Manages all active XMPP sessions:
- Session creation and authentication
- JID-to-session mapping
- Session lifecycle events
#### src/core/stanza-router.js
Routes XMPP stanzas to destinations:
- Message routing
- Presence broadcasting
- IQ request handling
#### src/core/host-manager.js
Manages virtual hosts:
- Multi-domain support
- Per-host configuration
- Module management per host
#### src/core/module-manager.js
Plugin/module system:
- Module loading and unloading
- Module API provisioning
- Event hooks
#### src/core/xmpp-stream.js
Handles XMPP stream negotiation:
- Stream features
- SASL authentication
- Resource binding
- Stream management
### Network Servers
#### src/core/c2s-server.js
Client-to-Server connections:
- TCP/TLS socket handling
- Connection limits
- Stream initialization
#### src/core/s2s-server.js
Server-to-Server federation:
- Dialback authentication
- Remote server connections
- Stanza forwarding
#### src/core/bosh-server.js
HTTP binding (BOSH):
- HTTP long-polling
- Session management
- CORS support
#### src/core/websocket-server.js
WebSocket support:
- WebSocket connections
- XMPP framing protocol
- Binary/text frames
#### src/core/component-server.js
External component protocol:
- Component authentication
- Component routing
- XEP-0114 support
### Storage
#### src/storage/storage-manager.js
Storage abstraction layer:
- Multiple backend support
- Store creation and management
- Host isolation
#### src/storage/storage/memory.js
In-memory storage implementation:
- Fast development storage
- No persistence
- Simple key-value store
### Modules
Modules extend server functionality. Each module can:
- Hook into events
- Handle stanzas
- Store data
- Interact with other modules
### Utilities
#### src/utils/logger.js
Winston-based logging:
- Multiple log levels
- File and console output
- Labeled loggers
## Configuration Files
### config/default.yaml
Main configuration file with:
- Server settings
- Network configuration
- Virtual hosts
- Module configuration
- Security settings
### .env
Environment-specific settings:
- Overrides YAML config
- Secrets and credentials
- Environment variables
## Runtime Directories
### logs/
Application log files:
- prosody-nodejs.log - Main log
- error.log - Error log
### data/
Runtime data storage:
- Session data
- Temporary files
- Database files (if file-based)
### certs/
TLS/SSL certificates:
- server.crt - Server certificate
- server.key - Private key
### uploads/
User file uploads (if enabled)
## Development Files
### test/
Unit and integration tests
### examples/
Example code and modules
### docs/
Comprehensive documentation
## Entry Points
1. **Server**: `npm start` runs `src/index.js`
2. **Development**: `npm run dev` runs with nodemon
3. **Tests**: `npm test` runs Jest tests
4. **Setup**: `./setup.sh` for initial setup
## Key Concepts
### Hosts
Virtual hosts allow multi-domain support. Each host can have:
- Own configuration
- Own modules
- Own data storage
### Sessions
Represent active client connections:
- Authenticated or not
- Associated with JID
- Has presence and priority
### Stanzas
XML elements in XMPP:
- message - Chat messages
- presence - Availability
- iq - Info/Query requests
### Modules
Extend server functionality:
- Load per host
- Hook into events
- Access server API
### Storage
Persistent data:
- Per-host isolation
- Multiple backends
- Key-value or document
```