const http = require("http"); const { URL } = require("url"); const targetBase = process.env.MEMOS_UPSTREAM || "http://memos:5230"; const listenPort = Number(process.env.PORT || 8080); function shouldRewrite(pathname) { return /^\/api\/v1\/users\/[^/]+\/settings$/.test(pathname); } function filterSettingsPayload(buffer) { const payload = JSON.parse(buffer.toString("utf8")); if (!Array.isArray(payload.settings)) { return buffer; } const settings = payload.settings.filter((item) => { const name = String((item && item.name) || ""); return !name.endsWith("/unknown"); }); return Buffer.from( JSON.stringify({ ...payload, settings, totalSize: settings.length, }), "utf8", ); } const server = http.createServer((req, res) => { const upstreamUrl = new URL(req.url, targetBase); const headers = { ...req.headers }; headers.host = upstreamUrl.host; const upstreamReq = http.request( upstreamUrl, { method: req.method, headers, }, (upstreamRes) => { const pathname = upstreamUrl.pathname; const contentType = String(upstreamRes.headers["content-type"] || ""); const canRewrite = shouldRewrite(pathname) && contentType.includes("application/json"); if (!canRewrite) { res.writeHead(upstreamRes.statusCode || 502, upstreamRes.headers); upstreamRes.pipe(res); return; } const chunks = []; upstreamRes.on("data", (chunk) => chunks.push(chunk)); upstreamRes.on("end", () => { try { const body = filterSettingsPayload(Buffer.concat(chunks)); const responseHeaders = { ...upstreamRes.headers }; responseHeaders["content-length"] = String(body.length); delete responseHeaders["content-encoding"]; res.writeHead(upstreamRes.statusCode || 200, responseHeaders); res.end(body); } catch (error) { res.writeHead(502, { "content-type": "text/plain; charset=utf-8" }); res.end(`memos proxy rewrite failed: ${error.message}`); } }); }, ); upstreamReq.on("error", (error) => { res.writeHead(502, { "content-type": "text/plain; charset=utf-8" }); res.end(`memos upstream error: ${error.message}`); }); req.pipe(upstreamReq); }); server.listen(listenPort, "0.0.0.0");