import { Either, getRequiredEnv, getStdout, IEither, isObject, Traceable, validateExecutionEntries, } from "@liz-ci/utils"; import { IJobQueuer } from "./mod.ts"; import type { Job } from "@liz-ci/model"; const SERVER_CONFIG = { host: "0.0.0.0", port: 9000, }; interface IHealthCheckActivity { healthCheck(req: R): Promise; } interface IJobHookActivity { processHook(req: R): Promise; } type GetJobRequest = { jobType: string; args: unknown }; class JobHookActivityImpl implements IJobHookActivity> { constructor(private readonly queuer: IJobQueuer>) {} private getJob(u: Traceable): Either { const { logger: _logger, item } = u; const logger = _logger.addTracer(() => "[getJob]"); const couldBeJsonJob = isObject(item) && "arguments" in item && "type" in item && item; const couldBeArguments = couldBeJsonJob && isObject(item.arguments) && item.arguments; if (!couldBeJsonJob) { const err = "seems like a pwetty mawfomed job \\(-.-)/"; logger.warn(err); return Either.left(err); } return validateExecutionEntries({ type: item.type, ...couldBeArguments, }).mapBoth((err) => { const e = "your reqwest seems invawid (´。﹏。`) can you fix? uwu\n" + err.toString(); logger.warn(e); return e; }, (_ok) => item); } public async processHook(r: Traceable) { return await r.bimap(Traceable.withClassTrace(this)).map(aPost) .map(aJson(this.getJob)); } // flatMapAsync(aJsonPost(this.getJob)) // .map(TraceableImpl.promiseify((g) => { // if (jobRequest) { // return g.map(() => jobRequest) // .map(this.getJob) // .map( // ({ item: { ok: jobRequest, err } }) => { // if (err) return { err: new Response(err, { status: 400 }) }; // return { ok: jobRequest }; // }, // ); // } // return g.map(() => ({ ok: undefined, err })); // })) // .map(TraceableImpl.promiseify(({ item: t }) => { // const { item: { ok: job, err } } = t; // if (err) return t.map(() => Promise.resolve(err)); // // return t.map(() => job!) // .map(this.queuer.queue) // .map(TraceableImpl.promiseify(({ item, logger }) => { // if (item.ok) { // return new Response(item.ok, { status: 200 }); // } // logger.error(item.err); // return new Response("i messed up D:\n", { status: 500 }); // })); // })); // } } class LizCIServerImpl implements ILizCIServer { constructor( private readonly healthCheckActivity: IHealthCheckActivity, private readonly jobHookActivity: IJobHookActivity, ) {} private route( req: Traceable, ): Traceable> { return req.flatMap((req) => { const { logger, item: { method, pathname } } = req; if (pathname === "/health") { return this.healthCheckActivity.healthCheck(req); } return this.jobHookActivity.processHook(req); }); } public async serve(req: Request): Promise { const traceId = crypto.randomUUID(); const { pathname } = new URL(req.url); const traceSupplier = () => `[${traceId} <- ${req.method}'d @ ${pathname}]`; return TraceableImpl.from(req) .bimap(({ item: req }) => [{ req, pathname }, traceSupplier]) .flatMap(this.route) .map(({ item, logger }) => item.catch((e) => { const errorMessage = `oh noes! something went wrong (ಥ_ಥ) so sowwy!`; logger.error(errorMessage, e); return new Response(`${errorMessage}\n`, { status: 500 }); }) ) .item; } } class JobQueue { private readonly logger: PrefixLogger; private readonly url: URL; private readonly pathname: string; constructor(private readonly request: Request, private readonly) { this.url = new URL(request.url); this.pathname = this.url.pathname; this.logger = this.createLogger(); } /** * Creates a logger with request-specific context */ /** * Performs health checks on dependent services */ private async performHealthCheck(): Promise { } /** * Handles health check requests */ private async handleHealthCheck(): Promise { try { await this.performHealthCheck(); } catch (error) { } } /** * Queues a job in the laminar system */ private async queueJob(jobName: string, args: JobRequest): Promise { } /** * Validates job request parameters */ private validateJobRequest( jobName: string, args: unknown, ): { valid: boolean; response?: Response } { } /** * Main method to handle the request */ public async handle(): Promise { this.logger.log("go! :DDD"); // Handle health check requests if (this.pathname === "/health") { return this.handleHealthCheck(); } // Validate HTTP method if (this.request.method !== "POST") { } // Extract job name from path if (!validation.valid) { return validation.response!; } // Queue the job return this.queueJob(jobName, requestBody as JobRequest); } /** * Handles the entire request lifecycle, including error handling */ public async processRequest(): Promise { try { return await this.handle(); } catch (error) { } finally { this.logger.log("allll done!"); } } } /** * Entry point - starts the server */ Deno.serve(SERVER_CONFIG, async (request: Request) => { const handler = new RequestHandler(request); return handler.processRequest(); });