Finish the NestJS migration — drop the legacy Express app

NestJS now serves the whole surface: every /api domain plus the platform
routes (uploads, /mcp, the OAuth/MCP SDK + /.well-known metadata and the
production SPA fallback). Removed server/src/app.ts, all of
server/src/routes/* and the strangler dispatcher; index.ts and the
integration suite share a single buildApp() bootstrap so prod and tests
can't drift.

- Platform/transport routes extracted to nest/platform/platform.routes.ts
  and mounted before app.init() — Nest's router answers an unmatched
  request with a 404, so a route registered after init is never reached.
  The SPA fallback is a NotFoundException filter and the catch-all uses a
  RegExp (Express 5's path-to-regexp rejects a bare '*').
- New modules: memories (/api/integrations/memories — the Journey
  gallery's Immich/Synology proxy), addons (GET /api/addons) and the
  cross-trip GET /api/reservations/upcoming.
- TrekExceptionFilter reproduces the old multer / err.statusCode handling
  so upload rejections keep their 400/413 { error } body and non-ASCII
  filenames survive (defParamCharset).
- addTripToJourney and the MCP get_journey_share_link tool gained the
  trip-access check they were missing.
- Re-pointed the 34 integration tests + the websocket test onto the Nest
  app; removed the now-meaningless Express-vs-Nest parity tests and a few
  orphaned client components.
This commit is contained in:
Maurice
2026-05-31 13:29:22 +02:00
parent fc7d8b5d12
commit bfe52579df
138 changed files with 2289 additions and 12666 deletions
+11 -7
View File
@@ -39,27 +39,31 @@ vi.mock('../../src/config', () => ({
JWT_SECRET: 'test-jwt-secret-for-trek-testing-only',
ENCRYPTION_KEY: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2',
updateJwtSecret: () => {},
DEFAULT_LANGUAGE: 'en',
}));
import { createApp } from '../../src/app';
import type { INestApplication } from '@nestjs/common';
import { buildApp } from '../../src/bootstrap';
import { createTables } from '../../src/db/schema';
import { runMigrations } from '../../src/db/migrations';
import { resetTestDb } from '../helpers/test-db';
import { resetTestDb, resetRateLimits } from '../helpers/test-db';
import { createUser, createTrip } from '../helpers/factories';
import { authCookie } from '../helpers/auth';
import { loginAttempts, mfaAttempts } from '../../src/routes/auth';
import { setupWebSocket } from '../../src/websocket';
import { createEphemeralToken } from '../../src/services/ephemeralTokens';
let server: http.Server;
let wsUrl: string;
let nestApp: INestApplication;
beforeAll(async () => {
createTables(testDb);
runMigrations(testDb);
const app = createApp();
server = http.createServer(app);
// Real WebSocket against the unified NestJS app (Express is gone). buildApp owns
// the same composition production uses; we attach the real ws server to it.
nestApp = await buildApp();
server = http.createServer(nestApp.getHttpAdapter().getInstance());
setupWebSocket(server);
await new Promise<void>(resolve => server.listen(0, resolve));
@@ -71,13 +75,13 @@ afterAll(async () => {
await new Promise<void>((resolve, reject) =>
server.close(err => err ? reject(err) : resolve())
);
await nestApp.close();
testDb.close();
});
beforeEach(() => {
resetTestDb(testDb);
loginAttempts.clear();
mfaAttempts.clear();
resetRateLimits(nestApp);
});
/** Buffered WebSocket wrapper that never drops messages. */