refactor(api): extract shared API prefix URL resolution

This commit is contained in:
Arthur Bassi 2026-06-17 09:34:39 -03:00
parent 503aefa09a
commit b0ce047154
3 changed files with 40 additions and 23 deletions

View file

@ -1,28 +1,12 @@
import ky, { type HTTPError, type KyInstance, type Options } from "ky"; import ky, { type HTTPError, type KyInstance, type Options } from "ky";
import { env } from "@/lib/env"; import { env } from "@/lib/env";
import { resolveApiPrefixUrl } from "@/lib/resolve-api-prefix-url";
import { API_PATHS } from "@/api/api-paths"; import { API_PATHS } from "@/api/api-paths";
import { createApiErrorFromResponse, mapHttpStatusToMessage } from "@/api/api-error"; import { createApiErrorFromResponse, mapHttpStatusToMessage } from "@/api/api-error";
import { queryKeys } from "@/infra/query-key/query-key"; import { queryKeys } from "@/infra/query-key/query-key";
import queryClient from "@/lib/query/query-client"; import queryClient from "@/lib/query/query-client";
import { clearAuth, getAccessToken } from "@/lib/auth/auth-storage"; import { clearAuth, getAccessToken } from "@/lib/auth/auth-storage";
function resolvePrefixUrl(url: string): string {
const trimmed = url.trim() || "/api";
if (trimmed.startsWith("http")) {
return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
}
const path = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
const pathWithSlash = path.endsWith("/") ? path : `${path}/`;
const origin =
typeof window !== "undefined" && window.location?.origin
? window.location.origin
: "http://localhost:3000";
return `${origin}${pathWithSlash}`;
}
function clearSessionAndRedirectToLogin(): void { function clearSessionAndRedirectToLogin(): void {
clearAuth(); clearAuth();
queryClient.setQueryData(queryKeys.auth.session(), null); queryClient.setQueryData(queryKeys.auth.session(), null);
@ -37,7 +21,7 @@ function isAuthLoginRequest(request: Request): boolean {
} }
export const api: KyInstance = ky.create({ export const api: KyInstance = ky.create({
prefixUrl: resolvePrefixUrl(env.apiUrl), prefixUrl: resolveApiPrefixUrl(env.apiUrl),
headers: { headers: {
Accept: "application/json", Accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",

View file

@ -1,5 +1,6 @@
import { API_PATHS } from "@/api/api-paths"; import { API_PATHS } from "@/api/api-paths";
import { env } from "@/lib/env"; import { env } from "@/lib/env";
import { resolveApiBaseUrl } from "@/lib/resolve-api-prefix-url";
import type { import type {
VendorPortalComment, VendorPortalComment,
VendorPortalDispatchDetail, VendorPortalDispatchDetail,
@ -9,11 +10,8 @@ import type {
} from "@/domain/vendor-portal/types/vendor-portal"; } from "@/domain/vendor-portal/types/vendor-portal";
function resolveVendorPortalUrl(path: string): string { function resolveVendorPortalUrl(path: string): string {
const base = env.apiUrl.trim().replace(/\/$/, ""); const base = resolveApiBaseUrl(env.apiUrl);
const prefix = base.startsWith("http") return `${base}/${API_PATHS.vendorPortal.base}${path}`;
? base
: `${typeof window !== "undefined" ? window.location.origin : "http://localhost:3000"}${base.startsWith("/") ? base : `/${base}`}`;
return `${prefix}/${API_PATHS.vendorPortal.base}${path}`;
} }
async function vendorPortalCall<T>( async function vendorPortalCall<T>(

View file

@ -0,0 +1,35 @@
const DEFAULT_ORIGIN = "http://localhost:3000";
function getDefaultOrigin(): string {
if (typeof window !== "undefined" && window.location?.origin) {
return window.location.origin;
}
return DEFAULT_ORIGIN;
}
/**
* Resolves VITE_API_URL to a Ky-compatible prefixUrl (always ends with /).
*
* Contract:
* - Relative paths (e.g. `/api`) are joined with the app origin.
* - Absolute URLs are used as-is; they must already include `/api` for production.
* - API_PATHS do not include `/api` — only this prefix supplies it.
*/
export function resolveApiPrefixUrl(url: string, origin = getDefaultOrigin()): string {
const trimmed = url.trim() || "/api";
if (trimmed.startsWith("http")) {
return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
}
const path = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
const pathWithSlash = path.endsWith("/") ? path : `${path}/`;
return `${origin}${pathWithSlash}`;
}
/** API base without trailing slash (for fetch URLs outside Ky). */
export function resolveApiBaseUrl(url: string, origin = getDefaultOrigin()): string {
return resolveApiPrefixUrl(url, origin).replace(/\/$/, "");
}