package handlers import ( "encoding/json" "net/http" "strconv" "strings" "github.com/imc-vibe/backend/internal/auth" "github.com/imc-vibe/backend/internal/db" ) type UserHandler struct { db *db.DB } func NewUserHandler(database *db.DB) *UserHandler { return &UserHandler{db: database} } type CreateUserRequest struct { Email string `json:"email"` Password string `json:"password"` Quota int64 `json:"quota"` } type UpdateUserRequest struct { Password string `json:"password,omitempty"` Quota int64 `json:"quota,omitempty"` } func (h *UserHandler) List(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { Error(w, http.StatusMethodNotAllowed, "method not allowed") return } domainName := extractDomainNameFromPath(r.URL.Path) if domainName == "" { Error(w, http.StatusBadRequest, "domain name required") return } authCtx := GetAuthContext(r) if authCtx == nil { Error(w, http.StatusUnauthorized, "authentication required") return } canAccess, _ := h.db.CanAccessDomain(authCtx.UserID, domainName, authCtx.IsAdmin()) if !canAccess { Error(w, http.StatusForbidden, "access denied") return } domain, err := h.db.GetDomainByName(domainName) if err != nil { Error(w, http.StatusNotFound, "domain not found") return } users, err := h.db.GetUsersByDomain(domain.ID) if err != nil { Error(w, http.StatusInternalServerError, "database error") return } Success(w, users) } func (h *UserHandler) Get(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { Error(w, http.StatusMethodNotAllowed, "method not allowed") return } parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/api/domains/"), "/") if len(parts) < 4 { Error(w, http.StatusBadRequest, "invalid path") return } domainName := parts[0] authCtx := GetAuthContext(r) if authCtx == nil { Error(w, http.StatusUnauthorized, "authentication required") return } canAccess, _ := h.db.CanAccessDomain(authCtx.UserID, domainName, authCtx.IsAdmin()) if !canAccess { Error(w, http.StatusForbidden, "access denied") return } idStr := parts[3] id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { Error(w, http.StatusBadRequest, "invalid user id") return } user, err := h.db.GetUserByID(uint(id)) if err != nil { Error(w, http.StatusNotFound, "user not found") return } Success(w, user) } func (h *UserHandler) Create(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { Error(w, http.StatusMethodNotAllowed, "method not allowed") return } domainName := extractDomainNameFromPath(r.URL.Path) if domainName == "" { Error(w, http.StatusBadRequest, "domain name required") return } authCtx := GetAuthContext(r) if authCtx == nil { Error(w, http.StatusUnauthorized, "authentication required") return } canAccess, _ := h.db.CanAccessDomain(authCtx.UserID, domainName, authCtx.IsAdmin()) if !canAccess { Error(w, http.StatusForbidden, "access denied") return } domain, err := h.db.GetDomainByName(domainName) if err != nil { Error(w, http.StatusNotFound, "domain not found") return } var req CreateUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { Error(w, http.StatusBadRequest, "invalid request body") return } if req.Email == "" || req.Password == "" { Error(w, http.StatusBadRequest, "email and password required") return } existing, _ := h.db.GetUserByEmail(req.Email) if existing != nil { Error(w, http.StatusConflict, "user already exists") return } passwordHash := "{BLF-CRYPT}" + req.Password user, err := h.db.CreateUserInDomain(req.Email, passwordHash, req.Quota, domain.ID) if err != nil { Error(w, http.StatusInternalServerError, "failed to create user") return } Created(w, user) } func (h *UserHandler) Update(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPut { Error(w, http.StatusMethodNotAllowed, "method not allowed") return } parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/api/domains/"), "/") if len(parts) < 4 { Error(w, http.StatusBadRequest, "invalid path") return } domainName := parts[0] authCtx := GetAuthContext(r) if authCtx == nil { Error(w, http.StatusUnauthorized, "authentication required") return } canAccess, _ := h.db.CanAccessDomain(authCtx.UserID, domainName, authCtx.IsAdmin()) if !canAccess { Error(w, http.StatusForbidden, "access denied") return } idStr := parts[3] id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { Error(w, http.StatusBadRequest, "invalid user id") return } user, err := h.db.GetUserByID(uint(id)) if err != nil { Error(w, http.StatusNotFound, "user not found") return } var req UpdateUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { Error(w, http.StatusBadRequest, "invalid request body") return } if req.Password != "" { passwordHash := "{BLF-CRYPT}" + req.Password if err := h.db.UpdateUserPassword(user.ID, passwordHash); err != nil { Error(w, http.StatusInternalServerError, "failed to update password") return } } if req.Quota >= 0 { if err := h.db.UpdateUserQuota(user.ID, req.Quota); err != nil { Error(w, http.StatusInternalServerError, "failed to update quota") return } } Success(w, map[string]string{"message": "user updated"}) } func (h *UserHandler) Delete(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { Error(w, http.StatusMethodNotAllowed, "method not allowed") return } parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/api/domains/"), "/") if len(parts) < 4 { Error(w, http.StatusBadRequest, "invalid path") return } domainName := parts[0] authCtx := GetAuthContext(r) if authCtx == nil { Error(w, http.StatusUnauthorized, "authentication required") return } canAccess, _ := h.db.CanAccessDomain(authCtx.UserID, domainName, authCtx.IsAdmin()) if !canAccess { Error(w, http.StatusForbidden, "access denied") return } idStr := parts[3] id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { Error(w, http.StatusBadRequest, "invalid user id") return } if err := h.db.DeleteUser(uint(id)); err != nil { Error(w, http.StatusInternalServerError, "failed to delete user") return } NoContent(w) } func (h *UserHandler) ListAll(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { Error(w, http.StatusMethodNotAllowed, "method not allowed") return } authCtx := GetAuthContext(r) if authCtx == nil || !authCtx.IsAdmin() { Error(w, http.StatusForbidden, "admin access required") return } users, err := h.db.GetAllMailUsers() if err != nil { Error(w, http.StatusInternalServerError, "database error") return } Success(w, users) } func extractDomainNameFromPath(path string) string { parts := strings.Split(strings.TrimPrefix(path, "/api/"), "/") if len(parts) >= 2 && parts[0] == "domains" && parts[1] != "" { if idx := strings.Index(parts[1], "/"); idx > 0 { return parts[1][:idx] } return parts[1] } return "" } var _ = auth.RoleAdmin