package handlers import ( "encoding/json" "net/http" "strconv" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/v2/bson" "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" ) // PublicHandler handles unauthenticated public read-only requests type PublicHandler struct { spaceService *services.SpaceService noteService *services.NoteService } // NewPublicHandler creates a new PublicHandler func NewPublicHandler(spaceService *services.SpaceService, noteService *services.NoteService) *PublicHandler { return &PublicHandler{spaceService: spaceService, noteService: noteService} } // GetPublicSpace handles GET /public/spaces/{spaceId} func (h *PublicHandler) GetPublicSpace(w http.ResponseWriter, r *http.Request) { spaceID, err := bson.ObjectIDFromHex(mux.Vars(r)["spaceId"]) if err != nil { http.Error(w, "invalid space id", http.StatusBadRequest) return } space, err := h.spaceService.GetPublicSpace(r.Context(), spaceID) if err != nil { if err.Error() == "space is not public" { http.Error(w, "not found", http.StatusNotFound) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(space) } // ListPublicSpaces handles GET /public/spaces func (h *PublicHandler) ListPublicSpaces(w http.ResponseWriter, r *http.Request) { spaces, err := h.spaceService.GetPublicSpaces(r.Context()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{"spaces": spaces}) } // GetPublicNotes handles GET /public/spaces/{spaceId}/notes func (h *PublicHandler) GetPublicNotes(w http.ResponseWriter, r *http.Request) { spaceID, err := bson.ObjectIDFromHex(mux.Vars(r)["spaceId"]) if err != nil { http.Error(w, "invalid space id", http.StatusBadRequest) return } skip := 0 limit := 50 if v := r.URL.Query().Get("skip"); v != "" { if n, err := strconv.Atoi(v); err == nil && n >= 0 { skip = n } } if v := r.URL.Query().Get("limit"); v != "" { if n, err := strconv.Atoi(v); err == nil && n > 0 && n <= 100 { limit = n } } notes, err := h.noteService.GetPublicNotesBySpace(r.Context(), spaceID, skip, limit) if err != nil { if err.Error() == "space is not public" { http.Error(w, "not found", http.StatusNotFound) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{"notes": notes}) } // GetPublicNote handles GET /public/spaces/{spaceId}/notes/{noteId} func (h *PublicHandler) GetPublicNote(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) spaceID, err := bson.ObjectIDFromHex(vars["spaceId"]) if err != nil { http.Error(w, "invalid space id", http.StatusBadRequest) return } noteID, err := bson.ObjectIDFromHex(vars["noteId"]) if err != nil { http.Error(w, "invalid note id", http.StatusBadRequest) return } note, err := h.noteService.GetPublicNoteBySpaceAndID(r.Context(), spaceID, noteID) if err != nil { if err.Error() == "space is not public" || err.Error() == "note is not public" || err.Error() == "space not found" || err.Error() == "note not found" { http.Error(w, "not found", http.StatusNotFound) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(note) } // UnlockPublicNote verifies a public note password and returns full note content func (h *PublicHandler) UnlockPublicNote(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) spaceID, err := bson.ObjectIDFromHex(vars["spaceId"]) if err != nil { http.Error(w, "invalid space id", http.StatusBadRequest) return } noteID, err := bson.ObjectIDFromHex(vars["noteId"]) if err != nil { http.Error(w, "invalid note id", http.StatusBadRequest) return } var req dto.UnlockNoteRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid request body", http.StatusBadRequest) return } note, err := h.noteService.UnlockPublicNote(r.Context(), spaceID, noteID, req.Password) if err != nil { if err.Error() == "invalid note password" { http.Error(w, err.Error(), http.StatusUnauthorized) return } if err.Error() == "space is not public" || err.Error() == "space not found" || err.Error() == "note not found" { http.Error(w, "not found", http.StatusNotFound) return } http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(note) }