explorer-api.ts 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /**
  2. * Explorer data source: live indexer when up, else mock.
  3. */
  4. import {
  5. MOCK_BLOCKS,
  6. MOCK_TRANSACTIONS,
  7. getBlock as mockGetBlock,
  8. getTransaction as mockGetTx,
  9. } from "@/lib/explorer-mock";
  10. import type { MockBlock, MockTransaction } from "@/lib/explorer-types";
  11. const INDEXER =
  12. process.env.NEXT_PUBLIC_INDEXER_URL?.trim() || "http://127.0.0.1:4091";
  13. export type ExplorerSource = "live" | "mock";
  14. async function tryFetch<T>(path: string): Promise<T | null> {
  15. try {
  16. const res = await fetch(`${INDEXER}${path}`, {
  17. next: { revalidate: 0 },
  18. signal: AbortSignal.timeout(1500),
  19. });
  20. if (!res.ok) return null;
  21. return (await res.json()) as T;
  22. } catch {
  23. return null;
  24. }
  25. }
  26. export async function listTransactions(): Promise<{
  27. source: ExplorerSource;
  28. items: MockTransaction[];
  29. }> {
  30. const data = await tryFetch<{ items: MockTransaction[] }>("/v1/transactions");
  31. if (data?.items?.length) return { source: "live", items: data.items };
  32. return { source: "mock", items: [...MOCK_TRANSACTIONS] };
  33. }
  34. export async function listBlocks(): Promise<{
  35. source: ExplorerSource;
  36. items: MockBlock[];
  37. }> {
  38. const data = await tryFetch<{ items: MockBlock[] }>("/v1/blocks");
  39. if (data?.items?.length) return { source: "live", items: data.items };
  40. return { source: "mock", items: [...MOCK_BLOCKS] };
  41. }
  42. export async function fetchTransaction(
  43. hash: string,
  44. ): Promise<{ source: ExplorerSource; tx: MockTransaction | null }> {
  45. const live = await tryFetch<MockTransaction>(
  46. `/v1/transactions/${encodeURIComponent(hash)}`,
  47. );
  48. if (live && "hash" in live) return { source: "live", tx: live };
  49. return { source: "mock", tx: mockGetTx(hash) ?? null };
  50. }
  51. export async function fetchBlock(
  52. key: string,
  53. ): Promise<{ source: ExplorerSource; block: MockBlock | null }> {
  54. const live = await tryFetch<MockBlock>(
  55. `/v1/blocks/${encodeURIComponent(key)}`,
  56. );
  57. if (live && "height" in live) return { source: "live", block: live };
  58. return { source: "mock", block: mockGetBlock(key) ?? null };
  59. }
  60. export async function indexerHealth(): Promise<boolean> {
  61. const h = await tryFetch<{ ok?: boolean }>("/health");
  62. return Boolean(h?.ok);
  63. }
  64. export type HybridHealth = {
  65. ok?: boolean;
  66. mode?: string;
  67. privateLayer?: { ok?: boolean; chainId?: number; blockNumber?: number };
  68. publicLayer?: { ok?: boolean; chainId?: number; blockNumber?: number };
  69. sim?: { pendingAnchors?: number; postedAnchors?: number };
  70. };
  71. export async function fetchIndexerHybrid(): Promise<HybridHealth | null> {
  72. return tryFetch<HybridHealth>("/v1/hybrid");
  73. }