[dyad] Added tracking ID detection and display - wrote 3 file(s)

This commit is contained in:
[dyad]
2026-01-21 08:09:07 +01:00
parent f51d90d9cc
commit 28f5b5bc54
2 changed files with 53 additions and 16 deletions

View File

@@ -34,6 +34,7 @@ export interface DetectedSystem {
export interface DetectedTracker { export interface DetectedTracker {
name: string; name: string;
id?: string | null;
} }
export async function extractMetaData(url: string, keyword?: string) { export async function extractMetaData(url: string, keyword?: string) {
@@ -288,17 +289,33 @@ export async function extractMetaData(url: string, keyword?: string) {
}); });
const detectedTrackers: DetectedTracker[] = []; const detectedTrackers: DetectedTracker[] = [];
const uniqueTrackers = new Set<string>(); const uniqueTrackers = new Map<string, DetectedTracker>();
// 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 ( if (
htmlContent.includes("googletagmanager.com/gtag/js") || htmlContent.includes("googletagmanager.com/gtag/js") ||
htmlContent.includes("google-analytics.com/analytics.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")) { 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 // Facebook Pixel
@@ -306,7 +323,8 @@ export async function extractMetaData(url: string, keyword?: string) {
htmlContent.includes("connect.facebook.net") || htmlContent.includes("connect.facebook.net") ||
htmlContent.includes("fbq('init'") htmlContent.includes("fbq('init'")
) { ) {
uniqueTrackers.add("Facebook Pixel"); const fbIdMatch = htmlContent.match(/fbq\('init', '(\d+)'\)/);
addTracker("Facebook Pixel", fbIdMatch ? fbIdMatch[1] : null);
} }
// Hotjar // Hotjar
@@ -314,42 +332,52 @@ export async function extractMetaData(url: string, keyword?: string) {
htmlContent.includes("static.hotjar.com") || htmlContent.includes("static.hotjar.com") ||
htmlContent.includes("window.hj=window.hj||function()") htmlContent.includes("window.hj=window.hj||function()")
) { ) {
uniqueTrackers.add("Hotjar"); const hjIdMatch = htmlContent.match(/hjid:(\d+)/);
addTracker("Hotjar", hjIdMatch ? hjIdMatch[1] : null);
} }
// HubSpot // HubSpot
if (htmlContent.includes("js.hs-scripts.com")) { 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 // Segment
if (htmlContent.includes("cdn.segment.com")) { if (htmlContent.includes("cdn.segment.com")) {
uniqueTrackers.add("Segment"); const segmentIdMatch = htmlContent.match(/analytics\.load\("([^"]+)"\)/);
addTracker("Segment", segmentIdMatch ? segmentIdMatch[1] : null);
} }
// Mixpanel // Mixpanel
if (htmlContent.includes("cdn.mxpnl.com")) { if (htmlContent.includes("cdn.mxpnl.com")) {
uniqueTrackers.add("Mixpanel"); const mixpanelIdMatch = htmlContent.match(/mixpanel\.init\("([^"]+)"\)/);
addTracker("Mixpanel", mixpanelIdMatch ? mixpanelIdMatch[1] : null);
} }
// Vercel Analytics // Vercel Analytics
if (htmlContent.includes("/_vercel/insights/")) { if (htmlContent.includes("/_vercel/insights/")) {
uniqueTrackers.add("Vercel Analytics"); addTracker("Vercel Analytics");
} }
// Plausible // Plausible
if (htmlContent.includes("plausible.io/js/")) { if (htmlContent.includes("plausible.io/js/")) {
uniqueTrackers.add("Plausible"); const plausibleDomainMatch = htmlContent.match(/data-domain="([^"]+)"/);
addTracker(
"Plausible",
plausibleDomainMatch ? plausibleDomainMatch[1] : null
);
} }
// Microsoft Clarity // Microsoft Clarity
if (htmlContent.includes("clarity.ms/tag/")) { 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(...Array.from(uniqueTrackers.values()));
detectedTrackers.push({ name: tracker });
});
return { return {
data: { data: {

View File

@@ -17,6 +17,7 @@ import {
Code, Code,
Eye, Eye,
} from "lucide-react"; } from "lucide-react";
import { CopyButton } from "@/components/copy-button";
interface TrackingDisplayProps { interface TrackingDisplayProps {
trackers: DetectedTracker[]; trackers: DetectedTracker[];
@@ -101,11 +102,19 @@ export function TrackingDisplay({ trackers }: TrackingDisplayProps) {
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground"> <div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground">
<Icon className="h-6 w-6" /> <Icon className="h-6 w-6" />
</div> </div>
<div> <div className="flex-grow">
<h4 className="font-semibold text-lg">{tracker.name}</h4> <h4 className="font-semibold text-lg">{tracker.name}</h4>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
{info.description} {info.description}
</p> </p>
{tracker.id && (
<div className="mt-3 flex items-center gap-2 p-2 rounded-md bg-background border">
<p className="font-mono text-sm text-foreground flex-grow break-all">
{tracker.id}
</p>
<CopyButton textToCopy={tracker.id} />
</div>
)}
</div> </div>
</div> </div>
); );