aboutsummaryrefslogtreecommitdiff
path: root/internal/ingest/service.go
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-26 20:20:09 -0800
committerFuwn <[email protected]>2026-02-26 20:20:09 -0800
commit80dc07bf7bbf3ee9f7191a0446199d74cbb2d341 (patch)
tree746f1518723b64a5270131a1e19b6c6f5a312926 /internal/ingest/service.go
parentchore: remove accidentally committed binary (diff)
downloadplutia-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.go257
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