Skip to content
Open
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
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ VITE_WS_API_URL=
# Client-side Sentry DSN (optional). Leave empty to disable error reporting.
VITE_SENTRY_DSN=

# Umami Analytics (privacy-friendly, only loaded in production builds)
# VITE_UMAMI_URL: URL to your Umami script.js (e.g. https://analytics.example.com/script.js)
# VITE_UMAMI_WEBSITE_ID: Website ID from your Umami dashboard
VITE_UMAMI_URL=
VITE_UMAMI_WEBSITE_ID=

# Map interaction mode:
# - "flat" keeps pitch/rotation disabled (2D interaction)
# - "3d" enables pitch/rotation interactions (default)
Expand Down
3 changes: 0 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,6 @@
}
</script>

<!-- Umami Analytics -->
<script defer src="https://abacus.worldmonitor.app/script.js" data-website-id="e8800335-c853-46a8-8497-c993ed2f58bc" data-domains="worldmonitor.app,tech.worldmonitor.app,finance.worldmonitor.app,commodity.worldmonitor.app,happy.worldmonitor.app"></script>

<!-- Favicons -->
<link rel="icon" type="image/x-icon" href="/favico/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="/favico/favicon-32x32.png" />
Expand Down
13 changes: 13 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ import { installUtmInterceptor } from './utils/utm';

const sentryDsn = import.meta.env.VITE_SENTRY_DSN?.trim();

