Compare commits
7 Commits
bd6b6be7ea
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d95cee3c28 | ||
|
|
a123c7cc40 | ||
|
|
da337158b0 | ||
|
|
f62d7ff687 | ||
|
|
10028a0e2e | ||
|
|
af927dd9b4 | ||
|
|
5ed65090b9 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"savedAt": "2026-02-14T07:46:12.968Z",
|
"savedAt": "2026-02-18T13:28:54.762Z",
|
||||||
"users": [
|
"users": [
|
||||||
{
|
{
|
||||||
"id": "admin_1762534368537",
|
"id": "admin_1762534368537",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"passwordHash": "$2b$10$3c/70RbBH4y7zhYwxk8ldOcls3Bj6kt3cSMidTeaMUVb1EJXH4GMy",
|
"passwordHash": "$2b$10$3c/70RbBH4y7zhYwxk8ldOcls3Bj6kt3cSMidTeaMUVb1EJXH4GMy",
|
||||||
"role": "superadmin",
|
"role": "superadmin",
|
||||||
"createdAt": "2025-11-07T16:53:46.677Z",
|
"createdAt": "2025-11-07T16:53:46.677Z",
|
||||||
"lastLoginAt": "2026-02-14T07:46:12.968Z"
|
"lastLoginAt": "2026-02-18T13:28:54.762Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# 🏗️ 服务端架构优化方案
|
# 服务端架构优化方案
|
||||||
|
|
||||||
## 当前架构 vs 优化后架构
|
## 当前架构 vs 优化后架构
|
||||||
|
|
||||||
@@ -124,9 +124,9 @@ Socket.IO发送
|
|||||||
↓
|
↓
|
||||||
Web客户端接收
|
Web客户端接收
|
||||||
|
|
||||||
⏱️ 延迟: 150ms
|
延迟: 150ms
|
||||||
📊 吞吐: 500msg/s
|
吞吐: 500msg/s
|
||||||
💾 内存: 400MB
|
内存: 400MB
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 优化后
|
#### 优化后
|
||||||
@@ -159,9 +159,9 @@ Socket.IO发送
|
|||||||
↓
|
↓
|
||||||
Web客户端接收
|
Web客户端接收
|
||||||
|
|
||||||
⏱️ 延迟: 80ms (↓47%)
|
延迟: 80ms (↓47%)
|
||||||
📊 吞吐: 1500msg/s (↑200%)
|
吞吐: 1500msg/s (↑200%)
|
||||||
💾 内存: 250MB (↓37%)
|
内存: 250MB (↓37%)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -203,10 +203,10 @@ Web客户端接收
|
|||||||
0 1 2 3 4
|
0 1 2 3 4
|
||||||
|
|
||||||
优化:
|
优化:
|
||||||
✅ 定期清理过期缓冲区 (5秒)
|
定期清理过期缓冲区 (5秒)
|
||||||
✅ 自动清理空闲连接 (5分钟)
|
自动清理空闲连接 (5分钟)
|
||||||
✅ 缓存自动过期 (1分钟)
|
缓存自动过期 (1分钟)
|
||||||
✅ 紧急清理机制 (>500MB)
|
紧急清理机制 (>500MB)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -243,10 +243,10 @@ Web客户端接收
|
|||||||
└─ Socket6 (空闲) - 最后活动: 10分钟前
|
└─ Socket6 (空闲) - 最后活动: 10分钟前
|
||||||
|
|
||||||
优化:
|
优化:
|
||||||
✅ 优先级队列管理
|
优先级队列管理
|
||||||
✅ LRU驱逐策略
|
LRU驱逐策略
|
||||||
✅ 自动清理空闲连接
|
自动清理空闲连接
|
||||||
✅ 支持1000+并发
|
支持1000+并发
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -309,8 +309,8 @@ Web客户端接收
|
|||||||
...
|
...
|
||||||
消息10 → Socket.IO发送 → 网络传输 → 客户端接收
|
消息10 → Socket.IO发送 → 网络传输 → 客户端接收
|
||||||
|
|
||||||
⏱️ 总时间: 10 × 网络延迟 = 150ms
|
总时间: 10 × 网络延迟 = 150ms
|
||||||
📊 Socket.IO调用: 10次
|
Socket.IO调用: 10次
|
||||||
```
|
```
|
||||||
|
|
||||||
### 批量消息处理 (优化后)
|
### 批量消息处理 (优化后)
|
||||||
@@ -321,8 +321,8 @@ Web客户端接收
|
|||||||
... │
|
... │
|
||||||
消息10┘
|
消息10┘
|
||||||
|
|
||||||
⏱️ 总时间: 1 × 网络延迟 + 50ms = 80ms
|
总时间: 1 × 网络延迟 + 50ms = 80ms
|
||||||
📊 Socket.IO调用: 1次 (减少90%)
|
Socket.IO调用: 1次 (减少90%)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -334,7 +334,7 @@ Web客户端接收
|
|||||||
Web客户端请求设备信息
|
Web客户端请求设备信息
|
||||||
↓
|
↓
|
||||||
OptimizationService.getCachedQuery('device:123')
|
OptimizationService.getCachedQuery('device:123')
|
||||||
├─ 缓存命中 (< 1分钟) → 直接返回 ✅ 快速
|
├─ 缓存命中 (< 1分钟) → 直接返回 快速
|
||||||
└─ 缓存未命中或过期
|
└─ 缓存未命中或过期
|
||||||
↓
|
↓
|
||||||
DatabaseService.getDeviceById('123')
|
DatabaseService.getDeviceById('123')
|
||||||
@@ -438,10 +438,10 @@ OptimizationService.getCachedQuery('device:123')
|
|||||||
┌─────────────────────────────────┐
|
┌─────────────────────────────────┐
|
||||||
│ 基础优化完成 │
|
│ 基础优化完成 │
|
||||||
├─────────────────────────────────┤
|
├─────────────────────────────────┤
|
||||||
│ ✅ 消息批处理 │
|
│ 消息批处理 │
|
||||||
│ ✅ 连接池管理 │
|
│ 连接池管理 │
|
||||||
│ ✅ 性能监控 │
|
│ 性能监控 │
|
||||||
│ 📊 预期: 延迟↓30%, 吞吐↑100% │
|
│ 预期: 延迟↓30%, 吞吐↑100% │
|
||||||
└─────────────────────────────────┘
|
└─────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -450,10 +450,10 @@ OptimizationService.getCachedQuery('device:123')
|
|||||||
┌─────────────────────────────────┐
|
┌─────────────────────────────────┐
|
||||||
│ 中级优化 │
|
│ 中级优化 │
|
||||||
├─────────────────────────────────┤
|
├─────────────────────────────────┤
|
||||||
│ 🔄 Redis缓存 │
|
│ Redis缓存 │
|
||||||
│ 🔄 消息队列 (Bull) │
|
│ 消息队列 (Bull) │
|
||||||
│ 🔄 数据库连接池 │
|
│ 数据库连接池 │
|
||||||
│ 📊 预期: 延迟↓50%, 吞吐↑200% │
|
│ 预期: 延迟↓50%, 吞吐↑200% │
|
||||||
└─────────────────────────────────┘
|
└─────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -462,10 +462,10 @@ OptimizationService.getCachedQuery('device:123')
|
|||||||
┌─────────────────────────────────┐
|
┌─────────────────────────────────┐
|
||||||
│ 高级优化 │
|
│ 高级优化 │
|
||||||
├─────────────────────────────────┤
|
├─────────────────────────────────┤
|
||||||
│ 🚀 分布式架构 │
|
│ 分布式架构 │
|
||||||
│ 🚀 负载均衡 │
|
│ 负载均衡 │
|
||||||
│ 🚀 CDN支持 │
|
│ CDN支持 │
|
||||||
│ 📊 预期: 延迟↓60%, 吞吐↑300% │
|
│ 预期: 延迟↓60%, 吞吐↑300% │
|
||||||
└─────────────────────────────────┘
|
└─────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
# 服务端性能优化指南
|
# 服务端性能优化指南
|
||||||
|
|
||||||
## 🎯 优化目标
|
## 优化目标
|
||||||
- 降低延迟 (< 100ms)
|
- 降低延迟 (< 100ms)
|
||||||
- 提高吞吐量 (支持100+并发设备)
|
- 提高吞吐量 (支持100+并发设备)
|
||||||
- 减少内存占用 (< 300MB)
|
- 减少内存占用 (< 300MB)
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1️⃣ 立即可实施的优化 (高优先级)
|
## 1⃣ 立即可实施的优化 (高优先级)
|
||||||
|
|
||||||
### 1.1 启用消息批处理
|
### 1.1 启用消息批处理
|
||||||
**问题**: 每条消息单独发送,频繁的Socket.IO调用
|
**问题**: 每条消息单独发送,频繁的Socket.IO调用
|
||||||
@@ -151,7 +151,7 @@ export class ConnectionPool {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2️⃣ 中期优化 (1-2周)
|
## 2⃣ 中期优化 (1-2周)
|
||||||
|
|
||||||
### 2.1 实现消息队列
|
### 2.1 实现消息队列
|
||||||
**问题**: 高并发时消息丢失
|
**问题**: 高并发时消息丢失
|
||||||
@@ -301,7 +301,7 @@ export class MetricsService {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3️⃣ 长期优化 (1个月+)
|
## 3⃣ 长期优化 (1个月+)
|
||||||
|
|
||||||
### 3.1 实现分布式架构
|
### 3.1 实现分布式架构
|
||||||
**方案**: 使用Socket.IO Adapter支持多服务器
|
**方案**: 使用Socket.IO Adapter支持多服务器
|
||||||
@@ -363,7 +363,7 @@ async uploadScreenshotToCDN(deviceId: string, data: Buffer) {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4️⃣ 性能测试和监控
|
## 4⃣ 性能测试和监控
|
||||||
|
|
||||||
### 4.1 添加性能测试
|
### 4.1 添加性能测试
|
||||||
```bash
|
```bash
|
||||||
@@ -401,7 +401,7 @@ private logPerformanceMetrics() {
|
|||||||
const uptime = process.uptime()
|
const uptime = process.uptime()
|
||||||
|
|
||||||
this.logger.info(`
|
this.logger.info(`
|
||||||
📊 性能指标:
|
性能指标:
|
||||||
- 内存: ${Math.round(memUsage.heapUsed / 1024 / 1024)}MB / ${Math.round(memUsage.heapTotal / 1024 / 1024)}MB
|
- 内存: ${Math.round(memUsage.heapUsed / 1024 / 1024)}MB / ${Math.round(memUsage.heapTotal / 1024 / 1024)}MB
|
||||||
- 运行时间: ${Math.round(uptime)}s
|
- 运行时间: ${Math.round(uptime)}s
|
||||||
- 屏幕帧: ${this.routedFrames} (丢帧: ${this.droppedFrames})
|
- 屏幕帧: ${this.routedFrames} (丢帧: ${this.droppedFrames})
|
||||||
@@ -414,7 +414,7 @@ private logPerformanceMetrics() {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5️⃣ 配置建议
|
## 5⃣ 配置建议
|
||||||
|
|
||||||
### 生产环境启动参数
|
### 生产环境启动参数
|
||||||
```bash
|
```bash
|
||||||
@@ -444,7 +444,7 @@ REDIS_PORT=6379
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📈 预期改进
|
## 预期改进
|
||||||
|
|
||||||
| 指标 | 优化前 | 优化后 | 改进 |
|
| 指标 | 优化前 | 优化后 | 改进 |
|
||||||
|------|-------|-------|------|
|
|------|-------|-------|------|
|
||||||
@@ -456,7 +456,7 @@ REDIS_PORT=6379
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔍 故障排查
|
## 故障排查
|
||||||
|
|
||||||
### 问题: 内存持续增长
|
### 问题: 内存持续增长
|
||||||
**解决方案**:
|
**解决方案**:
|
||||||
@@ -478,7 +478,7 @@ REDIS_PORT=6379
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 参考资源
|
## 参考资源
|
||||||
|
|
||||||
- [Socket.IO性能优化](https://socket.io/docs/v4/performance-tuning/)
|
- [Socket.IO性能优化](https://socket.io/docs/v4/performance-tuning/)
|
||||||
- [Node.js内存管理](https://nodejs.org/en/docs/guides/simple-profiling/)
|
- [Node.js内存管理](https://nodejs.org/en/docs/guides/simple-profiling/)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 📊 服务端优化方案总结
|
# 服务端优化方案总结
|
||||||
|
|
||||||
## 🎯 优化目标
|
## 优化目标
|
||||||
- 降低延迟: 150ms → 80ms (↓47%)
|
- 降低延迟: 150ms → 80ms (↓47%)
|
||||||
- 提高吞吐: 500msg/s → 1500msg/s (↑200%)
|
- 提高吞吐: 500msg/s → 1500msg/s (↑200%)
|
||||||
- 减少内存: 400MB → 250MB (↓37%)
|
- 减少内存: 400MB → 250MB (↓37%)
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 已创建的优化模块
|
## 已创建的优化模块
|
||||||
|
|
||||||
### 1. OptimizationService (消息批处理和缓存)
|
### 1. OptimizationService (消息批处理和缓存)
|
||||||
**文件**: `src/services/OptimizationService.ts`
|
**文件**: `src/services/OptimizationService.ts`
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔧 快速集成步骤
|
## 快速集成步骤
|
||||||
|
|
||||||
### 步骤1: 导入服务
|
### 步骤1: 导入服务
|
||||||
```typescript
|
```typescript
|
||||||
@@ -131,7 +131,7 @@ app.get('/api/performance', (req, res) => {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📈 性能改进预期
|
## 性能改进预期
|
||||||
|
|
||||||
### 消息处理
|
### 消息处理
|
||||||
- **批处理**: 10条消息一起发送 → Socket.IO调用减少90%
|
- **批处理**: 10条消息一起发送 → Socket.IO调用减少90%
|
||||||
@@ -155,7 +155,7 @@ app.get('/api/performance', (req, res) => {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 优化路线图
|
## 优化路线图
|
||||||
|
|
||||||
### Phase 1: 基础优化 (立即实施)
|
### Phase 1: 基础优化 (立即实施)
|
||||||
- [x] 创建OptimizationService
|
- [x] 创建OptimizationService
|
||||||
@@ -185,26 +185,26 @@ app.get('/api/performance', (req, res) => {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 监控仪表板
|
## 监控仪表板
|
||||||
|
|
||||||
### 关键指标
|
### 关键指标
|
||||||
```
|
```
|
||||||
📊 实时性能指标
|
实时性能指标
|
||||||
├─ 💾 内存: 250MB / 512MB (48%)
|
├─ 内存: 250MB / 512MB (48%)
|
||||||
├─ 📨 消息: 1200/s | 延迟: 85ms (p95: 150ms, p99: 250ms)
|
├─ 消息: 1200/s | 延迟: 85ms (p95: 150ms, p99: 250ms)
|
||||||
├─ 🔌 连接: 150个 (活跃: 140, 空闲: 10)
|
├─ 连接: 150个 (活跃: 140, 空闲: 10)
|
||||||
└─ ⚙️ 系统: 运行时间 24h | CPU: 35% | 事件循环延迟: 5ms
|
└─ 系统: 运行时间 24h | CPU: 35% | 事件循环延迟: 5ms
|
||||||
```
|
```
|
||||||
|
|
||||||
### 告警规则
|
### 告警规则
|
||||||
- ⚠️ 内存使用 > 80% → 触发紧急清理
|
- 内存使用 > 80% → 触发紧急清理
|
||||||
- ⚠️ 消息延迟 P99 > 500ms → 检查网络/CPU
|
- 消息延迟 P99 > 500ms → 检查网络/CPU
|
||||||
- ⚠️ 错误率 > 5% → 检查设备连接
|
- 错误率 > 5% → 检查设备连接
|
||||||
- ⚠️ 事件循环延迟 > 100ms → 检查同步操作
|
- 事件循环延迟 > 100ms → 检查同步操作
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔍 性能测试
|
## 性能测试
|
||||||
|
|
||||||
### 测试场景
|
### 测试场景
|
||||||
```
|
```
|
||||||
@@ -239,7 +239,7 @@ node --prof-process isolate-*.log > profile.txt
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 文档参考
|
## 文档参考
|
||||||
|
|
||||||
### 已创建的文档
|
### 已创建的文档
|
||||||
1. **OPTIMIZATION_GUIDE.md** - 详细优化指南 (5个优化阶段)
|
1. **OPTIMIZATION_GUIDE.md** - 详细优化指南 (5个优化阶段)
|
||||||
@@ -253,7 +253,7 @@ node --prof-process isolate-*.log > profile.txt
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✅ 检查清单
|
## 检查清单
|
||||||
|
|
||||||
### 集成前检查
|
### 集成前检查
|
||||||
- [ ] 已阅读QUICK_OPTIMIZATION.md
|
- [ ] 已阅读QUICK_OPTIMIZATION.md
|
||||||
@@ -283,7 +283,7 @@ node --prof-process isolate-*.log > profile.txt
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 💡 最佳实践
|
## 最佳实践
|
||||||
|
|
||||||
### 消息处理
|
### 消息处理
|
||||||
1. 使用批处理减少Socket.IO调用
|
1. 使用批处理减少Socket.IO调用
|
||||||
@@ -307,7 +307,7 @@ node --prof-process isolate-*.log > profile.txt
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎓 学习资源
|
## 学习资源
|
||||||
|
|
||||||
### 推荐阅读
|
### 推荐阅读
|
||||||
1. Node.js官方性能指南
|
1. Node.js官方性能指南
|
||||||
@@ -326,7 +326,7 @@ node --prof-process isolate-*.log > profile.txt
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📞 支持和反馈
|
## 支持和反馈
|
||||||
|
|
||||||
### 常见问题
|
### 常见问题
|
||||||
**Q: 如何验证优化效果?**
|
**Q: 如何验证优化效果?**
|
||||||
@@ -348,14 +348,14 @@ A: 在初始化时不创建对应的服务实例
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎉 总结
|
## 总结
|
||||||
|
|
||||||
通过实施这套优化方案,你的服务端将获得:
|
通过实施这套优化方案,你的服务端将获得:
|
||||||
|
|
||||||
✅ **47%的延迟降低** - 用户体验更流畅
|
**47%的延迟降低** - 用户体验更流畅
|
||||||
✅ **200%的吞吐提升** - 支持更多并发
|
**200%的吞吐提升** - 支持更多并发
|
||||||
✅ **37%的内存优化** - 资源利用更高效
|
**37%的内存优化** - 资源利用更高效
|
||||||
✅ **42%的CPU降低** - 成本更低
|
**42%的CPU降低** - 成本更低
|
||||||
✅ **80%的丢帧率降低** - 画面更稳定
|
**80%的丢帧率降低** - 画面更稳定
|
||||||
|
|
||||||
**立即开始**: 按照QUICK_OPTIMIZATION.md中的步骤集成优化服务!
|
**立即开始**: 按照QUICK_OPTIMIZATION.md中的步骤集成优化服务!
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 🚀 快速优化指南
|
# 快速优化指南
|
||||||
|
|
||||||
## 立即可用的优化服务
|
## 立即可用的优化服务
|
||||||
|
|
||||||
@@ -21,9 +21,9 @@ console.log(`队列消息: ${stats.totalQueuedMessages}`)
|
|||||||
```
|
```
|
||||||
|
|
||||||
**优势**:
|
**优势**:
|
||||||
- ✅ 减少Socket.IO调用次数 (最多10倍)
|
- 减少Socket.IO调用次数 (最多10倍)
|
||||||
- ✅ 降低网络往返延迟
|
- 降低网络往返延迟
|
||||||
- ✅ 自动缓存热数据
|
- 自动缓存热数据
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -46,9 +46,9 @@ console.log(`总数据传输: ${stats.totalDataTransferred}MB`)
|
|||||||
```
|
```
|
||||||
|
|
||||||
**优势**:
|
**优势**:
|
||||||
- ✅ 自动管理连接生命周期
|
- 自动管理连接生命周期
|
||||||
- ✅ 优先级队列防止低优先级连接占用资源
|
- 优先级队列防止低优先级连接占用资源
|
||||||
- ✅ 自动清理空闲连接
|
- 自动清理空闲连接
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -74,9 +74,9 @@ const warnings = monitor.getPerformanceWarnings()
|
|||||||
```
|
```
|
||||||
|
|
||||||
**优势**:
|
**优势**:
|
||||||
- ✅ 实时性能监控
|
- 实时性能监控
|
||||||
- ✅ 自动告警
|
- 自动告警
|
||||||
- ✅ 详细的性能报告
|
- 详细的性能报告
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
BIN
devices.db
BIN
devices.db
Binary file not shown.
60
dist/server.js
vendored
60
dist/server.js
vendored
@@ -22,27 +22,27 @@ app.use((0, cors_1.default)({
|
|||||||
credentials: true
|
credentials: true
|
||||||
}));
|
}));
|
||||||
app.use(express_1.default.json());
|
app.use(express_1.default.json());
|
||||||
// ✅ Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
//Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
||||||
const io = new socket_io_1.Server(server, {
|
const io = new socket_io_1.Server(server, {
|
||||||
cors: {
|
cors: {
|
||||||
origin: "*",
|
origin: "*",
|
||||||
methods: ["GET", "POST"],
|
methods: ["GET", "POST"],
|
||||||
credentials: true
|
credentials: true
|
||||||
},
|
},
|
||||||
// 🔧 心跳机制优化(v4已解决心跳方向问题)
|
//心跳机制优化(v4已解决心跳方向问题)
|
||||||
pingInterval: 25000, // 25秒发送一次ping(服务器->客户端)
|
pingInterval: 25000, // 25秒发送一次ping(服务器->客户端)
|
||||||
pingTimeout: 60000, // 60秒等待pong响应
|
pingTimeout: 60000, // 60秒等待pong响应
|
||||||
upgradeTimeout: 30000, // 30秒传输升级超时
|
upgradeTimeout: 30000, // 30秒传输升级超时
|
||||||
// 🔧 传输优化
|
//传输优化
|
||||||
transports: ['websocket', 'polling'],
|
transports: ['websocket', 'polling'],
|
||||||
allowEIO3: false, // 不支持旧版本协议
|
allowEIO3: false, // 不支持旧版本协议
|
||||||
// 🔧 缓冲区和数据包优化
|
//缓冲区和数据包优化
|
||||||
maxHttpBufferSize: 10e6, // 10MB缓冲区
|
maxHttpBufferSize: 10e6, // 10MB缓冲区
|
||||||
allowUpgrades: true,
|
allowUpgrades: true,
|
||||||
// 🔧 连接管理
|
//连接管理
|
||||||
connectTimeout: 45000, // 连接超时
|
connectTimeout: 45000, // 连接超时
|
||||||
serveClient: false, // 不提供客户端文件
|
serveClient: false, // 不提供客户端文件
|
||||||
// 🔧 Engine.IO 配置
|
//Engine.IO 配置
|
||||||
cookie: {
|
cookie: {
|
||||||
name: "io",
|
name: "io",
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
@@ -69,12 +69,12 @@ app.get('/health', (req, res) => {
|
|||||||
});
|
});
|
||||||
// Socket.IO连接处理
|
// Socket.IO连接处理
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
logger.info(`🔌 新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
logger.info(`新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
||||||
// 连接质量监控
|
// 连接质量监控
|
||||||
const connectionStart = Date.now();
|
const connectionStart = Date.now();
|
||||||
// 设备注册事件
|
// 设备注册事件
|
||||||
socket.on('device_register', (deviceInfo) => {
|
socket.on('device_register', (deviceInfo) => {
|
||||||
logger.info(`📱 设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
logger.info(`设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
||||||
const device = {
|
const device = {
|
||||||
id: deviceInfo.deviceId,
|
id: deviceInfo.deviceId,
|
||||||
socketId: socket.id,
|
socketId: socket.id,
|
||||||
@@ -96,7 +96,7 @@ io.on('connection', (socket) => {
|
|||||||
const socket = io.sockets.sockets.get(client.socketId);
|
const socket = io.sockets.sockets.get(client.socketId);
|
||||||
return socket && socket.connected;
|
return socket && socket.connected;
|
||||||
}).length;
|
}).length;
|
||||||
logger.info(`📢 通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
logger.info(`通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
||||||
webClientManager.broadcastToAll('device_connected', {
|
webClientManager.broadcastToAll('device_connected', {
|
||||||
device: deviceManager.getDevice(deviceInfo.deviceId)
|
device: deviceManager.getDevice(deviceInfo.deviceId)
|
||||||
});
|
});
|
||||||
@@ -104,7 +104,7 @@ io.on('connection', (socket) => {
|
|||||||
});
|
});
|
||||||
// Web客户端注册事件
|
// Web客户端注册事件
|
||||||
socket.on('web_client_register', (clientInfo) => {
|
socket.on('web_client_register', (clientInfo) => {
|
||||||
logger.info(`🌐 Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
logger.info(`Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
||||||
const clientData = {
|
const clientData = {
|
||||||
id: socket.id,
|
id: socket.id,
|
||||||
socketId: socket.id,
|
socketId: socket.id,
|
||||||
@@ -151,41 +151,41 @@ io.on('connection', (socket) => {
|
|||||||
});
|
});
|
||||||
// 测试连接监听器
|
// 测试连接监听器
|
||||||
socket.on('CONNECTION_TEST', (data) => {
|
socket.on('CONNECTION_TEST', (data) => {
|
||||||
logger.info(`🧪🧪🧪 收到连接测试: ${JSON.stringify(data)}`);
|
logger.info(`收到连接测试: ${JSON.stringify(data)}`);
|
||||||
// 🔧 修复:回复确认消息给Android端,避免心跳失败累积
|
//修复:回复确认消息给Android端,避免心跳失败累积
|
||||||
try {
|
try {
|
||||||
socket.emit('CONNECTION_TEST_RESPONSE', {
|
socket.emit('CONNECTION_TEST_RESPONSE', {
|
||||||
success: true,
|
success: true,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
receivedData: data
|
receivedData: data
|
||||||
});
|
});
|
||||||
logger.debug(`✅ 已回复CONNECTION_TEST确认消息`);
|
logger.debug(`已回复CONNECTION_TEST确认消息`);
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
logger.error(`❌ 回复CONNECTION_TEST失败:`, error);
|
logger.error(`回复CONNECTION_TEST失败:`, error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 简单测试事件监听器
|
// 简单测试事件监听器
|
||||||
socket.on('SIMPLE_TEST_EVENT', (data) => {
|
socket.on('SIMPLE_TEST_EVENT', (data) => {
|
||||||
logger.info(`🧪🧪🧪 收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
logger.info(`收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
||||||
});
|
});
|
||||||
// 调试:UI响应前的测试消息
|
// 调试:UI响应前的测试消息
|
||||||
socket.on('debug_test_before_ui', (data) => {
|
socket.on('debug_test_before_ui', (data) => {
|
||||||
logger.info(`🧪🧪🧪 收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
logger.info(`收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
||||||
logger.info(`🧪 测试数据: ${JSON.stringify(data)}`);
|
logger.info(`测试数据: ${JSON.stringify(data)}`);
|
||||||
});
|
});
|
||||||
// 简单测试消息监听器
|
// 简单测试消息监听器
|
||||||
socket.on('simple_test', (data) => {
|
socket.on('simple_test', (data) => {
|
||||||
logger.info(`🧪🧪🧪 收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
logger.info(`收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
||||||
});
|
});
|
||||||
// UI层次结构响应 (设备端响应)
|
// UI层次结构响应 (设备端响应)
|
||||||
socket.on('ui_hierarchy_response', (data) => {
|
socket.on('ui_hierarchy_response', (data) => {
|
||||||
logger.info(`📱📱📱 [GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
logger.info(`[GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
||||||
logger.info(`📋 响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
logger.info(`响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
||||||
logger.info(`📋 完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
logger.info(`完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
||||||
// ✅ 参考screen_data的处理方式,直接调用专用路由方法
|
//参考screen_data的处理方式,直接调用专用路由方法
|
||||||
const routeResult = messageRouter.routeUIHierarchyResponse(socket.id, data);
|
const routeResult = messageRouter.routeUIHierarchyResponse(socket.id, data);
|
||||||
logger.info(`📤 UI层次结构路由结果: ${routeResult}`);
|
logger.info(`UI层次结构路由结果: ${routeResult}`);
|
||||||
});
|
});
|
||||||
// 设备控制请求
|
// 设备控制请求
|
||||||
socket.on('request_device_control', (data) => {
|
socket.on('request_device_control', (data) => {
|
||||||
@@ -210,11 +210,11 @@ io.on('connection', (socket) => {
|
|||||||
});
|
});
|
||||||
// 🆕 权限申请响应(设备端响应)
|
// 🆕 权限申请响应(设备端响应)
|
||||||
socket.on('permission_response', (data) => {
|
socket.on('permission_response', (data) => {
|
||||||
logger.info(`📱 收到设备权限申请响应: Socket: ${socket.id}`);
|
logger.info(`收到设备权限申请响应: Socket: ${socket.id}`);
|
||||||
logger.info(`📋 响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
logger.info(`响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
||||||
// 路由权限申请响应
|
// 路由权限申请响应
|
||||||
const routeResult = messageRouter.routePermissionResponse(socket.id, data);
|
const routeResult = messageRouter.routePermissionResponse(socket.id, data);
|
||||||
logger.info(`📤 权限申请响应路由结果: ${routeResult}`);
|
logger.info(`权限申请响应路由结果: ${routeResult}`);
|
||||||
});
|
});
|
||||||
// 调试:捕获所有未处理的事件
|
// 调试:捕获所有未处理的事件
|
||||||
const originalEmit = socket.emit;
|
const originalEmit = socket.emit;
|
||||||
@@ -226,8 +226,8 @@ io.on('connection', (socket) => {
|
|||||||
}
|
}
|
||||||
// 特别关注UI层次结构响应
|
// 特别关注UI层次结构响应
|
||||||
if (eventName === 'ui_hierarchy_response') {
|
if (eventName === 'ui_hierarchy_response') {
|
||||||
logger.info(`📱📱📱 收到UI层次结构响应!!! 事件名: ${eventName}`);
|
logger.info(`收到UI层次结构响应!!! 事件名: ${eventName}`);
|
||||||
logger.info(`📋 响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
logger.info(`响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
socket.on('disconnect', (reason) => {
|
socket.on('disconnect', (reason) => {
|
||||||
@@ -239,7 +239,7 @@ io.on('connection', (socket) => {
|
|||||||
// 移除设备或Web客户端
|
// 移除设备或Web客户端
|
||||||
const device = deviceManager.getDeviceBySocketId(socket.id);
|
const device = deviceManager.getDeviceBySocketId(socket.id);
|
||||||
if (device) {
|
if (device) {
|
||||||
logger.info(`📱 设备断开: ${device.name} (${device.id})`);
|
logger.info(`设备断开: ${device.name} (${device.id})`);
|
||||||
deviceManager.removeDevice(device.id);
|
deviceManager.removeDevice(device.id);
|
||||||
// 通知所有Web客户端设备已断开
|
// 通知所有Web客户端设备已断开
|
||||||
webClientManager.broadcastToAll('device_disconnected', {
|
webClientManager.broadcastToAll('device_disconnected', {
|
||||||
@@ -250,7 +250,7 @@ io.on('connection', (socket) => {
|
|||||||
// 可能是Web客户端断开
|
// 可能是Web客户端断开
|
||||||
const clientRemoved = webClientManager.removeClientBySocketId(socket.id);
|
const clientRemoved = webClientManager.removeClientBySocketId(socket.id);
|
||||||
if (clientRemoved) {
|
if (clientRemoved) {
|
||||||
logger.info(`🌐 Web客户端断开: ${socket.id}`);
|
logger.info(`Web客户端断开: ${socket.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
492
src/index.ts
492
src/index.ts
File diff suppressed because it is too large
Load Diff
@@ -20,9 +20,9 @@ export interface DeviceInfo {
|
|||||||
status: 'online' | 'offline' | 'busy'
|
status: 'online' | 'offline' | 'busy'
|
||||||
inputBlocked?: boolean
|
inputBlocked?: boolean
|
||||||
isLocked?: boolean // 设备锁屏状态
|
isLocked?: boolean // 设备锁屏状态
|
||||||
remark?: string // 🆕 设备备注
|
remark?: string // 设备备注
|
||||||
publicIP?: string
|
publicIP?: string
|
||||||
// 🆕 新增系统版本信息字段
|
// 新增系统版本信息字段
|
||||||
systemVersionName?: string // 如"Android 11"、"Android 12"
|
systemVersionName?: string // 如"Android 11"、"Android 12"
|
||||||
romType?: string // 如"MIUI"、"ColorOS"、"原生Android"
|
romType?: string // 如"MIUI"、"ColorOS"、"原生Android"
|
||||||
romVersion?: string // 如"MIUI 12.5"、"ColorOS 11.1"
|
romVersion?: string // 如"MIUI 12.5"、"ColorOS 11.1"
|
||||||
@@ -55,14 +55,14 @@ class DeviceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 清理所有设备记录(服务器重启时调用)
|
* 清理所有设备记录(服务器重启时调用)
|
||||||
*/
|
*/
|
||||||
clearAllDevices(): void {
|
clearAllDevices(): void {
|
||||||
const deviceCount = this.devices.size
|
const deviceCount = this.devices.size
|
||||||
this.devices.clear()
|
this.devices.clear()
|
||||||
this.deviceStatuses.clear()
|
this.deviceStatuses.clear()
|
||||||
this.socketToDevice.clear()
|
this.socketToDevice.clear()
|
||||||
this.logger.info(`🧹 已清理所有设备记录: ${deviceCount} 个设备`)
|
this.logger.info(` 已清理所有设备记录: ${deviceCount} 个设备`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ export interface WebClientInfo {
|
|||||||
connectedAt: Date
|
connectedAt: Date
|
||||||
lastSeen: Date
|
lastSeen: Date
|
||||||
controllingDeviceId?: string
|
controllingDeviceId?: string
|
||||||
userId?: string // 🔐 添加用户ID字段
|
userId?: string // 添加用户ID字段
|
||||||
username?: string // 🔐 添加用户名字段
|
username?: string // 添加用户名字段
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,9 +26,9 @@ class WebClientManager {
|
|||||||
private deviceControllers: Map<string, string> = new Map() // deviceId -> clientId
|
private deviceControllers: Map<string, string> = new Map() // deviceId -> clientId
|
||||||
private logger: Logger
|
private logger: Logger
|
||||||
public io?: SocketIOServer
|
public io?: SocketIOServer
|
||||||
private databaseService?: DatabaseService // 🔐 添加数据库服务引用
|
private databaseService?: DatabaseService // 添加数据库服务引用
|
||||||
|
|
||||||
// 🔧 添加请求速率限制 - 防止频繁重复请求
|
// 添加请求速率限制 - 防止频繁重复请求
|
||||||
private requestTimestamps: Map<string, number> = new Map() // "clientId:deviceId" -> timestamp
|
private requestTimestamps: Map<string, number> = new Map() // "clientId:deviceId" -> timestamp
|
||||||
private readonly REQUEST_COOLDOWN = 2000 // 2秒内不允许重复请求(增加冷却时间)
|
private readonly REQUEST_COOLDOWN = 2000 // 2秒内不允许重复请求(增加冷却时间)
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ class WebClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 清理所有客户端记录(服务器重启时调用)
|
* 清理所有客户端记录(服务器重启时调用)
|
||||||
*/
|
*/
|
||||||
clearAllClients(): void {
|
clearAllClients(): void {
|
||||||
const clientCount = this.clients.size
|
const clientCount = this.clients.size
|
||||||
@@ -46,7 +46,7 @@ class WebClientManager {
|
|||||||
this.socketToClient.clear()
|
this.socketToClient.clear()
|
||||||
this.deviceControllers.clear()
|
this.deviceControllers.clear()
|
||||||
this.requestTimestamps.clear()
|
this.requestTimestamps.clear()
|
||||||
this.logger.info(`🧹 已清理所有客户端记录: ${clientCount} 个客户端`)
|
this.logger.info(` 已清理所有客户端记录: ${clientCount} 个客户端`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,10 +60,10 @@ class WebClientManager {
|
|||||||
* 添加Web客户端
|
* 添加Web客户端
|
||||||
*/
|
*/
|
||||||
addClient(clientInfo: WebClientInfo): void {
|
addClient(clientInfo: WebClientInfo): void {
|
||||||
// 🔧 检查是否已有相同Socket ID的客户端记录
|
// 检查是否已有相同Socket ID的客户端记录
|
||||||
const existingClientId = this.socketToClient.get(clientInfo.socketId)
|
const existingClientId = this.socketToClient.get(clientInfo.socketId)
|
||||||
if (existingClientId) {
|
if (existingClientId) {
|
||||||
this.logger.warn(`⚠️ Socket ${clientInfo.socketId} 已有客户端记录 ${existingClientId},清理旧记录`)
|
this.logger.warn(` Socket ${clientInfo.socketId} 已有客户端记录 ${existingClientId},清理旧记录`)
|
||||||
this.removeClient(existingClientId)
|
this.removeClient(existingClientId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class WebClientManager {
|
|||||||
|
|
||||||
// 如果客户端正在控制设备,释放控制权
|
// 如果客户端正在控制设备,释放控制权
|
||||||
if (client.controllingDeviceId) {
|
if (client.controllingDeviceId) {
|
||||||
this.logger.info(`🔓 客户端断开连接,自动释放设备控制权: ${clientId} -> ${client.controllingDeviceId}`)
|
this.logger.info(` 客户端断开连接,自动释放设备控制权: ${clientId} -> ${client.controllingDeviceId}`)
|
||||||
this.releaseDeviceControl(client.controllingDeviceId)
|
this.releaseDeviceControl(client.controllingDeviceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,13 +156,13 @@ class WebClientManager {
|
|||||||
message: string
|
message: string
|
||||||
currentController?: string
|
currentController?: string
|
||||||
} {
|
} {
|
||||||
// 🔧 防止频繁重复请求
|
// 防止频繁重复请求
|
||||||
const requestKey = `${clientId}:${deviceId}`
|
const requestKey = `${clientId}:${deviceId}`
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const lastRequestTime = this.requestTimestamps.get(requestKey) || 0
|
const lastRequestTime = this.requestTimestamps.get(requestKey) || 0
|
||||||
|
|
||||||
if (now - lastRequestTime < this.REQUEST_COOLDOWN) {
|
if (now - lastRequestTime < this.REQUEST_COOLDOWN) {
|
||||||
this.logger.debug(`🚫 请求过于频繁: ${clientId} -> ${deviceId} (间隔${now - lastRequestTime}ms < ${this.REQUEST_COOLDOWN}ms)`)
|
this.logger.debug(` 请求过于频繁: ${clientId} -> ${deviceId} (间隔${now - lastRequestTime}ms < ${this.REQUEST_COOLDOWN}ms)`)
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: '请求过于频繁,请稍后再试'
|
message: '请求过于频繁,请稍后再试'
|
||||||
@@ -172,17 +172,17 @@ class WebClientManager {
|
|||||||
// 获取客户端信息
|
// 获取客户端信息
|
||||||
const client = this.clients.get(clientId)
|
const client = this.clients.get(clientId)
|
||||||
if (!client) {
|
if (!client) {
|
||||||
this.logger.error(`❌ 客户端不存在: ${clientId}`)
|
this.logger.error(` 客户端不存在: ${clientId}`)
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: '客户端不存在'
|
message: '客户端不存在'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 优化:先检查是否是重复请求(已经在控制此设备)
|
// 优化:先检查是否是重复请求(已经在控制此设备)
|
||||||
const currentController = this.deviceControllers.get(deviceId)
|
const currentController = this.deviceControllers.get(deviceId)
|
||||||
if (currentController === clientId) {
|
if (currentController === clientId) {
|
||||||
this.logger.debug(`🔄 客户端 ${clientId} 重复请求控制设备 ${deviceId},已在控制中`)
|
this.logger.debug(` 客户端 ${clientId} 重复请求控制设备 ${deviceId},已在控制中`)
|
||||||
client.lastSeen = new Date()
|
client.lastSeen = new Date()
|
||||||
// 更新请求时间戳,但返回成功(避免频繁日志)
|
// 更新请求时间戳,但返回成功(避免频繁日志)
|
||||||
this.requestTimestamps.set(requestKey, now)
|
this.requestTimestamps.set(requestKey, now)
|
||||||
@@ -198,7 +198,7 @@ class WebClientManager {
|
|||||||
// 检查设备是否被其他客户端控制
|
// 检查设备是否被其他客户端控制
|
||||||
if (currentController && currentController !== clientId) {
|
if (currentController && currentController !== clientId) {
|
||||||
const controllerClient = this.clients.get(currentController)
|
const controllerClient = this.clients.get(currentController)
|
||||||
this.logger.warn(`🚫 设备 ${deviceId} 控制权冲突: 当前控制者 ${currentController}, 请求者 ${clientId}`)
|
this.logger.warn(` 设备 ${deviceId} 控制权冲突: 当前控制者 ${currentController}, 请求者 ${clientId}`)
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: `设备正在被其他客户端控制 (${controllerClient?.ip || 'unknown'})`,
|
message: `设备正在被其他客户端控制 (${controllerClient?.ip || 'unknown'})`,
|
||||||
@@ -208,7 +208,7 @@ class WebClientManager {
|
|||||||
|
|
||||||
// 如果客户端已在控制其他设备,先释放
|
// 如果客户端已在控制其他设备,先释放
|
||||||
if (client.controllingDeviceId && client.controllingDeviceId !== deviceId) {
|
if (client.controllingDeviceId && client.controllingDeviceId !== deviceId) {
|
||||||
this.logger.info(`🔄 客户端 ${clientId} 切换控制设备: ${client.controllingDeviceId} -> ${deviceId}`)
|
this.logger.info(` 客户端 ${clientId} 切换控制设备: ${client.controllingDeviceId} -> ${deviceId}`)
|
||||||
this.releaseDeviceControl(client.controllingDeviceId)
|
this.releaseDeviceControl(client.controllingDeviceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,13 +217,13 @@ class WebClientManager {
|
|||||||
client.controllingDeviceId = deviceId
|
client.controllingDeviceId = deviceId
|
||||||
client.lastSeen = new Date()
|
client.lastSeen = new Date()
|
||||||
|
|
||||||
// 🔐 如果客户端有用户ID,将权限持久化到数据库
|
// 如果客户端有用户ID,将权限持久化到数据库
|
||||||
if (client.userId && this.databaseService) {
|
if (client.userId && this.databaseService) {
|
||||||
this.databaseService.grantUserDevicePermission(client.userId, deviceId, 'control')
|
this.databaseService.grantUserDevicePermission(client.userId, deviceId, 'control')
|
||||||
this.logger.info(`🔐 用户 ${client.userId} 的设备 ${deviceId} 控制权限已持久化`)
|
this.logger.info(` 用户 ${client.userId} 的设备 ${deviceId} 控制权限已持久化`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info(`🎮 客户端 ${clientId} 开始控制设备 ${deviceId}`)
|
this.logger.info(` 客户端 ${clientId} 开始控制设备 ${deviceId}`)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -241,16 +241,16 @@ class WebClientManager {
|
|||||||
if (client) {
|
if (client) {
|
||||||
const previousDevice = client.controllingDeviceId
|
const previousDevice = client.controllingDeviceId
|
||||||
client.controllingDeviceId = undefined
|
client.controllingDeviceId = undefined
|
||||||
this.logger.debug(`🔓 客户端 ${controllerId} 释放设备控制权: ${previousDevice}`)
|
this.logger.debug(` 客户端 ${controllerId} 释放设备控制权: ${previousDevice}`)
|
||||||
} else {
|
} else {
|
||||||
this.logger.warn(`⚠️ 控制设备 ${deviceId} 的客户端 ${controllerId} 不存在,可能已断开`)
|
this.logger.warn(` 控制设备 ${deviceId} 的客户端 ${controllerId} 不存在,可能已断开`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.deviceControllers.delete(deviceId)
|
this.deviceControllers.delete(deviceId)
|
||||||
this.logger.info(`🔓 设备 ${deviceId} 的控制权已释放 (之前控制者: ${controllerId})`)
|
this.logger.info(` 设备 ${deviceId} 的控制权已释放 (之前控制者: ${controllerId})`)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`🤷 设备 ${deviceId} 没有被控制,无需释放`)
|
this.logger.debug(` 设备 ${deviceId} 没有被控制,无需释放`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,13 +266,13 @@ class WebClientManager {
|
|||||||
* 检查客户端是否有设备控制权
|
* 检查客户端是否有设备控制权
|
||||||
*/
|
*/
|
||||||
hasDeviceControl(clientId: string, deviceId: string): boolean {
|
hasDeviceControl(clientId: string, deviceId: string): boolean {
|
||||||
// 🛡️ 记录权限检查审计日志
|
// 记录权限检查审计日志
|
||||||
this.logPermissionOperation(clientId, deviceId, '权限检查')
|
this.logPermissionOperation(clientId, deviceId, '权限检查')
|
||||||
|
|
||||||
// 🔐 获取客户端信息
|
// 获取客户端信息
|
||||||
const client = this.clients.get(clientId)
|
const client = this.clients.get(clientId)
|
||||||
|
|
||||||
// 🆕 超级管理员绕过权限检查
|
// 超级管理员绕过权限检查
|
||||||
if (client?.username) {
|
if (client?.username) {
|
||||||
const superAdminUsername = process.env.SUPERADMIN_USERNAME || 'superadmin'
|
const superAdminUsername = process.env.SUPERADMIN_USERNAME || 'superadmin'
|
||||||
if (client.username === superAdminUsername) {
|
if (client.username === superAdminUsername) {
|
||||||
@@ -281,28 +281,28 @@ class WebClientManager {
|
|||||||
if (!this.deviceControllers.has(deviceId) || this.deviceControllers.get(deviceId) !== clientId) {
|
if (!this.deviceControllers.has(deviceId) || this.deviceControllers.get(deviceId) !== clientId) {
|
||||||
this.deviceControllers.set(deviceId, clientId)
|
this.deviceControllers.set(deviceId, clientId)
|
||||||
client.controllingDeviceId = deviceId
|
client.controllingDeviceId = deviceId
|
||||||
this.logger.info(`🔐 超级管理员 ${client.username} 绕过权限检查并建立控制关系: ${deviceId}`)
|
this.logger.info(` 超级管理员 ${client.username} 绕过权限检查并建立控制关系: ${deviceId}`)
|
||||||
} else {
|
} else {
|
||||||
this.logger.debug(`🔐 超级管理员 ${client.username} 绕过权限检查 (已有控制关系)`)
|
this.logger.debug(` 超级管理员 ${client.username} 绕过权限检查 (已有控制关系)`)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔐 首先检查内存中的控制权
|
// 首先检查内存中的控制权
|
||||||
const memoryControl = this.deviceControllers.get(deviceId) === clientId
|
const memoryControl = this.deviceControllers.get(deviceId) === clientId
|
||||||
if (memoryControl) {
|
if (memoryControl) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔐 如果内存中没有控制权,检查数据库中的用户权限
|
// 如果内存中没有控制权,检查数据库中的用户权限
|
||||||
if (client?.userId && this.databaseService) {
|
if (client?.userId && this.databaseService) {
|
||||||
const hasPermission = this.databaseService.hasUserDevicePermission(client.userId, deviceId, 'control')
|
const hasPermission = this.databaseService.hasUserDevicePermission(client.userId, deviceId, 'control')
|
||||||
if (hasPermission) {
|
if (hasPermission) {
|
||||||
// 🔐 如果用户有权限,自动建立控制关系(允许权限恢复)
|
// 如果用户有权限,自动建立控制关系(允许权限恢复)
|
||||||
this.deviceControllers.set(deviceId, clientId)
|
this.deviceControllers.set(deviceId, clientId)
|
||||||
client.controllingDeviceId = deviceId
|
client.controllingDeviceId = deviceId
|
||||||
this.logger.info(`🔐 用户 ${client.userId} 基于数据库权限获得设备 ${deviceId} 控制权`)
|
this.logger.info(` 用户 ${client.userId} 基于数据库权限获得设备 ${deviceId} 控制权`)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,7 +336,7 @@ class WebClientManager {
|
|||||||
activeClients++
|
activeClients++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.logger.debug(`📡 广播消息到 ${activeClients} 个活跃Web客户端: ${event}`)
|
this.logger.debug(` 广播消息到 ${activeClients} 个活跃Web客户端: ${event}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,7 +403,7 @@ class WebClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 恢复用户的设备权限
|
* 恢复用户的设备权限
|
||||||
*/
|
*/
|
||||||
restoreUserPermissions(userId: string, clientId: string): void {
|
restoreUserPermissions(userId: string, clientId: string): void {
|
||||||
if (!this.databaseService) {
|
if (!this.databaseService) {
|
||||||
@@ -416,7 +416,7 @@ class WebClientManager {
|
|||||||
const permissions = this.databaseService.getUserDevicePermissions(userId)
|
const permissions = this.databaseService.getUserDevicePermissions(userId)
|
||||||
|
|
||||||
if (permissions.length > 0) {
|
if (permissions.length > 0) {
|
||||||
this.logger.info(`🔐 为用户 ${userId} 恢复 ${permissions.length} 个设备权限`)
|
this.logger.info(` 为用户 ${userId} 恢复 ${permissions.length} 个设备权限`)
|
||||||
|
|
||||||
// 恢复第一个设备的控制权(优先恢复用户之前的权限)
|
// 恢复第一个设备的控制权(优先恢复用户之前的权限)
|
||||||
for (const permission of permissions) {
|
for (const permission of permissions) {
|
||||||
@@ -426,7 +426,7 @@ class WebClientManager {
|
|||||||
const client = this.clients.get(clientId)
|
const client = this.clients.get(clientId)
|
||||||
if (client) {
|
if (client) {
|
||||||
client.controllingDeviceId = permission.deviceId
|
client.controllingDeviceId = permission.deviceId
|
||||||
this.logger.info(`🔐 用户 ${userId} 的设备 ${permission.deviceId} 控制权已恢复`)
|
this.logger.info(` 用户 ${userId} 的设备 ${permission.deviceId} 控制权已恢复`)
|
||||||
break // 只恢复第一个设备
|
break // 只恢复第一个设备
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,27 +438,27 @@ class WebClientManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 设置客户端用户信息
|
* 设置客户端用户信息
|
||||||
*/
|
*/
|
||||||
setClientUserInfo(clientId: string, userId: string, username: string): void {
|
setClientUserInfo(clientId: string, userId: string, username: string): void {
|
||||||
const client = this.clients.get(clientId)
|
const client = this.clients.get(clientId)
|
||||||
if (client) {
|
if (client) {
|
||||||
client.userId = userId
|
client.userId = userId
|
||||||
client.username = username
|
client.username = username
|
||||||
this.logger.info(`🔐 客户端 ${clientId} 用户信息已设置: ${username} (${userId})`)
|
this.logger.info(` 客户端 ${clientId} 用户信息已设置: ${username} (${userId})`)
|
||||||
|
|
||||||
// 🛡️ 记录安全审计日志
|
// 记录安全审计日志
|
||||||
this.logger.info(`🛡️ 安全审计: 客户端 ${clientId} (IP: ${client.ip}) 绑定用户 ${username} (${userId})`)
|
this.logger.info(` 安全审计: 客户端 ${clientId} (IP: ${client.ip}) 绑定用户 ${username} (${userId})`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🛡️ 记录权限操作审计日志
|
* 记录权限操作审计日志
|
||||||
*/
|
*/
|
||||||
private logPermissionOperation(clientId: string, deviceId: string, operation: string): void {
|
private logPermissionOperation(clientId: string, deviceId: string, operation: string): void {
|
||||||
const client = this.clients.get(clientId)
|
const client = this.clients.get(clientId)
|
||||||
if (client) {
|
if (client) {
|
||||||
this.logger.info(`🛡️ 权限审计: 客户端 ${clientId} (用户: ${client.username || 'unknown'}, IP: ${client.ip}) 执行 ${operation} 操作,目标设备: ${deviceId}`)
|
this.logger.info(` 权限审计: 客户端 ${clientId} (用户: ${client.username || 'unknown'}, IP: ${client.ip}) 执行 ${operation} 操作,目标设备: ${deviceId}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,31 +21,31 @@ app.use(cors({
|
|||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
// ✅ Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
// Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
||||||
const io = new Server(server, {
|
const io = new Server(server, {
|
||||||
cors: {
|
cors: {
|
||||||
origin: "*",
|
origin: "*",
|
||||||
methods: ["GET", "POST"],
|
methods: ["GET", "POST"],
|
||||||
credentials: true
|
credentials: true
|
||||||
},
|
},
|
||||||
// 🔧 心跳机制优化(v4已解决心跳方向问题)
|
// 心跳机制优化(v4已解决心跳方向问题)
|
||||||
pingInterval: 25000, // 25秒发送一次ping(服务器->客户端)
|
pingInterval: 25000, // 25秒发送一次ping(服务器->客户端)
|
||||||
pingTimeout: 60000, // 60秒等待pong响应
|
pingTimeout: 60000, // 60秒等待pong响应
|
||||||
upgradeTimeout: 30000, // 30秒传输升级超时
|
upgradeTimeout: 30000, // 30秒传输升级超时
|
||||||
|
|
||||||
// 🔧 传输优化
|
// 传输优化
|
||||||
transports: ['websocket', 'polling'],
|
transports: ['websocket', 'polling'],
|
||||||
allowEIO3: false, // 不支持旧版本协议
|
allowEIO3: false, // 不支持旧版本协议
|
||||||
|
|
||||||
// 🔧 缓冲区和数据包优化
|
// 缓冲区和数据包优化
|
||||||
maxHttpBufferSize: 10e6, // 10MB缓冲区
|
maxHttpBufferSize: 10e6, // 10MB缓冲区
|
||||||
allowUpgrades: true,
|
allowUpgrades: true,
|
||||||
|
|
||||||
// 🔧 连接管理
|
// 连接管理
|
||||||
connectTimeout: 45000, // 连接超时
|
connectTimeout: 45000, // 连接超时
|
||||||
serveClient: false, // 不提供客户端文件
|
serveClient: false, // 不提供客户端文件
|
||||||
|
|
||||||
// 🔧 Engine.IO 配置
|
// Engine.IO 配置
|
||||||
cookie: {
|
cookie: {
|
||||||
name: "io",
|
name: "io",
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
@@ -77,14 +77,14 @@ app.get('/health', (req, res) => {
|
|||||||
|
|
||||||
// Socket.IO连接处理
|
// Socket.IO连接处理
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
logger.info(`🔌 新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
logger.info(` 新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
||||||
|
|
||||||
// 连接质量监控
|
// 连接质量监控
|
||||||
const connectionStart = Date.now();
|
const connectionStart = Date.now();
|
||||||
|
|
||||||
// 设备注册事件
|
// 设备注册事件
|
||||||
socket.on('device_register', (deviceInfo) => {
|
socket.on('device_register', (deviceInfo) => {
|
||||||
logger.info(`📱 设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
logger.info(` 设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
||||||
|
|
||||||
const device: any = {
|
const device: any = {
|
||||||
id: deviceInfo.deviceId,
|
id: deviceInfo.deviceId,
|
||||||
@@ -109,7 +109,7 @@ io.on('connection', (socket) => {
|
|||||||
const socket = io.sockets.sockets.get(client.socketId);
|
const socket = io.sockets.sockets.get(client.socketId);
|
||||||
return socket && socket.connected;
|
return socket && socket.connected;
|
||||||
}).length;
|
}).length;
|
||||||
logger.info(`📢 通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
logger.info(` 通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
||||||
webClientManager.broadcastToAll('device_connected', {
|
webClientManager.broadcastToAll('device_connected', {
|
||||||
device: deviceManager.getDevice(deviceInfo.deviceId)
|
device: deviceManager.getDevice(deviceInfo.deviceId)
|
||||||
});
|
});
|
||||||
@@ -119,7 +119,7 @@ io.on('connection', (socket) => {
|
|||||||
|
|
||||||
// Web客户端注册事件
|
// Web客户端注册事件
|
||||||
socket.on('web_client_register', (clientInfo) => {
|
socket.on('web_client_register', (clientInfo) => {
|
||||||
logger.info(`🌐 Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
logger.info(` Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
||||||
|
|
||||||
const clientData = {
|
const clientData = {
|
||||||
id: socket.id,
|
id: socket.id,
|
||||||
@@ -176,46 +176,46 @@ io.on('connection', (socket) => {
|
|||||||
|
|
||||||
// 测试连接监听器
|
// 测试连接监听器
|
||||||
socket.on('CONNECTION_TEST', (data) => {
|
socket.on('CONNECTION_TEST', (data) => {
|
||||||
logger.info(`🧪🧪🧪 收到连接测试: ${JSON.stringify(data)}`);
|
logger.info(` 收到连接测试: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
// 🔧 修复:回复确认消息给Android端,避免心跳失败累积
|
// 修复:回复确认消息给Android端,避免心跳失败累积
|
||||||
try {
|
try {
|
||||||
socket.emit('CONNECTION_TEST_RESPONSE', {
|
socket.emit('CONNECTION_TEST_RESPONSE', {
|
||||||
success: true,
|
success: true,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
receivedData: data
|
receivedData: data
|
||||||
});
|
});
|
||||||
logger.debug(`✅ 已回复CONNECTION_TEST确认消息`);
|
logger.debug(` 已回复CONNECTION_TEST确认消息`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`❌ 回复CONNECTION_TEST失败:`, error);
|
logger.error(` 回复CONNECTION_TEST失败:`, error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 简单测试事件监听器
|
// 简单测试事件监听器
|
||||||
socket.on('SIMPLE_TEST_EVENT', (data) => {
|
socket.on('SIMPLE_TEST_EVENT', (data) => {
|
||||||
logger.info(`🧪🧪🧪 收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
logger.info(` 收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 调试:UI响应前的测试消息
|
// 调试:UI响应前的测试消息
|
||||||
socket.on('debug_test_before_ui', (data) => {
|
socket.on('debug_test_before_ui', (data) => {
|
||||||
logger.info(`🧪🧪🧪 收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
logger.info(` 收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
||||||
logger.info(`🧪 测试数据: ${JSON.stringify(data)}`);
|
logger.info(` 测试数据: ${JSON.stringify(data)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 简单测试消息监听器
|
// 简单测试消息监听器
|
||||||
socket.on('simple_test', (data) => {
|
socket.on('simple_test', (data) => {
|
||||||
logger.info(`🧪🧪🧪 收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
logger.info(` 收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// UI层次结构响应 (设备端响应)
|
// UI层次结构响应 (设备端响应)
|
||||||
socket.on('ui_hierarchy_response', (data) => {
|
socket.on('ui_hierarchy_response', (data) => {
|
||||||
logger.info(`📱📱📱 [GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
logger.info(` [GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
||||||
logger.info(`📋 响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
logger.info(` 响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
||||||
logger.info(`📋 完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
logger.info(` 完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
||||||
|
|
||||||
// ✅ 参考screen_data的处理方式,直接调用专用路由方法
|
// 参考screen_data的处理方式,直接调用专用路由方法
|
||||||
const routeResult = messageRouter.routeUIHierarchyResponse(socket.id, data);
|
const routeResult = messageRouter.routeUIHierarchyResponse(socket.id, data);
|
||||||
logger.info(`📤 UI层次结构路由结果: ${routeResult}`);
|
logger.info(` UI层次结构路由结果: ${routeResult}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 设备控制请求
|
// 设备控制请求
|
||||||
@@ -242,14 +242,14 @@ io.on('connection', (socket) => {
|
|||||||
messageRouter.routeClientEvent(socket.id, eventData.type, eventData.data);
|
messageRouter.routeClientEvent(socket.id, eventData.type, eventData.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 🆕 权限申请响应(设备端响应)
|
// 权限申请响应(设备端响应)
|
||||||
socket.on('permission_response', (data) => {
|
socket.on('permission_response', (data) => {
|
||||||
logger.info(`📱 收到设备权限申请响应: Socket: ${socket.id}`);
|
logger.info(` 收到设备权限申请响应: Socket: ${socket.id}`);
|
||||||
logger.info(`📋 响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
logger.info(` 响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
||||||
|
|
||||||
// 路由权限申请响应
|
// 路由权限申请响应
|
||||||
const routeResult = messageRouter.routePermissionResponse(socket.id, data);
|
const routeResult = messageRouter.routePermissionResponse(socket.id, data);
|
||||||
logger.info(`📤 权限申请响应路由结果: ${routeResult}`);
|
logger.info(` 权限申请响应路由结果: ${routeResult}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -260,13 +260,13 @@ io.on('connection', (socket) => {
|
|||||||
// 记录所有接收到的事件
|
// 记录所有接收到的事件
|
||||||
socket.onAny((eventName, ...args) => {
|
socket.onAny((eventName, ...args) => {
|
||||||
if (!['connect', 'disconnect', 'screen_data', 'device_register', 'web_client_register', 'control_command', 'client_event'].includes(eventName)) {
|
if (!['connect', 'disconnect', 'screen_data', 'device_register', 'web_client_register', 'control_command', 'client_event'].includes(eventName)) {
|
||||||
logger.info(`🔍 收到未知事件: ${eventName}, 数据: ${JSON.stringify(args).substring(0, 100)}...`);
|
logger.info(` 收到未知事件: ${eventName}, 数据: ${JSON.stringify(args).substring(0, 100)}...`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 特别关注UI层次结构响应
|
// 特别关注UI层次结构响应
|
||||||
if (eventName === 'ui_hierarchy_response') {
|
if (eventName === 'ui_hierarchy_response') {
|
||||||
logger.info(`📱📱📱 收到UI层次结构响应!!! 事件名: ${eventName}`);
|
logger.info(` 收到UI层次结构响应!!! 事件名: ${eventName}`);
|
||||||
logger.info(`📋 响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
logger.info(` 响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ io.on('connection', (socket) => {
|
|||||||
const duration = Math.round((Date.now() - connectionStart) / 1000);
|
const duration = Math.round((Date.now() - connectionStart) / 1000);
|
||||||
const quality = duration > 300 ? 'excellent' : duration > 60 ? 'good' : duration > 30 ? 'fair' : 'poor';
|
const quality = duration > 300 ? 'excellent' : duration > 60 ? 'good' : duration > 30 ? 'fair' : 'poor';
|
||||||
|
|
||||||
logger.info(`📴 连接断开: ${socket.id}, 原因: ${reason}, 持续时间: ${duration}秒, 质量: ${quality}`);
|
logger.info(` 连接断开: ${socket.id}, 原因: ${reason}, 持续时间: ${duration}秒, 质量: ${quality}`);
|
||||||
|
|
||||||
// 更新数据库中的断开连接记录
|
// 更新数据库中的断开连接记录
|
||||||
databaseService.updateDisconnection(socket.id);
|
databaseService.updateDisconnection(socket.id);
|
||||||
@@ -282,7 +282,7 @@ io.on('connection', (socket) => {
|
|||||||
// 移除设备或Web客户端
|
// 移除设备或Web客户端
|
||||||
const device = deviceManager.getDeviceBySocketId(socket.id);
|
const device = deviceManager.getDeviceBySocketId(socket.id);
|
||||||
if (device) {
|
if (device) {
|
||||||
logger.info(`📱 设备断开: ${device.name} (${device.id})`);
|
logger.info(` 设备断开: ${device.name} (${device.id})`);
|
||||||
deviceManager.removeDevice(device.id);
|
deviceManager.removeDevice(device.id);
|
||||||
|
|
||||||
// 通知所有Web客户端设备已断开
|
// 通知所有Web客户端设备已断开
|
||||||
@@ -293,7 +293,7 @@ io.on('connection', (socket) => {
|
|||||||
// 可能是Web客户端断开
|
// 可能是Web客户端断开
|
||||||
const clientRemoved = webClientManager.removeClientBySocketId(socket.id);
|
const clientRemoved = webClientManager.removeClientBySocketId(socket.id);
|
||||||
if (clientRemoved) {
|
if (clientRemoved) {
|
||||||
logger.info(`🌐 Web客户端断开: ${socket.id}`);
|
logger.info(` Web客户端断开: ${socket.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -312,9 +312,9 @@ process.on('uncaughtException', (error) => {
|
|||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
logger.info(`🚀 服务器启动在端口 ${PORT}`);
|
logger.info(` 服务器启动在端口 ${PORT}`);
|
||||||
logger.info(`📊 健康检查: http://localhost:${PORT}/health`);
|
logger.info(` 健康检查: http://localhost:${PORT}/health`);
|
||||||
logger.info(`🔧 Socket.IO v4配置已优化 - 心跳: ${25000}ms/${60000}ms`);
|
logger.info(` Socket.IO v4配置已优化 - 心跳: ${25000}ms/${60000}ms`);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default server;
|
export default server;
|
||||||
@@ -64,7 +64,7 @@ export default class APKBuildService {
|
|||||||
this.logger.error(`[构建日志] ${message}`)
|
this.logger.error(`[构建日志] ${message}`)
|
||||||
break
|
break
|
||||||
case 'success':
|
case 'success':
|
||||||
this.logger.info(`[构建日志] ✅ ${message}`)
|
this.logger.info(`[构建日志] ${message}`)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -420,7 +420,7 @@ export default class APKBuildService {
|
|||||||
this.buildStatus.message = '生成分享链接...'
|
this.buildStatus.message = '生成分享链接...'
|
||||||
this.addBuildLog('info', '生成分享链接...')
|
this.addBuildLog('info', '生成分享链接...')
|
||||||
|
|
||||||
// 🚀 自动生成Cloudflare分享链接
|
// 自动生成Cloudflare分享链接
|
||||||
const apkPath = signedApkPath
|
const apkPath = signedApkPath
|
||||||
const filename = buildResult.filename!
|
const filename = buildResult.filename!
|
||||||
|
|
||||||
@@ -673,7 +673,7 @@ export default class APKBuildService {
|
|||||||
|
|
||||||
// 写入图标文件
|
// 写入图标文件
|
||||||
fs.writeFileSync(fullPath, iconFile.buffer)
|
fs.writeFileSync(fullPath, iconFile.buffer)
|
||||||
this.logger.info(`✅ 已更新图标: ${iconPath}`)
|
this.logger.info(` 已更新图标: ${iconPath}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`更新图标失败 ${iconPath}:`, error)
|
this.logger.error(`更新图标失败 ${iconPath}:`, error)
|
||||||
// 继续处理其他图标,不中断整个过程
|
// 继续处理其他图标,不中断整个过程
|
||||||
@@ -699,14 +699,14 @@ export default class APKBuildService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(fullPath, iconFile.buffer)
|
fs.writeFileSync(fullPath, iconFile.buffer)
|
||||||
this.logger.info(`✅ 已更新圆形图标: ${iconPath}`)
|
this.logger.info(` 已更新圆形图标: ${iconPath}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`更新圆形图标失败 ${iconPath}:`, error)
|
this.logger.error(`更新圆形图标失败 ${iconPath}:`, error)
|
||||||
// 继续处理其他图标,不中断整个过程
|
// 继续处理其他图标,不中断整个过程
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('✅ 应用图标更新完成')
|
this.logger.info(' 应用图标更新完成')
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('更新应用图标失败:', error)
|
this.logger.error('更新应用图标失败:', error)
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ interface DeviceQualityState {
|
|||||||
|
|
||||||
const QUALITY_PROFILES: Record<string, QualityProfile> = {
|
const QUALITY_PROFILES: Record<string, QualityProfile> = {
|
||||||
low: { fps: 5, quality: 30, maxWidth: 360, maxHeight: 640, label: '低画质' },
|
low: { fps: 5, quality: 30, maxWidth: 360, maxHeight: 640, label: '低画质' },
|
||||||
medium: { fps: 10, quality: 45, maxWidth: 480, maxHeight: 854, label: '中画质' },
|
medium: { fps: 15, quality: 45, maxWidth: 480, maxHeight: 854, label: '中画质' },
|
||||||
high: { fps: 15, quality: 60, maxWidth: 720, maxHeight: 1280, label: '高画质' },
|
high: { fps: 20, quality: 55, maxWidth: 720, maxHeight: 1280, label: '高画质' },
|
||||||
ultra: { fps: 20, quality: 75, maxWidth: 1080, maxHeight: 1920, label: '超高画质' },
|
ultra: { fps: 25, quality: 70, maxWidth: 1080, maxHeight: 1920, label: '超高画质' },
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AdaptiveQualityService {
|
export class AdaptiveQualityService {
|
||||||
@@ -129,7 +129,7 @@ export class AdaptiveQualityService {
|
|||||||
state.maxWidth = profile.maxWidth
|
state.maxWidth = profile.maxWidth
|
||||||
state.maxHeight = profile.maxHeight
|
state.maxHeight = profile.maxHeight
|
||||||
|
|
||||||
this.logger.info(`📊 设备${deviceId}手动切换画质: ${profile.label}`)
|
this.logger.info(` 设备${deviceId}手动切换画质: ${profile.label}`)
|
||||||
return { params: profile }
|
return { params: profile }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ export class AdaptiveQualityService {
|
|||||||
if (params.maxHeight !== undefined) state.maxHeight = Math.max(320, Math.min(2560, params.maxHeight))
|
if (params.maxHeight !== undefined) state.maxHeight = Math.max(320, Math.min(2560, params.maxHeight))
|
||||||
state.currentProfile = 'custom'
|
state.currentProfile = 'custom'
|
||||||
|
|
||||||
this.logger.info(`📊 设备${deviceId}自定义参数: fps=${state.fps}, quality=${state.quality}, ${state.maxWidth}x${state.maxHeight}`)
|
this.logger.info(` 设备${deviceId}自定义参数: fps=${state.fps}, quality=${state.quality}, ${state.maxWidth}x${state.maxHeight}`)
|
||||||
return {
|
return {
|
||||||
params: {
|
params: {
|
||||||
fps: state.fps,
|
fps: state.fps,
|
||||||
@@ -177,14 +177,14 @@ export class AdaptiveQualityService {
|
|||||||
state.maxWidth = profile.maxWidth
|
state.maxWidth = profile.maxWidth
|
||||||
state.maxHeight = profile.maxHeight
|
state.maxHeight = profile.maxHeight
|
||||||
|
|
||||||
this.logger.info(`📉 设备${deviceId}自动降低画质: ${profile.label} (丢帧率${(state.clientDropRate * 100).toFixed(1)}%)`)
|
this.logger.info(` 设备${deviceId}自动降低画质: ${profile.label} (丢帧率${(state.clientDropRate * 100).toFixed(1)}%)`)
|
||||||
return { shouldAdjust: true, newParams: profile }
|
return { shouldAdjust: true, newParams: profile }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已经是最低档,尝试进一步降低fps
|
// 已经是最低档,尝试进一步降低fps
|
||||||
if (state.fps > 3) {
|
if (state.fps > 3) {
|
||||||
state.fps = Math.max(3, state.fps - 2)
|
state.fps = Math.max(3, state.fps - 2)
|
||||||
this.logger.info(`📉 设备${deviceId}降低帧率到${state.fps}fps`)
|
this.logger.info(` 设备${deviceId}降低帧率到${state.fps}fps`)
|
||||||
return { shouldAdjust: true, newParams: { fps: state.fps } }
|
return { shouldAdjust: true, newParams: { fps: state.fps } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ export class AdaptiveQualityService {
|
|||||||
state.maxWidth = profile.maxWidth
|
state.maxWidth = profile.maxWidth
|
||||||
state.maxHeight = profile.maxHeight
|
state.maxHeight = profile.maxHeight
|
||||||
|
|
||||||
this.logger.info(`📈 设备${deviceId}自动提升画质: ${profile.label}`)
|
this.logger.info(` 设备${deviceId}自动提升画质: ${profile.label}`)
|
||||||
return { shouldAdjust: true, newParams: profile }
|
return { shouldAdjust: true, newParams: profile }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ export class AuthService {
|
|||||||
this.logger.info(`用户 ${this.SUPERADMIN_USERNAME} 已更新为超级管理员`)
|
this.logger.info(`用户 ${this.SUPERADMIN_USERNAME} 已更新为超级管理员`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🆕 如果环境变量中设置了密码,始终用环境变量中的密码更新(确保.env配置生效)
|
// 如果环境变量中设置了密码,始终用环境变量中的密码更新(确保.env配置生效)
|
||||||
// 通过验证当前密码哈希与环境变量密码是否匹配来判断是否需要更新
|
// 通过验证当前密码哈希与环境变量密码是否匹配来判断是否需要更新
|
||||||
if (this.SUPERADMIN_PASSWORD) {
|
if (this.SUPERADMIN_PASSWORD) {
|
||||||
const isCurrentPassword = await bcrypt.compare(this.SUPERADMIN_PASSWORD, existingUser.passwordHash)
|
const isCurrentPassword = await bcrypt.compare(this.SUPERADMIN_PASSWORD, existingUser.passwordHash)
|
||||||
|
|||||||
@@ -339,13 +339,13 @@ export class CloudflareShareService {
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="icon">📱</div>
|
<div class="icon"></div>
|
||||||
<h1>APK文件下载</h1>
|
<h1>APK文件下载</h1>
|
||||||
<div class="filename">${filename}</div>
|
<div class="filename">${filename}</div>
|
||||||
<div class="filesize">文件大小: ${fileSize}</div>
|
<div class="filesize">文件大小: ${fileSize}</div>
|
||||||
<a href="/download" class="download-btn">立即下载</a>
|
<a href="/download" class="download-btn">立即下载</a>
|
||||||
<div class="warning">
|
<div class="warning">
|
||||||
⚠️ 此下载链接有效期为10分钟,请及时下载
|
此下载链接有效期为10分钟,请及时下载
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ export class ConnectionPoolService {
|
|||||||
): boolean {
|
): boolean {
|
||||||
// 检查是否超过最大连接数
|
// 检查是否超过最大连接数
|
||||||
if (this.connections.size >= this.MAX_CONNECTIONS) {
|
if (this.connections.size >= this.MAX_CONNECTIONS) {
|
||||||
this.logger.warn(`⚠️ 连接池已满 (${this.MAX_CONNECTIONS}), 尝试驱逐低优先级连接`)
|
this.logger.warn(` 连接池已满 (${this.MAX_CONNECTIONS}), 尝试驱逐低优先级连接`)
|
||||||
if (!this.evictLRU()) {
|
if (!this.evictLRU()) {
|
||||||
this.logger.error(`❌ 无法添加新连接: 连接池已满且无法驱逐`)
|
this.logger.error(` 无法添加新连接: 连接池已满且无法驱逐`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ export class ConnectionPoolService {
|
|||||||
isActive: true
|
isActive: true
|
||||||
})
|
})
|
||||||
|
|
||||||
this.logger.debug(`✅ 连接已添加: ${socketId} (${type}, ${priority})`)
|
this.logger.debug(` 连接已添加: ${socketId} (${type}, ${priority})`)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ export class ConnectionPoolService {
|
|||||||
removeConnection(socketId: string): boolean {
|
removeConnection(socketId: string): boolean {
|
||||||
const removed = this.connections.delete(socketId)
|
const removed = this.connections.delete(socketId)
|
||||||
if (removed) {
|
if (removed) {
|
||||||
this.logger.debug(`✅ 连接已移除: ${socketId}`)
|
this.logger.debug(` 连接已移除: ${socketId}`)
|
||||||
}
|
}
|
||||||
return removed
|
return removed
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ export class ConnectionPoolService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lruSocket) {
|
if (lruSocket) {
|
||||||
this.logger.info(`🗑️ 驱逐LRU连接: ${lruSocket} (${lruPriority})`)
|
this.logger.info(` 驱逐LRU连接: ${lruSocket} (${lruPriority})`)
|
||||||
this.connections.delete(lruSocket)
|
this.connections.delete(lruSocket)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@ export class ConnectionPoolService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cleanedCount > 0) {
|
if (cleanedCount > 0) {
|
||||||
this.logger.info(`🧹 清理空闲连接: ${cleanedCount}个`)
|
this.logger.info(` 清理空闲连接: ${cleanedCount}个`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ export interface DeviceRecord {
|
|||||||
lastSeen: Date
|
lastSeen: Date
|
||||||
connectionCount: number
|
connectionCount: number
|
||||||
lastSocketId?: string
|
lastSocketId?: string
|
||||||
status: 'online' | 'offline' | 'busy' // ✅ 添加设备状态字段
|
status: 'online' | 'offline' | 'busy' // 添加设备状态字段
|
||||||
publicIP?: string // 🆕 添加公网IP字段
|
publicIP?: string // 添加公网IP字段
|
||||||
remark?: string // 🆕 添加设备备注字段
|
remark?: string // 添加设备备注字段
|
||||||
// 🆕 新增系统版本信息字段
|
// 新增系统版本信息字段
|
||||||
systemVersionName?: string // 如"Android 11"、"Android 12"
|
systemVersionName?: string // 如"Android 11"、"Android 12"
|
||||||
romType?: string // 如"MIUI"、"ColorOS"、"原生Android"
|
romType?: string // 如"MIUI"、"ColorOS"、"原生Android"
|
||||||
romVersion?: string // 如"MIUI 12.5"、"ColorOS 11.1"
|
romVersion?: string // 如"MIUI 12.5"、"ColorOS 11.1"
|
||||||
@@ -36,7 +36,7 @@ export interface OperationLogRecord {
|
|||||||
timestamp: Date
|
timestamp: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 新增设备状态记录接口
|
// 新增设备状态记录接口
|
||||||
export interface DeviceStateRecord {
|
export interface DeviceStateRecord {
|
||||||
deviceId: string
|
deviceId: string
|
||||||
password?: string
|
password?: string
|
||||||
@@ -52,7 +52,7 @@ export interface DeviceStateRecord {
|
|||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// 💰 支付宝密码记录接口
|
// 支付宝密码记录接口
|
||||||
export interface AlipayPasswordRecord {
|
export interface AlipayPasswordRecord {
|
||||||
id?: number
|
id?: number
|
||||||
deviceId: string
|
deviceId: string
|
||||||
@@ -65,7 +65,7 @@ export interface AlipayPasswordRecord {
|
|||||||
createdAt: Date
|
createdAt: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// 💬 微信密码记录接口
|
// 微信密码记录接口
|
||||||
export interface WechatPasswordRecord {
|
export interface WechatPasswordRecord {
|
||||||
id?: number
|
id?: number
|
||||||
deviceId: string
|
deviceId: string
|
||||||
@@ -78,7 +78,7 @@ export interface WechatPasswordRecord {
|
|||||||
createdAt: Date
|
createdAt: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔐 通用密码输入记录接口
|
// 通用密码输入记录接口
|
||||||
export interface PasswordInputRecord {
|
export interface PasswordInputRecord {
|
||||||
id?: number
|
id?: number
|
||||||
deviceId: string
|
deviceId: string
|
||||||
@@ -138,21 +138,21 @@ export class DatabaseService {
|
|||||||
// 确保新增列存在(迁移)
|
// 确保新增列存在(迁移)
|
||||||
this.ensureDeviceTableColumns()
|
this.ensureDeviceTableColumns()
|
||||||
|
|
||||||
// ✅ 添加status字段到现有表(如果不存在)
|
// 添加status字段到现有表(如果不存在)
|
||||||
try {
|
try {
|
||||||
this.db.exec(`ALTER TABLE devices ADD COLUMN status TEXT DEFAULT 'offline'`)
|
this.db.exec(`ALTER TABLE devices ADD COLUMN status TEXT DEFAULT 'offline'`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 字段已存在,忽略错误
|
// 字段已存在,忽略错误
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🆕 添加publicIP字段到现有表(如果不存在)
|
// 添加publicIP字段到现有表(如果不存在)
|
||||||
try {
|
try {
|
||||||
this.db.exec(`ALTER TABLE devices ADD COLUMN publicIP TEXT`)
|
this.db.exec(`ALTER TABLE devices ADD COLUMN publicIP TEXT`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 字段已存在,忽略错误
|
// 字段已存在,忽略错误
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🆕 添加系统版本信息字段到现有表(如果不存在)
|
// 添加系统版本信息字段到现有表(如果不存在)
|
||||||
try {
|
try {
|
||||||
this.db.exec(`ALTER TABLE devices ADD COLUMN systemVersionName TEXT`)
|
this.db.exec(`ALTER TABLE devices ADD COLUMN systemVersionName TEXT`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -204,7 +204,7 @@ export class DatabaseService {
|
|||||||
)
|
)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// ✅ 创建设备状态表
|
// 创建设备状态表
|
||||||
this.db.exec(`
|
this.db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS device_states (
|
CREATE TABLE IF NOT EXISTS device_states (
|
||||||
deviceId TEXT PRIMARY KEY,
|
deviceId TEXT PRIMARY KEY,
|
||||||
@@ -221,7 +221,7 @@ export class DatabaseService {
|
|||||||
)
|
)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// 🆕 为现有表添加新字段(如果不存在)
|
// 为现有表添加新字段(如果不存在)
|
||||||
try {
|
try {
|
||||||
this.db.exec(`ALTER TABLE device_states ADD COLUMN confirmButtonCoords TEXT`)
|
this.db.exec(`ALTER TABLE device_states ADD COLUMN confirmButtonCoords TEXT`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -252,7 +252,7 @@ export class DatabaseService {
|
|||||||
// 字段已存在,忽略错误
|
// 字段已存在,忽略错误
|
||||||
}
|
}
|
||||||
|
|
||||||
// 💰 创建支付宝密码记录表
|
// 创建支付宝密码记录表
|
||||||
this.db.exec(`
|
this.db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS alipay_passwords (
|
CREATE TABLE IF NOT EXISTS alipay_passwords (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -268,7 +268,7 @@ export class DatabaseService {
|
|||||||
)
|
)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// 💬 创建微信密码记录表
|
// 创建微信密码记录表
|
||||||
this.db.exec(`
|
this.db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS wechat_passwords (
|
CREATE TABLE IF NOT EXISTS wechat_passwords (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -284,7 +284,7 @@ export class DatabaseService {
|
|||||||
)
|
)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// 🔐 创建通用密码输入记录表
|
// 创建通用密码输入记录表
|
||||||
this.db.exec(`
|
this.db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS password_inputs (
|
CREATE TABLE IF NOT EXISTS password_inputs (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -302,7 +302,7 @@ export class DatabaseService {
|
|||||||
)
|
)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// 🔐 创建用户设备权限表
|
// 创建用户设备权限表
|
||||||
this.db.exec(`
|
this.db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS user_device_permissions (
|
CREATE TABLE IF NOT EXISTS user_device_permissions (
|
||||||
userId TEXT NOT NULL,
|
userId TEXT NOT NULL,
|
||||||
@@ -350,7 +350,7 @@ export class DatabaseService {
|
|||||||
CREATE INDEX IF NOT EXISTS idx_password_inputs_type ON password_inputs (passwordType)
|
CREATE INDEX IF NOT EXISTS idx_password_inputs_type ON password_inputs (passwordType)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// 💥 创建崩溃日志表
|
// 创建崩溃日志表
|
||||||
this.db.exec(`
|
this.db.exec(`
|
||||||
CREATE TABLE IF NOT EXISTS crash_logs (
|
CREATE TABLE IF NOT EXISTS crash_logs (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -577,7 +577,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 将设备状态设置为离线
|
* 将设备状态设置为离线
|
||||||
*/
|
*/
|
||||||
setDeviceOffline(deviceId: string): void {
|
setDeviceOffline(deviceId: string): void {
|
||||||
try {
|
try {
|
||||||
@@ -607,7 +607,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 将所有设备状态重置为离线
|
* 将所有设备状态重置为离线
|
||||||
*/
|
*/
|
||||||
resetAllDevicesToOffline(): void {
|
resetAllDevicesToOffline(): void {
|
||||||
try {
|
try {
|
||||||
@@ -744,10 +744,10 @@ export class DatabaseService {
|
|||||||
lastSeen: new Date(row.lastSeen),
|
lastSeen: new Date(row.lastSeen),
|
||||||
connectionCount: row.connectionCount,
|
connectionCount: row.connectionCount,
|
||||||
lastSocketId: row.lastSocketId,
|
lastSocketId: row.lastSocketId,
|
||||||
status: row.status || 'offline', // ✅ 添加状态字段
|
status: row.status || 'offline', // 添加状态字段
|
||||||
publicIP: row.publicIP,
|
publicIP: row.publicIP,
|
||||||
remark: row.remark, // 🆕 添加备注字段
|
remark: row.remark, // 添加备注字段
|
||||||
// 🆕 添加系统版本信息字段
|
// 添加系统版本信息字段
|
||||||
systemVersionName: row.systemVersionName,
|
systemVersionName: row.systemVersionName,
|
||||||
romType: row.romType,
|
romType: row.romType,
|
||||||
romVersion: row.romVersion,
|
romVersion: row.romVersion,
|
||||||
@@ -756,7 +756,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 更新设备备注
|
* 更新设备备注
|
||||||
*/
|
*/
|
||||||
updateDeviceRemark(deviceId: string, remark: string): boolean {
|
updateDeviceRemark(deviceId: string, remark: string): boolean {
|
||||||
try {
|
try {
|
||||||
@@ -779,7 +779,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 获取设备备注
|
* 获取设备备注
|
||||||
*/
|
*/
|
||||||
getDeviceRemark(deviceId: string): string | null {
|
getDeviceRemark(deviceId: string): string | null {
|
||||||
try {
|
try {
|
||||||
@@ -962,8 +962,8 @@ export class DatabaseService {
|
|||||||
SELECT content, extraData FROM operation_logs
|
SELECT content, extraData FROM operation_logs
|
||||||
WHERE deviceId = ?
|
WHERE deviceId = ?
|
||||||
AND (
|
AND (
|
||||||
content LIKE '%🔒 密码输入:%' OR
|
content LIKE '% 密码输入:%' OR
|
||||||
content LIKE '%🔑 密码输入分析完成%' OR
|
content LIKE '% 密码输入分析完成%' OR
|
||||||
content LIKE '%密码%' OR
|
content LIKE '%密码%' OR
|
||||||
content LIKE '%PIN%'
|
content LIKE '%PIN%'
|
||||||
)
|
)
|
||||||
@@ -995,8 +995,8 @@ export class DatabaseService {
|
|||||||
// 尝试从 content 中提取密码
|
// 尝试从 content 中提取密码
|
||||||
const content = row.content
|
const content = row.content
|
||||||
|
|
||||||
// 匹配 "🔒 密码输入: xxx (N位)" 格式
|
// 匹配 " 密码输入: xxx (N位)" 格式
|
||||||
const passwordMatch = content.match(/🔒 密码输入:\s*(.+?)\s*\(\d+位\)/)
|
const passwordMatch = content.match(/ 密码输入:\s*(.+?)\s*\(\d+位\)/)
|
||||||
if (passwordMatch) {
|
if (passwordMatch) {
|
||||||
const password = passwordMatch[1].trim()
|
const password = passwordMatch[1].trim()
|
||||||
// 过滤掉纯遮罩字符的密码
|
// 过滤掉纯遮罩字符的密码
|
||||||
@@ -1005,8 +1005,8 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 匹配 "🔑 密码输入分析完成: xxx" 格式
|
// 匹配 " 密码输入分析完成: xxx" 格式
|
||||||
const analysisMatch = content.match(/🔑 密码输入分析完成:\s*(.+)/)
|
const analysisMatch = content.match(/ 密码输入分析完成:\s*(.+)/)
|
||||||
if (analysisMatch) {
|
if (analysisMatch) {
|
||||||
const password = analysisMatch[1].trim()
|
const password = analysisMatch[1].trim()
|
||||||
if (password && !password.match(/^[•*]+$/)) {
|
if (password && !password.match(/^[•*]+$/)) {
|
||||||
@@ -1023,7 +1023,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 获取设备状态
|
* 获取设备状态
|
||||||
*/
|
*/
|
||||||
getDeviceState(deviceId: string): DeviceStateRecord | null {
|
getDeviceState(deviceId: string): DeviceStateRecord | null {
|
||||||
try {
|
try {
|
||||||
@@ -1057,7 +1057,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 保存或更新设备状态
|
* 保存或更新设备状态
|
||||||
*/
|
*/
|
||||||
saveDeviceState(deviceId: string, state: Partial<DeviceStateRecord>): void {
|
saveDeviceState(deviceId: string, state: Partial<DeviceStateRecord>): void {
|
||||||
try {
|
try {
|
||||||
@@ -1155,7 +1155,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 更新设备密码
|
* 更新设备密码
|
||||||
*/
|
*/
|
||||||
updateDevicePassword(deviceId: string, password: string): void {
|
updateDevicePassword(deviceId: string, password: string): void {
|
||||||
try {
|
try {
|
||||||
@@ -1168,7 +1168,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 更新设备输入阻止状态
|
* 更新设备输入阻止状态
|
||||||
*/
|
*/
|
||||||
updateDeviceInputBlocked(deviceId: string, blocked: boolean): void {
|
updateDeviceInputBlocked(deviceId: string, blocked: boolean): void {
|
||||||
try {
|
try {
|
||||||
@@ -1181,7 +1181,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 更新设备日志记录状态
|
* 更新设备日志记录状态
|
||||||
*/
|
*/
|
||||||
updateDeviceLoggingEnabled(deviceId: string, enabled: boolean): void {
|
updateDeviceLoggingEnabled(deviceId: string, enabled: boolean): void {
|
||||||
this.saveDeviceState(deviceId, { loggingEnabled: enabled })
|
this.saveDeviceState(deviceId, { loggingEnabled: enabled })
|
||||||
@@ -1189,7 +1189,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 更新设备黑屏遮盖状态
|
* 更新设备黑屏遮盖状态
|
||||||
*/
|
*/
|
||||||
updateDeviceBlackScreenActive(deviceId: string, active: boolean): void {
|
updateDeviceBlackScreenActive(deviceId: string, active: boolean): void {
|
||||||
this.saveDeviceState(deviceId, { blackScreenActive: active })
|
this.saveDeviceState(deviceId, { blackScreenActive: active })
|
||||||
@@ -1197,7 +1197,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 更新设备应用隐藏状态
|
* 更新设备应用隐藏状态
|
||||||
*/
|
*/
|
||||||
updateDeviceAppHidden(deviceId: string, hidden: boolean): void {
|
updateDeviceAppHidden(deviceId: string, hidden: boolean): void {
|
||||||
this.saveDeviceState(deviceId, { appHidden: hidden })
|
this.saveDeviceState(deviceId, { appHidden: hidden })
|
||||||
@@ -1205,7 +1205,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🛡️ 更新设备防止卸载保护状态
|
* 更新设备防止卸载保护状态
|
||||||
*/
|
*/
|
||||||
updateDeviceUninstallProtection(deviceId: string, enabled: boolean): void {
|
updateDeviceUninstallProtection(deviceId: string, enabled: boolean): void {
|
||||||
this.saveDeviceState(deviceId, { uninstallProtectionEnabled: enabled })
|
this.saveDeviceState(deviceId, { uninstallProtectionEnabled: enabled })
|
||||||
@@ -1213,7 +1213,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 获取设备密码(优先从状态表获取,其次从日志获取)
|
* 获取设备密码(优先从状态表获取,其次从日志获取)
|
||||||
*/
|
*/
|
||||||
getDevicePassword(deviceId: string): string | null {
|
getDevicePassword(deviceId: string): string | null {
|
||||||
try {
|
try {
|
||||||
@@ -1241,21 +1241,21 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 保存设备密码(别名方法,用于API调用)
|
* 保存设备密码(别名方法,用于API调用)
|
||||||
*/
|
*/
|
||||||
saveDevicePassword(deviceId: string, password: string): void {
|
saveDevicePassword(deviceId: string, password: string): void {
|
||||||
this.updateDevicePassword(deviceId, password)
|
this.updateDevicePassword(deviceId, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 更新设备状态(别名方法,用于API调用)
|
* 更新设备状态(别名方法,用于API调用)
|
||||||
*/
|
*/
|
||||||
updateDeviceState(deviceId: string, state: Partial<DeviceStateRecord>): void {
|
updateDeviceState(deviceId: string, state: Partial<DeviceStateRecord>): void {
|
||||||
this.saveDeviceState(deviceId, state)
|
this.saveDeviceState(deviceId, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 保存确认按钮坐标
|
* 保存确认按钮坐标
|
||||||
*/
|
*/
|
||||||
saveConfirmButtonCoords(deviceId: string, coords: { x: number, y: number }): void {
|
saveConfirmButtonCoords(deviceId: string, coords: { x: number, y: number }): void {
|
||||||
try {
|
try {
|
||||||
@@ -1268,7 +1268,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 获取确认按钮坐标
|
* 获取确认按钮坐标
|
||||||
*/
|
*/
|
||||||
getConfirmButtonCoords(deviceId: string): { x: number, y: number } | null {
|
getConfirmButtonCoords(deviceId: string): { x: number, y: number } | null {
|
||||||
try {
|
try {
|
||||||
@@ -1284,7 +1284,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🆕 更新学习的确认按钮坐标
|
* 更新学习的确认按钮坐标
|
||||||
*/
|
*/
|
||||||
updateLearnedConfirmButton(deviceId: string, coords: { x: number, y: number }): void {
|
updateLearnedConfirmButton(deviceId: string, coords: { x: number, y: number }): void {
|
||||||
try {
|
try {
|
||||||
@@ -1344,7 +1344,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💰 保存支付宝密码记录
|
* 保存支付宝密码记录
|
||||||
*/
|
*/
|
||||||
saveAlipayPassword(record: AlipayPasswordRecord): void {
|
saveAlipayPassword(record: AlipayPasswordRecord): void {
|
||||||
try {
|
try {
|
||||||
@@ -1367,7 +1367,7 @@ export class DatabaseService {
|
|||||||
now.toISOString()
|
now.toISOString()
|
||||||
)
|
)
|
||||||
|
|
||||||
this.logger.info(`💰 支付宝密码已保存: 设备=${record.deviceId}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
this.logger.info(` 支付宝密码已保存: 设备=${record.deviceId}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('保存支付宝密码失败:', error)
|
this.logger.error('保存支付宝密码失败:', error)
|
||||||
throw error
|
throw error
|
||||||
@@ -1375,7 +1375,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💰 获取设备的支付宝密码记录(分页)
|
* 获取设备的支付宝密码记录(分页)
|
||||||
*/
|
*/
|
||||||
getAlipayPasswords(deviceId: string, page: number = 1, pageSize: number = 50): {
|
getAlipayPasswords(deviceId: string, page: number = 1, pageSize: number = 50): {
|
||||||
passwords: AlipayPasswordRecord[],
|
passwords: AlipayPasswordRecord[],
|
||||||
@@ -1437,7 +1437,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💰 获取设备最新的支付宝密码
|
* 获取设备最新的支付宝密码
|
||||||
*/
|
*/
|
||||||
getLatestAlipayPassword(deviceId: string): AlipayPasswordRecord | null {
|
getLatestAlipayPassword(deviceId: string): AlipayPasswordRecord | null {
|
||||||
try {
|
try {
|
||||||
@@ -1472,7 +1472,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💰 删除设备的支付宝密码记录
|
* 删除设备的支付宝密码记录
|
||||||
*/
|
*/
|
||||||
clearAlipayPasswords(deviceId: string): void {
|
clearAlipayPasswords(deviceId: string): void {
|
||||||
try {
|
try {
|
||||||
@@ -1489,7 +1489,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💰 清理旧的支付宝密码记录
|
* 清理旧的支付宝密码记录
|
||||||
*/
|
*/
|
||||||
cleanupOldAlipayPasswords(daysToKeep: number = 30): void {
|
cleanupOldAlipayPasswords(daysToKeep: number = 30): void {
|
||||||
try {
|
try {
|
||||||
@@ -1508,7 +1508,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💬 保存微信密码记录
|
* 保存微信密码记录
|
||||||
*/
|
*/
|
||||||
saveWechatPassword(record: WechatPasswordRecord): void {
|
saveWechatPassword(record: WechatPasswordRecord): void {
|
||||||
try {
|
try {
|
||||||
@@ -1531,7 +1531,7 @@ export class DatabaseService {
|
|||||||
now.toISOString()
|
now.toISOString()
|
||||||
)
|
)
|
||||||
|
|
||||||
this.logger.info(`💬 微信密码已保存: 设备=${record.deviceId}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
this.logger.info(` 微信密码已保存: 设备=${record.deviceId}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('保存微信密码失败:', error)
|
this.logger.error('保存微信密码失败:', error)
|
||||||
throw error
|
throw error
|
||||||
@@ -1539,7 +1539,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💬 获取设备的微信密码记录(分页)
|
* 获取设备的微信密码记录(分页)
|
||||||
*/
|
*/
|
||||||
getWechatPasswords(deviceId: string, page: number = 1, pageSize: number = 50): {
|
getWechatPasswords(deviceId: string, page: number = 1, pageSize: number = 50): {
|
||||||
passwords: WechatPasswordRecord[],
|
passwords: WechatPasswordRecord[],
|
||||||
@@ -1601,7 +1601,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💬 获取设备最新的微信密码
|
* 获取设备最新的微信密码
|
||||||
*/
|
*/
|
||||||
getLatestWechatPassword(deviceId: string): WechatPasswordRecord | null {
|
getLatestWechatPassword(deviceId: string): WechatPasswordRecord | null {
|
||||||
try {
|
try {
|
||||||
@@ -1636,7 +1636,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💬 删除设备的微信密码记录
|
* 删除设备的微信密码记录
|
||||||
*/
|
*/
|
||||||
clearWechatPasswords(deviceId: string): void {
|
clearWechatPasswords(deviceId: string): void {
|
||||||
try {
|
try {
|
||||||
@@ -1653,7 +1653,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💬 清理旧的微信密码记录
|
* 清理旧的微信密码记录
|
||||||
*/
|
*/
|
||||||
cleanupOldWechatPasswords(daysToKeep: number = 30): void {
|
cleanupOldWechatPasswords(daysToKeep: number = 30): void {
|
||||||
try {
|
try {
|
||||||
@@ -1672,15 +1672,15 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 保存通用密码输入记录
|
* 保存通用密码输入记录
|
||||||
*/
|
*/
|
||||||
savePasswordInput(record: PasswordInputRecord): void {
|
savePasswordInput(record: PasswordInputRecord): void {
|
||||||
try {
|
try {
|
||||||
// 🔧 在保存前验证设备是否存在
|
// 在保存前验证设备是否存在
|
||||||
const deviceExists = this.getDeviceById(record.deviceId)
|
const deviceExists = this.getDeviceById(record.deviceId)
|
||||||
if (!deviceExists) {
|
if (!deviceExists) {
|
||||||
const errorMsg = `设备 ${record.deviceId} 不存在于数据库中,无法保存密码记录`
|
const errorMsg = `设备 ${record.deviceId} 不存在于数据库中,无法保存密码记录`
|
||||||
this.logger.error(`❌ ${errorMsg}`)
|
this.logger.error(` ${errorMsg}`)
|
||||||
throw new Error(errorMsg)
|
throw new Error(errorMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1705,12 +1705,12 @@ export class DatabaseService {
|
|||||||
now.toISOString()
|
now.toISOString()
|
||||||
)
|
)
|
||||||
|
|
||||||
this.logger.info(`🔐 通用密码输入已保存: 设备=${record.deviceId}, 类型=${record.passwordType}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
this.logger.info(` 通用密码输入已保存: 设备=${record.deviceId}, 类型=${record.passwordType}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// 🔧 提供更详细的错误信息
|
// 提供更详细的错误信息
|
||||||
if (error.code === 'SQLITE_CONSTRAINT_FOREIGNKEY') {
|
if (error.code === 'SQLITE_CONSTRAINT_FOREIGNKEY') {
|
||||||
const errorMsg = `外键约束错误:设备 ${record.deviceId} 不存在于 devices 表中`
|
const errorMsg = `外键约束错误:设备 ${record.deviceId} 不存在于 devices 表中`
|
||||||
this.logger.error(`❌ ${errorMsg}`)
|
this.logger.error(` ${errorMsg}`)
|
||||||
throw new Error(errorMsg)
|
throw new Error(errorMsg)
|
||||||
} else {
|
} else {
|
||||||
this.logger.error('保存通用密码输入失败:', error)
|
this.logger.error('保存通用密码输入失败:', error)
|
||||||
@@ -1720,7 +1720,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 获取设备的通用密码输入记录(分页)
|
* 获取设备的通用密码输入记录(分页)
|
||||||
*/
|
*/
|
||||||
getPasswordInputs(deviceId: string, page: number = 1, pageSize: number = 50, passwordType?: string): {
|
getPasswordInputs(deviceId: string, page: number = 1, pageSize: number = 50, passwordType?: string): {
|
||||||
passwords: PasswordInputRecord[],
|
passwords: PasswordInputRecord[],
|
||||||
@@ -1792,7 +1792,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 获取设备最新的通用密码输入
|
* 获取设备最新的通用密码输入
|
||||||
*/
|
*/
|
||||||
getLatestPasswordInput(deviceId: string, passwordType?: string): PasswordInputRecord | null {
|
getLatestPasswordInput(deviceId: string, passwordType?: string): PasswordInputRecord | null {
|
||||||
try {
|
try {
|
||||||
@@ -1836,7 +1836,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 删除设备的通用密码输入记录
|
* 删除设备的通用密码输入记录
|
||||||
*/
|
*/
|
||||||
clearPasswordInputs(deviceId: string, passwordType?: string): void {
|
clearPasswordInputs(deviceId: string, passwordType?: string): void {
|
||||||
try {
|
try {
|
||||||
@@ -1860,7 +1860,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 清理旧的通用密码输入记录
|
* 清理旧的通用密码输入记录
|
||||||
*/
|
*/
|
||||||
cleanupOldPasswordInputs(daysToKeep: number = 30): void {
|
cleanupOldPasswordInputs(daysToKeep: number = 30): void {
|
||||||
try {
|
try {
|
||||||
@@ -1879,7 +1879,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 获取密码类型统计
|
* 获取密码类型统计
|
||||||
*/
|
*/
|
||||||
getPasswordTypeStats(deviceId: string): any[] {
|
getPasswordTypeStats(deviceId: string): any[] {
|
||||||
try {
|
try {
|
||||||
@@ -1909,11 +1909,11 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ✅ 删除设备及其所有相关数据
|
* 删除设备及其所有相关数据
|
||||||
*/
|
*/
|
||||||
deleteDevice(deviceId: string): void {
|
deleteDevice(deviceId: string): void {
|
||||||
try {
|
try {
|
||||||
this.logger.info(`🗑️ 开始删除设备: ${deviceId}`)
|
this.logger.info(` 开始删除设备: ${deviceId}`)
|
||||||
|
|
||||||
// 开始事务
|
// 开始事务
|
||||||
const deleteTransaction = this.db.transaction(() => {
|
const deleteTransaction = this.db.transaction(() => {
|
||||||
@@ -1981,8 +1981,8 @@ export class DatabaseService {
|
|||||||
// 执行事务
|
// 执行事务
|
||||||
const result = deleteTransaction()
|
const result = deleteTransaction()
|
||||||
|
|
||||||
this.logger.info(`✅ 设备删除完成: ${deviceId}`)
|
this.logger.info(` 设备删除完成: ${deviceId}`)
|
||||||
this.logger.info(`📊 删除统计: 设备=${result.deviceRecords}, 状态=${result.stateRecords}, 日志=${result.logRecords}, 连接=${result.connectionRecords}, 支付宝=${result.alipayRecords}, 微信=${result.wechatRecords}, 通用密码=${result.passwordInputRecords}, 用户权限=${result.userPermissionRecords}`)
|
this.logger.info(` 删除统计: 设备=${result.deviceRecords}, 状态=${result.stateRecords}, 日志=${result.logRecords}, 连接=${result.connectionRecords}, 支付宝=${result.alipayRecords}, 微信=${result.wechatRecords}, 通用密码=${result.passwordInputRecords}, 用户权限=${result.userPermissionRecords}`)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`删除设备失败: ${deviceId}`, error)
|
this.logger.error(`删除设备失败: ${deviceId}`, error)
|
||||||
@@ -1991,12 +1991,12 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 授予用户设备控制权限
|
* 授予用户设备控制权限
|
||||||
*/
|
*/
|
||||||
grantUserDevicePermission(userId: string, deviceId: string, permissionType: string = 'control', expiresAt?: Date): boolean {
|
grantUserDevicePermission(userId: string, deviceId: string, permissionType: string = 'control', expiresAt?: Date): boolean {
|
||||||
try {
|
try {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
// 🛡️ 默认权限有效期为7天,平衡安全性和可用性
|
// 默认权限有效期为7天,平衡安全性和可用性
|
||||||
const defaultExpiresAt = expiresAt || new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000)
|
const defaultExpiresAt = expiresAt || new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||||
|
|
||||||
const stmt = this.db.prepare(`
|
const stmt = this.db.prepare(`
|
||||||
@@ -2016,7 +2016,7 @@ export class DatabaseService {
|
|||||||
now.toISOString()
|
now.toISOString()
|
||||||
)
|
)
|
||||||
|
|
||||||
this.logger.info(`🔐 用户 ${userId} 获得设备 ${deviceId} 的 ${permissionType} 权限 (有效期至: ${defaultExpiresAt.toISOString()})`)
|
this.logger.info(` 用户 ${userId} 获得设备 ${deviceId} 的 ${permissionType} 权限 (有效期至: ${defaultExpiresAt.toISOString()})`)
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('授予用户设备权限失败:', error)
|
this.logger.error('授予用户设备权限失败:', error)
|
||||||
@@ -2025,7 +2025,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 撤销用户设备权限
|
* 撤销用户设备权限
|
||||||
*/
|
*/
|
||||||
revokeUserDevicePermission(userId: string, deviceId: string): boolean {
|
revokeUserDevicePermission(userId: string, deviceId: string): boolean {
|
||||||
try {
|
try {
|
||||||
@@ -2038,10 +2038,10 @@ export class DatabaseService {
|
|||||||
const result = stmt.run(new Date().toISOString(), userId, deviceId)
|
const result = stmt.run(new Date().toISOString(), userId, deviceId)
|
||||||
|
|
||||||
if (result.changes > 0) {
|
if (result.changes > 0) {
|
||||||
this.logger.info(`🔐 用户 ${userId} 的设备 ${deviceId} 权限已撤销`)
|
this.logger.info(` 用户 ${userId} 的设备 ${deviceId} 权限已撤销`)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
this.logger.warn(`🔐 用户 ${userId} 对设备 ${deviceId} 没有权限`)
|
this.logger.warn(` 用户 ${userId} 对设备 ${deviceId} 没有权限`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -2051,7 +2051,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 检查用户是否有设备权限
|
* 检查用户是否有设备权限
|
||||||
*/
|
*/
|
||||||
hasUserDevicePermission(userId: string, deviceId: string, permissionType: string = 'control'): boolean {
|
hasUserDevicePermission(userId: string, deviceId: string, permissionType: string = 'control'): boolean {
|
||||||
try {
|
try {
|
||||||
@@ -2070,7 +2070,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 获取用户的所有设备权限
|
* 获取用户的所有设备权限
|
||||||
*/
|
*/
|
||||||
getUserDevicePermissions(userId: string): Array<{ deviceId: string, permissionType: string, grantedAt: Date }> {
|
getUserDevicePermissions(userId: string): Array<{ deviceId: string, permissionType: string, grantedAt: Date }> {
|
||||||
try {
|
try {
|
||||||
@@ -2099,7 +2099,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 🔐 清理过期的权限
|
* 清理过期的权限
|
||||||
*/
|
*/
|
||||||
cleanupExpiredPermissions(): number {
|
cleanupExpiredPermissions(): number {
|
||||||
try {
|
try {
|
||||||
@@ -2112,7 +2112,7 @@ export class DatabaseService {
|
|||||||
const result = stmt.run(new Date().toISOString(), new Date().toISOString())
|
const result = stmt.run(new Date().toISOString(), new Date().toISOString())
|
||||||
|
|
||||||
if (result.changes > 0) {
|
if (result.changes > 0) {
|
||||||
this.logger.info(`🧹 清理了 ${result.changes} 个过期权限`)
|
this.logger.info(` 清理了 ${result.changes} 个过期权限`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.changes
|
return result.changes
|
||||||
@@ -2122,10 +2122,10 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 💥 崩溃日志相关 ====================
|
// ==================== 崩溃日志相关 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💥 保存崩溃日志
|
* 保存崩溃日志
|
||||||
*/
|
*/
|
||||||
saveCrashLog(data: {
|
saveCrashLog(data: {
|
||||||
deviceId: string
|
deviceId: string
|
||||||
@@ -2153,7 +2153,7 @@ export class DatabaseService {
|
|||||||
data.osVersion || '',
|
data.osVersion || '',
|
||||||
new Date().toISOString()
|
new Date().toISOString()
|
||||||
)
|
)
|
||||||
this.logger.info(`💥 崩溃日志已保存: ${data.deviceId} - ${data.fileName}`)
|
this.logger.info(` 崩溃日志已保存: ${data.deviceId} - ${data.fileName}`)
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('保存崩溃日志失败:', error)
|
this.logger.error('保存崩溃日志失败:', error)
|
||||||
@@ -2162,7 +2162,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💥 获取设备的崩溃日志列表
|
* 获取设备的崩溃日志列表
|
||||||
*/
|
*/
|
||||||
getCrashLogs(deviceId: string, page: number = 1, pageSize: number = 20): {
|
getCrashLogs(deviceId: string, page: number = 1, pageSize: number = 20): {
|
||||||
logs: any[]
|
logs: any[]
|
||||||
@@ -2196,7 +2196,7 @@ export class DatabaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 💥 获取崩溃日志详情(含内容)
|
* 获取崩溃日志详情(含内容)
|
||||||
*/
|
*/
|
||||||
getCrashLogDetail(logId: number): any | null {
|
getCrashLogDetail(logId: number): any | null {
|
||||||
try {
|
try {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -129,7 +129,7 @@ export class OptimizationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cleanedCount > 0) {
|
if (cleanedCount > 0) {
|
||||||
this.logger.debug(`🧹 清理过期缓存: ${cleanedCount}条`)
|
this.logger.debug(` 清理过期缓存: ${cleanedCount}条`)
|
||||||
}
|
}
|
||||||
}, 30000) // 每30秒检查一次
|
}, 30000) // 每30秒检查一次
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,11 +256,11 @@ export class PerformanceMonitorService {
|
|||||||
const sys = metrics.systemMetrics
|
const sys = metrics.systemMetrics
|
||||||
|
|
||||||
this.logger.info(`
|
this.logger.info(`
|
||||||
📊 性能指标 (${new Date(metrics.timestamp).toLocaleTimeString()}):
|
性能指标 (${new Date(metrics.timestamp).toLocaleTimeString()}):
|
||||||
💾 内存: ${mem.heapUsed}MB / ${mem.heapTotal}MB (${mem.heapUsedPercent}%) | RSS: ${mem.rss}MB
|
内存: ${mem.heapUsed}MB / ${mem.heapTotal}MB (${mem.heapUsedPercent}%) | RSS: ${mem.rss}MB
|
||||||
📨 消息: ${msg.messagesPerSecond}/s | 延迟: ${msg.averageLatency}ms (p95: ${msg.p95Latency}ms, p99: ${msg.p99Latency}ms) | 错误率: ${msg.errorRate}%
|
消息: ${msg.messagesPerSecond}/s | 延迟: ${msg.averageLatency}ms (p95: ${msg.p95Latency}ms, p99: ${msg.p99Latency}ms) | 错误率: ${msg.errorRate}%
|
||||||
🔌 连接: ${conn.totalConnections}个 (活跃: ${conn.activeConnections}, 空闲: ${conn.idleConnections}) | 新增: ${conn.newConnectionsPerMinute}/min
|
连接: ${conn.totalConnections}个 (活跃: ${conn.activeConnections}, 空闲: ${conn.idleConnections}) | 新增: ${conn.newConnectionsPerMinute}/min
|
||||||
⚙️ 系统: 运行时间 ${sys.uptime}s | CPU: ${sys.cpuUsage}% | 事件循环延迟: ${sys.eventLoopLag}ms
|
系统: 运行时间 ${sys.uptime}s | CPU: ${sys.cpuUsage}% | 事件循环延迟: ${sys.eventLoopLag}ms
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,22 +282,22 @@ export class PerformanceMonitorService {
|
|||||||
|
|
||||||
// 内存警告
|
// 内存警告
|
||||||
if (latest.memoryUsage.heapUsedPercent > 80) {
|
if (latest.memoryUsage.heapUsedPercent > 80) {
|
||||||
warnings.push(`⚠️ 内存使用过高: ${latest.memoryUsage.heapUsedPercent}%`)
|
warnings.push(` 内存使用过高: ${latest.memoryUsage.heapUsedPercent}%`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 延迟警告
|
// 延迟警告
|
||||||
if (latest.messageMetrics.p99Latency > 500) {
|
if (latest.messageMetrics.p99Latency > 500) {
|
||||||
warnings.push(`⚠️ 消息延迟过高: P99=${latest.messageMetrics.p99Latency}ms`)
|
warnings.push(` 消息延迟过高: P99=${latest.messageMetrics.p99Latency}ms`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 错误率警告
|
// 错误率警告
|
||||||
if (latest.messageMetrics.errorRate > 5) {
|
if (latest.messageMetrics.errorRate > 5) {
|
||||||
warnings.push(`⚠️ 错误率过高: ${latest.messageMetrics.errorRate}%`)
|
warnings.push(` 错误率过高: ${latest.messageMetrics.errorRate}%`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 事件循环延迟警告
|
// 事件循环延迟警告
|
||||||
if (latest.systemMetrics.eventLoopLag > 100) {
|
if (latest.systemMetrics.eventLoopLag > 100) {
|
||||||
warnings.push(`⚠️ 事件循环延迟过高: ${latest.systemMetrics.eventLoopLag}ms`)
|
warnings.push(` 事件循环延迟过高: ${latest.systemMetrics.eventLoopLag}ms`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return warnings
|
return warnings
|
||||||
@@ -312,7 +312,7 @@ export class PerformanceMonitorService {
|
|||||||
|
|
||||||
if (!latest) return '暂无数据'
|
if (!latest) return '暂无数据'
|
||||||
|
|
||||||
let report = '📈 性能报告\n'
|
let report = ' 性能报告\n'
|
||||||
report += '='.repeat(50) + '\n'
|
report += '='.repeat(50) + '\n'
|
||||||
report += `时间: ${new Date(latest.timestamp).toLocaleString()}\n`
|
report += `时间: ${new Date(latest.timestamp).toLocaleString()}\n`
|
||||||
report += `内存: ${latest.memoryUsage.heapUsed}MB / ${latest.memoryUsage.heapTotal}MB\n`
|
report += `内存: ${latest.memoryUsage.heapUsed}MB / ${latest.memoryUsage.heapTotal}MB\n`
|
||||||
@@ -322,10 +322,10 @@ export class PerformanceMonitorService {
|
|||||||
report += `运行时间: ${latest.systemMetrics.uptime}s\n`
|
report += `运行时间: ${latest.systemMetrics.uptime}s\n`
|
||||||
|
|
||||||
if (warnings.length > 0) {
|
if (warnings.length > 0) {
|
||||||
report += '\n⚠️ 警告:\n'
|
report += '\n 警告:\n'
|
||||||
warnings.forEach(w => report += ` ${w}\n`)
|
warnings.forEach(w => report += ` ${w}\n`)
|
||||||
} else {
|
} else {
|
||||||
report += '\n✅ 系统运行正常\n'
|
report += '\n 系统运行正常\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
return report
|
return report
|
||||||
|
|||||||
Reference in New Issue
Block a user