UNPKG

@mmote/niimblue-node

Version:

Headless clients for niimbluelib. Command line interface, simple REST server are also included.

118 lines (117 loc) 4.21 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SimpleServer = exports.readBodyJson = exports.writeObj = exports.RestError = void 0; const http_1 = __importDefault(require("http")); const zod_1 = require("zod"); class RestError extends Error { constructor(message, status = 500) { super(message); this.status = status; } } exports.RestError = RestError; const writeObj = (response, o, status = 200) => { response.setHeader("Content-Type", "application/json"); response.writeHead(status); response.end(JSON.stringify(o)); }; exports.writeObj = writeObj; const readBodyJson = async (request, schema) => { return new Promise((resolve, reject) => { const bodyParts = []; request .on("data", (chunk) => { bodyParts.push(chunk); }) .on("end", () => { let body = Buffer.concat(bodyParts).toString(); let data = null; try { data = JSON.parse(body); } catch (e) { reject(e); } if (data === null) { reject(new Error("No data")); } const result = schema.safeParse(data); if (result.success) { resolve(result.data); } else { reject(result.error); } }); }); }; exports.readBodyJson = readBodyJson; class SimpleServer { constructor() { this.routes = []; this.corsEnabled = false; } enableCors() { this.corsEnabled = true; } get(path, handler) { this.routes.push({ path, handler, method: "GET" }); } post(path, handler) { this.routes.push({ path, handler, method: "POST" }); } anything(path, handler) { this.routes.push({ path, handler }); } async onRequest(request, response) { if (request.url === undefined || request.method === undefined) { return; } console.log(`${request.socket.remoteAddress} ${request.method} ${request.url}`); if (this.corsEnabled) { response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Headers", "*"); response.setHeader("Access-Control-Allow-Methods", "OPTIONS, POST, GET"); response.setHeader("Access-Control-Max-Age", 2592000); // 30 days if (request.method === "OPTIONS") { response.writeHead(204); response.end(); return; } } try { const route = this.routes.find((r) => r.path === request.url && (r.method === undefined || r.method === request.method)); if (route === undefined) { (0, exports.writeObj)(response, { error: "Not found" }, 404); return; } if (request.method === "POST" && request.headers["content-type"] !== "application/json") { (0, exports.writeObj)(response, { error: "Only JSON accepted" }, 400); return; } const result = await route.handler(request); (0, exports.writeObj)(response, result, 200); } catch (e) { if (e instanceof zod_1.z.ZodError) { const error = e.issues.map((i) => `${i.path.join("→")}: ${i.message}`).join("\n"); (0, exports.writeObj)(response, { error }, 400); } else if (e instanceof RestError) { (0, exports.writeObj)(response, { error: e.message }, e.status); } else { (0, exports.writeObj)(response, { error: `${e}` }, 500); } } } start(host, port, listeningListener) { const server = http_1.default.createServer(); server.on("request", (req, res) => this.onRequest(req, res)); server.listen({ port, host }, listeningListener); } } exports.SimpleServer = SimpleServer;