Skip to content

Commit 177a624

Browse files
committed
Support runtime configuration to support k8s deployments
1 parent 17c940a commit 177a624

23 files changed

Lines changed: 105 additions & 21 deletions

truecover-app/Dockerfile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ COPY --from=build /app/build /usr/share/nginx/html
3737
# Copy nginx configuration
3838
COPY nginx.conf /etc/nginx/conf.d/default.conf
3939

40+
# Copy entrypoint script for runtime config injection
41+
COPY docker-entrypoint.sh /docker-entrypoint.sh
42+
RUN chmod +x /docker-entrypoint.sh
43+
4044
EXPOSE 80
4145

42-
CMD ["nginx", "-g", "daemon off;"]
46+
# Use entrypoint to generate env-config.js at container startup
47+
ENTRYPOINT ["/docker-entrypoint.sh"]

truecover-app/docker-entrypoint.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/sh
2+
# Docker entrypoint script for runtime environment configuration
3+
# Generates env-config.js with environment variables at container startup
4+
5+
# Create env-config.js with runtime environment variables
6+
cat > /usr/share/nginx/html/env-config.js << EOF
7+
// Runtime environment configuration - generated at container startup
8+
window.__ENV__ = {
9+
VITE_API_URL: "${VITE_API_URL:-}",
10+
VITE_MARTIN_URL: "${VITE_MARTIN_URL:-}",
11+
VITE_MAPBOX_TOKEN: "${VITE_MAPBOX_TOKEN:-}",
12+
VITE_CLERK_PUBLISHABLE_KEY: "${VITE_CLERK_PUBLISHABLE_KEY:-}"
13+
};
14+
EOF
15+
16+
echo "Generated /usr/share/nginx/html/env-config.js with runtime config"
17+
18+
# Start nginx
19+
exec nginx -g 'daemon off;'

truecover-app/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
<link rel="apple-touch-icon" href="/logo192.png" />
1313
<link rel="manifest" href="/manifest.json" />
1414
<title>True Cover</title>
15+
<!-- Runtime environment config - generated by docker-entrypoint.sh -->
16+
<script src="/env-config.js"></script>
1517
</head>
1618
<body>
1719
<noscript>You need to enable JavaScript to run this app.</noscript>

truecover-app/src/components/AddVisitModal.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import { useRounds } from '../hooks/useRounds';
66
import { FileData } from '../types';
77
import axios from 'axios';
88
import { useAuth } from '@clerk/clerk-react';
9+
import { env } from '../config/env';
910

10-
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5001';
11+
const API_URL = env.VITE_API_URL;
1112

