# CLAUDE.md — shared 共享库 ## 概述 shared 是 tlyq.ai 项目群的共享库,包含后端认证、告警、审计、企业微信推送等标准模块,以及前端 React UI 组件库。所有模块遵循零业务依赖、配置驱动、TypeScript 优先的设计原则。 --- ## 目录结构 ``` shared/ ├── lib/ # 后端服务端库(无 barrel,按路径直接导入) │ ├── auth/ # 认证标准库 │ │ ├── types.ts # 类型定义(AuthConfig, SessionPayload, OidcUserinfo 等) │ │ ├── jwt.ts # JWT 签名/验证(零依赖,Node crypto) │ │ ├── ldap.ts # LDAP 认证(ldapts 客户端) │ │ ├── oidc.ts # OIDC PKCE 流程(openid-client) │ │ ├── middleware.ts # Next.js 路由守卫工厂 │ │ └── user-sync.ts # OIDC 用户同步到本地 SQLite │ ├── alert/ # 告警引擎 │ │ ├── types.ts # 类型定义(AlertLevel, ServiceStatus, CheckResult 等) │ │ ├── alert-manager.ts # AlertManager 告警决策引擎 │ │ └── health-checker.ts # HealthChecker 健康检查引擎 │ ├── audit/ # 审计日志 │ │ ├── audit-schema.ts # audit_logs 表 DDL │ │ └── write-audit-log.ts # writeAuditLog 函数 │ ├── db/ # 数据库 Schema 定义 │ │ └── alert-schema.ts # 5 张表 DDL(services, alert_channels 等) │ └── wechat/ # 企业微信 Webhook │ ├── message-formatter.ts # 消息格式化 │ └── wechat-pusher.ts # WeChatPusher 推送客户端 └── ui/ # 前端 React 组件库(@tlyq/shared-ui) ├── package.json # 发布为 @tlyq/shared-ui v1.0.0 ├── index.tsx # Barrel 导出(9 个组件) ├── Button.tsx # 按钮(4 种 variant,3 种 size,loading 状态) ├── Badge.tsx # 标签(5 种 variant) ├── Card.tsx # 卡片容器 ├── Input.tsx # 输入框(含 label 和 error) ├── Select.tsx # 下拉选择 ├── Modal.tsx # 模态框(backdrop 点击关闭,body scroll 锁定) ├── Table.tsx # 表格(styled thead,tbody 由调用方提供) ├── Toast.tsx # 通知条(右下角固定定位) ├── Pagination.tsx # 分页(中文标签:"上一页"/"下一页") └── login-page.tsx # 统一登录页(SSO + LDAP 切换,不在 barrel 中导出) ``` --- ## 设计原则 ### 零业务依赖 共享库不包含任何站点特定的业务逻辑。所有需要外部状态(数据库、配置)的模块通过 TypeScript 接口注入依赖,不耦合具体实现。 ### 配置驱动 模块行为通过配置对象控制,而非硬编码。例如: - `createMiddleware(config)` 通过 `AuthConfig` 配置公开路径、管理路径、cookie 名称 - `AlertManager` 通过 `CooldownProvider` 接口获取冷却状态 - `HealthChecker` 通过 `registerChecker()` 注册检查器 ### TypeScript 优先 所有模块使用 TypeScript 编写,导出完整类型定义。消费方通过 `tsconfig.json` 的 `paths` 映射导入。 --- ## 各模块详解 ### lib/auth/ — 认证标准库 统一认证栈:OIDC SSO + LDAP 本地登录 + localadmin 应急用户 + 共享 JWT session。 #### types.ts ```typescript interface AuthConfig { jwtSecret: string cookieDomain: string cookieName?: string // 默认 'tlyq_session' cookieMaxAge?: number // 默认 7 天(秒) publicPaths?: string[] // 公开路径(不需要认证) adminPaths?: string[] // 管理路径(需要 admin 角色) ldapUrl?: string ldapBaseDn?: string autheliaUrl?: string oidcClientId?: string oidcClientSecret?: string oidcRedirectUri?: string } interface SessionPayload { sub: string // 用户 ID 或 username username: string role: string exp: number iat: number } interface OidcUserinfo { sub: string preferred_username: string email?: string name?: string groups?: string[] } interface LdapAuthResult { success: boolean username?: string displayName?: string email?: string isAdmin?: boolean error?: string } ``` #### jwt.ts ```typescript signJwt(options: { payload: SessionPayload, secret: string, expiresIn?: number }): string verifyJwt(token: string, secret: string): SessionPayload | null sharedCookieConfig(domain: string, maxAge?: number): CookieConfig ``` 零外部依赖,使用 Node.js `crypto` 模块实现 HS256 JWT 签名和验证。 #### ldap.ts ```typescript ldapAuth(config: { url: string, baseDn: string }, username: string, password: string): Promise checkAdminGroup(config: { url: string, baseDn: string }, username: string): Promise ``` 使用 `ldapts` 客户端进行 LDAP bind 认证和管理员组成员检查。 #### oidc.ts ```typescript discoverOidcConfig(url: string): Promise generatePkce(): { codeVerifier: string, codeChallenge: string } generateState(): string buildAuthorizeUrl(config: OidcConfig, params: { clientId: string, redirectUri: string, state: string, codeChallenge: string }): string exchangeCodeForToken(config: OidcConfig, code: string, verifier: string): Promise getUserinfo(url: string, accessToken: string): Promise ``` 完整的 OIDC 授权码 + PKCE 流程,基于 Authelia 实现。 #### middleware.ts ```typescript createMiddleware(config: AuthConfig): NextMiddleware ``` Next.js 路由守卫工厂。验证 `tlyq_session` cookie(JWT payload 解码,检查过期时间)。公开路径放行,管理路径检查 admin 角色,未认证请求重定向到 `/login`。 #### user-sync.ts ```typescript syncOidcUser(store: UserSyncStore, userinfo: OidcUserinfo): Promise<{ id: number, role: string, isNew: boolean }> ``` 将 OIDC userinfo 同步到本地 SQLite users 表。`UserSyncStore` 是依赖注入接口,不耦合具体数据库驱动。 --- ### lib/alert/ — 告警引擎 告警决策和健康检查的核心引擎。 #### types.ts ```typescript type AlertLevel = 'critical' | 'warning' | 'info' type ServiceStatus = 'normal' | 'abnormal' | 'unknown' type CheckType = 'http' | 'docker' | 'ssh' type AlertType = 'alert' | 'recovery' | 'flapping' interface HealthCheckConfig { type: CheckType url?: string // HTTP 检查 expectedStatus?: number expectedBody?: string containerName?: string // Docker 检查 command?: string // SSH 检查 } interface CheckResult { success: boolean responseTime?: number errorMessage?: string } interface AlertDecision { shouldAlert: boolean reason?: string suppressedBy?: 'quiet_period' | 'cooldown' | 'flapping' } interface AlertChannelConfig { id: number levelCritical: boolean levelWarning: boolean levelInfo: boolean quietEnabled: boolean quietStart?: string quietEnd?: string quietBypassCritical: boolean cooldownMinutes: number } ``` #### alert-manager.ts ```typescript class AlertManager { constructor(cooldownProvider: CooldownProvider) evaluate(channel: AlertChannelConfig, serviceId: number, level: AlertLevel, now?: Date): AlertDecision isQuietPeriod(channel: AlertChannelConfig, now?: Date): boolean isCoolingDown(channelId: number, serviceId: number, level: AlertLevel, cooldownMinutes: number): boolean recordAlert(channelId: number, serviceId: number, level: AlertLevel): void } ``` 三阶段告警决策:级别匹配 → 免打扰检查(支持跨午夜) → 冷却窗口检查。 `CooldownProvider` 是依赖注入接口: ```typescript interface CooldownProvider { getCooldown(key: string): number | null // 返回 Unix 时间戳 setCooldown(key: string, timestamp: number): void } ``` #### health-checker.ts ```typescript class HealthChecker { registerChecker(type: CheckType, handler: Checker): void check(checks: HealthCheckConfig[], timeoutMs: number): Promise } class HttpChecker implements Checker { ... } class DockerChecker implements Checker { ... } ``` 策略模式:通过 `registerChecker()` 注册检查器,新检查类型(如 SSH)可无缝扩展。内部使用 `Promise.allSettled` 并发执行。 --- ### lib/audit/ — 审计日志 #### audit-schema.ts ```typescript const AUDIT_LOGS_TABLE_SQL: string // audit_logs 表 DDL ``` #### write-audit-log.ts ```typescript interface AuditLogEntry { userId?: number username?: string action: string entityType: string entityId?: number details?: Record ipAddress?: string } interface AuditStore { exec(sql: string): void } function writeAuditLog(store: AuditStore, entry: AuditLogEntry): void ``` 使用 SQL 字符串拼接 + 单引号转义(非参数化查询),这是已知的设计选择。 --- ### lib/db/ — 数据库 Schema #### alert-schema.ts ```typescript const SERVICES_TABLE_SQL: string const ALERT_CHANNELS_TABLE_SQL: string const ALERT_SETTINGS_TABLE_SQL: string const STATUS_HISTORY_TABLE_SQL: string const ALERT_HISTORY_TABLE_SQL: string const ALL_TABLES_SQL: string // 以上 5 个的拼接 ``` 定义监控相关的 5 张核心表,供各站点初始化数据库使用。 --- ### lib/wechat/ — 企业微信 Webhook #### message-formatter.ts ```typescript formatAvailabilityMessage(params: { serviceName: string, status: string, ... }): string formatAlertLevel(level: AlertLevel): string // 返回带 emoji 的级别标签 ``` #### wechat-pusher.ts ```typescript class WeChatPusher { constructor(webhookUrl: string) pushMarkdown(title: string, content: string): Promise pushText(content: string): Promise } class WeChatPusherCompat { // 兼容 issue-ai 的旧接口(返回 boolean) pushMarkdown(title: string, content: string): Promise } ``` 5 秒超时,markdown 格式推送。`WeChatPusherCompat` 为迁移提供向后兼容。 --- ### ui/ — React 组件库 所有组件均为 `'use client'`(Next.js App Router 兼容),使用 Tailwind CSS 工具类,支持 `dark:` 暗色模式。 **导出方式**: ```typescript import { Button, Card, Badge, Table, Modal, Input, Select, Toast, Pagination } from '@shared/ui' import LoginPage from '@shared/ui/login-page' // 单独导入,不在 barrel 中 ``` | 组件 | 核心 Props | 说明 | |------|-----------|------| | Button | `variant: 'primary'|'secondary'|'danger'|'ghost'`, `size: 'sm'|'md'|'lg'`, `loading: boolean` | forwardRef,loading 时显示 SVG spinner | | Badge | `variant: 'default'|'success'|'warning'|'danger'|'info'` | 药丸形状标签 | | Card | `children`, `className?` | 白色/暗色圆角容器 | | Input | `label?`, `error?` | 带标签和错误提示的输入框 | | Select | `label?`, `options: {value, label}[]` | 下拉选择器 | | Modal | `open: boolean`, `onClose`, `title?`, `maxWidth?` | 无 portal 模态框,backdrop 点击关闭,锁定 body scroll | | Table | `headers: string[]`, `children: ReactNode` | 样式化 thead,tbody 由调用方提供 | | Toast | `message`, `type: 'success'|'error'|'info'`, `onClose` | 右下角固定通知条 | | Pagination | `page`, `totalPages`, `onPageChange` | 中文标签,totalPages <= 1 时隐藏 | | LoginPage | `siteName: string`, `title?: string` | 完整登录页,支持 SSO 和 LDAP 切换 | --- ## 如何导入 ### tsconfig.json 配置 在消费方项目的 `tsconfig.json` 中添加路径映射: ```json { "compilerOptions": { "paths": { "@/*": ["./src/*"], "@shared/*": ["./shared/*"] } } } ``` ### 符号链接 在消费方项目根目录创建符号链接: ```bash cd /Users/niuniu/programs/docker/monitor-ai ln -s ../shared shared ``` ### 导入示例 ```typescript // 后端认证 import { signJwt, verifyJwt } from '@shared/lib/auth/jwt' import { ldapAuth } from '@shared/lib/auth/ldap' import { discoverOidcConfig, buildAuthorizeUrl } from '@shared/lib/auth/oidc' // 告警引擎 import { AlertManager } from '@shared/lib/alert/alert-manager' import { HealthChecker, HttpChecker, DockerChecker } from '@shared/lib/alert/health-checker' // 审计日志 import { writeAuditLog } from '@shared/lib/audit/write-audit-log' // 数据库 Schema import { ALL_TABLES_SQL } from '@shared/lib/db/alert-schema' // 企业微信 import { WeChatPusher } from '@shared/lib/wechat/wechat-pusher' // UI 组件 import { Button, Card, Badge, Table, Modal, Input, Select, Toast, Pagination } from '@shared/ui' import LoginPage from '@shared/ui/login-page' ``` --- ## 设计模式 | 模式 | 应用位置 | 说明 | |------|---------|------| | 依赖注入 | `CooldownProvider`, `UserSyncStore`, `AuditStore`, `Checker` 接口 | 通过接口注入外部状态,不耦合具体数据库驱动 | | 策略模式 | `HealthChecker.registerChecker()` | 可插拔检查器,新检查类型无需修改引擎 | | 工厂模式 | `createMiddleware(config)` | 返回配置化的 Next.js middleware | | 适配器模式 | `WeChatPusherCompat` | 包装 `WeChatPusher` 提供旧接口兼容 | | Barrel 导出 | `ui/index.tsx` | 统一导出入口,消费方一行导入 |