7.0 KiB
7.0 KiB
CLAUDE.md — oa.tlyq.ai 统一门户
项目概述
oa-ai 是基于 Next.js 的 OA 统一门户,域名 oa.tlyq.ai。提供站点聚合导航、LLDAP 统一认证入口、用户管理(创建/删除/角色分配)、密码修改功能。各子站点(assets/issue)各自直连 LLDAP 认证,通过共享 JWT(tlyq_session cookie, domain=.tlyq.ai)实现跨站点免登录。
快速参考
| 属性 | 值 |
|---|---|
| 站点域名 | oa.tlyq.ai |
| 服务器 | txjp(IP: 43.133.38.210) |
| 代码路径 | /root/docker/oa-ai/ |
| 本地端口 | 6179 |
| 容器名 | oa-ai |
| 默认账号 | admin / admin123(LLDAP 统一管理) |
常用命令
npm run dev # 本地开发(端口 6179)
npm run build # 生产构建
关键文件
| 文件 | 职责 |
|---|---|
src/middleware.ts |
全局路由守卫:检查 tlyq_session cookie,未认证跳转 /login |
src/lib/jwt.ts |
共享 JWT 签发/验证(HS256,与 assets/issue 共用密钥和格式) |
src/lib/ldap.ts |
LLDAP 认证(ldapAuth)+ 密码修改(ldapChangePassword)+ 用户存在性检查 |
src/lib/email.ts |
邮件发送(Resend API,创建用户时发送密码设置链接,不含明文密码) |
src/lib/setup-token.ts |
一次性密码设置 token 签发/验证(JWT,24 小时有效) |
src/app/setup-password/page.tsx |
密码设置页(公开,通过邮件链接 token 访问) |
src/app/api/auth/setup-password/route.ts |
密码设置 API(验证 token + 调用 lldap_set_password) |
src/app/page.tsx |
门户首页:站点卡片导航(核心系统 + 其他站点) |
src/app/login/page.tsx |
登录页(LLDAP 认证) |
src/app/profile/page.tsx |
个人信息页(账户信息 + 修改密码) |
src/app/admin/create-user/page.tsx |
用户管理页(创建/删除/角色管理,仅 admin 可见) |
src/app/api/auth/login/route.ts |
登录 API(OA 仅 LLDAP 认证,无本地 DB) |
src/app/api/auth/logout/route.ts |
退出 API(清除 tlyq_session) |
src/app/api/auth/change-password/route.ts |
修改密码(docker exec 调 lldap_set_password) |
src/app/api/admin/create-user/route.ts |
创建用户(SQLite 写 LLDAP + 自动同步站点 + 角色设置) |
src/app/api/admin/users/route.ts |
用户列表/删除(GET/DELETE) |
src/app/api/admin/user-roles/route.ts |
角色查询/修改(GET/PUT) |
src/components/ThemeToggle.tsx |
深色/浅色主题切换按钮 |
src/app/profile/change-password-form.tsx |
修改密码客户端组件 |
src/app/admin/create-user/role-manager.tsx |
角色管理组件(待保存机制 + 批量提交) |
认证机制
OA 本身不存储用户数据(无本地 users 表),纯 LLDAP 认证:
- 用户登录 → LDAP bind 验证 → 签发
tlyq_sessionJWT(7 天) - 中间件检查
tlyq_session→ 有效则设置sessioncookie → 放行 - 退出清除
tlyq_session
跨站点共享:assets/issue 各自中间件优先检查 tlyq_session,有效则免登录。
环境配置
本地与云端差异
| 环境变量 | 本地开发 | 云服务器(txjp) |
|---|---|---|
LDAP_URL |
ldap://localhost:3890 |
ldap://lldap:3890 |
LDAP_BASE_DN |
dc=tlyq,dc=ai |
同 |
LDAP_ADMIN_DN |
uid=admin,ou=people,dc=tlyq,dc=ai |
同 |
| — | 运行时动态读取 | LLDAP admin 密码通过 docker exec lldap printenv 获取,不存本地 |
JWT_SECRET |
dev-secret-key-local |
强随机值(与 assets/issue 相同) |
COOKIE_DOMAIN |
""(空) |
.tlyq.ai |
RESEND_API_KEY |
re_xxxxxxxxxxxx |
Resend API Key(Sending Access 权限) |
.env 示例
LDAP_URL=ldap://localhost:3890
LDAP_BASE_DN=dc=tlyq,dc=ai
LDAP_ADMIN_DN=uid=admin,ou=people,dc=tlyq,dc=ai
JWT_SECRET=dev-secret-key-local
COOKIE_DOMAIN=
NODE_ENV=development
RESEND_API_KEY=re_xxxxxxxxxxxx
Docker 部署
txjp 服务器
├── oa-ai(容器) ← Next.js standalone,监听 3000
├── nginx-ai ← 反向代理 oa.tlyq.ai → oa-ai:3000
└── webnet(external) ← 共享网络
生产环境需要 OA 容器能访问 Docker socket(/var/run/docker.sock)以执行 docker exec lldap 修改密码和创建用户。
主题切换
支持深色/浅色双主题:
layout.tsx注入<script>读取 localStorage/系统偏好设置darkclass- CSS 变量
var(--bg)、var(--text)等响应主题变化 ThemeToggle组件:document.documentElement.classList.toggle('dark')+ localStorage 持久化- 配色对齐 assets/issue(slate/blue)
站点 URL 规则
源码中站点跳转 URL 均使用 localhost,部署时 deploy-ai.sh 通过 sed 自动替换为生产域名。
开发规范
- 新增页面:在
src/app/下创建,中间件自动检查认证 - API 路由:
/api/auth/公开,/api/admin/需 admin 权限 - admin 校验:
verifySharedJwt(token).username === 'admin'
Git Tag 规范
使用日期版本号 vYYYY.MM.DD(如 v2026.05.18)。提交后打 tag 再推送:
git tag v$(date +%Y.%m.%d) && git push origin main && git push origin v$(date +%Y.%m.%d)
同一天多次提交只打一个 tag。详见根目录 CLAUDE.md。
故障排查
OA 502 Bad Gateway
常见原因:nginx DNS 缓存旧 IP。OA 容器重启后 IP 变化,nginx 仍向旧 IP 发送请求。
解决:
docker restart nginx-ai
用户管理/角色管理一直加载
原因:OA 容器未挂载 assets/issue 的 SQLite 数据库文件,或 LLDAP 容器缺少 sqlite3。
检查:
docker exec oa-ai ls /data/other-sites/assets/ # 应有 assets.db, assets.db-wal, assets.db-shm
docker exec oa-ai ls /data/other-sites/issue/ # 应有 issue.db, issue.db-wal, issue.db-shm
docker exec lldap which sqlite3 # 应有 /usr/bin/sqlite3
LLDAP 容器 sqlite3 安装(每次容器重建后需重新安装):
docker exec lldap apk add --no-cache sqlite
生产部署关键点
- Docker socket:OA 需
/var/run/docker.sock挂载(用于docker exec lldap创建用户和改密) - 数据库卷:OA 需挂载 assets/issue 的 Docker volume 整个数据目录(非单个文件),确保 SQLite WAL 文件(
.db-wal/.db-shm)共享,否则权限管理页面无法实时反映子站点用户变更:/var/lib/docker/volumes/assets-ai_assets-data/_data→/data/other-sites/assets/var/lib/docker/volumes/issue-ai_issue-data/_data→/data/other-sites/issue
- WAL 共享:OA 容器必须挂载整个目录而非单个
.db文件。挂载单个文件会导致 WAL 文件独立,sqlite3 CLI 读取过期数据。参见docker-compose.yml中的 volumes 配置 - JWT 密钥:三站点必须一致(
JWT_SECRET=oa-shared-jwt-secret-tlyq-2026) - COOKIE_DOMAIN:生产必须设为
.tlyq.ai