You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
50 lines
1.7 KiB
50 lines
1.7 KiB
// backend/metrics.js (ESM) |
|
// Mount with: import registerMetrics from './metrics.js'; registerMetrics(app, { query: sql }); |
|
|
|
export default function registerMetrics(app, db) { |
|
if (!app || !db || typeof db.query !== 'function') { |
|
throw new Error('metrics: app and a {query(sql, [params])} db are required'); |
|
} |
|
|
|
let cache = { at: 0, data: null }; |
|
const CACHE_MS = 5_000; // front-end polls at 10–60 min; 5s is fine here |
|
|
|
app.get('/api/metrics', async (_req, res) => { |
|
try { |
|
const now = Date.now(); |
|
if (cache.data && (now - cache.at) < CACHE_MS) { |
|
res.setHeader('Cache-Control', 'no-store'); |
|
return res.json(cache.data); |
|
} |
|
|
|
// Users = distinct payer addresses who have at least one successful upload (cid not null) |
|
const u = await db.query(` |
|
SELECT COUNT(DISTINCT lower(address))::bigint AS users |
|
FROM public.payments |
|
WHERE address IS NOT NULL AND address <> '' AND cid IS NOT NULL |
|
`); |
|
|
|
// Uploads + total bytes = only rows with a CID |
|
const p = await db.query(` |
|
SELECT |
|
COUNT(*)::bigint AS uploads, |
|
COALESCE(SUM(size_bytes), 0)::bigint AS bytes |
|
FROM public.payments |
|
WHERE cid IS NOT NULL |
|
`); |
|
|
|
const payload = { |
|
users: Number(u.rows?.[0]?.users ?? 0), |
|
uploads: Number(p.rows?.[0]?.uploads?? 0), |
|
bytes: Number(p.rows?.[0]?.bytes ?? 0), |
|
}; |
|
|
|
cache = { at: now, data: payload }; |
|
res.setHeader('Cache-Control', 'no-store'); |
|
res.json(payload); |
|
} catch (err) { |
|
console.error('metrics route error:', err); |
|
res.status(500).json({ ok:false, error:'metrics_failed' }); |
|
} |
|
}); |
|
}
|
|
|