fix(api): add global /api prefix to resolve frontend route mismatch
Some checks failed
ci/woodpecker/push/api Pipeline failed
Some checks failed
ci/woodpecker/push/api Pipeline failed
Frontend API client prefixes all paths with /api/ (e.g. /api/projects,
/api/knowledge/entries) but backend controllers had no global prefix,
causing 404s on all data-fetching pages in production.
- Add setGlobalPrefix('api') in main.ts with exclusions for /health
(Docker healthcheck) and /auth/* (BetterAuth OAuth flow)
- Strip redundant 'api/' from federation and CSRF controller paths
that already included the prefix manually
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ interface AuthenticatedRequest extends Request {
|
||||
user?: AuthenticatedUser;
|
||||
}
|
||||
|
||||
@Controller("api/v1/csrf")
|
||||
@Controller("v1/csrf")
|
||||
export class CsrfController {
|
||||
constructor(private readonly csrfService: CsrfService) {}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { AuthenticatedRequest } from "../common/types/user.types";
|
||||
import type { CommandMessageDetails, CommandResponse } from "./types/message.types";
|
||||
import type { FederationMessageStatus } from "@prisma/client";
|
||||
|
||||
@Controller("api/v1/federation")
|
||||
@Controller("v1/federation")
|
||||
export class CommandController {
|
||||
private readonly logger = new Logger(CommandController.name);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
IncomingEventAckDto,
|
||||
} from "./dto/event.dto";
|
||||
|
||||
@Controller("api/v1/federation")
|
||||
@Controller("v1/federation")
|
||||
export class EventController {
|
||||
private readonly logger = new Logger(EventController.name);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
ValidateFederatedTokenDto,
|
||||
} from "./dto/federated-auth.dto";
|
||||
|
||||
@Controller("api/v1/federation/auth")
|
||||
@Controller("v1/federation/auth")
|
||||
export class FederationAuthController {
|
||||
private readonly logger = new Logger(FederationAuthController.name);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
} from "./dto/connection.dto";
|
||||
import { FederationConnectionStatus } from "@prisma/client";
|
||||
|
||||
@Controller("api/v1/federation")
|
||||
@Controller("v1/federation")
|
||||
export class FederationController {
|
||||
private readonly logger = new Logger(FederationController.name);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { AuthenticatedRequest } from "../common/types/user.types";
|
||||
import type { QueryMessageDetails, QueryResponse } from "./types/message.types";
|
||||
import type { FederationMessageStatus } from "@prisma/client";
|
||||
|
||||
@Controller("api/v1/federation")
|
||||
@Controller("v1/federation")
|
||||
export class QueryController {
|
||||
private readonly logger = new Logger(QueryController.name);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { ValidationPipe } from "@nestjs/common";
|
||||
import { RequestMethod, ValidationPipe } from "@nestjs/common";
|
||||
import cookieParser from "cookie-parser";
|
||||
import { AppModule } from "./app.module";
|
||||
import { getTrustedOrigins } from "./auth/auth.config";
|
||||
@@ -47,6 +47,16 @@ async function bootstrap() {
|
||||
|
||||
app.useGlobalFilters(new GlobalExceptionFilter());
|
||||
|
||||
// Set global API prefix — all routes get /api/* except auth and health
|
||||
// Auth routes are excluded because BetterAuth expects /auth/* paths
|
||||
// Health is excluded because Docker healthchecks hit /health directly
|
||||
app.setGlobalPrefix("api", {
|
||||
exclude: [
|
||||
{ path: "health", method: RequestMethod.GET },
|
||||
{ path: "auth/(.*)", method: RequestMethod.ALL },
|
||||
],
|
||||
});
|
||||
|
||||
// Configure CORS for cookie-based authentication
|
||||
// Origin list is shared with BetterAuth trustedOrigins via getTrustedOrigins()
|
||||
const trustedOrigins = getTrustedOrigins();
|
||||
|
||||
Reference in New Issue
Block a user