import { redirect } from "next/navigation"; import { isStaff } from "@/lib/admin/is-staff"; import { resolveStaffUser } from "@/lib/admin/staff-user"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; import { clientIp, rateLimit } from "@/lib/rate-limit"; export { isStaff }; export async function getMinStaffRank(): Promise { const n = Number(await siteSettings.get("min_staff_rank", "7")); return Number.isFinite(n) ? n : 7; } export interface StaffUser { id: number; rank: number; username: string; } /** * Gate for admin pages and actions: redirects to /login when unauthenticated * and to / when authenticated but not staff. Returns the staff user otherwise. */ export async function requireStaff(): Promise { const session = await auth(); if (!session?.user?.id) redirect("/login"); const minRank = await getMinStaffRank(); const staff = await resolveStaffUser(session.user.id, minRank, (id) => prisma.user.findUnique({ where: { id }, select: { id: true, rank: true, username: true }, }), ); if (!staff) redirect("/"); return staff; } /** * Like requireStaff but also rate-limits the action per staff user (30 requests * per minute). Use on sensitive admin actions (ban, rank-change, settings edit). */ export async function requireStaffRateLimited(): Promise { const staff = await requireStaff(); const ip = await clientIp(); if (!(await rateLimit(`admin:${staff.id}:${ip}`, 30, 60_000)).ok) redirect("/admin?error=ratelimit"); return staff; }