Make API URL dynamic based on hostname
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled

- localhost/127.0.0.1 → http://localhost:8000/api/v1
- Local network IPs → http://{hostname}:8000/api/v1
- Production domains → https://{hostname}/api/v1 (requires reverse proxy)
This commit is contained in:
2025-12-09 08:09:46 +01:00
parent ca5f2739db
commit db8cc399ef

View File

@ -1,20 +1,44 @@
/**
* API client for pounce backend
*
* API URL is determined dynamically based on the current hostname:
* - localhost/127.0.0.1 → http://localhost:8000/api/v1
* - Local network IPs (10.x, 192.168.x) → http://{hostname}:8000/api/v1
* - Production (any other domain) → https://{hostname}/api/v1 (requires reverse proxy)
*/
// Ensure API_BASE ends with /api/v1 and no trailing slash
const getApiBase = () => {
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'
// Remove trailing slash if present
const cleanBase = baseUrl.replace(/\/$/, '')
// Add /api/v1 if not present
if (cleanBase.endsWith('/api/v1')) {
return cleanBase
}
return `${cleanBase}/api/v1`
const getApiBase = (): string => {
// Server-side rendering: use environment variable or localhost
if (typeof window === 'undefined') {
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api/v1'
return baseUrl.replace(/\/$/, '')
}
const API_BASE = getApiBase()
const { protocol, hostname, port } = window.location
// Localhost development
if (hostname === 'localhost' || hostname === '127.0.0.1') {
return 'http://localhost:8000/api/v1'
}
// Local network (10.x.x.x, 192.168.x.x, 172.16-31.x.x)
if (/^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)/.test(hostname)) {
return `http://${hostname}:8000/api/v1`
}
// Production: use same protocol and domain with /api/v1 path
// This requires a reverse proxy (nginx/caddy) to route /api/v1 to the backend
return `${protocol}//${hostname}/api/v1`
}
// Lazy-evaluated to ensure window is available on client
let _apiBase: string | null = null
const getApiBaseUrl = (): string => {
if (_apiBase === null) {
_apiBase = getApiBase()
}
return _apiBase
}
interface ApiError {
detail: string
@ -44,7 +68,7 @@ class ApiClient {
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const url = `${API_BASE}${endpoint}`
const url = `${getApiBaseUrl()}${endpoint}`
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...options.headers as Record<string, string>,