import { ECOSYSTEMS } from '@cashumints/shared'; import type { FedimintDetail, Health, LnurlDetail, MintDetail, MintListItem, Stats, } from '@cashumints/shared'; /** * Build-time API base. Islands use PUBLIC_API_BASE (see api-client.ts) so the browser * can be pointed at a different origin than the build machine. */ export const API_URL = import.meta.env['API_URL'] ?? process.env['API_URL'] ?? 'http://127.0.0.1:8787'; async function get(path: string): Promise { const res = await fetch(`${API_URL}${path}`, { headers: { Accept: 'application/json' } }); if (!res.ok) throw new Error(`${path} responded ${res.status}`); return (await res.json()) as T; } /** * Build-time fetches. A failure here fails the build on purpose: shipping a site with * an empty mint list would be worse than not shipping. * * `fetchMints` asks for `type=cashu` rather than for everything, which is what keeps * /mints, the mint pages, the home page grid and the sitemap's mint entries meaning * exactly what they meant before federations were indexed. Each ecosystem is its own * call, its own pages and its own count; nothing has to filter a mixed list and nothing * silently grew when a second or a third was added. */ export const fetchMints = (): Promise => get('/api/mints?type=cashu'); export const fetchFedimints = (): Promise => get('/api/mints?type=fedimint'); export const fetchMint = (host: string): Promise => get(`/api/mints/${encodeURIComponent(host)}`); /** The same endpoint, typed for a federation's flattened fields. */ export const fetchFedimint = (host: string): Promise => get(`/api/mints/${encodeURIComponent(host)}`); export const fetchLnurlMints = (): Promise => get('/api/mints?type=lnurl'); /** The same endpoint again, typed for an LNURL mint's flattened fields. */ export const fetchLnurlMint = (host: string): Promise => get(`/api/mints/${encodeURIComponent(host)}`); export const fetchStats = (): Promise => get('/api/stats'); /** * Every ecosystem's listing, in `ECOSYSTEMS` order, fetched together. * * For the pages that are about *all* of them rather than one — /reviews and the home * page's review strip — where naming each ecosystem's fetch by hand is exactly the kind * of list that gets one entry short. The filter pills on /reviews are already built from * `ECOSYSTEMS`, so before this existed a new ecosystem got a pill that could never match * anything: the pill came from that list and the data did not. This closes that gap, and * a fourth ecosystem now needs no edit on either side. * * One request per ecosystem rather than a single unfiltered `/api/mints`, deliberately: * the per-type responses are what every other page already asks for, so they are warm in * the API's own path, and a caller gets them back already grouped. */ export async function fetchAllListings(): Promise { return Promise.all( ECOSYSTEMS.map((type) => get(`/api/mints?type=${encodeURIComponent(type)}`)), ); } /** Health is allowed to be down at build time: the ticker just renders without it. */ export async function fetchHealth(): Promise { try { return await get('/api/health'); } catch { return null; } } /** * Browser-facing API origin baked into the prerendered markup. Empty means same origin: * icons resolve to /icons on whatever host serves the page, which the dev server proxy * and the nginx config in the README both forward to the API. Never fall back to * API_URL here — that is a build-machine address (127.0.0.1) and a visitor's browser * cannot reach it. Set PUBLIC_API_URL only when the API lives on its own origin. */ export const PUBLIC_API_URL = process.env['PUBLIC_API_URL'] ?? ''; /** URL for a cached mint icon, or null. */ export function iconUrl(icon: string | null): string | null { return icon ? `${PUBLIC_API_URL}${icon}` : null; }