Big commit
This commit is contained in:
208
server/src/db.ts
208
server/src/db.ts
@@ -36,6 +36,34 @@ export async function initDB(): Promise<void> {
|
||||
);
|
||||
`);
|
||||
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS servers (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
ownerId TEXT NOT NULL,
|
||||
ownerPublicKey TEXT NOT NULL,
|
||||
isPrivate INTEGER NOT NULL DEFAULT 0,
|
||||
maxUsers INTEGER NOT NULL DEFAULT 0,
|
||||
currentUsers INTEGER NOT NULL DEFAULT 0,
|
||||
tags TEXT NOT NULL DEFAULT '[]',
|
||||
createdAt INTEGER NOT NULL,
|
||||
lastSeen INTEGER NOT NULL
|
||||
);
|
||||
`);
|
||||
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS join_requests (
|
||||
id TEXT PRIMARY KEY,
|
||||
serverId TEXT NOT NULL,
|
||||
userId TEXT NOT NULL,
|
||||
userPublicKey TEXT NOT NULL,
|
||||
displayName TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
createdAt INTEGER NOT NULL
|
||||
);
|
||||
`);
|
||||
|
||||
persist();
|
||||
}
|
||||
|
||||
@@ -46,6 +74,10 @@ function persist(): void {
|
||||
fs.writeFileSync(DB_FILE, buffer);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Auth Users */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export interface AuthUser {
|
||||
id: string;
|
||||
username: string;
|
||||
@@ -100,3 +132,179 @@ export async function createUser(user: AuthUser): Promise<void> {
|
||||
stmt.free();
|
||||
persist();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Servers */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export interface ServerInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
ownerId: string;
|
||||
ownerPublicKey: string;
|
||||
isPrivate: boolean;
|
||||
maxUsers: number;
|
||||
currentUsers: number;
|
||||
tags: string[];
|
||||
createdAt: number;
|
||||
lastSeen: number;
|
||||
}
|
||||
|
||||
function rowToServer(r: any): ServerInfo {
|
||||
return {
|
||||
id: String(r.id),
|
||||
name: String(r.name),
|
||||
description: r.description ? String(r.description) : undefined,
|
||||
ownerId: String(r.ownerId),
|
||||
ownerPublicKey: String(r.ownerPublicKey),
|
||||
isPrivate: !!r.isPrivate,
|
||||
maxUsers: Number(r.maxUsers),
|
||||
currentUsers: Number(r.currentUsers),
|
||||
tags: JSON.parse(String(r.tags || '[]')),
|
||||
createdAt: Number(r.createdAt),
|
||||
lastSeen: Number(r.lastSeen),
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAllPublicServers(): Promise<ServerInfo[]> {
|
||||
if (!db) await initDB();
|
||||
const stmt: any = db!.prepare('SELECT * FROM servers WHERE isPrivate = 0');
|
||||
const results: ServerInfo[] = [];
|
||||
while (stmt.step()) {
|
||||
results.push(rowToServer(stmt.getAsObject()));
|
||||
}
|
||||
stmt.free();
|
||||
return results;
|
||||
}
|
||||
|
||||
export async function getServerById(id: string): Promise<ServerInfo | null> {
|
||||
if (!db) await initDB();
|
||||
const stmt: any = db!.prepare('SELECT * FROM servers WHERE id = ? LIMIT 1');
|
||||
stmt.bind([id]);
|
||||
let row: ServerInfo | null = null;
|
||||
if (stmt.step()) {
|
||||
row = rowToServer(stmt.getAsObject());
|
||||
}
|
||||
stmt.free();
|
||||
return row;
|
||||
}
|
||||
|
||||
export async function upsertServer(server: ServerInfo): Promise<void> {
|
||||
if (!db) await initDB();
|
||||
const stmt = db!.prepare(`
|
||||
INSERT OR REPLACE INTO servers (id, name, description, ownerId, ownerPublicKey, isPrivate, maxUsers, currentUsers, tags, createdAt, lastSeen)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
stmt.bind([
|
||||
server.id,
|
||||
server.name,
|
||||
server.description ?? null,
|
||||
server.ownerId,
|
||||
server.ownerPublicKey,
|
||||
server.isPrivate ? 1 : 0,
|
||||
server.maxUsers,
|
||||
server.currentUsers,
|
||||
JSON.stringify(server.tags),
|
||||
server.createdAt,
|
||||
server.lastSeen,
|
||||
]);
|
||||
stmt.step();
|
||||
stmt.free();
|
||||
persist();
|
||||
}
|
||||
|
||||
export async function deleteServer(id: string): Promise<void> {
|
||||
if (!db) await initDB();
|
||||
const stmt = db!.prepare('DELETE FROM servers WHERE id = ?');
|
||||
stmt.bind([id]);
|
||||
stmt.step();
|
||||
stmt.free();
|
||||
// Also clean up related join requests
|
||||
const jStmt = db!.prepare('DELETE FROM join_requests WHERE serverId = ?');
|
||||
jStmt.bind([id]);
|
||||
jStmt.step();
|
||||
jStmt.free();
|
||||
persist();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Join Requests */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export interface JoinRequest {
|
||||
id: string;
|
||||
serverId: string;
|
||||
userId: string;
|
||||
userPublicKey: string;
|
||||
displayName: string;
|
||||
status: 'pending' | 'approved' | 'rejected';
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
function rowToJoinRequest(r: any): JoinRequest {
|
||||
return {
|
||||
id: String(r.id),
|
||||
serverId: String(r.serverId),
|
||||
userId: String(r.userId),
|
||||
userPublicKey: String(r.userPublicKey),
|
||||
displayName: String(r.displayName),
|
||||
status: String(r.status) as JoinRequest['status'],
|
||||
createdAt: Number(r.createdAt),
|
||||
};
|
||||
}
|
||||
|
||||
export async function createJoinRequest(req: JoinRequest): Promise<void> {
|
||||
if (!db) await initDB();
|
||||
const stmt = db!.prepare(`
|
||||
INSERT INTO join_requests (id, serverId, userId, userPublicKey, displayName, status, createdAt)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
stmt.bind([req.id, req.serverId, req.userId, req.userPublicKey, req.displayName, req.status, req.createdAt]);
|
||||
stmt.step();
|
||||
stmt.free();
|
||||
persist();
|
||||
}
|
||||
|
||||
export async function getJoinRequestById(id: string): Promise<JoinRequest | null> {
|
||||
if (!db) await initDB();
|
||||
const stmt: any = db!.prepare('SELECT * FROM join_requests WHERE id = ? LIMIT 1');
|
||||
stmt.bind([id]);
|
||||
let row: JoinRequest | null = null;
|
||||
if (stmt.step()) {
|
||||
row = rowToJoinRequest(stmt.getAsObject());
|
||||
}
|
||||
stmt.free();
|
||||
return row;
|
||||
}
|
||||
|
||||
export async function getPendingRequestsForServer(serverId: string): Promise<JoinRequest[]> {
|
||||
if (!db) await initDB();
|
||||
const stmt: any = db!.prepare('SELECT * FROM join_requests WHERE serverId = ? AND status = ?');
|
||||
stmt.bind([serverId, 'pending']);
|
||||
const results: JoinRequest[] = [];
|
||||
while (stmt.step()) {
|
||||
results.push(rowToJoinRequest(stmt.getAsObject()));
|
||||
}
|
||||
stmt.free();
|
||||
return results;
|
||||
}
|
||||
|
||||
export async function updateJoinRequestStatus(id: string, status: JoinRequest['status']): Promise<void> {
|
||||
if (!db) await initDB();
|
||||
const stmt = db!.prepare('UPDATE join_requests SET status = ? WHERE id = ?');
|
||||
stmt.bind([status, id]);
|
||||
stmt.step();
|
||||
stmt.free();
|
||||
persist();
|
||||
}
|
||||
|
||||
export async function deleteStaleJoinRequests(maxAgeMs: number): Promise<void> {
|
||||
if (!db) await initDB();
|
||||
const cutoff = Date.now() - maxAgeMs;
|
||||
const stmt = db!.prepare('DELETE FROM join_requests WHERE createdAt < ?');
|
||||
stmt.bind([cutoff]);
|
||||
stmt.step();
|
||||
stmt.free();
|
||||
persist();
|
||||
}
|
||||
|
||||
@@ -1,40 +1,25 @@
|
||||
import dotenv from 'dotenv';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
// Load .env from project root (one level up from server/)
|
||||
dotenv.config({ path: path.resolve(__dirname, '..', '..', '.env') });
|
||||
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import { createServer } from 'http';
|
||||
import { createServer as createHttpServer } from 'http';
|
||||
import { createServer as createHttpsServer } from 'https';
|
||||
import { WebSocketServer, WebSocket } from 'ws';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const USE_SSL = (process.env.SSL ?? 'false').toLowerCase() === 'true';
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// In-memory storage for servers and users
|
||||
interface ServerInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
ownerId: string;
|
||||
ownerPublicKey: string;
|
||||
isPrivate: boolean;
|
||||
maxUsers: number;
|
||||
currentUsers: number;
|
||||
tags: string[];
|
||||
createdAt: number;
|
||||
lastSeen: number;
|
||||
}
|
||||
|
||||
interface JoinRequest {
|
||||
id: string;
|
||||
serverId: string;
|
||||
userId: string;
|
||||
userPublicKey: string;
|
||||
displayName: string;
|
||||
status: 'pending' | 'approved' | 'rejected';
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
// In-memory runtime state (WebSocket connections only – not persisted)
|
||||
interface ConnectedUser {
|
||||
oderId: string;
|
||||
ws: WebSocket;
|
||||
@@ -43,43 +28,38 @@ interface ConnectedUser {
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
const servers = new Map<string, ServerInfo>();
|
||||
const joinRequests = new Map<string, JoinRequest>();
|
||||
const connectedUsers = new Map<string, ConnectedUser>();
|
||||
|
||||
// Persistence
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
const DATA_DIR = path.join(process.cwd(), 'data');
|
||||
const SERVERS_FILE = path.join(DATA_DIR, 'servers.json');
|
||||
const USERS_FILE = path.join(DATA_DIR, 'users.json');
|
||||
// Database
|
||||
import crypto from 'crypto';
|
||||
import {
|
||||
initDB,
|
||||
getUserByUsername,
|
||||
createUser,
|
||||
getAllPublicServers,
|
||||
getServerById,
|
||||
upsertServer,
|
||||
deleteServer as dbDeleteServer,
|
||||
createJoinRequest,
|
||||
getJoinRequestById,
|
||||
getPendingRequestsForServer,
|
||||
updateJoinRequestStatus,
|
||||
deleteStaleJoinRequests,
|
||||
ServerInfo,
|
||||
JoinRequest,
|
||||
} from './db';
|
||||
|
||||
function ensureDataDir() {
|
||||
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
function saveServers() {
|
||||
ensureDataDir();
|
||||
fs.writeFileSync(SERVERS_FILE, JSON.stringify(Array.from(servers.values()), null, 2));
|
||||
}
|
||||
|
||||
function loadServers() {
|
||||
ensureDataDir();
|
||||
if (fs.existsSync(SERVERS_FILE)) {
|
||||
const raw = fs.readFileSync(SERVERS_FILE, 'utf-8');
|
||||
const list: ServerInfo[] = JSON.parse(raw);
|
||||
list.forEach(s => servers.set(s.id, s));
|
||||
}
|
||||
}
|
||||
function hashPassword(pw: string) { return crypto.createHash('sha256').update(pw).digest('hex'); }
|
||||
|
||||
// REST API Routes
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/api/health', (req, res) => {
|
||||
app.get('/api/health', async (req, res) => {
|
||||
const allServers = await getAllPublicServers();
|
||||
res.json({
|
||||
status: 'ok',
|
||||
timestamp: Date.now(),
|
||||
serverCount: servers.size,
|
||||
serverCount: allServers.length,
|
||||
connectedUsers: connectedUsers.size,
|
||||
});
|
||||
});
|
||||
@@ -129,40 +109,31 @@ app.get('/api/image-proxy', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Basic auth (demo - file-based)
|
||||
interface AuthUser { id: string; username: string; passwordHash: string; displayName: string; createdAt: number; }
|
||||
let authUsers: AuthUser[] = [];
|
||||
function saveUsers() { ensureDataDir(); fs.writeFileSync(USERS_FILE, JSON.stringify(authUsers, null, 2)); }
|
||||
function loadUsers() { ensureDataDir(); if (fs.existsSync(USERS_FILE)) { authUsers = JSON.parse(fs.readFileSync(USERS_FILE,'utf-8')); } }
|
||||
import crypto from 'crypto';
|
||||
import { initDB, getUserByUsername, createUser } from './db';
|
||||
function hashPassword(pw: string) { return crypto.createHash('sha256').update(pw).digest('hex'); }
|
||||
|
||||
// Auth
|
||||
app.post('/api/users/register', async (req, res) => {
|
||||
const { username, password, displayName } = req.body;
|
||||
if (!username || !password) return res.status(400).json({ error: 'Missing username/password' });
|
||||
await initDB();
|
||||
const exists = await getUserByUsername(username);
|
||||
if (exists) return res.status(409).json({ error: 'Username taken' });
|
||||
const user: AuthUser = { id: uuidv4(), username, passwordHash: hashPassword(password), displayName: displayName || username, createdAt: Date.now() };
|
||||
const user = { id: uuidv4(), username, passwordHash: hashPassword(password), displayName: displayName || username, createdAt: Date.now() };
|
||||
await createUser(user);
|
||||
res.status(201).json({ id: user.id, username: user.username, displayName: user.displayName });
|
||||
});
|
||||
|
||||
app.post('/api/users/login', async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
await initDB();
|
||||
const user = await getUserByUsername(username);
|
||||
if (!user || user.passwordHash !== hashPassword(password)) return res.status(401).json({ error: 'Invalid credentials' });
|
||||
res.json({ id: user.id, username: user.username, displayName: user.displayName });
|
||||
});
|
||||
|
||||
// Search servers
|
||||
app.get('/api/servers', (req, res) => {
|
||||
app.get('/api/servers', async (req, res) => {
|
||||
const { q, tags, limit = 20, offset = 0 } = req.query;
|
||||
|
||||
let results = Array.from(servers.values())
|
||||
.filter(s => !s.isPrivate)
|
||||
let results = await getAllPublicServers();
|
||||
|
||||
results = results
|
||||
.filter(s => {
|
||||
if (q) {
|
||||
const query = String(q).toLowerCase();
|
||||
@@ -179,8 +150,6 @@ app.get('/api/servers', (req, res) => {
|
||||
return true;
|
||||
});
|
||||
|
||||
// Keep servers visible permanently until deleted; do not filter by lastSeen
|
||||
|
||||
const total = results.length;
|
||||
results = results.slice(Number(offset), Number(offset) + Number(limit));
|
||||
|
||||
@@ -188,14 +157,13 @@ app.get('/api/servers', (req, res) => {
|
||||
});
|
||||
|
||||
// Register a server
|
||||
app.post('/api/servers', (req, res) => {
|
||||
app.post('/api/servers', async (req, res) => {
|
||||
const { id: clientId, name, description, ownerId, ownerPublicKey, isPrivate, maxUsers, tags } = req.body;
|
||||
|
||||
if (!name || !ownerId || !ownerPublicKey) {
|
||||
return res.status(400).json({ error: 'Missing required fields' });
|
||||
}
|
||||
|
||||
// Use client-provided ID if available, otherwise generate one
|
||||
const id = clientId || uuidv4();
|
||||
const server: ServerInfo = {
|
||||
id,
|
||||
@@ -211,17 +179,16 @@ app.post('/api/servers', (req, res) => {
|
||||
lastSeen: Date.now(),
|
||||
};
|
||||
|
||||
servers.set(id, server);
|
||||
saveServers();
|
||||
await upsertServer(server);
|
||||
res.status(201).json(server);
|
||||
});
|
||||
|
||||
// Update server
|
||||
app.put('/api/servers/:id', (req, res) => {
|
||||
app.put('/api/servers/:id', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { ownerId, ...updates } = req.body;
|
||||
|
||||
const server = servers.get(id);
|
||||
const server = await getServerById(id);
|
||||
if (!server) {
|
||||
return res.status(404).json({ error: 'Server not found' });
|
||||
}
|
||||
@@ -230,18 +197,17 @@ app.put('/api/servers/:id', (req, res) => {
|
||||
return res.status(403).json({ error: 'Not authorized' });
|
||||
}
|
||||
|
||||
const updated = { ...server, ...updates, lastSeen: Date.now() };
|
||||
servers.set(id, updated);
|
||||
saveServers();
|
||||
const updated: ServerInfo = { ...server, ...updates, lastSeen: Date.now() };
|
||||
await upsertServer(updated);
|
||||
res.json(updated);
|
||||
});
|
||||
|
||||
// Heartbeat - keep server alive
|
||||
app.post('/api/servers/:id/heartbeat', (req, res) => {
|
||||
app.post('/api/servers/:id/heartbeat', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { currentUsers } = req.body;
|
||||
|
||||
const server = servers.get(id);
|
||||
const server = await getServerById(id);
|
||||
if (!server) {
|
||||
return res.status(404).json({ error: 'Server not found' });
|
||||
}
|
||||
@@ -250,18 +216,17 @@ app.post('/api/servers/:id/heartbeat', (req, res) => {
|
||||
if (typeof currentUsers === 'number') {
|
||||
server.currentUsers = currentUsers;
|
||||
}
|
||||
servers.set(id, server);
|
||||
saveServers();
|
||||
await upsertServer(server);
|
||||
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
// Remove server
|
||||
app.delete('/api/servers/:id', (req, res) => {
|
||||
app.delete('/api/servers/:id', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { ownerId } = req.body;
|
||||
|
||||
const server = servers.get(id);
|
||||
const server = await getServerById(id);
|
||||
if (!server) {
|
||||
return res.status(404).json({ error: 'Server not found' });
|
||||
}
|
||||
@@ -270,17 +235,16 @@ app.delete('/api/servers/:id', (req, res) => {
|
||||
return res.status(403).json({ error: 'Not authorized' });
|
||||
}
|
||||
|
||||
servers.delete(id);
|
||||
saveServers();
|
||||
await dbDeleteServer(id);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
// Request to join a server
|
||||
app.post('/api/servers/:id/join', (req, res) => {
|
||||
app.post('/api/servers/:id/join', async (req, res) => {
|
||||
const { id: serverId } = req.params;
|
||||
const { userId, userPublicKey, displayName } = req.body;
|
||||
|
||||
const server = servers.get(serverId);
|
||||
const server = await getServerById(serverId);
|
||||
if (!server) {
|
||||
return res.status(404).json({ error: 'Server not found' });
|
||||
}
|
||||
@@ -296,7 +260,7 @@ app.post('/api/servers/:id/join', (req, res) => {
|
||||
createdAt: Date.now(),
|
||||
};
|
||||
|
||||
joinRequests.set(requestId, request);
|
||||
await createJoinRequest(request);
|
||||
|
||||
// Notify server owner via WebSocket
|
||||
if (server.isPrivate) {
|
||||
@@ -310,11 +274,11 @@ app.post('/api/servers/:id/join', (req, res) => {
|
||||
});
|
||||
|
||||
// Get join requests for a server
|
||||
app.get('/api/servers/:id/requests', (req, res) => {
|
||||
app.get('/api/servers/:id/requests', async (req, res) => {
|
||||
const { id: serverId } = req.params;
|
||||
const { ownerId } = req.query;
|
||||
|
||||
const server = servers.get(serverId);
|
||||
const server = await getServerById(serverId);
|
||||
if (!server) {
|
||||
return res.status(404).json({ error: 'Server not found' });
|
||||
}
|
||||
@@ -323,41 +287,58 @@ app.get('/api/servers/:id/requests', (req, res) => {
|
||||
return res.status(403).json({ error: 'Not authorized' });
|
||||
}
|
||||
|
||||
const requests = Array.from(joinRequests.values())
|
||||
.filter(r => r.serverId === serverId && r.status === 'pending');
|
||||
|
||||
const requests = await getPendingRequestsForServer(serverId);
|
||||
res.json({ requests });
|
||||
});
|
||||
|
||||
// Approve/reject join request
|
||||
app.put('/api/requests/:id', (req, res) => {
|
||||
app.put('/api/requests/:id', async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { ownerId, status } = req.body;
|
||||
|
||||
const request = joinRequests.get(id);
|
||||
const request = await getJoinRequestById(id);
|
||||
if (!request) {
|
||||
return res.status(404).json({ error: 'Request not found' });
|
||||
}
|
||||
|
||||
const server = servers.get(request.serverId);
|
||||
const server = await getServerById(request.serverId);
|
||||
if (!server || server.ownerId !== ownerId) {
|
||||
return res.status(403).json({ error: 'Not authorized' });
|
||||
}
|
||||
|
||||
request.status = status;
|
||||
joinRequests.set(id, request);
|
||||
await updateJoinRequestStatus(id, status);
|
||||
const updated = { ...request, status };
|
||||
|
||||
// Notify the requester
|
||||
notifyUser(request.userId, {
|
||||
type: 'request_update',
|
||||
request,
|
||||
request: updated,
|
||||
});
|
||||
|
||||
res.json(request);
|
||||
res.json(updated);
|
||||
});
|
||||
|
||||
// WebSocket Server for real-time signaling
|
||||
const server = createServer(app);
|
||||
function buildServer() {
|
||||
if (USE_SSL) {
|
||||
// Look for certs relative to project root (one level up from server/)
|
||||
const certDir = path.resolve(__dirname, '..', '..', '.certs');
|
||||
const certFile = path.join(certDir, 'localhost.crt');
|
||||
const keyFile = path.join(certDir, 'localhost.key');
|
||||
if (!fs.existsSync(certFile) || !fs.existsSync(keyFile)) {
|
||||
console.error(`SSL=true but certs not found in ${certDir}`);
|
||||
console.error('Run ./generate-cert.sh first.');
|
||||
process.exit(1);
|
||||
}
|
||||
return createHttpsServer(
|
||||
{ cert: fs.readFileSync(certFile), key: fs.readFileSync(keyFile) },
|
||||
app,
|
||||
);
|
||||
}
|
||||
return createHttpServer(app);
|
||||
}
|
||||
|
||||
const server = buildServer();
|
||||
const wss = new WebSocketServer({ server });
|
||||
|
||||
wss.on('connection', (ws: WebSocket) => {
|
||||
@@ -559,24 +540,20 @@ function findUserByUserId(oderId: string): ConnectedUser | undefined {
|
||||
return Array.from(connectedUsers.values()).find(u => u.oderId === oderId);
|
||||
}
|
||||
|
||||
// Cleanup old data periodically
|
||||
// Simple cleanup only for stale join requests (keep servers permanent)
|
||||
// Cleanup stale join requests periodically (older than 24 h)
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
joinRequests.forEach((request, id) => {
|
||||
if (now - request.createdAt > 24 * 60 * 60 * 1000) {
|
||||
joinRequests.delete(id);
|
||||
}
|
||||
});
|
||||
deleteStaleJoinRequests(24 * 60 * 60 * 1000).catch(err =>
|
||||
console.error('Failed to clean up stale join requests:', err),
|
||||
);
|
||||
}, 60 * 1000);
|
||||
|
||||
initDB().then(() => {
|
||||
server.listen(PORT, () => {
|
||||
console.log(`🚀 MetoYou signaling server running on port ${PORT}`);
|
||||
console.log(` REST API: http://localhost:${PORT}/api`);
|
||||
console.log(` WebSocket: ws://localhost:${PORT}`);
|
||||
// Load servers on startup
|
||||
loadServers();
|
||||
const proto = USE_SSL ? 'https' : 'http';
|
||||
const wsProto = USE_SSL ? 'wss' : 'ws';
|
||||
console.log(`🚀 MetoYou signaling server running on port ${PORT} (SSL=${USE_SSL})`);
|
||||
console.log(` REST API: ${proto}://localhost:${PORT}/api`);
|
||||
console.log(` WebSocket: ${wsProto}://localhost:${PORT}`);
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error('Failed to initialize database:', err);
|
||||
|
||||
Reference in New Issue
Block a user