refactor: 重构硬件指纹模块并清理无关依赖

- 添加 ohash 和 systeminformation 依赖项到项目中
- 将硬件指纹生成模块从工具包移至服务器应用,并统一优化注释中的标点符号为中文全角格式,提升代码注释的可读性与一致性。
- 将硬件指纹获取方法的导入路径从 '@furtherverse/utils/fingerprint' 更新为 '@/lib/fingerprint'。
- 移除对本地 workspace 包 @furtherverse/utils 的依赖并清理相关配置
- 删除未使用的工具包配置文件并移除相关依赖项
- 删除硬件指纹测试文件,移除对 systeminformation 模块的模拟和相关测试用例。
- 移除对 fingerprint 工具函数的导出
- 删除未使用的 tsconfig 配置文件
This commit is contained in:
2026-01-24 03:42:12 +08:00
parent 8ceb212033
commit b0d3245b9d
8 changed files with 35 additions and 249 deletions

View File

@@ -15,7 +15,6 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@furtherverse/utils": "workspace:*",
"@orpc/client": "catalog:",
"@orpc/contract": "catalog:",
"@orpc/openapi": "catalog:",
@@ -30,9 +29,11 @@
"@tauri-apps/api": "catalog:",
"drizzle-orm": "catalog:",
"drizzle-zod": "catalog:",
"ohash": "catalog:",
"postgres": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"systeminformation": "catalog:",
"uuid": "catalog:",
"zod": "catalog:"
},

View File

