LobeChat + Logto + MinIO

源码深度解析 — 三位一体 AI 平台架构全景

v2.1.47 • Next.js 16 + Better Auth + RustFS • 源码级剖析
62
Monorepo 包
47
tRPC 路由
29
数据库 Schema
40+
LLM 供应商
11
SSO 提供方

1 系统总览 三位一体

💬
LobeChat
AI 对话平台主体
Next.js 16 + Vite SPA
端口 3210
🔐
Logto (SSO)
统一身份认证
OIDC 协议接入
Better Auth 集成
📦
RustFS / MinIO
S3 兼容对象存储
文件上传/预览/RAG
端口 9000/9001
LobeChat ← OAuth2/OIDC → Logto   |   LobeChat ← S3 API → RustFS/MinIO   |   LobeChat ← SQL → PostgreSQL   |   LobeChat ← Cache → Redis

LobeChat 是一个 全栈 AI 对话平台,采用 monorepo 架构(62 个包),支持 Web/桌面/移动端三端部署。认证通过 Better Auth 框架 + Logto 作为 OIDC SSO 提供方;文件存储使用 S3 兼容协议,生产环境用 RustFS(Rust 实现的 MinIO 替代品)。

LobeChat 技术栈
  • 前端: React 19 + Vite SPA + React Router v7
  • 后端: Next.js 16 App Router + tRPC v11.8
  • 认证: Better Auth v1.4.6 (会话/密码/2FA/Passkey/OAuth)
  • 状态: Zustand 5.0 + SWR + React Query
  • 样式: antd-style (CSS-in-JS) + Ant Design 6 + @lobehub/ui v5.5
  • 动画: Motion (Framer Motion) v12
  • 校验: Zod v3 (tRPC 输入校验) + superjson (序列化)
  • 数据库: Drizzle ORM v0.45 + PostgreSQL 17 (ParadeDB) + pgvector
  • 缓存: Redis 7 (ioredis) — 会话 + 文件预签名 URL
  • AI: 40+ LLM 供应商 + OpenAI SDK v4 + 多 Agent 编排
  • 国际化: react-i18next v16, 20+ 语言
  • 图片处理: Sharp v0.34 (服务端, 仅 AI 生图/视频用)
  • 桌面端: Electron (desktop-bridge, electron-client-ipc)
  • PWA: vite-plugin-pwa (离线缓存 + Service Worker)
  • 可观测性: OpenTelemetry (分布式链路追踪)
  • 安全: ssrf-safe-fetch (SSRF 防护) + RBAC 权限表 (已建未接入)
  • 搜索: SearXNG (自托管元搜索引擎)
  • Monorepo: pnpm workspace, 60 个包 (agent-runtime, model-runtime, 30+ builtin-tool, database, types, const...)
  • 测试: Vitest v3 + Playwright (E2E)
  • 构建: Vite v7 (SPA) + Next.js 16 (SSR) 双构建, bun 运行脚本
Logto 认证体系
  • 认证框架: Better Auth v1.4.6 (开源 Clerk 替代)
  • Logto 接入: Generic OIDC Provider (PKCE 增强)
  • SSO 11 种: Google, GitHub, Azure, Apple, Logto, Auth0, Keycloak, Okta, Casdoor, Authentik, Cloudflare ZT
  • 登录方式: 邮箱密码 (bcrypt) / Magic Link (15min) / OTP (5min) / Passkey (WebAuthn) / OAuth 社交登录
  • 会话管理: Cookie (30 天过期) + Redis 二级缓存 (10 分钟 TTL) + DB 兜底
  • Webhook: Logto User.Data.Updated (同步头像/姓名) + User.SuspensionStatus.Updated (自动登出)
  • 安全: CSRF 防护, Rate Limit (3 次/60 秒), JWKS (RS256), Webhook 签名验证 (HMAC-SHA256)
  • 密码策略: 8-64 字符, bcrypt 哈希, Clerk 迁移兼容
  • 双因素: TOTP + 备份码 (twoFactorEnabled)
  • 邮箱验证: 可选, 验证链接 1 小时过期
  • 白名单: AUTH_ALLOWED_EMAILS 限制注册域名/邮箱
  • OIDC 服务: 可选启用 JWKS_KEY, LobeChat 自身作为 OIDC Provider
  • DB 表: users, accounts, sessions, verifications, two_factor (Better Auth 管理)
  • tRPC 中间件链: OpenTelemetry → oidcAuth → userAuth → serverDatabase → 业务逻辑
