From 0351dba6179035f1cfc608701308d31e94f64806 Mon Sep 17 00:00:00 2001 From: aiyimickey <39365912+aiyimickey@users.noreply.github.com> Date: Thu, 14 May 2026 16:36:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20OA=20=E7=BB=9F=E4=B8=80=E9=97=A8?= =?UTF-8?q?=E6=88=B7=E5=88=9D=E5=A7=8B=E5=8C=96=20=E2=80=94=20LLDAP=20?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=20+=20=E5=85=B1=E4=BA=AB=20JWT=20+=20?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OA 聚合导航首页(站点卡片) - LLDAP 统一认证登录/登出 - 共享 JWT(tlyq_session, domain=.tlyq.ai)实现跨站点免登录 - 用户管理:创建/删除用户、角色分配、密码修改 - 深色/浅色主题切换 - 邮件通知(163 企业邮箱) --- .env.example | 11 + .gitignore | 4 + CHANGELOG.md | 67 ++ CLAUDE.md | 174 +++ README.md | 50 + docker-compose.local.yml | 6 + docker-compose.yml | 33 + next-env.d.ts | 6 + next.config.ts | 7 + package-lock.json | 1123 ++++++++++++++++++++ package.json | 23 + src/app/admin/create-user/page.tsx | 267 +++++ src/app/admin/create-user/role-manager.tsx | 167 +++ src/app/api/admin/create-user/route.ts | 147 +++ src/app/api/admin/roles/route.ts | 32 + src/app/api/admin/user-roles/route.ts | 90 ++ src/app/api/admin/users/route.ts | 77 ++ src/app/api/auth/change-password/route.ts | 56 + src/app/api/auth/login/route.ts | 32 + src/app/api/auth/logout/route.ts | 8 + src/app/api/auth/me/route.ts | 23 + src/app/layout.tsx | 38 + src/app/login/page.tsx | 82 ++ src/app/page.tsx | 109 ++ src/app/profile/change-password-form.tsx | 159 +++ src/app/profile/page.tsx | 47 + src/app/settings/page.tsx | 5 + src/components/Header.tsx | 26 + src/components/HeaderUI.tsx | 62 ++ src/components/ThemeToggle.tsx | 37 + src/lib/email.ts | 76 ++ src/lib/jwt.ts | 61 ++ src/lib/ldap.ts | 74 ++ src/middleware.ts | 62 ++ tsconfig.json | 40 + 35 files changed, 3281 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CLAUDE.md create mode 100644 README.md create mode 100644 docker-compose.local.yml create mode 100644 docker-compose.yml create mode 100644 next-env.d.ts create mode 100644 next.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/app/admin/create-user/page.tsx create mode 100644 src/app/admin/create-user/role-manager.tsx create mode 100644 src/app/api/admin/create-user/route.ts create mode 100644 src/app/api/admin/roles/route.ts create mode 100644 src/app/api/admin/user-roles/route.ts create mode 100644 src/app/api/admin/users/route.ts create mode 100644 src/app/api/auth/change-password/route.ts create mode 100644 src/app/api/auth/login/route.ts create mode 100644 src/app/api/auth/logout/route.ts create mode 100644 src/app/api/auth/me/route.ts create mode 100644 src/app/layout.tsx create mode 100644 src/app/login/page.tsx create mode 100644 src/app/page.tsx create mode 100644 src/app/profile/change-password-form.tsx create mode 100644 src/app/profile/page.tsx create mode 100644 src/app/settings/page.tsx create mode 100644 src/components/Header.tsx create mode 100644 src/components/HeaderUI.tsx create mode 100644 src/components/ThemeToggle.tsx create mode 100644 src/lib/email.ts create mode 100644 src/lib/jwt.ts create mode 100644 src/lib/ldap.ts create mode 100644 src/middleware.ts create mode 100644 tsconfig.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..654512a --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# OA 门户环境变量 +LDAP_URL=ldap://localhost:3890 +LDAP_BASE_DN=dc=tlyq,dc=ai +JWT_SECRET=change-me-same-across-all-sites +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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e64760 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.next/ +.env +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..79c28a7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,67 @@ +# 变更日志 + +## 2026-05-14 + +- [新增] 管理员权限接入 LLDAP `lldap_admin` 组:`isLldapAdmin()` 通过 LDAP 查询组成员决定管理权限,不再硬编码 `username === 'admin'`;将用户加入该组即可获得 OA 管理权限 +- [优化] `/api/auth/me` 响应增加 `isAdmin` 字段,前端复用判断权限 +- [修复] 部署后 Cloudflare 缓存旧 HTML 引用已删除 chunk 导致 Application Error:middleware + nginx 双层 `Cache-Control: no-cache` 防缓存,部署脚本 `docker compose restart` 确保容器重启 +- [修复] deploy-ai.sh 对无 Dockerfile 站点(OA)容器不重启,Next.js 进程仍使用旧 `.next`:`docker compose up -d` 后追加 `docker compose restart` +- [新增] `nginx-proxy-ai/conf.d/oa-ai.conf` 纳入版本控制,新服务器可直接部署 +- [优化] 统一顶栏 Header 组件:提取 `Header.tsx`(服务端)+ `HeaderUI.tsx`(客户端复用),三处页面(首页/用户管理/个人信息)共用同一顶栏,退出按钮、displayName、返回链接风格完全一致 +- [优化] 个人信息页"修改密码"改为折叠按钮,点击后展开表单;新增"确认新密码"字段 + 两次输入一致性校验 +- [优化] 门户首页欢迎语和右上角个人信息优先显示 displayName(显示名),回退 username +- [优化] 门户退出按钮改为 SVG 门+箭头图标,暗色/亮色模式均清晰可见,hover 高亮 +- [优化] 创建用户弹窗(未填邮箱时)新增显示用户名和显示名,"复制密码"改为"复制信息",复制内容与邮件一致(含用户名、密码、登录地址、安全提示) +- [修复] deploy-ai.sh 中 OA 的 localhost→域名 sed 替换在 macOS BSD sed 下静默失败,导致生产环境 OA 导航链接指向 localhost:改用 `sed $SED_I_BACKUP`,同时 page.tsx 新增 `siteUrl()` 自动根据 `NODE_ENV` 构造域名 +- [修复] OA 容器缺少 SMTP 环境变量,创建用户填邮箱时邮件发送静默失败:docker-compose.yml 添加 SMTP_HOST/PORT/USER/PASS/FROM +- [修复] OA 创建用户报 401:`LDAP_ADMIN_PASS` 回退默认值与服务器 LLDAP 实际密码不匹配 → 改为 `docker exec lldap printenv` 动态获取 +- [修复] assets/issue 缺少 LDAP_URL/LDAP_BASE_DN,容器内回退 localhost 不可达 → docker-compose.yml 添加 +- [修复] assets/issue 缺少 COOKIE_DOMAIN,JWT_SECRET 不一致,跨站点免登录失效 → 统一硬编码 JWT_SECRET + COOKIE_DOMAIN +- [修复] assets/issue 新用户 SSO 免登录自动创建:`getCurrentUser()` 增加 `INSERT OR IGNORE` 逻辑 +- [修复] assets/issue Docker 镜像原生模块 musl/glibc 不兼容导致登录 API 500 → Dockerfile 改为 `npm install` 在 COPY standalone 之后 + `npm rebuild better-sqlite3` + 删除 musl 变体 +- [修复] deploy-ai.sh `--build` 在无 Dockerfile 站点(OA)失败导致容器未重启 → 改为检测 Dockerfile 存在才加 `--build` +- [部署] deploy-ai.sh 新增构建后清理 musl 残留 + 根据 Dockerfile 存在与否自动决定是否 `--build` +- [调整] 所有站点 docker-compose.yml 统一 JWT_SECRET=oa-shared-jwt-secret-tlyq-2026、COOKIE_DOMAIN=.tlyq.ai + +## 2026-05-12 + +- [新增] 修改密码增加复杂度要求:至少 8 位,大写/小写/数字/特殊字符 4 选 3,前端实时显示密码强度指示器 +- [修复] OA 容器只挂载单个 .db 文件导致 SQLite WAL 不共享,权限管理中已删除用户仍可见:改为挂载整个数据目录,WAL 文件实时同步 +- [新增] 创建用户支持邮件发送密码:新增邮箱输入框(选填),填写后通过 nodemailer 将用户名和密码发送至指定邮箱,未填写则弹窗显示(原流程) +- [新增] `src/lib/email.ts` 邮件发送工具(163 企业邮箱 SMTP,HTML + 纯文本双版本) +- [调整] 用户管理页标签重命名:「用户管理」→「删除用户」,「角色管理」→「权限管理」 +- [调整] 邮件提示修改密码只能通过 OA 统一门户,不能在子站点修改 +- [部署] OA 站点部署至 txjp 服务器,绑定域名 oa.tlyq.ai +- [部署] Cloudflare Origin Certificate 部署(覆盖 *.tlyq.ai,15 年有效期) +- [修复] OA 用户管理/角色管理加载缓慢:容器缺少 assets/issue 数据库卷挂载,LLDAP 缺少 sqlite3 +- [修复] OA 容器重启后 nginx DNS 缓存旧 IP 导致 502:重启 nginx 解决 +- [修复] www-ai 容器未启动导致 nginx 重载失败,连锁影响 OA 配置不生效 +- [调整] 所有站点 nginx 改为运行时 DNS 解析(resolver 127.0.0.11),容器离线不再阻塞 nginx 重载 +- [调整] OA API 路径改用环境变量(ASSETS_DB_PATH / ISSUE_DB_PATH),生产使用 Docker volume 路径 +- [调整] sso.tlyq.ai 301 重定向至 oa.tlyq.ai +- [调整] assets/issue/oa JWT 密钥统一为 oa-shared-jwt-secret-tlyq-2026 +- [调整] assets/issue docker-compose 移除 AUTHELIA_URL,添加 LDAP/COOKIE/INTERNAL_API_KEY 环境变量 +- [调整] LLDAP admin 密码同步为 3Vm!Y!@RCiPs + +## 2026-05-11 + +- [新增] OA 统一门户项目创建:Next.js 框架、LLDAP 认证、共享 JWT 跨站点免登录 +- [新增] 门户首页:6 站点卡片聚合导航(资产管理/工单跟踪/官网/云平台/Token工厂/代码仓库) +- [新增] 登录/退出功能:直连 LLDAP 认证,签发 tlyq_session 共享 JWT +- [新增] 个人信息页:账户信息展示 + 会话有效期 + 修改密码(docker exec 调 lldap_set_password) +- [新增] 用户管理(admin 专属):创建用户/用户列表/角色管理三 tab +- [新增] 创建用户自动生成 12 位复杂密码,弹窗显示 + 一键复制 +- [新增] 创建用户自动同步至 assets-ai/issue-ai,支持角色预设 +- [新增] 创建用户角色下拉从各站点实时获取可用角色列表 +- [新增] 用户列表含删除功能(LLDAP + 各站点 SQLite 同步删除) +- [新增] 角色管理:按站点修改用户角色,待保存机制 + 批量提交 + 确认弹窗 +- [新增] 深色/浅色主题切换(CSS 变量 + localStorage 持久化,同 assets/issue 风格) +- [新增] 统一顶栏:主题按钮 + 用户头像 + 个人信息链接 + 图标退出 +- [新增] 站点卡片 hover 域名提示 + 动画效果 +- [新增] CLAUDE.md / README.md / CHANGELOG.md +- [调整] 登录逻辑 v2.1:LLDAP 优先认证 + 本地密码缓存回退 + localadmin 应急用户 +- [调整] LLDAP 删除用户后,assets/issue 实时检测并踢出已登录用户 +- [调整] 会话有效期显示精确到秒 +- [优化] 用户管理页与首页顶栏样式统一(主题按钮/头像/退出图标) +- [优化] 密码特殊字符通过 base64 编码传输,避免 shell 转义问题 +- [优化] 站点卡片统一高度,移动端适配 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..335a204 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,174 @@ +# 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 统一管理) | + +### 常用命令 + +```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` | 邮件发送(nodemailer,163 企业邮箱,创建用户时发送凭证) | +| `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 认证: + +1. 用户登录 → LDAP bind 验证 → 签发 `tlyq_session` JWT(7 天) +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 +└── webnet(external) ← 共享网络 +``` + +生产环境需要 OA 容器能访问 Docker socket(`/var/run/docker.sock`)以执行 `docker exec lldap` 修改密码和创建用户。 + +--- + +## 主题切换 + +支持深色/浅色双主题: +- `layout.tsx` 注入 `