Add persistent server-side address cache

This commit is contained in:
Unheard0840
2026-08-29 16:39:44 +02:00
parent c66bbfa618
commit 0c78b1ddd5
+195 -17
View File
@@ -22,6 +22,133 @@ const {
const port = 8080
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 = []) {
return new Promise((resolve) => {
if (!fulcrumUrl) {
@@ -247,23 +374,17 @@ async function getStatus() {
}
}
async function getAddressData(address) {
let scripthash
try {
scripthash = addressToScripthash(address)
} catch (error) {
throw new Error(
error instanceof Error
? error.message
: 'Invalid Bitcoin address',
)
}
const fulcrumUrl = process.env.MUNIN_FULCRUM_URL || null
async function getAddressDataFromFulcrum(
address,
scripthash,
) {
const fulcrumUrl =
process.env.MUNIN_FULCRUM_URL || null
if (!fulcrumUrl) {
throw new Error('Fulcrum URL is not configured')
throw new Error(
'Fulcrum URL is not configured',
)
}
const balance = await queryFulcrum(
@@ -309,12 +430,63 @@ async function getAddressData(address) {
Number(balance.result?.confirmed || 0),
unconfirmedBalance:
Number(balance.result?.unconfirmed || 0),
utxos: Array.isArray(utxos.result)
utxos:
Array.isArray(utxos.result)
? utxos.result
: [],
history: Array.isArray(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,
utxos: data.utxos,
history: data.history,
lastSynced:
data.lastSynced || null,
cacheAgeMs:
Number(data.cacheAgeMs || 0),
error: null,
})
} catch (error) {
@@ -342,6 +518,8 @@ async function getAddressesWithData() {
unconfirmedBalance: null,
utxos: [],
history: [],
lastSynced: null,
cacheAgeMs: null,
error:
error instanceof Error
? error.message