New tab lets you search for any user by name or email, then shows: - Summary cards (cost, tokens, conversations, visits) for the period - All their conversations with titles, models, and cost breakdown - Model usage breakdown with prompt/completion split - CSV export of the full user report Two new API endpoints: /api/search-users and /api/user-detail Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
104 lines
3 KiB
JavaScript
104 lines
3 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const analytics = require('../services/analytics');
|
|
|
|
router.get('/summary', async (req, res) => {
|
|
try {
|
|
const data = await analytics.getSummary(req.db, req.query);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('Summary error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
router.get('/top-users', async (req, res) => {
|
|
try {
|
|
const limit = parseInt(req.query.limit) || 10;
|
|
const data = await analytics.getTopUsers(req.db, req.query, limit);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('Top users error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
router.get('/top-models', async (req, res) => {
|
|
try {
|
|
const limit = parseInt(req.query.limit) || 10;
|
|
const data = await analytics.getTopModels(req.db, req.query, limit);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('Top models error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
router.get('/top-agents', async (req, res) => {
|
|
try {
|
|
const limit = parseInt(req.query.limit) || 10;
|
|
const data = await analytics.getTopAgents(req.db, req.query, limit);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('Top agents error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
router.get('/cost-breakdown', async (req, res) => {
|
|
try {
|
|
const data = await analytics.getCostBreakdown(req.db, req.query);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('Cost breakdown error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
router.get('/usage-over-time', async (req, res) => {
|
|
try {
|
|
const data = await analytics.getUsageOverTime(req.db, req.query);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('Usage over time error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
router.get('/top-conversations', async (req, res) => {
|
|
try {
|
|
const limit = parseInt(req.query.limit) || 20;
|
|
const userId = req.query.userId || null;
|
|
const data = await analytics.getTopConversations(req.db, req.query, limit, userId);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('Top conversations error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
router.get('/search-users', async (req, res) => {
|
|
try {
|
|
const q = req.query.q || '';
|
|
if (q.length < 2) return res.json([]);
|
|
const data = await analytics.searchUsers(req.db, q, 10);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('Search users error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
router.get('/user-detail', async (req, res) => {
|
|
try {
|
|
const userId = req.query.userId;
|
|
if (!userId) return res.status(400).json({ error: 'userId required' });
|
|
const data = await analytics.getUserDetail(req.db, req.query, userId);
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error('User detail error:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|