multica/apps/web/lib/device.ts
Naiyuan Qing 242be23876 feat(utils): unify encrypted Device ID across all platforms
- Add common generateEncryptedId() utility in @multica/utils
- All Device IDs now use same encryption algorithm (40 hex chars)
- Web: store encrypted format directly in localStorage
- Desktop: use shared utility, accept encrypted ID from Web
- Hub: use shared utility for hub-id generation
- Telegram: use shared utility for device ID generation
- Gateway hook: use encrypted format for client connections

Algorithm: sha256(sha256(uuid).slice(0,32)).slice(0,8) + sha256(uuid).slice(0,32)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-13 17:34:23 +08:00

55 lines
1.6 KiB
TypeScript

/**
* Device ID management for Multica Web
* Stores encrypted format directly (40 hex chars)
*/
const DEVICE_ID_KEY = 'MULTICA_DEVICE_ID'
// SHA-256 hash function (using Web Crypto API)
async function sha256(text: string): Promise<string> {
const buffer = new TextEncoder().encode(text)
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer)
const hashArray = Array.from(new Uint8Array(hashBuffer))
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
}
// Generate encrypted device ID (40 hex chars)
async function generateEncryptedDeviceId(): Promise<string> {
const uuid = crypto.randomUUID()
const firstHash = (await sha256(uuid)).slice(0, 32)
return (await sha256(firstHash)).slice(0, 8) + firstHash
}
// Validate encrypted ID format (40 hex characters)
function isValidEncryptedId(id: string): boolean {
return typeof id === 'string' && /^[a-f0-9]{40}$/i.test(id)
}
// Cached promise for async generation
let deviceIdPromise: Promise<string> | null = null
/**
* Get or create Device ID (encrypted 40-char format)
* Stored in localStorage, ready to use directly
*/
export async function getOrCreateDeviceId(): Promise<string> {
if (typeof window === 'undefined') return ''
const existing = localStorage.getItem(DEVICE_ID_KEY)
// If already encrypted format, return as-is
if (existing && isValidEncryptedId(existing)) {
return existing
}
// Generate new encrypted ID
if (!deviceIdPromise) {
deviceIdPromise = generateEncryptedDeviceId().then((id) => {
localStorage.setItem(DEVICE_ID_KEY, id)
return id
})
}
return deviceIdPromise
}