Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | 1x 1x 1x 1x 1x 1x 1x 1x 19x 19x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 32x 17x 4x 17x 3x 3x 5x 17x 17x 1x 1x 1x 3x 3x 2x 2x 2x 1x 1x 1x 1x | /**
* Admin log query routes — query and trace logs stored in Firestore.
*
* GET /admin/logs → Query logs with filters (admin only)
* GET /admin/logs/trace/:traceId → Get all logs for a session trace (admin only)
*/
const router = require('express').Router();
const { db } = require('../utils/firebase');
const { requireAdmin } = require('../middleware/auth');
const log = require('../utils/log');
const DEFAULT_LIMIT = 50;
const MAX_LIMIT = 200;
const MAX_TRACE_LIMIT = 500;
// GET /admin/logs — Query logs with filters
router.get('/admin/logs', async (req, res) => {
try {
if (await requireAdmin(req, res)) return;
const {
level,
source,
userId,
sessionTraceId,
requestTraceId,
route,
keyword,
startTime,
endTime,
cursor,
} = req.query;
let limit = Number.parseInt(req.query.limit, 10) || DEFAULT_LIMIT;
if (limit < 1) limit = DEFAULT_LIMIT;
if (limit > MAX_LIMIT) limit = MAX_LIMIT;
let query = db.collection('logs').orderBy('timestamp', 'desc');
// Apply Firestore .where() filters
if (level) query = query.where('level', '==', level);
if (source) query = query.where('source', '==', source);
if (userId) query = query.where('userId', '==', userId);
if (sessionTraceId) query = query.where('sessionTraceId', '==', sessionTraceId);
if (requestTraceId) query = query.where('requestTraceId', '==', requestTraceId);
// Time range filters
if (startTime) query = query.where('timestamp', '>=', Number(startTime));
if (endTime) query = query.where('timestamp', '<=', Number(endTime));
// Pagination
if (cursor) query = query.startAfter(Number(cursor));
query = query.limit(limit);
const snapshot = await query.get();
let logs = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
// Client-side filters (can't be compound-queried in Firestore)
if (route) {
logs = logs.filter((entry) => entry.context?.route === route || entry.route === route);
}
if (keyword) {
const lowerKeyword = keyword.toLowerCase();
logs = logs.filter(
(entry) =>
entry.message?.toLowerCase().includes(lowerKeyword) ||
(entry.context && JSON.stringify(entry.context).toLowerCase().includes(lowerKeyword)),
);
}
const nextCursor =
snapshot.docs.length === limit ? snapshot.docs.at(-1).data().timestamp : null;
res.json({ logs, nextCursor });
} catch (err) {
log.error('admin-logs', 'Error querying logs', { error: err.message });
res.status(500).json({ error: 'Internal server error' });
}
});
// GET /admin/logs/trace/:traceId — Get all logs for a session trace
router.get('/admin/logs/trace/:traceId', async (req, res) => {
try {
if (await requireAdmin(req, res)) return;
const { traceId } = req.params;
const snapshot = await db
.collection('logs')
.where('sessionTraceId', '==', traceId)
.orderBy('timestamp', 'asc')
.limit(MAX_TRACE_LIMIT)
.get();
const logs = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
res.json({ logs });
} catch (err) {
log.error('admin-logs', 'Error querying trace logs', {
traceId: req.params.traceId,
error: err.message,
});
res.status(500).json({ error: 'Internal server error' });
}
});
module.exports = router;
|