330
docs/API.md
Archivo normal
330
docs/API.md
Archivo normal
@@ -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
283
docs/ARCHITECTURE.md
Archivo normal
@@ -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
415
docs/DEPLOYMENT.md
Archivo normal
@@ -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
326
docs/MODULE_DEVELOPMENT.md
Archivo normal
@@ -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
283
docs/QUICKSTART.md
Archivo normal
@@ -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
262
docs/STRUCTURE.md
Archivo normal
@@ -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
|
||||
```
|
||||
Referencia en una nueva incidencia
Block a user