feat: impersonation system, semi-finalist detail page, tRPC resilience
- Add super-admin impersonation: "Login As" from user list, red banner with "Return to Admin", audit logged start/end, nested impersonation blocked, onboarding gate skipped during impersonation - Fix semi-finalist stats: check latest terminal state (not any PASSED), use passwordHash OR status=ACTIVE for activation check - Add /admin/semi-finalists detail page with search, category/status filters - Add account_reminder_days setting to notifications tab - Add tRPC resilience: retry on 503/HTML responses, custom fetch detects nginx error pages, exponential backoff (2s/4s/8s) - Reduce dashboard polling intervals (60s stats, 30s activity, 120s semi) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
import type { NextAuthConfig } from 'next-auth'
|
||||
import type { UserRole } from '@prisma/client'
|
||||
|
||||
type ImpersonationInfo = {
|
||||
originalId: string
|
||||
originalRole: UserRole
|
||||
originalRoles: UserRole[]
|
||||
originalEmail: string
|
||||
}
|
||||
|
||||
// Extend the built-in session types
|
||||
declare module 'next-auth' {
|
||||
interface Session {
|
||||
@@ -11,6 +18,7 @@ declare module 'next-auth' {
|
||||
role: UserRole
|
||||
roles: UserRole[]
|
||||
mustSetPassword?: boolean
|
||||
impersonating?: ImpersonationInfo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +35,7 @@ declare module '@auth/core/jwt' {
|
||||
role: UserRole
|
||||
roles?: UserRole[]
|
||||
mustSetPassword?: boolean
|
||||
impersonating?: ImpersonationInfo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,15 +70,16 @@ export const authConfig: NextAuthConfig = {
|
||||
return false // Will redirect to signIn page
|
||||
}
|
||||
|
||||
// Check if user needs to set password
|
||||
// Check if user needs to set password (skip during impersonation)
|
||||
const mustSetPassword = auth?.user?.mustSetPassword
|
||||
const isImpersonating = !!(auth?.user as Record<string, unknown>)?.impersonating
|
||||
const passwordSetupAllowedPaths = [
|
||||
'/set-password',
|
||||
'/api/auth',
|
||||
'/api/trpc',
|
||||
]
|
||||
|
||||
if (mustSetPassword) {
|
||||
if (mustSetPassword && !isImpersonating) {
|
||||
// Allow access to password setup related paths
|
||||
if (passwordSetupAllowedPaths.some((path) => pathname.startsWith(path))) {
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user