package handlers import ( "crypto/rand" "encoding/base64" "encoding/json" "fmt" "net/http" "time" "github.com/gin-gonic/gin" "airwallex-admin/models" ) func ListTokens(c *gin.Context) { var tokens []models.ApiToken models.DB.Where("is_active = ?", true).Find(&tokens) type tokenResponse struct { ID uint `json:"id"` Name string `json:"name"` Token string `json:"token"` Permissions string `json:"permissions"` IsActive bool `json:"is_active"` CreatedAt time.Time `json:"created_at"` ExpiresAt *time.Time `json:"expires_at"` LastUsedAt *time.Time `json:"last_used_at"` } var result []tokenResponse for _, t := range tokens { masked := t.Token if len(masked) > 12 { masked = masked[:8] + "..." + masked[len(masked)-4:] } result = append(result, tokenResponse{ ID: t.ID, Name: t.Name, Token: masked, Permissions: t.Permissions, IsActive: t.IsActive, CreatedAt: t.CreatedAt, ExpiresAt: t.ExpiresAt, LastUsedAt: t.LastUsedAt, }) } if result == nil { result = []tokenResponse{} } c.JSON(http.StatusOK, result) } type createTokenRequest struct { Name string `json:"name" binding:"required"` Permissions []string `json:"permissions"` ExpiresInDays *int `json:"expires_in_days"` } func CreateToken(c *gin.Context) { var req createTokenRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": "Invalid request body"}) return } // Generate random token tokenBytes := make([]byte, 32) if _, err := rand.Read(tokenBytes); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"detail": "Failed to generate token"}) return } rawToken := base64.RawURLEncoding.EncodeToString(tokenBytes) // Permissions to JSON string if req.Permissions == nil { req.Permissions = []string{} } permJSON, _ := json.Marshal(req.Permissions) token := models.ApiToken{ Name: req.Name, Token: rawToken, Permissions: string(permJSON), IsActive: true, } if req.ExpiresInDays != nil && *req.ExpiresInDays > 0 { expiresAt := time.Now().Add(time.Duration(*req.ExpiresInDays) * 24 * time.Hour) token.ExpiresAt = &expiresAt } if result := models.DB.Create(&token); result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"detail": "Failed to create token"}) return } username := c.GetString("username") models.DB.Create(&models.AuditLog{ Action: "create_token", ResourceType: "api_token", ResourceID: fmt.Sprintf("%d", token.ID), Operator: username, IPAddress: c.ClientIP(), }) c.JSON(http.StatusOK, gin.H{ "id": token.ID, "name": token.Name, "token": rawToken, "permissions": req.Permissions, "is_active": token.IsActive, "created_at": token.CreatedAt, "expires_at": token.ExpiresAt, }) } func DeleteToken(c *gin.Context) { tokenID := c.Param("id") result := models.DB.Model(&models.ApiToken{}).Where("id = ?", tokenID).Update("is_active", false) if result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"detail": "Failed to revoke token"}) return } if result.RowsAffected == 0 { c.JSON(http.StatusNotFound, gin.H{"detail": "Token not found"}) return } username := c.GetString("username") models.DB.Create(&models.AuditLog{ Action: "delete_token", ResourceType: "api_token", ResourceID: tokenID, Operator: username, IPAddress: c.ClientIP(), }) c.JSON(http.StatusOK, gin.H{"detail": "Token revoked"}) }