package handler import ( "net/http" "sql-platform/internal/model" "github.com/gin-gonic/gin" ) // GET /api/v1/workflows/:id func GetWorkflow(c *gin.Context) { id := c.Param("id") var wf model.WorkflowDefinition if err := model.DB.First(&wf, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"detail": "Workflow not found"}) return } var steps []model.WorkflowStep model.DB.Where("workflow_id = ?", wf.ID).Order("step_number").Find(&steps) c.JSON(http.StatusOK, gin.H{ "id": wf.ID, "name": wf.Name, "description": wf.Description, "category": wf.Category, "is_default": wf.IsDefault, "is_active": wf.IsActive, "created_at": wf.CreatedAt, "updated_at": wf.UpdatedAt, "created_by": wf.CreatedBy, "steps": steps, }) } // POST /api/v1/workflows func CreateWorkflow(c *gin.Context) { var input struct { Name string `json:"name" binding:"required"` Description string `json:"description"` Category string `json:"category"` IsActive *bool `json:"is_active"` IsDefault *bool `json:"is_default"` Steps []struct { StepNumber int `json:"step_number"` Name string `json:"name"` Description string `json:"description"` StepType string `json:"step_type"` ApproverRole string `json:"approver_role"` ApproverIDs string `json:"approver_ids"` IsRequired *bool `json:"is_required"` TimeoutHours *int `json:"timeout_hours"` AutoApproveOnTimeout *bool `json:"auto_approve_on_timeout"` } `json:"steps"` } if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) return } active := true if input.IsActive != nil { active = *input.IsActive } wf := model.WorkflowDefinition{ Name: input.Name, Description: &input.Description, Category: &input.Category, IsActive: &active, IsDefault: input.IsDefault, } if err := model.DB.Create(&wf).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"detail": err.Error()}) return } for _, s := range input.Steps { step := model.WorkflowStep{ WorkflowID: wf.ID, StepNumber: s.StepNumber, Name: s.Name, Description: &s.Description, StepType: s.StepType, ApproverRole: &s.ApproverRole, ApproverIDs: &s.ApproverIDs, IsRequired: s.IsRequired, TimeoutHours: s.TimeoutHours, AutoApproveOnTimeout: s.AutoApproveOnTimeout, } model.DB.Create(&step) } c.JSON(http.StatusCreated, gin.H{"id": wf.ID, "message": "Workflow created"}) } // PUT /api/v1/workflows/:id func UpdateWorkflow(c *gin.Context) { id := c.Param("id") var wf model.WorkflowDefinition if err := model.DB.First(&wf, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"detail": "Workflow not found"}) return } var input map[string]interface{} if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) return } model.DB.Model(&wf).Updates(input) // If steps provided, replace them if stepsRaw, ok := input["steps"]; ok { model.DB.Where("workflow_id = ?", wf.ID).Delete(&model.WorkflowStep{}) if stepsArr, ok := stepsRaw.([]interface{}); ok { for i, sRaw := range stepsArr { if sMap, ok := sRaw.(map[string]interface{}); ok { step := model.WorkflowStep{ WorkflowID: wf.ID, StepNumber: i + 1, } if v, ok := sMap["name"].(string); ok { step.Name = v } if v, ok := sMap["step_type"].(string); ok { step.StepType = v } if v, ok := sMap["approver_role"].(string); ok { step.ApproverRole = &v } if v, ok := sMap["approver_ids"].(string); ok { step.ApproverIDs = &v } if v, ok := sMap["description"].(string); ok { step.Description = &v } model.DB.Create(&step) } } } } c.JSON(http.StatusOK, gin.H{"message": "Workflow updated"}) } // DELETE /api/v1/workflows/:id func DeleteWorkflow(c *gin.Context) { id := c.Param("id") model.DB.Where("workflow_id = ?", id).Delete(&model.WorkflowStep{}) model.DB.Delete(&model.WorkflowDefinition{}, id) c.JSON(http.StatusOK, gin.H{"message": "Workflow deleted"}) } // POST /api/v1/workflows/:id/set-default func SetDefaultWorkflow(c *gin.Context) { id := c.Param("id") // unset all defaults model.DB.Model(&model.WorkflowDefinition{}).Where("is_default = ?", true).Update("is_default", false) // set this one as default defaultVal := true model.DB.Model(&model.WorkflowDefinition{}).Where("id = ?", id).Update("is_default", defaultVal) c.JSON(http.StatusOK, gin.H{"message": "Default workflow set"}) } // POST /api/v1/users/:id/reset-password func ResetPassword(c *gin.Context) { id := c.Param("id") var input struct { NewPassword string `json:"new_password" binding:"required"` } if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) return } _, _ = id, input.NewPassword // TODO: hash password and update c.JSON(http.StatusOK, gin.H{"message": "Password reset (placeholder)"}) } // POST /api/v1/users/batch-import func BatchImportUsers(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Batch import (placeholder)", "count": 0}) } // POST /api/v1/users/batch-import-ldap func BatchImportLdapUsers(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "LDAP import (placeholder)", "count": 0}) } // GET /api/v1/users/import-template func ImportTemplate(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "Template download (placeholder)"}) } // POST /api/v1/users/:userId/reset-password func ResetUserPassword(c *gin.Context) { userId := c.Param("userId") var input struct { NewPassword string `json:"new_password" binding:"required"` } if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()}) return } // TODO: actually hash and save _ = userId c.JSON(http.StatusOK, gin.H{"message": "Password reset (placeholder)"}) }