at main 2.6 kB view raw
1package plc 2 3import ( 4 "regexp" 5 "strings" 6) 7 8// MaxHandleLength is the maximum allowed handle length for database storage 9const MaxHandleLength = 500 10 11// Handle validation regex per AT Protocol spec 12// Ensures proper domain format: alphanumeric labels separated by dots, TLD starts with letter 13var handleRegex = 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])?$`) 14 15// ExtractHandle safely extracts the handle from a PLC operation 16func ExtractHandle(op *PLCOperation) string { 17 if op == nil || op.Operation == nil { 18 return "" 19 } 20 21 // Get "alsoKnownAs" 22 aka, ok := op.Operation["alsoKnownAs"].([]interface{}) 23 if !ok { 24 return "" 25 } 26 27 // Find the handle (e.g., "at://handle.bsky.social") 28 for _, item := range aka { 29 if handle, ok := item.(string); ok { 30 if strings.HasPrefix(handle, "at://") { 31 return strings.TrimPrefix(handle, "at://") 32 } 33 } 34 } 35 return "" 36} 37 38// ValidateHandle checks if a handle is valid for database storage 39// Returns empty string if handle is invalid (too long or wrong format) 40func ValidateHandle(handle string) string { 41 if handle == "" { 42 return "" 43 } 44 45 // Check length first (faster) 46 if len(handle) > MaxHandleLength { 47 return "" 48 } 49 50 // Validate format using regex 51 if !handleRegex.MatchString(handle) { 52 return "" 53 } 54 55 return handle 56} 57 58// ExtractPDS safely extracts the PDS endpoint from a PLC operation 59func ExtractPDS(op *PLCOperation) string { 60 if op == nil || op.Operation == nil { 61 return "" 62 } 63 64 // Get "services" 65 services, ok := op.Operation["services"].(map[string]interface{}) 66 if !ok { 67 return "" 68 } 69 70 // Get "atproto_pds" 71 pdsService, ok := services["atproto_pds"].(map[string]interface{}) 72 if !ok { 73 return "" 74 } 75 76 // Get "endpoint" 77 if endpoint, ok := pdsService["endpoint"].(string); ok { 78 return endpoint 79 } 80 81 return "" 82} 83 84// DIDInfo contains extracted metadata from a PLC operation 85type DIDInfo struct { 86 Handle string 87 PDS string 88} 89 90// ExtractDIDInfo extracts both handle and PDS from an operation 91func ExtractDIDInfo(op *PLCOperation) DIDInfo { 92 return DIDInfo{ 93 Handle: ExtractHandle(op), 94 PDS: ExtractPDS(op), 95 } 96} 97 98// ExtractDIDInfoMap creates a map of DID -> info from operations 99// Processes in reverse order to get the latest state for each DID 100func ExtractDIDInfoMap(ops []PLCOperation) map[string]DIDInfo { 101 infoMap := make(map[string]DIDInfo) 102 103 // Process in reverse to get latest state 104 for i := len(ops) - 1; i >= 0; i-- { 105 op := ops[i] 106 if _, exists := infoMap[op.DID]; !exists { 107 infoMap[op.DID] = ExtractDIDInfo(&op) 108 } 109 } 110 111 return infoMap 112}