UNPKG

@needle-tools/networking

Version:
136 lines (115 loc) 4.13 kB
/** * @param {import("express").Express} app * @param {import("../@types/index").NetworkingOptions} options */ module.exports.startServerExpress = function (app, options) { const networking = create(app, options); // necessary to make app.ws work require("express-ws")(app, options.server); const endpoint = getEndpoint(options); console.log("Using endpoint", endpoint); app.ws(endpoint, function (ws, request) { networking.onConnection(ws); }); setupBlob(app); return networking; }; const { FastifyProxy } = require("./proxy"); const { blobStorage } = require("./storage"); /** * @param {import("fastify").FastifyInstance} fastify * @param {import("../@types/index").NetworkingOptions} options */ module.exports.startServerFastify = function (fastify, options) { const networking = create(fastify, options); fastify.register(require('fastify-websocket')); const endpoint = getEndpoint(options) console.log("Using endpoint", endpoint); fastify.get(endpoint, { websocket: true }, (connection, req) => { const proxy = new FastifyProxy(connection); networking.onConnection(proxy); }); setupBlob(fastify); return networking; }; function getEndpoint(options) { if (options && options.endpoint !== undefined && typeof options.endpoint === "string") { return options.endpoint; } return "/"; } function create(app, options) { console.info("Starting networking server"); const networking = require("./networking"); networking.init(app, options); return networking; } function setupBlob(app) { app.get("/api/needle/blob/:key", async (request, response) => { const key = request.params.key; console.log("Requesting blob", key); const url = await blobStorage.getDownloadURL(key); // if we get a string it's the download url if (typeof url === "string") { return response.status(302).redirect(url); } response.status(400).send(url); }); app.post("/api/needle/blob", async (request, response) => { // prevent users from uploading too many files const origin = request.headers["origin"]; const userAgent = request.headers["user-agent"]; if (!allowUpload(origin, userAgent)) { console.log("Upload request blocked", origin, userAgent); return response.status(429).send({ error: "too many requests" }); } // prevent files from being too large const maxSize = 50_000_000; if (parseInt(request.headers["filesize"]) > maxSize) { console.log("Upload request too large", request.headers["filesize"]); return response.status(400).send({ error: "file too large" }); } const data = { filename: request.headers["filename"], content_md5: request.headers["content-md5"], key: request.headers["content-md5"], content_type: request.headers["content-type"], content_length: parseInt(request.headers["filesize"]), } // the key is base64 encoded, make sure we don't have any slashes // hence we need to replace them with underscores data.key = data.key.replace(/\//g, "_"); const obj = await blobStorage.getUploadURL(data); if (!obj) { return response.status(400).send({ error: "unknown error" }); } // if we get an error object, return it if ("error" in obj) { return response.status(400).send(obj); } if (!("key" in obj)) { return response.status(400).send({ error: "no key" }); } const result = { ...obj, key: data.key, download: `/api/needle/blob/${data.key}`, } response.send(result); }); const requestCount = new Map(); function allowUpload(origin, userAgent) { const key = origin + userAgent; let count = requestCount.get(key); if (count === undefined) { count = 0; // reset count after 1 minute setTimeout(() => { requestCount.delete(key); }, 60_000); } if (count >= 30) { return false; } requestCount.set(key, count + 1); return true; } }