- go.mod module path matches repo zkcoi/Meshray-Manager (case-distinct from zkcoi/Meshray/core)
- rewrite internal imports meshray/{internal,web,pkg} -> Meshray-Manager/... (core refs kept)
- sync README.md / install.sh repo URLs; add CHANGELOG entry
222 lines
6.1 KiB
Go
222 lines
6.1 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"errors"
|
|
|
|
"git.zkcoi.com/zkcoi/Meshray-Manager/internal/model"
|
|
"git.zkcoi.com/zkcoi/Meshray-Manager/internal/store/sqlite"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// 字符集用于生成随机密码(保留用于特殊场景)
|
|
const passwordChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
|
|
|
|
// generateRandomPassword 使用 crypto/rand 生成安全的随机密码
|
|
func generateRandomPassword(length int) string {
|
|
b := make([]byte, length)
|
|
_, err := rand.Read(b)
|
|
if err != nil {
|
|
// 极端情况下回退到简单方案(几乎不会发生)
|
|
return "REPLACE_WITH_SECURE_PASSWORD"
|
|
}
|
|
// 使用 Base64 编码,确保包含各种字符
|
|
encoded := base64.StdEncoding.EncodeToString(b)
|
|
// 截取所需长度(Base64 编码后长度为 4/3 倍)
|
|
if len(encoded) >= length {
|
|
return encoded[:length]
|
|
}
|
|
return encoded
|
|
}
|
|
|
|
// GenerateRandomPassword 生成随机密码(公开函数)
|
|
func GenerateRandomPassword(length int) string {
|
|
return generateRandomPassword(length)
|
|
}
|
|
|
|
// UserService 用户服务
|
|
type UserService struct {
|
|
store *sqlite.Store
|
|
}
|
|
|
|
// NewUserService 创建用户服务实例
|
|
func NewUserService(store *sqlite.Store) *UserService {
|
|
return &UserService{store: store}
|
|
}
|
|
|
|
// InitializeAdmin 初始化管理员账户(首次启动时调用)
|
|
func (s *UserService) InitializeAdmin() (username, password string, err error) {
|
|
// 检查是否已存在 admin 用户
|
|
var existingUser model.User
|
|
if err := s.store.DB().Where("role = ?", "admin").First(&existingUser).Error; err == nil {
|
|
// 已存在,返回当前用户名(密码不返回)
|
|
return existingUser.Username, "", nil
|
|
}
|
|
|
|
// 生成随机密码
|
|
randomPassword := generateRandomPassword(16)
|
|
|
|
// 密码加密
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(randomPassword), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
// 创建 admin 用户
|
|
adminUser := &model.User{
|
|
Username: "admin",
|
|
PasswordHash: string(hashedPassword),
|
|
Email: "",
|
|
Role: "admin",
|
|
Status: "active",
|
|
}
|
|
|
|
if err := s.store.DB().Create(adminUser).Error; err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
return "admin", randomPassword, nil
|
|
}
|
|
|
|
// Authenticate 验证用户登录
|
|
func (s *UserService) Authenticate(username, password string) (*model.User, error) {
|
|
var user model.User
|
|
if err := s.store.DB().Where("username = ?", username).First(&user).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, errors.New("用户不存在")
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// 验证密码
|
|
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil {
|
|
return nil, errors.New("密码错误")
|
|
}
|
|
|
|
// 检查用户状态
|
|
if user.Status != "active" {
|
|
return nil, errors.New("账户已被禁用")
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
// ChangePasswordRequest 修改密码请求
|
|
type ChangePasswordRequest struct {
|
|
OldPassword string `json:"old_password"`
|
|
NewPassword string `json:"new_password"`
|
|
}
|
|
|
|
// ChangePassword 修改用户密码
|
|
func (s *UserService) ChangePassword(userID uint, req *ChangePasswordRequest) error {
|
|
// 1. 查询用户
|
|
var user model.User
|
|
if err := s.store.DB().First(&user, userID).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errors.New("用户不存在")
|
|
}
|
|
return err
|
|
}
|
|
|
|
// 2. 验证旧密码
|
|
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.OldPassword)); err != nil {
|
|
return errors.New("原密码错误")
|
|
}
|
|
|
|
// 3. 验证新密码强度
|
|
if len(req.NewPassword) < 6 {
|
|
return errors.New("密码长度不能少于 6 位")
|
|
}
|
|
|
|
// 4. 加密新密码
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 5. 更新密码
|
|
user.PasswordHash = string(hashedPassword)
|
|
if err := s.store.DB().Save(&user).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetUserByID 根据 ID 获取用户
|
|
func (s *UserService) GetUserByID(userID uint) (*model.User, error) {
|
|
var user model.User
|
|
if err := s.store.DB().First(&user, userID).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
// UpdateUser 更新用户信息
|
|
func (s *UserService) UpdateUser(userID uint, updates map[string]interface{}) (*model.User, error) {
|
|
var user model.User
|
|
if err := s.store.DB().First(&user, userID).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := s.store.DB().Model(&user).Updates(updates).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
// UpdateAdminProfile 更新管理员资料(仅允许 admin 用户调用)
|
|
func (s *UserService) UpdateAdminProfile(username, email, password string) error {
|
|
var user model.User
|
|
if err := s.store.DB().Where("role = ?", "admin").First(&user).Error; err != nil {
|
|
return errors.New("管理员账户不存在")
|
|
}
|
|
|
|
updates := make(map[string]interface{})
|
|
if username != "" && username != user.Username {
|
|
// 检查新用户名是否已被使用
|
|
var existing model.User
|
|
if err := s.store.DB().Where("username = ? AND role = ?", username, "admin").First(&existing).Error; err == nil {
|
|
return errors.New("用户名已存在")
|
|
}
|
|
updates["username"] = username
|
|
}
|
|
|
|
if email != "" && email != user.Email {
|
|
updates["email"] = email
|
|
}
|
|
|
|
if password != "" {
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
updates["password_hash"] = string(hashedPassword)
|
|
}
|
|
|
|
if len(updates) == 0 {
|
|
return nil // 没有需要更新的
|
|
}
|
|
|
|
return s.store.DB().Model(&user).Updates(updates).Error
|
|
}
|
|
|
|
// ResetAdminPassword 重置管理员密码(命令行工具使用)
|
|
func (s *UserService) ResetAdminPassword(newPassword string) error {
|
|
var user model.User
|
|
if err := s.store.DB().Where("role = ?", "admin").First(&user).Error; err != nil {
|
|
return errors.New("管理员账户不存在")
|
|
}
|
|
|
|
// 加密新密码
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.store.DB().Model(&user).Update("password_hash", string(hashedPassword)).Error
|
|
}
|