package handlers import ( "encoding/json" "net/http" "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/interfaces/middleware" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/v2/bson" ) // SpaceHandler handles space endpoints type SpaceHandler struct { spaceService *services.SpaceService } // NewSpaceHandler creates a new space handler func NewSpaceHandler(spaceService *services.SpaceService) *SpaceHandler { return &SpaceHandler{ spaceService: spaceService, } } // CreateSpace creates a new space func (h *SpaceHandler) CreateSpace(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } var req dto.CreateSpaceRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } userObjID, _ := bson.ObjectIDFromHex(userID) space, err := h.spaceService.CreateSpace(r.Context(), userObjID, &req) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(space) } // GetUserSpaces retrieves all spaces for the user func (h *SpaceHandler) GetUserSpaces(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } userObjID, _ := bson.ObjectIDFromHex(userID) spaces, err := h.spaceService.GetUserSpaces(r.Context(), userObjID) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(spaces) } // GetSpace retrieves a space func (h *SpaceHandler) GetSpace(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } spaceID := mux.Vars(r)["spaceId"] userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } spaceObjID, _ := bson.ObjectIDFromHex(spaceID) userObjID, _ := bson.ObjectIDFromHex(userID) space, err := h.spaceService.GetSpaceByID(r.Context(), spaceObjID, userObjID) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(space) } // UpdateSpace updates a space func (h *SpaceHandler) UpdateSpace(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPut { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } spaceID := mux.Vars(r)["spaceId"] userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } var req dto.CreateSpaceRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } spaceObjID, _ := bson.ObjectIDFromHex(spaceID) userObjID, _ := bson.ObjectIDFromHex(userID) space, err := h.spaceService.UpdateSpace(r.Context(), spaceObjID, userObjID, &req) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(space) } // DeleteSpace deletes a space func (h *SpaceHandler) DeleteSpace(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } spaceID := mux.Vars(r)["spaceId"] userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } spaceObjID, _ := bson.ObjectIDFromHex(spaceID) userObjID, _ := bson.ObjectIDFromHex(userID) if err := h.spaceService.DeleteSpace(r.Context(), spaceObjID, userObjID); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.WriteHeader(http.StatusNoContent) } // GetSpaceMembers retrieves all members in a space (owner only) func (h *SpaceHandler) GetSpaceMembers(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } spaceID := mux.Vars(r)["spaceId"] userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } spaceObjID, _ := bson.ObjectIDFromHex(spaceID) userObjID, _ := bson.ObjectIDFromHex(userID) members, err := h.spaceService.GetSpaceMembers(r.Context(), spaceObjID, userObjID) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{"members": members}) } // AddMember adds a member to a space (owner/editor) func (h *SpaceHandler) AddMember(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } spaceID := mux.Vars(r)["spaceId"] userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } var req dto.AddSpaceMemberRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } spaceObjID, _ := bson.ObjectIDFromHex(spaceID) userObjID, _ := bson.ObjectIDFromHex(userID) targetUserObjID, err := bson.ObjectIDFromHex(req.UserID) if err != nil { http.Error(w, "Invalid user id", http.StatusBadRequest) return } if err := h.spaceService.AddMember(r.Context(), spaceObjID, userObjID, targetUserObjID); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(map[string]string{"message": "member added"}) } // RemoveMember removes a member from a space (owner/editor) func (h *SpaceHandler) RemoveMember(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } spaceID := mux.Vars(r)["spaceId"] targetUserID := mux.Vars(r)["userId"] userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } spaceObjID, err := bson.ObjectIDFromHex(spaceID) if err != nil { http.Error(w, "Invalid space id", http.StatusBadRequest) return } userObjID, err := bson.ObjectIDFromHex(userID) if err != nil { http.Error(w, "Invalid user id", http.StatusBadRequest) return } targetUserObjID, err := bson.ObjectIDFromHex(targetUserID) if err != nil { http.Error(w, "Invalid target user id", http.StatusBadRequest) return } if err := h.spaceService.RemoveMember(r.Context(), spaceObjID, userObjID, targetUserObjID); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.WriteHeader(http.StatusNoContent) } // GetAvailableUsers returns user options for member selection (owner only) func (h *SpaceHandler) GetAvailableUsers(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } spaceID := mux.Vars(r)["spaceId"] userID, err := middleware.GetUserIDFromContext(r.Context()) if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } spaceObjID, _ := bson.ObjectIDFromHex(spaceID) userObjID, _ := bson.ObjectIDFromHex(userID) users, err := h.spaceService.ListAvailableUsers(r.Context(), spaceObjID, userObjID) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{"users": users}) }