shared/CLAUDE.md

424 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 张表 DDLservices, 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 种 variant3 种 sizeloading 状态)
├── Badge.tsx # 标签5 种 variant
├── Card.tsx # 卡片容器
├── Input.tsx # 输入框(含 label 和 error
├── Select.tsx # 下拉选择
├── Modal.tsx # 模态框backdrop 点击关闭body scroll 锁定)
├── Table.tsx # 表格styled theadtbody 由调用方提供)
├── Toast.tsx # 通知条(右下角固定定位)
├── Pagination.tsx # 分页(中文标签:"上一页"/"下一页"
├── hooks/ # React Hooks
│ ├── useDirtyTracker.ts # 脏状态追踪("修改后才显示保存按钮"模式)
│ └── index.ts # Barrel 导出
└── 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<LdapAuthResult>
checkAdminGroup(config: { url: string, baseDn: string }, username: string): Promise<boolean>
```
使用 `ldapts` 客户端进行 LDAP bind 认证和管理员组成员检查。
#### oidc.ts
```typescript
discoverOidcConfig(url: string): Promise<OidcConfig>
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<TokenSet>
getUserinfo(url: string, accessToken: string): Promise<OidcUserinfo>
```
完整的 OIDC 授权码 + PKCE 流程,基于 Authelia 实现。
#### middleware.ts
```typescript
createMiddleware(config: AuthConfig): NextMiddleware
```
Next.js 路由守卫工厂。验证 `tlyq_session` cookieJWT 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<CheckResult[]>
}
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<string, unknown>
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<boolean>
pushText(content: string): Promise<boolean>
}
class WeChatPusherCompat {
// 兼容 issue-ai 的旧接口(返回 boolean
pushMarkdown(title: string, content: string): Promise<boolean>
}
```
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` | forwardRefloading 时显示 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` | 样式化 theadtbody 由调用方提供 |
| Toast | `message`, `type: 'success'|'error'|'info'`, `onClose` | 右下角固定通知条 |
| Pagination | `page`, `totalPages`, `onPageChange` | 中文标签totalPages <= 1 时隐藏 |
| useDirtyTracker | `isDirty(key, item): boolean`, `markClean(key, item): void`, `removeItem(key): void` | 脏状态追踪 Hook"修改后才显示保存按钮"模式 |
| 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` | 统一导出入口,消费方一行导入 |