All files / web/src/lib/auth roles.ts

100% Statements 38/38
100% Branches 10/10
100% Functions 2/2
100% Lines 38/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 391x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 6x 5x 6x 4x 4x 4x 4x 4x 4x 3x 6x 6x 1x 1x 1x 6x 1x 1x 1x 1x  
import { eq } from 'drizzle-orm'
import { db, schema } from '@/db'
import { isAdminEmail } from './admin-emails'
 
export type UserRole = 'guest' | 'user' | 'admin'
 
interface ResolveUserRoleInput {
  userId?: string | null
  email?: string | null
}
 
/**
 * Resolve the current application role for an authenticated user.
 *
 * The users table is authoritative. ADMIN_EMAILS remains a bootstrap override
 * for first-time admin promotion and emergency access.
 */
export async function resolveUserRole({ userId, email }: ResolveUserRoleInput): Promise<UserRole> {
  if (!userId) return 'guest'
 
  if (isAdminEmail(email)) return 'admin'
 
  try {
    const user = await db.query.users.findFirst({
      columns: { role: true },
      where: eq(schema.users.id, userId),
    })
 
    return user?.role === 'admin' ? 'admin' : 'user'
  } catch (err) {
    console.error('[auth] Failed to resolve user role:', err)
    return 'user'
  }
}
 
export async function isUserAdmin(input: ResolveUserRoleInput): Promise<boolean> {
  return (await resolveUserRole(input)) === 'admin'
}