91 lines
3.1 KiB
JavaScript
91 lines
3.1 KiB
JavaScript
import { Router } from 'express';
|
|
import { db } from '../db.js';
|
|
|
|
export const statsRouter = Router();
|
|
|
|
// GET /api/stats/overview
|
|
// Bundles everything the "Overview" panel needs in one call, since the
|
|
// frontend renders all three columns (users / activity / general) together.
|
|
statsRouter.get('/overview', (req, res) => {
|
|
const currentUsers = db
|
|
.prepare(`
|
|
SELECT u.id, u.username, s.started_at
|
|
FROM sessions s
|
|
JOIN users u ON u.id = s.user_id
|
|
WHERE s.ended_at IS NULL
|
|
ORDER BY s.started_at DESC
|
|
LIMIT 20
|
|
`)
|
|
.all();
|
|
|
|
const recentActivity = db
|
|
.prepare('SELECT id, description, created_at FROM activity_log ORDER BY created_at DESC LIMIT 20')
|
|
.all();
|
|
|
|
const statRows = db.prepare('SELECT key, value FROM site_stats').all();
|
|
const stats = Object.fromEntries(statRows.map((r) => [r.key, r.value]));
|
|
|
|
const totalUsers = db.prepare('SELECT COUNT(*) AS count FROM users').get().count;
|
|
const mostRecentUser = db
|
|
.prepare('SELECT username, id FROM users ORDER BY joined_at DESC LIMIT 1')
|
|
.get();
|
|
|
|
res.json({
|
|
currentUsers,
|
|
recentActivity,
|
|
general: {
|
|
users: { total: totalUsers, mostRecent: mostRecentUser ?? null },
|
|
reports: {
|
|
named: stats.reports_named ?? '0',
|
|
associated: stats.reports_associated ?? '0',
|
|
logged: stats.reports_logged ?? '0',
|
|
},
|
|
build: {
|
|
pipeline: stats.build_pipeline ?? 'unknown',
|
|
buildNumber: stats.build_number ?? '0',
|
|
version: stats.version ?? '0.0.0',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
// GET /api/stats/users (just the current-users list, if you want it standalone)
|
|
statsRouter.get('/users', (req, res) => {
|
|
const rows = db
|
|
.prepare(`
|
|
SELECT u.id, u.username, s.started_at
|
|
FROM sessions s
|
|
JOIN users u ON u.id = s.user_id
|
|
WHERE s.ended_at IS NULL
|
|
ORDER BY s.started_at DESC
|
|
`)
|
|
.all();
|
|
res.json(rows);
|
|
});
|
|
|
|
// POST /api/stats/session/start { username }
|
|
statsRouter.post('/session/start', (req, res) => {
|
|
const { username } = req.body ?? {};
|
|
if (!username) return res.status(400).json({ error: 'username is required' });
|
|
|
|
let user = db.prepare('SELECT id FROM users WHERE username = ?').get(username);
|
|
if (!user) {
|
|
const result = db.prepare('INSERT INTO users (username) VALUES (?)').run(username);
|
|
user = { id: result.lastInsertRowid };
|
|
}
|
|
db.prepare('UPDATE users SET last_seen_at = strftime(\'%Y-%m-%dT%H:%M:%fZ\', \'now\') WHERE id = ?').run(user.id);
|
|
const session = db.prepare('INSERT INTO sessions (user_id) VALUES (?)').run(user.id);
|
|
res.status(201).json({ sessionId: session.lastInsertRowid, userId: user.id });
|
|
});
|
|
|
|
// POST /api/stats/session/end { sessionId }
|
|
statsRouter.post('/session/end', (req, res) => {
|
|
const { sessionId } = req.body ?? {};
|
|
if (!sessionId) return res.status(400).json({ error: 'sessionId is required' });
|
|
const result = db
|
|
.prepare('UPDATE sessions SET ended_at = strftime(\'%Y-%m-%dT%H:%M:%fZ\', \'now\') WHERE id = ? AND ended_at IS NULL')
|
|
.run(sessionId);
|
|
if (result.changes === 0) return res.status(404).json({ error: 'no active session with that id' });
|
|
res.status(204).end();
|
|
});
|