Skip to content

Commit

Permalink
add cors (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisCarriere authored Oct 30, 2023
1 parent c8e3d11 commit 3b4e9d2
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 13 deletions.
5 changes: 4 additions & 1 deletion index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { config } from "./src/config.js";
import { logger } from "./src/logger.js";
import GET from "./src/fetch/GET.js";
import OPTIONS from "./src/fetch/OPTIONS.js";
import * as prometheus from "./src/prometheus.js";
import { BadRequest } from "./src/fetch/cors.js";

if (config.verbose) logger.enable();

Expand All @@ -10,8 +12,9 @@ const app = Bun.serve({
port: config.port,
fetch(req: Request) {
if (req.method === "GET") return GET(req);
if (req.method === "OPTIONS") return OPTIONS(req);
prometheus.request_error.inc({pathname: new URL(req.url).pathname, status: 400});
return new Response("Invalid request", { status: 400 });
return BadRequest
}
});

Expand Down
11 changes: 6 additions & 5 deletions src/fetch/GET.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ import * as prometheus from "../prometheus.js";
import { logger } from "../logger.js";
import swaggerHtml from "../../swagger/index.html"
import swaggerFavicon from "../../swagger/favicon.png"
import { NotFound, toFile, toJSON, toText } from "./cors.js";

export default async function (req: Request) {
const { pathname} = new URL(req.url);
prometheus.request.inc({pathname});
if ( pathname === "/" ) return new Response(Bun.file(swaggerHtml));
if ( pathname === "/favicon.png" ) return new Response(Bun.file(swaggerFavicon));
if ( pathname === "/" ) return toFile(Bun.file(swaggerHtml));
if ( pathname === "/favicon.png" ) return toFile(Bun.file(swaggerFavicon));
if ( pathname === "/health" ) return health(req);
if ( pathname === "/metrics" ) return new Response(await registry.metrics(), {headers: {"Content-Type": registry.contentType}});
if ( pathname === "/openapi" ) return new Response(openapi, {headers: {"Content-Type": "application/json"}});
if ( pathname === "/metrics" ) return toText(await registry.metrics());
if ( pathname === "/openapi" ) return toJSON(openapi);
if ( pathname === "/chains" ) return chains(req);
if ( pathname === "/block" ) return block(req);
logger.warn(`Not found: ${pathname}`);
prometheus.request_error.inc({pathname, status: 404});
return new Response("Not found", { status: 404 });
return NotFound;
}
5 changes: 5 additions & 0 deletions src/fetch/OPTIONS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { CORS_HEADERS } from "./cors.js";

export default async function (req: Request) {
return new Response('Departed', {headers: CORS_HEADERS});
}
5 changes: 3 additions & 2 deletions src/fetch/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ import { makeQuery } from "../clickhouse/makeQuery.js";
import { logger } from "../logger.js";
import { Block, getBlock } from "../queries.js";
import * as prometheus from "../prometheus.js";
import { BadRequest, toJSON } from "./cors.js";

export default async function (req: Request) {
try {
const { searchParams } = new URL(req.url);
logger.info({searchParams: Object.fromEntries(Array.from(searchParams))});
const query = await getBlock(searchParams);
const response = await makeQuery<Block>(query)
return new Response(JSON.stringify(response.data), { headers: { "Content-Type": "application/json" } });
return toJSON(response.data);
} catch (e: any) {
logger.error(e);
prometheus.request_error.inc({pathname: "/block", status: 400});
return new Response(e.message, { status: 400 });
return BadRequest
}
}
5 changes: 3 additions & 2 deletions src/fetch/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { makeQuery } from "../clickhouse/makeQuery.js";
import { logger } from "../logger.js";
import * as prometheus from "../prometheus.js";
import { getChain } from "../queries.js";
import { BadRequest, toJSON } from "./cors.js";

export async function supportedChainsQuery() {
const response = await makeQuery<{chain: string}>(getChain());
Expand All @@ -11,10 +12,10 @@ export async function supportedChainsQuery() {
export default async function (req: Request) {
try {
const chains = await supportedChainsQuery();
return new Response(JSON.stringify(chains), { headers: { "Content-Type": "application/json" } });
return toJSON(chains);
} catch (e: any) {
logger.error(e);
prometheus.request_error.inc({pathname: "/chains", status: 400});
return new Response(e.message, { status: 400 });
return BadRequest;
}
}
37 changes: 37 additions & 0 deletions src/fetch/cors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { BunFile } from "bun";

export const BadRequest = toText('Bad Request', 400);
export const NotFound = toText('Not Found', 404);
export const InternalServerError = toText("Internal Server Error", 500);

export const CORS_HEADERS = new Headers({
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, WWW-Authenticate",
});
export const JSON_HEADERS = new Headers({"Content-Type": "application/json"});
export const TEXT_HEADERS = new Headers({"Content-Type": "text/plain; version=0.0.4; charset=utf-8"});

export function appendHeaders(...args: Headers[]) {
const headers = new Headers(CORS_HEADERS); // CORS as default headers
for (const arg of args) {
for (const [key, value] of arg.entries()) {
headers.set(key, value);
}
}
return headers;
};

export function toJSON(body: any, status = 200, headers = new Headers()) {
const data = typeof body == "string" ? body : JSON.stringify(body, null, 2);
return new Response(data, { status, headers: appendHeaders(JSON_HEADERS, headers) });
}

export function toText(body: string, status = 200, headers = new Headers()) {
return new Response(body, { status, headers: appendHeaders(TEXT_HEADERS, headers) });
}

export function toFile(body: BunFile, status = 200, headers = new Headers()) {
const fileHeaders = new Headers({"Content-Type": body.type});
return new Response(body, { status, headers: appendHeaders(fileHeaders, headers) });
}
7 changes: 4 additions & 3 deletions src/fetch/health.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import client from "../clickhouse/createClient.js";
import { logger } from "../logger.js";
import * as prometheus from "../prometheus.js";
import { InternalServerError, toText } from "./cors.js";

export default async function (req: Request) {
try {
const response = await client.ping();
if (response.success === false) throw new Error(response.error.message);
if (response.success === true ) return new Response("OK");
return new Response("Unknown response from ClickHouse");
if (response.success === true ) return toText("OK");
return InternalServerError;
} catch (e: any) {
logger.error(e);
prometheus.request_error.inc({ pathname: "/health", status: 500});
return new Response(e.message, { status: 500 });
return InternalServerError;
}
}

0 comments on commit 3b4e9d2

Please sign in to comment.