💾 Archived View for iich.space › src › modules › mission-control › application.ts captured on 2022-03-01 at 16:04:40.

View Raw

More Information

⬅️ Previous capture (2021-12-03)

-=-=-=-=-=-=-

import { Request, Response, Server, Status } from '@/gemini';
import { createLogger } from '@/log';

import { I18n } from './i18n';
import { Router } from './router';
import { TemplateBuilder } from './template-builder';

export type Params = Record<string, string>;

export interface ApplicationOptions {
  i18n?: I18n;
}

export interface Extra {
  params: Params;
  tb: TemplateBuilder;
}

export type Handler = (
  req: Request,
  res: Response,
  extra: Extra,
) => Promise<void> | void;

const log = createLogger();

export class Application extends Router<Handler> {
  i18n: I18n | null = null;

  async handle(req: Request, res: Response): Promise<void> {
    await (async () => {
      const tb = new TemplateBuilder(req.url, this.i18n);

      for (const { params, value: handler } of this.match(req.url.pathname)) {
        const stuff = { params, tb };

        try {
          await handler(req, res, stuff);
        } catch (error: unknown) {
          log.error(error);

          try {
            res.sendStatus(
              Status.PERMANENT_FAILURE,
              error instanceof Error ? error.toString() : '',
            );
            res.end();
          } catch (error: unknown) {
            log.error(error);
          }

          return;
        }

        if (res.isClosed) {
          return;
        }
      }

      log.debug('handle fallthrough');

      res.end();
    })();
  }
}

export const createApplication = (
  server: Server,
  options: ApplicationOptions = {},
): Application => {
  const application = new Application();

  if (options.i18n !== undefined) {
    application.i18n = options.i18n;
  }

  server.on('request', (req, res) => application.handle(req, res));

  return application;
};