feat: implement unified deployment with Docker and Railway integration

This commit introduces a comprehensive deployment strategy that unifies the frontend and backend into a single Docker container served by the Rust backend, streamlining the deployment process and improving production architecture.

Key changes:
- Split CI/CD workflows: separated build.yaml (for CI/PR checks) and deploy.yaml (for production deployment)
- Implemented unified Docker deployment where the Axum server serves both API routes (under /api) and frontend static files
- Added GitHub Container Registry integration for Docker image distribution
- Updated Railway configuration to use the new healthcheck path (/api/health)
- Enhanced postgres.ts script with named volumes and constants for better container management
- Added API client utilities (web/lib/api.ts) and environment configuration (web/.env.example) for frontend-backend communication
- Configured Vite proxy for local development while supporting same-origin requests in production
- Updated Dockerfile to include frontend static files and proper environment variable handling

This architecture eliminates the need for separate deployments and CORS configuration, as the frontend and API are served from the same origin.
This commit is contained in:
Ryan Walters
2025-11-02 19:31:22 -06:00
parent 4002729ef7
commit 45e6131121
11 changed files with 276 additions and 116 deletions

11
web/.env.example Normal file
View File

@@ -0,0 +1,11 @@
# Frontend Environment Variables
# API URL (for production builds)
# In production with unified deployment, this should be "/api" (same-origin)
# For local development, this is handled by the Vite proxy
VITE_API_URL=/api
# API Proxy Target (for local development only)
# Point this to your local backend server
# Default: http://localhost:3001 (backend runs on 3001, frontend on 3000)
VITE_API_TARGET=http://localhost:3001

13
web/lib/api.ts Normal file
View File

@@ -0,0 +1,13 @@
// Get API base URL from environment variable, or default to /api for same-origin requests
export const API_BASE_URL = import.meta.env.VITE_API_URL || "/api";
/**
* Helper function to construct full API URLs
* @param path - API endpoint path (without leading slash, e.g., "leaderboard/global")
* @returns Full API URL
*/
export function getApiUrl(path: string): string {
// Remove leading slash if present to avoid double slashes
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
return `${API_BASE_URL}/${cleanPath}`;
}

View File

@@ -8,4 +8,14 @@ export default defineConfig({
build: {
target: "es2022",
},
server: {
// Proxy API requests to the backend server during local development
// In production, both frontend and API are served from the same origin
proxy: {
'/api': {
target: process.env.VITE_API_TARGET || 'http://localhost:3001',
changeOrigin: true,
},
},
},
});