package handlers import ( "net/http" "strconv" "strings" "github.com/gin-gonic/gin" "github.com/imc-vibe/backend/internal/db" ) type AliasHandler struct { db *db.DB } func NewAliasHandler(database *db.DB) *AliasHandler { return &AliasHandler{db: database} } type CreateAliasRequest struct { Source string `json:"source" binding:"required"` Destination string `json:"destination" binding:"required"` } func (h *AliasHandler) List(c *gin.Context) { domainName := c.Param("name") if domainName == "" { Error(c, http.StatusBadRequest, "domain name required") return } authCtx := GetAuthContext(c) if authCtx == nil { Error(c, http.StatusUnauthorized, "authentication required") return } canAccess, _ := h.db.CanAccessDomain(authCtx.UserID, domainName, authCtx.IsAdmin()) if !canAccess { Error(c, http.StatusForbidden, "access denied") return } domain, err := h.db.GetDomainByName(domainName) if err != nil { Error(c, http.StatusNotFound, "domain not found") return } aliases, err := h.db.GetAliasesByDomain(domain.ID) if err != nil { Error(c, http.StatusInternalServerError, "database error") return } Success(c, aliases) } func (h *AliasHandler) Create(c *gin.Context) { domainName := c.Param("name") if domainName == "" { Error(c, http.StatusBadRequest, "domain name required") return } authCtx := GetAuthContext(c) if authCtx == nil { Error(c, http.StatusUnauthorized, "authentication required") return } canAccess, _ := h.db.CanAccessDomain(authCtx.UserID, domainName, authCtx.IsAdmin()) if !canAccess { Error(c, http.StatusForbidden, "access denied") return } domain, err := h.db.GetDomainByName(domainName) if err != nil { Error(c, http.StatusNotFound, "domain not found") return } var req CreateAliasRequest if err := c.ShouldBindJSON(&req); err != nil { Error(c, http.StatusBadRequest, "invalid request body") return } // Validate source email format if err := validateEmailLocalPart(req.Source); err != nil { Error(c, http.StatusBadRequest, "source "+err.Error()) return } // Ensure source is in the correct domain if !strings.HasSuffix(req.Source, "@"+domainName) { Error(c, http.StatusBadRequest, "source must be in domain "+domainName) return } // Validate destination - can be single email or comma-separated list destinations := strings.Split(req.Destination, ",") for _, dest := range destinations { dest = strings.TrimSpace(dest) if dest == "" { Error(c, http.StatusBadRequest, "destination cannot be empty") return } // Destination doesn't need @domain validation since it can be external if strings.Contains(dest, "@") { if err := validateEmailLocalPart(dest); err != nil { Error(c, http.StatusBadRequest, "destination "+err.Error()) return } } } // Check for duplicate alias existing, _ := h.db.GetAliasBySource(req.Source) if existing != nil { Error(c, http.StatusConflict, "alias already exists") return } alias, err := h.db.CreateAliasInDomain(req.Source, req.Destination, domain.ID) if err != nil { Error(c, http.StatusInternalServerError, "failed to create alias") return } Created(c, alias) } func (h *AliasHandler) Delete(c *gin.Context) { domainName := c.Param("name") idStr := c.Param("id") if domainName == "" || idStr == "" { Error(c, http.StatusBadRequest, "domain name and alias id required") return } authCtx := GetAuthContext(c) if authCtx == nil { Error(c, http.StatusUnauthorized, "authentication required") return } canAccess, _ := h.db.CanAccessDomain(authCtx.UserID, domainName, authCtx.IsAdmin()) if !canAccess { Error(c, http.StatusForbidden, "access denied") return } id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { Error(c, http.StatusBadRequest, "invalid alias id") return } if err := h.db.DeleteAlias(uint(id)); err != nil { Error(c, http.StatusInternalServerError, "failed to delete alias") return } NoContent(c) }