import type { H3Event } from 'h3' import { eq } from 'drizzle-orm' import { users } from '../database/schema' import { getUserApprovedRoleCodes } from './roles' /** * Role validation cache TTL (Time To Live) * Roles are re-validated against DB after this period */ const ROLE_VALIDATION_TTL = 5 * 60 * 1000 // 5 minutes in milliseconds /** * Get user's active role with automatic validation * * Features: * - Returns cached role from session if TTL hasn't expired * - Re-validates against DB after TTL expires (every 5 minutes) * - Auto-fallback if active role was revoked by admin * - Updates session automatically with validated data * * @param event - H3 Event from API handler * @returns Active role code (e.g., 'private', 'educator', 'company') * @throws 401 if user is not authenticated */ export async function getUserActiveRole(event: H3Event): Promise { const session = await getUserSession(event) if (!session?.user) { throw createError({ statusCode: 401, message: 'Not authenticated', }) } const now = Date.now() const lastValidated = session.rolesLastValidated ? new Date(session.rolesLastValidated).getTime() : 0 const needsRevalidation = now - lastValidated > ROLE_VALIDATION_TTL if (needsRevalidation) { // TTL expired → Re-validate against database const currentRoles = await getUserApprovedRoleCodes(session.user.id) const activeRole = session.activeRoleCode || 'private' // Check: Was active role revoked by admin? if (!currentRoles.includes(activeRole)) { // Auto-fallback to first available role const newActiveRole = currentRoles[0] || 'private' await replaceUserSession(event, { activeRoleCode: newActiveRole, userApprovedRoles: currentRoles, rolesLastValidated: new Date().toISOString(), roleChangedByAdmin: true, // Flag for client notification }) return newActiveRole } // Role still valid, just update cache + timestamp await replaceUserSession(event, { userApprovedRoles: currentRoles, rolesLastValidated: new Date().toISOString(), }) } return session.activeRoleCode || 'private' } /** * Set user's active role with validation * * Validates that user has the requested role before switching. * Updates both session (immediate) and database (preference for next login). * * @param event - H3 Event from API handler * @param roleCode - Role to switch to (must be in user's approved roles) * @throws 401 if not authenticated * @throws 403 if user doesn't have the requested role */ export async function setUserActiveRole( event: H3Event, roleCode: string ): Promise { const session = await requireUserSession(event) // Validate that user has this role const approvedRoles = await getUserApprovedRoleCodes(session.user.id) if (!approvedRoles.includes(roleCode)) { throw createError({ statusCode: 403, message: 'Du hast diese Rolle nicht', }) } // Update session with new active role await replaceUserSession(event, { activeRoleCode: roleCode, roleChangedByAdmin: false, // User switched manually rolesLastValidated: new Date().toISOString(), // Reset TTL }) // Update database: Save as user preference for next login const db = useDatabase() await db .update(users) .set({ lastActiveRoleCode: roleCode }) .where(eq(users.id, session.user.id)) }