- Move ORM models from internal/model to pkg/model organized by module (auth/k8s/registry) - Add authentication module with login, user management handlers - Update all import paths to use new model locations - Add frontend auth pages (Login, UserManagement) and authStore - Remove deprecated internal/model/model.go
208 lines
5.2 KiB
Go
208 lines
5.2 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strconv"
|
|
|
|
auth "gitea.loveuer.com/loveuer/cluster/pkg/model/auth"
|
|
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
|
|
"gitea.loveuer.com/loveuer/cluster/pkg/tool"
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func UserList(ctx context.Context, db *gorm.DB) fiber.Handler {
|
|
return func(c fiber.Ctx) error {
|
|
var users []auth.User
|
|
if err := db.Find(&users).Error; err != nil {
|
|
return resp.R500(c, "", nil, err.Error())
|
|
}
|
|
|
|
return resp.R200(c, map[string]interface{}{
|
|
"users": users,
|
|
})
|
|
}
|
|
}
|
|
|
|
func UserCreate(ctx context.Context, db *gorm.DB) fiber.Handler {
|
|
return func(c fiber.Ctx) error {
|
|
var req struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
Email string `json:"email"`
|
|
Nickname string `json:"nickname"`
|
|
Role string `json:"role"`
|
|
Status string `json:"status"`
|
|
Permissions string `json:"permissions"`
|
|
}
|
|
|
|
body := c.Body()
|
|
if len(body) == 0 {
|
|
return resp.R400(c, "EMPTY_BODY", nil, "request body is empty")
|
|
}
|
|
|
|
if err := json.Unmarshal(body, &req); err != nil {
|
|
return resp.R400(c, "INVALID_REQUEST", nil, "invalid request body")
|
|
}
|
|
|
|
if req.Username == "" || req.Password == "" {
|
|
return resp.R400(c, "MISSING_FIELDS", nil, "username and password are required")
|
|
}
|
|
|
|
if err := tool.CheckPassword(req.Password); err != nil {
|
|
return resp.R400(c, "WEAK_PASSWORD", nil, err.Error())
|
|
}
|
|
|
|
var existing auth.User
|
|
if err := db.Unscoped().Where("username = ?", req.Username).First(&existing).Error; err == nil {
|
|
return resp.R400(c, "USER_EXISTS", nil, "username already exists")
|
|
}
|
|
|
|
user := &auth.User{
|
|
Username: req.Username,
|
|
Password: tool.NewPassword(req.Password),
|
|
Email: req.Email,
|
|
Nickname: req.Nickname,
|
|
Role: req.Role,
|
|
Status: req.Status,
|
|
Permissions: req.Permissions,
|
|
}
|
|
|
|
if user.Role == "" {
|
|
user.Role = "user"
|
|
}
|
|
if user.Status == "" {
|
|
user.Status = "active"
|
|
}
|
|
|
|
if err := db.Create(user).Error; err != nil {
|
|
return resp.R500(c, "", nil, err.Error())
|
|
}
|
|
|
|
return resp.R200(c, map[string]interface{}{
|
|
"user": user,
|
|
})
|
|
}
|
|
}
|
|
|
|
func UserUpdate(ctx context.Context, db *gorm.DB) fiber.Handler {
|
|
return func(c fiber.Ctx) error {
|
|
userID := c.Params("id")
|
|
if userID == "" {
|
|
return resp.R400(c, "MISSING_ID", nil, "user id is required")
|
|
}
|
|
|
|
authHeader := c.Get("Authorization")
|
|
var currentUserID uint
|
|
if authHeader != "" {
|
|
tokenString := authHeader
|
|
if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
|
|
tokenString = authHeader[7:]
|
|
}
|
|
|
|
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
|
return []byte(JWTSecret), nil
|
|
})
|
|
|
|
if err == nil && token.Valid {
|
|
if claims, ok := token.Claims.(*Claims); ok {
|
|
currentUserID = claims.UserID
|
|
}
|
|
}
|
|
}
|
|
|
|
targetUserID, _ := strconv.ParseUint(userID, 10, 32)
|
|
isSelf := currentUserID == uint(targetUserID)
|
|
|
|
var req struct {
|
|
Email string `json:"email"`
|
|
Nickname string `json:"nickname"`
|
|
Password string `json:"password"`
|
|
Role string `json:"role"`
|
|
Status string `json:"status"`
|
|
Permissions string `json:"permissions"`
|
|
}
|
|
|
|
body := c.Body()
|
|
if len(body) == 0 {
|
|
return resp.R400(c, "EMPTY_BODY", nil, "request body is empty")
|
|
}
|
|
|
|
if err := json.Unmarshal(body, &req); err != nil {
|
|
return resp.R400(c, "INVALID_REQUEST", nil, "invalid request body")
|
|
}
|
|
|
|
var user auth.User
|
|
if err := db.First(&user, userID).Error; err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return resp.R404(c, "USER_NOT_FOUND", nil, "user not found")
|
|
}
|
|
return resp.R500(c, "", nil, err.Error())
|
|
}
|
|
|
|
user.Email = req.Email
|
|
user.Nickname = req.Nickname
|
|
|
|
if req.Password != "" {
|
|
if err := tool.CheckPassword(req.Password); err != nil {
|
|
return resp.R400(c, "WEAK_PASSWORD", nil, err.Error())
|
|
}
|
|
user.Password = tool.NewPassword(req.Password)
|
|
}
|
|
|
|
if !isSelf {
|
|
user.Role = req.Role
|
|
user.Status = req.Status
|
|
user.Permissions = req.Permissions
|
|
}
|
|
|
|
if err := db.Save(&user).Error; err != nil {
|
|
return resp.R500(c, "", nil, err.Error())
|
|
}
|
|
|
|
return resp.R200(c, map[string]interface{}{
|
|
"user": user,
|
|
})
|
|
}
|
|
}
|
|
|
|
func UserDelete(ctx context.Context, db *gorm.DB) fiber.Handler {
|
|
return func(c fiber.Ctx) error {
|
|
userID := c.Params("id")
|
|
if userID == "" {
|
|
return resp.R400(c, "MISSING_ID", nil, "user id is required")
|
|
}
|
|
|
|
var user auth.User
|
|
if err := db.First(&user, userID).Error; err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return resp.R404(c, "USER_NOT_FOUND", nil, "user not found")
|
|
}
|
|
return resp.R500(c, "", nil, err.Error())
|
|
}
|
|
|
|
if user.Username == "admin" {
|
|
return resp.R403(c, "CANNOT_DELETE_ADMIN", nil, "cannot delete admin user")
|
|
}
|
|
|
|
var count int64
|
|
if err := db.Model(&auth.User{}).Count(&count).Error; err != nil {
|
|
return resp.R500(c, "", nil, err.Error())
|
|
}
|
|
|
|
if count <= 1 {
|
|
return resp.R403(c, "LAST_USER", nil, "cannot delete the last user")
|
|
}
|
|
|
|
if err := db.Delete(&user).Error; err != nil {
|
|
return resp.R500(c, "", nil, err.Error())
|
|
}
|
|
|
|
return resp.R200(c, map[string]interface{}{
|
|
"message": "user deleted successfully",
|
|
})
|
|
}
|
|
}
|