Skip to content

Commit 01b4d09

Browse files
authored
Merge pull request #133 from IABTechLab/sas-UID2-6299-docker-images2
environment variables for react apps
2 parents a728be4 + c3f13b0 commit 01b4d09

File tree

8 files changed

+257
-38
lines changed

8 files changed

+257
-38
lines changed

web-integrations/google-secure-signals/react-client-side/package-lock.json

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web-integrations/google-secure-signals/react-client-side/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
},
1919
"devDependencies": {
2020
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
21+
"cross-env": "^7.0.3",
2122
"dotenv-cli": "^10.0.0"
2223
},
2324
"overrides": {
@@ -28,7 +29,7 @@
2829
},
2930
"scripts": {
3031
"start": "node server.js",
31-
"dev": "PORT=3044 dotenv -e ../../../.env -- react-scripts start",
32+
"dev": "cross-env PORT=3044 dotenv -e ../../../.env -- react-scripts start",
3233
"build": "react-scripts build",
3334
"test": "react-scripts test",
3435
"eject": "react-scripts eject"

web-integrations/google-secure-signals/react-client-side/server.js

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
require('dotenv').config({ path: '../../../.env' });
2-
3-
console.log('process.env', process.env);
1+
// Load environment variables from .env file (for local development)
2+
// In Docker, env_file in docker-compose.yml already provides these, so this is optional
3+
try {
4+
require('dotenv').config({ path: '../../../.env' });
5+
} catch (error) {
6+
// Silently fail if .env file doesn't exist (e.g., in Docker where env vars are provided via env_file)
7+
// This is expected behavior when running in Docker
8+
}
49

510
const fs = require('fs');
611
const path = require('path');
@@ -21,9 +26,19 @@ app.get('/ops/healthcheck', (req, res) => {
2126
// Helper function to serve index.html with environment variable replacement
2227
function serveIndexHtml(req, res) {
2328
// Try build directory first (production), then public (development)
24-
let indexPath = path.join(__dirname, 'build', 'index.html');
25-
if (!fs.existsSync(indexPath)) {
26-
indexPath = path.join(__dirname, 'public', 'index.html');
29+
const buildPath = path.join(__dirname, 'build');
30+
const buildIndexPath = path.join(buildPath, 'index.html');
31+
const publicIndexPath = path.join(__dirname, 'public', 'index.html');
32+
33+
let indexPath;
34+
if (fs.existsSync(buildIndexPath)) {
35+
indexPath = buildIndexPath;
36+
} else if (fs.existsSync(publicIndexPath)) {
37+
console.warn('Warning: build directory not found. Serving from public directory. Run "npm run build" to build the React app.');
38+
indexPath = publicIndexPath;
39+
} else {
40+
res.status(500).send('Error: Neither build nor public index.html found. Please run "npm run build" first.');
41+
return;
2742
}
2843

2944
let html = fs.readFileSync(indexPath, 'utf8');
@@ -36,18 +51,40 @@ function serveIndexHtml(req, res) {
3651
html = html.replace(/__UID_JS_SDK_URL_PLACEHOLDER__/g, uidJsSdkUrl);
3752
html = html.replace(/__PUBLIC_URL_PLACEHOLDER__/g, publicUrl);
3853

39-
// Debug: log if replacement happened
54+
// Verify replacement worked - if placeholder still exists, log error
4055
if (html.includes('__UID_JS_SDK_URL_PLACEHOLDER__')) {
41-
console.warn('Warning: Placeholder __UID_JS_SDK_URL_PLACEHOLDER__ was not replaced in', indexPath);
56+
console.error('ERROR: Placeholder __UID_JS_SDK_URL_PLACEHOLDER__ was not replaced in', indexPath);
4257
}
4358

59+
// Inject runtime environment variables as a script tag
60+
// This allows the React app to read environment variables at runtime (for Kubernetes)
61+
const runtimeEnv = {
62+
REACT_APP_UID_JS_SDK_NAME: process.env.REACT_APP_UID_JS_SDK_NAME,
63+
REACT_APP_UID_CLIENT_BASE_URL: process.env.REACT_APP_UID_CLIENT_BASE_URL,
64+
REACT_APP_UID_SECURE_SIGNALS_SDK_URL: process.env.REACT_APP_UID_SECURE_SIGNALS_SDK_URL,
65+
REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY: process.env.REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY,
66+
REACT_APP_IDENTITY_NAME: process.env.REACT_APP_IDENTITY_NAME,
67+
REACT_APP_DOCS_BASE_URL: process.env.REACT_APP_DOCS_BASE_URL,
68+
REACT_APP_UID_CSTG_SUBSCRIPTION_ID: process.env.REACT_APP_UID_CSTG_SUBSCRIPTION_ID,
69+
REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY: process.env.REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY,
70+
};
71+
72+
// Inject script tag before closing </head> tag
73+
const envScript = `<script>window.__REACT_APP_ENV__ = ${JSON.stringify(runtimeEnv)};</script>`;
74+
html = html.replace('</head>', `${envScript}</head>`);
75+
76+
// Set content type explicitly
77+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
4478
res.send(html);
4579
}
4680

47-
// Route handler for index - must be before static middleware
81+
// Route handlers for index - must be before static middleware
82+
// These must be registered BEFORE express.static() to ensure they intercept index.html requests
4883
app.get('/', serveIndexHtml);
84+
app.get('/index.html', serveIndexHtml);
4985

5086
// Serve static files from build directory (production) or public (development)
87+
// IMPORTANT: This must come AFTER the route handlers to ensure index.html is processed by serveIndexHtml
5188
const buildPath = path.join(__dirname, 'build');
5289
const publicPath = path.join(__dirname, 'public');
5390

@@ -74,5 +111,14 @@ app.get('*', (req, res, next) => {
74111
});
75112

76113
app.listen(port, () => {
77-
console.log(`Example app listening at http://localhost:${port}`);
114+
const buildPath = path.join(__dirname, 'build');
115+
if (fs.existsSync(buildPath)) {
116+
console.log(`Example app listening at http://localhost:${port}`);
117+
console.log('Serving production build from build/ directory');
118+
} else {
119+
console.log(`Example app listening at http://localhost:${port}`);
120+
console.warn('WARNING: build/ directory not found. Serving from public/ directory.');
121+
console.warn('For production, run "npm run build" first.');
122+
console.warn('For development, use "npm run dev" instead.');
123+
}
78124
});

web-integrations/google-secure-signals/react-client-side/src/SecureSignalsApp.tsx

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,46 @@ declare global {
77
getAdvertisingToken: any;
88
google: any;
99
googletag: any;
10+
__REACT_APP_ENV__?: {
11+
REACT_APP_UID_JS_SDK_NAME?: string;
12+
REACT_APP_UID_CLIENT_BASE_URL?: string;
13+
REACT_APP_UID_SECURE_SIGNALS_SDK_URL?: string;
14+
REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY?: string;
15+
REACT_APP_IDENTITY_NAME?: string;
16+
REACT_APP_DOCS_BASE_URL?: string;
17+
REACT_APP_UID_CSTG_SUBSCRIPTION_ID?: string;
18+
REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY?: string;
19+
};
1020
}
1121
}
1222

1323
// Declare global variables
1424
declare const google: any;
1525

16-
// Environment variables
17-
const UID_JS_SDK_NAME = process.env.REACT_APP_UID_JS_SDK_NAME;
18-
const UID_BASE_URL = process.env.REACT_APP_UID_CLIENT_BASE_URL;
19-
const SECURE_SIGNALS_SDK_URL = process.env.REACT_APP_UID_SECURE_SIGNALS_SDK_URL;
20-
const SECURE_SIGNALS_STORAGE_KEY = process.env.REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY;
21-
const IDENTITY_NAME = process.env.REACT_APP_IDENTITY_NAME;
22-
const DOCS_BASE_URL = process.env.REACT_APP_DOCS_BASE_URL;
26+
// Helper function to get environment variables from runtime (Kubernetes) or build-time
27+
function getEnvVar(key: string): string | undefined {
28+
// First try runtime environment (injected by server.js for Kubernetes)
29+
if (typeof window !== 'undefined' && window.__REACT_APP_ENV__) {
30+
const value = window.__REACT_APP_ENV__[key as keyof typeof window.__REACT_APP_ENV__];
31+
if (value !== undefined && value !== null) {
32+
return value;
33+
}
34+
}
35+
// Fallback to build-time environment variable
36+
return process.env[key];
37+
}
38+
39+
// Environment variables (read from runtime or build-time)
40+
const UID_JS_SDK_NAME = getEnvVar('REACT_APP_UID_JS_SDK_NAME') || '__uid2';
41+
const UID_BASE_URL = getEnvVar('REACT_APP_UID_CLIENT_BASE_URL');
42+
const SECURE_SIGNALS_SDK_URL = getEnvVar('REACT_APP_UID_SECURE_SIGNALS_SDK_URL') || '';
43+
const SECURE_SIGNALS_STORAGE_KEY = getEnvVar('REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY') || '';
44+
const IDENTITY_NAME = getEnvVar('REACT_APP_IDENTITY_NAME') || 'UID2';
45+
const DOCS_BASE_URL = getEnvVar('REACT_APP_DOCS_BASE_URL') || 'https://unifiedid.com/docs';
2346

2447
const clientSideIdentityOptions = {
25-
subscriptionId: process.env.REACT_APP_UID_CSTG_SUBSCRIPTION_ID,
26-
serverPublicKey: process.env.REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY,
48+
subscriptionId: getEnvVar('REACT_APP_UID_CSTG_SUBSCRIPTION_ID'),
49+
serverPublicKey: getEnvVar('REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY'),
2750
};
2851

2952
const SecureSignalsApp = () => {
@@ -49,10 +72,19 @@ const SecureSignalsApp = () => {
4972
const loginAttemptedRef = useRef(false);
5073

5174
// Helper function to get SDK instance
52-
const getSDK = () => window[UID_JS_SDK_NAME];
75+
const getSDK = () => {
76+
const sdk = window[UID_JS_SDK_NAME];
77+
if (!sdk) {
78+
console.error(`SDK not found at window.${UID_JS_SDK_NAME}. Make sure the SDK script is loaded.`);
79+
}
80+
return sdk;
81+
};
5382

5483
const updateElements = useCallback((status) => {
5584
const sdk = getSDK();
85+
if (!sdk) {
86+
return;
87+
}
5688
const token = sdk.getAdvertisingToken();
5789

5890
// Check for opt-out: only if user attempted login, and we got identity null with no token
@@ -178,9 +210,40 @@ const SecureSignalsApp = () => {
178210
useEffect(() => {
179211
// Add callbacks for UID2/EUID JS SDK
180212
let sdk = getSDK();
181-
sdk = sdk || { callbacks: [] };
213+
if (!sdk) {
214+
// SDK not loaded yet, wait for it
215+
const checkSDK = setInterval(() => {
216+
sdk = getSDK();
217+
if (sdk) {
218+
clearInterval(checkSDK);
219+
if (!sdk.callbacks) {
220+
sdk.callbacks = [];
221+
}
222+
sdk.callbacks.push(onIdentityUpdated);
223+
sdk.callbacks.push((eventType: string, payload: any) => {
224+
if (eventType === 'SdkLoaded') {
225+
sdk.init({
226+
baseUrl: UID_BASE_URL,
227+
});
228+
}
229+
if (eventType === 'InitCompleted') {
230+
if (sdk.isLoginRequired()) {
231+
sdk.setIdentity(identity);
232+
setIdentity(identity);
233+
}
234+
}
235+
});
236+
}
237+
}, 100);
238+
return () => clearInterval(checkSDK);
239+
}
240+
241+
// SDK is available, set up callbacks
242+
if (!sdk.callbacks) {
243+
sdk.callbacks = [];
244+
}
182245
sdk.callbacks.push(onIdentityUpdated);
183-
sdk.callbacks.push((eventType, payload) => {
246+
sdk.callbacks.push((eventType: string, payload: any) => {
184247
if (eventType === 'SdkLoaded') {
185248
sdk.init({
186249
baseUrl: UID_BASE_URL,
@@ -261,6 +324,18 @@ const SecureSignalsApp = () => {
261324

262325
try {
263326
const sdk = getSDK();
327+
if (!sdk) {
328+
console.error('SDK not available. Make sure the SDK script is loaded.');
329+
return;
330+
}
331+
332+
// Check if crypto.subtle is available (required for SDK)
333+
if (typeof window !== 'undefined' && !window.crypto?.subtle) {
334+
console.error('crypto.subtle is not available. This requires HTTPS or a secure context.');
335+
alert('crypto.subtle is not available. Please access this page over HTTPS.');
336+
return;
337+
}
338+
264339
await sdk.setIdentityFromEmail(email, clientSideIdentityOptions);
265340
loadSecureSignals();
266341
} catch (e) {

web-integrations/javascript-sdk/react-client-side/package-lock.json

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web-integrations/javascript-sdk/react-client-side/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
},
1919
"devDependencies": {
2020
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
21+
"cross-env": "^7.0.3",
2122
"dotenv-cli": "^10.0.0"
2223
},
2324
"overrides": {
@@ -28,7 +29,7 @@
2829
},
2930
"scripts": {
3031
"start": "node server.js",
31-
"dev": "PORT=3034 dotenv -e ../../../.env -- react-scripts start",
32+
"dev": "cross-env PORT=3034 dotenv -e ../../../.env -- react-scripts start",
3233
"build": "react-scripts build",
3334
"test": "react-scripts test",
3435
"eject": "react-scripts eject"

0 commit comments

Comments
 (0)