Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
"cross-env": "^7.0.3",
"dotenv-cli": "^10.0.0"
},
"overrides": {
Expand All @@ -28,7 +29,7 @@
},
"scripts": {
"start": "node server.js",
"dev": "PORT=3044 dotenv -e ../../../.env -- react-scripts start",
"dev": "cross-env PORT=3044 dotenv -e ../../../.env -- react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
Expand Down
66 changes: 56 additions & 10 deletions web-integrations/google-secure-signals/react-client-side/server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
require('dotenv').config({ path: '../../../.env' });

console.log('process.env', process.env);
// Load environment variables from .env file (for local development)
// In Docker, env_file in docker-compose.yml already provides these, so this is optional
try {
require('dotenv').config({ path: '../../../.env' });
} catch (error) {
// Silently fail if .env file doesn't exist (e.g., in Docker where env vars are provided via env_file)
// This is expected behavior when running in Docker
}

const fs = require('fs');
const path = require('path');
Expand All @@ -21,9 +26,19 @@ app.get('/ops/healthcheck', (req, res) => {
// Helper function to serve index.html with environment variable replacement
function serveIndexHtml(req, res) {
// Try build directory first (production), then public (development)
let indexPath = path.join(__dirname, 'build', 'index.html');
if (!fs.existsSync(indexPath)) {
indexPath = path.join(__dirname, 'public', 'index.html');
const buildPath = path.join(__dirname, 'build');
const buildIndexPath = path.join(buildPath, 'index.html');
const publicIndexPath = path.join(__dirname, 'public', 'index.html');

let indexPath;
if (fs.existsSync(buildIndexPath)) {
indexPath = buildIndexPath;
} else if (fs.existsSync(publicIndexPath)) {
console.warn('Warning: build directory not found. Serving from public directory. Run "npm run build" to build the React app.');
indexPath = publicIndexPath;
} else {
res.status(500).send('Error: Neither build nor public index.html found. Please run "npm run build" first.');
return;
}

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

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

// Inject runtime environment variables as a script tag
// This allows the React app to read environment variables at runtime (for Kubernetes)
const runtimeEnv = {
REACT_APP_UID_JS_SDK_NAME: process.env.REACT_APP_UID_JS_SDK_NAME,
REACT_APP_UID_CLIENT_BASE_URL: process.env.REACT_APP_UID_CLIENT_BASE_URL,
REACT_APP_UID_SECURE_SIGNALS_SDK_URL: process.env.REACT_APP_UID_SECURE_SIGNALS_SDK_URL,
REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY: process.env.REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY,
REACT_APP_IDENTITY_NAME: process.env.REACT_APP_IDENTITY_NAME,
REACT_APP_DOCS_BASE_URL: process.env.REACT_APP_DOCS_BASE_URL,
REACT_APP_UID_CSTG_SUBSCRIPTION_ID: process.env.REACT_APP_UID_CSTG_SUBSCRIPTION_ID,
REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY: process.env.REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY,
};

// Inject script tag before closing </head> tag
const envScript = `<script>window.__REACT_APP_ENV__ = ${JSON.stringify(runtimeEnv)};</script>`;
html = html.replace('</head>', `${envScript}</head>`);

// Set content type explicitly
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.send(html);
}

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

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

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

app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
const buildPath = path.join(__dirname, 'build');
if (fs.existsSync(buildPath)) {
console.log(`Example app listening at http://localhost:${port}`);
console.log('Serving production build from build/ directory');
} else {
console.log(`Example app listening at http://localhost:${port}`);
console.warn('WARNING: build/ directory not found. Serving from public/ directory.');
console.warn('For production, run "npm run build" first.');
console.warn('For development, use "npm run dev" instead.');
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,46 @@ declare global {
getAdvertisingToken: any;
google: any;
googletag: any;
__REACT_APP_ENV__?: {
REACT_APP_UID_JS_SDK_NAME?: string;
REACT_APP_UID_CLIENT_BASE_URL?: string;
REACT_APP_UID_SECURE_SIGNALS_SDK_URL?: string;
REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY?: string;
REACT_APP_IDENTITY_NAME?: string;
REACT_APP_DOCS_BASE_URL?: string;
REACT_APP_UID_CSTG_SUBSCRIPTION_ID?: string;
REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY?: string;
};
}
}

// Declare global variables
declare const google: any;

// Environment variables
const UID_JS_SDK_NAME = process.env.REACT_APP_UID_JS_SDK_NAME;
const UID_BASE_URL = process.env.REACT_APP_UID_CLIENT_BASE_URL;
const SECURE_SIGNALS_SDK_URL = process.env.REACT_APP_UID_SECURE_SIGNALS_SDK_URL;
const SECURE_SIGNALS_STORAGE_KEY = process.env.REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY;
const IDENTITY_NAME = process.env.REACT_APP_IDENTITY_NAME;
const DOCS_BASE_URL = process.env.REACT_APP_DOCS_BASE_URL;
// Helper function to get environment variables from runtime (Kubernetes) or build-time
function getEnvVar(key: string): string | undefined {
// First try runtime environment (injected by server.js for Kubernetes)
if (typeof window !== 'undefined' && window.__REACT_APP_ENV__) {
const value = window.__REACT_APP_ENV__[key as keyof typeof window.__REACT_APP_ENV__];
if (value !== undefined && value !== null) {
return value;
}
}
// Fallback to build-time environment variable
return process.env[key];
}

// Environment variables (read from runtime or build-time)
const UID_JS_SDK_NAME = getEnvVar('REACT_APP_UID_JS_SDK_NAME') || '__uid2';
const UID_BASE_URL = getEnvVar('REACT_APP_UID_CLIENT_BASE_URL');
const SECURE_SIGNALS_SDK_URL = getEnvVar('REACT_APP_UID_SECURE_SIGNALS_SDK_URL') || '';
const SECURE_SIGNALS_STORAGE_KEY = getEnvVar('REACT_APP_UID_SECURE_SIGNALS_STORAGE_KEY') || '';
const IDENTITY_NAME = getEnvVar('REACT_APP_IDENTITY_NAME') || 'UID2';
const DOCS_BASE_URL = getEnvVar('REACT_APP_DOCS_BASE_URL') || 'https://unifiedid.com/docs';

const clientSideIdentityOptions = {
subscriptionId: process.env.REACT_APP_UID_CSTG_SUBSCRIPTION_ID,
serverPublicKey: process.env.REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY,
subscriptionId: getEnvVar('REACT_APP_UID_CSTG_SUBSCRIPTION_ID'),
serverPublicKey: getEnvVar('REACT_APP_UID_CSTG_SERVER_PUBLIC_KEY'),
};

const SecureSignalsApp = () => {
Expand All @@ -49,10 +72,19 @@ const SecureSignalsApp = () => {
const loginAttemptedRef = useRef(false);

// Helper function to get SDK instance
const getSDK = () => window[UID_JS_SDK_NAME];
const getSDK = () => {
const sdk = window[UID_JS_SDK_NAME];
if (!sdk) {
console.error(`SDK not found at window.${UID_JS_SDK_NAME}. Make sure the SDK script is loaded.`);
}
return sdk;
};

const updateElements = useCallback((status) => {
const sdk = getSDK();
if (!sdk) {
return;
}
const token = sdk.getAdvertisingToken();

// Check for opt-out: only if user attempted login, and we got identity null with no token
Expand Down Expand Up @@ -178,9 +210,40 @@ const SecureSignalsApp = () => {
useEffect(() => {
// Add callbacks for UID2/EUID JS SDK
let sdk = getSDK();
sdk = sdk || { callbacks: [] };
if (!sdk) {
// SDK not loaded yet, wait for it
const checkSDK = setInterval(() => {
sdk = getSDK();
if (sdk) {
clearInterval(checkSDK);
if (!sdk.callbacks) {
sdk.callbacks = [];
}
sdk.callbacks.push(onIdentityUpdated);
sdk.callbacks.push((eventType: string, payload: any) => {
if (eventType === 'SdkLoaded') {
sdk.init({
baseUrl: UID_BASE_URL,
});
}
if (eventType === 'InitCompleted') {
if (sdk.isLoginRequired()) {
sdk.setIdentity(identity);
setIdentity(identity);
}
}
});
}
}, 100);
return () => clearInterval(checkSDK);
}

// SDK is available, set up callbacks
if (!sdk.callbacks) {
sdk.callbacks = [];
}
sdk.callbacks.push(onIdentityUpdated);
sdk.callbacks.push((eventType, payload) => {
sdk.callbacks.push((eventType: string, payload: any) => {
if (eventType === 'SdkLoaded') {
sdk.init({
baseUrl: UID_BASE_URL,
Expand Down Expand Up @@ -261,6 +324,18 @@ const SecureSignalsApp = () => {

try {
const sdk = getSDK();
if (!sdk) {
console.error('SDK not available. Make sure the SDK script is loaded.');
return;
}

// Check if crypto.subtle is available (required for SDK)
if (typeof window !== 'undefined' && !window.crypto?.subtle) {
console.error('crypto.subtle is not available. This requires HTTPS or a secure context.');
alert('crypto.subtle is not available. Please access this page over HTTPS.');
return;
}

await sdk.setIdentityFromEmail(email, clientSideIdentityOptions);
loadSecureSignals();
} catch (e) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.0",
"cross-env": "^7.0.3",
"dotenv-cli": "^10.0.0"
},
"overrides": {
Expand All @@ -28,7 +29,7 @@
},
"scripts": {
"start": "node server.js",
"dev": "PORT=3034 dotenv -e ../../../.env -- react-scripts start",
"dev": "cross-env PORT=3034 dotenv -e ../../../.env -- react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
Expand Down
Loading