+31
internal/api/handlers.go
+31
internal/api/handlers.go
···
515
515
resp.json(result)
516
516
}
517
517
518
+
// handleGetDIDByHandle resolves a handle to a DID
519
+
func (s *Server) handleGetDIDByHandle(w http.ResponseWriter, r *http.Request) {
520
+
resp := newResponse(w)
521
+
vars := mux.Vars(r)
522
+
handle := vars["handle"]
523
+
524
+
// Normalize handle (remove @ prefix if present)
525
+
handle = strings.TrimPrefix(handle, "@")
526
+
527
+
// Look up DID by handle
528
+
didRecord, err := s.db.GetDIDByHandle(r.Context(), handle)
529
+
if err != nil {
530
+
if err == sql.ErrNoRows {
531
+
if !s.plcIndexDIDs {
532
+
resp.error("Handle not found. Note: DID indexing is disabled in configuration.", http.StatusNotFound)
533
+
} else {
534
+
resp.error("Handle not found.", http.StatusNotFound)
535
+
}
536
+
} else {
537
+
resp.error(err.Error(), http.StatusInternalServerError)
538
+
}
539
+
return
540
+
}
541
+
542
+
// Return just the handle and DID
543
+
resp.json(map[string]string{
544
+
"handle": handle,
545
+
"did": didRecord.DID,
546
+
})
547
+
}
548
+
518
549
// ===== DID HANDLERS =====
519
550
520
551
func (s *Server) handleGetDID(w http.ResponseWriter, r *http.Request) {
+3
-2
internal/api/server.go
+3
-2
internal/api/server.go
···
75
75
api.HandleFunc("/pds/{endpoint}/repos/stats", s.handleGetPDSRepoStats).Methods("GET")
76
76
api.HandleFunc("/pds/repos/{did}", s.handleGetDIDRepos).Methods("GET")
77
77
78
-
// Global DID route
78
+
// Global DID routes
79
79
api.HandleFunc("/did/{did}", s.handleGetGlobalDID).Methods("GET")
80
+
api.HandleFunc("/handle/{handle}", s.handleGetDIDByHandle).Methods("GET") // NEW
80
81
81
82
// PLC Bundle routes
82
83
api.HandleFunc("/plc/bundles", s.handleGetPLCBundles).Methods("GET")
···
97
98
// DID routes
98
99
api.HandleFunc("/plc/did/{did}", s.handleGetDID).Methods("GET")
99
100
api.HandleFunc("/plc/did/{did}/history", s.handleGetDIDHistory).Methods("GET")
100
-
api.HandleFunc("/plc/dids/stats", s.handleGetDIDStats).Methods("GET") // NEW
101
+
api.HandleFunc("/plc/dids/stats", s.handleGetDIDStats).Methods("GET")
101
102
102
103
// Mempool routes
103
104
api.HandleFunc("/mempool/stats", s.handleGetMempoolStats).Methods("GET")
+2
-1
internal/storage/db.go
+2
-1
internal/storage/db.go
···
75
75
GetPLCMetrics(ctx context.Context, limit int) ([]*PLCMetrics, error)
76
76
GetEndpointStats(ctx context.Context) (*EndpointStats, error)
77
77
78
-
// DID operations - UPDATED SIGNATURES
78
+
// DID operations
79
79
UpsertDID(ctx context.Context, did string, bundleNum int, handle, pds string) error
80
80
UpsertDIDFromMempool(ctx context.Context, did string, handle, pds string) error
81
81
GetDIDRecord(ctx context.Context, did string) (*DIDRecord, error)
82
+
GetDIDByHandle(ctx context.Context, handle string) (*DIDRecord, error) // NEW
82
83
GetGlobalDIDInfo(ctx context.Context, did string) (*GlobalDIDInfo, error)
83
84
AddBundleDIDs(ctx context.Context, bundleNum int, dids []string) error
84
85
GetTotalDIDCount(ctx context.Context) (int64, error)
+48
-2
internal/storage/postgres.go
+48
-2
internal/storage/postgres.go
···
966
966
countryCode := extractString(ipInfo, "location", "country_code")
967
967
asn := extractInt(ipInfo, "asn", "asn")
968
968
asnOrg := extractString(ipInfo, "asn", "org")
969
-
isDatacenter := extractBool(ipInfo, "company", "type", "hosting")
970
-
isVPN := extractBool(ipInfo, "security", "vpn")
969
+
970
+
// Extract top-level boolean flags
971
+
isDatacenter := false
972
+
if val, ok := ipInfo["is_datacenter"].(bool); ok {
973
+
isDatacenter = val
974
+
}
975
+
976
+
isVPN := false
977
+
if val, ok := ipInfo["is_vpn"].(bool); ok {
978
+
isVPN = val
979
+
}
980
+
971
981
lat := extractFloat(ipInfo, "location", "latitude")
972
982
lon := extractFloat(ipInfo, "location", "longitude")
973
983
···
1694
1704
1695
1705
if handle.Valid {
1696
1706
record.Handle = handle.String
1707
+
}
1708
+
if pds.Valid {
1709
+
record.CurrentPDS = pds.String
1710
+
}
1711
+
1712
+
if err := json.Unmarshal(bundleNumbersJSON, &record.BundleNumbers); err != nil {
1713
+
return nil, err
1714
+
}
1715
+
1716
+
return &record, nil
1717
+
}
1718
+
1719
+
func (p *PostgresDB) GetDIDByHandle(ctx context.Context, handle string) (*DIDRecord, error) {
1720
+
query := `
1721
+
SELECT did, handle, pds, bundle_numbers, created_at
1722
+
FROM dids
1723
+
WHERE handle = $1
1724
+
`
1725
+
1726
+
var record DIDRecord
1727
+
var bundleNumbersJSON []byte
1728
+
var recordHandle, pds sql.NullString
1729
+
1730
+
err := p.db.QueryRowContext(ctx, query, handle).Scan(
1731
+
&record.DID,
1732
+
&recordHandle,
1733
+
&pds,
1734
+
&bundleNumbersJSON,
1735
+
&record.CreatedAt,
1736
+
)
1737
+
if err != nil {
1738
+
return nil, err
1739
+
}
1740
+
1741
+
if recordHandle.Valid {
1742
+
record.Handle = recordHandle.String
1697
1743
}
1698
1744
if pds.Valid {
1699
1745
record.CurrentPDS = pds.String