Skip to content

Commit eb065c5

Browse files
committed
refactor: remove runtime config generation and implement API for app configuration
- Removed the runtime configuration generation from the startup script, streamlining the application startup process. - Added a new endpoint `/config` in the app router to serve application configuration, fetching values from environment variables. - Updated the frontend to retrieve configuration dynamically from the new API instead of relying on a static runtime config file. - Refactored the PostHog initialization to use the fetched configuration, improving analytics setup.
1 parent 8517627 commit eb065c5

File tree

7 files changed

+100
-46
lines changed

7 files changed

+100
-46
lines changed

scripts/startup.sh

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
11
#!/bin/bash
22
set -e
33

4-
# Create runtime config with environment variables
5-
mkdir -p /app/frontend/dist/assets
6-
cat > /app/frontend/dist/assets/runtime-config.js <<EOL
7-
window.RUNTIME_CONFIG = {
8-
CODER_URL: "${CODER_URL}",
9-
VITE_PUBLIC_POSTHOG_KEY: "${VITE_PUBLIC_POSTHOG_KEY}",
10-
VITE_PUBLIC_POSTHOG_HOST: "${VITE_PUBLIC_POSTHOG_HOST}"
11-
};
12-
EOL
13-
144
# Start the application
155
exec uvicorn main:app --host 0.0.0.0 --port 8000 --workers $API_WORKERS

src/backend/routers/app_router.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,14 @@ async def get_build_info():
2222
# Return a default response if file doesn't exist
2323
print(f"Error reading build-info.json: {str(e)}")
2424
return {"buildHash": "development", "timestamp": int(time.time())}
25+
26+
@app_router.get("/config")
27+
async def get_app_config():
28+
"""
29+
Return runtime configuration for the frontend
30+
"""
31+
return {
32+
"coderUrl": os.getenv("CODER_URL", ""),
33+
"posthogKey": os.getenv("VITE_PUBLIC_POSTHOG_KEY", ""),
34+
"posthogHost": os.getenv("VITE_PUBLIC_POSTHOG_HOST", "")
35+
}

src/frontend/index.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
name="viewport"
77
content="width=device-width, initial-scale=1, shrink-to-fit=no"
88
/>
9-
<meta name="theme-color" content="#000000" />
9+
<meta name="theme-color" content="#untime0" />
1010

1111
<title>Pad.ws</title>
1212
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.png" />
13-
<script src="/assets/runtime-config.js"></script>
1413
</head>
1514

1615
<body>

src/frontend/src/AuthGate.tsx

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useEffect, useRef, useState } from "react";
22
import { useAuthCheck } from "./api/hooks";
3+
import { getAppConfig } from "./api/configService";
34

