refactor(web): replace BACKEND_URL with buildBackendUrl for dynamic URL construction

This commit is contained in:
Anish Sarkar 2026-06-16 04:56:23 +05:30
parent 66659ee8d3
commit 371ff866c7
9 changed files with 138 additions and 79 deletions

View file

@ -1,5 +1,5 @@
import type { ZodType } from "zod";
import { BACKEND_URL } from "@/lib/env-config";
import { buildBackendUrl } from "@/lib/env-config";
import { getClientPlatform } from "../agent-filesystem";
import { getBearerToken, handleUnauthorized, refreshAccessToken } from "../auth-utils";
import {
@ -31,8 +31,6 @@ export type RequestOptions = {
};
class BaseApiService {
baseUrl: string;
noAuthEndpoints: string[] = ["/auth/jwt/login", "/auth/register", "/auth/refresh"];
// Prefixes that don't require auth (checked with startsWith)
@ -44,12 +42,9 @@ class BaseApiService {
return typeof window !== "undefined" ? getBearerToken() || "" : "";
}
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
// Keep for backward compatibility, but token is now always read from localStorage
setBearerToken(_bearerToken: string) {
void _bearerToken;
// No-op: token is now always read fresh from localStorage via the getter
}
@ -102,8 +97,7 @@ class BaseApiService {
throw new AuthenticationError("You are not authenticated. Please login again.");
}
// Construct the full URL. Empty baseUrl is valid for same-origin proxy mode.
const fullUrl = this.baseUrl ? new URL(url, this.baseUrl).toString() : url;
const fullUrl = buildBackendUrl(url);
// Prepare fetch options
const fetchOptions: RequestInit = {
@ -379,7 +373,8 @@ class BaseApiService {
options?: Omit<RequestOptions, "method" | "responseType" | "body"> & { body: FormData }
) {
// Remove Content-Type from options headers if present
const { "Content-Type": _, ...headersWithoutContentType } = options?.headers ?? {};
const headersWithoutContentType = { ...(options?.headers ?? {}) };
delete headersWithoutContentType["Content-Type"];
return this.request(url, responseSchema, {
method: "POST",
@ -394,4 +389,4 @@ class BaseApiService {
}
}
export const baseApiService = new BaseApiService(BACKEND_URL);
export const baseApiService = new BaseApiService();

View file

@ -16,6 +16,44 @@ export const AUTH_TYPE = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE || "G
// same-origin relative requests (e.g. /api/v1/... and /auth/...).
export const BACKEND_URL = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL ?? "";
type BackendUrlParam = string | number | boolean | null | undefined;
/**
* Build browser-facing backend URLs without breaking proxy mode.
*
* In proxy mode BACKEND_URL intentionally stays empty, so callers must keep
* same-origin relative URLs ("/api/v1/...") and let Caddy route them. When
* BACKEND_URL is explicitly configured, the same path resolves against that
* absolute backend origin.
*/
export function buildBackendUrl(
path: string,
params?: Record<string, BackendUrlParam>
): string {
const backendPath = path.startsWith("/") ? path : `/${path}`;
const queryParams = new URLSearchParams();
if (params) {
for (const [key, value] of Object.entries(params)) {
if (value !== null && value !== undefined) {
queryParams.append(key, String(value));
}
}
}
if (BACKEND_URL) {
const url = new URL(backendPath, BACKEND_URL);
for (const [key, value] of queryParams) {
url.searchParams.append(key, value);
}
return url.toString();
}
const queryString = queryParams.toString();
if (!queryString) return backendPath;
return `${backendPath}${backendPath.includes("?") ? "&" : "?"}${queryString}`;
}
// Server-side backend URL. Relative browser URLs do not work from RSC/API route
// code, so server callers should use Docker DNS or an explicit public backend.
export const SERVER_BACKEND_URL =