Files
at-container-registry/pkg/atproto/resolver.go
T

101 lines
3.1 KiB
Go

package atproto
import (
"context"
"fmt"
"github.com/bluesky-social/indigo/atproto/syntax"
)
// ResolveDIDToPDS resolves a DID to its PDS endpoint.
// Uses the shared identity directory with cache TTL and event-driven invalidation.
func ResolveDIDToPDS(ctx context.Context, did string) (string, error) {
directory := GetDirectory()
didParsed, err := syntax.ParseDID(did)
if err != nil {
return "", fmt.Errorf("invalid DID: %w", err)
}
ident, err := directory.LookupDID(ctx, didParsed)
if err != nil {
return "", fmt.Errorf("failed to resolve DID: %w", err)
}
pdsEndpoint := ident.PDSEndpoint()
if pdsEndpoint == "" {
return "", fmt.Errorf("no PDS endpoint found for DID")
}
return pdsEndpoint, nil
}
// ResolveIdentity resolves an ATProto identifier (handle or DID) to DID, handle, and PDS endpoint.
// Uses the shared identity directory with cache TTL and event-driven invalidation.
//
// If the handle is invalid (handle.invalid), it returns the DID as the handle for display purposes.
// Returns: did, handle, pdsEndpoint, error
func ResolveIdentity(ctx context.Context, identifier string) (string, string, string, error) {
directory := GetDirectory()
atID, err := syntax.ParseAtIdentifier(identifier)
if err != nil {
return "", "", "", fmt.Errorf("invalid identifier %q: %w", identifier, err)
}
ident, err := directory.Lookup(ctx, *atID)
if err != nil {
return "", "", "", fmt.Errorf("failed to resolve identity %q: %w", identifier, err)
}
did := ident.DID.String()
handle := ident.Handle.String()
pdsEndpoint := ident.PDSEndpoint()
// If handle is invalid, use DID as display name
if handle == "handle.invalid" || handle == "" {
handle = did
}
// PDS endpoint is required for XRPC calls
if pdsEndpoint == "" {
return "", "", "", fmt.Errorf("no PDS endpoint found for identifier %q", identifier)
}
return did, handle, pdsEndpoint, nil
}
// ResolveHandleToDID resolves a handle or DID to just the DID.
// Uses the shared identity directory with cache TTL and event-driven invalidation.
// This is useful when you only need the DID and don't care about handle/PDS.
func ResolveHandleToDID(ctx context.Context, identifier string) (string, error) {
directory := GetDirectory()
atID, err := syntax.ParseAtIdentifier(identifier)
if err != nil {
return "", fmt.Errorf("invalid identifier: %w", err)
}
ident, err := directory.Lookup(ctx, *atID)
if err != nil {
return "", err
}
return ident.DID.String(), nil
}
// InvalidateIdentity purges cached identity data for a DID or handle.
// This should be called when identity changes are detected (e.g., via Jetstream events)
// to ensure the cache is refreshed on the next lookup.
//
// Use cases:
// - Handle changes (identity events from Jetstream)
// - Account deactivation/migration (account events from Jetstream)
// - PDS migrations (deactivation followed by reactivation at new PDS)
func InvalidateIdentity(ctx context.Context, identifier string) error {
directory := GetDirectory()
atID, err := syntax.ParseAtIdentifier(identifier)
if err != nil {
return fmt.Errorf("invalid identifier for cache invalidation: %w", err)
}
return directory.Purge(ctx, *atID)
}