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
30 changes: 29 additions & 1 deletion EssentialCSharp.Web/wwwroot/js/appinsights-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
(function () {
const SDK_URL = "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js";
const CONSENT_EVENT = "ecs:consent-changed";
const DistributedTracingModes = {
AI: 0,
AI_AND_W3C: 1,
W3C: 2
};

let appInsights = null;
let sdkLoadPromise = null;
Expand All @@ -15,6 +20,25 @@
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
}

function getTryDotNetOrigin() {
const value = window.TRYDOTNET_ORIGIN;
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
}

function getCorrelationHeaderDomains() {
const domains = [window.location.hostname];
const tryDotNetOrigin = getTryDotNetOrigin();
if (tryDotNetOrigin) {
try {
domains.push(new URL(tryDotNetOrigin).hostname);
} catch (error) {
console.warn("Ignoring invalid TryDotNet origin for App Insights correlation:", error);
}
}

return Array.from(new Set(domains.filter(Boolean)));
}

function hasAnalyticsConsent() {
if (window.consentManager && typeof window.consentManager.hasAnalyticsConsent === "function") {
return window.consentManager.hasAnalyticsConsent();
Expand Down Expand Up @@ -113,7 +137,11 @@
const instance = new window.Microsoft.ApplicationInsights.ApplicationInsights({
config: {
connectionString,
disableAjaxTracking: true, // avoid duplicate/debatable dependency telemetry from browser fetch/XHR
disableAjaxTracking: false,
disableFetchTracking: false,
distributedTracingMode: DistributedTracingModes.W3C,
correlationHeaderDomains: getCorrelationHeaderDomains(),
enableCorsCorrelation: true,
disableTelemetry: false
}
});
Expand Down
12 changes: 12 additions & 0 deletions EssentialCSharp.Web/wwwroot/js/chat-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ const errorIconClassByType = {
'connection-error': 'fas fa-plug'
};

function getTraceHeaders() {
const headers = {};
if (typeof window.ecsGetCorrelationContext === 'function') {
const traceparent = window.ecsGetCorrelationContext();
if (typeof traceparent === 'string' && traceparent.length > 0) {
headers.traceparent = traceparent;
}
}
return headers;
}

// hCaptcha integration — invisible widget renders once and is reused across messages.
// When HCAPTCHA_SITE_KEY is null (dev / captcha not configured), all captcha calls are no-ops.
const captchaSiteKey = window.HCAPTCHA_SITE_KEY || null;
Expand Down Expand Up @@ -338,6 +349,7 @@ export function useChatWidget() {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...getTraceHeaders()
},
body: JSON.stringify(requestBody)
});
Expand Down
15 changes: 13 additions & 2 deletions EssentialCSharp.Web/wwwroot/js/trydotnet-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ function getCorrelationContext() {
return null;
}

function getTraceHeaders() {
const headers = {};
const correlationContext = getCorrelationContext();
if (typeof correlationContext === 'string' && correlationContext.length > 0) {
headers.traceparent = correlationContext;
}
return headers;
}

function trackTryEvent(name, properties = {}, measurements = {}) {
const appInsights = getAppInsights();
if (!appInsights || typeof appInsights.trackEvent !== 'function') {
Expand Down Expand Up @@ -241,7 +250,7 @@ export function useTryDotNet() {
try {
// Check the actual script endpoint rather than the bare origin,
// which may not have a handler and would return 404.
const res = await fetch(`${origin}/api/trydotnet.min.js`, {
await fetch(`${origin}/api/trydotnet.min.js`, {
method: 'HEAD',
mode: 'no-cors',
signal: controller.signal,
Expand Down Expand Up @@ -489,7 +498,9 @@ export function useTryDotNet() {
* @returns {Promise<string>} The listing source code (extracted snippet)
*/
async function fetchListingCode(chapter, listing) {
const response = await fetch(`/api/ListingSourceCode/chapter/${chapter}/listing/${listing}`);
const response = await fetch(`/api/ListingSourceCode/chapter/${chapter}/listing/${listing}`, {
headers: getTraceHeaders()
});
if (!response.ok) {
throw new Error(ERROR_MESSAGES.fetchFailed);
}
Expand Down