138 lines
3.3 KiB
TypeScript
138 lines
3.3 KiB
TypeScript
// FIXME: rename this to make-app.ts and adjust imports accordingly
|
|
|
|
import{contentTypes} from './content-types'
|
|
import{httpCodes}from'./http-codes'
|
|
import express, {
|
|
type Express,
|
|
type NextFunction,
|
|
type Request as ExpressRequest,
|
|
type Response as ExpressResponse,
|
|
} from "express";
|
|
import { formatError, formatErrorHtml } from "./errors";
|
|
import {isRedirect, InternalHandler, AuthenticationRequired,
|
|
AuthorizationDenied, Call,type Method, type ProcessedRoute,methodParser, type Result, type Route,massageMethod } from "./types";
|
|
|
|
|
|
|
|
import { cli } from "./cli";
|
|
import{processRoutes}from'./routing'
|
|
|
|
|
|
process.on('uncaughtException', (err) => {
|
|
console.error(formatError(err));
|
|
process.exit(1);
|
|
});
|
|
|
|
process.on('unhandledRejection', (reason) => {
|
|
console.error(formatError(reason));
|
|
});
|
|
|
|
|
|
type MakeAppArgs={routes:Route[],
|
|
processTitle?: string,
|
|
}
|
|
|
|
export interface DiachronApp extends Express {
|
|
start: () => void
|
|
}
|
|
|
|
const makeApp = ({routes, processTitle}: MakeAppArgs) => {
|
|
if (process.title) {
|
|
process.title = `diachron:${cli.listen.port}`;
|
|
}
|
|
|
|
const processedRoutes = processRoutes(routes)
|
|
|
|
async function handler(
|
|
req: ExpressRequest,
|
|
_res: ExpressResponse,
|
|
): Promise<Result> {
|
|
const method = await methodParser.parseAsync(req.method);
|
|
|
|
const byMethod = processedRoutes[method];
|
|
console.log(
|
|
"DEBUG: req.path =",
|
|
JSON.stringify(req.path),
|
|
"method =",
|
|
method,
|
|
);
|
|
for (const [_idx, pr] of byMethod.entries()) {
|
|
const match = pr.matcher(req.path);
|
|
console.log("DEBUG: trying pattern, match result =", match);
|
|
if (match) {
|
|
console.log("match", match);
|
|
const resp = await pr.handler(req);
|
|
|
|
return resp;
|
|
}
|
|
}
|
|
|
|
const retval: Result = {
|
|
code: httpCodes.clientErrors.NotFound,
|
|
contentType: contentTypes.text.plain,
|
|
result: "not found!",
|
|
};
|
|
|
|
return retval;
|
|
}
|
|
|
|
// I don't like going around tsc but this is simple enough that it's probably OK.
|
|
const app = express() as DiachronApp
|
|
app.start = function() {
|
|
this.listen(cli.listen.port, cli.listen.host, () => {
|
|
console.log(`Listening on ${cli.listen.host}:${cli.listen.port}`);
|
|
});
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
app.use(express.json())
|
|
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
app.use(async (req: ExpressRequest, res: ExpressResponse) => {
|
|
const result0 = await handler(req, res);
|
|
|
|
const code = result0.code.code;
|
|
const result = result0.result;
|
|
|
|
console.log(result);
|
|
|
|
// Set any cookies from the result
|
|
if (result0.cookies) {
|
|
for (const cookie of result0.cookies) {
|
|
res.cookie(cookie.name, cookie.value, cookie.options ?? {});
|
|
}
|
|
}
|
|
|
|
if (isRedirect(result0)) {
|
|
res.redirect(code, result0.redirect);
|
|
} else {
|
|
res.status(code).send(result);
|
|
}
|
|
});
|
|
|
|
app.use(
|
|
(
|
|
err: Error,
|
|
_req: ExpressRequest,
|
|
res: ExpressResponse,
|
|
_next: NextFunction,
|
|
) => {
|
|
console.error(formatError(err));
|
|
res.status(500).type("html").send(formatErrorHtml(err));
|
|
},
|
|
);
|
|
|
|
return app;
|
|
}
|
|
|
|
export{makeApp};
|
|
|
|
function _isPromise<T>(value: T | Promise<T>): value is Promise<T> {
|
|
return typeof (value as any)?.then === "function";
|
|
}
|
|
|