@xtr-dev/rondevu-server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +12 -0
- package/API.md +428 -0
- package/DEPLOYMENT.md +346 -0
- package/Dockerfile +57 -0
- package/README.md +242 -0
- package/build.js +17 -0
- package/dist/index.js +437 -0
- package/dist/index.js.map +7 -0
- package/package.json +26 -0
- package/src/app.ts +228 -0
- package/src/config.ts +26 -0
- package/src/index.ts +59 -0
- package/src/storage/kv.ts +241 -0
- package/src/storage/sqlite.ts +258 -0
- package/src/storage/types.ts +90 -0
- package/src/worker.ts +39 -0
- package/tsconfig.json +20 -0
- package/wrangler.toml.example +26 -0
package/DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Deployment Guide
|
|
2
|
+
|
|
3
|
+
This guide covers deploying Rondevu to various platforms.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Cloudflare Workers](#cloudflare-workers)
|
|
8
|
+
- [Docker](#docker)
|
|
9
|
+
- [Node.js](#nodejs)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Cloudflare Workers
|
|
14
|
+
|
|
15
|
+
Deploy to Cloudflare's edge network using Cloudflare Workers and KV storage.
|
|
16
|
+
|
|
17
|
+
### Prerequisites
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g wrangler
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Setup
|
|
24
|
+
|
|
25
|
+
1. **Login to Cloudflare**
|
|
26
|
+
```bash
|
|
27
|
+
wrangler login
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. **Create KV Namespace**
|
|
31
|
+
```bash
|
|
32
|
+
# For production
|
|
33
|
+
wrangler kv:namespace create SESSIONS
|
|
34
|
+
|
|
35
|
+
# This will output something like:
|
|
36
|
+
# { binding = "SESSIONS", id = "abc123..." }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
3. **Update wrangler.toml**
|
|
40
|
+
|
|
41
|
+
Edit `wrangler.toml` and replace `YOUR_KV_NAMESPACE_ID` with the ID from step 2:
|
|
42
|
+
|
|
43
|
+
```toml
|
|
44
|
+
[[kv_namespaces]]
|
|
45
|
+
binding = "SESSIONS"
|
|
46
|
+
id = "abc123..." # Your actual KV namespace ID
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
4. **Configure Environment Variables** (Optional)
|
|
50
|
+
|
|
51
|
+
Update `wrangler.toml` to customize settings:
|
|
52
|
+
|
|
53
|
+
```toml
|
|
54
|
+
[vars]
|
|
55
|
+
SESSION_TIMEOUT = "300000" # Session timeout in milliseconds
|
|
56
|
+
CORS_ORIGINS = "https://example.com,https://app.example.com"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Local Development
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Run locally with Wrangler
|
|
63
|
+
npx wrangler dev
|
|
64
|
+
|
|
65
|
+
# The local development server will:
|
|
66
|
+
# - Start on http://localhost:8787
|
|
67
|
+
# - Use a local KV namespace automatically
|
|
68
|
+
# - Hot-reload on file changes
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Production Deployment
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Deploy to Cloudflare Workers
|
|
75
|
+
npx wrangler deploy
|
|
76
|
+
|
|
77
|
+
# This will output your worker URL:
|
|
78
|
+
# https://rondevu.YOUR_SUBDOMAIN.workers.dev
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Custom Domain (Optional)
|
|
82
|
+
|
|
83
|
+
1. Go to your Cloudflare Workers dashboard
|
|
84
|
+
2. Select your worker
|
|
85
|
+
3. Click "Triggers" → "Add Custom Domain"
|
|
86
|
+
4. Enter your domain (e.g., `api.example.com`)
|
|
87
|
+
|
|
88
|
+
### Monitoring
|
|
89
|
+
|
|
90
|
+
View logs and analytics:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Stream real-time logs
|
|
94
|
+
npx wrangler tail
|
|
95
|
+
|
|
96
|
+
# View in dashboard
|
|
97
|
+
# Visit: https://dash.cloudflare.com → Workers & Pages
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Environment Variables
|
|
101
|
+
|
|
102
|
+
| Variable | Default | Description |
|
|
103
|
+
|----------|---------|-------------|
|
|
104
|
+
| `SESSION_TIMEOUT` | `300000` | Session timeout in milliseconds |
|
|
105
|
+
| `CORS_ORIGINS` | `*` | Comma-separated allowed origins |
|
|
106
|
+
|
|
107
|
+
### Pricing
|
|
108
|
+
|
|
109
|
+
Cloudflare Workers Free Tier includes:
|
|
110
|
+
- 100,000 requests/day
|
|
111
|
+
- 10ms CPU time per request
|
|
112
|
+
- KV: 100,000 reads/day, 1,000 writes/day
|
|
113
|
+
|
|
114
|
+
For higher usage, see [Cloudflare Workers pricing](https://workers.cloudflare.com/#plans).
|
|
115
|
+
|
|
116
|
+
### Advantages
|
|
117
|
+
|
|
118
|
+
- **Global Edge Network**: Deploy to 300+ locations worldwide
|
|
119
|
+
- **Instant Scaling**: Handles traffic spikes automatically
|
|
120
|
+
- **Low Latency**: Runs close to your users
|
|
121
|
+
- **No Server Management**: Fully serverless
|
|
122
|
+
- **Free Tier**: Generous limits for small projects
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Docker
|
|
127
|
+
|
|
128
|
+
### Quick Start
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Build
|
|
132
|
+
docker build -t rondevu .
|
|
133
|
+
|
|
134
|
+
# Run with in-memory SQLite
|
|
135
|
+
docker run -p 3000:3000 -e STORAGE_PATH=:memory: rondevu
|
|
136
|
+
|
|
137
|
+
# Run with persistent SQLite
|
|
138
|
+
docker run -p 3000:3000 \
|
|
139
|
+
-v $(pwd)/data:/app/data \
|
|
140
|
+
-e STORAGE_PATH=/app/data/sessions.db \
|
|
141
|
+
rondevu
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Docker Compose
|
|
145
|
+
|
|
146
|
+
Create a `docker-compose.yml`:
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
version: '3.8'
|
|
150
|
+
|
|
151
|
+
services:
|
|
152
|
+
rondevu:
|
|
153
|
+
build: .
|
|
154
|
+
ports:
|
|
155
|
+
- "3000:3000"
|
|
156
|
+
environment:
|
|
157
|
+
- PORT=3000
|
|
158
|
+
- STORAGE_TYPE=sqlite
|
|
159
|
+
- STORAGE_PATH=/app/data/sessions.db
|
|
160
|
+
- SESSION_TIMEOUT=300000
|
|
161
|
+
- CORS_ORIGINS=*
|
|
162
|
+
volumes:
|
|
163
|
+
- ./data:/app/data
|
|
164
|
+
restart: unless-stopped
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Run with:
|
|
168
|
+
```bash
|
|
169
|
+
docker-compose up -d
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Environment Variables
|
|
173
|
+
|
|
174
|
+
| Variable | Default | Description |
|
|
175
|
+
|----------|---------|-------------|
|
|
176
|
+
| `PORT` | `3000` | Server port |
|
|
177
|
+
| `STORAGE_TYPE` | `sqlite` | Storage backend |
|
|
178
|
+
| `STORAGE_PATH` | `/app/data/sessions.db` | SQLite database path |
|
|
179
|
+
| `SESSION_TIMEOUT` | `300000` | Session timeout in ms |
|
|
180
|
+
| `CORS_ORIGINS` | `*` | Allowed CORS origins |
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Node.js
|
|
185
|
+
|
|
186
|
+
### Production Deployment
|
|
187
|
+
|
|
188
|
+
1. **Install Dependencies**
|
|
189
|
+
```bash
|
|
190
|
+
npm ci --production
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
2. **Build TypeScript**
|
|
194
|
+
```bash
|
|
195
|
+
npm run build
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
3. **Set Environment Variables**
|
|
199
|
+
```bash
|
|
200
|
+
export PORT=3000
|
|
201
|
+
export STORAGE_TYPE=sqlite
|
|
202
|
+
export STORAGE_PATH=./data/sessions.db
|
|
203
|
+
export SESSION_TIMEOUT=300000
|
|
204
|
+
export CORS_ORIGINS=*
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
4. **Run**
|
|
208
|
+
```bash
|
|
209
|
+
npm start
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Process Manager (PM2)
|
|
213
|
+
|
|
214
|
+
For production, use a process manager like PM2:
|
|
215
|
+
|
|
216
|
+
1. **Install PM2**
|
|
217
|
+
```bash
|
|
218
|
+
npm install -g pm2
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
2. **Create ecosystem.config.js**
|
|
222
|
+
```javascript
|
|
223
|
+
module.exports = {
|
|
224
|
+
apps: [{
|
|
225
|
+
name: 'rondevu',
|
|
226
|
+
script: './dist/index.js',
|
|
227
|
+
instances: 'max',
|
|
228
|
+
exec_mode: 'cluster',
|
|
229
|
+
env: {
|
|
230
|
+
NODE_ENV: 'production',
|
|
231
|
+
PORT: 3000,
|
|
232
|
+
STORAGE_TYPE: 'sqlite',
|
|
233
|
+
STORAGE_PATH: './data/sessions.db',
|
|
234
|
+
SESSION_TIMEOUT: 300000,
|
|
235
|
+
CORS_ORIGINS: '*'
|
|
236
|
+
}
|
|
237
|
+
}]
|
|
238
|
+
};
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
3. **Start with PM2**
|
|
242
|
+
```bash
|
|
243
|
+
pm2 start ecosystem.config.js
|
|
244
|
+
pm2 save
|
|
245
|
+
pm2 startup
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Systemd Service
|
|
249
|
+
|
|
250
|
+
Create `/etc/systemd/system/rondevu.service`:
|
|
251
|
+
|
|
252
|
+
```ini
|
|
253
|
+
[Unit]
|
|
254
|
+
Description=Rondevu Peer Discovery and Signaling Server
|
|
255
|
+
After=network.target
|
|
256
|
+
|
|
257
|
+
[Service]
|
|
258
|
+
Type=simple
|
|
259
|
+
User=www-data
|
|
260
|
+
WorkingDirectory=/opt/rondevu
|
|
261
|
+
ExecStart=/usr/bin/node dist/index.js
|
|
262
|
+
Restart=on-failure
|
|
263
|
+
Environment=PORT=3000
|
|
264
|
+
Environment=STORAGE_TYPE=sqlite
|
|
265
|
+
Environment=STORAGE_PATH=/opt/rondevu/data/sessions.db
|
|
266
|
+
Environment=SESSION_TIMEOUT=300000
|
|
267
|
+
Environment=CORS_ORIGINS=*
|
|
268
|
+
|
|
269
|
+
[Install]
|
|
270
|
+
WantedBy=multi-user.target
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Enable and start:
|
|
274
|
+
```bash
|
|
275
|
+
sudo systemctl enable rondevu
|
|
276
|
+
sudo systemctl start rondevu
|
|
277
|
+
sudo systemctl status rondevu
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Troubleshooting
|
|
283
|
+
|
|
284
|
+
### Docker
|
|
285
|
+
|
|
286
|
+
**Issue: Permission denied on /app/data**
|
|
287
|
+
- Ensure volume permissions are correct
|
|
288
|
+
- The container runs as user `node` (UID 1000)
|
|
289
|
+
|
|
290
|
+
**Issue: Database locked**
|
|
291
|
+
- Don't share the same SQLite database file across multiple containers
|
|
292
|
+
- Use one instance or implement a different storage backend
|
|
293
|
+
|
|
294
|
+
### Node.js
|
|
295
|
+
|
|
296
|
+
**Issue: EADDRINUSE**
|
|
297
|
+
- Port is already in use, change `PORT` environment variable
|
|
298
|
+
|
|
299
|
+
**Issue: Database is locked**
|
|
300
|
+
- Another process is using the database
|
|
301
|
+
- Ensure only one instance is running with the same database file
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Performance Tuning
|
|
306
|
+
|
|
307
|
+
### Node.js/Docker
|
|
308
|
+
|
|
309
|
+
- Set `SESSION_TIMEOUT` appropriately to balance resource usage
|
|
310
|
+
- For high traffic, use `STORAGE_PATH=:memory:` with session replication
|
|
311
|
+
- Consider horizontal scaling with a shared database backend
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Security Considerations
|
|
316
|
+
|
|
317
|
+
1. **HTTPS**: Always use HTTPS in production
|
|
318
|
+
- Use a reverse proxy (nginx, Caddy) for Node.js deployments
|
|
319
|
+
- Docker deployments should be behind a reverse proxy
|
|
320
|
+
|
|
321
|
+
2. **Rate Limiting**: Implement rate limiting at the proxy level
|
|
322
|
+
|
|
323
|
+
3. **CORS**: Configure CORS origins appropriately
|
|
324
|
+
- Don't use `*` in production
|
|
325
|
+
- Set specific allowed origins: `https://example.com,https://app.example.com`
|
|
326
|
+
|
|
327
|
+
4. **Input Validation**: SDP offers/answers are stored as-is; validate on client side
|
|
328
|
+
|
|
329
|
+
5. **Session Codes**: UUID v4 codes provide strong entropy (2^122 combinations)
|
|
330
|
+
|
|
331
|
+
6. **Origin Isolation**: Sessions are isolated by Origin header to organize topics by domain
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Scaling
|
|
336
|
+
|
|
337
|
+
### Horizontal Scaling
|
|
338
|
+
|
|
339
|
+
- **Docker/Node.js**: Use a shared database (not SQLite) for multiple instances
|
|
340
|
+
- Implement a Redis or PostgreSQL storage adapter
|
|
341
|
+
|
|
342
|
+
### Vertical Scaling
|
|
343
|
+
|
|
344
|
+
- Increase `SESSION_TIMEOUT` or cleanup frequency as needed
|
|
345
|
+
- Monitor database size and connection pool
|
|
346
|
+
- For Node.js, monitor memory usage and increase if needed
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Build stage
|
|
2
|
+
FROM node:20-alpine AS builder
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
# Copy package files
|
|
7
|
+
COPY package*.json ./
|
|
8
|
+
|
|
9
|
+
# Install dependencies
|
|
10
|
+
RUN npm ci
|
|
11
|
+
|
|
12
|
+
# Copy source files
|
|
13
|
+
COPY tsconfig.json ./
|
|
14
|
+
COPY build.js ./
|
|
15
|
+
COPY src ./src
|
|
16
|
+
|
|
17
|
+
# Build TypeScript
|
|
18
|
+
RUN npm run build
|
|
19
|
+
|
|
20
|
+
# Production stage
|
|
21
|
+
FROM node:20-alpine
|
|
22
|
+
|
|
23
|
+
WORKDIR /app
|
|
24
|
+
|
|
25
|
+
# Install production dependencies only
|
|
26
|
+
COPY package*.json ./
|
|
27
|
+
RUN npm ci --omit=dev && \
|
|
28
|
+
npm cache clean --force
|
|
29
|
+
|
|
30
|
+
# Copy built files from builder
|
|
31
|
+
COPY --from=builder /app/dist ./dist
|
|
32
|
+
|
|
33
|
+
# Create data directory for SQLite
|
|
34
|
+
RUN mkdir -p /app/data && \
|
|
35
|
+
chown -R node:node /app
|
|
36
|
+
|
|
37
|
+
# Switch to non-root user
|
|
38
|
+
USER node
|
|
39
|
+
|
|
40
|
+
# Environment variables with defaults
|
|
41
|
+
ENV PORT=3000
|
|
42
|
+
ENV STORAGE_TYPE=sqlite
|
|
43
|
+
ENV STORAGE_PATH=/app/data/sessions.db
|
|
44
|
+
ENV SESSION_TIMEOUT=300000
|
|
45
|
+
ENV CODE_CHARS=0123456789
|
|
46
|
+
ENV CODE_LENGTH=9
|
|
47
|
+
ENV CORS_ORIGINS=*
|
|
48
|
+
|
|
49
|
+
# Expose port
|
|
50
|
+
EXPOSE 3000
|
|
51
|
+
|
|
52
|
+
# Health check
|
|
53
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
54
|
+
CMD node -e "require('http').get('http://localhost:${PORT}/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
|
|
55
|
+
|
|
56
|
+
# Start server
|
|
57
|
+
CMD ["node", "dist/index.js"]
|
package/README.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Rondevu
|
|
2
|
+
|
|
3
|
+
An open signaling and tracking server for peer discovery. Enables peers to find each other through a topic-based HTTP API with Origin isolation for organizing peer-to-peer applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Fast & Lightweight** - Built with [Hono](https://hono.dev/) framework
|
|
8
|
+
- 📂 **Topic-Based Organization** - Group sessions by topic for easy peer discovery
|
|
9
|
+
- 🔒 **Origin Isolation** - Sessions are isolated by HTTP Origin header to group topics by domain
|
|
10
|
+
- 🏷️ **Peer Identification** - Info field prevents duplicate connections to same peer
|
|
11
|
+
- 🔌 **Pluggable Storage** - Storage interface supports SQLite and in-memory adapters
|
|
12
|
+
- 🐳 **Docker Ready** - Minimal Alpine-based Docker image
|
|
13
|
+
- ⏱️ **Session Timeout** - Configurable session expiration from initiation time
|
|
14
|
+
- 🔐 **Type Safe** - Written in TypeScript with full type definitions
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
### Using Node.js
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Install dependencies
|
|
22
|
+
npm install
|
|
23
|
+
|
|
24
|
+
# Run in development mode
|
|
25
|
+
npm run dev
|
|
26
|
+
|
|
27
|
+
# Build and run in production
|
|
28
|
+
npm run build
|
|
29
|
+
npm start
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Using Docker
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Build the image
|
|
36
|
+
docker build -t rondevu .
|
|
37
|
+
|
|
38
|
+
# Run with default settings (SQLite database)
|
|
39
|
+
docker run -p 3000:3000 rondevu
|
|
40
|
+
|
|
41
|
+
# Run with in-memory storage
|
|
42
|
+
docker run -p 3000:3000 -e STORAGE_TYPE=memory rondevu
|
|
43
|
+
|
|
44
|
+
# Run with custom timeout (10 minutes)
|
|
45
|
+
docker run -p 3000:3000 -e SESSION_TIMEOUT=600000 rondevu
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Using Cloudflare Workers
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Install Wrangler CLI
|
|
52
|
+
npm install -g wrangler
|
|
53
|
+
|
|
54
|
+
# Login to Cloudflare
|
|
55
|
+
wrangler login
|
|
56
|
+
|
|
57
|
+
# Create KV namespace
|
|
58
|
+
wrangler kv:namespace create SESSIONS
|
|
59
|
+
|
|
60
|
+
# Update wrangler.toml with the KV namespace ID
|
|
61
|
+
|
|
62
|
+
# Deploy to Cloudflare's edge network
|
|
63
|
+
npx wrangler deploy
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
See [DEPLOYMENT.md](./DEPLOYMENT.md#cloudflare-workers) for detailed instructions.
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
Configuration is done through environment variables:
|
|
71
|
+
|
|
72
|
+
| Variable | Description | Default |
|
|
73
|
+
|--------------------|--------------------------------------------------|-------------|
|
|
74
|
+
| `PORT` | Server port | `3000` |
|
|
75
|
+
| `STORAGE_TYPE` | Storage backend: `sqlite` or `memory` | `sqlite` |
|
|
76
|
+
| `STORAGE_PATH` | Path to SQLite database file | `./data.db` |
|
|
77
|
+
| `SESSION_TIMEOUT` | Session timeout in milliseconds | `300000` |
|
|
78
|
+
| `CORS_ORIGINS` | Comma-separated list of allowed origins | `*` |
|
|
79
|
+
|
|
80
|
+
### Example .env file
|
|
81
|
+
|
|
82
|
+
```env
|
|
83
|
+
PORT=3000
|
|
84
|
+
STORAGE_TYPE=sqlite
|
|
85
|
+
STORAGE_PATH=./sessions.db
|
|
86
|
+
SESSION_TIMEOUT=300000
|
|
87
|
+
CORS_ORIGINS=https://example.com,https://app.example.com
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## API Documentation
|
|
91
|
+
|
|
92
|
+
See [API.md](./API.md) for complete API documentation.
|
|
93
|
+
|
|
94
|
+
### Quick Overview
|
|
95
|
+
|
|
96
|
+
**List all active topics (with pagination):**
|
|
97
|
+
```bash
|
|
98
|
+
curl -X GET http://localhost:3000/ \
|
|
99
|
+
-H "Origin: https://example.com"
|
|
100
|
+
# Returns: {"topics":[{"topic":"my-room","count":3}],"pagination":{...}}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Create an offer (announce yourself as available):**
|
|
104
|
+
```bash
|
|
105
|
+
curl -X POST http://localhost:3000/my-room/offer \
|
|
106
|
+
-H "Content-Type: application/json" \
|
|
107
|
+
-H "Origin: https://example.com" \
|
|
108
|
+
-d '{"info":"peer-123","offer":"<SIGNALING_DATA>"}'
|
|
109
|
+
# Returns: {"code":"550e8400-e29b-41d4-a716-446655440000"}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**List available peers in a topic:**
|
|
113
|
+
```bash
|
|
114
|
+
curl -X GET http://localhost:3000/my-room/sessions \
|
|
115
|
+
-H "Origin: https://example.com"
|
|
116
|
+
# Returns: {"sessions":[...]}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Connect to a peer:**
|
|
120
|
+
```bash
|
|
121
|
+
curl -X POST http://localhost:3000/answer \
|
|
122
|
+
-H "Content-Type: application/json" \
|
|
123
|
+
-H "Origin: https://example.com" \
|
|
124
|
+
-d '{"code":"550e8400-...","answer":"<SIGNALING_DATA>","side":"answerer"}'
|
|
125
|
+
# Returns: {"success":true}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Architecture
|
|
129
|
+
|
|
130
|
+
### Storage Interface
|
|
131
|
+
|
|
132
|
+
The storage layer is abstracted through a simple interface, making it easy to implement custom storage backends:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
interface Storage {
|
|
136
|
+
createSession(origin: string, topic: string, info: string, offer: string, expiresAt: number): Promise<string>;
|
|
137
|
+
listSessionsByTopic(origin: string, topic: string): Promise<Session[]>;
|
|
138
|
+
getSession(code: string, origin: string): Promise<Session | null>;
|
|
139
|
+
updateSession(code: string, origin: string, update: Partial<Session>): Promise<void>;
|
|
140
|
+
deleteSession(code: string): Promise<void>;
|
|
141
|
+
cleanup(): Promise<void>;
|
|
142
|
+
close(): Promise<void>;
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Built-in Storage Adapters
|
|
147
|
+
|
|
148
|
+
**SQLite Storage** (`sqlite.ts`)
|
|
149
|
+
- For Node.js/Docker deployments
|
|
150
|
+
- Persistent file-based or in-memory
|
|
151
|
+
- Automatic session cleanup
|
|
152
|
+
- Simple and reliable
|
|
153
|
+
|
|
154
|
+
**Cloudflare KV Storage** (`kv.ts`)
|
|
155
|
+
- For Cloudflare Workers deployments
|
|
156
|
+
- Global edge storage
|
|
157
|
+
- Automatic TTL-based expiration
|
|
158
|
+
- Distributed and highly available
|
|
159
|
+
|
|
160
|
+
### Custom Storage Adapters
|
|
161
|
+
|
|
162
|
+
You can implement your own storage adapter by implementing the `Storage` interface:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { Storage, Session } from './storage/types';
|
|
166
|
+
|
|
167
|
+
export class CustomStorage implements Storage {
|
|
168
|
+
async createSession(offer: string, expiresAt: number): Promise<string> {
|
|
169
|
+
// Your implementation
|
|
170
|
+
}
|
|
171
|
+
// ... implement other methods
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Development
|
|
176
|
+
|
|
177
|
+
### Project Structure
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
rondevu/
|
|
181
|
+
├── src/
|
|
182
|
+
│ ├── index.ts # Node.js server entry point
|
|
183
|
+
│ ├── app.ts # Hono application
|
|
184
|
+
│ ├── config.ts # Configuration
|
|
185
|
+
│ └── storage/
|
|
186
|
+
│ ├── types.ts # Storage interface
|
|
187
|
+
│ ├── sqlite.ts # SQLite adapter
|
|
188
|
+
│ └── codeGenerator.ts # Code generation utility
|
|
189
|
+
├── Dockerfile # Docker build configuration
|
|
190
|
+
├── build.js # Build script
|
|
191
|
+
├── API.md # API documentation
|
|
192
|
+
└── README.md # This file
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Building
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# Build TypeScript
|
|
199
|
+
npm run build
|
|
200
|
+
|
|
201
|
+
# Run built version
|
|
202
|
+
npm start
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Docker Build
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Build the image
|
|
209
|
+
docker build -t rondevu .
|
|
210
|
+
|
|
211
|
+
# Run with volume for persistent storage
|
|
212
|
+
docker run -p 3000:3000 -v $(pwd)/data:/app/data rondevu
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## How It Works
|
|
216
|
+
|
|
217
|
+
1. **Discover topics** (optional): Call `GET /` to see all active topics and peer counts
|
|
218
|
+
2. **Peer A** announces availability by posting to `/:topic/offer` with peer identifier and signaling data
|
|
219
|
+
3. Server generates a unique UUID code and stores the session (bucketed by Origin and topic)
|
|
220
|
+
4. **Peer B** discovers available peers using `GET /:topic/sessions`
|
|
221
|
+
5. **Peer B** filters out their own session using the info field to avoid self-connection
|
|
222
|
+
6. **Peer B** selects a peer and posts their connection data to `POST /answer` with the session code
|
|
223
|
+
7. Both peers exchange signaling data through `POST /answer` endpoint
|
|
224
|
+
8. Both peers poll for updates using `POST /poll` to retrieve connection information
|
|
225
|
+
9. Sessions automatically expire after the configured timeout
|
|
226
|
+
|
|
227
|
+
This allows peers in distributed systems to discover each other without requiring a centralized registry, while maintaining isolation between different applications through Origin headers.
|
|
228
|
+
|
|
229
|
+
### Origin Isolation
|
|
230
|
+
|
|
231
|
+
Sessions are isolated by the HTTP `Origin` header, ensuring that:
|
|
232
|
+
- Peers can only see sessions from their own origin
|
|
233
|
+
- Session codes cannot be accessed cross-origin
|
|
234
|
+
- Topics are organized by application domain
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT
|
|
239
|
+
|
|
240
|
+
## Contributing
|
|
241
|
+
|
|
242
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
package/build.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Build script using esbuild
|
|
2
|
+
const esbuild = require('esbuild');
|
|
3
|
+
|
|
4
|
+
esbuild.build({
|
|
5
|
+
entryPoints: ['src/index.ts'],
|
|
6
|
+
bundle: true,
|
|
7
|
+
platform: 'node',
|
|
8
|
+
target: 'node20',
|
|
9
|
+
outfile: 'dist/index.js',
|
|
10
|
+
format: 'cjs',
|
|
11
|
+
external: [
|
|
12
|
+
'better-sqlite3',
|
|
13
|
+
'@hono/node-server',
|
|
14
|
+
'hono'
|
|
15
|
+
],
|
|
16
|
+
sourcemap: true,
|
|
17
|
+
}).catch(() => process.exit(1));
|