第8轮调试报告:Docker 容器化 + PWA/Service Worker + WebSocket 深度测试
日期: 2026年5月20日 15:23 CST
范围: Docker 配置审计、PWA 配置审计、WebSocket 代码审查与连接测试、前端构建配置审计
方法: 静态代码审查 + HTTP/WebSocket 端点测试
目录
- Docker 配置审计
- PWA 配置审计
- WebSocket 代码审查与测试
- 前端构建配置审计
- 问题汇总与修复优先级
1. Docker 配置审计
1.1 Docker Compose 文件概览
1.2 发现的问题
🔴 严重 (P0)
| # |
问题 |
位置 |
影响 |
| 1 |
Caddyfile 缺失 |
docker-compose.yml:12 引用 ./Caddyfile,但文件不存在 |
生产环境 docker-compose up 时 Caddy 容器将启动失败,整个集群不可访问 |
| 2 |
docker-compose.dev.yml 服务定义顺序错误 |
docker-compose.dev.yml:108-133 ai-core 在 iot-debug-service 之前定义,但 depends_on 引用了后者 |
Docker Compose 会按依赖顺序启动,但配置中 ai-core 出现在 iot-debug-service 前面,可能造成启动顺序混乱 |
🟡 中等 (P1)
| # |
问题 |
位置 |
影响 |
| 3 |
memory-service 和 tool-engine Dockerfile 缺少健康检查 |
backend/memory-service/Dockerfile 和 backend/tool-engine/Dockerfile 没有 HEALTHCHECK 指令 |
Docker Compose depends_on: condition: service_healthy 将无法判断这两个服务是否就绪 |
| 4 |
memory-service 和 tool-engine Dockerfile 缺少非 root 用户 |
同上,缺少 RUN adduser -D -H cyrene && USER cyrene |
容器以 root 运行,违反最小权限原则 |
| 5 |
memory-service 和 tool-engine Dockerfile 缺少时区设置 |
同上,缺少 tzdata 安装和 Asia/Shanghai 时区配置 |
日志时间戳使用 UTC,与 gateway/ai-core 不一致 |
| 6 |
Redis 生产环境无密码 |
docker-compose.yml:38 REDIS_PASSWORD: ${REDIS_PASSWORD:-} 默认为空 |
生产环境 Redis 无密码保护,存在安全隐患 |
| 7 |
Qdrant 和 MinIO 无健康检查 |
docker-compose.yml:133-147 |
无法在 depends_on 中使用 condition 判断这些服务是否就绪 |
🟢 轻微 (P2)
| # |
问题 |
位置 |
影响 |
| 8 |
Alpine 版本不一致 |
gateway/ai-core/iot-debug 用 alpine:3.20,memory-service/tool-engine/voice-service 用 alpine:3.21 |
镜像版本碎片化,增加维护成本和安全扫描复杂度 |
| 9 |
memory-service/tool-engine Dockerfile 缺少 -ldflags="-s -w" |
编译命令缺少 strip 参数 |
二进制文件体积更大(约多 30%) |
| 10 |
voice-service Dockerfile 只复制 go.mod 不复制 go.sum |
backend/voice-service/Dockerfile:9 COPY go.mod ./ 缺少 go.sum |
go mod download 时可能下载不一致的依赖版本 |
| 11 |
NATS 在开发环境定义但未被使用 |
docker-compose.dev.yml:54-58 |
占用资源,增加启动时间;如果计划使用 NATS 则无问题 |
| 12 |
生产环境不暴露后端端口 |
docker-compose.yml gateway 等服务没有 ports: 映射 |
这是合理的设计(通过 Caddy 反向代理),但 gateway 的 Dockerfile healthcheck 使用 localhost:8080,依赖 Caddy 链路可能不准确 |
1.3 Dockerfile 质量对比
| 特性 |
Gateway |
AI-Core |
IoT-Debug |
Voice-Svc |
Memory-Svc |
Tool-Engine |
| 多阶段构建 |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
| 静态编译 (CGO_ENABLED=0) |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
| Strip 二进制 (-ldflags="-s -w") |
✅ |
✅ |
✅ |
✅ |
❌ |
❌ |
| 非 root 用户 |
✅ |
✅ |
✅ |
✅ |
❌ |
❌ |
| HEALTHCHECK |
✅ |
✅ |
✅ |
✅ |
❌ |
❌ |
| 时区设置 (Asia/Shanghai) |
✅ |
✅ |
✅ |
✅ |
❌ |
❌ |
| ca-certificates |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
| git (构建阶段) |
✅ |
✅ |
✅ |
✅ |
❌ |
❌ |
| go.sum 复制 |
✅ |
✅ |
N/A |
❌ |
✅ |
✅ |
| 运行时资源文件复制 |
N/A |
✅ persona |
N/A |
N/A |
N/A |
N/A |
1.4 网络配置
生产环境使用 Docker 内部 DNS(服务名即主机名),服务间通信正确:
gateway → ai-core:8081, memory-service:8091, tool-engine:8092, voice-service:8093, iot-debug-service:8083
tool-engine → iot-debug-service:8083
- 所有服务 →
postgres:5432
2. PWA 配置审计
2.1 文件清单
2.2 🔴 严重问题 (P0)
问题 13: Service Worker 重复注册
frontend/web/src/main.tsx:7-15 和 frontend/web/src/hooks/usePWA.ts:25-56 都注册了 Service Worker (/sw.js)。
main.tsx 在 window.load 事件中注册
usePWA.ts 的 registerServiceWorker() 函数也在 window.load 中注册
两个注册会产生竞争条件:第二个注册调用可能覆盖第一个,导致 SW 更新监听器丢失。如果 usePWA hook 未被挂载(例如未渲染 Header 组件),SW 仍会被 main.tsx 注册,但 usePWA 的更新检测将失效。
2.3 🟡 中等问题 (P1)
2.4 🟢 轻微问题 (P2)
2.5 优点
- ✅
sw.js 缓存策略合理:静态资源缓存优先,API 网络优先,WebSocket 不缓存
- ✅
offline.html 设计良好,包含自动重连和优雅降级
- ✅
usePWA.ts 全面覆盖 PWA 生命周期:安装提示、更新检测、在线/离线状态
- ✅ Push 通知点击支持深度链接到会话
- ✅ manifest.json 包含
share_target 和 shortcuts
3. WebSocket 代码审查与测试
3.1 架构概览
3.2 连接测试结果
WebSocket 端点可正常访问,认证机制生效。
3.3 代码质量评估
| 特性 |
状态 |
说明 |
| 客户端管理 (register/unregister) |
✅ |
使用 channel 模式的 Hub,线程安全 |
| 用户索引 (userClients) |
✅ |
支持按用户 ID 快速查找连接 |
| 会话状态追踪 |
✅ |
支持 idle/thinking/streaming/error 状态 |
| 广播机制 |
✅ |
BroadcastToAll, SendToUser, SendToSession |
| 闲置清理 |
✅ |
每5分钟清理,标记超时会话为 idle |
| IoT 设备轮询 |
✅ |
每10秒从 IoT 服务获取设备状态并广播 |
| 对话缓存 |
✅ |
sync.Map 缓存,最多50条消息 |
| 持久化存储集成 |
✅ |
可选 SessionStore 注入 |
| 特性 |
状态 |
说明 |
| Ping/Pong 心跳 |
✅ |
服务端每54秒发送 Ping,客户端60秒内需回复 Pong |
| 读写超时 |
✅ |
写超时10s,读超时60s(基于 Pong) |
| 消息大小限制 |
✅ |
最大 65536 字节 |
| 优雅关闭 |
✅ |
通道关闭时发送 CloseMessage |
| 通道满处理 |
✅ |
记录日志而非静默丢弃 |
消息类型支持完整:
- 客户端消息:
message, voice_input, ping, history
- 服务端消息:
response, stream_chunk, stream_end, history_response, device_update, notification, background_thinking, multi_message, stream_segments, pong, error
3.4 前端 WebSocket Hook
frontend/web/src/hooks/useWebSocket.ts (275行)
| 特性 |
状态 |
说明 |
| 自动重连 |
✅ |
断开后3秒自动重连 |
| 会话感知 |
✅ |
currentSessionId 变化时重建连接 |
| 消息类型处理 |
✅ |
完整覆盖所有服务端消息类型 |
| 历史竞态防护 |
✅ |
HTTP 已加载时忽略 WS history_response |
| 桌面通知集成 |
✅ |
Notification API 集成 |
| 连接状态反馈 |
✅ |
详细日志,包含 instance ID |
| 消息丢弃保护 |
✅ |
未就绪时发送提示消息 |
3.5 发现的问题
🔴 严重 (P0)
| # |
问题 |
位置 |
说明 |
| 20 |
Hub.Run() 广播循环中 RLock 下执行写操作 |
backend/gateway/internal/ws/hub.go:239-249 |
case message := <-h.broadcast: 块中使用了 h.mu.RLock() 但在 default 分支中执行 delete(h.clients, client) 和 close(client.Send) — 这是写操作,应该使用 Lock() 而非 RLock()。虽然 close(channel) 和 delete from map 在 Go 中可能不直接 panic,但这是不正确的锁使用,可能导致竞态条件 |
🟡 中等 (P1)
🟢 轻微 (P2)
3.6 WebSocket 安全性总结
| 安全维度 |
状态 |
| 认证 |
✅ JWT token(query 参数或 Authorization header) |
| 授权 |
✅ 主对话仅限 admin_ 前缀用户 |
| 传输加密 |
⚠️ 当前为 ws://(明文),生产需 Caddy TLS 升级为 wss:// |
| 消息验证 |
✅ JSON 解析失败时仅记录日志继续 |
| 输入大小限制 |
✅ 最大 65536 字节 |
| 跨域控制 |
⚠️ CheckOrigin 全允许 |
| 速率限制 |
❌ 无 |
4. 前端构建配置审计
| 配置项 |
状态 |
说明 |
| React 插件 |
✅ |
@vitejs/plugin-react |
| 路径别名 |
✅ |
@/ → ./src/ |
| 开发服务器端口 |
✅ |
5173 |
| API 代理 |
✅ |
/api → http://localhost:8080 |
| WebSocket 代理 |
✅ |
/ws → ws://localhost:8080 (ws: true) |
4.2 发现的问题
🟡 中等 (P1)
🟢 轻微 (P2)
| # |
问题 |
位置 |
说明 |
| 29 |
缺少环境变量类型声明 |
使用的 import.meta.env.VITE_WS_URL 未在 vite-env.d.ts 中声明 |
TypeScript 可能报类型错误 |
5. 问题汇总与修复优先级
统计
| 严重级别 |
数量 |
| 🔴 严重 (P0) |
4 |
| 🟡 中等 (P1) |
12 |
| 🟢 轻微 (P2) |
13 |
| 总计 |
29 |
P0 修复列表(必须立即修复)
| # |
问题 |
修复建议 |
| 1 |
Caddyfile 缺失 |
创建 Caddyfile 或改用 Nginx/Traefik |
| 2 |
docker-compose.dev.yml 服务定义顺序 |
将 iot-debug-service 移到 ai-core 之前 |
| 13 |
SW 重复注册 |
移除 main.tsx 中的 SW 注册,仅保留 usePWA.ts 的 registerServiceWorker() |
| 20 |
Hub.Run() 广播 RLock 下写操作 |
将 h.mu.RLock() 改为 h.mu.Lock(),或将 close/delete 操作移至单独加写锁的块 |
P1 修复列表(建议本迭代修复)
- Docker: memory-service/tool-engine 添加 HEALTHCHECK、非 root 用户、时区设置、ldflags;Redis 生产密码;voice-service 补全 go.sum
- PWA: 添加 Apple meta 标签;SW 缓存资源列表扩展;CACHE_NAME 使用构建时变量
- WebSocket: CheckOrigin 限制生产域名;添加消息速率限制;实现 voice_input
- 构建: 集成 vite-plugin-pwa;添加生产构建配置
总体评价
- Docker 配置: gateway/ai-core/iot-debug 三个核心服务的 Dockerfile 质量优秀(多阶段构建、非 root 用户、健康检查、二进制 strip、时区设置),但 memory-service 和 tool-engine 需要补齐。最严重的问题是 Caddyfile 缺失导致生产环境无法启动。
- PWA: 基础实现扎实,离线页面设计精良,但 SW 重复注册和缺少 Apple meta 标签是两个需要修复的问题。
- WebSocket: 架构设计成熟,Hub 模式 + 用户索引 + 会话追踪 + 对话缓存 + IoT 广播功能完整。主要问题是广播循环中的锁使用不正确和缺少速率限制。前端 useWebSocket hook 质量高,包含自动重连和竞态防护。
- 构建配置: 基础配置合理,但缺少 PWA 插件自动化和生产构建优化。