RustFS/MinIO 存储系统
  • 协议: AWS S3 兼容 (@aws-sdk/client-s3 v3)
  • 生产后端: RustFS (Rust 重写 MinIO) / MinIO / AWS S3 / Cloudflare R2
  • 上传流程: 预签名 PUT URL (1 小时) → 浏览器直传 S3 (不经服务器) → DB 记录
  • 下载流程: /f/{fileId} 代理 → Redis 缓存预签名 GET URL (4 分钟 TTL, URL 5 分钟过期) → 302 重定向
  • 去重: SHA256 哈希, globalFiles 表 (多用户共享同一 S3 对象)
  • 两表设计: files (用户级, 按 userId 隔离) + global_files (全局去重, hash_id 主键)
  • 代理 URL: /f/{fileId} 统一入口, 对客户端隐藏 S3 细节
  • ACL 模式: S3_SET_ACL=1 公开读 + PUBLIC_DOMAIN 直链 / =0 全部预签名 URL
  • 路径样式: S3_ENABLE_PATH_STYLE=1 (MinIO/RustFS) / =0 (AWS 虚拟主机样式)
  • 媒体缓存: uploadMedia() 自动设 Cache-Control: public, max-age=31536000 (1 年)
  • 大小验证: 服务端通过 S3 HeadObject 验证实际文件大小 (防客户端伪造)
  • RAG 管道: 文件上传 → 异步分块 (chunks) → 批量向量嵌入 (batch=50, concurrency=10) → pgvector 语义搜索
  • Docker 初始化: rustfs-init 容器用 minio/mc 创建 bucket + 设置匿名读策略
  • 配额控制: checkFileStorageUsage 中间件, FileModel.countUsage() 统计用量

2 整体架构 Architecture

2.1 Monorepo 结构

~/Projects/business/20260330-LobeChat公司AI平台/
├── src/                    ← 主应用源码 (Next.js + Vite SPA)
│   ├── spa/                ← SPA 入口 (web/mobile/desktop)
│   ├── app/                ← Next.js App Router (SSR + API)
│   ├── store/              ← Zustand 状态管理 (29+ stores)
│   ├── server/             ← tRPC 路由 + 服务层 + 模块
│   ├── libs/               ← Better Auth / tRPC / i18n 配置
│   ├── features/           ← 按领域组织的 UI 组件
│   ├── routes/             ← React Router 页面
│   ├── services/           ← 客户端服务层
│   ├── envs/               ← 环境变量定义 (14 个文件)
│   └── config/             ← 应用配置
├── packages/               ← 62 个 Monorepo 包
│   ├── database/           ← Drizzle ORM schemas + models + migrations
│   ├── agent-runtime/      ← Agent 执行引擎
│   ├── model-runtime/      ← 40+ LLM 供应商适配器
│   ├── builtin-tool-*/     ← 30+ 内置工具
│   └── ...                 ← types, const, utils, config 等
├── apps/desktop/           ← Electron 桌面应用
├── docker-compose/         ← 部署编排
│   ├── deploy/             ← 生产部署 (LobeChat+PG+Redis+RustFS+SearXNG)
│   └── dev/                ← 开发环境 (只有基础设施)
├── Dockerfile             ← 多阶段构建 (343 行)
├── drizzle.config.ts      ← 数据库迁移配置
└── package.json           ← pnpm + bun, v2.1.47

2.2 请求生命周期

浏览器/客户端
    │
    ▼
React SPA (Vite 构建, React Router v7)
    │  ← Zustand Store 管理状态
    ▼
tRPC Client (自动生成 React hooks)
    │  ← superjson 序列化 (Date/Map/Set)
    ▼
