package main import ( "encoding/json" "net/http" "strings" ) func writeJSON(w http.ResponseWriter, code int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) _ = json.NewEncoder(w).Encode(v) } func jsonError(w http.ResponseWriter, code int, msg string) { writeJSON(w, code, map[string]string{"error": msg}) } // healthz — for k8s probes / load balancer. func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { writeJSON(w, 200, map[string]string{"status": "ok"}) } func (s *Server) handleListPosts(w http.ResponseWriter, r *http.Request) { channel := r.URL.Query().Get("channel") posts, err := s.store.ListPosts(channel, 20) if err != nil { jsonError(w, 500, err.Error()) return } if posts == nil { posts = []Post{} // never return null to clients } writeJSON(w, 200, map[string]any{"posts": posts}) } func (s *Server) handleGetPost(w http.ResponseWriter, r *http.Request) { id := strings.TrimPrefix(r.URL.Path, "/api/posts/") if id == "" || strings.Contains(id, "/") { jsonError(w, 400, "bad id") return } post, err := s.store.GetPost(id) if err != nil { jsonError(w, 404, "not found") return } comments, err := s.store.ListComments(id) if err != nil { jsonError(w, 500, err.Error()) return } if comments == nil { comments = []Comment{} } writeJSON(w, 200, PostWithComments{Post: *post, Comments: comments}) } func (s *Server) handleCreatePost(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { jsonError(w, 405, "POST only") return } author := r.Header.Get("X-Posts-Author") if author == "" { jsonError(w, 401, "X-Posts-Author required (v0: no auth, just header)") return } var req CreatePostRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { jsonError(w, 400, "bad json") return } if strings.TrimSpace(req.Body) == "" { jsonError(w, 400, "body required") return } post, err := s.store.CreatePost(author, req.Title, req.Body, req.Channels) if err != nil { jsonError(w, 500, err.Error()) return } writeJSON(w, 201, post) } func (s *Server) handleCreateComment(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { jsonError(w, 405, "POST only") return } author := r.Header.Get("X-Posts-Author") if author == "" { jsonError(w, 401, "X-Posts-Author required") return } // path: /api/posts/:id/comments parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/") // expected: ["api","posts",":id","comments"] if len(parts) != 4 { jsonError(w, 400, "bad path") return } postID := parts[2] var req CreateCommentRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { jsonError(w, 400, "bad json") return } if strings.TrimSpace(req.Body) == "" { jsonError(w, 400, "body required") return } c, err := s.store.AddComment(postID, req.ParentID, author, req.Body) if err != nil { jsonError(w, 400, err.Error()) return } writeJSON(w, 201, c) }