Skip to content

Commit 452478b

Browse files
solonkoolesyaOlesia Solonko
andauthored
Refactor Application Insight integration in frontend (#41)
Co-authored-by: Olesia Solonko <[email protected]>
1 parent 4759026 commit 452478b

File tree

7 files changed

+45
-127
lines changed

7 files changed

+45
-127
lines changed

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ services:
2222
- "8080:80"
2323
environment:
2424
- API_BASE_URL=${API_BASE_URL:-http://backend:5000}
25+
- APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=b4d987bc-1712-4026-aafb-f590fd854ab3;IngestionEndpoint=https://norwayeast-0.in.applicationinsights.azure.com/;LiveEndpoint=https://norwayeast.livediagnostics.monitor.azure.com/;ApplicationId=9fc4692c-7609-4abb-b547-49203ac14d4a
2526
depends_on:
2627
- backend
2728

frontend/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ FROM node:24-alpine AS build
22
WORKDIR /app
33
COPY package.json package-lock.json ./
44
COPY build.js ./
5+
RUN npm install
56
RUN npm ci
67
COPY src ./src
78

@@ -13,4 +14,9 @@ RUN npm run build
1314
FROM nginx:1.29-alpine
1415
WORKDIR /usr/share/nginx/html
1516
COPY --from=build /app/dist/ ./
17+
COPY docker-entrypoint.sh /docker-entrypoint.sh
18+
RUN chmod +x /docker-entrypoint.sh
19+
20+
ENTRYPOINT ["/docker-entrypoint.sh"]
21+
CMD ["nginx", "-g", "daemon off;"]
1622
EXPOSE 80

frontend/build.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,6 @@ if (apiBaseUrl) {
1818
html = html.replace("</head>", `${scriptTag}\n</head>`);
1919
}
2020

21-
// Add Application Insights connection string if available
22-
const appInsightsConnectionString = process.env.APPLICATIONINSIGHTS_CONNECTION_STRING || "";
23-
if (appInsightsConnectionString) {
24-
const appInsightsScript = `<script>window.APPLICATIONINSIGHTS_CONNECTION_STRING = "${appInsightsConnectionString}";</script>`;
25-
html = html.replace("</head>", `${appInsightsScript}\n</head>`);
26-
}
27-
2821
const distHtmlPath = path.join(__dirname, "dist", "index.html");
2922
fs.writeFileSync(distHtmlPath, html);
3023

@@ -34,9 +27,3 @@ const distJsDir = path.join(__dirname, "dist", "js");
3427
fs.readdirSync(srcJsDir).forEach((file) => {
3528
fs.copyFileSync(path.join(srcJsDir, file), path.join(distJsDir, file));
3629
});
37-
38-
if (appInsightsConnectionString) {
39-
console.log("Application Insights: Enabled");
40-
} else {
41-
console.log("Application Insights: Disabled (no connection string provided)");
42-
}

frontend/docker-entrypoint.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/sh
2+
set -e
3+
4+
# Replace placeholder with runtime environment variable
5+
if [ -n "$APPLICATIONINSIGHTS_CONNECTION_STRING" ]; then
6+
echo "Injecting Application Insights connection string..."
7+
sed -i "s|__APPINSIGHTS_CONN__|$APPLICATIONINSIGHTS_CONNECTION_STRING|g" /usr/share/nginx/html/index.html
8+
else
9+
echo "No APPLICATIONINSIGHTS_CONNECTION_STRING provided!"
10+
fi
11+
12+
# Start nginx
13+
exec "$@"

frontend/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@
1313
"type": "commonjs",
1414
"devDependencies": {
1515
"prettier": "^2.8.8"
16+
},
17+
"dependencies": {
18+
"@microsoft/applicationinsights-web": "^3.0.0"
1619
}
1720
}

frontend/src/index.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
<meta charset="utf-8" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0">
77
<link href="https://unpkg.com/[email protected]/dist/maplibre-gl.css" rel="stylesheet" />
8+
<!-- Application Insights -->
9+
<script>
10+
window.APPLICATIONINSIGHTS_CONNECTION_STRING = "__APPINSIGHTS_CONN__";
11+
</script>
12+
<script src="https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js"></script>
13+
<script src="js/appInsights.js"></script>
814
<script src="https://unpkg.com/[email protected]/dist/maplibre-gl.js"></script>
915
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
1016
<!-- Base map configuration -->
@@ -17,8 +23,6 @@
1723
<!-- <script src="js/points-layer.js"></script> -->
1824
<!-- Add this to your index.html file, wherever your other script tags are located -->
1925
<!-- <script src="js/norway-hazard-tiles.js"></script> -->
20-
<!-- Application Insights -->
21-
<script src="js/appInsights.js"></script>
2226
<style>
2327
/* Reset and full screen layout */
2428
* {

frontend/src/js/appInsights.js

Lines changed: 16 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,16 @@
1-
(function() {
2-
const connectionString = window.APPLICATIONINSIGHTS_CONNECTION_STRING;
3-
4-
if (!connectionString) {
5-
return;
6-
}
7-
8-
const script = document.createElement('script');
9-
script.src = 'https://js.monitor.azure.com/scripts/b/ai.2.min.js';
10-
script.async = true;
11-
12-
script.onload = function() {
13-
const appInsights = new Microsoft.ApplicationInsights.ApplicationInsights({
14-
config: {
15-
connectionString: connectionString,
16-
enableAutoRouteTracking: false,
17-
disableExceptionTracking: true,
18-
disableTelemetry: false,
19-
samplingPercentage: 100
20-
}
21-
});
22-
23-
appInsights.loadAppInsights();
24-
25-
// Generate anonymous user ID for session tracking
26-
const userId = sessionStorage.getItem('ai_user') ||
27-
(function() {
28-
// Prefer cryptographically secure random values
29-
let randStr = '';
30-
if (window.crypto && window.crypto.getRandomValues) {
31-
// Generate 16 random bytes, then encode as hex
32-
const buf = new Uint8Array(16);
33-
window.crypto.getRandomValues(buf);
34-
randStr = Array.from(buf, b => b.toString(16).padStart(2, '0')).join('');
35-
} else {
36-
// Fallback if crypto not supported (rare): use Math.random, but warn
37-
randStr = Math.random().toString(36).substring(2, 15);
38-
}
39-
return 'user_' + randStr;
40-
})();
41-
sessionStorage.setItem('ai_user', userId);
42-
appInsights.setAuthenticatedUserContext(userId);
43-
44-
// Collect comprehensive user statistics
45-
const userStats = {
46-
referrer: document.referrer || 'direct',
47-
userAgent: navigator.userAgent,
48-
language: navigator.language,
49-
languages: navigator.languages ? navigator.languages.join(',') : '',
50-
screenResolution: `${screen.width}x${screen.height}`,
51-
colorDepth: screen.colorDepth,
52-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
53-
platform: navigator.platform,
54-
cookieEnabled: navigator.cookieEnabled,
55-
onLine: navigator.onLine,
56-
deviceMemory: navigator.deviceMemory || 'unknown',
57-
connection: navigator.connection ? {
58-
effectiveType: navigator.connection.effectiveType,
59-
downlink: navigator.connection.downlink,
60-
rtt: navigator.connection.rtt
61-
} : 'unknown'
62-
};
63-
64-
// Track page view with enhanced user statistics
65-
appInsights.trackPageView({
66-
name: 'Tsunami Map',
67-
properties: userStats
68-
});
69-
70-
// Track user location (with permission)
71-
if (navigator.geolocation && 'geolocation' in navigator) {
72-
navigator.geolocation.getCurrentPosition(
73-
function(position) {
74-
// Track approximate location (country/region level for privacy)
75-
appInsights.trackEvent('UserLocation', {
76-
latitude: Math.round(position.coords.latitude * 10) / 10, // Rounded for privacy
77-
longitude: Math.round(position.coords.longitude * 10) / 10, // Rounded for privacy
78-
accuracy: position.coords.accuracy > 1000 ? 'low' : 'high'
79-
});
80-
},
81-
function(error) {
82-
// Track location access denied/failed
83-
appInsights.trackEvent('LocationAccessDenied', {
84-
error: error.message || 'unknown'
85-
});
86-
},
87-
{ enableHighAccuracy: false, timeout: 5000, maximumAge: 300000 }
88-
);
89-
}
90-
91-
// Track map interactions
92-
window.trackMapInteraction = function(action, properties) {
93-
appInsights.trackEvent('MapInteraction', {
94-
action: action,
95-
...properties,
96-
timestamp: new Date().toISOString()
97-
});
98-
};
99-
100-
// Track session duration
101-
let sessionStart = Date.now();
102-
window.addEventListener('beforeunload', function() {
103-
const sessionDuration = Math.round((Date.now() - sessionStart) / 1000);
104-
appInsights.trackEvent('SessionEnd', {
105-
durationSeconds: sessionDuration,
106-
userId: userId
107-
});
108-
});
109-
};
110-
111-
document.head.appendChild(script);
112-
})();
1+
const appInsights = new Microsoft.ApplicationInsights.ApplicationInsights({
2+
config: {
3+
connectionString: window.APPLICATIONINSIGHTS_CONNECTION_STRING,
4+
enableAutoRouteTracking: true
5+
}
6+
});
7+
appInsights.loadAppInsights();
8+
appInsights.trackPageView();
9+
10+
window.appInsights = appInsights;
11+
12+
// Example custom event
13+
appInsights.trackEvent({
14+
name: "Tsunami Map",
15+
properties: userStats
16+
});

0 commit comments

Comments
 (0)