@jillen/analytics
Version:
Advanced analytics package for Next.js applications with bot detection and comprehensive tracking
154 lines (147 loc) • 5.08 kB
JavaScript
'use strict';
var isbot = require('isbot');
// server/middleware-utils.ts
// analytics-constants.ts
var ANALYTICS_CONFIG = {
SERVER_URL: process.env.NEXT_PUBLIC_ANALYTICS_SERVER_URL || "",
SITE_ID: process.env.NEXT_PUBLIC_ANALYTICS_SITE_ID || ""
};
function validateAnalyticsConfig() {
const { SERVER_URL, SITE_ID } = ANALYTICS_CONFIG;
if (!SERVER_URL || !SITE_ID) {
console.warn("[Analytics] Missing environment variables:", {
SERVER_URL: SERVER_URL ? "\u2713" : "\u2717 NEXT_PUBLIC_ANALYTICS_SERVER_URL",
SITE_ID: SITE_ID ? "\u2713" : "\u2717 NEXT_PUBLIC_ANALYTICS_SITE_ID"
});
return false;
}
return true;
}
// analytics-bot-utils.ts
function generateSimpleBotVisitorId(ip, userAgent) {
const normalizedUA = userAgent.toLowerCase().replace(/\s+/g, "");
const combined = `bot:${ip}:${normalizedUA}`;
return Buffer.from(combined).toString("base64").replace(/[^a-zA-Z0-9]/g, "").substring(0, 16);
}
function generateSimpleBotSessionId(ip) {
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
const combined = `bot-session:${ip}:${today}`;
return Buffer.from(combined).toString("base64").replace(/[^a-zA-Z0-9]/g, "").substring(0, 16);
}
function trackBotVisit(request, pathname) {
void (async () => {
try {
if (!validateAnalyticsConfig()) {
return;
}
const serverUrl = ANALYTICS_CONFIG.SERVER_URL;
const siteId = ANALYTICS_CONFIG.SITE_ID;
const userAgent = request.headers.get("user-agent") || "";
const ip = request.headers.get("x-forwarded-for")?.split(",")[0] || request.headers.get("x-real-ip") || "unknown";
const visitorId = generateSimpleBotVisitorId(ip, userAgent);
const sessionId = generateSimpleBotSessionId(ip);
const botPayload = {
// Required fields (minimal values)
siteId,
path: pathname,
visitorId,
sessionId,
eventType: "pageview",
// Essential bot data
userAgent,
ipAddress: ip,
// Client-side fields (bots don't have these)
isNewVisitor: true,
screenResolution: null,
viewportSize: null,
connectionType: null,
clientTimeZone: null,
sessionStartTime: (/* @__PURE__ */ new Date()).toISOString(),
// Optional server fields (skip expensive lookups)
referrer: request.headers.get("referer") || null,
country: null,
city: null,
region: null,
continent: null,
latitude: null,
longitude: null,
timezone: null,
postalCode: null,
host: request.headers.get("host") || null,
protocol: request.headers.get("x-forwarded-proto") || null,
deploymentUrl: null,
edgeRegion: null,
language: null,
doNotTrack: false,
isMobile: /Mobi|Android/i.test(userAgent),
cacheStatus: "UNKNOWN"
};
const endpoint = `${serverUrl}/api/log/ingest`;
await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
mode: "cors",
body: JSON.stringify(botPayload)
});
} catch {
return;
}
})();
}
// server/middleware-utils.ts
function setupAnalyticsMiddleware(request) {
const pathname = request.nextUrl.pathname;
const headers = new Headers(request.headers);
headers.set("x-pathname", pathname);
const userAgent = request.headers.get("user-agent") || "";
if (isbot.isbot(userAgent)) {
trackBotVisit(request, pathname);
}
return {
headers,
pathname
};
}
// server/header-parser.ts
function parseAnalyticsHeaders(headers) {
const ip = headers.get("x-forwarded-for") || headers.get("x-real-ip") || "unknown";
const country = headers.get("x-vercel-ip-country") || null;
const userAgent = headers.get("user-agent") || "";
const city = headers.get("x-vercel-ip-city") || null;
const region = headers.get("x-vercel-ip-country-region") || null;
const continent = headers.get("x-vercel-ip-continent") || null;
const latitude = headers.get("x-vercel-ip-latitude") || null;
const longitude = headers.get("x-vercel-ip-longitude") || null;
const timezone = headers.get("x-vercel-ip-timezone") || null;
const postalCode = headers.get("x-vercel-ip-postal-code") || null;
const host = headers.get("host") || null;
const protocol = headers.get("x-forwarded-proto");
const deploymentUrl = headers.get("x-vercel-deployment-url") || null;
const vercelId = headers.get("x-vercel-id") || null;
const edgeRegion = vercelId ? vercelId.split("::")[0] : null;
const cacheStatus = null;
return {
ip,
country,
city,
region,
continent,
latitude,
longitude,
timezone,
postalCode,
host,
protocol,
deploymentUrl,
userAgent,
edgeRegion,
cacheStatus
};
}
exports.parseAnalyticsHeaders = parseAnalyticsHeaders;
exports.setupAnalyticsMiddleware = setupAnalyticsMiddleware;
exports.trackBotVisit = trackBotVisit;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map