feat(auth): 添加认证模块和图片批注功能(项目初始化)

- 实现用户登录、登出、密码修改等认证功能
- 添加会话管理和权限控制中间件
- 创建图片批注组件和相关API路由
- 实现批注的增删改查功能
- 添加Docker和Git忽略配置文件
- 创建系统架构文档和开发约定说明
- 集成认证模块到前端应用路由中
This commit is contained in:
yuzhe
2026-07-21 15:28:55 +08:00
commit b0c498fbb6
81 changed files with 10826 additions and 0 deletions

81
docs/architecture.md Normal file
View File

@@ -0,0 +1,81 @@
# 架构与数据模型
## 系统边界
Delivery Desk 是单体 Web 应用React 前端调用 Express API前后端共享 `shared/types.ts` 类型。开发环境使用 SQLite 和本地上传目录;正式环境使用 PostgreSQL 和腾讯云 COS。
```mermaid
flowchart LR
browser["浏览器"] --> app["Express / React 应用"]
app --> database["SQLite 或 PostgreSQL"]
app --> local["本地 uploads开发"]
app --> cos["腾讯云 COS正式"]
customer["客户验收链接"] --> app
client["外部 API 客户端"] --> app
```
## 业务层级
```text
运营组
└── 项目
└── 作品交付集
└── 作品
└── 版本
```
- 一个运营组只能有一位组管理员,可以有多位光影叙事。
- 平台管理员可以有多位,不属于固定运营组。
- 普通工作台账号只能读写所属运营组的数据;平台管理员可跨组管理。
- 客户会话只绑定一个项目,不能跨项目浏览。
- 平台级 API Key 可创建项目;项目级 API Key 只能操作指定项目。
## 运行结构
- `src/`React 页面、组件、状态和 API 客户端。
- `api/routes/`HTTP 路由与输入校验。
- `api/services/`:作品、存储等业务编排。
- `api/repositories/`:查询封装。
- `api/database.ts`SQLite/PostgreSQL 统一查询接口和事务。
- `api/db.ts`SQLite 初始化及增量迁移。
- `db/postgres/schema.sql`PostgreSQL 当前完整 schema。
- `shared/types.ts`:前后端共享领域类型。
`DATABASE_URL` 存在时使用 PostgreSQL否则使用 SQLite。两套数据库必须保持相同业务约束涉及表或字段的修改必须同时更新 `api/db.ts``db/postgres/schema.sql` 及迁移验证脚本。
## 主要数据表
| 表 | 用途 |
|---|---|
| `operation_groups` | 运营组及启停状态 |
| `users` / `sessions` | 工作台账号、角色和登录会话 |
| `customer_sessions` | 客户项目级验收会话 |
| `projects` | 项目、客户访问密码和访问期限 |
| `collections` | 项目下的作品交付集 |
| `notes` | 作品当前状态和当前版本 |
| `work_versions` | 各版本标题、正文、标签和状态快照 |
| `images` | 版本图片、顺序、存储提供方和对象 Key |
| `annotations` | 图片坐标批注 |
| `text_annotations` | 标题或正文的版本级批注 |
| `work_comments` | 作品总体反馈与回复 |
| `review_events` | 提交、修改、通过、重新打开等验收记录 |
| `api_keys` | 平台级或项目级 API Key 的哈希与状态 |
| `storage_configs` | 加密后的 COS 配置及启用状态 |
| `audit_logs` | 管理和业务操作审计 |
## 存储流程
平台管理员在管理页新增 COS 配置。SecretId 和 SecretKey 使用 `COS_CONFIG_ENCRYPTION_KEY` 派生的 AES-256-GCM 密钥加密后写入数据库,读取配置的接口不会返回明文。
启用配置前会在目标桶的 `.delivery-desk-check/` 路径依次上传、读取并删除一个临时对象。启用后,新上传文件写入:
```text
<path-prefix>/originals/YYYY/MM/<uuid>.<ext>
```
未启用 COS 时,上传文件保存在本地 `uploads/`。图片 URL 按产品约定为公开随机地址,不提供对象级访问鉴权。
## 验收状态
作品状态为 `draft``pending``changes_requested``approved`。客户只能看到非草稿作品;客户可通过或要求修改,要求修改必须填写原因。已通过作品只能由平台管理员或所属组管理员填写原因后重新打开,历史事件保留。

