feat: Implement dynamic QR code redirection with comprehensive scan tracking, device/OS detection, and geo-location.
This commit is contained in:
parent
d0a114c1c3
commit
49673e84b6
|
|
@ -114,9 +114,40 @@ async function trackScan(qrId: string, request: NextRequest) {
|
||||||
// Hash IP for privacy
|
// Hash IP for privacy
|
||||||
const ipHash = hashIP(ip);
|
const ipHash = hashIP(ip);
|
||||||
|
|
||||||
const isTablet = /tablet|ipad|playbook|silk|android(?!.*mobile)/i.test(userAgent);
|
// Device Detection Logic:
|
||||||
const isMobile = /mobile|android|iphone/i.test(userAgent);
|
// 1. Windows or Linux -> Always Desktop
|
||||||
const device = isTablet ? 'tablet' : isMobile ? 'mobile' : 'desktop';
|
// 2. Explicit iPad/Tablet keywords -> Tablet
|
||||||
|
// 3. Mac + Chrome browser -> Desktop (real Mac users often use Chrome)
|
||||||
|
// 4. Mac + Safari + No Referrer -> Likely iPad scanning a QR code
|
||||||
|
// 5. Mobile keywords -> Mobile
|
||||||
|
// 6. Everything else -> Desktop
|
||||||
|
|
||||||
|
const isWindows = /windows/i.test(userAgent);
|
||||||
|
const isLinux = /linux/i.test(userAgent) && !/android/i.test(userAgent);
|
||||||
|
const isExplicitTablet = /tablet|ipad|playbook|silk/i.test(userAgent);
|
||||||
|
const isAndroidTablet = /android/i.test(userAgent) && !/mobile/i.test(userAgent);
|
||||||
|
const isMacintosh = /macintosh/i.test(userAgent);
|
||||||
|
const isChrome = /chrome/i.test(userAgent);
|
||||||
|
const isSafari = /safari/i.test(userAgent) && !isChrome;
|
||||||
|
const hasReferrer = !!referer;
|
||||||
|
|
||||||
|
// iPad in desktop mode: Mac + Safari (no Chrome) + No Referrer (physical scan)
|
||||||
|
const isLikelyiPadScan = isMacintosh && isSafari && !hasReferrer;
|
||||||
|
|
||||||
|
let device: string;
|
||||||
|
if (isWindows || isLinux) {
|
||||||
|
device = 'desktop';
|
||||||
|
} else if (isExplicitTablet || isAndroidTablet || isLikelyiPadScan) {
|
||||||
|
device = 'tablet';
|
||||||
|
} else if (/mobile|iphone/i.test(userAgent)) {
|
||||||
|
device = 'mobile';
|
||||||
|
} else if (isMacintosh && isChrome) {
|
||||||
|
device = 'desktop'; // Mac with Chrome = real desktop
|
||||||
|
} else if (isMacintosh && hasReferrer) {
|
||||||
|
device = 'desktop'; // Mac with referrer = probably clicked a link on desktop
|
||||||
|
} else {
|
||||||
|
device = 'desktop'; // Default fallback
|
||||||
|
}
|
||||||
|
|
||||||
// Detect OS
|
// Detect OS
|
||||||
let os = 'unknown';
|
let os = 'unknown';
|
||||||
|
|
@ -137,7 +168,9 @@ async function trackScan(qrId: string, request: NextRequest) {
|
||||||
const utmMedium = searchParams.get('utm_medium');
|
const utmMedium = searchParams.get('utm_medium');
|
||||||
const utmCampaign = searchParams.get('utm_campaign');
|
const utmCampaign = searchParams.get('utm_campaign');
|
||||||
|
|
||||||
// Check if this is a unique scan (first scan from this IP today)
|
// Check if this is a unique scan (first scan from this IP + Device today)
|
||||||
|
// We include a simplified device fingerprint so different devices on same IP count as unique
|
||||||
|
const deviceFingerprint = hashIP(userAgent.substring(0, 100)); // Hash the user agent for privacy
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
|
@ -145,6 +178,9 @@ async function trackScan(qrId: string, request: NextRequest) {
|
||||||
where: {
|
where: {
|
||||||
qrId,
|
qrId,
|
||||||
ipHash,
|
ipHash,
|
||||||
|
userAgent: {
|
||||||
|
startsWith: userAgent.substring(0, 50), // Match same device type
|
||||||
|
},
|
||||||
ts: {
|
ts: {
|
||||||
gte: today,
|
gte: today,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,15 @@ export function parseUserAgent(userAgent: string | null): { device: string | nul
|
||||||
let device: string | null = null;
|
let device: string | null = null;
|
||||||
let os: string | null = null;
|
let os: string | null = null;
|
||||||
|
|
||||||
// Detect device - check tablet FIRST since iPad can match mobile patterns
|
// Detect device
|
||||||
if (/Tablet|iPad/i.test(userAgent)) {
|
// iPadOS 13+ sends "Macintosh" user agent.
|
||||||
|
// Without referrer info here, we fall back to checking for Safari-only Mac UAs (common for iPad)
|
||||||
|
const isIPad = /iPad/i.test(userAgent) ||
|
||||||
|
(/Macintosh/i.test(userAgent) && /Safari/i.test(userAgent) && !/Chrome/i.test(userAgent));
|
||||||
|
|
||||||
|
if (isIPad || /Tablet|PlayBook|Silk/i.test(userAgent)) {
|
||||||
device = 'tablet';
|
device = 'tablet';
|
||||||
} else if (/Mobile|Android|iPhone/i.test(userAgent)) {
|
} else if (/Mobile|Android|iPhone/i.test(userAgent) && !isIPad) {
|
||||||
device = 'mobile';
|
device = 'mobile';
|
||||||
} else {
|
} else {
|
||||||
device = 'desktop';
|
device = 'desktop';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue