@mmote/niimblue-node
Version:
Headless clients for niimbluelib. Command line interface, simple REST server are also included.
118 lines (117 loc) • 4.21 kB
JavaScript
;
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;