From f4ff28e51ccf7e9b3e7374fc087f7dac944bb044 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 12 May 2021 08:08:42 -0400 Subject: [PATCH] Switch main's route handlers to be static functions (#2712) --- src/common/vars.ts | 8 +- src/main/__test__/router.test.ts | 36 ++--- src/main/lens-api.ts | 17 --- src/main/router.ts | 156 +++++++++++----------- src/main/routes/helm-route.ts | 70 +++++----- src/main/routes/kubeconfig-route.ts | 11 +- src/main/routes/metrics-route.ts | 19 ++- src/main/routes/port-forward-route.ts | 15 +-- src/main/routes/resource-applier-route.ts | 12 +- src/main/routes/version-route.ts | 10 +- src/main/utils/http-responses.ts | 15 +++ 11 files changed, 173 insertions(+), 196 deletions(-) delete mode 100644 src/main/lens-api.ts create mode 100644 src/main/utils/http-responses.ts diff --git a/src/common/vars.ts b/src/common/vars.ts index 03d3f1c63d..ef39088258 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -30,11 +30,11 @@ export const webpackDevServerPort = 9009; // Special runtime paths defineGlobal("__static", { get() { - if (isDevelopment) { - return path.resolve(contextDir, "static"); - } + const root = isDevelopment + ? contextDir + : (process.resourcesPath ?? contextDir); - return path.resolve(process.resourcesPath, "static"); + return path.resolve(root, "static"); } }); diff --git a/src/main/__test__/router.test.ts b/src/main/__test__/router.test.ts index 8c2fa9c822..393f2a035b 100644 --- a/src/main/__test__/router.test.ts +++ b/src/main/__test__/router.test.ts @@ -1,40 +1,42 @@ import { Router } from "../router"; -const staticRoot = __dirname; - -class TestRouter extends Router { - protected resolveStaticRootPath() { - return staticRoot; - } -} - describe("Router", () => { it("blocks path traversal attacks", async () => { - const router = new TestRouter(); - const res = { + const response: any = { statusCode: 200, end: jest.fn() }; - await router.handleStaticFile("../index.ts", res as any, {} as any, 0); + await (Router as any).handleStaticFile({ + params: { + path: "../index.ts", + }, + response, + raw: {}, + }); - expect(res.statusCode).toEqual(404); + expect(response.statusCode).toEqual(404); }); it("serves files under static root", async () => { - const router = new TestRouter(); - const res = { + const response: any = { statusCode: 200, write: jest.fn(), setHeader: jest.fn(), end: jest.fn() }; - const req = { + const req: any = { url: "" }; - await router.handleStaticFile("router.test.ts", res as any, req as any, 0); + await (Router as any).handleStaticFile({ + params: { + path: "router.test.ts", + }, + response, + raw: { req }, + }); - expect(res.statusCode).toEqual(200); + expect(response.statusCode).toEqual(200); }); }); diff --git a/src/main/lens-api.ts b/src/main/lens-api.ts deleted file mode 100644 index fafeffce91..0000000000 --- a/src/main/lens-api.ts +++ /dev/null @@ -1,17 +0,0 @@ -import http from "http"; - -export abstract class LensApi { - protected respondJson(res: http.ServerResponse, content: {}, status = 200) { - this.respond(res, JSON.stringify(content), "application/json", status); - } - - protected respondText(res: http.ServerResponse, content: string, status = 200) { - this.respond(res, content, "text/plain", status); - } - - protected respond(res: http.ServerResponse, content: string, contentType: string, status = 200) { - res.setHeader("Content-Type", contentType); - res.statusCode = status; - res.end(content); - } -} diff --git a/src/main/router.ts b/src/main/router.ts index 6fa14e1444..5a53eac9df 100644 --- a/src/main/router.ts +++ b/src/main/router.ts @@ -5,7 +5,7 @@ import path from "path"; import { readFile } from "fs-extra"; import { Cluster } from "./cluster"; import { apiPrefix, appName, publicPath, isDevelopment, webpackDevServerPort } from "../common/vars"; -import { helmRoute, kubeconfigRoute, metricsRoute, portForwardRoute, resourceApplierRoute, versionRoute } from "./routes"; +import { HelmApiRoute, KubeconfigRoute, MetricsRoute, PortForwardRoute, ResourceApplierApiRoute, VersionRoute } from "./routes"; import logger from "./logger"; export interface RouterRequestOpts { @@ -38,18 +38,29 @@ export interface LensApiRequest

