MWSE/Source/api.js

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;