diff options
| author | Fuwn <[email protected]> | 2026-02-26 20:20:09 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-26 20:20:09 -0800 |
| commit | 80dc07bf7bbf3ee9f7191a0446199d74cbb2d341 (patch) | |
| tree | 746f1518723b64a5270131a1e19b6c6f5a312926 /internal/ingest/service.go | |
| parent | chore: remove accidentally committed binary (diff) | |
| download | plutia-test-80dc07bf7bbf3ee9f7191a0446199d74cbb2d341.tar.xz plutia-test-80dc07bf7bbf3ee9f7191a0446199d74cbb2d341.zip | |
fix: align PLC compatibility read endpoints with plc.directory schema
Diffstat (limited to 'internal/ingest/service.go')
| -rw-r--r-- | internal/ingest/service.go | 257 |
1 files changed, 241 insertions, 16 deletions
diff --git a/internal/ingest/service.go b/internal/ingest/service.go index 1ea2a6e..6cb7512 100644 --- a/internal/ingest/service.go +++ b/internal/ingest/service.go @@ -920,24 +920,24 @@ func (s *Service) LoadCurrentPLCData(ctx context.Context, did string) (map[strin return nil, err } - if s.cfg.Mode != config.ModeMirror || s.blockLog == nil { - state, ok, err := s.store.GetState(did) + stateVal, stateOK, err := s.store.GetState(did) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - if !ok { - return nil, ErrDIDNotFound - } + if !stateOK { + return nil, ErrDIDNotFound + } + if s.cfg.Mode != config.ModeMirror || s.blockLog == nil { var doc map[string]any - if err := json.Unmarshal(state.DIDDocument, &doc); err != nil { + if err := json.Unmarshal(stateVal.DIDDocument, &doc); err != nil { return nil, err } - return doc, nil + return normalizePLCDataFromDIDDocument(did, doc, stateVal.RotationKeys), nil } last, err := s.LoadLatestDIDOperation(ctx, did) @@ -952,12 +952,7 @@ func (s *Service) LoadCurrentPLCData(ctx context.Context, did string) (map[strin return nil, fmt.Errorf("decode latest operation: %w", err) } - delete(op, "sig") - delete(op, "signature") - delete(op, "sigPayload") - delete(op, "signaturePayload") - - return op, nil + return normalizePLCDataFromOperation(did, op, stateVal.RotationKeys), nil } func (s *Service) StreamExport(ctx context.Context, after time.Time, limit int, emit func(types.ExportRecord) error) error { @@ -1046,6 +1041,236 @@ func detectNullified(operation []byte) bool { return v } +func normalizePLCDataFromOperation(did string, op map[string]any, fallbackRotation []string) map[string]any { + data := map[string]any{ + "did": did, + "verificationMethods": map[string]string{}, + "rotationKeys": []string{}, + "alsoKnownAs": []string{}, + "services": map[string]map[string]string{}, + } + + if aka := extractStringSlice(op["alsoKnownAs"]); len(aka) > 0 { + data["alsoKnownAs"] = aka + } else if handle, _ := op["handle"].(string); strings.TrimSpace(handle) != "" { + data["alsoKnownAs"] = []string{"at://" + handle} + } + + services := normalizeServices(op) + if len(services) > 0 { + data["services"] = services + } + + verificationMethods := normalizeVerificationMethods(op) + if len(verificationMethods) > 0 { + data["verificationMethods"] = verificationMethods + } + + rotationKeys := extractStringSlice(op["rotationKeys"]) + if len(rotationKeys) == 0 { + if recovery, _ := op["recoveryKey"].(string); strings.TrimSpace(recovery) != "" { + rotationKeys = append(rotationKeys, recovery) + } + } + + if len(rotationKeys) == 0 && len(fallbackRotation) > 0 { + rotationKeys = append(rotationKeys, fallbackRotation...) + } + + data["rotationKeys"] = dedupeStrings(rotationKeys) + + return data +} + +func normalizePLCDataFromDIDDocument(did string, doc map[string]any, fallbackRotation []string) map[string]any { + data := map[string]any{ + "did": did, + "verificationMethods": map[string]string{}, + "rotationKeys": dedupeStrings(fallbackRotation), + "alsoKnownAs": []string{}, + "services": map[string]map[string]string{}, + } + + if aka := extractStringSlice(doc["alsoKnownAs"]); len(aka) > 0 { + data["alsoKnownAs"] = aka + } + + if rawVMList, ok := doc["verificationMethod"].([]any); ok { + verificationMethods := map[string]string{} + + for _, rawVM := range rawVMList { + vm, ok := rawVM.(map[string]any) + if !ok { + continue + } + + name := "atproto" + if id, _ := vm["id"].(string); id != "" { + if idx := strings.LastIndex(id, "#"); idx >= 0 && idx < len(id)-1 { + name = id[idx+1:] + } + } + + if key, _ := vm["publicKeyMultibase"].(string); strings.TrimSpace(key) != "" { + verificationMethods[name] = key + } + } + + if len(verificationMethods) > 0 { + data["verificationMethods"] = verificationMethods + } + } + + if rawServiceList, ok := doc["service"].([]any); ok { + services := map[string]map[string]string{} + + for _, rawService := range rawServiceList { + service, ok := rawService.(map[string]any) + if !ok { + continue + } + + name := "atproto_pds" + if id, _ := service["id"].(string); id != "" { + name = strings.TrimPrefix(id, "#") + if strings.TrimSpace(name) == "" { + name = "atproto_pds" + } + } + + typ, _ := service["type"].(string) + endpoint, _ := service["serviceEndpoint"].(string) + if endpoint == "" { + endpoint, _ = service["endpoint"].(string) + } + + if strings.TrimSpace(endpoint) == "" { + continue + } + + services[name] = map[string]string{ + "type": typ, + "endpoint": endpoint, + } + } + + if len(services) > 0 { + data["services"] = services + } + } + + return data +} + +func normalizeServices(op map[string]any) map[string]map[string]string { + services := map[string]map[string]string{} + + if rawServices, ok := op["services"].(map[string]any); ok { + for name, rawEntry := range rawServices { + entry, ok := rawEntry.(map[string]any) + if !ok { + continue + } + + typ, _ := entry["type"].(string) + endpoint, _ := entry["endpoint"].(string) + if endpoint == "" { + endpoint, _ = entry["serviceEndpoint"].(string) + } + + if strings.TrimSpace(endpoint) == "" { + continue + } + + services[name] = map[string]string{ + "type": typ, + "endpoint": endpoint, + } + } + + return services + } + + if endpoint, _ := op["service"].(string); strings.TrimSpace(endpoint) != "" { + services["atproto_pds"] = map[string]string{ + "type": "AtprotoPersonalDataServer", + "endpoint": endpoint, + } + } + + return services +} + +func normalizeVerificationMethods(op map[string]any) map[string]string { + methods := map[string]string{} + + if rawVM, ok := op["verificationMethods"].(map[string]any); ok { + for name, rawValue := range rawVM { + value, _ := rawValue.(string) + if strings.TrimSpace(value) == "" { + continue + } + + methods[name] = value + } + + if len(methods) > 0 { + return methods + } + } + + if signingKey, _ := op["signingKey"].(string); strings.TrimSpace(signingKey) != "" { + methods["atproto"] = signingKey + } + + return methods +} + +func extractStringSlice(v any) []string { + raw, ok := v.([]any) + if !ok { + return nil + } + + out := make([]string, 0, len(raw)) + + for _, item := range raw { + s, _ := item.(string) + if strings.TrimSpace(s) == "" { + continue + } + + out = append(out, s) + } + + return out +} + +func dedupeStrings(input []string) []string { + if len(input) == 0 { + return []string{} + } + + out := make([]string, 0, len(input)) + seen := make(map[string]struct{}, len(input)) + + for _, item := range input { + item = strings.TrimSpace(item) + if item == "" { + continue + } + + if _, ok := seen[item]; ok { + continue + } + + seen[item] = struct{}{} + out = append(out, item) + } + + return out +} + func (s *Service) Flush(ctx context.Context) error { _ = ctx |