/** * 设备信息服务 — TypeScript 实现 * * 与 device_service.py 逻辑一致,保留原 Python 方法不变。 * 提供带缓存的机器码、用户 IP、MAC 地址查询。 * * 适用环境:Node.js / Electron */ import * as os from "node:os"; import * as dgram from "node:dgram"; import { DeviceFingerprint } from "./device"; import type { QueryMethod } from "./device"; // ============================================================ // 工具函数 // ============================================================ /** 对应 Python: _is_valid_ipv4(value) -> bool */ function isValidIPv4(value: string): boolean { const parts = value.split("."); if (parts.length !== 4) return false; for (const part of parts) { const num = parseInt(part, 10); if (isNaN(num) || num < 0 || num > 255) return false; if (part.length > 1 && part.startsWith("0")) return false; } // 排除环回地址和 0.0.0.0 if (value.startsWith("127.") || value === "0.0.0.0") return false; return true; } /** 对应 Python: _resolve_user_ip() -> Optional[str] */ function resolveUserIp(): string | null { // 优先通过 UDP "伪连接" 获取本机出站 IP try { const sock = dgram.createSocket("udp4"); // 同步方式不可行;使用 UDP connect + getsockname 的异步模式 // 但在 Node.js 中这本质是同步的 (connect 不发送数据) sock.connect(80, "8.8.8.8"); const address = sock.address(); sock.close(); if (address && typeof address.address === "string") { const ip = address.address; if (isValidIPv4(ip)) return ip; } } catch { // 忽略 } // 回退: 遍历网络接口查找第一个有效的非环回 IPv4 const interfaces = os.networkInterfaces(); for (const ifaceList of Object.values(interfaces)) { if (!ifaceList) continue; for (const iface of ifaceList) { if (iface.family === "IPv4") { const ip = iface.address; if (isValidIPv4(ip)) return ip; } } } return null; } /** 对应 Python: _resolve_user_mac_address() -> Optional[str] */ function resolveUserMacAddress(): string | null { const interfaces = os.networkInterfaces(); for (const ifaceList of Object.values(interfaces)) { if (!ifaceList) continue; for (const iface of ifaceList) { // 跳过环回接口 if (iface.internal) continue; if (iface.mac && iface.mac !== "00:00:00:00:00:00") { // 对应 Python: 检查多播位 const parts = iface.mac.split(":"); if (parts.length === 6) { const firstByte = parseInt(parts[0], 16); // uuid.getnode 的多播位检查: (node >> 40) & 1 if ((firstByte & 1) === 0) { return iface.mac.toUpperCase(); } } } } } return null; } // ============================================================ // 未解析标记 // ============================================================ const UNRESOLVED = Symbol("unresolved"); // ============================================================ // DeviceService 类 // ============================================================ export class DeviceService { private fingerprint = new DeviceFingerprint(); private cachedMachineCode: string | null | typeof UNRESOLVED = UNRESOLVED; private cachedUserIp: string | null | typeof UNRESOLVED = UNRESOLVED; private cachedUserMacAddress: string | null | typeof UNRESOLVED = UNRESOLVED; private warmupStarted = false; /** * 获取机器码(带缓存)。 * 对应 Python: get_machine_code() -> Optional[str] */ getMachineCode(method: QueryMethod = "auto"): string | null { if (this.cachedMachineCode !== UNRESOLVED) { return this.cachedMachineCode; } const machineCode = this.fingerprint.getMachineCode(method); if (this.cachedMachineCode === UNRESOLVED) { this.cachedMachineCode = machineCode; } return machineCode; } /** * 获取用户本机 IP(带缓存)。 * 对应 Python: get_user_ip() -> Optional[str] */ getUserIp(): string | null { if (this.cachedUserIp !== UNRESOLVED) { return this.cachedUserIp; } const value = resolveUserIp(); if (this.cachedUserIp === UNRESOLVED && value) { this.cachedUserIp = value; } return value; } /** * 获取用户 MAC 地址(带缓存)。 * 对应 Python: get_user_mac_address() -> Optional[str] */ getUserMacAddress(): string | null { if (this.cachedUserMacAddress !== UNRESOLVED) { return this.cachedUserMacAddress; } const value = resolveUserMacAddress(); if (this.cachedUserMacAddress === UNRESOLVED) { this.cachedUserMacAddress = value; } return value; } /** * 异步预热机器码(后台线程)。 * 对应 Python: warm_up_machine_code() -> None */ warmUpMachineCode(): void { if (this.cachedMachineCode !== UNRESOLVED) return; if (this.warmupStarted) return; this.warmupStarted = true; // 使用 setImmediate 模拟后台线程,不阻塞当前事件循环 setImmediate(() => { const machineCode = this.fingerprint.getMachineCode(); if (this.cachedMachineCode === UNRESOLVED) { this.cachedMachineCode = machineCode; } }); } } // ============================================================ // 便捷函数(单例) // ============================================================ const defaultService = new DeviceService(); /** 获取机器码(单例,带缓存) */ export function getMachineCode(method: QueryMethod = "auto"): string | null { return defaultService.getMachineCode(method); } /** 获取用户本机 IP(单例,带缓存) */ export function getUserIp(): string | null { return defaultService.getUserIp(); } /** 获取用户 MAC 地址(单例,带缓存) */ export function getUserMacAddress(): string | null { return defaultService.getUserMacAddress(); } /** 异步预热机器码 */ export function warmUpMachineCode(): void { defaultService.warmUpMachineCode(); }