feat(demo): iframe asset-rewrite proxy, AIUI mockArchy, QR 2s, dummy mints

- IndeeHub + Mempool: nginx reverse-proxy + strip X-Frame-Options/CSP + sub_filter
  rewrite of absolute asset paths so the frame-busting SPAs load in the iframe
  (mempool.space remains best-effort — third-party CSP/ws may still limit it).
- AIUI iframe gets ?mockArchy in demo → its built-in mock node data loads.
- Pay-with-mobile QR: invoice settles after ~2s (backend gate keyed by
  payment_hash) and the poll tightened to 1s, so the QR is visible before auto-pay.
- Wallet settings: dummy Cashu mints (4) + Fedimint federations (2, 222,500 sats),
  interactive per session (streaming.list/configure-mints, wallet.fedimint-list/
  join/balance).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-06-22 16:34:12 -04:00
co-authored by Claude Opus 4.8
parent 1b7335f4ac
commit 445f08a5c1
5 changed files with 103 additions and 14 deletions
+46 -4
View File
@@ -149,6 +149,10 @@ const SEED_WALLET = {
}
function randomHex(bytes) { return Array.from({length: bytes}, () => Math.floor(Math.random()*256).toString(16).padStart(2,'0')).join('') }
// Tracks when a content invoice was requested, so the demo can leave the QR on
// screen for a couple of seconds before reporting it paid.
const invoiceRequestedAt = new Map()
const SEED_BTCRELAY = {
settings: {
enabled_for_peers: true,
@@ -216,7 +220,20 @@ function seedUserState() {
}
function seedMockState() {
return { analyticsEnabled: false, nodeVisibility: 'discoverable' }
return {
analyticsEnabled: false,
nodeVisibility: 'discoverable',
cashuMints: [
'https://mint.minibits.cash/Bitcoin',
'https://stablenut.umint.cash',
'https://mint.coinos.io',
'https://8333.space:3338',
],
federations: [
{ federation_id: 'fed1' + randomHex(28), name: 'Archipelago Federation', balance_sats: 180_000 },
{ federation_id: 'fed1' + randomHex(28), name: 'Bitcoin Park Mint', balance_sats: 42_500 },
],
}
}
console.log(`[Auth] Dev mode: ${DEV_MODE}${DEMO ? ' (DEMO multi-session)' : ''}`)
@@ -1981,17 +1998,42 @@ app.post('/rpc/v1', (req, res) => {
}
case 'content.request-invoice': {
const price = params?.price_sats || 500
const payment_hash = randomHex(32)
invoiceRequestedAt.set(payment_hash, Date.now())
return res.json({
result: {
bolt11: 'lntb' + price + '1p' + randomHex(50),
payment_hash: randomHex(32),
payment_hash,
price_sats: price,
},
})
}
case 'content.invoice-status': {
// Settle after the first poll so the QR flow shows a realistic wait.
return res.json({ result: { paid: true } })
// Demo: leave the QR on screen for ~2s before the invoice "settles".
const at = invoiceRequestedAt.get(params?.payment_hash)
const paid = !at || (Date.now() - at) >= 2000
return res.json({ result: { paid } })
}
// ── Wallet settings: Cashu mints + Fedimint federations (interactive) ──
case 'streaming.list-mints': {
return res.json({ result: { mints: mockState.cashuMints } })
}
case 'streaming.configure-mints': {
if (Array.isArray(params?.mints)) mockState.cashuMints = params.mints
return res.json({ result: { success: true, mints: mockState.cashuMints } })
}
case 'wallet.fedimint-list': {
return res.json({ result: { federations: mockState.federations } })
}
case 'wallet.fedimint-join': {
const fed = { federation_id: 'fed1' + randomHex(28), name: 'Joined Federation', balance_sats: 0 }
mockState.federations.push(fed)
return res.json({ result: { success: true, ...fed } })
}
case 'wallet.fedimint-balance': {
const total = (mockState.federations || []).reduce((s, f) => s + (f.balance_sats || 0), 0)
return res.json({ result: { balance_sats: total } })
}
case 'content.request-onchain': {
const price = params?.price_sats || 500