feat: user auth.

This commit is contained in:
2026-04-30 19:10:47 +08:00
parent 23d27b4b6e
commit b8dfa150b2
5 changed files with 113 additions and 70 deletions
+47 -31
View File
@@ -7,6 +7,7 @@ import (
"net/http"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
@@ -17,29 +18,31 @@ type UserAdminController struct {
}
type userAdminPayload struct {
Email *string `json:"email"`
FlavorType models.UserFlavorType `json:"flavorType"`
IsActive *bool `json:"isActive"`
Password string `json:"password"`
Phone *string `json:"phone"`
RegionIDs []uint32 `json:"regionIds"`
Role models.UserRole `json:"role"`
Username string `json:"username"`
Email *string `json:"email"`
FlavorType models.UserFlavorType `json:"flavorType"`
IsActive *bool `json:"isActive"`
Password string `json:"password"`
Phone *string `json:"phone"`
RegionIDs []uint32 `json:"regionIds"`
RevokeTokens *bool `json:"revokeTokens"`
Role models.UserRole `json:"role"`
Username string `json:"username"`
}
type userAdminListItem struct {
CreatedAt int64 `json:"created_at"`
Email *string `json:"email"`
FlavorType models.UserFlavorType `json:"flavorType"`
ID uint `json:"id"`
IsActive bool `json:"isActive"`
KindergartenNames []string `json:"kindergartenNames"`
Phone *string `json:"phone"`
RegionIDs []uint32 `json:"regionIds"`
Regions []models.UserRegionBinding `json:"regions"`
Role models.UserRole `json:"role"`
UpdatedAt int64 `json:"updated_at"`
Username string `json:"username"`
CreatedAt int64 `json:"created_at"`
Email *string `json:"email"`
FlavorType models.UserFlavorType `json:"flavorType"`
ID uint `json:"id"`
IsActive bool `json:"isActive"`
KindergartenNames []string `json:"kindergartenNames"`
Phone *string `json:"phone"`
RegionIDs []uint32 `json:"regionIds"`
Regions []models.UserRegionBinding `json:"regions"`
Role models.UserRole `json:"role"`
TokenInvalidBefore int64 `json:"tokenInvalidBefore"`
UpdatedAt int64 `json:"updated_at"`
Username string `json:"username"`
}
func NewUserAdminController() *UserAdminController {
@@ -161,6 +164,7 @@ func (uc *UserAdminController) Update(c *gin.Context) {
}
if err := uc.DB.Transaction(func(tx *gorm.DB) error {
revokeTokens := payload.RevokeTokens != nil && *payload.RevokeTokens
updateData := map[string]interface{}{
"username": user.Username,
"email": user.Email,
@@ -172,6 +176,9 @@ func (uc *UserAdminController) Update(c *gin.Context) {
if strings.TrimSpace(payload.Password) != "" {
updateData["password"] = user.Password
}
if revokeTokens {
updateData["token_invalid_before"] = time.Now().UnixMilli()
}
if err := tx.Model(&models.User{}).Where("id = ?", user.ID).Updates(updateData).Error; err != nil {
return err
}
@@ -211,6 +218,10 @@ func (uc *UserAdminController) Delete(c *gin.Context) {
respondUserLookupError(c, err)
return
}
if isProtectedAdminAccount(user.Username) {
writeError(c, http.StatusBadRequest, "cannot delete protected admin user")
return
}
currentUserID, _, ok := currentUser(c)
if ok && currentUserID == user.ID {
writeError(c, http.StatusBadRequest, "cannot delete current user")
@@ -262,17 +273,18 @@ func (uc *UserAdminController) buildUserAdminItems(users []models.User) ([]userA
items := make([]userAdminListItem, 0, len(users))
for _, user := range users {
item := userAdminListItem{
CreatedAt: user.CreatedAt,
Email: user.Email,
FlavorType: user.FlavorType,
ID: user.ID,
IsActive: user.IsActive,
Phone: user.Phone,
RegionIDs: user.RegionIDs(),
Regions: user.Regions,
Role: user.Role,
UpdatedAt: user.UpdatedAt,
Username: user.Username,
CreatedAt: user.CreatedAt,
Email: user.Email,
FlavorType: user.FlavorType,
ID: user.ID,
IsActive: user.IsActive,
Phone: user.Phone,
RegionIDs: user.RegionIDs(),
Regions: user.Regions,
Role: user.Role,
TokenInvalidBefore: user.TokenInvalidBefore,
UpdatedAt: user.UpdatedAt,
Username: user.Username,
}
for _, regionID := range item.RegionIDs {
if kindergartenName, exists := kindergartenMap[regionID]; exists {
@@ -336,6 +348,10 @@ func normalizeOptionalString(value *string) *string {
return &trimmed
}
func isProtectedAdminAccount(username string) bool {
return strings.EqualFold(strings.TrimSpace(username), "admin")
}
func respondUserLookupError(c *gin.Context, err error) {
if errors.Is(err, gorm.ErrRecordNotFound) {
writeError(c, http.StatusNotFound, "user not found")