34 lines
1.2 KiB
TypeScript
34 lines
1.2 KiB
TypeScript
import { redirect } from "next/navigation";
|
|
import { isStaff } from "@/lib/admin/is-staff";
|
|
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
|
|
import { clientIp, rateLimit } from "@/lib/rate-limit";
|
|
|
|
export { isStaff };
|
|
|
|
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<StaffUser> {
|
|
const { session, permissions } = await getAdminContext();
|
|
if (!canAccess(permissions, PERMS.ADMIN_DASHBOARD, session.user.rank)) redirect("/");
|
|
return { id: session.user.id, rank: session.user.rank, username: session.user.username };
|
|
}
|
|
|
|
/**
|
|
* 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<StaffUser> {
|
|
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;
|
|
}
|