1213
interface AddVisitModalProps {
1314
isOpen: boolean;

truecover-app/src/components/AuthenticatedApp.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import {
1313
TacticalBadge,
1414
} from '../tactical-ui';
1515
import { useAuth } from '@clerk/clerk-react';
16+
import { env } from '../config/env';
1617

1718
type AppView = 'home' | 'adaptive-sampling' | 'coverage-prediction';
1819

19-
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5000';
20+
const API_URL = env.VITE_API_URL;
2021

2122
function AuthenticatedApp() {
2223
const { getToken } = useAuth();

truecover-app/src/components/CreateRoundModal.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import axios from 'axios';
44
import { useAuth } from '@clerk/clerk-react';
55
import { useIndicators } from '../hooks/useIndicators';
66
import { usePixelMetadataStats } from '../hooks/usePixels';
7+
import { env } from '../config/env';
78

8-
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5001';
9+
const API_URL = env.VITE_API_URL;
910

1011
interface CreateRoundModalProps {
1112
isOpen: boolean;

truecover-app/src/components/MapView.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { useAuth } from '@clerk/clerk-react';
1313
import { adminBoundariesApi, pixelsApi } from '../services/api';
1414
import { useGeneratePixels } from '../hooks/usePixels';
1515
import { tacticalToast } from '../tactical-ui';
16+
import { env } from '../config/env';
1617

1718
interface CampaignArea {
1819
id: string;
@@ -85,7 +86,7 @@ const getCentroid = (geometry: any): [number, number] => {
8586
return [sum[0] / coords.length, sum[1] / coords.length];
8687
};
8788

88-
const MARTIN_URL = import.meta.env.VITE_MARTIN_URL || 'http://localhost:3052';
89+
const MARTIN_URL = env.VITE_MARTIN_URL;
8990

9091
const MapView: React.FC<MapViewProps> = ({ data, selectedData, locations, mode = 'sampling', highlightRounds = [], showSampled = true, onToggleSampled, interpolationMode = 'none', selectedMetadataField = '', metadataVisualizationMode = 'fill', showPixels = false, onTogglePixels, pixelsBounds, onBoundsChange, campaignId, indicatorId, pixelVersion, pixelCount = 0, onGeneratePixels, histogramBrushRanges = null, histogramDataType = 'locations', sampledItemsCount = 0, planningMode = false, campaignAreas = [], showCampaignAreas = true, onToggleCampaignAreas, onAddAdminBoundaryToCampaign, selectedAreaBounds, className }) => {
9192
const [popupInfo, setPopupInfo] = useState<any>(null);
@@ -116,7 +117,7 @@ const MapView: React.FC<MapViewProps> = ({ data, selectedData, locations, mode =
116117
const [drawnFeature, setDrawnFeature] = useState<any>(null);
117118
const drawControlRef = useRef<MapboxDraw | null>(null);
118119
const geocoderInputRef = useRef<HTMLDivElement>(null);
119-
const mapboxToken = import.meta.env.VITE_MAPBOX_TOKEN;
120+
const mapboxToken = env.VITE_MAPBOX_TOKEN;
120121

121122
const { getToken } = useAuth();
122123
const generatePixelsMutation = useGeneratePixels();

truecover-app/src/components/RoundsManager.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import { StratifiedClusterSamplingWizard } from './StratifiedClusterSamplingWiza
66
import axios from 'axios';
77
import { useAuth } from '@clerk/clerk-react';
88
import { useIndicators } from '../hooks/useIndicators';
9+
import { env } from '../config/env';
910

10-
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5001';
11+
const API_URL = env.VITE_API_URL;
1112

1213
interface Round {
1314
id: string;

truecover-app/src/components/StratifiedClusterSamplingWizard.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import {
1414
import { DraggableAreaCard } from './DraggableAreaCard';
1515
import { CategoryColumn } from './CategoryColumn';
1616
import { useDivisions, useDistricts, useAdminBoundaryChildren } from '../hooks/useAdminBoundaries';
17+
import { env } from '../config/env';
1718

18-
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:5001';
19+
const API_URL = env.VITE_API_URL;
1920

2021
interface WorkflowProgress {
2122
status: string;

truecover-app/src/config/env.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Runtime environment configuration with fallback to build-time values
2+
// This allows environment variables to be injected at container startup
3+
// via env-config.js, while still working in development with Vite's import.meta.env
4+
5+
declare global {
6+
interface Window {
7+
__ENV__?: {
8+
VITE_API_URL?: string;
9+
VITE_MARTIN_URL?: string;
10+
VITE_MAPBOX_TOKEN?: string;
11+
VITE_CLERK_PUBLISHABLE_KEY?: string;
12+
};
13+
}
14+
}
15+
16+
// Helper to get env value with runtime override support
17+
const getEnv = (key: string, fallback: string = ''): string => {
18+
// First check runtime config (injected by docker-entrypoint.sh)
19+
const runtimeValue = window.__ENV__?.[key as keyof typeof window.__ENV__];
20+
if (runtimeValue) {
21+
return runtimeValue;
22+
}
23+
24+
// Fall back to build-time Vite env
25+
const buildValue = import.meta.env[key];
26+
if (buildValue) {
27+
return buildValue;
28+
}
29+
30+
// Return fallback
31+
return fallback;
32+
};
33+
34+
export const env = {
35+
VITE_API_URL: getEnv('VITE_API_URL', 'http://localhost:5001'),
36+
VITE_MARTIN_URL: getEnv('VITE_MARTIN_URL', 'http://localhost:3052'),
37+
VITE_MAPBOX_TOKEN: getEnv('VITE_MAPBOX_TOKEN', ''),
38+
VITE_CLERK_PUBLISHABLE_KEY: getEnv('VITE_CLERK_PUBLISHABLE_KEY', ''),
39+
};
40+
41+
export default env;

0 commit comments

Comments
 (0)