UNPKG

wranglebot

Version:

open source media asset management

345 lines 14.4 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _SocketServer_instances, _SocketServer_signToken, _SocketServer_jwtValid; import Client from "./Client.js"; import Betweeny from "./Betweeny.js"; import { EmailTemplate } from "./EmailTemplate.js"; import { Server } from "socket.io"; import LogBot from "logbotjs"; import jwt from "jsonwebtoken"; import User from "../core/accounts/User.js"; import { finder } from "../core/system/index.js"; import routes from "./routes/index.js"; import RouteFactory from "./RouteFactory.js"; class SocketServer { constructor(http, app, bot, mail, secret) { _SocketServer_instances.add(this); this.users = []; this.clients = []; this.hooks = []; this.sockets = new Set(); this.cache = {}; this.checkRequestAuthorization = (req, res, roles = []) => { const auth = req.get("authorization"); if (!auth || !auth.startsWith("Bearer ")) { res.status(401).send({ error: `Invalid Authorization Format or Token. Should be: Bearer <token>.` }); return false; } const token = auth.split(" ")[1]; const user = __classPrivateFieldGet(this, _SocketServer_instances, "m", _SocketServer_jwtValid).call(this, token); if (!user || (user && roles.length > 0 && !this.bot.accountManager.hasRole(user, roles))) { res.status(401).send({ status: "error", message: `Invalid Token or no permission to access this resource. Clearance: ${roles}, ${user ? "roles assigned to your user: " + user.roles.join(",") : "you are not logged in"}`, }); return false; } return user; }; this.server = new Server(http, { cors: { origin: "*", methods: ["GET", "POST"], allowedHeaders: ["Authorization"], }, }); this.bot = bot; this.mail = mail; this.app = app; this.secret = secret; } addToCache(key, value) { this.cache[key] = value; return this.cache[key]; } getFromCache(key) { return this.cache[key]; } removeFromCache(key) { delete this.cache[key]; } start() { return __awaiter(this, void 0, void 0, function* () { this.server.on("connection", (socket) => { this.response(200).tween(socket, "connected", { message: "Connected", }); this.sockets.add(socket); LogBot.log(200, "new connection " + socket.id, true); this.server.on("reconnect", (socket) => { LogBot.log(200, "reconnect " + socket.id, true); }); socket.on("disconnect", () => { LogBot.log(200, "disconnected " + socket.id, true); this.sockets.delete(socket); }); socket.once("auth", (betweeny) => { const { username, password, token } = betweeny.data; const user = this.checkAuth(username || null, password || null, token || null); if (user) { LogBot.log(200, "Authenticated " + user.username); const client = this.clients.find((client) => client.username === user.username); if (client) { client.socket = socket; client.syncHooks(); LogBot.log(200, "Client " + user.username + " has been remapped to previous socket"); } else { const newClient = new Client(socket, user.username); this.clients.push(newClient); this.setHooks(newClient); user.token = __classPrivateFieldGet(this, _SocketServer_instances, "m", _SocketServer_signToken).call(this, user); LogBot.log(200, "New session was opened for " + user.username); } this.response(200).tween(socket, "token", { token: user.token, username: user.username, email: user.email, firstName: user.firstName, lastName: user.lastName, libraries: user.libraries, roles: user.roles, }); } else { LogBot.log(401, "Authentication of user " + username + " failed"); this.response(401).tween(socket, "token", { status: "error", message: "Authorization failed. Please check your username and password", }); this.sockets.delete(socket); } }); }); const routeFactory = new RouteFactory({ baseUrl: "/api/v1", express: this.app, wrangleBot: this.bot, socketServer: this, mailServer: this.mail, }); for (let route of routes) { routeFactory.build(route); } const pathToPlugins = finder.getPathToUserData("custom/"); const plugins = finder.getContentOfFolder(pathToPlugins); for (let plugin of plugins) { const pluginFolders = finder.getContentOfFolder(pathToPlugins + plugin); if (pluginFolders.length === 0) { continue; } LogBot.log(100, "Loading third party endpoints of " + plugin); for (let pluginFolder of pluginFolders) { if (pluginFolder === "endpoints") { const pluginRoutes = finder.getContentOfFolder(pathToPlugins + plugin + "/endpoints"); for (let r of pluginRoutes) { LogBot.log(100, "Loading third party endpoint " + r + " of " + plugin); const template = yield import(pathToPlugins + plugin + "/endpoints/" + r); routeFactory.build(template.default); } } } } this.hook("subscribe", (client, betweeny) => __awaiter(this, void 0, void 0, function* () { try { const { data } = betweeny; const { event, id } = data; this.subscribe(client, event, id); } catch (e) { console.error(e); } })); this.hook("unsubscribe", (client, betweeny) => __awaiter(this, void 0, void 0, function* () { try { const { data } = betweeny; const { event, id } = data; this.unsubscribe(client, event, id); } catch (e) { console.error(e); } })); return this; }); } close() { this.server.close(); } signInClient(username, password, token) { const user = this.checkAuth(username, password, token); if (user === null) { throw new Error("Authentication failed"); } if (!user.token) user.token = __classPrivateFieldGet(this, _SocketServer_instances, "m", _SocketServer_signToken).call(this, user); return user; } checkAuth(username = null, password = null, token = null) { if (token) { return __classPrivateFieldGet(this, _SocketServer_instances, "m", _SocketServer_jwtValid).call(this, token); } else if (username && password) { const res = this.bot.accountManager.checkAuth(username, password); if (res) { const u = this.bot.accountManager.getOneUser(username); return u ? u : null; } } return null; } hasRole(userOrJWT, role) { if (userOrJWT instanceof User) { return this.bot.accountManager.hasRole(userOrJWT, role); } else if (userOrJWT instanceof String) { const user = __classPrivateFieldGet(this, _SocketServer_instances, "m", _SocketServer_jwtValid).call(this, userOrJWT); if (user) { return this.bot.accountManager.hasRole(user, role); } } return false; } isUser(req, res, username) { const token = req.get("authorization"); const user = __classPrivateFieldGet(this, _SocketServer_instances, "m", _SocketServer_jwtValid).call(this, token); return !(!user || user.username !== username); } getUser(req, res) { const auth = req.get("authorization"); const token = auth.split(" ")[1]; const user = __classPrivateFieldGet(this, _SocketServer_instances, "m", _SocketServer_jwtValid).call(this, token); if (user === null) { res.status(401).send({ error: LogBot.resolveErrorCode(401), }); throw new Error("Invalid token"); } return user; } sendMail(emailTemplate) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.mail.sendMail(this.createEmailTemplate(emailTemplate).compile()); } catch (e) { console.error(e); return false; } }); } hook(event, callback) { LogBot.log(200, "Hooking " + event, true); this.hooks.push({ event, callback, }); for (let client of this.clients) { client.socket.removeAllListeners(event); client.addHook(event, callback); } } unhook(event) { LogBot.log(200, "Unhooking " + event, true); for (let client of this.clients) { client.socket.removeAllListeners(event); } } setHooks(client, socket = null) { for (let hook of this.hooks) { client.addHook(hook.event, hook.callback); } } subscribe(client, event, id) { if (!client.subscriptions[event]) client.subscriptions[event] = []; client.subscriptions[event].push(id); } unsubscribe(client, event, id) { if (!client.subscriptions[event]) return; client.subscriptions[event] = client.subscriptions[event].filter((sub) => sub !== id); } getSubscriptions(user) { const client = this.clients.find((client) => client.username === user.username); if (!client) return []; return client.subscriptions; } broadcast(event, betweeny) { this.clients.forEach((client) => { client.socket.emit(event, betweeny.toJSON()); LogBot.log(100, "Broadcasting to " + client.username); }); } inform(event, id, data) { for (let client of this.clients) { if (client.subscriptions[event] && client.subscriptions[event].includes(id)) { this.response(200).tween(client.socket, "subscription", { event, id, data, }); } } } response(status) { return { tween: (socket, event, data) => { if (socket === null) { this.broadcast(event, new Betweeny(status, data)); } else { socket.emit(event, new Betweeny(status, data).toJSON()); } }, }; } createEmailTemplate(email) { return new EmailTemplate(email); } } _SocketServer_instances = new WeakSet(), _SocketServer_signToken = function _SocketServer_signToken(user, expiresIn = "30 days") { return jwt.sign({ user: user.username, }, this.secret, { expiresIn }); }, _SocketServer_jwtValid = function _SocketServer_jwtValid(token) { try { const decoded = jwt.verify(token, this.secret); const user = this.bot.accountManager.getOneUser(decoded.user); if (user) return user; } catch (e) { return null; } return null; }; export default function (http, app, bot, mail, secret) { return __awaiter(this, void 0, void 0, function* () { try { const socketServer = new SocketServer(http, app, bot, mail, secret); yield socketServer.start(); LogBot.log(200, `Socket server started.`); return socketServer; } catch (e) { LogBot.log(500, `Socket server failed to start: ${e.message}`); throw e; } }); } export { SocketServer, Betweeny, Client }; //# sourceMappingURL=SocketServer.js.map