A community based topic aggregation platform built on atproto
1package community 2 3import ( 4 "encoding/json" 5 "net/http" 6 "strconv" 7 8 "Coves/internal/core/communities" 9) 10 11// ListHandler handles listing communities 12type ListHandler struct { 13 service communities.Service 14} 15 16// NewListHandler creates a new list handler 17func NewListHandler(service communities.Service) *ListHandler { 18 return &ListHandler{ 19 service: service, 20 } 21} 22 23// HandleList lists communities with filters 24// GET /xrpc/social.coves.community.list?limit={n}&cursor={str}&sort={popular|active|new|alphabetical}&visibility={public|unlisted|private} 25func (h *ListHandler) HandleList(w http.ResponseWriter, r *http.Request) { 26 if r.Method != http.MethodGet { 27 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) 28 return 29 } 30 31 // Parse query parameters 32 query := r.URL.Query() 33 34 // Parse limit (1-100, default 50) 35 limit := 50 36 if limitStr := query.Get("limit"); limitStr != "" { 37 if l, err := strconv.Atoi(limitStr); err == nil { 38 if l < 1 { 39 limit = 1 40 } else if l > 100 { 41 limit = 100 42 } else { 43 limit = l 44 } 45 } 46 } 47 48 // Parse cursor (offset-based for now) 49 offset := 0 50 if cursorStr := query.Get("cursor"); cursorStr != "" { 51 if o, err := strconv.Atoi(cursorStr); err == nil && o >= 0 { 52 offset = o 53 } 54 } 55 56 // Parse sort enum (default: popular) 57 sort := query.Get("sort") 58 if sort == "" { 59 sort = "popular" 60 } 61 62 // Validate sort value 63 validSorts := map[string]bool{ 64 "popular": true, 65 "active": true, 66 "new": true, 67 "alphabetical": true, 68 } 69 if !validSorts[sort] { 70 http.Error(w, "Invalid sort value. Must be: popular, active, new, or alphabetical", http.StatusBadRequest) 71 return 72 } 73 74 // Validate visibility value if provided 75 visibility := query.Get("visibility") 76 if visibility != "" { 77 validVisibilities := map[string]bool{ 78 "public": true, 79 "unlisted": true, 80 "private": true, 81 } 82 if !validVisibilities[visibility] { 83 http.Error(w, "Invalid visibility value. Must be: public, unlisted, or private", http.StatusBadRequest) 84 return 85 } 86 } 87 88 req := communities.ListCommunitiesRequest{ 89 Limit: limit, 90 Offset: offset, 91 Sort: sort, 92 Visibility: visibility, 93 Category: query.Get("category"), 94 Language: query.Get("language"), 95 } 96 97 // Get communities from AppView DB 98 results, err := h.service.ListCommunities(r.Context(), req) 99 if err != nil { 100 handleServiceError(w, err) 101 return 102 } 103 104 // Build response 105 var cursor string 106 if len(results) == limit { 107 // More results available - return next cursor 108 cursor = strconv.Itoa(offset + len(results)) 109 } 110 // If len(results) < limit, we've reached the end - cursor remains empty string 111 112 response := map[string]interface{}{ 113 "communities": results, 114 "cursor": cursor, 115 } 116 117 w.Header().Set("Content-Type", "application/json") 118 w.WriteHeader(http.StatusOK) 119 if err := json.NewEncoder(w).Encode(response); err != nil { 120 // Log encoding errors but don't return error response (headers already sent) 121 // This follows Go's standard practice for HTTP handlers 122 _ = err 123 } 124}