oa-ai/CLAUDE.md

175 lines
6.6 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 — 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` |
| 服务器 | txjpIP: 43.133.38.210 |
| 代码路径 | `/root/docker/oa-ai/` |
| 本地端口 | 6179 |
| 容器名 | `oa-ai` |
| 默认账号 | `admin` / `admin123`LLDAP 统一管理) |
### 常用命令
```bash
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` | 邮件发送nodemailer163 企业邮箱,创建用户时发送凭证) |
| `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` | 登录 APIOA 仅 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 认证:
1. 用户登录 → LDAP bind 验证 → 签发 `tlyq_session` JWT7 天)
2. 中间件检查 `tlyq_session` → 有效则设置 `session` cookie → 放行
3. 退出清除 `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` | 同 |
| `LDAP_ADMIN_PASS` | `admin123` | LLDAP admin 密码 |
| `JWT_SECRET` | `dev-secret-key-local` | 强随机值(与 assets/issue 相同) |
| `COOKIE_DOMAIN` | `""`(空) | `.tlyq.ai` |
| `SMTP_HOST` | `smtphz.qiye.163.com` | 163 企业邮箱 |
| `SMTP_PORT` | `465` | SSL 端口 |
| `SMTP_USER` | `gxp@qx002575.com` | 发件邮箱 |
| `SMTP_PASS` | 见 .env | 邮箱密码 |
| `SMTP_FROM` | `gxp@qx002575.com` | 发件人地址 |
### `.env` 示例
```bash
LDAP_URL=ldap://localhost:3890
LDAP_BASE_DN=dc=tlyq,dc=ai
LDAP_ADMIN_DN=uid=admin,ou=people,dc=tlyq,dc=ai
LDAP_ADMIN_PASS=admin123
JWT_SECRET=dev-secret-key-local
COOKIE_DOMAIN=
NODE_ENV=development
SMTP_HOST=smtphz.qiye.163.com
SMTP_PORT=465
SMTP_USER=gxp@qx002575.com
SMTP_PASS=
SMTP_FROM=gxp@qx002575.com
```
---
## Docker 部署
```
txjp 服务器
├── oa-ai容器 ← Next.js standalone监听 3000
├── nginx-ai ← 反向代理 oa.tlyq.ai → oa-ai:3000
└── webnetexternal ← 共享网络
```
生产环境需要 OA 容器能访问 Docker socket`/var/run/docker.sock`)以执行 `docker exec lldap` 修改密码和创建用户。
---
## 主题切换
支持深色/浅色双主题:
- `layout.tsx` 注入 `<script>` 读取 localStorage/系统偏好设置 `dark` class
- CSS 变量 `var(--bg)`、`var(--text)` 等响应主题变化
- `ThemeToggle` 组件:`document.documentElement.classList.toggle('dark')` + localStorage 持久化
- 配色对齐 assets/issueslate/blue
---
## 站点 URL 规则
源码中站点跳转 URL 均使用 localhost部署时 `deploy-ai.sh` 通过 `sed` 自动替换为生产域名。
---
## 开发规范
- **新增页面**:在 `src/app/` 下创建,中间件自动检查认证
- **API 路由**`/api/auth/` 公开,`/api/admin/` 需 admin 权限
- **admin 校验**`verifySharedJwt(token).username === 'admin'`
---
## 故障排查
### OA 502 Bad Gateway
**常见原因**nginx DNS 缓存旧 IP。OA 容器重启后 IP 变化nginx 仍向旧 IP 发送请求。
**解决**
```bash
docker restart nginx-ai
```
### 用户管理/角色管理一直加载
**原因**OA 容器未挂载 assets/issue 的 SQLite 数据库文件,或 LLDAP 容器缺少 `sqlite3`
**检查**
```bash
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 安装**(每次容器重建后需重新安装):
```bash
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`