Index, probe, and announce LNURL mints in the API.
Wire discovery and probing for LNURL mints, add rate-limited POST /api/index for user submissions, and optionally announce confirmed state to relays. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+38
-2
@@ -11,6 +11,7 @@ import {
|
||||
type MintType,
|
||||
type ProbeSample,
|
||||
type RatingDistribution,
|
||||
type LnurlFields,
|
||||
type Stats,
|
||||
} from '@cashumints/shared';
|
||||
import { config, startedAt } from './config.ts';
|
||||
@@ -289,7 +290,8 @@ export async function getStats(): Promise<Stats> {
|
||||
* COUNT(*) FILTER, not SUM(status = 'online'): Postgres has no implicit cast from
|
||||
* boolean to integer, so the SQLite spelling is a type error there.
|
||||
*/
|
||||
const [counts, federations, reviews, fedimintReviews] = await Promise.all([
|
||||
const [counts, federations, lnurl, lnurlFunding, reviews, fedimintReviews, lnurlReviews] =
|
||||
await Promise.all([
|
||||
db.get<{ total: number; online: number; offline: number; degraded: number }>(
|
||||
`SELECT
|
||||
COUNT(*) AS total,
|
||||
@@ -306,6 +308,24 @@ export async function getStats(): Promise<Stats> {
|
||||
COUNT(*) FILTER (WHERE status = 'announced') AS announced
|
||||
FROM mints WHERE type = 'fedimint'`,
|
||||
),
|
||||
db.get<{ total: number; online: number; offline: number }>(
|
||||
`SELECT
|
||||
COUNT(*) AS total,
|
||||
COUNT(*) FILTER (WHERE status = 'online') AS online,
|
||||
COUNT(*) FILTER (WHERE status = 'offline') AS offline
|
||||
FROM mints WHERE type = 'lnurl'`,
|
||||
),
|
||||
/*
|
||||
* The degraded-funding count is read in JavaScript rather than in SQL, because the
|
||||
* flag lives inside `ecosystem_json` and neither a `LIKE '%"funding_available":false%'`
|
||||
* (which depends on how the two drivers happen to serialise) nor a JSON operator
|
||||
* (which SQLite and Postgres spell differently) is portable. There are a handful of
|
||||
* these rows, the whole result is memoised for a minute, and a correct answer on
|
||||
* both backends is worth one small scan.
|
||||
*/
|
||||
db.all<{ ecosystem_json: string | null }>(
|
||||
`SELECT ecosystem_json FROM mints WHERE type = 'lnurl' AND status = 'online'`,
|
||||
),
|
||||
db.get<{ n: number; last: number | null }>(
|
||||
`WITH latest AS (${LATEST_REVIEWS}) SELECT COUNT(*) AS n, MAX(created_at) AS last FROM latest`,
|
||||
),
|
||||
@@ -314,7 +334,18 @@ export async function getStats(): Promise<Stats> {
|
||||
SELECT COUNT(*) AS n FROM latest
|
||||
WHERE mint_url IN (SELECT url FROM mints WHERE type = 'fedimint')`,
|
||||
),
|
||||
]);
|
||||
db.get<{ n: number }>(
|
||||
`WITH latest AS (${LATEST_REVIEWS})
|
||||
SELECT COUNT(*) AS n FROM latest
|
||||
WHERE mint_url IN (SELECT url FROM mints WHERE type = 'lnurl')`,
|
||||
),
|
||||
]);
|
||||
|
||||
let lnurlDegradedFunding = 0;
|
||||
for (const row of lnurlFunding) {
|
||||
const fields = parseEcosystem<LnurlFields>(row);
|
||||
if (fields?.funding_available === false) lnurlDegradedFunding++;
|
||||
}
|
||||
|
||||
const cashuTotal = counts?.total ?? 0;
|
||||
|
||||
@@ -332,6 +363,11 @@ export async function getStats(): Promise<Stats> {
|
||||
fedimint_offline: federations?.offline ?? 0,
|
||||
fedimint_announced: federations?.announced ?? 0,
|
||||
fedimint_reviews: fedimintReviews?.n ?? 0,
|
||||
lnurl_total: lnurl?.total ?? 0,
|
||||
lnurl_online: lnurl?.online ?? 0,
|
||||
lnurl_offline: lnurl?.offline ?? 0,
|
||||
lnurl_degraded_funding: lnurlDegradedFunding,
|
||||
lnurl_reviews: lnurlReviews?.n ?? 0,
|
||||
};
|
||||
|
||||
statsCache = { at: now, value };
|
||||
|
||||
Reference in New Issue
Block a user