diff --git a/apps/api/src/lib/require-user.ts b/apps/api/src/lib/require-user.ts index 954f764..0c5dd12 100644 --- a/apps/api/src/lib/require-user.ts +++ b/apps/api/src/lib/require-user.ts @@ -1,4 +1,4 @@ -import type { Context } from 'hono'; +import type { Context, MiddlewareHandler } from 'hono'; import { auth } from '../auth'; export interface SessionUser { @@ -16,3 +16,11 @@ export async function getSessionUser(c: Context): Promise { export function isAdmin(u: SessionUser | null): boolean { return u?.role === 'admin'; } + +// Reusable gate for the whole /api/admin/* surface: 401 if unauthenticated, 403 if not an admin. +export const adminGuard: MiddlewareHandler = async (c, next) => { + const u = await getSessionUser(c); + if (!u) return c.json({ error: 'Unauthorized' }, 401); + if (!isAdmin(u)) return c.json({ error: 'Forbidden' }, 403); + await next(); +}; diff --git a/apps/api/src/routes/admin.ts b/apps/api/src/routes/admin.ts index 23d3cd2..8d39f2d 100644 --- a/apps/api/src/routes/admin.ts +++ b/apps/api/src/routes/admin.ts @@ -3,7 +3,7 @@ import { and, asc, desc, eq, gte, lte, type SQL } from 'drizzle-orm'; import { AdminUpdateSessionInput, CreateManualSessionInput, InsoleType } from '@solelog/shared'; import { db } from '../db/client'; import { activities, user, workSessions } from '../db/schema'; -import { getSessionUser, isAdmin } from '../lib/require-user'; +import { adminGuard } from '../lib/require-user'; import { toWorkSession } from '../lib/work-session'; import { buildSessionsCsv } from '../lib/csv'; @@ -59,12 +59,7 @@ function buildSessionFilters(q: ReportQuery): SQL[] { 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(); -}); +adminRoutes.use('/api/admin/*', adminGuard); const baseSelect = { session: workSessions, diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index c61f75d..a47e200 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -78,15 +78,33 @@ export const StartSessionInput = z.object({ }); export type StartSessionInput = z.infer; +export const UserStatus = z.enum(['active', 'inactive']); +export type UserStatus = z.infer; + export const AdminUser = z.object({ id: z.string(), email: z.string().email(), name: z.string(), role: Role, + status: UserStatus, created_at: z.string(), }); export type AdminUser = z.infer; +export const CreateUserInput = z.object({ + email: z.string().email(), + name: z.string().trim().min(1), + password: z.string().min(8), + role: Role, +}); +export type CreateUserInput = z.infer; + +export const SetRoleInput = z.object({ role: Role }); +export type SetRoleInput = z.infer; + +export const SetPasswordInput = z.object({ password: z.string().min(8) }); +export type SetPasswordInput = z.infer; + export const CreateManualSessionInput = z.object({ user_id: z.string(), activity_id: z.number().int(),