Next.js API Routes (/trpc, /api/*)
    │
    ▼
tRPC Middleware 链
    │  1. OpenTelemetry (分布式追踪)
    │  2. OIDC Auth (JWT/JWKS 验证, 可选)
    │  3. Better Auth (Session Cookie 验证)
    │  4. Server Database (DB 连接注入)
    │  5. Key Vaults (密钥解密)
    ▼
tRPC Router Handler (47 个子路由)
    │  ← Zod 输入校验
    ▼
Service Layer (业务逻辑)
    │
    ├──→ Database Models (Drizzle ORM → PostgreSQL)
    ├──→ S3 Module (AWS SDK → RustFS/MinIO)
    ├──→ Redis (会话缓存, 文件 URL 缓存)
    └──→ LLM Runtime (40+ 供应商, 流式响应)

3 LobeChat 核心源码 LobeChat

3.1 入口文件

src/spa/entry.web.tsx 24 行

Web SPA 入口,导入 desktopRoutes 配置,支持开发代理模式 (/_dangerous_local_dev_proxy),BootErrorBoundary 错误边界包裹。

src/spa/entry.desktop.tsx 13 行

Electron 桌面入口,使用相同路由配置,无代理 basename。

src/spa/entry.mobile.tsx 13 行

移动端入口,切换到 mobileRoutes 配置。

3.2 路由系统

文件: src/spa/router/desktopRouter.config.tsx (579 行)

完整路由结构 (点击展开)
/ (主布局 - 持久侧边栏 + 主内容区)
├── /agent/:aid              → 对话页面
│   ├── /                    → 聊天消息
│   ├── /profile             → Agent 资料
│   ├── /cron/:cronId        → 定时任务
│   └── /channel             → 频道管理
├── /group/:gid              → 多 Agent 群聊
├── /community               → 发现市场
│   ├── /agent               → 浏览 Agent
│   ├── /model               → 浏览模型
│   ├── /skill               → 浏览技能
│   ├── /mcp                 → 浏览 MCP
│   └── (detail)/:slug       → 详情页
├── /resource                → 知识库
│   └── /library/:id/:slug   → KB 详情
├── /settings                → 用户设置
│   ├── /profile             → 个人资料
│   ├── /provider/:id        → LLM 供应商配置
│   └── /:tab                → 其他标签页
├── /memory                  → 用户记忆系统
│   ├── /identities          → 身份
│   ├── /contexts            → 上下文
│   ├── /preferences         → 偏好
│   ├── /experiences         → 经验
│   └── /activities          → 活动
├── /video                   → 视频生成
├── /image                   → 图片生成
├── /eval                    → 评估基准
│   └── /bench/:id/runs/:id  → 评测结果
├── /page                    → 笔记/页面
│   └── /:id                 → 编辑页面
├── /onboarding              → 新手引导 (独立布局)
└── /share/t/:id             → 话题分享 (独立布局)

3.3 LLM / Agent 集成

packages/model-runtime/ 40+ 供应商

每个 LLM 供应商一个目录 (openai/, anthropic/, azure/, ollama/, bedrock/ 等),实现统一的 ModelRuntime 接口。运行时通过 runtimeMap.ts 注册表动态路由。

packages/agent-runtime/ Agent 编排

核心组件:GeneralChatAgent (单 Agent), GroupOrchestrationRuntime (多 Agent 协调), GroupOrchestrationSupervisor (Agent 路由/委派), UsageCounter (Token 计费), InterventionChecker (人工审批)。

4 认证系统 & Logto 集成 Logto

4.1 认证架构概览

核心设计
LobeChat 使用 Better Auth v1.4.6 作为认证框架(开源 Clerk 替代品),Logto 作为 Generic OIDC Provider 接入。认证状态通过 Cookie + Redis 管理(10 分钟缓存),tRPC 中间件链确保每个请求都经过验证。

4.2 核心配置文件

src/libs/better-auth/define-config.ts (333 行) — 主认证配置

代理配置 (开发环境)

检测 NODE_ENV=development 时读取 HTTPS_PROXY/HTTP_PROXY,用 undici ProxyAgent 设置全局代理——因为 Node.js 原生 fetch 不走系统代理,开发时 OAuth Token Exchange 需要。

认证常量

常量说明
VERIFICATION_LINK_EXPIRES_IN3600s (1h)邮箱验证链接过期
MAGIC_LINK_EXPIRES_IN900s (15min)Magic Link 过期
OTP_EXPIRES_IN300s (5min)手机验证码过期

Passkey 配置

getPasskeyRpID()APP_URL 安全提取 hostname 作为 WebAuthn Relying Party ID。未设置 APP_URL 时返回 undefined(兼容 E2E 测试)。

密码策略

8-64 字符,bcrypt 哈希,支持 Clerk 迁移(clerkMigration 钩子自动转换旧密码哈希)。

会话管理

session: {
  cookieCache: { enabled: true, maxAge: 600 },  // 10 分钟 Cookie 缓存
  expiresIn: 2592000,                            // 30 天过期
  updateAge: 86400,                              // 24h 刷新
}
secondaryStorage: Redis                          // Redis 作为二级会话存储

钩子 (Hooks)

  • after createUser: 新用户注册后创建 UserModel 关联记录
  • after linkAccount: OAuth 关联后同步头像/姓名到 LobeChat 用户表
  • before deleteUser: 删除所有关联数据(消息/文件/设置)

速率限制

rateLimit: {
  storage: 'secondary-storage',  // Redis
  customRules: {
    '/sign-in/email':    { max: 3, window: 60 },
    '/sign-up/email':    { max: 3, window: 60 },
    '/forget-password':  { max: 3, window: 60 },
    '/magic-link/*':     { max: 3, window: 60 },
    '/email-otp/*':      { max: 3, window: 60 },
  }
}
src/libs/better-auth/sso/providers/logto.ts — Logto OIDC 接入

配置

{
  providerId: 'logto',
  type: 'oidc',                     // Generic OIDC
  discoveryUrl: AUTH_LOGTO_ISSUER + '/.well-known/openid-configuration',
  clientId: AUTH_LOGTO_ID,
  clientSecret: AUTH_LOGTO_SECRET,
  scopes: ['openid', 'profile', 'email', 'offline_access'],
  pkce: true,                       // PKCE 安全增强
}

Profile Mapping

mapProfileToUser(profile) {
  return {
    email:    profile.email,
    name:     profile.name || profile.username,
    image:    profile.picture,          // Logto 头像字段
    username: profile.username,
  }
}
关键点
Logto 以 offline_access scope 获取 Refresh Token,实现长期会话。PKCE 防止授权码拦截攻击。
src/app/(backend)/api/webhooks/logto/route.ts — Logto Webhook

路径: POST /api/webhooks/logto

事件处理

事件处理逻辑
User.Data.Updated同步用户资料:avatar / email / fullName → Better Auth users 表
User.SuspensionStatus.Updated用户被封禁时自动登出所有会话(revoke sessions)

签名验证

// 使用 LOGTO_WEBHOOK_SIGNING_KEY 验证请求签名
const signature = headers['logto-signature-sha-256'];
const expected = crypto.createHmac('sha256', signingKey)
                       .update(rawBody)
                       .digest('hex');
if (signature !== expected) return 401;

4.3 全部 11 个 SSO 提供方

#提供方类型环境变量
1GoogleOAuth 2.0AUTH_GOOGLE_ID / AUTH_GOOGLE_SECRET
2GitHubOAuth 2.0AUTH_GITHUB_ID / AUTH_GITHUB_SECRET
3MicrosoftOAuth 2.0AUTH_MICROSOFT_ENTRA_ID / SECRET / TENANT_ID
4AppleOAuth 2.0AUTH_APPLE_ID / SECRET / TEAM_ID / KEY_ID
5LogtoOIDC GenericAUTH_LOGTO_ID / ISSUER / SECRET
6Auth0OIDC GenericAUTH_AUTH0_ID / ISSUER / SECRET
7KeycloakOIDC GenericAUTH_KEYCLOAK_ID / ISSUER / SECRET
8OktaOIDC GenericAUTH_OKTA_ID / ISSUER / SECRET
9CasdoorOIDC GenericAUTH_CASDOOR_ID / ISSUER / SECRET
10AuthentikOIDC GenericAUTH_AUTHENTIK_ID / ISSUER / SECRET
11Cloudflare ZTOIDC GenericAUTH_CLOUDFLARE_ZERO_TRUST_*

4.4 认证中间件链

tRPC Request
    │
    ▼
createLambdaContext()
    │  认证优先级:
    │  1. X-API-Key header → 查 DB apiKeys 表
    │  2. Oidc-Auth header → JWKS 验证 (ENABLE_OIDC=true)
    │  3. Cookie → Better Auth session 验证
    ▼
oidcAuth middleware
    │  如果有 OIDC token → 验证 JWT 签名
    │  注入 ctx.oidcAuth = { sub, payload }
    ▼
userAuth middleware
    │  从 ctx 获取 userId
    │  注入 ctx.userId
    ▼
serverDatabase middleware
    │  创建 Drizzle DB 实例
    │  注入 ctx.serverDB
    ▼
Router Handler
    │  ctx.userId + ctx.serverDB 可用
    │  创建 Model 实例处理业务逻辑

4.5 数据库 Schema

文件: packages/database/src/schemas/betterAuth.ts

表名关键字段用途
usersid, email, fullName, avatar, phone, username, role, banned, twoFactorEnabled用户主表 (email 归一化)
accountsid, userId, providerId, accountId, accessToken, refreshTokenOAuth 关联账号
sessionsid, userId, token, expiresAt, ipAddress, userAgent会话记录
verificationsid, identifier, value, expiresAt验证码/Magic Link
two_factorid, userId, secret, backupCodes双因素认证

5 文件存储 & MinIO/RustFS MinIO

5.1 存储架构

┌─────────────────────────────────────────────┐
│  CLIENT (浏览器)                            │
│  UploadService → XMLHttpRequest PUT         │
└──────────────┬──────────────────────────────┘
               │ 1. 获取预签名 URL
               ▼
┌──────────────────────────────────────────────┐
│  tRPC API 层                                │
│  upload.createS3PreSignedUrl({pathname})     │
│  file.createFile({hash, type, name, size})   │
│  /(backend)/f/[id] (文件代理路由)             │
└──────────────┬──────────────────────────────┘
               │ 2. S3 命令
               ▼
┌──────────────────────────────────────────────┐
│  Service 层                                 │
│  FileService (门面) → S3StaticFileImpl       │
└──────────────┬──────────────────────────────┘
               │ 3. AWS SDK v3
               ▼
┌──────────────────────────────────────────────┐
│  S3 兼容存储                                │
│  RustFS / MinIO / AWS S3 / Cloudflare R2    │
└──────────────────────────────────────────────┘

5.2 核心源码文件

src/server/modules/S3/index.ts (215 行) — S3 客户端模块

S3 基类方法

方法功能细节
constructor()初始化 S3Clientcredentials + endpoint + forcePathStyle + region
createPreSignedUrl(key)生成上传 URLPutObjectCommand, 1 小时有效
createPreSignedUrlForPreview(key, expiresIn?)生成预览 URLGetObjectCommand, 默认 2 小时
uploadBuffer(path, buffer, contentType)服务端上传PutObjectCommand + Cache-Control
uploadMedia(key, buffer)上传媒体文件Cache-Control: public, max-age=31536000 (1 年缓存)
getFileContent(key)获取文本内容UTF-8 字符串
getFileByteArray(key)获取二进制Uint8Array
getFileMetadata(key)获取元数据HeadObjectCommand → contentLength + contentType
deleteFile(key)删除文件DeleteObjectCommand
deleteFiles(keys)批量删除DeleteObjectsCommand (批量)

FileS3 包装类

继承 S3,从 fileEnv 自动读取配置初始化,用于依赖注入。

src/server/services/file/index.ts (366 行) — FileService 门面

关键方法

方法功能核心逻辑
createFileRecord()创建文件记录SHA256 去重 via globalFiles → 返回代理 URL /f/{fileId}
getFullFileUrl()智能 URL 解析ACL 公开 + 有 PUBLIC_DOMAIN → 直链; 否则 → 预签名 URL
getKeyFromFullUrl()URL → S3 Key/f/{fileId} → DB 查询; legacy S3 URL → pathname 解析
uploadBase64()Base64 上传base64 → Buffer → uploadMedia → createFileRecord
uploadFromUrl()外部 URL 上传fetch → Buffer → uploadMedia → createFileRecord
downloadFileToLocal()下载到临时目录返回 cleanup() 清理函数
去重机制
createFileRecord() 先查 globalFiles 表(SHA256 哈希),如果已存在则复用同一 S3 对象,只创建新的 files 记录(不同 fileId)。删除时只有没有任何 files 引用该 hash 时才删 S3 对象。
src/app/(backend)/f/[id]/route.ts (91 行) — 文件代理路由

流程

GET /f/{fileId}
    │
    ├─ 1. Redis 查缓存: key = 'file-proxy:{fileId}'
    │     命中? → 302 重定向到缓存的预签名 URL
    │
    ├─ 2. 未命中 → DB 查询: FileModel.getFileById(fileId)
    │     ⚠️ 无 userId 过滤 (公开可访问)
    │
    ├─ 3. 生成预签名 GET URL (5 分钟有效)
    │
    ├─ 4. 写入 Redis: TTL = 240s (4 分钟)
    │     (比 URL 有效期短 1 分钟,安全余量)
    │
    └─ 5. 返回 302 Location → 预签名 URL

5.3 上传完整流程

步骤 1: 生成路径
uploadService.generateFilePathMetadata()
→ filename = '{uuid}.{ext}'
→ dirname  = 'files/{timestamp_hour}'
→ pathname = 'files/2026-04-07/{uuid}.png'

步骤 2: 获取预签名 URL
tRPC: upload.createS3PreSignedUrl({pathname})
→ server: FileS3.createPreSignedUrl(pathname)
→ AWS SDK: getSignedUrl(PutObjectCommand, {expiresIn: 3600})
← 返回: https://rustfs:9000/lobe/files/2026-04-07/{uuid}.png?X-Amz-Signature=...

步骤 3: 直传 S3
XMLHttpRequest PUT → 预签名 URL
→ 浏览器直接上传到 RustFS/MinIO (不经过 LobeChat 服务器)
→ 支持进度回调 (xhr.upload.onprogress)
← 200 OK

步骤 4: 创建数据库记录
tRPC: file.createFile({hash, fileType, name, size, url})
→ server: FileModel.checkHash(sha256) → 检查去重
→ server: FileService.getFileMetadata(url) → 验证实际文件大小 (防伪造)
→ server: FileModel.create() → INSERT files + globalFiles
← 返回: {fileId: 'xxx', url: '/f/xxx'}

步骤 5: 使用
客户端使用 /f/{fileId} 作为统一 URL
→ 图片: <img src="/f/{fileId}" />
→ 下载: window.open('/f/{fileId}')

5.4 数据库 Schema (文件相关)

关键字段用途
global_fileshash_id (PK, SHA256), file_type, size, url, metadata, creator去重层 — 相同内容只存一份 S3 对象
filesid, user_id, file_hash (FK→global_files), name, size, url, source, parent_id, chunk_task_id, embedding_task_id用户文件记录 — 每个用户独立引用
documentsid, title, content, file_type, pages, source_type, file_id, knowledge_base_id, parent_id文档/文件夹 — RAG 知识库组织
两表设计的精妙之处
files 表是用户级别的(每个用户看到自己的文件列表),global_files 是全局去重的(SHA256 唯一)。User A 和 User B 上传同一文件,S3 只存一份,但各自有独立的 fileId 和代理 URL。删除 User A 的文件不影响 User B。只有当所有 files 记录都删除后,才清理 S3 对象。

6 数据库层 Drizzle ORM

6.1 技术选型

6.2 全部 29 个 Schema 文件

点击展开完整 Schema 列表
#文件说明
1agent.tsagents, agent configsAI Agent 定义
2agentBotProvider.tsagent_bot_providersAgent 供应商配置
3agentCronJob.tsagent_cron_jobs定时 Agent 任务
4agentDocuments.tsagent_documentsAgent 知识文档
5agentEvals.tsagent_evals, benchmarksAgent 评估
6agentSkill.tsagent_skillsAgent 技能
7aiInfra.tsai_infra configsAI 基础设施配置
8apiKey.tsapi_keys用户 API Key (带过期)
9asyncTask.tsasync_tasks后台任务队列
10betterAuth.tsusers, accounts, sessions, verifications, two_factorBetter Auth 认证表
11chatGroup.tschat_groups多方对话群组
12file.tsfiles, global_files, documents文件 + 去重 + 文档
13generation.tsgenerations图片/视频生成结果
14message.tsmessages, message_groups, thread_mappings聊天消息 + 嵌套树
15nextauth.ts(legacy NextAuth)旧版认证 (保留兼容)
16notification.tsnotifications用户通知
17oidc.tsoidc configsOIDC 服务配置
18rag.tschunks, embeddings, knowledge_bases, file_chunksRAG 系统
19ragEvals.tsrag evaluation datasetsRAG 评估
20rbac.tsroles, permissionsRBAC 权限控制
21relations.ts(Drizzle relations 定义)表关系映射
22session.tssessions (chat)聊天会话
23task.tstasks任务管理
24topic.tstopics, topic_threads对话主题
25user.tsuser_settings, user_installed_plugins用户设置 + 插件
26-29userMemories/*.tspersona, contexts, preferences, experiences, activities用户记忆系统

6.3 消息表设计 (核心)

messages 表 — LobeChat 最复杂的 schema
messages {
  id           TEXT PK
  role         TEXT           -- 'user' | 'assistant' | 'system' | 'tool'
  content      TEXT           -- 消息内容 (Markdown)
  editorData   JSONB          -- 富文本编辑器数据
  summary      TEXT           -- AI 摘要
  reasoning    TEXT           -- 思维链 (Chain of Thought)
  search       JSONB          -- 搜索结果
  metadata     JSONB          -- 自定义元数据
  model        TEXT           -- 使用的模型 (gpt-4o, claude-3.5-sonnet...)
  provider     TEXT           -- 供应商 (openai, anthropic...)
  tools        JSONB          -- 工具调用记录
  error        JSONB          -- 错误信息
  traceId      TEXT           -- OpenTelemetry 追踪 ID
  favorite     BOOLEAN

  -- 外键关系
  userId       FK → users
  sessionId    FK → sessions
  topicId      FK → topics
  threadId     FK → threads
  parentId     FK → messages (自引用, 树结构)
  quotaId      FK → messages (引用消息)
  agentId      FK → agents
  groupId      FK → chat_groups
  messageGroupId FK → message_groups (并行多模型)
}

message_groups {                -- 并行多模型对话
  id, topicId, userId
  parentGroupId  FK (自引用)
  parentMessageId FK
  type           'parallel' | 'compression'
  content, editorData, metadata
}

7 状态管理 Zustand

7.1 Store 组合模式

文件: src/store/chat/store.ts (82 行)

// Zustand Store 用 Slice 模式组合,每个领域一个 slice
ChatStore = ChatStoreState & ChatStoreAction

ChatStoreAction 包含:
├── ChatMessageAction      -- 消息 CRUD, 线程
├── ChatThreadAction       -- 线程操作
├── ChatAIChatAction       -- LLM 流式对话控制
├── ChatTopicAction        -- 主题管理
├── ChatTranslateAction    -- 消息翻译
├── ChatTTSAction          -- 文本转语音
├── ChatPluginAction       -- 插件执行
├── ChatBuiltinToolAction  -- 搜索/代码解释器
├── ChatPortalAction       -- 弹窗/侧边栏
├── OperationActions       -- 撤销/重做
└── ChatAIAgentAction      -- Agent 编排

// 创建方式
createWithEqualityFn()(
  subscribeWithSelector(   // 细粒度订阅
    devtools(              // Redux DevTools
      createStore          // 组合所有 slices
    )
  ),
  shallow                  // 浅比较
)

7.2 全部 Store 列表

chat/ 核心

消息、线程、主题、AI 对话、插件、TTS、翻译、Agent 编排。最复杂的 store,12 个 slices。

agent/

Agent CRUD、群组管理、收藏。

user/

用户设置、偏好、主题切换。

file/

上传进度、文件列表管理。

global/

全局设置、主题、语言、侧边栏状态。

session/

聊天会话生命周期。

7.3 Slice 内部结构

src/store/chat/slices/message/
├── actions/
│   ├── index.ts           -- 导出所有 action
│   ├── publicApi.ts       -- 用户触发: addMessage, updateMessage
│   ├── internals.ts       -- 内部操作
│   ├── optimisticUpdate.ts -- 乐观更新 (先改 UI, 再同步 DB)
│   ├── query.ts           -- 消息查询
│   └── runtimeState.ts    -- loading/error 状态
├── selectors/
│   ├── displayMessage.ts  -- UI 格式化
│   ├── dbMessage.ts       -- 原始 DB 数据
│   ├── messageState.ts    -- 元数据 (加载中, 错误)
│   └── chat.ts            -- 对话专用查询
├── reducer.ts             -- 不可变更新
└── supervisor.ts          -- 批量操作协调

8 tRPC API 层 47 路由

8.1 路由架构

文件: src/server/routers/lambda/index.ts (122 行)

全部 47+ 个 tRPC 子路由
分类路由说明
AgentagentAgent CRUD
agentBotProviderBot 供应商配置
agentCronJob定时任务
agentDocumentAgent 文档
agentEvalAgent 评估
agentSkillsAgent 技能
agentGroupAgent 群组
AIaiChatAI 对话 (流式)
aiModel模型配置
aiProviderAI 供应商管理
对话message消息 CRUD
topic主题管理
thread线程操作
session / sessionGroup会话管理
文件file文件 CRUD + 去重
upload预签名 URL 生成
document文档管理
知识库knowledge / knowledgeBase知识库管理
chunk文档分块 + 嵌入
ragEvalRAG 评估
生成image图片生成
video视频生成
generation / generationBatch / generationTopic生成记录
用户user用户配置
userMemory / userMemories用户记忆
其他plugin, search, market, config, exporter, importer, share, notification, notebook, apiKey, usage, task, brief, home, device, comfyui, klavis, oauthDeviceFlow各功能模块
商业accountDeletion, referral, spend, subscription, topUp付费/订阅功能

8.2 tRPC 中间件栈

// 公开路由
publicProcedure = t.procedure.use(openTelemetry)

// 认证路由
authedProcedure = t.procedure
  .use(openTelemetry)      // 分布式追踪
  .use(oidcAuth)           // OIDC JWT 验证 (可选)
  .use(userAuth)           // Better Auth Session 验证

// 文件路由 (额外检查)
fileProcedure = authedProcedure
  .use(checkFileStorageUsage)  // 存储配额检查
  .use(serverDatabase)         // DB 连接注入

9 Docker 部署 编排

9.1 生产环境架构

文件: docker-compose/deploy/docker-compose.yml

┌─────────────────────────────────────────────────────────────┐
│                    docker-compose (lobe-network)            │
│                                                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────────┐  │
│  │ lobehub  │  │ postgres │  │  redis   │  │  rustfs    │  │
│  │  :3210   │  │  :5432   │  │  :6379   │  │ :9000/:9001│  │
│  │          │  │          │  │          │  │            │  │
│  │ Next.js  │  │ ParadeDB │  │ Redis 7  │  │ S3 兼容    │  │
│  │ + tRPC   │  │ PG 17    │  │ Alpine   │  │ 对象存储   │  │
│  └─────┬────┘  └─────┬────┘  └─────┬────┘  └─────┬──────┘  │
│        │             │             │              │          │
│        ├── SQL ──────┘             │              │          │
│        ├── Redis ──────────────────┘              │          │
│        └── S3 API ────────────────────────────────┘          │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐                         │
│  │ rustfs-init  │  │   searxng   │                         │
│  │ (one-shot)   │  │   :8080      │                         │
│  │ 创建 bucket  │  │ 搜索引擎    │                         │
│  └──────────────┘  └──────────────┘                         │
└─────────────────────────────────────────────────────────────┘

9.2 Dockerfile 多阶段构建

阶段镜像职责产出
basenode:24-slim依赖安装 + 证书node_modules
builderbaseNext.js 构建 + Vite SPA + Drizzle.next/standalone + .next/static + spa/
appbusybox资源收集精简的应用文件
scratchnode:24-slim最终运行镜像~200MB, 非 root (nodejs:1001)

9.3 RustFS 初始化

# rustfs-init 服务 (一次性容器)
mc alias set rustfs "http://rustfs:9000" admin ${SECRET}
mc mb "rustfs/lobe" --ignore-existing            # 创建 bucket
mc anonymous set-json "/bucket.config.json" "rustfs/lobe"  # 设置匿名读权限

bucket.config.json 设置 lobe bucket 的匿名只读策略,允许公开访问已上传文件。

10 环境变量全表 配置中心

变量说明默认值
APP_URL公开访问 URL (https://chat.example.com)必填
INTERNAL_APP_URL内部服务间 URL (Docker 内部通信)必填
PORT服务端口3210
NODE_ENV运行环境production
ENABLE_BUSINESS_FEATURES启用商业功能false
变量说明默认值
AUTH_SECRET32 字节 base64 随机字符串必填
AUTH_SSO_PROVIDERS逗号分隔的 SSO 列表 (google,logto...)
AUTH_ALLOWED_EMAILS注册白名单 (域名或完整邮箱)空 (允许所有)
AUTH_DISABLE_EMAIL_PASSWORD禁用邮箱密码, 强制 SSOfalse
AUTH_EMAIL_VERIFICATION要求邮箱验证false
AUTH_LOGTO_IDLogto 应用 ID-
AUTH_LOGTO_ISSUERLogto Endpoint URL-
AUTH_LOGTO_SECRETLogto 应用密钥-
LOGTO_WEBHOOK_SIGNING_KEYLogto Webhook 签名密钥-
JWKS_KEYOIDC 服务模式 JWKS (RS256 RSA)-
变量说明默认值
S3_ENDPOINTS3 端点 URL必填
S3_ACCESS_KEY_ID访问密钥必填
S3_SECRET_ACCESS_KEY秘密密钥必填
S3_BUCKET存储桶名必填
S3_REGION区域us-east-1
S3_ENABLE_PATH_STYLE路径样式 (MinIO/RustFS 必须为 1)false
S3_PUBLIC_DOMAIN公开域名 (CDN)
S3_SET_ACL设置 public-read ACL0
S3_PREVIEW_URL_EXPIRE_IN预签名 URL 过期秒数7200
变量说明默认值
DATABASE_URLPostgreSQL 连接字符串必填
DATABASE_DRIVER驱动 (node / neon)node
KEY_VAULTS_SECRET32 字节 base64, 加密敏感字段必填
MIGRATION_DB设为 "1" 启动时跑迁移
变量说明默认值
REDIS_URLRedis 连接字符串必填
REDIS_DATABASE数据库索引0
REDIS_PREFIXKey 前缀lobechat
REDIS_TLS启用 TLSfalse

11 关键数据流 端到端

11.1 用户登录流程 (Logto SSO)

1. 用户点击 "使用 Logto 登录"
   → Better Auth Client: signIn.social({ provider: 'logto' })
   → 302 重定向 → Logto 授权页

2. Logto 授权
   → 用户输入凭据 / 选择社交登录
   → Logto 验证 → 生成 authorization_code
   → 302 回调 → LobeChat /api/auth/callback/logto

3. Token Exchange
   → Better Auth Server: 用 code 换 access_token + id_token
   → 解析 id_token → 获取 email, name, picture
   → mapProfileToUser() → 映射到 LobeChat 用户结构

4. 用户创建/关联
   → 查找已有用户 (by email)
   → 已有: linkAccount (关联 Logto 账号)
   → 新用户: createUser → Hook: UserModel.create()
   → 同步头像/姓名到 LobeChat users 表

5. 会话创建
   → 创建 Better Auth Session (30 天)
   → 写入 Redis (10 分钟缓存)
   → Set-Cookie: better-auth.session_token=...
   → 302 重定向 → /

6. 后续请求
   → Cookie → tRPC middleware → userAuth
   → Redis 缓存命中 → 直接获取 userId (无 DB 查询)
   → Redis 未命中 → 查 sessions 表 → 刷新缓存

11.2 发送消息 → AI 回复

1. 用户输入消息
   → ChatStore.sendMessage(content)
   → 乐观更新: 立即显示用户消息

2. tRPC 调用
   → aiChat.sendMessageInServer({
       messages: [...history, newMessage],
       model: 'gpt-4o',
       provider: 'openai'
     })

3. 服务端处理
   → 认证中间件链 (OIDC → Session → DB)
   → initModelRuntimeFromDB(db, userId, 'openai')
     → 从 DB 加载用户配置的 API Key (解密)
     → 实例化 OpenAI Provider

4. LLM 调用
   → modelRuntime.generateStream({
       messages, tools, temperature, ...
     })
   → OpenAI API: POST /v1/chat/completions (stream: true)

5. 流式响应
   → SSE: data: {delta: "你", ...}
   → StreamingHandler 实时更新 Store
   → UI 实时渲染 (打字机效果)

6. 完成
   → 保存 assistant 消息到 DB
   → 更新 Token 计数 (UsageCounter)
   → OpenTelemetry: 记录 traceId

11.3 文件上传 → RAG 嵌入

1. 用户拖入 PDF
   → UploadService.uploadFileToS3(file)

2. 预签名 + 直传
   → tRPC: upload.createS3PreSignedUrl → 获取 PUT URL
   → XHR PUT → RustFS/MinIO (浏览器直传)
   → tRPC: file.createFile → DB 记录 (files + globalFiles)

3. 文档解析 (异步)
   → chunk.createParseFileTask({fileId})
   → AsyncTask: 解析 PDF → 提取文本 → 分块 (chunks)
   → 写入 chunks 表 + file_chunks 关联

4. 向量嵌入 (异步)
   → chunk.createEmbeddingChunksTask({fileId})
   → 批量调用 Embedding API (batch=50, concurrency=10)
   → pgvector: INSERT embeddings (1536 维向量)

5. 语义搜索 (使用时)
   → chunk.semanticSearchChunks({query, topK: 10})
   → pgvector: cosine_distance 搜索
   → 返回最相关的文档块 + 相似度分数

12 Q&A 问答区 持续更新

问答分布在对应位置
详细的源码级问答已内嵌到相关章节中(点击蓝色按钮展开)。以下是通用问题。
Logto 和 Better Auth 是什么关系?为什么不直接用 Logto?

Better Auth 是 LobeChat 的认证框架(类似 Clerk/NextAuth 的开源替代),负责管理会话、密码哈希、Cookie、2FA、Passkey 等所有认证基础设施。Logto 只是作为一个 SSO Provider(OIDC 协议)接入 Better Auth。

简单说:Better Auth 是"认证引擎",Logto 是"其中一个登录入口"。这种设计的好处是 LobeChat 可以同时支持 11 种 SSO + 邮箱密码 + Magic Link + Passkey,而不被任何一个 IdP 绑死。

为什么用 RustFS 而不是直接用 MinIO?

RustFS 是 MinIO 的 Rust 重写版,API 完全兼容。LobeChat 的 docker-compose 默认用 RustFS 但代码里用的是标准 AWS SDK v3——所以可以无缝切换到 MinIO、AWS S3、Cloudflare R2 等任何 S3 兼容存储。配置只需改环境变量,代码零改动。

文件上传为什么不经过 LobeChat 服务器?

采用预签名 URL 直传模式:客户端先从服务端获取一个 1 小时有效的 PUT 预签名 URL,然后浏览器直接上传到 S3。这样大文件不会占用 LobeChat 服务器的带宽和内存,服务器只处理轻量的元数据。这是 S3 最佳实践。

内嵌问答索引
详细解释 可拖动