Feature 1: Anthropic API Integration - Add @anthropic-ai/sdk with adapter wrapping OpenAI-shaped interface - Support Claude models (opus, sonnet, haiku) with extended thinking - Auto-reset model on provider switch, JSON retry logic - Add Claude model pricing to ai-usage tracker - Update AI settings form with Anthropic provider option Feature 2: Remove Locale Settings UI - Strip Localization tab from admin settings - Remove i18n settings from router inferCategory and getFeatureFlags - Keep franc document language detection intact Feature 3: Test Environment with Role Impersonation - Add isTest field to User, Program, Project, Competition models - Test environment service: create/teardown with realistic dummy data - JWT-based impersonation for test users (@test.local emails) - Impersonation banner with quick-switch between test roles - Test environment panel in admin settings (SUPER_ADMIN only) - Email redirect: @test.local emails routed to admin with [TEST] prefix - Complete data isolation: 45+ isTest:false filters across platform - All global queries on User/Project/Program/Competition - AI services blocked from processing test data - Cron jobs skip test rounds/users - Analytics/exports exclude test data - Admin layout/pickers hide test programs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
103 lines
2.6 KiB
TypeScript
103 lines
2.6 KiB
TypeScript
import type { NextAuthConfig } from 'next-auth'
|
|
import type { UserRole } from '@prisma/client'
|
|
|
|
// Extend the built-in session types
|
|
declare module 'next-auth' {
|
|
interface Session {
|
|
user: {
|
|
id: string
|
|
email: string
|
|
name?: string | null
|
|
role: UserRole
|
|
mustSetPassword?: boolean
|
|
// Impersonation fields
|
|
isImpersonating?: boolean
|
|
realUserId?: string
|
|
realRole?: UserRole
|
|
impersonatedName?: string | null
|
|
}
|
|
}
|
|
|
|
interface User {
|
|
role?: UserRole
|
|
mustSetPassword?: boolean
|
|
}
|
|
}
|
|
|
|
declare module '@auth/core/jwt' {
|
|
interface JWT {
|
|
id: string
|
|
role: UserRole
|
|
mustSetPassword?: boolean
|
|
// Impersonation fields
|
|
impersonatedUserId?: string
|
|
impersonatedRole?: UserRole
|
|
impersonatedName?: string | null
|
|
realUserId?: string
|
|
realRole?: UserRole
|
|
}
|
|
}
|
|
|
|
// Edge-compatible auth config (no Node.js-only modules)
|
|
// This is used by middleware and can be extended in auth.ts for full functionality
|
|
export const authConfig: NextAuthConfig = {
|
|
providers: [], // Providers are added in auth.ts
|
|
callbacks: {
|
|
authorized({ auth, request: { nextUrl } }) {
|
|
const isLoggedIn = !!auth?.user
|
|
const { pathname } = nextUrl
|
|
|
|
// Public paths that don't require authentication
|
|
const publicPaths = [
|
|
'/login',
|
|
'/verify',
|
|
'/verify-email',
|
|
'/error',
|
|
'/accept-invite',
|
|
'/apply',
|
|
'/api/auth',
|
|
'/api/trpc', // tRPC handles its own auth via procedures
|
|
]
|
|
|
|
// Check if it's a public path
|
|
if (publicPaths.some((path) => pathname.startsWith(path))) {
|
|
return true
|
|
}
|
|
|
|
// If not logged in, redirect to login
|
|
if (!isLoggedIn) {
|
|
return false // Will redirect to signIn page
|
|
}
|
|
|
|
// Check if user needs to set password
|
|
const mustSetPassword = auth?.user?.mustSetPassword
|
|
const passwordSetupAllowedPaths = [
|
|
'/set-password',
|
|
'/api/auth',
|
|
'/api/trpc',
|
|
]
|
|
|
|
if (mustSetPassword) {
|
|
// Allow access to password setup related paths
|
|
if (passwordSetupAllowedPaths.some((path) => pathname.startsWith(path))) {
|
|
return true
|
|
}
|
|
// Redirect to set-password page
|
|
return Response.redirect(new URL('/set-password', nextUrl))
|
|
}
|
|
|
|
return true
|
|
},
|
|
},
|
|
pages: {
|
|
signIn: '/login',
|
|
verifyRequest: '/verify-email',
|
|
error: '/error',
|
|
newUser: '/set-password',
|
|
},
|
|
session: {
|
|
strategy: 'jwt',
|
|
maxAge: parseInt(process.env.SESSION_MAX_AGE || '86400'), // 24 hours
|
|
},
|
|
}
|