From c3dec09dae571802ca4010a3c35a0921ce2542b0 Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Wed, 27 May 2026 01:03:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(settings):=20=E4=B8=AA=E4=BA=BA=E8=B5=84?= =?UTF-8?q?=E6=96=99=E7=BC=96=E8=BE=91=20+=20=E5=A4=B4=E5=83=8F=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0/=E8=A3=81=E5=88=87=20+=20=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E8=A3=81=E5=88=87=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **新增功能:** 用户名编辑:输入框替换静态文本,白名单验证(中文/英文/数字/下划线/连字符),前端计数器(n/16),utf8对齐PG VARCHAR,XSS防控。 个性签名编辑:Textarea,128字符上限,实时计数器。 头像上传管线:校验→解码→裁切→CatmullRom缩放→WebP二分编码≤100KB→原子写入(.tmp→os.Rename)。限制5MB,128-3840px,JPEG/PNG/WebP。输出512x512 WebP。文件名 {uid}_{timestamp}.webp。清理旧头像。 自定义裁切弹窗:浅色主题,固定裁切框+图片平移/缩放(1×-3×滚轮),box-shadow遮罩,三等分网格。坐标映射pan/zoom→原图像素→subImage。 CSP修复:img-src允许data:URI(FileReader预览)。 **文件变更:** 修改: README, docs/{development,structure}.md, go.{mod,sum}, cmd/server/main.go, controller/settings, middleware/security, router, templates/settings/{index.html,css} 新增: internal/service/avatar_service.go, docs/settings.md --- README.md | 3 +- cmd/server/main.go | 1 + docs/development.md | 33 ++- docs/settings.md | 195 +++++++++++++ docs/structure.md | 11 +- go.mod | 2 + go.sum | 4 + internal/controller/settings_controller.go | 43 ++- internal/middleware/security.go | 3 +- internal/router/router.go | 4 +- internal/service/avatar_service.go | 213 +++++++++++++++ .../MetaLab-2026/html/settings/index.html | 256 +++++++++++++++++- .../MetaLab-2026/static/css/settings.css | 172 +++++++++++- 13 files changed, 923 insertions(+), 17 deletions(-) create mode 100644 docs/settings.md create mode 100644 internal/service/avatar_service.go diff --git a/README.md b/README.md index a35cd8f..d3b43cf 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ MetaLab 是一个正在开发中的开源技术社区平台,旨在为开发者 - ✅ **安全头**——Content-Security-Policy 等安全响应头 - ✅ **管理后台**——用户管理、角色权限控制 - ✅ **软删除**——用户数据可恢复,登录时自动恢复 -- ✅ **用户设置页**——个人资料设置 +- ✅ **用户设置页**——个人资料修改(用户名、个性签名、头像上传与裁切) +- ✅ **头像处理**——JPEG/PNG/WebP 上传,方形裁切,512×512 WebP 编码 ≤100KB - ✅ **多主题支持**——可切换的前端主题系统 ## 计划中 diff --git a/cmd/server/main.go b/cmd/server/main.go index d4856d3..349db07 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -62,6 +62,7 @@ func main() { r.Static("/static", "./templates/MetaLab-2026/static") r.Static("/admin/static", "./templates/admin/static") r.Static("/shared/static", "./templates/shared/static") + r.Static("/uploads/avatars", "./storage/uploads/avatars") // 路由注册 router.Setup(r, db, cfg) diff --git a/docs/development.md b/docs/development.md index 59d8b92..ef2a3e7 100644 --- a/docs/development.md +++ b/docs/development.md @@ -378,6 +378,37 @@ RememberMe bool `json:"remember_me"` --- +### 14. 个人设置页与头像上传 + +**涉及文件**:参考 [`docs/settings.md`](settings.md) 获取完整实现文档。 + +**功能概要**: + +| 功能 | 实现文件 | +|------|----------| +| 用户名编辑 | `auth_service.go` — `UpdateProfile()`,正则 `[\p{Han}a-zA-Z0-9_-]` 白名单 | +| 个性签名编辑 | 同上,0-128 字符 | +| 头像处理管线 | `avatar_service.go`(新文件)— 解码→裁切→CatmullRom缩放→WebP二分编码→原子写入 | +| 前端裁切弹窗 | `settings/index.html` — 固定框 + 图片平移/缩放 + 坐标映射 | +| CSS样式 | `settings.css` — 浅色主题裁切弹窗、Toast通知、输入框样式 | +| CSP放宽 | `security.go` — `img-src 'self' data:`(裁切预览用data URI) | +| 静态路由 | `main.go` — `/uploads/avatars` → `./storage/uploads/avatars` | + +**设计决策**: + +| 决策 | 选择 | 原因 | +|------|------|------| +| 用户名校验 | 白名单 regex | 比黑名单更安全,直接排除 `<>&"'` 等 XSS 向量 | +| 字符计数 | `utf8.RuneCountInString` | 确保与 PostgreSQL `varchar(16)` 语义一致 | +| 头像编码 | WebP + 二分查找质量 | 目标 ≤100KB,兼顾质量与体积 | +| 原子写入 | `.tmp` → `os.Rename` | 防止写入中断产生损坏文件 | +| 裁切方式 | 固定框 + 图片平移/缩放 | 框大小不变,zoom使框覆盖更小区域,真正支持精确定位 | +| GIF支持 | 不支持 | 裁切后无法保持动画,无实际用途 | +| 最小分辨率 | 128×128 | 防止马赛克图被拉升到 512×512 | +| CSP data: URI | 显式允许 img-src | 裁切预览使用 FileReader → data URI | + +--- + ## 二、解决方案建议 ### 2.1 PostgreSQL 密码认证(问题一记录) @@ -505,7 +536,7 @@ config.yaml # 主配置(服务/数据库/JWT 含 remember_expire/BCry | ~~**P0**~~ | ~~登出功能~~ | ✅ 已完成 | `POST /api/auth/logout` → 清除三个 Cookie + 跳转首页 | | **P1** | 首页完善 | 待做 | 当前仅有 Hero 区域,需添加帖子列表、话题导航等 | | **P1** | 帖子系统 | 待做 | CRUD + 话题分类 + Markdown 编辑 | -| **P1** | 用户设置页 | 待做 | `/settings` — 修改用户名/头像/密码/个人简介 | +| **P1** | 用户设置页 | ✅ 已完成 | `/settings/profile`、`/settings/account` — 用户名修改、个性签名、头像上传与裁切 | | **P2** | 管理后台 | 待做 | `/admin` — 用户管理、内容审核、主题切换 | | ~~**P2**~~ | ~~刷新令牌~~ | ✅ 已完成 | 记住我 + access/refresh 双 Token + 自动续期 + 登出 | | **P3** | 多主题支持 | 待做 | `MetaLab-2026` → `MetaLab-2027` 切换 | diff --git a/docs/settings.md b/docs/settings.md new file mode 100644 index 0000000..4dd3113 --- /dev/null +++ b/docs/settings.md @@ -0,0 +1,195 @@ +# 个人设置功能 + +> 实现日期:2026-05-27 + +## 概述 + +个人设置是用户修改公开资料和头像的功能模块,包含三个子功能: + +1. **用户名编辑** — 输入框替换静态文本,白名单字符 + XSS 过滤 +2. **个性签名编辑** — Textarea 控件,128 字符上限 +3. **头像上传与裁切** — 完整图片处理管线,WebP 编码 ≤100KB + +## 页面结构 + +``` +/settings/profile — 个人资料(用户名、头像、个性签名) +/settings/account — 账号信息(只读:UID、邮箱、状态、注册时间) +``` + +左侧导航栏切换 `profile` / `account` 两个 tab,通过 `:tab` 路由参数区分。 + +## 用户名编辑 + +### 规则 + +| 项目 | 规格 | +|------|------| +| 允许字符 | 中文、英文大小写、数字、下划线(`_`)、连字符(`-`) | +| 前后端正则 | Go: `^[\p{Han}a-zA-Z0-9_-]+$` / JS: `^[\u4e00-\u9fffa-zA-Z0-9_-]+$` | +| 长度限制 | 1-16 字符(Unicode `utf8.RuneCountInString`) | +| XSS 防护 | 白名单排除 `<>&"'` 等 HTML 特殊字符;Go `html/template` 自动转义 | +| 去重 | 变更时检查 `ExistsByUsername`,冲突返回 409 | + +### API + +``` +PUT /api/settings/profile +Content-Type: application/json +{ "username": "...", "bio": "..." } + +成功: { "success": true, "message": "个人资料已更新" } +冲突: 409 { "message": "该用户名已被占用" } +无效: 400 { "message": "用户名格式无效..." } +``` + +## 个性签名编辑 + +| 项目 | 规格 | +|------|------| +| 控件 | `