Skip to content

Commit 5d5a3e6

Browse files
committed
fixed production deduplication
1 parent 0e35cb6 commit 5d5a3e6

3 files changed

Lines changed: 40 additions & 5 deletions

File tree

frontend/src/lib/api.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,36 @@
1-
export const API_BASE_URL = import.meta.env.VITE_BACKEND_URL ?? "http://localhost:3000";
1+
const DEFAULT_API_BASE_URL = "http://localhost:3000";
2+
3+
const normalizeApiBaseUrl = (rawBaseUrl: string): string => {
4+
const baseWithoutTrailingSlash = rawBaseUrl.replace(/\/+$/, "");
5+
6+
try {
7+
const parsed = new URL(baseWithoutTrailingSlash);
8+
const duplicatedHostPrefix = `/${parsed.hostname}`;
9+
10+
// Handle accidental base URLs like http://host/host/api
11+
if (
12+
parsed.pathname === duplicatedHostPrefix ||
13+
parsed.pathname.startsWith(`${duplicatedHostPrefix}/`)
14+
) {
15+
parsed.pathname = parsed.pathname.slice(duplicatedHostPrefix.length) || "/";
16+
}
17+
18+
parsed.pathname = parsed.pathname.replace(/\/+$/, "") || "/";
19+
return `${parsed.origin}${parsed.pathname === "/" ? "" : parsed.pathname}`;
20+
} catch {
21+
return baseWithoutTrailingSlash;
22+
}
23+
};
24+
25+
export const API_BASE_URL = normalizeApiBaseUrl(
26+
import.meta.env.VITE_BACKEND_URL ?? DEFAULT_API_BASE_URL
27+
);
28+
29+
export const buildApiUrl = (endpoint: string): string => {
30+
if (/^https?:\/\//i.test(endpoint)) {
31+
return endpoint;
32+
}
33+
34+
const normalizedEndpoint = endpoint.replace(/^\/+/, "");
35+
return new URL(normalizedEndpoint, `${API_BASE_URL}/`).toString();
36+
};

frontend/src/lib/fetcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// src/lib/fetcher.ts
22

33
import { useAuth } from "@clerk/react";
4-
import { API_BASE_URL } from "./api";
4+
import { buildApiUrl } from "./api";
55

66
export const useApi = () => {
77
const { getToken } = useAuth();
@@ -12,7 +12,7 @@ export const useApi = () => {
1212
) => {
1313
const token = await getToken();
1414

15-
const res = await fetch(`${API_BASE_URL}${endpoint}`, {
15+
const res = await fetch(buildApiUrl(endpoint), {
1616
...options,
1717
headers: {
1818
...(options.headers || {}),

frontend/src/pages/DashboardPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useUser, useClerk, useAuth } from '@clerk/react'
22
import { useState } from 'react'
3+
import { buildApiUrl } from '../lib/api'
34

45
export function DashboardPage() {
56
const { user } = useUser()
@@ -15,8 +16,7 @@ export function DashboardPage() {
1516
try {
1617
const authToken = await getToken()
1718

18-
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:3000"
19-
const res = await fetch(`${backendUrl}/preferences`, {
19+
const res = await fetch(buildApiUrl('/preferences'), {
2020
headers: {
2121
'Authorization': `Bearer ${authToken}`,
2222
'Content-Type': 'application/json'

0 commit comments

Comments
 (0)