A community based topic aggregation platform built on atproto
1package vote 2 3import ( 4 "Coves/internal/api/middleware" 5 "Coves/internal/core/votes" 6 "encoding/json" 7 "log" 8 "net/http" 9) 10 11// CreateVoteHandler handles vote creation 12type CreateVoteHandler struct { 13 service votes.Service 14} 15 16// NewCreateVoteHandler creates a new create vote handler 17func NewCreateVoteHandler(service votes.Service) *CreateVoteHandler { 18 return &CreateVoteHandler{ 19 service: service, 20 } 21} 22 23// CreateVoteInput represents the request body for creating a vote 24type CreateVoteInput struct { 25 Subject struct { 26 URI string `json:"uri"` 27 CID string `json:"cid"` 28 } `json:"subject"` 29 Direction string `json:"direction"` 30} 31 32// CreateVoteOutput represents the response body for creating a vote 33type CreateVoteOutput struct { 34 URI string `json:"uri"` 35 CID string `json:"cid"` 36} 37 38// HandleCreateVote creates a vote on a post or comment 39// POST /xrpc/social.coves.vote.create 40// 41// Request body: { "subject": { "uri": "at://...", "cid": "..." }, "direction": "up" } 42// Response: { "uri": "at://...", "cid": "..." } 43// 44// Behavior: 45// - If no vote exists: creates new vote with given direction 46// - If vote exists with same direction: deletes vote (toggle off) 47// - If vote exists with different direction: updates to new direction 48func (h *CreateVoteHandler) HandleCreateVote(w http.ResponseWriter, r *http.Request) { 49 if r.Method != http.MethodPost { 50 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) 51 return 52 } 53 54 // Parse request body 55 var input CreateVoteInput 56 if err := json.NewDecoder(r.Body).Decode(&input); err != nil { 57 writeError(w, http.StatusBadRequest, "InvalidRequest", "Invalid request body") 58 return 59 } 60 61 // Validate required fields 62 if input.Subject.URI == "" { 63 writeError(w, http.StatusBadRequest, "InvalidRequest", "subject.uri is required") 64 return 65 } 66 if input.Subject.CID == "" { 67 writeError(w, http.StatusBadRequest, "InvalidRequest", "subject.cid is required") 68 return 69 } 70 if input.Direction == "" { 71 writeError(w, http.StatusBadRequest, "InvalidRequest", "direction is required") 72 return 73 } 74 75 // Validate direction 76 if input.Direction != "up" && input.Direction != "down" { 77 writeError(w, http.StatusBadRequest, "InvalidRequest", "direction must be 'up' or 'down'") 78 return 79 } 80 81 // Get OAuth session from context (injected by auth middleware) 82 session := middleware.GetOAuthSession(r) 83 if session == nil { 84 writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required") 85 return 86 } 87 88 // Create vote request 89 req := votes.CreateVoteRequest{ 90 Subject: votes.StrongRef{ 91 URI: input.Subject.URI, 92 CID: input.Subject.CID, 93 }, 94 Direction: input.Direction, 95 } 96 97 // Call service to create vote 98 response, err := h.service.CreateVote(r.Context(), session, req) 99 if err != nil { 100 handleServiceError(w, err) 101 return 102 } 103 104 // Return success response 105 output := CreateVoteOutput{ 106 URI: response.URI, 107 CID: response.CID, 108 } 109 110 w.Header().Set("Content-Type", "application/json") 111 w.WriteHeader(http.StatusOK) 112 if err := json.NewEncoder(w).Encode(output); err != nil { 113 log.Printf("Failed to encode response: %v", err) 114 } 115}