{ } } +function getMimeType(filename: string) { + const mimeTypes: Record = { + html: "text/html", + txt: "text/plain", + css: "text/css", + gif: "image/gif", + jpg: "image/jpeg", + png: "image/png", + svg: "image/svg+xml", + js: "application/javascript", + woff2: "font/woff2", + ttf: "font/ttf" + }; + + return mimeTypes[path.extname(filename).slice(1)] || "text/plain"; +} + export class Router { - protected router: any; - protected staticRootPath: string; + protected router = new Call.Router(); + protected static rootPath = path.resolve(__static); public constructor() { - this.router = new Call.Router(); this.addRoutes(); - this.staticRootPath = this.resolveStaticRootPath(); - } - - protected resolveStaticRootPath() { - return path.resolve(__static); } public async route(cluster: Cluster, req: http.IncomingMessage, res: http.ServerResponse): Promise { @@ -90,97 +101,80 @@ export class Router { }; } - protected getMimeType(filename: string) { - const mimeTypes: Record = { - html: "text/html", - txt: "text/plain", - css: "text/css", - gif: "image/gif", - jpg: "image/jpeg", - png: "image/png", - svg: "image/svg+xml", - js: "application/javascript", - woff2: "font/woff2", - ttf: "font/ttf" - }; + protected static async handleStaticFile({ params, response, raw: { req } }: LensApiRequest): Promise { + let filePath = params.path; - return mimeTypes[path.extname(filename).slice(1)] || "text/plain"; - } + for (let retryCount = 0; retryCount < 5; retryCount += 1) { + const asset = path.join(Router.rootPath, filePath); + const normalizedFilePath = path.resolve(asset); - async handleStaticFile(filePath: string, res: http.ServerResponse, req: http.IncomingMessage, retryCount = 0) { - const asset = path.join(this.staticRootPath, filePath); - const normalizedFilePath = path.resolve(asset); + if (!normalizedFilePath.startsWith(Router.rootPath)) { + response.statusCode = 404; - if (!normalizedFilePath.startsWith(this.staticRootPath)) { - res.statusCode = 404; - res.end(); + return response.end(); + } - return; + try { + const filename = path.basename(req.url); + // redirect requests to [appName].js, [appName].html /sockjs-node/ to webpack-dev-server (for hot-reload support) + const toWebpackDevServer = filename.includes(appName) || filename.includes("hot-update") || req.url.includes("sockjs-node"); + + if (isDevelopment && toWebpackDevServer) { + const redirectLocation = `http://localhost:${webpackDevServerPort}${req.url}`; + + response.statusCode = 307; + response.setHeader("Location", redirectLocation); + + return response.end(); + } + + const data = await readFile(asset); + + response.setHeader("Content-Type", getMimeType(asset)); + response.write(data); + response.end(); + } catch (err) { + if (retryCount > 5) { + logger.error("handleStaticFile:", err.toString()); + response.statusCode = 404; + + return response.end(); + } + + filePath = `${publicPath}/${appName}.html`; + } } - try { - const filename = path.basename(req.url); - // redirect requests to [appName].js, [appName].html /sockjs-node/ to webpack-dev-server (for hot-reload support) - const toWebpackDevServer = filename.includes(appName) || filename.includes("hot-update") || req.url.includes("sockjs-node"); - - if (isDevelopment && toWebpackDevServer) { - const redirectLocation = `http://localhost:${webpackDevServerPort}${req.url}`; - - res.statusCode = 307; - res.setHeader("Location", redirectLocation); - res.end(); - - return; - } - const data = await readFile(asset); - - res.setHeader("Content-Type", this.getMimeType(asset)); - res.write(data); - res.end(); - } catch (err) { - if (retryCount > 5) { - logger.error("handleStaticFile:", err.toString()); - res.statusCode = 404; - res.end(); - - return; - } - this.handleStaticFile(`${publicPath}/${appName}.html`, res, req, Math.max(retryCount, 0) + 1); - } } protected addRoutes() { // Static assets - this.router.add( - { method: "get", path: "/{path*}" }, - ({ params, response, raw: { req } }: LensApiRequest) => { - this.handleStaticFile(params.path, response, req); - }); + this.router.add({ method: "get", path: "/{path*}" }, Router.handleStaticFile); - this.router.add({ method: "get", path: "/version"}, versionRoute.getVersion.bind(versionRoute)); - this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, kubeconfigRoute.routeServiceAccountRoute.bind(kubeconfigRoute)); + this.router.add({ method: "get", path: "/version" }, VersionRoute.getVersion); + this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, KubeconfigRoute.routeServiceAccountRoute); // Metrics API - this.router.add({ method: "post", path: `${apiPrefix}/metrics` }, metricsRoute.routeMetrics.bind(metricsRoute)); + this.router.add({ method: "post", path: `${apiPrefix}/metrics` }, MetricsRoute.routeMetrics); // Port-forward API - this.router.add({ method: "post", path: `${apiPrefix}/pods/{namespace}/{resourceType}/{resourceName}/port-forward/{port}` }, portForwardRoute.routePortForward.bind(portForwardRoute)); + this.router.add({ method: "post", path: `${apiPrefix}/pods/{namespace}/{resourceType}/{resourceName}/port-forward/{port}` }, PortForwardRoute.routePortForward); // Helm API - this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, helmRoute.listCharts.bind(helmRoute)); - this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}` }, helmRoute.getChart.bind(helmRoute)); - this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}/values` }, helmRoute.getChartValues.bind(helmRoute)); + this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, HelmApiRoute.listCharts); + this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}` }, HelmApiRoute.getChart); + this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}/values` }, HelmApiRoute.getChartValues); - this.router.add({ method: "post", path: `${apiPrefix}/v2/releases` }, helmRoute.installChart.bind(helmRoute)); - this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmRoute.updateRelease.bind(helmRoute)); - this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback` }, helmRoute.rollbackRelease.bind(helmRoute)); - this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace?}` }, helmRoute.listReleases.bind(helmRoute)); - this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmRoute.getRelease.bind(helmRoute)); - this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/values` }, helmRoute.getReleaseValues.bind(helmRoute)); - this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/history` }, helmRoute.getReleaseHistory.bind(helmRoute)); - this.router.add({ method: "delete", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, helmRoute.deleteRelease.bind(helmRoute)); + this.router.add({ method: "post", path: `${apiPrefix}/v2/releases` }, HelmApiRoute.installChart); + this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, HelmApiRoute.updateRelease); + this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback` }, HelmApiRoute.rollbackRelease); + this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace?}` }, HelmApiRoute.listReleases); + this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, HelmApiRoute.getRelease); + this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/values` }, HelmApiRoute.getReleaseValues); + this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/history` }, HelmApiRoute.getReleaseHistory); + this.router.add({ method: "delete", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, HelmApiRoute.deleteRelease); // Resource Applier API - this.router.add({ method: "post", path: `${apiPrefix}/stack` }, resourceApplierRoute.applyResource.bind(resourceApplierRoute)); + this.router.add({ method: "post", path: `${apiPrefix}/stack` }, ResourceApplierApiRoute.applyResource); } } diff --git a/src/main/routes/helm-route.ts b/src/main/routes/helm-route.ts index e530e2edf0..89e229b626 100644 --- a/src/main/routes/helm-route.ts +++ b/src/main/routes/helm-route.ts @@ -1,143 +1,141 @@ import { LensApiRequest } from "../router"; import { helmService } from "../helm/helm-service"; -import { LensApi } from "../lens-api"; +import { respondJson, respondText } from "../utils/http-responses"; import logger from "../logger"; -class HelmApiRoute extends LensApi { - public async listCharts(request: LensApiRequest) { +export class HelmApiRoute { + static async listCharts(request: LensApiRequest) { const { response } = request; const charts = await helmService.listCharts(); - this.respondJson(response, charts); + respondJson(response, charts); } - public async getChart(request: LensApiRequest) { + static async getChart(request: LensApiRequest) { const { params, query, response } = request; try { const chart = await helmService.getChart(params.repo, params.chart, query.get("version")); - this.respondJson(response, chart); + respondJson(response, chart); } catch (error) { - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async getChartValues(request: LensApiRequest) { + static async getChartValues(request: LensApiRequest) { const { params, query, response } = request; try { const values = await helmService.getChartValues(params.repo, params.chart, query.get("version")); - this.respondJson(response, values); + respondJson(response, values); } catch (error) { - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async installChart(request: LensApiRequest) { + static async installChart(request: LensApiRequest) { const { payload, cluster, response } = request; try { const result = await helmService.installChart(cluster, payload); - this.respondJson(response, result, 201); + respondJson(response, result, 201); } catch (error) { logger.debug(error); - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async updateRelease(request: LensApiRequest) { + static async updateRelease(request: LensApiRequest) { const { cluster, params, payload, response } = request; try { const result = await helmService.updateRelease(cluster, params.release, params.namespace, payload ); - this.respondJson(response, result); + respondJson(response, result); } catch (error) { logger.debug(error); - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async rollbackRelease(request: LensApiRequest) { + static async rollbackRelease(request: LensApiRequest) { const { cluster, params, payload, response } = request; try { const result = await helmService.rollback(cluster, params.release, params.namespace, payload.revision); - this.respondJson(response, result); + respondJson(response, result); } catch (error) { logger.debug(error); - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async listReleases(request: LensApiRequest) { + static async listReleases(request: LensApiRequest) { const { cluster, params, response } = request; try { const result = await helmService.listReleases(cluster, params.namespace); - this.respondJson(response, result); + respondJson(response, result); } catch(error) { logger.debug(error); - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async getRelease(request: LensApiRequest) { + static async getRelease(request: LensApiRequest) { const { cluster, params, response } = request; try { const result = await helmService.getRelease(cluster, params.release, params.namespace); - this.respondJson(response, result); + respondJson(response, result); } catch (error) { logger.debug(error); - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async getReleaseValues(request: LensApiRequest) { + static async getReleaseValues(request: LensApiRequest) { const { cluster, params, response, query } = request; try { const result = await helmService.getReleaseValues(cluster, params.release, params.namespace, query.has("all")); - this.respondText(response, result); + respondText(response, result); } catch (error) { logger.debug(error); - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async getReleaseHistory(request: LensApiRequest) { + static async getReleaseHistory(request: LensApiRequest) { const { cluster, params, response } = request; try { const result = await helmService.getReleaseHistory(cluster, params.release, params.namespace); - this.respondJson(response, result); + respondJson(response, result); } catch (error) { logger.debug(error); - this.respondText(response, error, 422); + respondText(response, error, 422); } } - public async deleteRelease(request: LensApiRequest) { + static async deleteRelease(request: LensApiRequest) { const { cluster, params, response } = request; try { const result = await helmService.deleteRelease(cluster, params.release, params.namespace); - this.respondJson(response, result); + respondJson(response, result); } catch (error) { logger.debug(error); - this.respondText(response, error, 422); + respondText(response, error, 422); } } } - -export const helmRoute = new HelmApiRoute(); diff --git a/src/main/routes/kubeconfig-route.ts b/src/main/routes/kubeconfig-route.ts index bad5ecd57a..d2e3941a29 100644 --- a/src/main/routes/kubeconfig-route.ts +++ b/src/main/routes/kubeconfig-route.ts @@ -1,5 +1,5 @@ import { LensApiRequest } from "../router"; -import { LensApi } from "../lens-api"; +import { respondJson } from "../utils/http-responses"; import { Cluster } from "../cluster"; import { CoreV1Api, V1Secret } from "@kubernetes/client-node"; @@ -40,9 +40,8 @@ function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster }; } -class KubeconfigRoute extends LensApi { - - public async routeServiceAccountRoute(request: LensApiRequest) { +export class KubeconfigRoute { + static async routeServiceAccountRoute(request: LensApiRequest) { const { params, response, cluster} = request; const client = (await cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api); const secretList = await client.listNamespacedSecret(params.namespace); @@ -53,8 +52,6 @@ class KubeconfigRoute extends LensApi { }); const data = generateKubeConfig(params.account, secret, cluster); - this.respondJson(response, data); + respondJson(response, data); } } - -export const kubeconfigRoute = new KubeconfigRoute(); diff --git a/src/main/routes/metrics-route.ts b/src/main/routes/metrics-route.ts index b6fbdadb61..0dec1fc1ff 100644 --- a/src/main/routes/metrics-route.ts +++ b/src/main/routes/metrics-route.ts @@ -1,6 +1,6 @@ import _ from "lodash"; import { LensApiRequest } from "../router"; -import { LensApi } from "../lens-api"; +import { respondJson } from "../utils/http-responses"; import { Cluster, ClusterMetadataKey } from "../cluster"; import { ClusterPrometheusMetadata } from "../../common/cluster-store"; import logger from "../logger"; @@ -41,8 +41,8 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa return Promise.all(queries.map(loadMetric)); } -class MetricsRoute extends LensApi { - async routeMetrics({ response, cluster, payload, query }: LensApiRequest) { +export class MetricsRoute { + static async routeMetrics({ response, cluster, payload, query }: LensApiRequest) { const queryParams: IMetricsQuery = Object.fromEntries(query.entries()); const prometheusMetadata: ClusterPrometheusMetadata = {}; @@ -57,20 +57,19 @@ class MetricsRoute extends LensApi { if (!prometheusPath) { prometheusMetadata.success = false; - this.respondJson(response, {}); - return; + return respondJson(response, {}); } // return data in same structure as query if (typeof payload === "string") { const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams); - this.respondJson(response, data); + respondJson(response, data); } else if (Array.isArray(payload)) { const data = await loadMetrics(payload, cluster, prometheusPath, queryParams); - this.respondJson(response, data); + respondJson(response, data); } else { const queries = Object.entries(payload).map(([queryName, queryOpts]) => ( (prometheusProvider.getQueries(queryOpts) as Record)[queryName] @@ -78,16 +77,14 @@ class MetricsRoute extends LensApi { const result = await loadMetrics(queries, cluster, prometheusPath, queryParams); const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]])); - this.respondJson(response, data); + respondJson(response, data); } prometheusMetadata.success = true; } catch { prometheusMetadata.success = false; - this.respondJson(response, {}); + respondJson(response, {}); } finally { cluster.metadata[ClusterMetadataKey.PROMETHEUS] = prometheusMetadata; } } } - -export const metricsRoute = new MetricsRoute(); diff --git a/src/main/routes/port-forward-route.ts b/src/main/routes/port-forward-route.ts index 6edd81e537..ca8be52dcd 100644 --- a/src/main/routes/port-forward-route.ts +++ b/src/main/routes/port-forward-route.ts @@ -1,11 +1,11 @@ import { LensApiRequest } from "../router"; -import { LensApi } from "../lens-api"; import { spawn, ChildProcessWithoutNullStreams } from "child_process"; import { Kubectl } from "../kubectl"; import { shell } from "electron"; import * as tcpPortUsed from "tcp-port-used"; import logger from "../logger"; import { getPortFrom } from "../utils/get-port"; +import { respondJson } from "../utils/http-responses"; interface PortForwardArgs { clusterId: string; @@ -95,9 +95,8 @@ class PortForward { } } -class PortForwardRoute extends LensApi { - - public async routePortForward(request: LensApiRequest) { +export class PortForwardRoute { + static async routePortForward(request: LensApiRequest) { const { params, response, cluster} = request; const { namespace, port, resourceType, resourceName } = params; let portForward = PortForward.getPortforward({ @@ -117,18 +116,14 @@ class PortForwardRoute extends LensApi { const started = await portForward.start(); if (!started) { - this.respondJson(response, { + return respondJson(response, { message: "Failed to open port-forward" }, 400); - - return; } } portForward.open(); - this.respondJson(response, {}); + respondJson(response, {}); } } - -export const portForwardRoute = new PortForwardRoute(); diff --git a/src/main/routes/resource-applier-route.ts b/src/main/routes/resource-applier-route.ts index 1e532dde46..6183ece931 100644 --- a/src/main/routes/resource-applier-route.ts +++ b/src/main/routes/resource-applier-route.ts @@ -1,19 +1,17 @@ import { LensApiRequest } from "../router"; -import { LensApi } from "../lens-api"; +import { respondJson, respondText } from "../utils/http-responses"; import { ResourceApplier } from "../resource-applier"; -class ResourceApplierApiRoute extends LensApi { - public async applyResource(request: LensApiRequest) { +export class ResourceApplierApiRoute { + static async applyResource(request: LensApiRequest) { const { response, cluster, payload } = request; try { const resource = await new ResourceApplier(cluster).apply(payload); - this.respondJson(response, [resource], 200); + respondJson(response, [resource], 200); } catch (error) { - this.respondText(response, error, 422); + respondText(response, error, 422); } } } - -export const resourceApplierRoute = new ResourceApplierApiRoute(); diff --git a/src/main/routes/version-route.ts b/src/main/routes/version-route.ts index 81ada9eca7..8eaa96ca17 100644 --- a/src/main/routes/version-route.ts +++ b/src/main/routes/version-route.ts @@ -1,13 +1,11 @@ import { LensApiRequest } from "../router"; -import { LensApi } from "../lens-api"; +import { respondJson } from "../utils/http-responses"; import { getAppVersion } from "../../common/utils"; -class VersionRoute extends LensApi { - public async getVersion(request: LensApiRequest) { +export class VersionRoute { + static async getVersion(request: LensApiRequest) { const { response } = request; - this.respondJson(response, { version: getAppVersion()}, 200); + respondJson(response, { version: getAppVersion()}, 200); } } - -export const versionRoute = new VersionRoute(); diff --git a/src/main/utils/http-responses.ts b/src/main/utils/http-responses.ts new file mode 100644 index 0000000000..d41b124fb6 --- /dev/null +++ b/src/main/utils/http-responses.ts @@ -0,0 +1,15 @@ +import http from "http"; + +export function respondJson(res: http.ServerResponse, content: any, status = 200) { + respond(res, JSON.stringify(content), "application/json", status); +} + +export function respondText(res: http.ServerResponse, content: string, status = 200) { + respond(res, content, "text/plain", status); +} + +export function respond(res: http.ServerResponse, content: string, contentType: string, status = 200) { + res.setHeader("Content-Type", contentType); + res.statusCode = status; + res.end(content); +}