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}