refactor(api): extract adminGuard + add user-management contracts
This commit is contained in:
@@ -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<SessionUser | null> {
|
||||
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();
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -78,15 +78,33 @@ export const StartSessionInput = z.object({
|
||||
});
|
||||
export type StartSessionInput = z.infer<typeof StartSessionInput>;
|
||||
|
||||
export const UserStatus = z.enum(['active', 'inactive']);
|
||||
export type UserStatus = z.infer<typeof UserStatus>;
|
||||
|
||||
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<typeof AdminUser>;
|
||||
|
||||
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<typeof CreateUserInput>;
|
||||
|
||||
export const SetRoleInput = z.object({ role: Role });
|
||||
export type SetRoleInput = z.infer<typeof SetRoleInput>;
|
||||
|
||||
export const SetPasswordInput = z.object({ password: z.string().min(8) });
|
||||
export type SetPasswordInput = z.infer<typeof SetPasswordInput>;
|
||||
|
||||
export const CreateManualSessionInput = z.object({
|
||||
user_id: z.string(),
|
||||
activity_id: z.number().int(),
|
||||
|
||||
Reference in New Issue
Block a user