···
"Coves/internal/atproto/did"
17
-
// Community handle validation regex (!name@instance)
18
-
var communityHandleRegex = regexp.MustCompile(`^?@([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
17
+
// Community handle validation regex (DNS-valid handle: name.communities.instance.com)
18
+
// Matches standard DNS hostname format (RFC 1035)
19
+
var communityHandleRegex = regexp.MustCompile(`^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
type communityService struct {
22
-
didGen *did.Generator
23
-
pdsURL string // PDS URL for write-forward operations
24
-
instanceDID string // DID of this Coves instance
25
-
instanceDomain string // Domain of this instance (for handles)
26
-
pdsAccessToken string // Access token for authenticating to PDS as the instance
27
-
provisioner *PDSAccountProvisioner // V2: Creates PDS accounts for communities
23
+
didGen *did.Generator
24
+
pdsURL string // PDS URL for write-forward operations
25
+
instanceDID string // DID of this Coves instance
26
+
instanceDomain string // Domain of this instance (for handles)
27
+
pdsAccessToken string // Access token for authenticating to PDS as the instance
28
+
provisioner *PDSAccountProvisioner // V2: Creates PDS accounts for communities
// NewCommunityService creates a new community service
···
return nil, fmt.Errorf("failed to provision PDS account for community: %w", err)
82
-
// Build scoped handle for display: !{name}@{instance}
83
-
// Note: The community's atProto handle is pdsAccount.Handle (e.g., gaming.communities.coves.social)
84
-
// The scoped handle (!gaming@coves.social) is for UI/UX - cleaner than the full atProto handle
85
-
scopedHandle := fmt.Sprintf("!%s@%s", req.Name, s.instanceDomain)
87
-
// Validate the scoped handle
88
-
if err := s.ValidateHandle(scopedHandle); err != nil {
89
-
return nil, fmt.Errorf("generated scoped handle is invalid: %w", err)
83
+
// Validate the atProto handle
84
+
if err := s.ValidateHandle(pdsAccount.Handle); err != nil {
85
+
return nil, fmt.Errorf("generated atProto handle is invalid: %w", err)
// Build community profile record
profile := map[string]interface{}{
"$type": "social.coves.community.profile",
95
-
"handle": scopedHandle, // Display handle (!gaming@coves.social)
96
-
"atprotoHandle": pdsAccount.Handle, // Real atProto handle (gaming.communities.coves.social)
91
+
"handle": pdsAccount.Handle, // atProto handle (e.g., gaming.communities.coves.social)
92
+
"name": req.Name, // Short name for !mentions (e.g., "gaming")
"visibility": req.Visibility,
99
-
"hostedBy": s.instanceDID, // V2: Instance hosts, community owns
94
+
"hostedBy": s.instanceDID, // V2: Instance hosts, community owns
"createdBy": req.CreatedByDID,
"createdAt": time.Now().Format(time.RFC3339),
"federation": map[string]interface{}{
···
// Authenticate using community's access token
recordURI, recordCID, err := s.createRecordOnPDSAs(
139
-
pdsAccount.DID, // repo = community's DID (community owns its repo!)
134
+
pdsAccount.DID, // repo = community's DID (community owns its repo!)
"social.coves.community.profile",
141
-
"self", // canonical rkey for profile
136
+
"self", // canonical rkey for profile
143
-
pdsAccount.AccessToken, // authenticate as the community
138
+
pdsAccount.AccessToken, // authenticate as the community
return nil, fmt.Errorf("failed to create community profile record: %w", err)
···
// Build Community object with PDS credentials
151
-
DID: pdsAccount.DID, // Community's DID (owns the repo!)
152
-
Handle: scopedHandle, // !gaming@coves.social
146
+
DID: pdsAccount.DID, // Community's DID (owns the repo!)
147
+
Handle: pdsAccount.Handle, // atProto handle (e.g., gaming.communities.coves.social)
DisplayName: req.DisplayName,
Description: req.Description,
156
-
OwnerDID: pdsAccount.DID, // V2: Community owns itself
151
+
OwnerDID: pdsAccount.DID, // V2: Community owns itself
CreatedByDID: req.CreatedByDID,
HostedByDID: req.HostedByDID,
PDSEmail: pdsAccount.Email,
···
recordURI, recordCID, err := s.putRecordOnPDSAs(
292
-
existing.DID, // repo = community's own DID (V2!)
287
+
existing.DID, // repo = community's own DID (V2!)
"social.coves.community.profile",
294
-
"self", // V2: always "self"
289
+
"self", // V2: always "self"
296
-
existing.PDSAccessToken, // authenticate as the community
291
+
existing.PDSAccessToken, // authenticate as the community
return nil, fmt.Errorf("failed to update community on PDS: %w", err)