45
/**
56
* If unauthenticated, it shows the AuthModal as an overlay, but still renders the app behind it.
@@ -19,26 +20,42 @@ export default function AuthGate({ children }: { children: React.ReactNode }) {
1920
useEffect(() => {
2021
// Only run the Coder OIDC priming once per session, after auth is confirmed
2122
if (isAuthenticated === true && !coderAuthDone) {
22-
const iframe = document.createElement("iframe");
23-
iframe.style.display = "none";
24-
// Use runtime config if available, fall back to import.meta.env
25-
const coderUrl = window.RUNTIME_CONFIG?.CODER_URL || import.meta.env.CODER_URL;
26-
iframe.src = `${coderUrl}/api/v2/users/oidc/callback`;
27-
console.debug(`[pad.ws] (Silently) Priming Coder OIDC session for ${coderUrl}`);
23+
const setupIframe = async () => {
24+
try {
25+
// Get config from API
26+
const config = await getAppConfig();
27+
28+
if (!config.coderUrl) {
29+
console.warn('[pad.ws] Coder URL not found, skipping OIDC priming');
30+
setCoderAuthDone(true);
31+
return;
32+
}
33+
34+
const iframe = document.createElement("iframe");
35+
iframe.style.display = "none";
36+
iframe.src = `${config.coderUrl}/api/v2/users/oidc/callback`;
37+
console.debug(`[pad.ws] (Silently) Priming Coder OIDC session for ${config.coderUrl}`);
2838

29-
// Remove iframe as soon as it loads, or after 2s fallback
30-
const cleanup = () => {
31-
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
32-
setCoderAuthDone(true);
33-
};
34-
35-
iframe.onload = cleanup;
36-
document.body.appendChild(iframe);
37-
iframeRef.current = iframe;
39+
// Remove iframe as soon as it loads, or after fallback timeout
40+
const cleanup = () => {
41+
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
42+
setCoderAuthDone(true);
43+
};
3844

39-
// Fallback: remove iframe after 5s if onload doesn't fire
40-
timeoutRef.current = window.setTimeout(cleanup, 5000);
45+
iframe.onload = cleanup;
46+
document.body.appendChild(iframe);
47+
iframeRef.current = iframe;
4148

49+
// Fallback: remove iframe after 5s if onload doesn't fire
50+
timeoutRef.current = window.setTimeout(cleanup, 5000);
51+
} catch (error) {
52+
console.error('[pad.ws] Error setting up Coder OIDC priming:', error);
53+
setCoderAuthDone(true);
54+
}
55+
};
56+
57+
setupIframe();
58+
4259
// Cleanup on unmount or re-run
4360
return () => {
4461
if (iframeRef.current && iframeRef.current.parentNode) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { fetchApi } from './apiUtils';
2+
3+
/**
4+
* Application configuration interface
5+
*/
6+
export interface AppConfig {
7+
coderUrl: string;
8+
posthogKey: string;
9+
posthogHost: string;
10+
}
11+
12+
// Cache the config to avoid unnecessary API calls
13+
let cachedConfig: AppConfig | null = null;
14+
15+
/**
16+
* Get the application configuration from the API
17+
* @returns The application configuration
18+
*/
19+
export async function getAppConfig(): Promise<AppConfig> {
20+
// Return cached config if available
21+
if (cachedConfig) {
22+
return cachedConfig;
23+
}
24+
25+
try {
26+
// Fetch config from API
27+
const config = await fetchApi('/api/app/config');
28+
cachedConfig = config;
29+
return config;
30+
} catch (error) {
31+
console.error('[pad.ws] Failed to load application configuration:', error);
32+
// Return default values as fallback
33+
return {
34+
coderUrl: '',
35+
posthogKey: '',
36+
posthogHost: ''
37+
};
38+
}
39+
}

src/frontend/src/global.d.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
11
interface Window {
2-
RUNTIME_CONFIG?: {
3-
CODER_URL: string;
4-
VITE_PUBLIC_POSTHOG_KEY: string;
5-
VITE_PUBLIC_POSTHOG_HOST: string;
6-
};
72
ExcalidrawLib: any;
83
}

src/frontend/src/utils/posthog.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import posthog from 'posthog-js';
2+
import { getAppConfig } from '../api/configService';
23

3-
const posthogKey = window.RUNTIME_CONFIG?.VITE_PUBLIC_POSTHOG_KEY || import.meta.env.VITE_PUBLIC_POSTHOG_KEY;
4-
const posthogHost = window.RUNTIME_CONFIG?.VITE_PUBLIC_POSTHOG_HOST || import.meta.env.VITE_PUBLIC_POSTHOG_HOST;
4+
// Initialize PostHog with empty values first
5+
posthog.init('', { api_host: '' });
56

6-
// Initialize PostHog
7-
if (posthogKey) {
8-
posthog.init(posthogKey, {
9-
api_host: posthogHost,
10-
});
11-
console.debug('[pad.ws] PostHog initialized successfully');
12-
} else {
13-
console.warn('[pad.ws] PostHog API key not found. Analytics will not be tracked.');
14-
}
7+
// Then update with real values when config is loaded
8+
getAppConfig().then(config => {
9+
if (config.posthogKey) {
10+
posthog.init(config.posthogKey, {
11+
api_host: config.posthogHost,
12+
});
13+
console.debug('[pad.ws] PostHog initialized successfully');
14+
} else {
15+
console.warn('[pad.ws] PostHog API key not found. Analytics will not be tracked.');
16+
}
17+
});
1518

1619
// Helper function to track custom events
1720
export const capture = (eventName: string, properties?: Record<string, any>) => {
1821
posthog.capture(eventName, properties);
1922
};
2023

2124
// Export PostHog instance for direct use
22-
export default posthog;
25+
export default posthog;

0 commit comments

Comments
 (0)