1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Switch main's route handlers to be static functions (#2712)

This commit is contained in:
Sebastian Malton 2021-05-12 08:08:42 -04:00 committed by GitHub
parent b373411a16
commit f4ff28e51c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 173 additions and 196 deletions

View File

@ -30,11 +30,11 @@ export const webpackDevServerPort = 9009;
// Special runtime paths // Special runtime paths
defineGlobal("__static", { defineGlobal("__static", {
get() { get() {
if (isDevelopment) { const root = isDevelopment
return path.resolve(contextDir, "static"); ? contextDir
} : (process.resourcesPath ?? contextDir);
return path.resolve(process.resourcesPath, "static"); return path.resolve(root, "static");
} }
}); });

View File

@ -1,40 +1,42 @@
import { Router } from "../router"; import { Router } from "../router";
const staticRoot = __dirname;
class TestRouter extends Router {
protected resolveStaticRootPath() {
return staticRoot;
}
}
describe("Router", () => { describe("Router", () => {
it("blocks path traversal attacks", async () => { it("blocks path traversal attacks", async () => {
const router = new TestRouter(); const response: any = {
const res = {
statusCode: 200, statusCode: 200,
end: jest.fn() 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 () => { it("serves files under static root", async () => {
const router = new TestRouter(); const response: any = {
const res = {
statusCode: 200, statusCode: 200,
write: jest.fn(), write: jest.fn(),
setHeader: jest.fn(), setHeader: jest.fn(),
end: jest.fn() end: jest.fn()
}; };
const req = { const req: any = {
url: "" 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);
}); });
}); });

View File

@ -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);
}
}

View File

@ -5,7 +5,7 @@ import path from "path";
import { readFile } from "fs-extra"; import { readFile } from "fs-extra";
import { Cluster } from "./cluster"; import { Cluster } from "./cluster";
import { apiPrefix, appName, publicPath, isDevelopment, webpackDevServerPort } from "../common/vars"; 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"; import logger from "./logger";
export interface RouterRequestOpts { export interface RouterRequestOpts {
@ -38,18 +38,29 @@ export interface LensApiRequest<P = any> {
} }
} }
export class Router { function getMimeType(filename: string) {
protected router: any; const mimeTypes: Record<string, string> = {
protected staticRootPath: string; 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"
};
public constructor() { return mimeTypes[path.extname(filename).slice(1)] || "text/plain";
this.router = new Call.Router();
this.addRoutes();
this.staticRootPath = this.resolveStaticRootPath();
} }
protected resolveStaticRootPath() { export class Router {
return path.resolve(__static); protected router = new Call.Router();
protected static rootPath = path.resolve(__static);
public constructor() {
this.addRoutes();
} }
public async route(cluster: Cluster, req: http.IncomingMessage, res: http.ServerResponse): Promise<boolean> { public async route(cluster: Cluster, req: http.IncomingMessage, res: http.ServerResponse): Promise<boolean> {
@ -90,32 +101,17 @@ export class Router {
}; };
} }
protected getMimeType(filename: string) { protected static async handleStaticFile({ params, response, raw: { req } }: LensApiRequest): Promise<void> {
const mimeTypes: Record<string, string> = { let filePath = params.path;
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"; for (let retryCount = 0; retryCount < 5; retryCount += 1) {
} const asset = path.join(Router.rootPath, filePath);
async handleStaticFile(filePath: string, res: http.ServerResponse, req: http.IncomingMessage, retryCount = 0) {
const asset = path.join(this.staticRootPath, filePath);
const normalizedFilePath = path.resolve(asset); const normalizedFilePath = path.resolve(asset);
if (!normalizedFilePath.startsWith(this.staticRootPath)) { if (!normalizedFilePath.startsWith(Router.rootPath)) {
res.statusCode = 404; response.statusCode = 404;
res.end();
return; return response.end();
} }
try { try {
@ -126,61 +122,59 @@ export class Router {
if (isDevelopment && toWebpackDevServer) { if (isDevelopment && toWebpackDevServer) {
const redirectLocation = `http://localhost:${webpackDevServerPort}${req.url}`; const redirectLocation = `http://localhost:${webpackDevServerPort}${req.url}`;
res.statusCode = 307; response.statusCode = 307;
res.setHeader("Location", redirectLocation); response.setHeader("Location", redirectLocation);
res.end();
return; return response.end();
} }
const data = await readFile(asset); const data = await readFile(asset);
res.setHeader("Content-Type", this.getMimeType(asset)); response.setHeader("Content-Type", getMimeType(asset));
res.write(data); response.write(data);
res.end(); response.end();
} catch (err) { } catch (err) {
if (retryCount > 5) { if (retryCount > 5) {
logger.error("handleStaticFile:", err.toString()); logger.error("handleStaticFile:", err.toString());
res.statusCode = 404; response.statusCode = 404;
res.end();
return; return response.end();
} }
this.handleStaticFile(`${publicPath}/${appName}.html`, res, req, Math.max(retryCount, 0) + 1);
filePath = `${publicPath}/${appName}.html`;
} }
} }
}
protected addRoutes() { protected addRoutes() {
// Static assets // Static assets
this.router.add( this.router.add({ method: "get", path: "/{path*}" }, Router.handleStaticFile);
{ method: "get", path: "/{path*}" },
({ params, response, raw: { req } }: LensApiRequest) => {
this.handleStaticFile(params.path, response, req);
});
this.router.add({ method: "get", path: "/version"}, versionRoute.getVersion.bind(versionRoute)); this.router.add({ method: "get", path: "/version" }, VersionRoute.getVersion);
this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, kubeconfigRoute.routeServiceAccountRoute.bind(kubeconfigRoute)); this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, KubeconfigRoute.routeServiceAccountRoute);
// Metrics API // 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 // 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 // Helm API
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, helmRoute.listCharts.bind(helmRoute)); this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, HelmApiRoute.listCharts);
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}` }, HelmApiRoute.getChart);
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/{repo}/{chart}/values` }, HelmApiRoute.getChartValues);
this.router.add({ method: "post", path: `${apiPrefix}/v2/releases` }, helmRoute.installChart.bind(helmRoute)); this.router.add({ method: "post", path: `${apiPrefix}/v2/releases` }, HelmApiRoute.installChart);
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}` }, HelmApiRoute.updateRelease);
this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback` }, helmRoute.rollbackRelease.bind(helmRoute)); this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback` }, HelmApiRoute.rollbackRelease);
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace?}` }, helmRoute.listReleases.bind(helmRoute)); this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace?}` }, HelmApiRoute.listReleases);
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}` }, HelmApiRoute.getRelease);
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}/values` }, HelmApiRoute.getReleaseValues);
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/history` }, helmRoute.getReleaseHistory.bind(helmRoute)); 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}` }, helmRoute.deleteRelease.bind(helmRoute)); this.router.add({ method: "delete", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, HelmApiRoute.deleteRelease);
// Resource Applier API // 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);
} }
} }

View File

@ -1,143 +1,141 @@
import { LensApiRequest } from "../router"; import { LensApiRequest } from "../router";
import { helmService } from "../helm/helm-service"; import { helmService } from "../helm/helm-service";
import { LensApi } from "../lens-api"; import { respondJson, respondText } from "../utils/http-responses";
import logger from "../logger"; import logger from "../logger";
class HelmApiRoute extends LensApi { export class HelmApiRoute {
public async listCharts(request: LensApiRequest) { static async listCharts(request: LensApiRequest) {
const { response } = request; const { response } = request;
const charts = await helmService.listCharts(); 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; const { params, query, response } = request;
try { try {
const chart = await helmService.getChart(params.repo, params.chart, query.get("version")); const chart = await helmService.getChart(params.repo, params.chart, query.get("version"));
this.respondJson(response, chart); respondJson(response, chart);
} catch (error) { } 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; const { params, query, response } = request;
try { try {
const values = await helmService.getChartValues(params.repo, params.chart, query.get("version")); const values = await helmService.getChartValues(params.repo, params.chart, query.get("version"));
this.respondJson(response, values); respondJson(response, values);
} catch (error) { } 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; const { payload, cluster, response } = request;
try { try {
const result = await helmService.installChart(cluster, payload); const result = await helmService.installChart(cluster, payload);
this.respondJson(response, result, 201); respondJson(response, result, 201);
} catch (error) { } catch (error) {
logger.debug(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; const { cluster, params, payload, response } = request;
try { try {
const result = await helmService.updateRelease(cluster, params.release, params.namespace, payload ); const result = await helmService.updateRelease(cluster, params.release, params.namespace, payload );
this.respondJson(response, result); respondJson(response, result);
} catch (error) { } catch (error) {
logger.debug(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; const { cluster, params, payload, response } = request;
try { try {
const result = await helmService.rollback(cluster, params.release, params.namespace, payload.revision); const result = await helmService.rollback(cluster, params.release, params.namespace, payload.revision);
this.respondJson(response, result); respondJson(response, result);
} catch (error) { } catch (error) {
logger.debug(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; const { cluster, params, response } = request;
try { try {
const result = await helmService.listReleases(cluster, params.namespace); const result = await helmService.listReleases(cluster, params.namespace);
this.respondJson(response, result); respondJson(response, result);
} catch(error) { } catch(error) {
logger.debug(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; const { cluster, params, response } = request;
try { try {
const result = await helmService.getRelease(cluster, params.release, params.namespace); const result = await helmService.getRelease(cluster, params.release, params.namespace);
this.respondJson(response, result); respondJson(response, result);
} catch (error) { } catch (error) {
logger.debug(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; const { cluster, params, response, query } = request;
try { try {
const result = await helmService.getReleaseValues(cluster, params.release, params.namespace, query.has("all")); const result = await helmService.getReleaseValues(cluster, params.release, params.namespace, query.has("all"));
this.respondText(response, result); respondText(response, result);
} catch (error) { } catch (error) {
logger.debug(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; const { cluster, params, response } = request;
try { try {
const result = await helmService.getReleaseHistory(cluster, params.release, params.namespace); const result = await helmService.getReleaseHistory(cluster, params.release, params.namespace);
this.respondJson(response, result); respondJson(response, result);
} catch (error) { } catch (error) {
logger.debug(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; const { cluster, params, response } = request;
try { try {
const result = await helmService.deleteRelease(cluster, params.release, params.namespace); const result = await helmService.deleteRelease(cluster, params.release, params.namespace);
this.respondJson(response, result); respondJson(response, result);
} catch (error) { } catch (error) {
logger.debug(error); logger.debug(error);
this.respondText(response, error, 422); respondText(response, error, 422);
} }
} }
} }
export const helmRoute = new HelmApiRoute();

View File

@ -1,5 +1,5 @@
import { LensApiRequest } from "../router"; import { LensApiRequest } from "../router";
import { LensApi } from "../lens-api"; import { respondJson } from "../utils/http-responses";
import { Cluster } from "../cluster"; import { Cluster } from "../cluster";
import { CoreV1Api, V1Secret } from "@kubernetes/client-node"; import { CoreV1Api, V1Secret } from "@kubernetes/client-node";
@ -40,9 +40,8 @@ function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster
}; };
} }
class KubeconfigRoute extends LensApi { export class KubeconfigRoute {
static async routeServiceAccountRoute(request: LensApiRequest) {
public async routeServiceAccountRoute(request: LensApiRequest) {
const { params, response, cluster} = request; const { params, response, cluster} = request;
const client = (await cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api); const client = (await cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api);
const secretList = await client.listNamespacedSecret(params.namespace); const secretList = await client.listNamespacedSecret(params.namespace);
@ -53,8 +52,6 @@ class KubeconfigRoute extends LensApi {
}); });
const data = generateKubeConfig(params.account, secret, cluster); const data = generateKubeConfig(params.account, secret, cluster);
this.respondJson(response, data); respondJson(response, data);
} }
} }
export const kubeconfigRoute = new KubeconfigRoute();

View File

@ -1,6 +1,6 @@
import _ from "lodash"; import _ from "lodash";
import { LensApiRequest } from "../router"; import { LensApiRequest } from "../router";
import { LensApi } from "../lens-api"; import { respondJson } from "../utils/http-responses";
import { Cluster, ClusterMetadataKey } from "../cluster"; import { Cluster, ClusterMetadataKey } from "../cluster";
import { ClusterPrometheusMetadata } from "../../common/cluster-store"; import { ClusterPrometheusMetadata } from "../../common/cluster-store";
import logger from "../logger"; import logger from "../logger";
@ -41,8 +41,8 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa
return Promise.all(queries.map(loadMetric)); return Promise.all(queries.map(loadMetric));
} }
class MetricsRoute extends LensApi { export class MetricsRoute {
async routeMetrics({ response, cluster, payload, query }: LensApiRequest) { static async routeMetrics({ response, cluster, payload, query }: LensApiRequest) {
const queryParams: IMetricsQuery = Object.fromEntries(query.entries()); const queryParams: IMetricsQuery = Object.fromEntries(query.entries());
const prometheusMetadata: ClusterPrometheusMetadata = {}; const prometheusMetadata: ClusterPrometheusMetadata = {};
@ -57,20 +57,19 @@ class MetricsRoute extends LensApi {
if (!prometheusPath) { if (!prometheusPath) {
prometheusMetadata.success = false; prometheusMetadata.success = false;
this.respondJson(response, {});
return; return respondJson(response, {});
} }
// return data in same structure as query // return data in same structure as query
if (typeof payload === "string") { if (typeof payload === "string") {
const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams); const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams);
this.respondJson(response, data); respondJson(response, data);
} else if (Array.isArray(payload)) { } else if (Array.isArray(payload)) {
const data = await loadMetrics(payload, cluster, prometheusPath, queryParams); const data = await loadMetrics(payload, cluster, prometheusPath, queryParams);
this.respondJson(response, data); respondJson(response, data);
} else { } else {
const queries = Object.entries(payload).map(([queryName, queryOpts]) => ( const queries = Object.entries(payload).map(([queryName, queryOpts]) => (
(prometheusProvider.getQueries(queryOpts) as Record<string, string>)[queryName] (prometheusProvider.getQueries(queryOpts) as Record<string, string>)[queryName]
@ -78,16 +77,14 @@ class MetricsRoute extends LensApi {
const result = await loadMetrics(queries, cluster, prometheusPath, queryParams); const result = await loadMetrics(queries, cluster, prometheusPath, queryParams);
const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]])); const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]]));
this.respondJson(response, data); respondJson(response, data);
} }
prometheusMetadata.success = true; prometheusMetadata.success = true;
} catch { } catch {
prometheusMetadata.success = false; prometheusMetadata.success = false;
this.respondJson(response, {}); respondJson(response, {});
} finally { } finally {
cluster.metadata[ClusterMetadataKey.PROMETHEUS] = prometheusMetadata; cluster.metadata[ClusterMetadataKey.PROMETHEUS] = prometheusMetadata;
} }
} }
} }
export const metricsRoute = new MetricsRoute();

View File

@ -1,11 +1,11 @@
import { LensApiRequest } from "../router"; import { LensApiRequest } from "../router";
import { LensApi } from "../lens-api";
import { spawn, ChildProcessWithoutNullStreams } from "child_process"; import { spawn, ChildProcessWithoutNullStreams } from "child_process";
import { Kubectl } from "../kubectl"; import { Kubectl } from "../kubectl";
import { shell } from "electron"; import { shell } from "electron";
import * as tcpPortUsed from "tcp-port-used"; import * as tcpPortUsed from "tcp-port-used";
import logger from "../logger"; import logger from "../logger";
import { getPortFrom } from "../utils/get-port"; import { getPortFrom } from "../utils/get-port";
import { respondJson } from "../utils/http-responses";
interface PortForwardArgs { interface PortForwardArgs {
clusterId: string; clusterId: string;
@ -95,9 +95,8 @@ class PortForward {
} }
} }
class PortForwardRoute extends LensApi { export class PortForwardRoute {
static async routePortForward(request: LensApiRequest) {
public async routePortForward(request: LensApiRequest) {
const { params, response, cluster} = request; const { params, response, cluster} = request;
const { namespace, port, resourceType, resourceName } = params; const { namespace, port, resourceType, resourceName } = params;
let portForward = PortForward.getPortforward({ let portForward = PortForward.getPortforward({
@ -117,18 +116,14 @@ class PortForwardRoute extends LensApi {
const started = await portForward.start(); const started = await portForward.start();
if (!started) { if (!started) {
this.respondJson(response, { return respondJson(response, {
message: "Failed to open port-forward" message: "Failed to open port-forward"
}, 400); }, 400);
return;
} }
} }
portForward.open(); portForward.open();
this.respondJson(response, {}); respondJson(response, {});
} }
} }
export const portForwardRoute = new PortForwardRoute();

View File

@ -1,19 +1,17 @@
import { LensApiRequest } from "../router"; import { LensApiRequest } from "../router";
import { LensApi } from "../lens-api"; import { respondJson, respondText } from "../utils/http-responses";
import { ResourceApplier } from "../resource-applier"; import { ResourceApplier } from "../resource-applier";
class ResourceApplierApiRoute extends LensApi { export class ResourceApplierApiRoute {
public async applyResource(request: LensApiRequest) { static async applyResource(request: LensApiRequest) {
const { response, cluster, payload } = request; const { response, cluster, payload } = request;
try { try {
const resource = await new ResourceApplier(cluster).apply(payload); const resource = await new ResourceApplier(cluster).apply(payload);
this.respondJson(response, [resource], 200); respondJson(response, [resource], 200);
} catch (error) { } catch (error) {
this.respondText(response, error, 422); respondText(response, error, 422);
} }
} }
} }
export const resourceApplierRoute = new ResourceApplierApiRoute();

View File

@ -1,13 +1,11 @@
import { LensApiRequest } from "../router"; import { LensApiRequest } from "../router";
import { LensApi } from "../lens-api"; import { respondJson } from "../utils/http-responses";
import { getAppVersion } from "../../common/utils"; import { getAppVersion } from "../../common/utils";
class VersionRoute extends LensApi { export class VersionRoute {
public async getVersion(request: LensApiRequest) { static async getVersion(request: LensApiRequest) {
const { response } = request; const { response } = request;
this.respondJson(response, { version: getAppVersion()}, 200); respondJson(response, { version: getAppVersion()}, 200);
} }
} }
export const versionRoute = new VersionRoute();

View File

@ -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);
}