236 lines
6.5 KiB
TypeScript
236 lines
6.5 KiB
TypeScript
import type { Request } from "express";
|
|
|
|
export type DeviceType = "web" | "desktop" | "mobile";
|
|
|
|
export interface DeviceInfo {
|
|
type: DeviceType;
|
|
browser: string;
|
|
version: string;
|
|
os: string;
|
|
deviceInfo: string;
|
|
}
|
|
|
|
export function detectPlatform(req: Request): DeviceType {
|
|
const userAgent = req.headers["user-agent"] || "";
|
|
const electronHeader = req.headers["x-electron-app"];
|
|
|
|
if (electronHeader === "true" || userAgent.includes("Termix-Desktop")) {
|
|
return "desktop";
|
|
}
|
|
|
|
if (userAgent.includes("Termix-Mobile")) {
|
|
return "mobile";
|
|
}
|
|
|
|
return "web";
|
|
}
|
|
|
|
export function parseUserAgent(req: Request): DeviceInfo {
|
|
const userAgent = req.headers["user-agent"] || "Unknown";
|
|
const platform = detectPlatform(req);
|
|
|
|
if (platform === "desktop") {
|
|
return parseElectronUserAgent(userAgent);
|
|
}
|
|
|
|
if (platform === "mobile") {
|
|
return parseMobileUserAgent(userAgent);
|
|
}
|
|
|
|
return parseWebUserAgent(userAgent);
|
|
}
|
|
|
|
function parseElectronUserAgent(userAgent: string): DeviceInfo {
|
|
let os = "Unknown OS";
|
|
let version = "Unknown";
|
|
|
|
const termixMatch = userAgent.match(/Termix-Desktop\/([\d.]+)\s*\(([^;)]+)/);
|
|
if (termixMatch) {
|
|
version = termixMatch[1];
|
|
os = termixMatch[2].trim();
|
|
} else {
|
|
if (userAgent.includes("Windows")) {
|
|
os = parseWindowsVersion(userAgent);
|
|
} else if (userAgent.includes("Mac OS X")) {
|
|
os = parseMacVersion(userAgent);
|
|
} else if (userAgent.includes("macOS")) {
|
|
os = "macOS";
|
|
} else if (userAgent.includes("Linux")) {
|
|
os = "Linux";
|
|
}
|
|
|
|
const electronMatch = userAgent.match(/Electron\/([\d.]+)/);
|
|
if (electronMatch) {
|
|
version = electronMatch[1];
|
|
}
|
|
}
|
|
|
|
return {
|
|
type: "desktop",
|
|
browser: "Termix Desktop",
|
|
version,
|
|
os,
|
|
deviceInfo: `Termix Desktop on ${os}`,
|
|
};
|
|
}
|
|
|
|
function parseMobileUserAgent(userAgent: string): DeviceInfo {
|
|
let os = "Unknown OS";
|
|
let version = "Unknown";
|
|
|
|
const termixPlatformMatch = userAgent.match(/Termix-Mobile\/(Android|iOS)/i);
|
|
if (termixPlatformMatch) {
|
|
const platform = termixPlatformMatch[1];
|
|
if (platform.toLowerCase() === "android") {
|
|
const androidMatch = userAgent.match(/Android ([\d.]+)/);
|
|
os = androidMatch ? `Android ${androidMatch[1]}` : "Android";
|
|
} else if (platform.toLowerCase() === "ios") {
|
|
const iosMatch = userAgent.match(/OS ([\d_]+)/);
|
|
if (iosMatch) {
|
|
const iosVersion = iosMatch[1].replace(/_/g, ".");
|
|
os = `iOS ${iosVersion}`;
|
|
} else {
|
|
os = "iOS";
|
|
}
|
|
}
|
|
} else {
|
|
if (userAgent.includes("Android")) {
|
|
const androidMatch = userAgent.match(/Android ([\d.]+)/);
|
|
os = androidMatch ? `Android ${androidMatch[1]}` : "Android";
|
|
} else if (
|
|
userAgent.includes("iOS") ||
|
|
userAgent.includes("iPhone") ||
|
|
userAgent.includes("iPad")
|
|
) {
|
|
const iosMatch = userAgent.match(/OS ([\d_]+)/);
|
|
if (iosMatch) {
|
|
const iosVersion = iosMatch[1].replace(/_/g, ".");
|
|
os = `iOS ${iosVersion}`;
|
|
} else {
|
|
os = "iOS";
|
|
}
|
|
}
|
|
}
|
|
|
|
const versionMatch = userAgent.match(
|
|
/Termix-Mobile\/(?:Android|iOS|)([\d.]+)/i,
|
|
);
|
|
if (versionMatch) {
|
|
version = versionMatch[1];
|
|
}
|
|
|
|
return {
|
|
type: "mobile",
|
|
browser: "Termix Mobile",
|
|
version,
|
|
os,
|
|
deviceInfo: `Termix Mobile on ${os}`,
|
|
};
|
|
}
|
|
|
|
function parseWebUserAgent(userAgent: string): DeviceInfo {
|
|
let browser = "Unknown Browser";
|
|
let version = "Unknown";
|
|
let os = "Unknown OS";
|
|
|
|
if (userAgent.includes("Edg/")) {
|
|
const match = userAgent.match(/Edg\/([\d.]+)/);
|
|
browser = "Edge";
|
|
version = match ? match[1] : "Unknown";
|
|
} else if (userAgent.includes("Chrome/") && !userAgent.includes("Edg")) {
|
|
const match = userAgent.match(/Chrome\/([\d.]+)/);
|
|
browser = "Chrome";
|
|
version = match ? match[1] : "Unknown";
|
|
} else if (userAgent.includes("Firefox/")) {
|
|
const match = userAgent.match(/Firefox\/([\d.]+)/);
|
|
browser = "Firefox";
|
|
version = match ? match[1] : "Unknown";
|
|
} else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome")) {
|
|
const match = userAgent.match(/Version\/([\d.]+)/);
|
|
browser = "Safari";
|
|
version = match ? match[1] : "Unknown";
|
|
} else if (userAgent.includes("Opera/") || userAgent.includes("OPR/")) {
|
|
const match = userAgent.match(/(?:Opera|OPR)\/([\d.]+)/);
|
|
browser = "Opera";
|
|
version = match ? match[1] : "Unknown";
|
|
}
|
|
|
|
if (userAgent.includes("Windows")) {
|
|
os = parseWindowsVersion(userAgent);
|
|
} else if (userAgent.includes("Mac OS X")) {
|
|
os = parseMacVersion(userAgent);
|
|
} else if (userAgent.includes("Linux")) {
|
|
os = "Linux";
|
|
} else if (userAgent.includes("Android")) {
|
|
const match = userAgent.match(/Android ([\d.]+)/);
|
|
os = match ? `Android ${match[1]}` : "Android";
|
|
} else if (
|
|
userAgent.includes("iOS") ||
|
|
userAgent.includes("iPhone") ||
|
|
userAgent.includes("iPad")
|
|
) {
|
|
const match = userAgent.match(/OS ([\d_]+)/);
|
|
if (match) {
|
|
const iosVersion = match[1].replace(/_/g, ".");
|
|
os = `iOS ${iosVersion}`;
|
|
} else {
|
|
os = "iOS";
|
|
}
|
|
}
|
|
|
|
if (version !== "Unknown") {
|
|
const versionParts = version.split(".");
|
|
version = versionParts.slice(0, 2).join(".");
|
|
}
|
|
|
|
return {
|
|
type: "web",
|
|
browser,
|
|
version,
|
|
os,
|
|
deviceInfo: `${browser} ${version} on ${os}`,
|
|
};
|
|
}
|
|
|
|
function parseWindowsVersion(userAgent: string): string {
|
|
if (userAgent.includes("Windows NT 10.0")) {
|
|
return "Windows 10/11";
|
|
} else if (userAgent.includes("Windows NT 6.3")) {
|
|
return "Windows 8.1";
|
|
} else if (userAgent.includes("Windows NT 6.2")) {
|
|
return "Windows 8";
|
|
} else if (userAgent.includes("Windows NT 6.1")) {
|
|
return "Windows 7";
|
|
} else if (userAgent.includes("Windows NT 6.0")) {
|
|
return "Windows Vista";
|
|
} else if (
|
|
userAgent.includes("Windows NT 5.1") ||
|
|
userAgent.includes("Windows NT 5.2")
|
|
) {
|
|
return "Windows XP";
|
|
}
|
|
return "Windows";
|
|
}
|
|
|
|
function parseMacVersion(userAgent: string): string {
|
|
const match = userAgent.match(/Mac OS X ([\d_]+)/);
|
|
if (match) {
|
|
const version = match[1].replace(/_/g, ".");
|
|
const parts = version.split(".");
|
|
const major = parseInt(parts[0]);
|
|
const minor = parseInt(parts[1]);
|
|
|
|
if (major === 10) {
|
|
if (minor >= 15) return `macOS ${major}.${minor}`;
|
|
if (minor === 14) return "macOS Mojave";
|
|
if (minor === 13) return "macOS High Sierra";
|
|
if (minor === 12) return "macOS Sierra";
|
|
} else if (major >= 11) {
|
|
return `macOS ${major}`;
|
|
}
|
|
|
|
return `macOS ${version}`;
|
|
}
|
|
return "macOS";
|
|
}
|