From 1db84a384f27cbc26e2e8f128186b25cebc7d950 Mon Sep 17 00:00:00 2001 From: DevOps QA Date: Fri, 7 Aug 2026 19:02:10 +0000 Subject: [PATCH] fix(ssr): manually register engine manifest to fix 404 on all routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Angular 21 builder generates angular-app-engine-manifest.mjs at build time but does NOT inject the import into server.mjs. Without manually importing and registering the manifest via ɵsetAngularAppEngineManifest, AngularNodeAppEngine fails with 'Angular app engine manifest is not set' and every request returns 404. This matches the exact issue reported in PR #14 on staging where nginx starts successfully but returns 'Not Found' for all paths. --- server.ts | 54 +++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/server.ts b/server.ts index c07d46d..f4e84c7 100644 --- a/server.ts +++ b/server.ts @@ -7,25 +7,22 @@ import { import * as http from 'node:http'; /** - * Lazily instantiate AngularNodeAppEngine so the engine boots only when - * the first request arrives. All routes use RenderMode.Server — every - * request triggers on-demand SSR that fetches fresh API data before rendering. + * Manually import and register the Angular app engine manifest. + * The Angular 21 builder generates angular-app-engine-manifest.mjs at build time + * but does NOT automatically inject the import into server.mjs. + * Without this, AngularNodeAppEngine fails with "Angular app engine manifest is not set" + * and every SSR request returns 404. */ -let appEngine: AngularNodeAppEngine | null = null; -function getEngine(): AngularNodeAppEngine { - if (!appEngine) { - appEngine = new AngularNodeAppEngine(); - } - return appEngine; -} +// @ts-ignore — generated at build time by @angular/build +import engineManifest from './angular-app-engine-manifest.mjs'; +import { ɵsetAngularAppEngineManifest } from '@angular/ssr'; +ɵsetAngularAppEngineManifest(engineManifest); + +const appEngine = new AngularNodeAppEngine(); -/** - * Handle incoming requests using the Angular SSR engine. - * Every request is rendered on the server with fresh data from the API. - */ const requestHandler = createNodeRequestHandler(async (req, res, next) => { try { - const response = await getEngine().handle(req); + const response = await appEngine.handle(req); if (response) { await writeResponseToNodeResponse(response, res); @@ -37,17 +34,24 @@ const requestHandler = createNodeRequestHandler(async (req, res, next) => { } }); -export default requestHandler; - if (isMainModule(import.meta.url)) { - const httpServer = http.createServer(); - httpServer.on('request', async (req: http.IncomingMessage, res: http.ServerResponse) => { - requestHandler(req, res, () => { - res.statusCode = 404; - res.end('Not Found'); - }); + console.log('Node.js SSR server ready'); + const server = http.createServer(); + server.on('request', async (req, res) => { + try { + await requestHandler(req, res, () => { + res.statusCode = 404; + res.end('Not Found'); + }); + } catch (error) { + console.error('SSR error:', error); + res.statusCode = 500; + res.end('Internal server error'); + } }); - httpServer.listen(4200, () => { - console.log(`Node SSR server listening on http://localhost:4200`); + server.listen(4200, () => { + console.log('SSR server listening on http://localhost:4200'); }); } + +export { requestHandler };