/login: Formular gegen AuthProvider (Access-Token im Memory, Refresh per httpOnly-Cookie), Fehlermeldung bei falschen Zugangsdaten. (app)/layout.tsx: geschuetztes Layout, leitet ohne Session zu /login um, Header mit Nutzer-E-Mail + Abmelden. settings/layout.tsx: zusaetzliches Admin-Gate fuer /settings/users. /settings/users: Liste, Anlegen-Dialog (shadcn/Base UI Dialog+Select), Deaktivieren - alles ueber TanStack Query gegen die Users-API. proxy.ts (Next 16 Nachfolger von middleware.ts): optimistische Cookie-Praesenz-Pruefung fuer /login und /settings/*, echte Autorisierung bleibt serverseitig (RolesGuard). Backend-Fixes waehrend Browser-Verifikation gefunden und behoben: - CORS fehlte komplett (app.enableCors, FRONTEND_ORIGIN) - jeder Cross-Origin-Request von :3000 nach :3001 wurde geblockt. - React-StrictMode-Doppel-Effect liess zwei parallele Refresh-Aufrufe laufen; da Refresh-Tokens rotierend/einmalig sind, gewann einer und der andere bekam 401 -> Nutzer flog nach jedem Reload aus der Session. Fix: AbortController im Session-Restore-Effect. - Manifest verwies auf nicht existente PNG-Icons (404); durch public/icon.svg ersetzt. 7 Vitest-Tests (Login-Formular, Users-Liste, JWT-Decode), Lint, Typecheck und Build gruen. Kompletter Flow (Login, Reload-Persistenz, Benutzer anlegen/deaktivieren, Abmelden, Redirect ohne Session) live im Browser gegen den echten Server verifiziert.
35 lines
794 B
TypeScript
35 lines
794 B
TypeScript
import {
|
|
INestApplication,
|
|
RequestMethod,
|
|
ValidationPipe,
|
|
VersioningType,
|
|
} from '@nestjs/common';
|
|
import helmet from 'helmet';
|
|
import cookieParser from 'cookie-parser';
|
|
|
|
export function configureApp(app: INestApplication): void {
|
|
app.use(helmet());
|
|
app.use(cookieParser());
|
|
|
|
app.enableCors({
|
|
origin: process.env.FRONTEND_ORIGIN ?? 'http://localhost:3000',
|
|
credentials: true,
|
|
});
|
|
|
|
app.setGlobalPrefix('api', {
|
|
exclude: [
|
|
{ path: 'health', method: RequestMethod.GET },
|
|
{ path: 'health/ready', method: RequestMethod.GET },
|
|
],
|
|
});
|
|
app.enableVersioning({ type: VersioningType.URI, defaultVersion: '1' });
|
|
|
|
app.useGlobalPipes(
|
|
new ValidationPipe({
|
|
whitelist: true,
|
|
forbidNonWhitelisted: true,
|
|
transform: true,
|
|
}),
|
|
);
|
|
}
|