@@ -3,17 +3,17 @@ import si from 'systeminformation'
/**
*
* - strong: 2+
* - medium: 1
* - weak: 无强标识符/
* - strong: 2+ ()
* - medium: 1 ()
* - weak: 无强标识符(/)
*/
export type FingerprintQuality = 'strong' | 'medium' | 'weak'
/**
*
* ()
*/
export type NormalizedSystemInfo = {
/** 系统 UUID最稳定的硬件标识符 */
/** 系统 UUID(最稳定的硬件标识符) */
systemUuid: string | null
/** 系统序列号 */
systemSerial: string | null
@@ -25,9 +25,9 @@ export type NormalizedSystemInfo = {
biosVersion: string | null
/** BIOS 供应商 */
biosVendor: string | null
/** CPU 品牌标识用于质量评估 */
/** CPU 品牌标识(用于质量评估) */
cpuBrand: string | null
/** 主硬盘序列号可选高稳定性 */
/** 主硬盘序列号(可选,高稳定性) */
primaryDiskSerial?: string | null
}
@@ -36,14 +36,14 @@ export type NormalizedSystemInfo = {
*/
export type HardwareFingerprintOptions = {
/**
* TTL 10
*
* TTL(), 10
* ,
*/
cacheTtlMs?: number
/**
* true
* /
* ( true)
* 注意:在容/
*/
includePrimaryDisk?: boolean
}
@@ -52,7 +52,7 @@ export type HardwareFingerprintOptions = {
*
*/
export type HardwareFingerprintResult = {
/** 机器码HMAC-SHA256 哈希64 字符十六进制 */
/** 机器码(HMAC-SHA256 哈希,64 字符十六进制) */
fingerprint: string
/** 指纹质量等级 */
quality: FingerprintQuality
@@ -91,7 +91,7 @@ function computeQuality(info: NormalizedSystemInfo): {
}
/**
*
* ()
*/
async function collectNormalizedInfo(
opts: HardwareFingerprintOptions,
@@ -115,7 +115,7 @@ async function collectNormalizedInfo(
const bios = biosRes.status === 'fulfilled' ? biosRes.value : null
const cpu = cpuRes.status === 'fulfilled' ? cpuRes.value : null
// 提取主硬盘序列号通常是第一个物理磁盘
// 提取主硬盘序列号(通常是第一个物理磁盘)
let primaryDiskSerial: string | null = null
if (diskRes.status === 'fulfilled' && Array.isArray(diskRes.value)) {
const disks = diskRes.value as Array<{ serialNum?: string; type?: string }>
@@ -126,43 +126,43 @@ async function collectNormalizedInfo(
}
return {
// 系统级标识符最稳定
// 系统级标识符(最稳定)
systemUuid: (system?.uuid ?? uuid?.hardware ?? null) || null,
systemSerial: (system?.serial ?? null) || null,
// 主板标识符次稳定
// 主板标识符(次稳定)
baseboardSerial: (baseboard?.serial ?? null) || null,
baseboardManufacturer: (baseboard?.manufacturer ?? null) || null,
// BIOS 信息辅助识别
// BIOS 信息(辅助识别)
biosVersion: (bios?.version ?? null) || null,
biosVendor: (bios?.vendor ?? null) || null,
// CPU 信息辅助识别
// CPU 信息(辅助识别)
cpuBrand: (cpu?.brand ?? null) || null,
// 磁盘序列号可选高稳定性
// 磁盘序列号(可选,高稳定性)
...(opts.includePrimaryDisk !== false ? { primaryDiskSerial } : {}),
}
}
/**
/**
*
* ()
*
*
* 适用场景:客户端部署的软件授
*
*
* - SHA-256 Base64URL 43
* :
* - SHA-256 (Base64URL ,43 ),
* - 使 ohash
* - 使
* -
* - 客户端部署场景:客户可以看到代码,使
* - ()
* -
*
*
* :
* - 使 UUID
* -
* -
* - ,
*
* @example
* ```typescript
@@ -198,9 +198,9 @@ export async function getHardwareFingerprint(
// 计算质量
const { quality, count } = computeQuality(info)
// 使用 ohash 生成指纹自动序列化 + SHA-256 + Base64URL
// 使用 ohash 生成指纹(自动序列化 + SHA-256 + Base64URL)
const fingerprint = hash({
v: 1, // 版本号未来如需变更采集策略可递增
v: 1, // 版本号,未来如需变更采集策略可递增
info,
})
@@ -223,7 +223,7 @@ export async function getHardwareFingerprint(
}
/**
*
* ()
*/
export function clearFingerprintCache(): void {
cache = null

View File

@@ -1,4 +1,4 @@
import { getHardwareFingerprint } from '@furtherverse/utils/fingerprint'
import { getHardwareFingerprint } from '@/lib/fingerprint'
import { os } from '../server'
export const get = os.fingerprint.get.handler(async () => {

View File

@@ -25,7 +25,6 @@
"name": "@furtherverse/server",
"version": "1.0.0",
"dependencies": {
"@furtherverse/utils": "workspace:*",
"@orpc/client": "catalog:",
"@orpc/contract": "catalog:",
"@orpc/openapi": "catalog:",
@@ -40,9 +39,11 @@
"@tauri-apps/api": "catalog:",
"drizzle-orm": "catalog:",
"drizzle-zod": "catalog:",
"ohash": "catalog:",
"postgres": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"systeminformation": "catalog:",
"uuid": "catalog:",
"zod": "catalog:",
},
@@ -71,18 +72,6 @@
"name": "@furtherverse/tsconfig",
"version": "1.0.0",
},
"packages/utils": {
"name": "@furtherverse/utils",
"version": "1.0.0",
"dependencies": {
"ohash": "catalog:",
"systeminformation": "catalog:",
},
"devDependencies": {
"@furtherverse/tsconfig": "workspace:*",
"typescript": "catalog:",
},
},
},
"catalog": {
"@biomejs/biome": "^2.3.11",
@@ -262,8 +251,6 @@
"@furtherverse/tsconfig": ["@furtherverse/tsconfig@workspace:packages/tsconfig"],
"@furtherverse/utils": ["@furtherverse/utils@workspace:packages/utils"],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],

View File

@@ -1,25 +0,0 @@
{
"name": "@furtherverse/utils",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"fix": "biome check --write",
"typecheck": "tsc --noEmit"
},
"imports": {
"#*": "./src/*"
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*.ts"
},
"dependencies": {
"ohash": "catalog:",
"systeminformation": "catalog:"
},
"devDependencies": {
"@furtherverse/tsconfig": "workspace:*",
"typescript": "catalog:"
}
}

View File

@@ -1,173 +0,0 @@
import { beforeEach, describe, expect, it, mock } from 'bun:test'
import { getHardwareFingerprint } from './fingerprint'
// Mock systeminformation module
const mockSystemInfo = {
uuid: mock(() =>
Promise.resolve({
os: 'test-os-uuid',
hardware: 'test-hardware-uuid',
}),
),
baseboard: mock(() =>
Promise.resolve({
manufacturer: 'Test Manufacturer',
model: 'Test Model',
version: '1.0',
serial: 'TEST123',
}),
),
bios: mock(() =>
Promise.resolve({
vendor: 'Test Vendor',
version: '1.0.0',
releaseDate: '2024-01-01',
}),
),
system: mock(() =>
Promise.resolve({
manufacturer: 'Test System',
model: 'Test Model',
version: '1.0',
sku: 'TEST-SKU',
}),
),
diskLayout: mock(() =>
Promise.resolve([
{
device: '/dev/sda',
type: 'SSD',
name: 'Test Disk',
size: 512000000000,
},
]),
),
networkInterfaces: mock(() =>
Promise.resolve([
{
iface: 'eth0',
mac: '00:11:22:33:44:55',
ip4: '192.168.1.1',
},
]),
),
}
mock.module('systeminformation', () => ({
default: mockSystemInfo,
}))
describe('fingerprint', () => {
beforeEach(() => {
// Reset all mocks before each test
mockSystemInfo.uuid.mockClear()
mockSystemInfo.baseboard.mockClear()
mockSystemInfo.bios.mockClear()
mockSystemInfo.system.mockClear()
mockSystemInfo.diskLayout.mockClear()
mockSystemInfo.networkInterfaces.mockClear()
})
describe('getHardwareFingerprint', () => {
it('should return a fingerprint hash', async () => {
const fingerprint = await getHardwareFingerprint()
expect(fingerprint).toBeDefined()
expect(typeof fingerprint).toBe('string')
expect(fingerprint.length).toBeGreaterThan(0)
})
it('should call all system information methods', async () => {
await getHardwareFingerprint()
expect(mockSystemInfo.uuid).toHaveBeenCalledTimes(1)
expect(mockSystemInfo.baseboard).toHaveBeenCalledTimes(1)
expect(mockSystemInfo.bios).toHaveBeenCalledTimes(1)
expect(mockSystemInfo.system).toHaveBeenCalledTimes(1)
expect(mockSystemInfo.diskLayout).toHaveBeenCalledTimes(1)
expect(mockSystemInfo.networkInterfaces).toHaveBeenCalledTimes(1)
})
it('should return the same fingerprint for the same system info', async () => {
const fingerprint1 = await getHardwareFingerprint()
const fingerprint2 = await getHardwareFingerprint()
expect(fingerprint1).toBe(fingerprint2)
})
it('should return different fingerprint when system info changes', async () => {
const fingerprint1 = await getHardwareFingerprint()
// Change mock data
mockSystemInfo.uuid.mockImplementationOnce(() =>
Promise.resolve({
os: 'different-os-uuid',
hardware: 'different-hardware-uuid',
}),
)
const fingerprint2 = await getHardwareFingerprint()
expect(fingerprint1).not.toBe(fingerprint2)
})
it('should handle empty system information gracefully', async () => {
// Mock empty responses
mockSystemInfo.uuid.mockImplementationOnce(() =>
Promise.resolve({ os: '', hardware: '' }),
)
mockSystemInfo.baseboard.mockImplementationOnce(() =>
Promise.resolve({
manufacturer: '',
model: '',
version: '',
serial: '',
}),
)
mockSystemInfo.bios.mockImplementationOnce(() =>
Promise.resolve({ vendor: '', version: '', releaseDate: '' }),
)
mockSystemInfo.system.mockImplementationOnce(() =>
Promise.resolve({ manufacturer: '', model: '', version: '', sku: '' }),
)
mockSystemInfo.diskLayout.mockImplementationOnce(() =>
Promise.resolve([]),
)
mockSystemInfo.networkInterfaces.mockImplementationOnce(() =>
Promise.resolve([]),
)
const fingerprint = await getHardwareFingerprint()
expect(fingerprint).toBeDefined()
expect(typeof fingerprint).toBe('string')
})
it('should handle partial system information', async () => {
mockSystemInfo.baseboard.mockImplementationOnce(() =>
Promise.resolve({
manufacturer: 'Only Manufacturer',
model: '',
version: '',
serial: '',
}),
)
const fingerprint = await getHardwareFingerprint()
expect(fingerprint).toBeDefined()
expect(typeof fingerprint).toBe('string')
})
it('should be deterministic with the same input', async () => {
const results = await Promise.all([
getHardwareFingerprint(),
getHardwareFingerprint(),
getHardwareFingerprint(),
])
expect(results[0]).toBe(results[1])
expect(results[1]).toBe(results[2])
})
})
})

View File

@@ -1 +0,0 @@
export * from './fingerprint'

View File

@@ -1,3 +0,0 @@
{
"extends": "@furtherverse/tsconfig/base.json"
}