"use strict"; const express = require("express"); const router = express.Router(); const { Client } = require("./Client"); const { Room } = require("./Services/Room"); const apiKeys = new Map(); const webhooks = new Map(); function auth(req, res, next) { const key = req.headers['x-api-key']; if (!key) { return res.status(401).json({ status: 'fail', message: 'API_KEY_REQUIRED' }); } if (!apiKeys.has(key)) { return res.status(401).json({ status: 'fail', message: 'INVALID_API_KEY' }); } req.server = apiKeys.get(key); next(); } router.post('/auth/key', (req, res) => { const { domain } = req.body; if (!domain) { return res.json({ status: 'fail', message: 'DOMAIN_REQUIRED' }); } const key = require("crypto").randomUUID(); apiKeys.set(key, { domain, key }); res.json({ status: 'success', key }); }); router.post('/client/:id/send', auth, (req, res) => { const { id } = req.params; const { pack } = req.body; const client = Client.clients.get(id); if (!client) { return res.json({ status: 'fail', message: 'CLIENT_NOT_FOUND' }); } if (!pack) { return res.json({ status: 'fail', message: 'PACK_REQUIRED' }); } const fromServer = req.server; client.send([{ from: 'server', fromServer, pack }, 'server/pack']); res.json({ status: 'success' }); }); router.post('/room/:id/send', auth, (req, res) => { const { id } = req.params; const { pack, wom } = req.body; const room = Room.rooms.get(id); if (!room) { return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' }); } if (!pack) { return res.json({ status: 'fail', message: 'PACK_REQUIRED' }); } const fromServer = req.server; room.send( [{ from: 'server', fromServer, pack, roomId: id }, 'server/pack/room'], wom ? undefined : 'server', () => true ); res.json({ status: 'success' }); }); router.post('/room/create', auth, (req, res) => { const { name, accessType, joinType, description, credential } = req.body; if (!name) { return res.json({ status: 'fail', message: 'NAME_REQUIRED' }); } for (const [, room] of Room.rooms) { if (room.name === name) { return res.json({ status: 'fail', message: 'ROOM_ALREADY_EXISTS' }); } } const room = new Room(); room.name = name; room.accessType = accessType || 'public'; room.joinType = joinType || 'free'; room.description = description || ''; room.owner = { id: 'server', isServer: true }; if (credential) { const { createHash } = require("crypto"); room.credential = createHash("sha256").update(credential).digest("hex"); } room.publish(); res.json({ status: 'success', room: room.toJSON() }); }); router.post('/room/:id/join', auth, (req, res) => { const { id } = req.params; const { credential } = req.body; const room = Room.rooms.get(id); if (!room) { return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' }); } if (room.joinType === 'lock') { return res.json({ status: 'fail', message: 'ROOM_LOCKED' }); } if (room.joinType === 'password') { const { createHash } = require("crypto"); const hash = createHash(credential || '').digest("hex"); if (room.credential !== hash) { return res.json({ status: 'fail', message: 'WRONG_PASSWORD' }); } } const fakeClient = { id: 'server-joined', isServer: true, rooms: new Set(), send: (msg) => { const fromServer = req.server; room.send([{ from: 'server', fromServer, ...msg[0] }, msg[1]], 'server'); } }; room.join(fakeClient); res.json({ status: 'success', room: room.toJSON() }); }); router.delete('/room/:id/leave', auth, (req, res) => { const { id } = req.params; const room = Room.rooms.get(id); if (!room) { return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' }); } const fakeClient = { id: 'server-joined', isServer: true }; room.eject(fakeClient); res.json({ status: 'success' }); }); router.get('/room/:id', (req, res) => { const { id } = req.params; const room = Room.rooms.get(id); if (!room) { return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' }); } res.json({ status: 'success', room: room.toJSON() }); }); router.get('/rooms', (req, res) => { const rooms = []; for (const [id, room] of Room.rooms) { rooms.push({ id, name: room.name, accessType: room.accessType, joinType: room.joinType, description: room.description, clientCount: room.clients.size }); } res.json({ status: 'success', rooms }); }); router.get('/clients', (req, res) => { const clients = []; for (const [id, client] of Client.clients) { clients.push({ id, rooms: [...client.rooms], pairs: [...client.pairs] }); } res.json({ status: 'success', clients }); }); router.post('/webhook', auth, (req, res) => { const { url, events } = req.body; if (!url) { return res.json({ status: 'fail', message: 'URL_REQUIRED' }); } const server = req.server; webhooks.set(server.domain, { url, events: events || ['server/pack', 'server/pack/room'] }); res.json({ status: 'success' }); }); function triggerWebhook(event, data) { for (const [, webhook] of webhooks) { if (webhook.events.includes(event)) { fetch(webhook.url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ event, data }) }).catch(console.error); } } } module.exports = router; module.exports.triggerWebhook = triggerWebhook;