60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
/**
|
|
* Best-effort device / browser label from a User-Agent string.
|
|
* Not for security decisions — display and audit only.
|
|
*/
|
|
export function parseDeviceFromUserAgent(
|
|
userAgent: string | null | undefined,
|
|
): { device: string; userAgent: string | null } {
|
|
const ua = userAgent?.trim() || null;
|
|
if (!ua) {
|
|
return { device: "Unknown device", userAgent: null };
|
|
}
|
|
|
|
const os =
|
|
/Windows NT/i.test(ua)
|
|
? "Windows"
|
|
: /Android/i.test(ua)
|
|
? "Android"
|
|
: /iPhone|iPad|iPod/i.test(ua)
|
|
? "iOS"
|
|
: /Mac OS X|Macintosh/i.test(ua)
|
|
? "macOS"
|
|
: /CrOS/i.test(ua)
|
|
? "Chrome OS"
|
|
: /Linux/i.test(ua)
|
|
? "Linux"
|
|
: null;
|
|
|
|
const browser =
|
|
/Edg\//i.test(ua)
|
|
? "Edge"
|
|
: /OPR\/|Opera/i.test(ua)
|
|
? "Opera"
|
|
: /Firefox\//i.test(ua)
|
|
? "Firefox"
|
|
: /Chrome\//i.test(ua) && !/Edg\//i.test(ua)
|
|
? "Chrome"
|
|
: /Safari\//i.test(ua) && !/Chrome\//i.test(ua)
|
|
? "Safari"
|
|
: /curl\//i.test(ua)
|
|
? "curl"
|
|
: null;
|
|
|
|
const formFactor = /Mobile|Android.*Mobile|iPhone|iPod/i.test(ua)
|
|
? "Mobile"
|
|
: /iPad|Tablet|Android(?!.*Mobile)/i.test(ua)
|
|
? "Tablet"
|
|
: "Desktop";
|
|
|
|
const parts = [formFactor, os, browser].filter(Boolean);
|
|
return {
|
|
device: parts.length > 0 ? parts.join(" · ") : "Unknown device",
|
|
userAgent: ua.slice(0, 512),
|
|
};
|
|
}
|
|
|
|
export function getRequestUserAgent(request: Request): string | null {
|
|
const ua = request.headers.get("user-agent")?.trim();
|
|
return ua || null;
|
|
}
|