UNPKG

watusertracking

Version:

User activity tracking package

273 lines (271 loc) 12.2 kB
export function insitella() { const apiBaseURL = "http://192.168.10.152"; const formatDate = (date) => date.toISOString().split("T")[0]; const generateUserName = (length) => { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; return Array.from({ length }, () => chars.charAt(crypto.getRandomValues(new Uint32Array(1))[0] % chars.length) ).join(""); }; const saveUserNameInSession = (value) => sessionStorage.setItem("insitella_usernames", JSON.stringify(value)); const saveUserEventInSession = (value) => sessionStorage.setItem("insitella_userevents", JSON.stringify(value)); const getCookie = (cookieName) => { return document.cookie .split(";") .map((c) => c.trim()) .find((c) => c.startsWith(`${cookieName}=`)) ?.split("=")[1] || null; }; const createCookie = (name, value, hours) => { const exp = new Date(Date.now() + hours * 60 * 60 * 1000); document.cookie = `${name}=${value};expires=${exp.toUTCString()};path=/`; }; const detectDeviceType = () => { const ua = navigator.userAgent.toLowerCase(); return /ipad|tablet|playbook|silk/i.test(ua) ? "tablet" : /mobile|iphone|ipod|blackberry|opera mini|iemobile|windows phone|trident|opera mobi|mobilesafari|htc|nokia|symbian|samsung|lg|mot/i.test(ua) ? "mobile" : "pc"; }; const determineCurrentScreen = () => window.location.href.split("/").pop(); const closeCookiePopup = () => document.getElementById("cookiePopup")?.remove(); const handleCookieOnAcceptance = async () => { closeCookiePopup(); if (!navigator.geolocation) return; const getPosition = () => new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject) ); try { const { coords: { latitude, longitude }, } = await getPosition(); const url = `https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=json`; const response = await fetch(url); const locationData = await response.json(); const userType = sessionStorage.getItem("insitella_usernames") ? "Authenticated" : "Anonymous"; const deviceType = getCookie("deviceType"); const userInfo = { ip: ipAddress, userName: generateUserName(5), userType, browserName, formattedDate, formattedTime, clientName, deviceType, }; const locationInfo = { clientName, latitude: latitude.toString(), longitude: longitude.toString(), cityName: locationData.address?.city || locationData.address?.town || locationData.address?.village || "", country: locationData.address?.country || "" }; const deviceInfo = { clientName, DeviceName: deviceType, }; const configRes = await fetch(`${apiBaseURL}/config`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userInfo }), }); const { _id, serverUpdateTime } = await configRes.json(); createCookie("serverUpdateTime", serverUpdateTime, 30); createCookie("userId", _id, 30); locationInfo._id = _id; deviceInfo._id = _id; await fetch(`${apiBaseURL}/saveMapData`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(locationInfo), }); await fetch(`${apiBaseURL}/saveDeviceData`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(deviceInfo), }); createCookie("cookieAccepted", "true", 24); } catch (err) { } }; const htmlTemplate = ` <div id="cookiePopup" style="position: fixed; inset: 0; background: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 9999;"> <div class="wrapper" style="background: #fff; position: fixed; bottom: 20px; left: 50px; max-width: 500px; border-radius: 15px; text-align: center; border: 1px solid #493179; padding: 25px; box-shadow: 0 0 18px rgba(0, 0, 0, 0.13);"> <img src="../../assets/img/cookie.png" alt="Cookie" style="max-width: 90px;"> <div class="content" style="margin-top: 10px;"> <h1 style="font-size: 25px; font-weight: 600;">GDPR Compliance Notice</h1> <h5>What data do we collect?</h5> <ul style="list-style-type: disc; text-align: left;"> <li>Personal information such as name, email, and location when you sign up or interact with our platform.</li> <li>Usage patterns, preferences, and interactions to enhance your experience.</li> </ul> <div class="buttons" style="display: flex; justify-content: center;"> <button id="cookieCancelBtn" style="padding: 10px 20px; margin: 0 5px; border: none; font-size: 16px; font-weight: 500; border-radius: 5px; cursor: pointer; background: #eee; color: #333;">Cancel</button> <button id="cookieAcceptBtn" style="padding: 10px 20px; margin: 0 5px; border: none; font-size: 16px; font-weight: 500; border-radius: 5px; cursor: pointer; background: #493179; color: #fff;">Accept</button> </div> </div> </div> </div> `; const currentDate = new Date(); const formattedDate = formatDate(currentDate); const formattedTime = [currentDate.getHours(), currentDate.getMinutes(), currentDate.getSeconds()].map((n) => n.toLocaleString()).join(":"); const browserNameMap = { Firefox: "Mozilla Firefox", "Edg/": "Microsoft Edge", Chrome: "Google Chrome", Safari: "Apple Safari", Opera: "Opera", MSIE: "Internet Explorer", "Trident/": "Internet Explorer", }; const userAgent = navigator.userAgent; const browserName = Object.keys(browserNameMap).find((key) => userAgent.includes(key)) || "Unknown Browser"; const clientName = document.querySelector("title")?.innerHTML || ""; let ipAddress = ""; let pageName = ""; let newPageName = ""; let isPageChanged = false; if (!getCookie("deviceType")) createCookie("deviceType", detectDeviceType(), 24); document.addEventListener("DOMContentLoaded", () => { fetch("https://api.ipify.org?format=json") .then((res) => res.json()) .then(({ ip }) => { ipAddress = ip; const deviceType = getCookie("deviceType"); const userDetail = { userInfo: [{ ip, userName: generateUserName(5), browserName, dates: formattedDate, time: formattedTime, deviceType, clientName, }], }; const stored = JSON.parse(sessionStorage.getItem("insitella_usernames")); if (stored?.userInfo?.[0]?.ip !== ip) saveUserNameInSession(userDetail); }) .catch((err) => {}); if (!getCookie("cookieAccepted")) { const container = document.createElement("div"); container.innerHTML = htmlTemplate.trim(); document.body.appendChild(container.firstChild); document.getElementById("cookieAcceptBtn")?.addEventListener("click", handleCookieOnAcceptance); document.getElementById("cookieCancelBtn")?.addEventListener("click", closeCookiePopup); } pageName = determineCurrentScreen(); new MutationObserver(() => { const currentUrl = window.location.href; newPageName = currentUrl.substring(currentUrl.lastIndexOf("/") + 1); if (newPageName !== pageName) isPageChanged = true; }).observe(document.body, { subtree: true, childList: true }); }); (function () { let clickData = {}; let clickCounts = {}; let requestPayload = null; let isResponseToDB = false; let isSending = false; const userDetail = {}; const dataKeysToClear = ["insitella_userevents", "insitella_usernames"]; const updateClickCount = (text, type) => { const key = `${type}${text}`; clickCounts[key] = (clickCounts[key] || 0) + 1; const displayElement = document.getElementById(`${key}_click_count`); if (displayElement) displayElement.textContent = clickCounts[key]; if (!clickData[pageName]) clickData[pageName] = {}; clickData[pageName][key] = clickCounts[key]; userDetail.userEvents = [{ ...clickData }]; clickData = {}; clickCounts = {}; const existingUserEventsInSession = sessionStorage.getItem("insitella_userevents"); const newUserEvents = JSON.parse(JSON.stringify(userDetail)); if (!existingUserEventsInSession) { newUserEvents.userEvents[0].date = formattedDate; saveUserEventInSession([newUserEvents]); requestPayload = JSON.stringify(newUserEvents.userEvents); isResponseToDB = true; } else { let stored = JSON.parse(existingUserEventsInSession); const existingEvents = stored[0].userEvents; newUserEvents.userEvents.forEach((event, index) => { const targetEvent = existingEvents[index] || {}; const screen = Object.keys(event)[0]; if (!targetEvent[screen]) targetEvent[screen] = {}; for (const key in event[screen]) { targetEvent[screen][key] = (targetEvent[screen][key] || 0) + event[screen][key]; } existingEvents[index] = targetEvent; }); existingEvents[0].date = formattedDate; saveUserEventInSession([{ ...userDetail, userEvents: existingEvents }]); requestPayload = JSON.stringify(existingEvents); isResponseToDB = true; } }; const getParentContent = (el, selector) => { let content = el.textContent.trim(); let parent = el.parentElement.closest(selector); while (parent) { content = parent.textContent.trim(); parent = parent.parentElement.closest(selector); } return content; }; document.addEventListener("click", (event) => { const target = event.target; if (target.closest("button")) { updateClickCount(getParentContent(target, "button"), "btn_"); if (isPageChanged) pageName = newPageName; } else if (target.closest("a")) { updateClickCount(getParentContent(target, "a"), "link_"); if (isPageChanged) pageName = newPageName; } }); const sendUserEventData = async () => { const existingUserEventsInSession = sessionStorage.getItem("insitella_userevents"); if (isSending || !isResponseToDB || !requestPayload || !existingUserEventsInSession) return; isSending = true; const userId = getCookie("userId"); try { dataKeysToClear.forEach((key) => sessionStorage.removeItem(key)); const response = await fetch(`${apiBaseURL}/updateUserEvents/${userId}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: requestPayload, }); if (!response.ok) throw new Error(`Failed to send event data: ${response.status}`); const res = await response.json(); const existingUserEventsInSession = sessionStorage.getItem("insitella_userevents"); if (!existingUserEventsInSession) { isResponseToDB = false; requestPayload = null; } } catch (err) { throw new Error(`Error sending user event data: ${err.message}`); } finally { isSending = false; } }; let bootstrapTimer = null; let eventDataTimer = null; const startSendingEventData = () => { const interval = getCookie("serverUpdateTime"); if (interval) { if (eventDataTimer) clearInterval(eventDataTimer); eventDataTimer = setInterval(sendUserEventData, parseInt(interval)); if (bootstrapTimer) { clearInterval(bootstrapTimer); bootstrapTimer = null; } } }; sendUserEventData(); bootstrapTimer = setInterval(startSendingEventData, 1000); })(); } module.exports = { insitella };