fix: SPA fallback via HttpExceptionFilter for non-API 404s
Some checks failed
CI / Lint, Typecheck, Test & Build (push) Has been cancelled

Move SPA fallback from main.ts catch-all (didn't work - NestJS 404
handler runs first) to HttpExceptionFilter. Now non-API GET 404s
serve index.html so client-side routing works for /dashboard/* etc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 14:27:04 +00:00
parent 90ad75701d
commit 0d5e254479
2 changed files with 10 additions and 16 deletions

View File

@@ -1,13 +1,16 @@
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, Logger } from "@nestjs/common";
import { Response } from "express";
import { Request, Response } from "express";
import { join } from "path";
import { trace, SpanStatusCode } from "@opentelemetry/api";
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger("ExceptionFilter");
private readonly indexPath = join(__dirname, "..", "..", "..", "..", "web", "dist", "index.html");
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const request = ctx.getRequest<Request>();
const response = ctx.getResponse<Response>();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
@@ -30,6 +33,11 @@ export class HttpExceptionFilter implements ExceptionFilter {
this.logger.error(`Unhandled error: ${exception.message}`, exception.stack);
}
// SPA fallback: serve index.html for non-API GET 404s
if (status === HttpStatus.NOT_FOUND && request.method === "GET" && !request.path.startsWith("/api")) {
return response.sendFile(this.indexPath);
}
// Record error on active OTel span
const span = trace.getActiveSpan();
if (span) {