37
docs/handoff.md Normal file
View File

@@ -0,0 +1,37 @@
# 初版交接说明
## 已完成
- 三类工作台角色、运营组隔离、账号管理和 7 天会话
- 项目、作品交付集、作品、版本和验收状态
- 手动多图上传、封面、上传前拖拽排序及新版本
- 图片坐标批注、标题/正文批注、总体反馈和验收记录
- 客户项目链接、密码、姓名、期限和验收决定
- API Key、审计日志、COS 前端配置及连接测试
- SQLite/PostgreSQL 双运行时、迁移验证和 Docker 部署
- 桌面端与移动端响应式页面
## 初版上线前仍需完成
以下需求尚未在代码中完整落地,不应在交付时宣称可用:
- ZIP + CSV 批量导入和最多 100 个作品的异步批量 API
- `externalId` 幂等创建项目、作品交付集和作品
- webhook 与站内未读通知
- PDF 验收报告和最终原图 ZIP 导出
- 批注/回复的参考图片附件
- 项目、作品交付集、作品的回收站、归档恢复和永久删除规则
- 已上传作品在所有阶段的图片重新排序
- 在线人员状态、实时变更通知和并发版本冲突保护
- HEIC/HEIF 转换、缩略图流水线和 EXIF 定位信息清理
- 自动化端到端浏览器测试及真实腾讯云、PostgreSQL 部署演练
## 上线门槛
初版正式发布至少应满足:
1. 使用 PostgreSQL 和独立生产 COS 桶,完成一次备份恢复演练。
2. 轮换所有在聊天、截图或开发数据库中出现过的云密钥和临时密码。
3. 在 HTTPS 域名下验证平台管理员、组管理员、光影叙事和客户四条核心流程。
4. 根据真实交付承诺,从上方未完成清单中选定必须进入初版的项目。

94
docs/integration-guide.md Normal file
View File

@@ -0,0 +1,94 @@
# API 接入指南
## 认证方式
工作台网页使用 HttpOnly Cookie 会话。外部客户端使用:
```http
Authorization: Bearer dd_live_xxx
```
API Key 明文只在创建时返回一次,数据库仅保存 SHA-256 哈希。平台管理员创建平台级 Key组管理员创建本组项目级 Key。失效或越权请求会返回 `401``403`
## 主要路由
| 路由组 | 用途 |
|---|---|
| `/api/auth/*` | 登录、退出、当前账号、修改密码 |
| `/api/management/groups` | 运营组创建、改名、启停和管理员更换 |
| `/api/management/users` | 账号创建、改名、启停和重置密码 |
| `/api/management/api-keys` | API Key 创建、查询和吊销 |
| `/api/management/audit-logs` | 审计日志查询 |
| `/api/management/storage-configs` | COS 配置、连接测试和启用 |
| `/api/projects` | 项目创建、查询和编辑 |
| `/api/projects/:projectId/collections` | 作品交付集创建、查询和编辑 |
| `/api/notes` | 作品查询与 multipart 上传 |
| `/api/notes/:noteId/versions` | 上传作品新版本 |
| `/api/notes/:noteId/status` | 草稿与待验收状态切换 |
| `/api/notes/:noteId/text-annotations` | 标题/正文批注 |
| `/api/images/:imageId/annotations` | 图片坐标批注 |
| `/api/review/:slug/*` | 客户登录、浏览、反馈与验收 |
| `/api/health` | 数据库就绪检查 |
## 创建项目
平台级 API Key 可以指定目标运营组。项目级 Key 不能创建项目。
```bash
curl -X POST http://localhost:3001/api/projects \
-H "Authorization: Bearer $DELIVERY_DESK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"7 月内容计划","slug":"july-content","groupId":1,"client_description":"客户可见说明"}'
```
`slug` 仅支持小写字母、数字和连字符,并作为客户验收链接的一部分。
## 创建作品交付集
```bash
curl -X POST http://localhost:3001/api/projects/1/collections \
-H "Authorization: Bearer $DELIVERY_DESK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"2026 年 7 月交付","client_description":"本月交付内容"}'
```
## 上传作品
作品上传使用 `multipart/form-data`,至少一张、最多 30 张图片,单图最大 20 MB。图片数组顺序就是初始展示顺序第一张为封面。
```bash
curl -X POST http://localhost:3001/api/notes \
-H "Authorization: Bearer $DELIVERY_DESK_API_KEY" \
-F "collectionId=1" \
-F "title=作品标题" \
-F "description=正文内容" \
-F "tags=用户填写的标签原文" \
-F "images=@./01.jpg" \
-F "images=@./02.jpg"
```
当前接受 JPEG、PNG、GIF、WebP 和 AVIF。标签按一段原文保存和展示不会自动添加 `#` 或拆分为标签库。
## 创建新版本
```bash
curl -X POST http://localhost:3001/api/notes/12/versions \
-H "Authorization: Bearer $DELIVERY_DESK_API_KEY" \
-F "title=修改后的标题" \
-F "description=修改后的正文" \
-F "tags=修改后的标签原文" \
-F "images=@./v2-01.jpg"
```
批注绑定作品版本或具体图片,不会因新版本覆盖历史验收证据。
## 错误响应
错误统一以 JSON 返回:
```json
{ "error": "错误说明" }
```
常见状态码:`400` 输入无效、`401` 未认证、`403` 越权、`404` 资源不存在、`409` 唯一性或状态冲突、`500` 服务端错误。