// Umami Analytics — only load in production builds when env vars are configured
if (import.meta.env.PROD) {
Comment on lines +11 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the stale comment exists in analytics.ts
rg -n -C2 "index\.html" src/services/analytics.ts

Repository: protoLabsAI/worldmonitor

Length of output: 212


🏁 Script executed:

#!/bin/bash
# Get the full context of Umami loading in main.ts around lines 11-12
sed -n '1,30p' src/main.ts

Repository: protoLabsAI/worldmonitor

Length of output: 1234


🏁 Script executed:

#!/bin/bash
# Check if index.html contains the Umami script tag
rg -n "umami" -i index.html

Repository: protoLabsAI/worldmonitor

Length of output: 50


Update stale comment in src/services/analytics.ts.

The comment at line 66 states that "Umami initialises itself via the script tag in index.html", but this is outdated. Umami is now dynamically injected in src/main.ts (lines 11-22) when conditions are met (PROD build and environment variables configured). Update the comment in initAnalytics() to reflect this change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.ts` around lines 11 - 12, The comment inside initAnalytics() is
stale: replace the line that says "Umami initialises itself via the script tag
in index.html" with a concise note that Umami is now dynamically injected from
the main bootstrap (main.ts) when running a production build and required
environment variables are present (the code path that checks
import.meta.env.PROD and env vars); update the comment in initAnalytics() to
reference this dynamic injection behavior and remove the old
index.html/script-tag wording.

const umamiUrl = import.meta.env.VITE_UMAMI_URL?.trim();
const umamiWebsiteId = import.meta.env.VITE_UMAMI_WEBSITE_ID?.trim();
if (umamiUrl && umamiWebsiteId) {
const script = document.createElement('script');
script.defer = true;
script.src = umamiUrl;
script.dataset.websiteId = umamiWebsiteId;
document.head.appendChild(script);
}
}
Comment on lines +11 to +22
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for previous Umami configuration in index.html or related files
rg -n "data-domains|data-website-id" --type html
rg -n "umami" index.html 2>/dev/null || echo "index.html not found at root"
fd -t f "index.html" --exec rg -l "umami" {} \;

Repository: protoLabsAI/worldmonitor

Length of output: 382


🏁 Script executed:

#!/bin/bash
# 1. Verify src/main.ts current content at lines 11-22
echo "=== Current src/main.ts (lines 11-30) ==="
sed -n '11,30p' src/main.ts

echo -e "\n=== Check for analytics.ts and any Umami-related comments ==="
fd -t f "analytics.ts" --exec grep -n "index.html\|umami\|script" {} +

echo -e "\n=== Search for VITE_UMAMI environment variable references ==="
rg -n "VITE_UMAMI" --type ts --type js

echo -e "\n=== Check if public/index.html exists and contains Umami ==="
if [ -f "public/index.html" ]; then
  rg -n "umami\|data-domains" public/index.html
else
  echo "public/index.html not found"
fi

echo -e "\n=== Check .env files for Umami configuration ==="
fd -t f "\.env" --exec rg -l "UMAMI" {} \;

Repository: protoLabsAI/worldmonitor

Length of output: 2257


🏁 Script executed:

#!/bin/bash
# Check if there are any comments or references to domain restriction or data-domains elsewhere
echo "=== Search for 'domain' references in codebase ==="
rg -i "domain" --type ts --type js -B 2 -A 2 | head -50

echo -e "\n=== Check vite.config.ts for any Umami-related logic ==="
sed -n '620,630p' vite.config.ts

echo -e "\n=== Search for any TODO, FIXME, or NOTE about Umami ==="
rg -i "todo|fixme|note" --type ts --type js | rg -i "umami|analytics" | head -20

echo -e "\n=== Check if there's a .env.example or documentation about VITE_UMAMI ==="
fd -t f "\.env" -o "README*" -o "DEPLOY*" | xargs rg -l "VITE_UMAMI" 2>/dev/null || echo "No matches found"

Repository: protoLabsAI/worldmonitor

Length of output: 50532


Add data-domains attribute to restrict Umami tracking to production domains.

The previous Umami script in public/pro/index.html included data-domains="worldmonitor.app,tech.worldmonitor.app,finance.worldmonitor.app,commodity.worldmonitor.app,happy.worldmonitor.app". Without this attribute, the current implementation will track pageviews on Vercel preview deployments and local builds where import.meta.env.PROD is true (e.g., vite preview).

Additionally, the comment in src/services/analytics.ts:66 stating "Umami initialises itself via the script tag in index.html" is now stale and should be updated to reflect the new loading mechanism in src/main.ts.

♻️ Proposed fix
 // Umami Analytics — only load in production builds when env vars are configured
 if (import.meta.env.PROD) {
   const umamiUrl = import.meta.env.VITE_UMAMI_URL?.trim();
   const umamiWebsiteId = import.meta.env.VITE_UMAMI_WEBSITE_ID?.trim();
   if (umamiUrl && umamiWebsiteId) {
     const script = document.createElement('script');
     script.defer = true;
     script.src = umamiUrl;
     script.dataset.websiteId = umamiWebsiteId;
+    script.dataset.domains = 'worldmonitor.app,www.worldmonitor.app,tech.worldmonitor.app,finance.worldmonitor.app,commodity.worldmonitor.app,happy.worldmonitor.app';
     document.head.appendChild(script);
   }
 }

Alternatively, add a VITE_UMAMI_DOMAINS environment variable to make this configurable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main.ts` around lines 11 - 22, Add the Umami data-domains attribute when
creating the script in src/main.ts: after obtaining umamiUrl and umamiWebsiteId,
read an optional VITE_UMAMI_DOMAINS env var (or hardcode the same domains used
previously) and set script.dataset.domains = <domains string> before appending
the script element; also update the stale comment in src/services/analytics.ts
(around the comment at line referencing "Umami initialises itself via the script
tag in index.html") to mention that the script is injected from src/main.ts and
that data-domains is used to restrict tracking to production domains.


// Initialize Sentry error tracking (early as possible)
Sentry.init({
dsn: sentryDsn || undefined,
Expand Down
15 changes: 15 additions & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ function brotliPrecompressPlugin(): Plugin {
};
}

function umamiPlugin(umamiUrl: string, umamiWebsiteId: string): Plugin {
return {
name: 'umami-analytics',
apply: 'build',
transformIndexHtml(html) {
if (!umamiUrl || !umamiWebsiteId) return html;
const scriptTag = `\n <script defer src="${umamiUrl}" data-website-id="${umamiWebsiteId}"></script>`;
return html.replace('</head>', `${scriptTag}\n </head>`);
Comment on lines +45 to +48
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider validating the Umami URL format.

The umamiUrl value is directly interpolated into the src attribute without validation. While environment variables are typically trusted, a malformed URL (e.g., containing quotes or other special characters) could break the HTML or, in edge cases, introduce unintended behavior.

A simple URL validation would add defense in depth:

🛡️ Optional: Add basic URL validation
 function umamiPlugin(umamiUrl: string, umamiWebsiteId: string): Plugin {
   return {
     name: 'umami-analytics',
     apply: 'build',
     transformIndexHtml(html) {
-      if (!umamiUrl || !umamiWebsiteId) return html;
+      if (!umamiUrl || !umamiWebsiteId) return html;
+      try {
+        new URL(umamiUrl); // Validate URL format
+      } catch {
+        console.warn('[umami-analytics] Invalid VITE_UMAMI_URL, skipping injection');
+        return html;
+      }
       const scriptTag = `\n    <script defer src="${umamiUrl}" data-website-id="${umamiWebsiteId}"></script>`;
       return html.replace('</head>', `${scriptTag}\n  </head>`);
     },
   };
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
transformIndexHtml(html) {
if (!umamiUrl || !umamiWebsiteId) return html;
const scriptTag = `\n <script defer src="${umamiUrl}" data-website-id="${umamiWebsiteId}"></script>`;
return html.replace('</head>', `${scriptTag}\n </head>`);
transformIndexHtml(html) {
if (!umamiUrl || !umamiWebsiteId) return html;
try {
new URL(umamiUrl); // Validate URL format
} catch {
console.warn('[umami-analytics] Invalid VITE_UMAMI_URL, skipping injection');
return html;
}
const scriptTag = `\n <script defer src="${umamiUrl}" data-website-id="${umamiWebsiteId}"></script>`;
return html.replace('</head>', `${scriptTag}\n </head>`);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vite.config.ts` around lines 45 - 48, The transformIndexHtml hook currently
interpolates umamiUrl and umamiWebsiteId directly into scriptTag in
transformIndexHtml; add basic validation for umamiUrl before building scriptTag
(e.g., attempt to construct new URL(umamiUrl) in a try/catch or run a simple URL
regex) and only create/insert the scriptTag when the URL is valid; if validation
fails, log or warn and return the original html unchanged. Also ensure any
interpolation for umamiWebsiteId is sanitized (e.g., string-only) before
embedding to avoid injecting quotes/special chars.

},
};
}

function htmlVariantPlugin(activeMeta: VariantMeta, activeVariant: string, isDesktopBuild: boolean): Plugin {
return {
name: 'html-variant',
Expand Down Expand Up @@ -608,13 +620,16 @@ export default defineConfig(({ mode }) => {
const isDesktopBuild = process.env.VITE_DESKTOP_RUNTIME === '1';
const activeVariant = process.env.VITE_VARIANT || 'full';
const activeMeta = VARIANT_META[activeVariant] || VARIANT_META.full;
const umamiUrl = env.VITE_UMAMI_URL || '';
const umamiWebsiteId = env.VITE_UMAMI_WEBSITE_ID || '';

return {
define: {
__APP_VERSION__: JSON.stringify(pkg.version),
},
plugins: [
htmlVariantPlugin(activeMeta, activeVariant, isDesktopBuild),
umamiPlugin(umamiUrl, umamiWebsiteId),
polymarketPlugin(),
rssProxyPlugin(),
youtubeLivePlugin(),
Expand Down
Loading