feat(settings): 用户名校验强化、个性签名编辑、字符计数器及提示优化
- 用户名校验改用白名单正则(中英文/数字/下划线/连字符),字符数计法统一 - 简介→个性签名,新增 textarea 可编辑,上限 128 字符 - 用户名+签名各增加实时字符计数器(x/16、x/128) - API 合并为 PUT /api/settings/profile,一次提交两字段,按变更写入 - Toast 移至 NAV 下方,独立样式,5 秒自动消失 - 移除用户 ID 灰色底色,提示改为「个人资料已更新」 - 纯文本存储 + Go 模板自动转义防 XSS
This commit is contained in:
@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"unicode/utf8"
|
||||
|
||||
"metazone.cc/metalab/internal/common"
|
||||
"metazone.cc/metalab/internal/config"
|
||||
@ -41,6 +42,9 @@ var (
|
||||
var pwLetter = regexp.MustCompile(`[a-zA-Z]`)
|
||||
var pwDigit = regexp.MustCompile(`\d`)
|
||||
|
||||
// usernamePattern 用户名合法字符:中文、英文大小写、数字、下划线、连字符
|
||||
var usernamePattern = regexp.MustCompile(`^[\p{Han}a-zA-Z0-9_-]+$`)
|
||||
|
||||
// dummyHash 防时序攻击:当用户不存在时,仍对虚拟哈希执行完整的 bcrypt 比对
|
||||
// 在 init() 中按当前 bcrypt 成本生成,确保时序与真实校验一致
|
||||
var dummyHash []byte
|
||||
@ -184,3 +188,52 @@ func (s *AuthService) GetProfile(userID uint) (*model.User, error) {
|
||||
func (s *AuthService) InvalidateSessions(userID uint) error {
|
||||
return s.userRepo.IncrementTokenVersion(userID)
|
||||
}
|
||||
|
||||
// UpdateProfile 修改个人资料(用户名 + 个性签名)
|
||||
// 用户名校验:非空、1-16 字符、白名单、去重
|
||||
// 个性签名校验:0-128 字符纯文本,Go 模板自动 HTML 转义防 XSS
|
||||
// 任一字段无变更时跳过该字段的写库操作
|
||||
func (s *AuthService) UpdateProfile(userID uint, username, bio string) error {
|
||||
// 查当前用户
|
||||
user, err := s.userRepo.FindByID(userID)
|
||||
if err != nil {
|
||||
return common.ErrUserNotFound
|
||||
}
|
||||
|
||||
needsUpdate := false
|
||||
|
||||
// 用户名:校验 + 更新
|
||||
if n := utf8.RuneCountInString(username); n == 0 {
|
||||
return common.ErrUsernameInvalid
|
||||
} else if n > 16 {
|
||||
return common.ErrUsernameInvalid
|
||||
}
|
||||
if !usernamePattern.MatchString(username) {
|
||||
return common.ErrUsernameInvalid
|
||||
}
|
||||
if username != user.Username {
|
||||
exists, err := s.userRepo.ExistsByUsername(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return common.ErrUsernameTaken
|
||||
}
|
||||
user.Username = username
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
// 个性签名:长度校验 + 更新
|
||||
if utf8.RuneCountInString(bio) > 128 {
|
||||
return common.ErrBioTooLong
|
||||
}
|
||||
if bio != user.Bio {
|
||||
user.Bio = bio
|
||||
needsUpdate = true
|
||||
}
|
||||
|
||||
if !needsUpdate {
|
||||
return nil
|
||||
}
|
||||
return s.userRepo.Update(user)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user