14 KiB
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 # 分页(中文标签:"上一页"/"下一页")
├── 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
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
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
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
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
createMiddleware(config: AuthConfig): NextMiddleware
Next.js 路由守卫工厂。验证 tlyq_session cookie(JWT payload 解码,检查过期时间)。公开路径放行,管理路径检查 admin 角色,未认证请求重定向到 /login。
user-sync.ts
syncOidcUser(store: UserSyncStore, userinfo: OidcUserinfo): Promise<{ id: number, role: string, isNew: boolean }>
将 OIDC userinfo 同步到本地 SQLite users 表。UserSyncStore 是依赖注入接口,不耦合具体数据库驱动。
lib/alert/ — 告警引擎
告警决策和健康检查的核心引擎。
types.ts
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
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 是依赖注入接口:
interface CooldownProvider {
getCooldown(key: string): number | null // 返回 Unix 时间戳
setCooldown(key: string, timestamp: number): void
}
health-checker.ts
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
const AUDIT_LOGS_TABLE_SQL: string // audit_logs 表 DDL
write-audit-log.ts
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
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
formatAvailabilityMessage(params: { serviceName: string, status: string, ... }): string
formatAlertLevel(level: AlertLevel): string // 返回带 emoji 的级别标签
wechat-pusher.ts
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: 暗色模式。
导出方式:
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' |
| Badge | `variant: 'default' | 'success' |
| 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' |
| 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 中添加路径映射:
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"],
"@shared/*": ["./shared/*"]
}
}
}
符号链接
在消费方项目根目录创建符号链接:
cd /Users/niuniu/programs/docker/monitor-ai
ln -s ../shared shared
导入示例
// 后端认证
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 |
统一导出入口,消费方一行导入 |