package notes import ( "encoding/json" "net/http" "net/url" "strings" ) type Server struct { store Store } func NewServer(store Store) http.Handler { server := &Server{store: store} mux := http.NewServeMux() mux.HandleFunc("/healthz", server.handleHealth) mux.HandleFunc("/api/notes", server.handleCollection) mux.HandleFunc("/api/notes/", server.handleItem) return withCORS(mux) } func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { writeError(w, http.StatusMethodNotAllowed, "method not allowed") return } writeJSON(w, http.StatusOK, map[string]string{"status": "ok"}) } func (s *Server) handleCollection(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: writeJSON(w, http.StatusOK, s.store.List()) case http.MethodPost: var note Note if err := json.NewDecoder(r.Body).Decode(¬e); err != nil { writeError(w, http.StatusBadRequest, "invalid note json") return } saved, err := s.store.Upsert(note) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusOK, saved) default: writeError(w, http.StatusMethodNotAllowed, "method not allowed") } } func (s *Server) handleItem(w http.ResponseWriter, r *http.Request) { id, err := url.PathUnescape(strings.TrimPrefix(r.URL.Path, "/api/notes/")) if err != nil || strings.TrimSpace(id) == "" { writeError(w, http.StatusBadRequest, "note id is required") return } switch r.Method { case http.MethodPut: var note Note if err := json.NewDecoder(r.Body).Decode(¬e); err != nil { writeError(w, http.StatusBadRequest, "invalid note json") return } note.ID = id saved, err := s.store.Upsert(note) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusOK, saved) case http.MethodDelete: if err := s.store.Delete(id); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } w.WriteHeader(http.StatusNoContent) default: writeError(w, http.StatusMethodNotAllowed, "method not allowed") } } func withCORS(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Accept") if r.Method == http.MethodOptions { w.WriteHeader(http.StatusNoContent) return } next.ServeHTTP(w, r) }) } func writeJSON(w http.ResponseWriter, status int, value any) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(value) } func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, map[string]string{"error": message}) }