Add persistent server-side address cache

This commit is contained in:
Unheard0840
2026-08-29 16:39:44 +02:00
parent c66bbfa618
commit 0c78b1ddd5
+199 -21
View File
@@ -22,6 +22,133 @@ const {
const port = 8080 const port = 8080
const fulcrumTimeoutMs = 3000 const fulcrumTimeoutMs = 3000
const fs = require('fs')
const path = require('path')
const addressCacheDir = '/data/cache/addresses'
const addressCacheMaxAgeMs = 60000
const addressRefreshes = new Map()
function ensureAddressCacheDir() {
fs.mkdirSync(addressCacheDir, {
recursive: true,
})
}
function getAddressCachePath(scripthash) {
return path.join(
addressCacheDir,
`${scripthash}.json`,
)
}
function readAddressCache(scripthash) {
try {
const cachePath =
getAddressCachePath(scripthash)
const stat = fs.statSync(cachePath)
const data =
JSON.parse(
fs.readFileSync(
cachePath,
'utf8',
),
)
if (
!data ||
typeof data !== 'object' ||
!data.address
) {
return null
}
return {
data,
ageMs: Math.max(
0,
Date.now() - stat.mtimeMs,
),
}
} catch {
return null
}
}
function writeAddressCache(data) {
try {
ensureAddressCacheDir()
const cachePath =
getAddressCachePath(
data.scripthash,
)
const tempPath =
`${cachePath}.${process.pid}.tmp`
fs.writeFileSync(
tempPath,
JSON.stringify(data),
'utf8',
)
fs.renameSync(
tempPath,
cachePath,
)
} catch (error) {
console.error(
'Failed to write address cache:',
error,
)
}
}
function refreshAddressCache(
address,
scripthash,
) {
const existing =
addressRefreshes.get(scripthash)
if (existing) {
return existing
}
const refresh =
getAddressDataFromFulcrum(
address,
scripthash,
)
.then((data) => {
writeAddressCache(data)
return data
})
.catch((error) => {
console.error(
`Failed to refresh address ${address}:`,
error,
)
throw error
})
.finally(() => {
addressRefreshes.delete(
scripthash,
)
})
addressRefreshes.set(
scripthash,
refresh,
)
return refresh
}
function queryFulcrum(fulcrumUrl, method, params = []) { function queryFulcrum(fulcrumUrl, method, params = []) {
return new Promise((resolve) => { return new Promise((resolve) => {
if (!fulcrumUrl) { if (!fulcrumUrl) {
@@ -247,23 +374,17 @@ async function getStatus() {
} }
} }
async function getAddressData(address) { async function getAddressDataFromFulcrum(
let scripthash address,
scripthash,
try { ) {
scripthash = addressToScripthash(address) const fulcrumUrl =
} catch (error) { process.env.MUNIN_FULCRUM_URL || null
throw new Error(
error instanceof Error
? error.message
: 'Invalid Bitcoin address',
)
}
const fulcrumUrl = process.env.MUNIN_FULCRUM_URL || null
if (!fulcrumUrl) { if (!fulcrumUrl) {
throw new Error('Fulcrum URL is not configured') throw new Error(
'Fulcrum URL is not configured',
)
} }
const balance = await queryFulcrum( const balance = await queryFulcrum(
@@ -309,12 +430,63 @@ async function getAddressData(address) {
Number(balance.result?.confirmed || 0), Number(balance.result?.confirmed || 0),
unconfirmedBalance: unconfirmedBalance:
Number(balance.result?.unconfirmed || 0), Number(balance.result?.unconfirmed || 0),
utxos: Array.isArray(utxos.result) utxos:
? utxos.result Array.isArray(utxos.result)
: [], ? utxos.result
history: Array.isArray(history.result) : [],
? history.result history:
: [], Array.isArray(history.result)
? history.result
: [],
lastSynced:
new Date().toISOString(),
}
}
async function getAddressData(address) {
let scripthash
try {
scripthash =
addressToScripthash(address)
} catch (error) {
throw new Error(
error instanceof Error
? error.message
: 'Invalid Bitcoin address',
)
}
const cached =
readAddressCache(scripthash)
if (cached) {
if (
cached.ageMs >
addressCacheMaxAgeMs
) {
refreshAddressCache(
address,
scripthash,
).catch(() => {})
}
return {
...cached.data,
cacheAgeMs:
cached.ageMs,
}
}
const data =
await refreshAddressCache(
address,
scripthash,
)
return {
...data,
cacheAgeMs: 0,
} }
} }
@@ -333,6 +505,10 @@ async function getAddressesWithData() {
unconfirmedBalance: data.unconfirmedBalance, unconfirmedBalance: data.unconfirmedBalance,
utxos: data.utxos, utxos: data.utxos,
history: data.history, history: data.history,
lastSynced:
data.lastSynced || null,
cacheAgeMs:
Number(data.cacheAgeMs || 0),
error: null, error: null,
}) })
} catch (error) { } catch (error) {
@@ -342,6 +518,8 @@ async function getAddressesWithData() {
unconfirmedBalance: null, unconfirmedBalance: null,
utxos: [], utxos: [],
history: [], history: [],
lastSynced: null,
cacheAgeMs: null,
error: error:
error instanceof Error error instanceof Error
? error.message ? error.message