Skip to content

Instantly share code, notes, and snippets.

@allancalix
Created July 8, 2023 03:01
Show Gist options
  • Save allancalix/dee5ce4643e3cc6eaee32b64d27021cc to your computer and use it in GitHub Desktop.
Save allancalix/dee5ce4643e3cc6eaee32b64d27021cc to your computer and use it in GitHub Desktop.
@bull-board adapter implementation for Elysia

This is a working example of an Elysia adapter which registers a bunch of routes on the Elysia app as well as statically serves files from the static assets.

Some things you might want to support if you use this:

  • setbasePath => other plugins use this to modify the relative base path of the routes registered
  • "@bull-board/api/dist/typings/app.js"; this import works but it's weird depending on the dist/ directory
import * as path from "path";
import { staticPlugin } from "@elysiajs/static";
import ejs from "ejs";
import { Context, Elysia, HTTPMethod as ElysiaMethod } from "elysia";
import {
AppControllerRoute,
AppViewRoute,
BullBoardQueues,
ControllerHandlerReturnType,
FavIcon,
IMiscLink,
IServerAdapter,
UIConfig,
HTTPMethod,
} from "@bull-board/api/dist/typings/app.js";
export class ElysiaAdapter implements IServerAdapter {
private basePath = "";
private bullBoardQueues: BullBoardQueues | undefined;
private errorHandler: ((error: Error) => ControllerHandlerReturnType) | undefined;
private statics: { path: string; route: string } | undefined;
private apiRoutes: AppControllerRoute[] | undefined;
private entryRoute: AppViewRoute | undefined;
private viewPath: string | undefined;
private uiConfig: UIConfig = {};
setQueues(bullBoardQueues: BullBoardQueues): IServerAdapter {
this.bullBoardQueues = bullBoardQueues;
return this;
}
setViewsPath(viewPath: string): IServerAdapter {
this.viewPath = viewPath;
return this;
}
setStaticPath(staticsRoute: string, staticsPath: string): IServerAdapter {
this.statics = { route: staticsRoute, path: staticsPath };
return this;
}
setErrorHandler(
handler: (error: Error) => ControllerHandlerReturnType,
): IServerAdapter {
this.errorHandler = handler;
return this;
}
setEntryRoute(route: AppViewRoute): IServerAdapter {
this.entryRoute = route;
return this;
}
setApiRoutes(routes: AppControllerRoute[]): IServerAdapter {
this.apiRoutes = routes;
return this;
}
setUIConfig(
config: Partial<{
boardTitle: string;
boardLogo: {
path: string;
width?: string | number;
height?: string | number;
};
miscLinks: IMiscLink[];
favIcon: FavIcon;
}>,
): IServerAdapter {
this.uiConfig = config;
return this;
}
parseMethod(method: HTTPMethod): ElysiaMethod {
switch (method) {
case "get":
case "post":
case "put":
return method.toUpperCase() as ElysiaMethod;
}
}
public plugin() {
if (!this.statics) {
throw new Error(`Please call 'setStaticPath' before using 'registerPlugin'`);
} else if (!this.entryRoute) {
throw new Error(`Please call 'setEntryRoute' before using 'registerPlugin'`);
} else if (!this.viewPath) {
throw new Error(`Please call 'setViewsPath' before using 'registerPlugin'`);
} else if (!this.apiRoutes) {
throw new Error(`Please call 'setApiRoutes' before using 'registerPlugin'`);
} else if (!this.bullBoardQueues) {
throw new Error(`Please call 'setQueues' before using 'registerPlugin'`);
} else if (!this.errorHandler) {
throw new Error(
`Please call 'setErrorHandler' before using 'registerPlugin'`,
);
}
return (app: Elysia): Elysia => {
const queues = this.bullBoardQueues;
function handler(handler: any) {
return async (context: Context): Promise<Response> => {
const response = await handler({
queues: queues as any,
params: context.params,
query: context.query,
});
return new Response(JSON.stringify(response.body), {
status: response.status || 200,
});
};
}
app.use(
staticPlugin({
prefix: this.statics?.route,
assets: this.statics?.path,
}),
);
const { method, route, handler: viewHandler } = this.entryRoute!;
const viewRoutes = Array.isArray(route) ? route : [route];
viewRoutes.forEach((localPath) => {
app.route(this.parseMethod(method), localPath, async (context) => {
const { name, params } = viewHandler({
basePath: this.basePath,
uiConfig: this.uiConfig,
});
const template = Bun.file(path.join(this.viewPath!, name));
const render = await ejs.compile(await template.text())(params);
return new Response(render, {
headers: {
"Content-Type": "text/html",
},
status: 200,
});
});
});
this.apiRoutes?.forEach((route) => {
const methods = Array.isArray(route.method)
? route.method
: [route.method];
methods.forEach((method) => {
const paths = Array.isArray(route.route)
? route.route
: [route.route];
paths.forEach((path) => {
app.route(this.parseMethod(method), path, handler(route.handler));
});
});
});
return app;
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment