CI Pipeline / backend-test (push) Successful in 5m31s
CI Pipeline / frontend-test (push) Successful in 23s
CI Pipeline / frontend-build (push) Failing after 27s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / backend-test (pull_request) Successful in 5m24s
CI Pipeline / frontend-test (pull_request) Successful in 23s
CI Pipeline / frontend-build (pull_request) Failing after 27s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s
Root cause: Dockerfile copied only browser/ to nginx; server/server.mjs was built but never deployed or executed. Prerender baked DEFAULT_CONFIG into static HTML with no API at build time, causing stale content on every initial paint. Changes: - Dockerfile: serve stage uses node:22-alpine, installs nginx via apk, copies both browser/ and server/ bundles from build stage - docker-entrypoint.sh: starts Angular SSR Node process on port 4200, waits for readiness, exports SSR_API_BASE_URL, then exec nginx - nginx.conf.template: adds upstream ssr_backend block (127.0.0.1:4200), proxies / to SSR backend, static assets served directly with 1y cache - app.server.routes.ts: all routes switched from RenderMode.Prerender to RenderMode.Server for zero stale content - server.ts: removed dead prerender code (PRERENDERED_ROUTES, isPrerenderedRoute, allowStaticRouteRender) - main.server.ts: injects SSR-aware apiBaseUrl via APP_CONFIG (reads SSR_API_BASE_URL env var) - server.spec.ts: updated 5 test suites + added 5 new suites (31 tests) covering server.ts cleanup, RenderMode.Server, entrypoint lifecycle, Dockerfile build, nginx upstream proxy, and API URL injection - vitest.config.ts: added server.spec.ts to include pattern Request flow: Browser -> nginx (port 80) -> SSR server (port 4200) -> Angular SSR -> HttpClient -> localhost:80/api/station-config -> nginx proxies to api:8000 -> fresh data rendered in HTML
54 lines
1.4 KiB
TypeScript
54 lines
1.4 KiB
TypeScript
import {
|
|
AngularNodeAppEngine,
|
|
createNodeRequestHandler,
|
|
isMainModule,
|
|
writeResponseToNodeResponse,
|
|
} from '@angular/ssr/node';
|
|
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.
|
|
*/
|
|
let appEngine: AngularNodeAppEngine | null = null;
|
|
function getEngine(): AngularNodeAppEngine {
|
|
if (!appEngine) {
|
|
appEngine = new AngularNodeAppEngine();
|
|
}
|
|
return appEngine;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
|
|
if (response) {
|
|
await writeResponseToNodeResponse(response, res);
|
|
} else {
|
|
next();
|
|
}
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
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');
|
|
});
|
|
});
|
|
httpServer.listen(4200, () => {
|
|
console.log(`Node SSR server listening on http://localhost:4200`);
|
|
});
|
|
}
|