update

Changed files
+84 -5
internal
+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
··· 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
··· 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
··· 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