import type { UserRole } from "@fitai/shared"; import { NextResponse } from "next/server"; /** * Check if a user can access data from a specific gym * * @param userRole - The role of the requesting user * @param userGymId - The gym ID of the requesting user (null for superAdmin) * @param targetGymId - The gym ID being accessed (undefined means all gyms) * @returns true if access is allowed * * @example * // SuperAdmin accessing any gym * canAccessGym("superAdmin", null, "gym_123") // true * * // Admin accessing their own gym * canAccessGym("admin", "gym_abc", "gym_abc") // true * * // Admin trying to access other gym * canAccessGym("admin", "gym_abc", "gym_xyz") // false */ export function canAccessGym( userRole: UserRole, userGymId: string | null, targetGymId: string | undefined, ): boolean { // SuperAdmin can access any gym if (userRole === "superAdmin") { return true; } // If no target gym specified, allow access to own gym if (targetGymId === undefined) { return true; } // Non-superAdmins must have a gymId if (!userGymId) { return false; } // Must match the gym return userGymId === targetGymId; } /** * Check if a user can manage (create/update/delete) another user * Based on role hierarchy and gym membership * * @param requesterRole - Role of the user making the request * @param requesterGymId - Gym ID of the requesting user * @param targetRole - Role of the user being managed * @param targetGymId - Gym ID of the user being managed * @returns true if management is allowed * * @example * // SuperAdmin can manage anyone * canManageUser("superAdmin", null, "admin", "gym_abc") // true * * // Admin can manage trainers and clients in their gym * canManageUser("admin", "gym_abc", "trainer", "gym_abc") // true * canManageUser("admin", "gym_abc", "client", "gym_abc") // true * * // Admin cannot manage users in other gyms * canManageUser("admin", "gym_abc", "trainer", "gym_xyz") // false * * // Trainer can manage clients in their gym * canManageUser("trainer", "gym_abc", "client", "gym_abc") // true */ export function canManageUser( requesterRole: UserRole, requesterGymId: string | null, targetRole: UserRole, targetGymId: string | null, ): boolean { // SuperAdmin can manage anyone if (requesterRole === "superAdmin") { return true; } // Role hierarchy: admin > trainer > client const roleHierarchy: Record = { superAdmin: 4, admin: 3, trainer: 2, client: 1, }; const requesterLevel = roleHierarchy[requesterRole]; const targetLevel = roleHierarchy[targetRole]; // Cannot manage users at same or higher level if (requesterLevel <= targetLevel) { return false; } // Must have gym access if (!requesterGymId) { return false; } // For admin/trainer, target must be in same gym if (requesterRole === "admin" || requesterRole === "trainer") { return requesterGymId === targetGymId; } return false; } /** * Check if a trainer can access a specific client's data * Trainer must be assigned to that client via trainerClients table * * @param trainerGymId - Gym ID of the trainer * @param clientGymId - Gym ID of the client * @returns true if in same gym * * @note In a more complex system, this would query trainerClients table * For now, we use gym-based access (trainer and client in same gym) */ export function canTrainerAccessClient( trainerGymId: string | null, clientGymId: string | null, ): boolean { if (!trainerGymId || !clientGymId) { return false; } return trainerGymId === clientGymId; } /** * Get the roles that a specific role can invite * Based on role hierarchy * * @param role - The role wanting to invite * @returns Array of roles that can be invited * * @example * getInvitableRoles("superAdmin") // ["admin", "trainer", "client"] * getInvitableRoles("admin") // ["trainer", "client"] * getInvitableRoles("trainer") // ["client"] * getInvitableRoles("client") // [] */ export function getInvitableRoles(role: UserRole): UserRole[] { const invitationRules: Record = { superAdmin: ["admin", "trainer", "client"], admin: ["trainer", "client"], trainer: ["client"], client: [], }; return invitationRules[role] ?? []; } /** * Validate gym access and return error response if denied * Use this in API routes to enforce gym-based access control * * @param request - NextRequest for getting query params * @param userRole - Current user's role * @param userGymId - Current user's gym ID * @param targetGymId - Gym being accessed (optional) * @returns NextResponse if denied, undefined if allowed * * @example * export async function GET(request: NextRequest) { * const error = await validateGymAccess(request, role, gymId); * if (error) return error; * // ... proceed * } */ export function validateGymAccess( userRole: UserRole, userGymId: string | null, targetGymId?: string, ): NextResponse | undefined { if (!canAccessGym(userRole, userGymId, targetGymId)) { return NextResponse.json( { error: "Forbidden - Cannot access this gym's data" }, { status: 403 }, ); } return undefined; } /** * Get filter condition for gym-scoped queries * Returns appropriate filter based on user role * * @param userRole - Current user's role * @param userGymId - Current user's gym ID * @returns Filter object or undefined (no filter = all gyms) * * @example * // For superAdmin: returns undefined (no filter) * getGymFilter("superAdmin", null) // undefined * * // For admin: returns { gymId: "gym_abc" } * getGymFilter("admin", "gym_abc") // { gymId: "gym_abc" } */ export function getGymFilter( userRole: UserRole, userGymId: string | null, ): { gymId: string } | undefined { // SuperAdmin sees all gyms if (userRole === "superAdmin") { return undefined; } // Other roles filtered by their gym if (userGymId) { return { gymId: userGymId }; } // No gym assigned - return filter that matches nothing return { gymId: "" }; } /** * Get all gym IDs a user can access * SuperAdmin gets all gyms, others get only their own * * @param userRole - Current user's role * @param userGymId - Current user's gym ID * @param allGymIds - List of all gym IDs in system (for superAdmin) * @returns Array of accessible gym IDs * * @example * getAccessibleGymIds("admin", "gym_abc", ["gym_abc", "gym_xyz"]) * // Returns: ["gym_abc"] * * getAccessibleGymIds("superAdmin", null, ["gym_abc", "gym_xyz"]) * // Returns: ["gym_abc", "gym_xyz"] */ export function getAccessibleGymIds( userRole: UserRole, userGymId: string | null, allGymIds: string[], ): string[] { if (userRole === "superAdmin") { return allGymIds; } if (userGymId) { return [userGymId]; } return []; }