feat(api): admin-only cross-user work-session views (/api/admin/sessions)

This commit is contained in:
Bas van Rossem
2026-06-17 17:47:17 +02:00
parent f2cc0973c7
commit dc8f550665
3 changed files with 108 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import { health } from './routes/health';
import { me } from './routes/me';
import { activitiesRoutes } from './routes/activities';
import { sessionsRoutes } from './routes/sessions';
import { adminRoutes } from './routes/admin';
import { auth } from './auth';
import { env } from './env';
@@ -24,5 +25,6 @@ export function createApp(): Hono {
app.route('/', me);
app.route('/', activitiesRoutes);
app.route('/', sessionsRoutes);
app.route('/', adminRoutes);
return app;
}

View File

@@ -0,0 +1,60 @@
import { Hono } from 'hono';
import { desc, eq } from 'drizzle-orm';
import { db } from '../db/client';
import { activities, user, workSessions } from '../db/schema';
import { getSessionUser, isAdmin } from '../lib/require-user';
import { toWorkSession } from '../lib/work-session';
export const adminRoutes = new Hono();
// Gate the whole /api/admin/* surface to admins.
adminRoutes.use('/api/admin/*', async (c, next) => {
const sessionUser = await getSessionUser(c);
if (!sessionUser) return c.json({ error: 'Unauthorized' }, 401);
if (!isAdmin(sessionUser)) return c.json({ error: 'Forbidden' }, 403);
await next();
});
const baseSelect = {
session: workSessions,
activityName: activities.name,
userName: user.name,
userEmail: user.email,
};
adminRoutes.get('/api/admin/sessions', async (c) => {
const rows = await db
.select(baseSelect)
.from(workSessions)
.leftJoin(activities, eq(workSessions.activityId, activities.id))
.leftJoin(user, eq(workSessions.userId, user.id))
.orderBy(desc(workSessions.startTime));
return c.json(
rows.map((r) =>
toWorkSession(r.session, {
activityName: r.activityName,
userName: r.userName,
userEmail: r.userEmail,
})
)
);
});
adminRoutes.get('/api/admin/sessions/active', async (c) => {
const rows = await db
.select(baseSelect)
.from(workSessions)
.leftJoin(activities, eq(workSessions.activityId, activities.id))
.leftJoin(user, eq(workSessions.userId, user.id))
.where(eq(workSessions.status, 'active'))
.orderBy(desc(workSessions.startTime));
return c.json(
rows.map((r) =>
toWorkSession(r.session, {
activityName: r.activityName,
userName: r.userName,
userEmail: r.userEmail,
})
)
);
});