diff --git a/src/app/actions.ts b/src/app/actions.ts index bc42312..6d5c9f1 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -34,6 +34,7 @@ export interface DetectedSystem { export interface DetectedTracker { name: string; + id?: string | null; } export async function extractMetaData(url: string, keyword?: string) { @@ -288,17 +289,33 @@ export async function extractMetaData(url: string, keyword?: string) { }); const detectedTrackers: DetectedTracker[] = []; - const uniqueTrackers = new Set(); + const uniqueTrackers = new Map(); - // Google Analytics / Tag Manager + const addTracker = (name: string, id: string | null = null) => { + if (!uniqueTrackers.has(name)) { + uniqueTrackers.set(name, { name, id }); + } else { + const existing = uniqueTrackers.get(name)!; + if (!existing.id && id) { + existing.id = id; + uniqueTrackers.set(name, existing); + } + } + }; + + // Google Analytics if ( htmlContent.includes("googletagmanager.com/gtag/js") || htmlContent.includes("google-analytics.com/analytics.js") ) { - uniqueTrackers.add("Google Analytics"); + const gaIdMatch = htmlContent.match(/(G|UA)-[A-Z0-9-]+/); + addTracker("Google Analytics", gaIdMatch ? gaIdMatch[0] : null); } + + // Google Tag Manager if (htmlContent.includes("googletagmanager.com/gtm.js")) { - uniqueTrackers.add("Google Tag Manager"); + const gtmIdMatch = htmlContent.match(/GTM-[A-Z0-9]+/); + addTracker("Google Tag Manager", gtmIdMatch ? gtmIdMatch[0] : null); } // Facebook Pixel @@ -306,7 +323,8 @@ export async function extractMetaData(url: string, keyword?: string) { htmlContent.includes("connect.facebook.net") || htmlContent.includes("fbq('init'") ) { - uniqueTrackers.add("Facebook Pixel"); + const fbIdMatch = htmlContent.match(/fbq\('init', '(\d+)'\)/); + addTracker("Facebook Pixel", fbIdMatch ? fbIdMatch[1] : null); } // Hotjar @@ -314,42 +332,52 @@ export async function extractMetaData(url: string, keyword?: string) { htmlContent.includes("static.hotjar.com") || htmlContent.includes("window.hj=window.hj||function()") ) { - uniqueTrackers.add("Hotjar"); + const hjIdMatch = htmlContent.match(/hjid:(\d+)/); + addTracker("Hotjar", hjIdMatch ? hjIdMatch[1] : null); } // HubSpot if (htmlContent.includes("js.hs-scripts.com")) { - uniqueTrackers.add("HubSpot"); + const hsIdMatch = htmlContent.match(/js\.hs-scripts\.com\/(\d+)\.js/); + addTracker("HubSpot", hsIdMatch ? hsIdMatch[1] : null); } // Segment if (htmlContent.includes("cdn.segment.com")) { - uniqueTrackers.add("Segment"); + const segmentIdMatch = htmlContent.match(/analytics\.load\("([^"]+)"\)/); + addTracker("Segment", segmentIdMatch ? segmentIdMatch[1] : null); } // Mixpanel if (htmlContent.includes("cdn.mxpnl.com")) { - uniqueTrackers.add("Mixpanel"); + const mixpanelIdMatch = htmlContent.match(/mixpanel\.init\("([^"]+)"\)/); + addTracker("Mixpanel", mixpanelIdMatch ? mixpanelIdMatch[1] : null); } // Vercel Analytics if (htmlContent.includes("/_vercel/insights/")) { - uniqueTrackers.add("Vercel Analytics"); + addTracker("Vercel Analytics"); } // Plausible if (htmlContent.includes("plausible.io/js/")) { - uniqueTrackers.add("Plausible"); + const plausibleDomainMatch = htmlContent.match(/data-domain="([^"]+)"/); + addTracker( + "Plausible", + plausibleDomainMatch ? plausibleDomainMatch[1] : null + ); } // Microsoft Clarity if (htmlContent.includes("clarity.ms/tag/")) { - uniqueTrackers.add("Microsoft Clarity"); + const clarityIdMatch = htmlContent.match(/clarity\.ms\/tag\/([a-z0-9]+)/); + addTracker( + "Microsoft Clarity", + clarityIdMatch ? clarityIdMatch[1] : null + ); } - uniqueTrackers.forEach((tracker) => { - detectedTrackers.push({ name: tracker }); - }); + detectedTrackers.push(...Array.from(uniqueTrackers.values())); return { data: { diff --git a/src/components/tracking-display.tsx b/src/components/tracking-display.tsx index 8c401d7..438e105 100644 --- a/src/components/tracking-display.tsx +++ b/src/components/tracking-display.tsx @@ -17,6 +17,7 @@ import { Code, Eye, } from "lucide-react"; +import { CopyButton } from "@/components/copy-button"; interface TrackingDisplayProps { trackers: DetectedTracker[]; @@ -101,11 +102,19 @@ export function TrackingDisplay({ trackers }: TrackingDisplayProps) {
-
+

{tracker.name}

{info.description}

+ {tracker.id && ( +
+

+ {tracker.id} +

+ +
+ )}
);