A community based topic aggregation platform built on atproto

feat(communities): add update handler and XRPC endpoint

Implements social.coves.community.update endpoint for updating
community profiles (displayName, description, visibility, etc.).

Changes:
- Add UpdateHandler with XRPC endpoint support
- Register update route in community routes
- Uses community's own PDS credentials (V2 architecture)
- Preserves OAuth TODO for production deployment

Related: Update endpoint was defined in service layer but had
no HTTP handler or route registration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Changed files
+81 -2
internal
api
handlers
community
routes
+76
internal/api/handlers/community/update.go
···
···
+
package community
+
+
import (
+
"Coves/internal/core/communities"
+
"encoding/json"
+
"net/http"
+
)
+
+
// UpdateHandler handles community updates
+
type UpdateHandler struct {
+
service communities.Service
+
}
+
+
// NewUpdateHandler creates a new update handler
+
func NewUpdateHandler(service communities.Service) *UpdateHandler {
+
return &UpdateHandler{
+
service: service,
+
}
+
}
+
+
// HandleUpdate updates an existing community
+
// POST /xrpc/social.coves.community.update
+
// Body matches UpdateCommunityRequest
+
func (h *UpdateHandler) HandleUpdate(w http.ResponseWriter, r *http.Request) {
+
if r.Method != http.MethodPost {
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+
return
+
}
+
+
// Parse request body
+
var req communities.UpdateCommunityRequest
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+
writeError(w, http.StatusBadRequest, "InvalidRequest", "Invalid request body")
+
return
+
}
+
+
// Validate required fields
+
if req.CommunityDID == "" {
+
writeError(w, http.StatusBadRequest, "InvalidRequest", "communityDid is required")
+
return
+
}
+
+
// TODO(Communities-OAuth): Extract authenticated user DID from request context
+
// This MUST be replaced with OAuth middleware before production deployment
+
// Expected implementation:
+
// userDID := r.Context().Value("authenticated_user_did").(string)
+
// req.UpdatedByDID = userDID
+
// For now, we require client to send it (INSECURE - allows impersonation)
+
if req.UpdatedByDID == "" {
+
writeError(w, http.StatusUnauthorized, "AuthRequired", "Authentication required")
+
return
+
}
+
+
// Update community via service (write-forward to PDS)
+
community, err := h.service.UpdateCommunity(r.Context(), req)
+
if err != nil {
+
handleServiceError(w, err)
+
return
+
}
+
+
// Return success response matching lexicon output
+
response := map[string]interface{}{
+
"uri": community.RecordURI,
+
"cid": community.RecordCID,
+
"did": community.DID,
+
"handle": community.Handle,
+
}
+
+
w.Header().Set("Content-Type", "application/json")
+
w.WriteHeader(http.StatusOK)
+
if err := json.NewEncoder(w).Encode(response); err != nil {
+
// Log encoding errors but don't return error response (headers already sent)
+
// This follows Go's standard practice for HTTP handlers
+
_ = err
+
}
+
}
+5 -2
internal/api/routes/community.go
···
// Initialize handlers
createHandler := community.NewCreateHandler(service)
getHandler := community.NewGetHandler(service)
listHandler := community.NewListHandler(service)
searchHandler := community.NewSearchHandler(service)
subscribeHandler := community.NewSubscribeHandler(service)
···
// social.coves.community.create - create a new community
r.Post("/xrpc/social.coves.community.create", createHandler.HandleCreate)
// social.coves.community.subscribe - subscribe to a community
r.Post("/xrpc/social.coves.community.subscribe", subscribeHandler.HandleSubscribe)
// social.coves.community.unsubscribe - unsubscribe from a community
r.Post("/xrpc/social.coves.community.unsubscribe", subscribeHandler.HandleUnsubscribe)
-
// TODO: Add update and delete handlers when implemented
-
// r.Post("/xrpc/social.coves.community.update", updateHandler.HandleUpdate)
// r.Post("/xrpc/social.coves.community.delete", deleteHandler.HandleDelete)
}
···
// Initialize handlers
createHandler := community.NewCreateHandler(service)
getHandler := community.NewGetHandler(service)
+
updateHandler := community.NewUpdateHandler(service)
listHandler := community.NewListHandler(service)
searchHandler := community.NewSearchHandler(service)
subscribeHandler := community.NewSubscribeHandler(service)
···
// social.coves.community.create - create a new community
r.Post("/xrpc/social.coves.community.create", createHandler.HandleCreate)
+
// social.coves.community.update - update an existing community
+
r.Post("/xrpc/social.coves.community.update", updateHandler.HandleUpdate)
+
// social.coves.community.subscribe - subscribe to a community
r.Post("/xrpc/social.coves.community.subscribe", subscribeHandler.HandleSubscribe)
// social.coves.community.unsubscribe - unsubscribe from a community
r.Post("/xrpc/social.coves.community.unsubscribe", subscribeHandler.HandleUnsubscribe)
+
// TODO: Add delete handler when implemented
// r.Post("/xrpc/social.coves.community.delete", deleteHandler.HandleDelete)
}