package main import ( "strconv" "strings" "github.com/gin-gonic/gin" "gorm.io/gorm" ) func (a *App) audit(operator uint, action, targetType, targetID, metadata string) { _ = a.db.Create(&AuditLog{OperatorID: operator, Action: action, TargetType: targetType, TargetID: targetID, Metadata: metadata}).Error } func defaultPermissions(role string) []string { if role == RolePrivileged { return []string{PermDownload, PermRedeem, PermWebDAV, PermWebDAVConfig} } if role == RoleUser { return []string{PermDownload, PermRedeem} } return nil } func (a *App) adminListUsers(c *gin.Context) { var users []User if err := a.db.Order("id asc").Find(&users).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } for i := range users { users[i].PasswordHash = "" } c.JSON(200, gin.H{"data": users}) } func (a *App) adminCreateUser(c *gin.Context) { var in struct { Username string `json:"username"` Password string `json:"password"` Role string `json:"role"` Credits int64 `json:"credits"` } if c.BindJSON(&in) != nil || strings.TrimSpace(in.Username) == "" || len(in.Password) < 8 { c.JSON(400, gin.H{"error": "username and password(8+) required"}) return } if in.Role == "" { in.Role = RoleUser } if in.Role != RoleUser && in.Role != RolePrivileged && in.Role != RoleAdmin { c.JSON(400, gin.H{"error": "invalid role"}) return } hash, err := hashPassword(in.Password) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } var u User err = a.db.Transaction(func(tx *gorm.DB) error { u = User{Username: strings.TrimSpace(in.Username), PasswordHash: hash, Role: in.Role, Status: UserActive} if err := tx.Create(&u).Error; err != nil { return err } if err := tx.Create(&CreditAccount{UserID: u.ID, Balance: in.Credits}).Error; err != nil { return err } for _, p := range defaultPermissions(in.Role) { if err := tx.Create(&UserPermission{UserID: u.ID, Permission: p, Enabled: true}).Error; err != nil { return err } } if in.Credits > 0 { if err := tx.Create(&CreditTransaction{UserID: u.ID, Type: "grant", Amount: in.Credits, BalanceAfter: in.Credits, ReferenceType: "admin", ReferenceID: strconv.FormatUint(uint64(c.MustGet("user").(*User).ID), 10), Reason: "initial credits"}).Error; err != nil { return err } } return nil }) if err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } u.PasswordHash = "" c.JSON(201, u) } func (a *App) adminAudit(c *gin.Context) { var logs []AuditLog q := a.db.Order("id desc").Limit(200) if action := c.Query("action"); action != "" { q = q.Where("action = ?", action) } if err := q.Find(&logs).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{"data": logs}) } func (a *App) adminUpdateUser(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.Status(400) return } var in struct { Password *string `json:"password"` Role *string `json:"role"` Status *string `json:"status"` } if c.BindJSON(&in) != nil { c.Status(400) return } var u User if a.db.First(&u, id).Error != nil { c.Status(404) return } if in.Password != nil { if len(*in.Password) < 8 { c.JSON(400, gin.H{"error": "password must be 8+"}) return } h, e := hashPassword(*in.Password) if e != nil { c.JSON(500, gin.H{"error": e.Error()}) return } u.PasswordHash = h } if in.Role != nil { if *in.Role != RoleUser && *in.Role != RolePrivileged && *in.Role != RoleAdmin { c.JSON(400, gin.H{"error": "invalid role"}) return } u.Role = *in.Role } if in.Status != nil { if *in.Status != UserActive && *in.Status != UserDisabled { c.JSON(400, gin.H{"error": "invalid status"}) return } u.Status = *in.Status } if err := a.db.Save(&u).Error; err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } u.PasswordHash = "" c.JSON(200, u) }