|
@@ -0,0 +1,53 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * Visitor Tracking Service
|
|
|
|
|
+ * Sets a unique persistent cookie to identify visitors across sessions
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+const VISITOR_COOKIE_NAME = 'r3d_visitor_id';
|
|
|
|
|
+const COOKIE_EXPIRY_DAYS = 365;
|
|
|
|
|
+
|
|
|
|
|
+export function initVisitorTracking() {
|
|
|
|
|
+ // 1. Check if we already have a visitor ID
|
|
|
|
|
+ const existingId = getCookie(VISITOR_COOKIE_NAME);
|
|
|
|
|
+ if (existingId) return existingId;
|
|
|
|
|
+
|
|
|
|
|
+ // 2. Generate new unique ID
|
|
|
|
|
+ const newId = generateUniqueId();
|
|
|
|
|
+
|
|
|
|
|
+ // 3. Set the cookie
|
|
|
|
|
+ setCookie(VISITOR_COOKIE_NAME, newId, COOKIE_EXPIRY_DAYS);
|
|
|
|
|
+
|
|
|
|
|
+ return newId;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export function getVisitorId(): string | null {
|
|
|
|
|
+ return getCookie(VISITOR_COOKIE_NAME);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function generateUniqueId(): string {
|
|
|
|
|
+ // Use crypto.randomUUID if available, fallback to random string
|
|
|
|
|
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
|
|
|
+ return crypto.randomUUID();
|
|
|
|
|
+ }
|
|
|
|
|
+ return Math.random().toString(36).substring(2, 15) +
|
|
|
|
|
+ Math.random().toString(36).substring(2, 15);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function setCookie(name: string, value: string, days: number) {
|
|
|
|
|
+ const date = new Date();
|
|
|
|
|
+ date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
|
|
|
+ const expires = "expires=" + date.toUTCString();
|
|
|
|
|
+ // SameSite=Lax is good for tracking but reasonably secure
|
|
|
|
|
+ document.cookie = `${name}=${value};${expires};path=/;SameSite=Lax`;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getCookie(name: string): string | null {
|
|
|
|
|
+ const nameEQ = name + "=";
|
|
|
|
|
+ const ca = document.cookie.split(';');
|
|
|
|
|
+ for (let i = 0; i < ca.length; i++) {
|
|
|
|
|
+ let c = ca[i];
|
|
|
|
|
+ while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
|
|
|
|
+ if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+}
|