monitor-ai/CLAUDE.md

20 KiB
Raw Permalink Blame History

CLAUDE.md — monitor.tlyq.ai 告警监控中心

项目概述

monitor-ai 是独立部署的统一告警监控中心,监控 tlyq.ai 基础设施所有容器Authelia、Redis、LLDAP、OA、assets、issue、Gitea、Nginx、www、cloud、token的健康状态通过企业微信 Webhook 推送告警。部署在腾讯云txjp 服务器),域名 monitor.tlyq.ai


快速参考

属性
站点域名 monitor.tlyq.ai
服务器 txjpIP: 43.133.38.210
代码路径 /root/docker/monitor-ai/
本地端口 6181
容器名 monitor-ai
数据库 SQLitedata/monitor.db
默认账号 localadmin / 首次部署自动生成

常用命令

cd /Users/niuniu/programs/docker/monitor-ai
npm run dev          # 本地开发http://localhost:6181
npm run build        # 生产构建
npm run start        # 启动生产服务

技术栈

技术 版本 用途
Next.js 15 App Router Web 框架standalone 输出)
SQLite 3.x 数据库(通过 execFileSync + sqlite3 CLI
Tailwind CSS v4 样式(@tailwindcss/postcss
lucide-react ^1.8.0 图标库
ldapts ^6.0.0 LDAP 认证客户端
openid-client ^5.7.1 OIDC 认证
tsx ^4.0.0 Worker TypeScript 执行

目录结构

monitor-ai/
├── Dockerfile                  # 两阶段构建node:22-alpine
├── docker-compose.yml          # 容器编排webnet external
├── entrypoint.sh               # Docker 入口双进程守护Worker + Server
├── next.config.ts              # output: standalone, transpilePackages
├── shared -> ../shared         # 共享库符号链接
├── scripts/
│   ├── monitor-worker.ts       # Worker 独立入口
│   ├── deploy-monitor.sh       # 部署脚本
│   └── monitor-watchdog.sh     # 健康检查兜底脚本
├── src/
│   ├── middleware.ts            # Edge Runtime 路由守卫
│   ├── app/
│   │   ├── page.tsx            # 仪表盘(四层:标题 + 状态条 + KPI + 服务卡片)
│   │   ├── login/page.tsx      # 登录页
│   │   ├── services/page.tsx   # 服务管理
│   │   ├── settings/page.tsx   # 告警设置
│   │   ├── alerts/page.tsx     # 告警历史
│   │   ├── status-history/page.tsx  # 状态变更历史
│   │   ├── admin/              # 管理页面users/roles/audit-logs
│   │   └── api/                # API 路由
│   ├── components/
│   │   ├── Sidebar.tsx         # 侧边栏导航
│   │   ├── ThemeProvider.tsx   # 主题上下文
│   │   └── ThemeToggle.tsx     # 主题切换
│   └── lib/
│       ├── config.ts           # 配置常量
│       ├── db.ts               # 数据库操作封装execFileSync + escapeSql
│       ├── auth-config.ts      # 认证配置
│       ├── permissions.ts      # RBAC 权限定义
│       ├── audit.ts            # 审计日志封装
│       └── monitor-worker.ts   # MonitorWorker 核心类
└── data/
    └── monitor.db              # SQLite 数据库

关键文件

文件 职责
src/lib/monitor-worker.ts MonitorWorker 核心类30s tick健康检查 + 状态管理 + 告警推送)
src/lib/db.ts SQLite 操作封装execFileSync + escapeSql无 ORM
src/lib/permissions.ts RBAC 权限定义11 个权限点admin/editor/viewer/localadmin
src/lib/audit.ts 审计日志封装(调用 @shared/lib/audit
src/lib/auth-config.ts 认证配置OIDC + LDAP
src/middleware.ts Edge Runtime 路由守卫Cookie JWT 验证,公开/管理路径控制)
scripts/monitor-worker.ts Worker 独立入口(信号处理、优雅退出)
entrypoint.sh Docker 入口双进程守护Worker + Server任一退出则容器退出
shared/lib/alert/ 告警引擎AlertManager、HealthChecker、类型定义
shared/lib/wechat/ 企业微信推送WeChatPusher、消息格式化

数据库 Schema

表概览

表名 说明 来源
services 被监控服务列表 @shared/lib/db/alert-schema
alert_channels Webhook 告警渠道配置 @shared/lib/db/alert-schema
alert_settings 全局设置 + 冷却状态持久化 @shared/lib/db/alert-schema
status_history 服务状态变更事件 @shared/lib/db/alert-schema
alert_history 告警推送记录 @shared/lib/db/alert-schema
users 用户账号 src/lib/db.ts(内联)
permissions 权限定义 src/lib/db.ts(内联)
role_permissions 角色-权限映射 src/lib/db.ts(内联)
audit_logs 审计日志 @shared/lib/audit/audit-schema

services 表

字段 类型 说明
id INTEGER PK 主键
name TEXT 服务名称
category TEXT 分类endpoint / container / custom
alert_level TEXT 告警级别critical / warning / info
checks TEXT 检查配置JSON 数组HealthCheckConfig[]
check_interval INTEGER 检查间隔秒数(默认 30
check_timeout INTEGER 检查超时秒数(默认 10
enabled INTEGER 是否启用(默认 1
current_status TEXT 当前状态normal / abnormal / unknown
status_since TEXT 状态持续起始时间
display_order INTEGER 显示排序

alert_channels 表

字段 类型 说明
id INTEGER PK 主键
name TEXT 渠道名称
channel_type TEXT 渠道类型(默认 'wechat'
webhook_url TEXT Webhook 地址
enabled INTEGER 是否启用
level_critical INTEGER 是否接收 critical 告警
level_warning INTEGER 是否接收 warning 告警
level_info INTEGER 是否接收 info 告警
quiet_enabled INTEGER 是否启用免打扰
quiet_start TEXT 免打扰开始时间
quiet_end TEXT 免打扰结束时间
quiet_bypass_critical INTEGER critical 是否绕过免打扰
cooldown_minutes INTEGER 冷却时间(分钟)

status_history 表

字段 类型 说明
id INTEGER PK 主键
service_id INTEGER FK 关联服务
from_status TEXT 原状态
to_status TEXT 新状态
started_at TEXT 状态开始时间
ended_at TEXT 状态结束时间
duration_seconds INTEGER 持续时长
error_message TEXT 错误信息
failed_checks TEXT 失败检查详情JSON
truncated_by_restart INTEGER 是否被重启截断

alert_history 表

字段 类型 说明
id INTEGER PK 主键
channel_id INTEGER FK 告警渠道
service_id INTEGER FK 关联服务
status_event_id INTEGER FK 关联状态事件
alert_type TEXT 告警类型alert / recovery / flapping
level TEXT 告警级别
title TEXT 标题
content TEXT 内容
sent_at TEXT 发送时间
success INTEGER 是否成功
response_code INTEGER HTTP 响应码

users 表

字段 类型 说明
id INTEGER PK 主键
username TEXT UNIQUE 用户名
display_name TEXT 显示名
email TEXT 邮箱
role TEXT 角色admin / editor / viewer
password_hash TEXT 密码哈希
is_active INTEGER 是否启用

默认用户:localadminadmin 角色BCrypt 密码哈希)

预置角色

角色 权限 说明
admin 全部 11 个权限 管理员
editor dashboard:view, services:view, alerts:view, status_history:view, status_history:stats 编辑者
viewer dashboard:view, services:view, status_history:view 只读
localadmin 硬编码绕过hasPermission 始终返回 true 应急管理员

权限列表

dashboard:viewservices:viewservices:managealerts:viewalerts:managestatus_history:viewstatus_history:statsaudit:viewusers:viewusers:manageroles:manage


API 路由

公开 API

方法 路径 说明
GET /api/health 自身健康检查(返回 {status:'OK'}

认证

登录逻辑OIDC SSO 优先 + LDAP 本地登录 + localadmin 应急用户。 登录成功签发 tlyq_session cookie共享 JWTdomain=.tlyq.ai。 中间件验证 JWT payloadEdge Runtime不解密签名仅解码 payload 检查过期时间)。

方法 路径 说明
GET /api/auth/login/oidc OIDC SSO 重定向到 Authelia
POST /api/auth/login LDAP 或 localadmin 登录
GET /api/auth/callback OIDC 回调处理
POST /api/auth/logout 登出(清除 cookie重定向 Authelia end_session
GET /api/auth/me 当前用户信息

服务管理

方法 路径 权限 说明
GET /api/services 登录 服务列表(含解析后的 JSON checks
POST /api/services services:manage 新增服务
PUT /api/services/[id] services:manage 更新服务
DELETE /api/services/[id] services:manage 删除服务
POST /api/services/[id]/check services:manage 手动触发单次健康检查

告警渠道

方法 路径 权限 说明
GET /api/alert-channels alerts:manage 渠道列表webhook_url 脱敏)
POST /api/alert-channels alerts:manage 新增渠道
PUT /api/alert-channels/[id] alerts:manage 更新渠道
DELETE /api/alert-channels/[id] alerts:manage 删除渠道
POST /api/alert-channels/[id]/test alerts:manage 测试推送

告警历史 / 状态历史

方法 路径 权限 说明
GET /api/alerts alerts:view 告警历史(分页)
GET /api/status-history 登录 状态变更历史(分页,可按 service_id 筛选)
GET /api/status 登录 所有服务实时状态汇总

内部 APIx-internal-key 鉴权)

方法 路径 说明
GET /api/internal/users 返回用户列表
POST /api/internal/users OA 同步用户角色

管理

方法 路径 权限 说明
GET /api/admin/worker-status dashboard:view Worker 运行状态

Worker 架构

MonitorWorker 核心类

位置:src/lib/monitor-worker.ts

MonitorWorker
├── HealthChecker@shared/lib/alert/health-checker
│   ├── HttpChecker  — HTTP 状态码 + 响应体匹配
│   └── DockerChecker — docker ps 容器状态检查
├── AlertManager@shared/lib/alert/alert-manager
│   ├── evaluate()   — 三阶段告警决策(级别匹配 → 免打扰 → 冷却)
│   └── CooldownProvider — alert_settings 表持久化冷却状态
└── WeChatPusher@shared/lib/wechat/wechat-pusher
    └── pushMarkdown() — 企业微信 Webhook 推送

运行机制

阶段 间隔 说明
健康检查 30 秒 tick 遍历所有 enabled 服务并发执行检查Promise.allSettled
抖动检测 10 分钟窗口 同一服务 10 分钟内状态切换 >= 5 次 → 触发 flapping 告警,抑制后续告警
持续异常提醒 30 分钟 服务持续 abnormal 状态,每 30 分钟发送提醒
恢复通知 即时 服务恢复正常时发送 recovery 通知,绕过免打扰和冷却
WAL 检查点 1 小时 被动 WAL checkpointPASSIVE
数据清理 每天 删除 180 天前的 status_history 和 alert_history每批 LIMIT 1000

Docker 入口entrypoint.sh

entrypoint.sh
├── npx tsx scripts/monitor-worker.ts &   # 后台启动 Worker
├── node server.js &                       # 后台启动 Next.js Server
└── 守护循环:任一进程退出则容器退出       # 保证双进程存活
    └── trap SIGTERM/SIGINT → 清理退出

认证机制

  • OIDC SSO:通过 Authelia 进行 PKCE 授权码流程,登录成功后签发 tlyq_session 共享 JWT cookie
  • LDAP 本地登录:通过 LLDAP 进行 bind 认证,签发 tlyq_session cookie
  • localadmin:纯本地 BCrypt 认证,不依赖 LLDAP/OIDC用于应急登录
  • Edge Runtime 中间件src/middleware.ts 运行在 Edge Runtime使用 atob 解码 JWT payload 检查过期时间,不验证签名(签名验证在 route handler 层)

环境配置

本地与云端差异

环境变量 本地开发 云服务器txjp 说明
DATABASE_PATH ./data/monitor.db /app/data/monitor.db Docker volume 挂载
MONITOR_MODE dev local 运行模式
AUTHELIA_URL https://127.0.0.1:6180 https://sso.tlyq.ai Authelia 地址
OIDC_CLIENT_ID monitor-oidc OIDC 客户端 ID
OIDC_CLIENT_SECRET 本地生成的值 服务器生成的值 OIDC 客户端密钥(每个环境独立)
OIDC_REDIRECT_URI http://localhost:6181/api/auth/callback https://monitor.tlyq.ai/api/auth/callback OIDC 回调地址
JWT_SECRET dev-jwt-secret-change-in-production 部署脚本自动生成 JWT 签名密钥
COOKIE_DOMAIN .tlyq.ai Cookie 域
LDAP_URL ldap://ldap-ai:3890 ldap://ldap-ai:3890 LLDAP 地址Docker 内网)
LOCALADMIN_PASSWORD admin123 部署脚本自动生成 应急管理员密码
NODE_TLS_REJECT_UNAUTHORIZED 0 0 TLS 证书验证Authelia 自签名)

.env.local 示例

DATABASE_PATH=./data/monitor.db
MONITOR_MODE=dev
AUTHELIA_URL=https://127.0.0.1:6180
OIDC_CLIENT_ID=monitor-oidc
OIDC_CLIENT_SECRET=<本地生成的值>
OIDC_REDIRECT_URI=http://localhost:6181/api/auth/callback
JWT_SECRET=dev-jwt-secret-change-in-production
COOKIE_DOMAIN=.tlyq.ai
LDAP_URL=ldap://ldap-ai:3890
LOCALADMIN_PASSWORD=admin123
NODE_TLS_REJECT_UNAUTHORIZED=0

共享库引用

用途 导入路径
@shared/lib/auth/jwt-v2 JWT 签名/验证HS256含 iss import { signJwtV2, verifyJwtV2 } from '@shared/lib/auth/jwt-v2'
@shared/lib/auth/jwt JWT 签名/验证V1 兼容,无 iss import { signJwt, verifyJwt } from '@shared/lib/auth/jwt'
@shared/lib/auth/middleware-v2 路由守卫工厂V2单 cookie 模型) import { createMiddlewareV2 } from '@shared/lib/auth/middleware-v2'
@shared/lib/auth/middleware 路由守卫工厂V1已废弃 import { createMiddleware } from '@shared/lib/auth/middleware'
@shared/lib/auth/oidc OIDC PKCE 流程 import { discoverOidcConfig, buildAuthorizeUrl } from '@shared/lib/auth/oidc'
@shared/lib/auth/ldap LDAP 认证 import { ldapAuth } from '@shared/lib/auth/ldap'
@shared/lib/alert/alert-manager 告警决策引擎 import { AlertManager } from '@shared/lib/alert/alert-manager'
@shared/lib/alert/health-checker 健康检查引擎 import { HealthChecker, HttpChecker, DockerChecker } from '@shared/lib/alert/health-checker'
@shared/lib/audit/write-audit-log 审计日志 import { writeAuditLog } from '@shared/lib/audit/write-audit-log'
@shared/lib/wechat/wechat-pusher 企业微信推送 import { WeChatPusher } from '@shared/lib/wechat/wechat-pusher'
@shared/lib/wechat/message-formatter 消息格式化 import { formatAlertLevel } from '@shared/lib/wechat/message-formatter'
@shared/lib/db/alert-schema 数据库 Schema 常量 import { ALL_TABLES_SQL } from '@shared/lib/db/alert-schema'
@shared/ui UI 组件库 import { Button, Card, Badge } from '@shared/ui'
@shared/ui/login-page 统一登录页 import LoginPage from '@shared/ui/login-page'

路径映射tsconfig.json 中配置 "@shared/*": ["./shared/*"]shared 是指向 ../shared 的符号链接。


关键设计决策

execFileSync 数组参数

数据库操作使用 execFileSync('sqlite3', [dbPath, '-json', sql]) 而非 ORM。SQL 通过数组参数传递,避免 shell 注入。所有用户输入通过 escapeSql() 函数转义(单引号双写)。

Edge Runtime 中间件

src/middleware.ts 运行在 Edge Runtime不能使用 Node.js crypto 模块。JWT 验证仅解码 payload 检查过期时间,签名验证留给 route handler 层处理。

双进程守护

entrypoint.sh 同时启动 Worker 和 Server使用 bash 守护循环监控。任一进程退出则整个容器退出,由 Docker 重启策略接管。

冷却状态持久化

告警冷却状态存储在 alert_settings 表中key 格式:cooldown:{channel_id}:{service_id}:{level}),容器重启后冷却状态不丢失。


Docker 部署

txjp 服务器
├── monitor-ai容器              ← Next.js standalone + Worker监听 6181
├── nginx-ai                        ← 反向代理 monitor.tlyq.ai → monitor-ai:6181
└── webnetexternal              ← 共享网络

部署:bash deploy-monitor.sh(本地模式)或 bash deploy-monitor.sh --remote --host <IP>

源码打包上传 → 服务器 npm install + npm run build.next 挂载进容器生效。

生产环境变量

DATABASE_PATH=/app/data/monitor.db
MONITOR_MODE=local
AUTHELIA_URL=https://sso.tlyq.ai
OIDC_CLIENT_ID=monitor-oidc
OIDC_CLIENT_SECRET=<部署脚本自动生成>
OIDC_REDIRECT_URI=https://monitor.tlyq.ai/api/auth/callback
JWT_SECRET=<部署脚本自动生成>
COOKIE_DOMAIN=.tlyq.ai
LDAP_URL=ldap://ldap-ai:3890
LOCALADMIN_PASSWORD=<部署脚本自动生成>
NODE_ENV=production
NODE_TLS_REJECT_UNAUTHORIZED=0

开发规范

  • 新增 API:在 src/app/api/ 下创建路由 → 顶部调用 initDatabase()getCurrentUser() 验证 → checkPermission() 校验
  • 新增页面:在 src/app/ 下创建 → 布局由 client-layout.tsx 提供Sidebar + ThemeProvider
  • 权限格式resource:action,如 checkPermission(user, 'services:manage')
  • 审计日志:所有写操作 APIPOST/PUT/DELETE必须添加审计日志
    import { writeAuditLog } from '@/lib/audit'
    
    writeAuditLog({
      userId: user.id,
      action: 'create' | 'update' | 'delete',
      entityType: 'service' | 'alert_channel' | 'user',
      entityId: id,
      details: { ... },
      ipAddress: getClientIP(request)
    })
    
  • 日期处理(时区规范):整个系统统一使用 UTC+8北京时间。必须遵守
    1. JavaScript/TypeScript:禁止使用 Date.toISOString() 格式化本地日期,应使用本地时间方法拼接
    2. SQLite:所有 datetime('now') 必须写成 datetime('now', '+8 hours')
    3. INSERT 时间列:数据库表 DEFAULT 已统一为 datetime('now', '+8 hours')INSERT 时可省略

健康检查兜底

scripts/monitor-watchdog.sh 是独立于 monitor-ai 的兜底脚本,通过 crontab 每 5 分钟执行:

*/5 * * * * /root/docker/monitor-ai/scripts/monitor-watchdog.sh >> /var/log/monitor-watchdog.log 2>&1

/api/health 连续失败时,直接通过企业微信 Webhook 发送告警,并尝试重启容器。


故障排查

# 容器日志
ssh txjp "docker logs monitor-ai"

# Worker 日志(实时)
ssh txjp "docker logs -f monitor-ai 2>&1 | grep -i worker"

# 数据库查看
ssh txjp "docker exec monitor-ai sqlite3 /app/data/monitor.db 'SELECT id, name, current_status FROM services;'"

# 健康检查
ssh txjp "curl -s -k https://monitor.tlyq.ai/api/health"

# 手动触发检查
ssh txjp "docker exec monitor-ai sqlite3 /app/data/monitor.db 'UPDATE services SET current_status=\"unknown\";'"

# 重建镜像(新增依赖后)
ssh txjp "cd /root/docker/monitor-ai && docker compose build --no-cache && docker compose down && docker compose up -d"

Git Tag 规范

使用日期版本号 vYYYY.MM.DD(如 v2026.07.01)。提交后打 tag 再推送:

git tag v$(date +%Y.%m.%d) && git push origin main && git push origin v$(date +%Y.%m.%d)

同一天多次提交只打一个 tag。详见根目录 CLAUDE.md