236 lines
6.0 KiB
JavaScript
236 lines
6.0 KiB
JavaScript
"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;
|