From 83b79fe0da445d5679906d867bae606872f26e37 Mon Sep 17 00:00:00 2001 From: robot Date: Sun, 9 Aug 2026 08:13:35 +0000 Subject: [PATCH] =?UTF-8?q?fix(ssr):=20remove=20spider-only=20gate=20?= =?UTF-8?q?=E2=80=94=20serve=20full=20SSR=20to=20all=20users?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CSR shell served to regular browsers caused a visible blank-to-content flash: the browser painted an empty , then waited for Angular to bootstrap and lazy-load the route before any content appeared. All requests now route through the Angular SSR engine (appEngine.handle()), which fetches fresh station config from the API via APP_INITIALIZER before rendering. The first paint carries real content, colors, and assets. --- server.spec.ts | 34 ++++++++++++++++------------------ server.ts | 23 +++++++++++------------ 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/server.spec.ts b/server.spec.ts index 5ca3bbe..5aa8a71 100644 --- a/server.spec.ts +++ b/server.spec.ts @@ -33,28 +33,26 @@ vi.mock('./angular-app-engine-manifest.mjs', () => ({ })); describe('SSR server — server.ts', () => { - it('should export a request handler', async () => { + it('should route all requests through Angular SSR engine (no spider gate)', async () => { + const fs = require('fs'); + const content = fs.readFileSync('./server.ts', 'utf-8'); + expect(content).toContain('requestHandler(req, res,'); + // Should NOT have spider-only SSR gate + expect(content).not.toContain('if (!isSpider(userAgent))'); + expect(content).not.toContain('isSpider'); + }); + + it('should still have serveCsrShell as a fallback for when SSR engine fails', async () => { + const fs = require('fs'); + const content = fs.readFileSync('./server.ts', 'utf-8'); + expect(content).toContain('serveCsrShell'); + }); + + it('should have request handler export', async () => { const mod = await import('./server'); expect(mod.requestHandler).toBeDefined(); }); - it('should have spider detection imports and serveCsrShell', async () => { - const fs = require('fs'); - const content = fs.readFileSync('./server.ts', 'utf-8'); - expect(content).toContain('isSpider'); - expect(content).toContain('getUserAgent'); - expect(content).toContain('serveCsrShell'); - expect(content).toContain('spider_detection'); - }); - - it('should gate SSR behind spider detection', async () => { - const fs = require('fs'); - const content = fs.readFileSync('./server.ts', 'utf-8'); - // Verify the spider detection gate exists - expect(content).toContain("if (!isSpider(userAgent))"); - expect(content).toContain('serveCsrShell(res)'); - }); - it('should inject fresh station config as window.__STATION_CONFIG', async () => { const fs = require('fs'); const content = fs.readFileSync('./server.ts', 'utf-8'); diff --git a/server.ts b/server.ts index 7d01e5b..6c6461a 100644 --- a/server.ts +++ b/server.ts @@ -8,7 +8,6 @@ import * as fs from 'node:fs'; import * as http from 'node:http'; import * as https from 'node:https'; import * as path from 'node:path'; -import { isSpider, getUserAgent } from './spider_detection'; /** * Manually import and register the Angular app engine manifest. @@ -263,17 +262,17 @@ if (isMainModule(import.meta.url)) { const server = http.createServer(); server.on('request', async (req, res) => { try { - // ── Spider detection gate ────────────────────────── - // Only search engine spiders get full SSR rendering. - // Regular users receive the CSR app shell from disk, - // enhanced with fresh station config injected at runtime. - const userAgent = getUserAgent(req.headers); - if (!isSpider(userAgent)) { - await serveCsrShell(res); - return; - } - - // Spider — render full HTML via Angular SSR with live API data + // ── SSR rendering for all users ──────────────────── + // Every user — spiders and regular browsers — gets full + // Angular SSR rendering. The SSR engine (main.server.ts) + // fetches fresh station config from the API via APP_INITIALIZER + // before rendering, so the first paint carries real content, + // colors, and assets. No CSR shell flash, no blank gap. + // + // NOTE: The spider-only SSR gate was removed because serving + // a CSR shell (empty ) to regular browsers caused + // a visible blank-to-content transition. The SSR engine is + // fast enough to handle regular traffic. await requestHandler(req, res, () => { res.statusCode = 404; res.end('Not Found'); -- 2.54.0