import * as plugins from './rendertron.plugins.js'; import * as paths from './rendertron.paths.js'; import { logger } from './rendertron.logging.js'; import { db } from './rendertron.db.js'; import { PrerenderResult } from './rendertron.classes.prerenderresult.js'; import { PrerenderManager } from './rendertron.classes.prerendermanager.js'; import { TaskManager } from './rendertron.taskmanager.js'; export class Rendertron { public projectinfo!: plugins.projectinfo.ProjectInfo; public serviceServerInstance!: plugins.typedserver.utilityservers.UtilityServiceServer; public prerenderManager!: PrerenderManager; public taskManager!: TaskManager; /** * starts the financeflow instance */ public async start() { this.projectinfo = await plugins.projectinfo.ProjectInfo.create(paths.packageDir); await db.init(); this.prerenderManager = new PrerenderManager(); this.taskManager = new TaskManager(this); await this.prerenderManager.start(); await this.taskManager.start(); this.serviceServerInstance = new plugins.typedserver.utilityservers.UtilityServiceServer({ serviceDomain: 'rendertron.lossless.one', serviceName: 'rendertron', serviceVersion: this.projectinfo.npm.version, addCustomRoutes: async (serverArg) => { serverArg.addRoute( '/render/*', 'GET', async (ctxArg) => { const requestedUrl = `${ctxArg.url.pathname.replace('/render/', '')}${ctxArg.url.search}`; logger.log('info', `Got SSR request for ${requestedUrl}`); if (requestedUrl.startsWith('https://url(')) { logger.log('warn', `relative url error for ${requestedUrl}`); return new Response('error due to relative protocol', { status: 500 }); } const originResponse = await plugins.smartrequest.SmartRequest.create() .url(requestedUrl) .options({ keepAlive: false }) .get() .catch((error) => { logger.log('warn', `the origin request errored for ${requestedUrl}`); return null; }); if (!originResponse) { return new Response(`rendertron encountered an error for ${requestedUrl}`, { status: 502 }); } const responseHeaders = new Headers(); for (const [headerKey, headerValue] of Object.entries(originResponse.headers)) { if (Array.isArray(headerValue)) { for (const headerValueItem of headerValue) { responseHeaders.append(headerKey, headerValueItem); } } else if (headerValue !== undefined) { responseHeaders.set(headerKey, headerValue.toString()); } } if (originResponse.headers['content-type']?.includes('text/html')) { logger.log('info', `Piping ${requestedUrl} through smartssr.`); return new Response(await this.prerenderManager.getPrerenderResultForUrl(requestedUrl), { status: originResponse.status, headers: responseHeaders, }); } else { logger.log('info', `Serving ${requestedUrl} directly.`); return new Response(await originResponse.arrayBuffer(), { status: originResponse.status, headers: responseHeaders, }); } } ); }, }); await this.serviceServerInstance.start(); } public async stop() { this.serviceServerInstance ? await this.serviceServerInstance.stop() : null; this.prerenderManager ? await this.prerenderManager.stop() : null; this.taskManager ? await this.taskManager.stop() : null; if (db.status === 'connected') { await db.close(); } } }