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
|
||||
const ipHash = hashIP(ip);
|
||||
|
||||
const isTablet = /tablet|ipad|playbook|silk|android(?!.*mobile)/i.test(userAgent);
|
||||
const isMobile = /mobile|android|iphone/i.test(userAgent);
|
||||
const device = isTablet ? 'tablet' : isMobile ? 'mobile' : 'desktop';
|
||||
// Device Detection Logic:
|
||||
// 1. Windows or Linux -> Always 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
|
||||
let os = 'unknown';
|
||||
|
|
@ -137,7 +168,9 @@ async function trackScan(qrId: string, request: NextRequest) {
|
|||
const utmMedium = searchParams.get('utm_medium');
|
||||
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();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
|
|
@ -145,6 +178,9 @@ async function trackScan(qrId: string, request: NextRequest) {
|
|||
where: {
|
||||
qrId,
|
||||
ipHash,
|
||||
userAgent: {
|
||||
startsWith: userAgent.substring(0, 50), // Match same device type
|
||||
},
|
||||
ts: {
|
||||
gte: today,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,10 +24,15 @@ export function parseUserAgent(userAgent: string | null): { device: string | nul
|
|||
let device: string | null = null;
|
||||
let os: string | null = null;
|
||||
|
||||
// Detect device - check tablet FIRST since iPad can match mobile patterns
|
||||
if (/Tablet|iPad/i.test(userAgent)) {
|
||||
// Detect device
|
||||
// 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';
|
||||
} else if (/Mobile|Android|iPhone/i.test(userAgent)) {
|
||||
} else if (/Mobile|Android|iPhone/i.test(userAgent) && !isIPad) {
|
||||
device = 'mobile';
|
||||
} else {
|
||||
device = 'desktop';
|
||||
|
|
|
|||
Loading…
Reference in New Issue