A community based topic aggregation platform built on atproto

fix(blocking): address PR review feedback - improve error handling

- Fix P1 issue: properly bubble up database errors instead of masking as conflict
* Only return ErrBlockAlreadyExists when getErr is ErrBlockNotFound (race condition)
* Real DB errors (outages, connection failures) now propagate to operators
- Remove unused V1 functions flagged by linter:
* createRecordOnPDS, deleteRecordOnPDS, callPDS (replaced by *As versions)
- Apply automatic code formatting via golangci-lint --fix:
* Align struct field tags in CommunityBlock
* Fix comment alignment across test files
* Remove trailing whitespace
- All tests passing, linter clean

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

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

Changed files
+37 -67
internal
api
handlers
community
core
communities
db
tests
+1 -1
internal/api/handlers/community/subscribe.go
···
// Parse request body
var req struct {
-
Community string `json:"community"` // DID only (per lexicon)
+
Community string `json:"community"` // DID only (per lexicon)
ContentVisibility int `json:"contentVisibility"` // Optional: 1-5 scale, defaults to 3
}
+2 -2
internal/core/communities/community.go
···
// CommunityBlock represents a user blocking a community
// Block records live in the user's repository (at://user_did/social.coves.community.block/{rkey})
type CommunityBlock struct {
-
ID int `json:"id" db:"id"`
+
BlockedAt time.Time `json:"blockedAt" db:"blocked_at"`
UserDID string `json:"userDid" db:"user_did"`
CommunityDID string `json:"communityDid" db:"community_did"`
-
BlockedAt time.Time `json:"blockedAt" db:"blocked_at"`
RecordURI string `json:"recordUri,omitempty" db:"record_uri"`
RecordCID string `json:"recordCid,omitempty" db:"record_cid"`
+
ID int `json:"id" db:"id"`
}
// Membership represents active participation with reputation tracking
+12 -40
internal/core/communities/service.go
···
"bytes"
"context"
"encoding/json"
+
"errors"
"fmt"
"io"
"log"
···
// Block exists in our index - return it
return existingBlock, nil
}
-
// Race condition: PDS has the block but Jetstream hasn't indexed it yet
-
// Return typed conflict error so handler can return 409 instead of 500
-
// This is normal in eventually-consistent systems
-
return nil, ErrBlockAlreadyExists
+
// Only treat as "already exists" if the error is ErrBlockNotFound (race condition)
+
// Any other error (DB outage, connection failure, etc.) should bubble up
+
if errors.Is(getErr, ErrBlockNotFound) {
+
// Race condition: PDS has the block but Jetstream hasn't indexed it yet
+
// Return typed conflict error so handler can return 409 instead of 500
+
// This is normal in eventually-consistent systems
+
return nil, ErrBlockAlreadyExists
+
}
+
// Real datastore error - bubble it up so operators see the failure
+
return nil, fmt.Errorf("PDS reported duplicate block but failed to fetch from index: %w", getErr)
}
return nil, fmt.Errorf("failed to create block on PDS: %w", err)
}
···
// PDS write-forward helpers
-
func (s *communityService) createRecordOnPDS(ctx context.Context, repoDID, collection, rkey string, record map[string]interface{}) (string, string, error) {
-
endpoint := fmt.Sprintf("%s/xrpc/com.atproto.repo.createRecord", strings.TrimSuffix(s.pdsURL, "/"))
-
-
payload := map[string]interface{}{
-
"repo": repoDID,
-
"collection": collection,
-
"record": record,
-
}
-
-
if rkey != "" {
-
payload["rkey"] = rkey
-
}
-
-
return s.callPDS(ctx, "POST", endpoint, payload)
-
}
-
// createRecordOnPDSAs creates a record with a specific access token (for V2 community auth)
func (s *communityService) createRecordOnPDSAs(ctx context.Context, repoDID, collection, rkey string, record map[string]interface{}, accessToken string) (string, string, error) {
endpoint := fmt.Sprintf("%s/xrpc/com.atproto.repo.createRecord", strings.TrimSuffix(s.pdsURL, "/"))
···
return s.callPDSWithAuth(ctx, "POST", endpoint, payload, accessToken)
}
-
func (s *communityService) deleteRecordOnPDS(ctx context.Context, repoDID, collection, rkey string) error {
-
endpoint := fmt.Sprintf("%s/xrpc/com.atproto.repo.deleteRecord", strings.TrimSuffix(s.pdsURL, "/"))
-
-
payload := map[string]interface{}{
-
"repo": repoDID,
-
"collection": collection,
-
"rkey": rkey,
-
}
-
-
_, _, err := s.callPDS(ctx, "POST", endpoint, payload)
-
return err
-
}
-
// deleteRecordOnPDSAs deletes a record with a specific access token (for user-scoped deletions)
-
func (s *communityService) deleteRecordOnPDSAs(ctx context.Context, repoDID, collection, rkey string, accessToken string) error {
+
func (s *communityService) deleteRecordOnPDSAs(ctx context.Context, repoDID, collection, rkey, accessToken string) error {
endpoint := fmt.Sprintf("%s/xrpc/com.atproto.repo.deleteRecord", strings.TrimSuffix(s.pdsURL, "/"))
payload := map[string]interface{}{
···
_, _, err := s.callPDSWithAuth(ctx, "POST", endpoint, payload, accessToken)
return err
-
}
-
-
func (s *communityService) callPDS(ctx context.Context, method, endpoint string, payload map[string]interface{}) (string, string, error) {
-
// Use instance's access token
-
return s.callPDSWithAuth(ctx, method, endpoint, payload, s.pdsAccessToken)
}
// callPDSWithAuth makes a PDS call with a specific access token (V2: for community authentication)
···
}
// Helper functions
-
-1
internal/db/postgres/community_repo_blocks.go
···
return exists, nil
}
-
+6 -6
tests/integration/community_blocking_test.go
···
func createBlockingTestCommunity(t *testing.T, repo communities.Repository, name, did string) *communities.Community {
community := &communities.Community{
-
DID: did,
-
Handle: fmt.Sprintf("!%s@coves.test", name),
-
Name: name,
-
DisplayName: fmt.Sprintf("Test Community %s", name),
-
Description: "Test community for blocking tests",
-
OwnerDID: did,
+
DID: did,
+
Handle: fmt.Sprintf("!%s@coves.test", name),
+
Name: name,
+
DisplayName: fmt.Sprintf("Test Community %s", name),
+
Description: "Test community for blocking tests",
+
OwnerDID: did,
CreatedByDID: "did:plc:test-creator",
HostedByDID: "did:plc:test-instance",
Visibility: "public",
+4 -5
tests/integration/community_e2e_test.go
···
CID: subscribeResp.CID,
Record: map[string]interface{}{
"$type": "social.coves.community.subscription",
-
"subject": community.DID,
+
"subject": community.DID,
"contentVisibility": float64(5), // JSON numbers are float64
"createdAt": time.Now().Format(time.RFC3339),
},
···
CID: subscription.RecordCID,
Record: map[string]interface{}{
"$type": "social.coves.community.subscription",
-
"subject": community.DID,
+
"subject": community.DID,
"contentVisibility": float64(3),
"createdAt": time.Now().Format(time.RFC3339),
},
···
Operation: "delete",
Collection: "social.coves.community.subscription",
RKey: rkey,
-
CID: "", // No CID on deletes
-
Record: nil, // No record data on deletes
+
CID: "", // No CID on deletes
+
Record: nil, // No record data on deletes
},
}
if handleErr := consumer.HandleEvent(context.Background(), &deleteEvent); handleErr != nil {
···
return community
-
// authenticateWithPDS authenticates with the PDS and returns access token and DID
func authenticateWithPDS(pdsURL, handle, password string) (string, string, error) {
+12 -12
tests/integration/subscription_indexing_test.go
···
RKey: rkey,
CID: "bafytest123",
Record: map[string]interface{}{
-
"$type": "social.coves.community.subscription",
-
"subject": community.DID,
+
"$type": "social.coves.community.subscription",
+
"subject": community.DID,
"createdAt": time.Now().Format(time.RFC3339),
"contentVisibility": float64(5), // JSON numbers decode as float64
},
···
RKey: rkey,
CID: "bafydefault",
Record: map[string]interface{}{
-
"$type": "social.coves.community.subscription",
-
"subject": community.DID,
+
"$type": "social.coves.community.subscription",
+
"subject": community.DID,
"createdAt": time.Now().Format(time.RFC3339),
// contentVisibility NOT provided
},
···
t.Run("clamps contentVisibility to valid range (1-5)", func(t *testing.T) {
testCases := []struct {
+
name string
input float64
expected int
-
name string
}{
{input: 0, expected: 1, name: "zero clamped to 1"},
{input: -5, expected: 1, name: "negative clamped to 1"},
···
RKey: rkey,
CID: "bafyidempotent",
Record: map[string]interface{}{
-
"$type": "social.coves.community.subscription",
-
"subject": community.DID,
+
"$type": "social.coves.community.subscription",
+
"subject": community.DID,
"createdAt": time.Now().Format(time.RFC3339),
"contentVisibility": float64(4),
},
···
RKey: rkey,
CID: "bafycreate",
Record: map[string]interface{}{
-
"$type": "social.coves.community.subscription",
-
"subject": community.DID,
+
"$type": "social.coves.community.subscription",
+
"subject": community.DID,
"createdAt": time.Now().Format(time.RFC3339),
"contentVisibility": float64(3),
},
···
Operation: "delete",
Collection: "social.coves.community.subscription",
RKey: rkey,
-
CID: "", // No CID on deletes
+
CID: "", // No CID on deletes
Record: nil, // No record data on deletes
},
}
···
RKey: rkey,
CID: "bafycount",
Record: map[string]interface{}{
-
"$type": "social.coves.community.subscription",
-
"subject": community.DID,
+
"$type": "social.coves.community.subscription",
+
"subject": community.DID,
"createdAt": time.Now().Format(time.RFC3339),
"contentVisibility": float64(3),
},