From d53c08d7a0c70552c0e409ec864bfde527558f31 Mon Sep 17 00:00:00 2001 From: JSC Date: Sat, 9 Aug 2025 14:43:09 +0200 Subject: [PATCH] feat: add environment configuration files and update API base URL handling for production --- .env.development.template | 2 ++ .env.production.template | 3 +++ package.json | 1 + src/components/ThemeProvider.tsx | 11 ++++++++++- src/components/player/Player.tsx | 4 ++-- src/contexts/SocketContext.tsx | 7 ++++++- src/lib/api/client.ts | 12 ++++++++++-- src/lib/api/config.ts | 15 ++++++++++++++- src/lib/api/services/files.ts | 1 - vite.config.ts | 7 +++++++ 10 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 .env.development.template create mode 100644 .env.production.template diff --git a/.env.development.template b/.env.development.template new file mode 100644 index 0000000..95ae960 --- /dev/null +++ b/.env.development.template @@ -0,0 +1,2 @@ +# Development Environment Variables +VITE_API_BASE_URL=http://localhost:8000 \ No newline at end of file diff --git a/.env.production.template b/.env.production.template new file mode 100644 index 0000000..fd4d01e --- /dev/null +++ b/.env.production.template @@ -0,0 +1,3 @@ +# Production Environment Variables +# In production with reverse proxy, we use relative URLs (same origin) +# No API base URL needed - the reverse proxy handles /api routing \ No newline at end of file diff --git a/package.json b/package.json index cd49257..379d343 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite --port 8001", "build": "tsc -b && vite build", + "build:production": "tsc -b && vite build --mode production", "lint": "eslint .", "preview": "vite preview" }, diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx index 55f78dd..80717fc 100644 --- a/src/components/ThemeProvider.tsx +++ b/src/components/ThemeProvider.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useContext, useEffect, useState } from 'react' import { ThemeProviderContext, type Theme } from '@/contexts/ThemeContext' type ThemeProviderProps = { @@ -49,3 +49,12 @@ export function ThemeProvider({ ) } + +export const useTheme = () => { + const context = useContext(ThemeProviderContext) + + if (context === undefined) + throw new Error('useTheme must be used within a ThemeProvider') + + return context +} diff --git a/src/components/player/Player.tsx b/src/components/player/Player.tsx index c97fedb..e4bf56e 100644 --- a/src/components/player/Player.tsx +++ b/src/components/player/Player.tsx @@ -26,7 +26,7 @@ import { ArrowRight, ArrowRightToLine } from 'lucide-react' -import { playerService, type PlayerState, type PlayerMode } from '@/lib/api/services/player' +import { playerService, type PlayerState, type PlayerMode, type MessageResponse } from '@/lib/api/services/player' import { filesService } from '@/lib/api/services/files' import { playerEvents, PLAYER_EVENTS } from '@/lib/events' import { toast } from 'sonner' @@ -79,7 +79,7 @@ export function Player({ className }: PlayerProps) { } }, []) - const executeAction = useCallback(async (action: () => Promise, actionName: string) => { + const executeAction = useCallback(async (action: () => Promise, actionName: string) => { setIsLoading(true) try { await action() diff --git a/src/contexts/SocketContext.tsx b/src/contexts/SocketContext.tsx index c0f899c..fd1017c 100644 --- a/src/contexts/SocketContext.tsx +++ b/src/contexts/SocketContext.tsx @@ -27,7 +27,12 @@ export function SocketProvider({ children }: SocketProviderProps) { const createSocket = useCallback(() => { if (!user) return null - const newSocket = io('http://localhost:8000', { + // Get socket URL - use relative URL in production with reverse proxy + const socketUrl = import.meta.env.PROD + ? '' // Use relative URL in production (same origin as frontend) + : (import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000') + + const newSocket = io(socketUrl, { withCredentials: true, transports: ['polling', 'websocket'], timeout: 20000, diff --git a/src/lib/api/client.ts b/src/lib/api/client.ts index 0808717..9bf84f9 100644 --- a/src/lib/api/client.ts +++ b/src/lib/api/client.ts @@ -12,7 +12,15 @@ export class BaseApiClient implements ApiClient { } private buildURL(endpoint: string, params?: Record): string { - const url = new URL(endpoint, this.baseURL) + let url: URL + + if (this.baseURL) { + // Full base URL provided + url = new URL(endpoint, this.baseURL) + } else { + // Use relative URL (for reverse proxy) + url = new URL(endpoint, window.location.origin) + } if (params) { Object.entries(params).forEach(([key, value]) => { @@ -22,7 +30,7 @@ export class BaseApiClient implements ApiClient { }) } - return url.toString() + return this.baseURL ? url.toString() : url.pathname + url.search } private async request( diff --git a/src/lib/api/config.ts b/src/lib/api/config.ts index 7a2bde0..d7b974f 100644 --- a/src/lib/api/config.ts +++ b/src/lib/api/config.ts @@ -1,6 +1,19 @@ // API Configuration +const getApiBaseUrl = () => { + // If VITE_API_BASE_URL is explicitly set to empty string, use relative URLs + if (import.meta.env.VITE_API_BASE_URL === '') { + return '' // Use relative URLs (reverse proxy handles routing) + } + // In production with reverse proxy, use relative URL (same origin) + if (import.meta.env.PROD) { + return '' // Use relative URLs in production (reverse proxy handles routing) + } + // Use environment variable in development, fallback to localhost + return import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000' +} + export const API_CONFIG = { - BASE_URL: 'http://localhost:8000', + BASE_URL: getApiBaseUrl(), TIMEOUT: 30000, // 30 seconds RETRY_ATTEMPTS: 1, diff --git a/src/lib/api/services/files.ts b/src/lib/api/services/files.ts index 6834ebe..9e68c17 100644 --- a/src/lib/api/services/files.ts +++ b/src/lib/api/services/files.ts @@ -1,4 +1,3 @@ -import { apiClient } from '../client' import { API_CONFIG } from '../config' export class FilesService { diff --git a/vite.config.ts b/vite.config.ts index 6a7f1ee..48bfd45 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,4 +11,11 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, + // Production build optimization + build: { + outDir: 'dist', + sourcemap: false, // Disable source maps in production for security + }, + // For reverse proxy deployment, ensure assets are served from root + base: '/', })