import busboy from "busboy"; import HtmlDocument from "./html/HtmlDocument.js"; import XNode from "./html/XNode.js"; // import Content, { PageResult, Redirect } from "./Content.js"; import { LocalFile } from "./core/LocalFile.js"; import { WrappedRequest, WrappedResponse } from "./core/Wrapped.js"; import { ServiceProvider } from "@entity-access/entity-access/dist/di/di.js"; import { IClassOf } from "@entity-access/entity-access/dist/decorators/IClassOf.js"; import { OutgoingHttpHeaders } from "http"; import Content, { IContent, Redirect } from "./Content.js"; import JsonGenerator from "@entity-access/entity-access/dist/common/JsonGenerator.js"; import ServerLogger from "./core/ServerLogger.js"; export const isPage = Symbol("isPage"); export interface IRouteCheck { scope: ServiceProvider; method: string; current: string; path: string[]; route: { [key: string]: string}; request: WrappedRequest; } export interface IFormData { fields: { [key: string]: string}; files: LocalFile[]; } /** * Page should not contain any reference to underlying request/response objects. */ export default abstract class Page { static [isPage] = true; /** * This static method determines if the path can be handled by this page or not. * @param pageContext page related items * @returns true if it can handle the path, default is true */ static canHandle(pageContext: IRouteCheck) : boolean | Promise { return true; } request: WrappedRequest; response: WrappedResponse; route: {[key: string]: string}; maxUploadSize = void 0 as number; get query(): TQuery { return this.request?.query as any; } get body(): TInput { return this.request?.body; } get form() { return this.request?.form; } get sessionUser() { return this.request?.sessionUser; } get url() { return this.request?.URL.toString(); } get method() { return this.request?.method; } get headers() { return this.request?.headers; } signal: AbortSignal; currentPath: string[]; childPath: string[]; filePath: string; cacheControl: string; disposables: Disposable[] = []; private formDataPromise: Promise; constructor() { this.cacheControl = "no-cache, no-store, max-age=0"; } abstract run(): Content | Promise; resolve(c: IClassOf): T { return ServiceProvider.resolve(this, c); } reportError(error) { ServerLogger.reportError({ url: this.url, error }); } protected registerDisposable(d: Disposable | AsyncDisposable) { if (d[Symbol.asyncDispose]) { this.disposables.push({ [Symbol.dispose]() { d[Symbol.asyncDispose]().catch(ServerLogger.error); } }); return; } if (d[Symbol.dispose]) { this.disposables.push(d as any); } } protected content(h: IContent): Content; protected content(body: string, status?: number, contentType?: string, headers?: OutgoingHttpHeaders): Content; protected content(body: string | Partial, status?: number, contentType?: string, headers?: OutgoingHttpHeaders) { if (typeof body === "object") { return Content.create(body); } return Content.create({ body, status, contentType, headers }); } protected json(o: any, indent = 0, headers = void 0 as OutgoingHttpHeaders) { // const content = indent // ? JSON.stringify(o, undefined, indent) // : JSON.stringify(o); const jsr = new JsonGenerator(this); headers ??= {}; headers["content-type"] = "application/json; charset=utf-8"; return Content.readable(jsr.reader(o), { headers }); } protected redirect(location: string, { status = 301, headers = void 0 } = {}) { return new Redirect(location, status, headers); } protected notFound(suppressLog = true): Content | Promise { return Content.html( Not found The page you are looking for is not found.
{this.url} not found
, { status: 404, suppressLog } ); } protected serverError(error, status = 500): Content | Promise { return Content.html( Server Error There was an error processing you request.
{error.stack ?? error}
, { status } ); } }