Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CORS #46

Merged
merged 1 commit into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}
Loading