Expand ecash explorer capabilities
Add Fedimint discovery, dual SQLite/Postgres storage, richer review handling, and generated social imagery.
This commit is contained in:
+81
-4
@@ -1,6 +1,24 @@
|
||||
/** Shapes returned by the API. The web app builds against these. */
|
||||
|
||||
export type MintStatus = 'online' | 'degraded' | 'offline' | 'unknown';
|
||||
/**
|
||||
* Statuses a listed thing can be in.
|
||||
*
|
||||
* `announced` is the Fedimint addition, and it is deliberately not a synonym for
|
||||
* `unknown`. `unknown` means "we have not checked yet"; `announced` means "there is no
|
||||
* check we can run" — the federation exists on Nostr, nothing has confirmed it since,
|
||||
* and nothing here will claim otherwise. A Cashu mint never carries it.
|
||||
*/
|
||||
export type MintStatus = 'online' | 'degraded' | 'offline' | 'unknown' | 'announced';
|
||||
|
||||
/**
|
||||
* Which ecosystem a listing belongs to.
|
||||
*
|
||||
* Deliberately open rather than a closed union: `mints.type` is a stored TEXT column
|
||||
* with no CHECK constraint, so an older build reading a database written by a newer one
|
||||
* has to be able to hold a value it does not have a page for. Compare against
|
||||
* `ANNOUNCEMENT_KINDS` rather than switching exhaustively on this.
|
||||
*/
|
||||
export type MintType = 'cashu' | 'fedimint' | (string & {});
|
||||
|
||||
/** One item of `GET /api/mints`. */
|
||||
export interface MintListItem {
|
||||
@@ -8,6 +26,8 @@ export interface MintListItem {
|
||||
host: string;
|
||||
name: string | null;
|
||||
icon: string | null;
|
||||
/** 'cashu' or 'fedimint'. Rows written before the column existed read as 'cashu'. */
|
||||
type: MintType;
|
||||
status: MintStatus;
|
||||
last_online: number | null;
|
||||
review_count: number;
|
||||
@@ -25,8 +45,13 @@ export interface ProbeSample {
|
||||
|
||||
export type RatingDistribution = Record<'1' | '2' | '3' | '4' | '5', number>;
|
||||
|
||||
/** `GET /api/mints/:host`. */
|
||||
export interface MintDetail extends MintListItem {
|
||||
/**
|
||||
* `GET /api/mints/:host`.
|
||||
*
|
||||
* The Fedimint keys are optional and absent on a Cashu mint, which is what keeps the
|
||||
* Cashu payload unchanged. Read them through `FedimintDetail` after checking `type`.
|
||||
*/
|
||||
export interface MintDetail extends MintListItem, Partial<FedimintFields> {
|
||||
description: string | null;
|
||||
pubkey: string | null;
|
||||
info: MintInfo | null;
|
||||
@@ -41,7 +66,49 @@ export interface MintDetail extends MintListItem {
|
||||
probes_recent: ProbeSample[];
|
||||
}
|
||||
|
||||
/** `GET /api/stats`. */
|
||||
/**
|
||||
* What `ecosystem_json` holds for a Fedimint row, and what `GET /api/mints/:host`
|
||||
* spreads across the detail payload for one.
|
||||
*
|
||||
* Flattened rather than nested so a Cashu payload is byte for byte what it always was:
|
||||
* these keys are simply absent on one. A third ecosystem adds its own interface here
|
||||
* and its own optional keys below; nothing existing has to move.
|
||||
*/
|
||||
export interface FedimintFields {
|
||||
/** The `d` tag of the announcement: 64 hex characters. `host` is derived from it. */
|
||||
federation_id: string;
|
||||
/** Every `u` tag that was a usable invite code, in announcement order. */
|
||||
invite_codes: string[];
|
||||
/** The `modules` tag, split: `["ln", "mint", "wallet", "lnv2", "meta"]`. */
|
||||
modules: string[];
|
||||
/** The `n` tag, normalized — `bitcoin` and `mainnet` both arrive as `mainnet`. */
|
||||
network: string | null;
|
||||
/** `created_at` of the newest announcement seen for this federation. */
|
||||
announced_at: number | null;
|
||||
/** Who published that announcement. The `a` tag of a review points back at them. */
|
||||
announcer_pubkey: string | null;
|
||||
/**
|
||||
* Who confirmed the status, when anything did: `"fedimint.observer"` today.
|
||||
*
|
||||
* null means nothing has, and then `status` is `announced` and never `online`. The
|
||||
* page prints this next to the status, because "someone else says it is up" is a
|
||||
* different claim from "we checked".
|
||||
*/
|
||||
status_source: string | null;
|
||||
}
|
||||
|
||||
/** `GET /api/mints/:host` for a Fedimint federation: the detail plus its own fields. */
|
||||
export type FedimintDetail = MintDetail & FedimintFields;
|
||||
|
||||
/**
|
||||
* `GET /api/stats`.
|
||||
*
|
||||
* The four `mints_*` fields count Cashu mints and only Cashu mints, exactly as they did
|
||||
* before federations were indexed: they are read by the pulse ticker, the /mints
|
||||
* description and the home page, and a number that silently changed meaning would be
|
||||
* worse than a new field. `cashu_total` is the same number under a name that says so,
|
||||
* and every ecosystem added later gets its own `*_total` beside `fedimint_total`.
|
||||
*/
|
||||
export interface Stats {
|
||||
mints_total: number;
|
||||
mints_online: number;
|
||||
@@ -50,6 +117,16 @@ export interface Stats {
|
||||
reviews_total: number;
|
||||
last_review_at: number | null;
|
||||
updated_at: number;
|
||||
/** Same value as `mints_total`, named for the ecosystem it counts. */
|
||||
cashu_total: number;
|
||||
fedimint_total: number;
|
||||
/** Federations a real check confirmed were up. Never inferred from an announcement. */
|
||||
fedimint_online: number;
|
||||
fedimint_offline: number;
|
||||
/** Federations announced on Nostr that no check has ever confirmed. */
|
||||
fedimint_announced: number;
|
||||
/** Reviews of federations (`k` = 38173), included in `reviews_total`. */
|
||||
fedimint_reviews: number;
|
||||
}
|
||||
|
||||
/** `GET /api/health`. */
|
||||
|
||||
Reference in New Issue
Block a user