From 5bdc37ebcfd0f2c45a8a11fa5617002146178554 Mon Sep 17 00:00:00 2001 From: Jacob Hagstedt Date: Tue, 23 Jun 2026 22:13:38 +0200 Subject: [PATCH] Add base path --- backend/app/main.py | 3 ++- docker-compose.yml | 3 +++ frontend/Dockerfile | 2 ++ frontend/src/App.tsx | 2 +- frontend/src/api/client.ts | 17 +++++++++-------- frontend/vite.config.ts | 2 +- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/backend/app/main.py b/backend/app/main.py index c816b64..e2722f3 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,3 +1,4 @@ +import os from fastapi import FastAPI from contextlib import asynccontextmanager from fastapi.middleware.cors import CORSMiddleware @@ -12,7 +13,7 @@ async def lifespan(app: FastAPI): yield -app = FastAPI(lifespan=lifespan) +app = FastAPI(lifespan=lifespan, root_path=os.getenv("ROOT_PATH", "")) # CORS middleware app.add_middleware( diff --git a/docker-compose.yml b/docker-compose.yml index 55e5001..4884033 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,12 +8,15 @@ services: environment: - DATABASE_URL=sqlite+aiosqlite:///./data/sleep.db - CORS_ORIGINS=http://localhost + - ROOT_PATH=/api/ingress/timer restart: unless-stopped frontend: build: context: ./frontend platforms: [linux/arm64] + args: + - VITE_BASE_PATH=/api/ingress/timer ports: - "8080:80" depends_on: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 5cf23b7..be475fc 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -2,8 +2,10 @@ FROM node:22-alpine AS build ARG VITE_APP_VERSION=unknown ARG VITE_APP_GITSHA=unknown +ARG VITE_BASE_PATH ENV VITE_APP_VERSION=$VITE_APP_VERSION ENV VITE_APP_GITSHA=$VITE_APP_GITSHA +ENV VITE_BASE_PATH=$VITE_BASE_PATH WORKDIR /app # Copy package files first to leverage Docker cache diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7660ac6..9cfcdc6 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -9,7 +9,7 @@ import Footer from './components/Footer'; function App() { return ( - +
diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index bdceba8..16e66c8 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -9,7 +9,8 @@ import type { SummaryResponse } from '@/types'; -const BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; +const API_BASE = import.meta.env.BASE_URL.replace(/\/$/, '') +export const apiUrl = (path: string) => `${API_BASE}${path}` /** * Convert a local HH:MM time string to its UTC equivalent. @@ -33,7 +34,7 @@ export function localToUTC(localValue: string): string { async function fetchStats(endpoint: string, from_date: string, to_date: string, extraParams: Record = {}): Promise { const params = new URLSearchParams({ from_date, to_date, ...extraParams }); - const response = await fetch(`${BASE_URL}/api/stats/${endpoint}?${params.toString()}`); + const response = await fetch(apiUrl(`/api/stats/${endpoint}?${params.toString()}`)); if (!response.ok) throw new Error(`Failed to fetch ${endpoint}`); return response.json(); } @@ -60,19 +61,19 @@ export async function fetchWakeSleepTimes(from_date: string, to_date: string, wa // Session API functions export async function fetchActiveSession(): Promise { - const response = await fetch(`${BASE_URL}/api/sessions/active`); + const response = await fetch(apiUrl('/api/sessions/active')); if (!response.ok) return null; return response.json(); } export async function fetchSessions(): Promise { - const response = await fetch(`${BASE_URL}/api/sessions`); + const response = await fetch(apiUrl('/api/sessions')); if (!response.ok) throw new Error('Failed to fetch sessions'); return response.json(); } export async function createSession(payload: SleepSessionCreate): Promise { - const response = await fetch(`${BASE_URL}/api/sessions`, { + const response = await fetch(apiUrl('/api/sessions'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), @@ -82,7 +83,7 @@ export async function createSession(payload: SleepSessionCreate): Promise { - const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}/end`, { + const response = await fetch(apiUrl(`/api/sessions/${sessionId}/end`), { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), @@ -92,14 +93,14 @@ export async function endSession(sessionId: number, payload: SleepSessionUpdate) } export async function deleteSession(sessionId: number): Promise { - const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}`, { + const response = await fetch(apiUrl(`/api/sessions/${sessionId}`), { method: 'DELETE', }); if (!response.ok) throw new Error('Failed to delete session'); } export async function updateSession(sessionId: number, payload: SleepSessionUpdate): Promise { - const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}`, { + const response = await fetch(apiUrl(`/api/sessions/${sessionId}`), { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index ae39db2..cafe78a 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -19,7 +19,7 @@ if (isDev) { } export default defineConfig({ - base: './', + base: process.env.VITE_BASE_PATH || '/', plugins: [react(), tailwindcss()], define: { __APP_VERSION__: JSON.stringify(APP_VERSION),