Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Security fixes: - Block self-registration via magic link (PrismaAdapter createUser throws) - Magic links only sent to existing ACTIVE users (prevents enumeration) - signIn callback rejects non-existent users (defense-in-depth) - Change schema default role from JURY_MEMBER to APPLICANT - Add authentication to live-voting SSE stream endpoint - Fix false FILE_OPENED/FILE_DOWNLOADED audit events on page load (remove purpose from eagerly pre-fetched URL queries) Bug fixes: - Fix impersonation skeleton screen on applicant dashboard - Fix onboarding redirect loop in auth layout Observer dashboard redesign (Steps 1-6): - Clickable round pipeline with selected round highlighting - Round-type-specific dashboard panels (intake, filtering, evaluation, submission, mentoring, live final, deliberation) - Enhanced activity feed with server-side humanization - Previous round comparison section - New backend queries for round-specific analytics Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import { redirect } from 'next/navigation'
|
|
import Image from 'next/image'
|
|
import { auth } from '@/lib/auth'
|
|
import { prisma } from '@/lib/prisma'
|
|
|
|
export default async function AuthLayout({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode
|
|
}) {
|
|
let session = null
|
|
try {
|
|
session = await auth()
|
|
} catch (error) {
|
|
// Auth check failed, continue as unauthenticated
|
|
console.error('Auth check failed in auth layout:', error)
|
|
}
|
|
|
|
// Redirect logged-in users to their dashboard
|
|
// But NOT if they still need to set their password
|
|
if (session?.user && !session.user.mustSetPassword) {
|
|
// Verify user still exists in DB and check onboarding status
|
|
const dbUser = await prisma.user.findUnique({
|
|
where: { id: session.user.id },
|
|
select: { id: true, onboardingCompletedAt: true },
|
|
})
|
|
|
|
if (dbUser) {
|
|
// If user hasn't completed onboarding, don't redirect away from auth pages.
|
|
// The /onboarding page lives in this (auth) layout, so they need to stay here.
|
|
if (!dbUser.onboardingCompletedAt) {
|
|
// Fall through — let them access /onboarding (and other auth pages)
|
|
} else {
|
|
const role = session.user.role
|
|
if (role === 'SUPER_ADMIN' || role === 'PROGRAM_ADMIN') {
|
|
redirect('/admin')
|
|
} else if (role === 'JURY_MEMBER') {
|
|
redirect('/jury')
|
|
} else if (role === 'OBSERVER') {
|
|
redirect('/observer')
|
|
} else if (role === 'MENTOR') {
|
|
redirect('/mentor')
|
|
} else if (role === 'APPLICANT') {
|
|
redirect('/applicant')
|
|
}
|
|
}
|
|
}
|
|
// If user doesn't exist in DB, fall through and show auth page
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen flex flex-col">
|
|
{/* Simple header with logo */}
|
|
<header className="border-b bg-card">
|
|
<div className="container-app py-4">
|
|
<Image
|
|
src="/images/MOPC-blue-long.png"
|
|
alt="MOPC - Monaco Ocean Protection Challenge"
|
|
width={160}
|
|
height={50}
|
|
className="h-12 w-auto"
|
|
priority
|
|
/>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Main content */}
|
|
<main className="flex-1 flex items-center justify-center p-4">
|
|
{children}
|
|
</main>
|
|
|
|
{/* Simple footer */}
|
|
<footer className="border-t bg-card py-4">
|
|
<div className="container-app text-center text-sm text-muted-foreground">
|
|
© {new Date().getFullYear()} Monaco Ocean Protection Challenge
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
)
|
|
}
|