70
docs/operator-runbook.md Normal file
View File

@@ -0,0 +1,70 @@
# 部署与运维手册
## 环境变量
| 变量 | 必需 | 说明 |
|---|---|---|
| `NODE_ENV` | 是 | 正式环境设为 `production` |
| `PORT` | 否 | API 与生产静态站端口,默认 `3001` |
| `DATABASE_URL` | 正式环境 | PostgreSQL 连接串;缺省时使用 SQLite |
| `PGSSL` | 否 | 内网 PostgreSQL 可设为 `disable` |
| `PG_POOL_MAX` | 否 | 连接池上限,默认 `10` |
| `CORS_ORIGIN` | 公网分离部署 | 允许来源,多个值以逗号分隔 |
| `POSTGRES_PASSWORD` | Docker | `compose.yaml` 使用的数据库密码 |
| `COS_CONFIG_ENCRYPTION_KEY` | 正式环境 | 至少 32 字符的稳定随机密钥 |
| `INITIAL_ADMIN_USERNAME` | 首次初始化 | 平台管理员账号,默认 `admin` |
| `INITIAL_ADMIN_DISPLAY_NAME` | 首次初始化 | 平台管理员显示名 |
| `INITIAL_ADMIN_PASSWORD` | 首次初始化 | 空 PostgreSQL 必须提供的临时密码 |
真实环境变量只放在部署平台或未提交的 `.env` 中。
## 部署
```bash
cp .env.example .env
# 编辑 .env 后:
docker compose up -d --build
```
在 HTTPS 反向代理后公开应用。不要直接提交 TLS 私钥、数据库文件、`.env``data/``uploads/`
## 冒烟检查
```bash
curl http://127.0.0.1:3001/api/health
docker compose ps
docker compose logs --tail=100 app
```
健康接口应返回 `success: true`,且正式环境的 `database` 应为 `postgres`
## COS 配置
1. 使用平台管理员进入“平台管理 → 腾讯云 COS”。
2. 填写地域、带 APPID 的存储桶名、公开访问域名、可选 CDN 域名、路径前缀及密钥。
3. 保存后执行连接测试。
4. 测试通过后启用配置。
连接测试会真实执行一次上传、读取和删除,因此密钥至少需要目标前缀的这三项权限。测试对象会尽力清理;请求中断时可检查 `<path-prefix>/.delivery-desk-check/` 是否残留临时文件。
COS 使用公开 URL。必须关闭桶列表功能并使用不可枚举对象名拿到 URL 的人可以直接访问文件。
## 数据备份与恢复
- PostgreSQL 使用托管备份或定期 `pg_dump`,恢复流程需在预发布环境演练。
- COS 开启版本控制或生命周期策略前先评估成本。
- 本地 SQLite 的 `data/` 只用于开发,不作为正式备份方案。
- `COS_CONFIG_ENCRYPTION_KEY` 必须与数据库备份一同安全托管;遗失后无法解密已保存的 COS 凭证。
## 发布前检查
```bash
pnpm install --frozen-lockfile
pnpm check
pnpm lint
pnpm build
pnpm test:postgres-runtime
```
正式切换前还应验证管理员首次改密、客户访问门禁、COS 上传、客户批注与验收、数据库备份及 HTTPS Cookie。