A community based topic aggregation platform built on atproto
1package community 2 3import ( 4 "Coves/internal/api/middleware" 5 "Coves/internal/core/communities" 6 "encoding/json" 7 "log" 8 "net/http" 9 "strings" 10) 11 12// BlockHandler handles community blocking operations 13type BlockHandler struct { 14 service communities.Service 15} 16 17// NewBlockHandler creates a new block handler 18func NewBlockHandler(service communities.Service) *BlockHandler { 19 return &BlockHandler{ 20 service: service, 21 } 22} 23 24// HandleBlock blocks a community 25// POST /xrpc/social.coves.community.block 26// Body: { "community": "did:plc:xxx" or "!gaming@coves.social" } 27func (h *BlockHandler) HandleBlock(w http.ResponseWriter, r *http.Request) { 28 if r.Method != http.MethodPost { 29 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) 30 return 31 } 32 33 // Parse request body 34 var req struct { 35 Community string `json:"community"` // DID or handle 36 } 37 38 if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 39 writeError(w, http.StatusBadRequest, "InvalidRequest", "Invalid request body") 40 return 41 } 42 43 if req.Community == "" { 44 writeError(w, http.StatusBadRequest, "InvalidRequest", "community is required") 45 return 46 } 47 48 // Validate format (DID or handle) 49 if !strings.HasPrefix(req.Community, "did:") && !strings.HasPrefix(req.Community, "!") { 50 writeError(w, http.StatusBadRequest, "InvalidRequest", 51 "community must be a DID (did:plc:...) or handle (!name@instance.com)") 52 return 53 } 54 55 // Extract authenticated user DID and access token from request context (injected by auth middleware) 56 userDID := middleware.GetUserDID(r) 57 if userDID == "" { 58 writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required") 59 return 60 } 61 62 userAccessToken := middleware.GetUserAccessToken(r) 63 if userAccessToken == "" { 64 writeError(w, http.StatusUnauthorized, "AuthRequired", "Missing access token") 65 return 66 } 67 68 // Block via service (write-forward to PDS) 69 block, err := h.service.BlockCommunity(r.Context(), userDID, userAccessToken, req.Community) 70 if err != nil { 71 handleServiceError(w, err) 72 return 73 } 74 75 // Return success response (following atProto conventions for block responses) 76 response := map[string]interface{}{ 77 "block": map[string]interface{}{ 78 "recordUri": block.RecordURI, 79 "recordCid": block.RecordCID, 80 }, 81 } 82 83 w.Header().Set("Content-Type", "application/json") 84 w.WriteHeader(http.StatusOK) 85 if err := json.NewEncoder(w).Encode(response); err != nil { 86 log.Printf("Failed to encode response: %v", err) 87 } 88} 89 90// HandleUnblock unblocks a community 91// POST /xrpc/social.coves.community.unblock 92// Body: { "community": "did:plc:xxx" or "!gaming@coves.social" } 93func (h *BlockHandler) HandleUnblock(w http.ResponseWriter, r *http.Request) { 94 if r.Method != http.MethodPost { 95 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) 96 return 97 } 98 99 // Parse request body 100 var req struct { 101 Community string `json:"community"` 102 } 103 104 if err := json.NewDecoder(r.Body).Decode(&req); err != nil { 105 writeError(w, http.StatusBadRequest, "InvalidRequest", "Invalid request body") 106 return 107 } 108 109 if req.Community == "" { 110 writeError(w, http.StatusBadRequest, "InvalidRequest", "community is required") 111 return 112 } 113 114 // Validate format (DID or handle) 115 if !strings.HasPrefix(req.Community, "did:") && !strings.HasPrefix(req.Community, "!") { 116 writeError(w, http.StatusBadRequest, "InvalidRequest", 117 "community must be a DID (did:plc:...) or handle (!name@instance.com)") 118 return 119 } 120 121 // Extract authenticated user DID and access token from request context (injected by auth middleware) 122 userDID := middleware.GetUserDID(r) 123 if userDID == "" { 124 writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required") 125 return 126 } 127 128 userAccessToken := middleware.GetUserAccessToken(r) 129 if userAccessToken == "" { 130 writeError(w, http.StatusUnauthorized, "AuthRequired", "Missing access token") 131 return 132 } 133 134 // Unblock via service (delete record on PDS) 135 err := h.service.UnblockCommunity(r.Context(), userDID, userAccessToken, req.Community) 136 if err != nil { 137 handleServiceError(w, err) 138 return 139 } 140 141 // Return success response 142 w.Header().Set("Content-Type", "application/json") 143 w.WriteHeader(http.StatusOK) 144 if err := json.NewEncoder(w).Encode(map[string]interface{}{ 145 "success": true, 146 }); err != nil { 147 log.Printf("Failed to encode response: %v", err) 148 } 149}