95 lines
5.5 KiB
Markdown
95 lines
5.5 KiB
Markdown
|
|
# 设计:照片墙服务器存图(按用户、最多 10 张)
|
|||
|
|
|
|||
|
|
> 把照片墙从「纯前端 localStorage 存图」升级为「图片存服务器、按登录用户隔离」。
|
|||
|
|
> 排版与装饰仍存浏览器。前置阅读:[photo-wall.md](../../photo-wall.md)、
|
|||
|
|
> [auth-login-gate.md](../../auth-login-gate.md)。
|
|||
|
|
|
|||
|
|
## 1. 目标与职责切分
|
|||
|
|
|
|||
|
|
| 存哪 | 内容 | 跨设备 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| **服务器**(`PHOTO_DIR/<用户>/`,≤10 张/用户) | 图片字节本身 | ✅ 任意设备登录可见 |
|
|||
|
|
| **浏览器 localStorage** | 背景、装饰(便签/胶带/贴纸/印章/手绘)、**每张图的排版覆盖**(位置/旋转/缩放/层级/标注/宽度,按图片 id 关联) | ❌ 各浏览器独立 |
|
|||
|
|
|
|||
|
|
**加载逻辑**:登录后拉取本用户图片列表 → 每张取字节显示 → 若 localStorage 有该 id
|
|||
|
|
的排版记录则套用,否则**自动散落排版**(沿用软木板随机摆放风格,按数量铺开避免重叠)。
|
|||
|
|
图片严格私有:读取必须带登录凭证,后端校验身份后才返回字节。
|
|||
|
|
|
|||
|
|
## 2. 鉴权(JWT)
|
|||
|
|
|
|||
|
|
现状:登录返回的 `token` 是时间戳 SHA256,**无任何接口校验**。本设计赋予其真实含义。
|
|||
|
|
|
|||
|
|
- `auth::issue_token(username)` 改为签发 **HS256 JWT**(`jsonwebtoken`,已是依赖):
|
|||
|
|
claims `sub = 用户名`、`exp = 现在 + 30 天`,密钥取环境变量 `JWT_SECRET`。
|
|||
|
|
- 未设 `JWT_SECRET` → 用内置开发兜底密钥并打印警告(仅本地开发用;生产必须设)。
|
|||
|
|
- 新增 `auth::verify_token(token) -> Option<String>`(返回用户名)。
|
|||
|
|
- 新增 axum 提取器 `AuthUser(String)`:读 `Authorization: Bearer <jwt>`,校验通过产出
|
|||
|
|
用户名,否则 `401`。受保护接口用它。
|
|||
|
|
- **影响**:token 格式变化,上线后现有登录态失效,需**重新登录一次**。
|
|||
|
|
|
|||
|
|
## 3. 后端接口与存储
|
|||
|
|
|
|||
|
|
存储布局:
|
|||
|
|
- 根目录 `PHOTO_DIR`(默认 `photo-wall`,相对进程 cwd;线上 cwd 为
|
|||
|
|
`/opt/internet-project/`,即 `/opt/internet-project/photo-wall/`)。
|
|||
|
|
- 每用户一子目录,目录名 = 用户名经字符集白名单 `[A-Za-z0-9_-]` 校验后使用;
|
|||
|
|
含非法字符的用户名拒绝(`400`)。
|
|||
|
|
- 每图文件名 `<16字节随机 hex>.<ext>`,`ext ∈ {jpg,png,gif,webp}`。
|
|||
|
|
**无独立元数据存储**:列目录即得列表(无需数据库)。
|
|||
|
|
- **图片 `id` 即完整文件名**(含扩展名,如 `a1b2…f9.jpg`)。`GET /:id` 据扩展名
|
|||
|
|
决定 `Content-Type`。`id` 仅允许 `[0-9a-f]+\.(jpg|png|gif|webp)`,否则 `404`。
|
|||
|
|
|
|||
|
|
接口(全部经 `AuthUser`,挂在 web 路由):
|
|||
|
|
|
|||
|
|
| 方法 | 路径 | 行为 | 返回 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| GET | `/api/web/photos` | 列出当前用户图片 | `{ items: [{ id }], max: 10 }` |
|
|||
|
|
| POST | `/api/web/photos` | 上传一张(body `{ dataUrl }` base64 data URL) | `201 { id }` |
|
|||
|
|
| GET | `/api/web/photos/:id` | 流式返回字节(属主校验,文件须在该用户目录) | `200` + `Content-Type` |
|
|||
|
|
| DELETE | `/api/web/photos/:id` | 删除一张 | `204` |
|
|||
|
|
|
|||
|
|
约束与错误:
|
|||
|
|
- 满 10 张再传 → `409`(前端也禁用上传按钮)。
|
|||
|
|
- 单图解码后 > 8 MB → `413`。
|
|||
|
|
- 类型不在白名单 → `415`。
|
|||
|
|
- `:id` 含路径分隔/`..` 或文件不存在 → `404`(防目录穿越;id 仅允许 hex+扩展名)。
|
|||
|
|
- 放宽对应路由的 axum `DefaultBodyLimit` 至 ~16 MB(容纳 base64)。
|
|||
|
|
|
|||
|
|
## 4. 前端数据流
|
|||
|
|
|
|||
|
|
- `useAuth`:在 `Auth` 接口里暴露 `token`(当前已存 session,仅未对外返回)。
|
|||
|
|
`App.tsx` 把 `auth.token` 传给 `PhotoWallPage`。
|
|||
|
|
- 新增 `Client/src/api/photos.ts`:`listPhotos(token)`、`uploadPhoto(token, dataUrl)`、
|
|||
|
|
`deletePhoto(token, id)`、`fetchPhotoObjectUrl(token, id)`(带 `Bearer` 头 fetch →
|
|||
|
|
`blob` → `URL.createObjectURL`)。
|
|||
|
|
- `PhotoWallPage`:
|
|||
|
|
- 挂载时:`listPhotos` → 并发取每张 objectUrl → 建照片元素;按图片 id 从
|
|||
|
|
localStorage 合并排版,缺失则自动散落(需图片自然宽高算比例,载入后取
|
|||
|
|
`naturalWidth/Height`)。
|
|||
|
|
- 持久化结构改为
|
|||
|
|
`{ bgIndex, decorations: El[], photoLayout: { [id]: {x,y,rot,scale,z,caption,w} } }`
|
|||
|
|
——**照片字节不再进 localStorage**。读到旧结构(照片带 base64)时只取其装饰部分。
|
|||
|
|
- 上传:选图 → 读 dataURL → `uploadPhoto` → 成功后插入照片元素(自动摆放)。满 10 禁用。
|
|||
|
|
- 删除(右键):`deletePhoto` → 移除元素 + 清该 id 本地排版 + `revokeObjectURL`。
|
|||
|
|
- 卸载时释放所有 objectUrl。
|
|||
|
|
- **右键菜单改进**(用户反馈:当前调整幅度太小):
|
|||
|
|
- 默认步进调大:旋转 ±15°、照片放大/缩小 ±30px、其它元素 scale ±0.25。
|
|||
|
|
- 新增「旋转角度」子项:预设角度(如 0/±15/±30/±45/90)直接设定。
|
|||
|
|
- 新增「放大比例」子项:预设比例(如 0.5×/0.75×/1×/1.5×/2×)直接设定。
|
|||
|
|
|
|||
|
|
## 5. 部署与运维
|
|||
|
|
|
|||
|
|
- 这是**后端改动** → 全量重部署:重编 Rust → 传二进制 → `restart_rust.sh` 重启
|
|||
|
|
(非 frontend-only)。
|
|||
|
|
- 服务器 `weather.env` 增加 `JWT_SECRET=<随机串>`;可选 `PHOTO_DIR`(默认即对)。
|
|||
|
|
- 首次上传自动创建 `photo-wall/<用户>/`。
|
|||
|
|
- CORS(仅影响 dev 跨源):在 `routes/web.rs` 的 `CorsLayer` 增加 `DELETE` 方法与
|
|||
|
|
`Authorization` 请求头;生产同源不受影响。
|
|||
|
|
- 上线后提示用户**重新登录一次**。
|
|||
|
|
|
|||
|
|
## 6. 不做(YAGNI)
|
|||
|
|
|
|||
|
|
- 服务器端图片压缩/缩放(小规模,35GB 盘够用;前端显示尺寸小,将来需要再加)。
|
|||
|
|
- 排版/装饰上云(明确只存浏览器)。
|
|||
|
|
- 图片重命名/相册/分享链接等。
|