A community based topic aggregation platform built on atproto
at main 3.7 kB view raw
1package comments 2 3import ( 4 "Coves/internal/api/middleware" 5 "Coves/internal/core/comments" 6 "encoding/json" 7 "log" 8 "net/http" 9) 10 11// CreateCommentHandler handles comment creation requests 12type CreateCommentHandler struct { 13 service comments.Service 14} 15 16// NewCreateCommentHandler creates a new handler for creating comments 17func NewCreateCommentHandler(service comments.Service) *CreateCommentHandler { 18 return &CreateCommentHandler{ 19 service: service, 20 } 21} 22 23// CreateCommentInput matches the lexicon input schema for social.coves.community.comment.create 24type CreateCommentInput struct { 25 Reply struct { 26 Root struct { 27 URI string `json:"uri"` 28 CID string `json:"cid"` 29 } `json:"root"` 30 Parent struct { 31 URI string `json:"uri"` 32 CID string `json:"cid"` 33 } `json:"parent"` 34 } `json:"reply"` 35 Content string `json:"content"` 36 Facets []interface{} `json:"facets,omitempty"` 37 Embed interface{} `json:"embed,omitempty"` 38 Langs []string `json:"langs,omitempty"` 39 Labels interface{} `json:"labels,omitempty"` 40} 41 42// CreateCommentOutput matches the lexicon output schema 43type CreateCommentOutput struct { 44 URI string `json:"uri"` 45 CID string `json:"cid"` 46} 47 48// HandleCreate handles comment creation requests 49// POST /xrpc/social.coves.community.comment.create 50// 51// Request body: { "reply": { "root": {...}, "parent": {...} }, "content": "..." } 52// Response: { "uri": "at://...", "cid": "..." } 53func (h *CreateCommentHandler) HandleCreate(w http.ResponseWriter, r *http.Request) { 54 // 1. Check method is POST 55 if r.Method != http.MethodPost { 56 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) 57 return 58 } 59 60 // 2. Limit request body size to prevent DoS attacks (100KB should be plenty for comments) 61 r.Body = http.MaxBytesReader(w, r.Body, 100*1024) 62 63 // 3. Parse JSON body into CreateCommentInput 64 var input CreateCommentInput 65 if err := json.NewDecoder(r.Body).Decode(&input); err != nil { 66 writeError(w, http.StatusBadRequest, "InvalidRequest", "Invalid request body") 67 return 68 } 69 70 // 4. Get OAuth session from context (injected by auth middleware) 71 session := middleware.GetOAuthSession(r) 72 if session == nil { 73 writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required") 74 return 75 } 76 77 // 5. Convert labels interface{} to *comments.SelfLabels if provided 78 var labels *comments.SelfLabels 79 if input.Labels != nil { 80 labelsJSON, err := json.Marshal(input.Labels) 81 if err != nil { 82 writeError(w, http.StatusBadRequest, "InvalidLabels", "Invalid labels format") 83 return 84 } 85 var selfLabels comments.SelfLabels 86 if err := json.Unmarshal(labelsJSON, &selfLabels); err != nil { 87 writeError(w, http.StatusBadRequest, "InvalidLabels", "Invalid labels structure") 88 return 89 } 90 labels = &selfLabels 91 } 92 93 // 6. Convert input to CreateCommentRequest 94 req := comments.CreateCommentRequest{ 95 Reply: comments.ReplyRef{ 96 Root: comments.StrongRef{ 97 URI: input.Reply.Root.URI, 98 CID: input.Reply.Root.CID, 99 }, 100 Parent: comments.StrongRef{ 101 URI: input.Reply.Parent.URI, 102 CID: input.Reply.Parent.CID, 103 }, 104 }, 105 Content: input.Content, 106 Facets: input.Facets, 107 Embed: input.Embed, 108 Langs: input.Langs, 109 Labels: labels, 110 } 111 112 // 7. Call service to create comment 113 response, err := h.service.CreateComment(r.Context(), session, req) 114 if err != nil { 115 handleServiceError(w, err) 116 return 117 } 118 119 // 8. Return JSON response with URI and CID 120 output := CreateCommentOutput{ 121 URI: response.URI, 122 CID: response.CID, 123 } 124 125 w.Header().Set("Content-Type", "application/json") 126 w.WriteHeader(http.StatusOK) 127 if err := json.NewEncoder(w).Encode(output); err != nil { 128 log.Printf("Failed to encode response: %v", err) 129 } 130}