import { app } from 'electron' import crypto from 'crypto' import os from 'os' import { execSync } from 'child_process' // 激活功能开关 - 设置为 false 可以临时禁用激活功能 const ACTIVATION_ENABLED = false // API 配置 const API_BASE = 'https://miyu.admin.aiqji.com' // 加密密钥(混淆存储) const _k1 = [0x43, 0x69, 0x70, 0x68, 0x65, 0x72] // Cipher const _k2 = [0x54, 0x61, 0x6c, 0x6b, 0x32, 0x30, 0x32, 0x36] // Talk2026 const ENCRYPTION_KEY = Buffer.from([..._k1, ..._k2]).toString() // 激活状态接口 export interface ActivationStatus { isActivated: boolean type: string | null expiresAt: string | null activatedAt: string | null daysRemaining: number | null deviceId: string } // 激活结果接口 export interface ActivationResult { success: boolean message: string data?: { type: string expires_at: string | null activated_at: string } } class ActivationService { private deviceId: string | null = null private cachedStatus: ActivationStatus | null = null private lastCheckTime: number = 0 private readonly CHECK_INTERVAL = 5 * 60 * 1000 // 5分钟缓存 /** * 获取设备唯一标识(CPU + 主板信息) */ getDeviceId(): string { if (this.deviceId) return this.deviceId try { const factors: string[] = [] // 1. CPU 信息 const cpus = os.cpus() if (cpus.length > 0) { factors.push(cpus[0].model) } // 2. 主板序列号(Windows) if (process.platform === 'win32') { try { // 尝试使用 PowerShell 获取序列号 (替代已弃用的 wmic) const biosSerial = execSync('powershell -Command "Get-WmiObject Win32_Bios | Select-Object -ExpandProperty SerialNumber"', { encoding: 'buffer', stdio: ['ignore', 'pipe', 'ignore'] // 忽略 stderr 防止错误输出 }) const serial = biosSerial.toString('utf8').trim() if (serial && serial !== 'To be filled by O.E.M.') { factors.push(serial) } } catch { } // 3. 主板UUID(更可靠的标识) try { // 尝试使用 PowerShell 获取 UUID const uuid = execSync('powershell -Command "Get-WmiObject Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID"', { encoding: 'buffer', stdio: ['ignore', 'pipe', 'ignore'] }) const uuidValue = uuid.toString('utf8').trim() if (uuidValue && uuidValue !== 'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF') { factors.push(uuidValue) } } catch { } } // 组合并哈希 const combined = factors.join('|') this.deviceId = crypto.createHash('sha256').update(combined).digest('hex').substring(0, 32) return this.deviceId } catch (e) { // 降级方案:使用随机 ID 并持久化 console.error('获取设备ID失败:', e) this.deviceId = crypto.randomBytes(16).toString('hex') return this.deviceId } } /** * 加密数据 */ private encrypt(data: string): string { const iv = crypto.randomBytes(16) const key = crypto.scryptSync(ENCRYPTION_KEY, 'salt', 32) const cipher = crypto.createCipheriv('aes-256-cbc', key, iv) let encrypted = cipher.update(data, 'utf8', 'hex') encrypted += cipher.final('hex') return iv.toString('hex') + ':' + encrypted } /** * 解密数据 */ private decrypt(encryptedData: string): string | null { try { const [ivHex, encrypted] = encryptedData.split(':') if (!ivHex || !encrypted) return null const iv = Buffer.from(ivHex, 'hex') const key = crypto.scryptSync(ENCRYPTION_KEY, 'salt', 32) const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv) let decrypted = decipher.update(encrypted, 'hex', 'utf8') decrypted += decipher.final('utf8') return decrypted } catch { return null } } /** * 生成签名(防篡改) */ private generateSignature(data: object): string { const sortedKeys = Object.keys(data).sort() const str = sortedKeys.map(k => `${k}=${(data as any)[k]}`).join('&') return crypto.createHmac('sha256', ENCRYPTION_KEY).update(str).digest('hex') } /** * 验证签名 */ private verifySignature(data: object, signature: string): boolean { return this.generateSignature(data) === signature } /** * 验证激活码(在线) */ async verifyCode(code: string): Promise<{ success: boolean; message: string }> { // 如果激活功能被禁用,直接返回成功 if (!ACTIVATION_ENABLED) { return { success: true, message: '激活码有效(功能已禁用)' } } try { const response = await fetch(`${API_BASE}/api/verify`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code }) }) const result = await response.json() as { success: boolean; message?: string } return { success: result.success, message: result.message || (result.success ? '激活码有效' : '激活码无效') } } catch (e) { return { success: false, message: '网络错误,请检查网络连接' } } } /** * 激活设备 */ async activate(code: string): Promise { // 如果激活功能被禁用,直接返回成功 if (!ACTIVATION_ENABLED) { // 清除缓存,强制刷新状态 this.cachedStatus = null this.lastCheckTime = 0 return { success: true, message: '激活成功(功能已禁用)', data: { type: 'permanent', expires_at: null, activated_at: new Date().toISOString() } } } try { const deviceId = this.getDeviceId() const response = await fetch(`${API_BASE}/api/activate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code, device_id: deviceId }) }) const result = await response.json() as { success: boolean message?: string data?: { type: string expires_at: string | null activated_at: string } } if (result.success && result.data) { // 保存激活信息到本地(加密存储) await this.saveActivationData({ code, deviceId, type: result.data.type, expiresAt: result.data.expires_at, activatedAt: result.data.activated_at, timestamp: Date.now() }) // 清除缓存 this.cachedStatus = null this.lastCheckTime = 0 return { success: true, message: '激活成功', data: result.data } } return { success: false, message: result.message || '激活失败' } } catch (e) { return { success: false, message: '网络错误,请检查网络连接' } } } /** * 检查激活状态 */ async checkActivation(): Promise { const deviceId = this.getDeviceId() // 如果激活功能被禁用,直接返回已激活状态 if (!ACTIVATION_ENABLED) { const status: ActivationStatus = { isActivated: true, type: 'permanent', expiresAt: null, activatedAt: new Date().toISOString(), daysRemaining: null, deviceId } this.cachedStatus = status this.lastCheckTime = Date.now() return status } // 使用缓存 if (this.cachedStatus && Date.now() - this.lastCheckTime < this.CHECK_INTERVAL) { return this.cachedStatus } // 1. 先检查本地存储 const localData = await this.loadActivationData() if (!localData) { return this.createInactiveStatus(deviceId) } // 2. 验证设备ID if (localData.deviceId !== deviceId) { // 设备ID不匹配,可能是复制的数据 await this.clearActivationData() return this.createInactiveStatus(deviceId) } // 3. 检查本地过期时间 if (localData.expiresAt) { const expiresDate = new Date(localData.expiresAt) const now = new Date() if (now > expiresDate) { // 本地已过期 const status: ActivationStatus = { isActivated: false, type: localData.type, expiresAt: localData.expiresAt, activatedAt: localData.activatedAt, daysRemaining: 0, deviceId } this.cachedStatus = status this.lastCheckTime = Date.now() return status } } // 4. 在线验证(每次启动或间隔时间后) try { const response = await fetch(`${API_BASE}/api/check`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ device_id: deviceId }) }) const result = await response.json() as { success: boolean message?: string data?: { type: string expires_at: string | null activated_at: string } } if (result.success && result.data) { // 更新本地数据 await this.saveActivationData({ ...localData, type: result.data.type, expiresAt: result.data.expires_at, activatedAt: result.data.activated_at, timestamp: Date.now() }) const status = this.createActiveStatus(deviceId, result.data) this.cachedStatus = status this.lastCheckTime = Date.now() return status } else { // 服务器说未激活或已过期 if (result.message === '激活已过期') { const status: ActivationStatus = { isActivated: false, type: localData.type, expiresAt: localData.expiresAt, activatedAt: localData.activatedAt, daysRemaining: 0, deviceId } this.cachedStatus = status this.lastCheckTime = Date.now() return status } // 设备未激活 await this.clearActivationData() return this.createInactiveStatus(deviceId) } } catch { // 网络错误,使用本地数据(离线模式) const status = this.createActiveStatus(deviceId, { type: localData.type, expires_at: localData.expiresAt, activated_at: localData.activatedAt }) this.cachedStatus = status this.lastCheckTime = Date.now() return status } } /** * 创建未激活状态 */ private createInactiveStatus(deviceId: string): ActivationStatus { return { isActivated: false, type: null, expiresAt: null, activatedAt: null, daysRemaining: null, deviceId } } /** * 创建已激活状态 */ private createActiveStatus(deviceId: string, data: { type: string expires_at: string | null activated_at: string }): ActivationStatus { let daysRemaining: number | null = null if (data.expires_at) { const expiresDate = new Date(data.expires_at) const now = new Date() const diffTime = expiresDate.getTime() - now.getTime() daysRemaining = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) if (daysRemaining < 0) daysRemaining = 0 } return { isActivated: true, type: data.type, expiresAt: data.expires_at, activatedAt: data.activated_at, daysRemaining, deviceId } } /** * 保存激活数据(加密) */ private async saveActivationData(data: { code: string deviceId: string type: string expiresAt: string | null activatedAt: string timestamp: number }): Promise { try { const { ConfigService } = await import('./config') const configService = new ConfigService() // 添加签名 const signature = this.generateSignature(data) const dataWithSig = { ...data, sig: signature } // 加密存储 const encrypted = this.encrypt(JSON.stringify(dataWithSig)) configService.set('activationData' as any, encrypted) configService.close() } catch (e) { console.error('保存激活数据失败:', e) } } /** * 加载激活数据(解密) */ private async loadActivationData(): Promise<{ code: string deviceId: string type: string expiresAt: string | null activatedAt: string timestamp: number } | null> { try { const { ConfigService } = await import('./config') const configService = new ConfigService() const encrypted = configService.get('activationData' as any) as string configService.close() if (!encrypted) return null const decrypted = this.decrypt(encrypted) if (!decrypted) return null const data = JSON.parse(decrypted) // 验证签名 const { sig, ...dataWithoutSig } = data if (!this.verifySignature(dataWithoutSig, sig)) { console.error('激活数据签名验证失败') return null } return dataWithoutSig } catch (e) { console.error('加载激活数据失败:', e) return null } } /** * 清除激活数据 */ private async clearActivationData(): Promise { try { const { ConfigService } = await import('./config') const configService = new ConfigService() configService.set('activationData' as any, '') configService.close() } catch (e) { console.error('清除激活数据失败:', e) } } /** * 获取激活类型显示名称 */ getTypeDisplayName(type: string | null): string { if (!type) return '未激活' const typeMap: Record = { '30days': '30天试用版', '90days': '90天标准版', '365days': '365天专业版', 'permanent': '永久版' } return typeMap[type] || type } /** * 清除缓存(用于强制刷新) */ clearCache(): void { this.cachedStatus = null this.lastCheckTime = 0 } } export const activationService = new ActivationService()