mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-05 05:04:10 -05:00
fix(verification): add OTP dev skip (#1395)
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { env } from '@/lib/env'
|
||||
import { hasEmailService } from '@/lib/email/mailer'
|
||||
import { isProd } from '@/lib/environment'
|
||||
import { VerifyContent } from '@/app/(auth)/verify/verify-content'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export default function VerifyPage() {
|
||||
const hasResendKey = Boolean(env.RESEND_API_KEY)
|
||||
const emailServiceConfigured = hasEmailService()
|
||||
|
||||
return <VerifyContent hasResendKey={hasResendKey} isProduction={isProd} />
|
||||
return <VerifyContent hasEmailService={emailServiceConfigured} isProduction={isProd} />
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { createLogger } from '@/lib/logs/console/logger'
|
||||
const logger = createLogger('useVerification')
|
||||
|
||||
interface UseVerificationParams {
|
||||
hasResendKey: boolean
|
||||
hasEmailService: boolean
|
||||
isProduction: boolean
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ interface UseVerificationReturn {
|
||||
isInvalidOtp: boolean
|
||||
errorMessage: string
|
||||
isOtpComplete: boolean
|
||||
hasResendKey: boolean
|
||||
hasEmailService: boolean
|
||||
isProduction: boolean
|
||||
verifyCode: () => Promise<void>
|
||||
resendCode: () => void
|
||||
@@ -28,7 +28,7 @@ interface UseVerificationReturn {
|
||||
}
|
||||
|
||||
export function useVerification({
|
||||
hasResendKey,
|
||||
hasEmailService,
|
||||
isProduction,
|
||||
}: UseVerificationParams): UseVerificationReturn {
|
||||
const router = useRouter()
|
||||
@@ -74,10 +74,10 @@ export function useVerification({
|
||||
}, [searchParams])
|
||||
|
||||
useEffect(() => {
|
||||
if (email && !isSendingInitialOtp && hasResendKey) {
|
||||
if (email && !isSendingInitialOtp && hasEmailService) {
|
||||
setIsSendingInitialOtp(true)
|
||||
}
|
||||
}, [email, isSendingInitialOtp, hasResendKey])
|
||||
}, [email, isSendingInitialOtp, hasEmailService])
|
||||
|
||||
const isOtpComplete = otp.length === 6
|
||||
|
||||
@@ -157,7 +157,7 @@ export function useVerification({
|
||||
}
|
||||
|
||||
function resendCode() {
|
||||
if (!email || !hasResendKey) return
|
||||
if (!email || !hasEmailService) return
|
||||
|
||||
setIsLoading(true)
|
||||
setErrorMessage('')
|
||||
@@ -197,17 +197,27 @@ export function useVerification({
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (!isProduction || !hasResendKey) {
|
||||
if (!isProduction && !hasEmailService) {
|
||||
setIsVerified(true)
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
window.location.href = '/workspace'
|
||||
}, 1000)
|
||||
const handleRedirect = async () => {
|
||||
try {
|
||||
await refetchSession()
|
||||
} catch (error) {
|
||||
logger.warn('Failed to refetch session during dev verification skip:', error)
|
||||
}
|
||||
|
||||
return () => clearTimeout(timeoutId)
|
||||
if (isInviteFlow && redirectUrl) {
|
||||
window.location.href = redirectUrl
|
||||
} else {
|
||||
router.push('/workspace')
|
||||
}
|
||||
}
|
||||
|
||||
handleRedirect()
|
||||
}
|
||||
}
|
||||
}, [isProduction, hasResendKey, router])
|
||||
}, [isProduction, hasEmailService, router, isInviteFlow, redirectUrl])
|
||||
|
||||
return {
|
||||
otp,
|
||||
@@ -217,7 +227,7 @@ export function useVerification({
|
||||
isInvalidOtp,
|
||||
errorMessage,
|
||||
isOtpComplete,
|
||||
hasResendKey,
|
||||
hasEmailService,
|
||||
isProduction,
|
||||
verifyCode,
|
||||
resendCode,
|
||||
|
||||
@@ -10,15 +10,15 @@ import { inter } from '@/app/fonts/inter'
|
||||
import { soehne } from '@/app/fonts/soehne/soehne'
|
||||
|
||||
interface VerifyContentProps {
|
||||
hasResendKey: boolean
|
||||
hasEmailService: boolean
|
||||
isProduction: boolean
|
||||
}
|
||||
|
||||
function VerificationForm({
|
||||
hasResendKey,
|
||||
hasEmailService,
|
||||
isProduction,
|
||||
}: {
|
||||
hasResendKey: boolean
|
||||
hasEmailService: boolean
|
||||
isProduction: boolean
|
||||
}) {
|
||||
const {
|
||||
@@ -32,7 +32,7 @@ function VerificationForm({
|
||||
verifyCode,
|
||||
resendCode,
|
||||
handleOtpChange,
|
||||
} = useVerification({ hasResendKey, isProduction })
|
||||
} = useVerification({ hasEmailService, isProduction })
|
||||
|
||||
const [countdown, setCountdown] = useState(0)
|
||||
const [isResendDisabled, setIsResendDisabled] = useState(false)
|
||||
@@ -93,7 +93,7 @@ function VerificationForm({
|
||||
<p className={`${inter.className} font-[380] text-[16px] text-muted-foreground`}>
|
||||
{isVerified
|
||||
? 'Your email has been verified. Redirecting to dashboard...'
|
||||
: hasResendKey
|
||||
: hasEmailService
|
||||
? `A verification code has been sent to ${email || 'your email'}`
|
||||
: !isProduction
|
||||
? 'Development mode: Check your console logs for the verification code'
|
||||
@@ -106,7 +106,7 @@ function VerificationForm({
|
||||
<div className='space-y-6'>
|
||||
<p className='text-center text-muted-foreground text-sm'>
|
||||
Enter the 6-digit code to verify your account.
|
||||
{hasResendKey ? " If you don't see it in your inbox, check your spam folder." : ''}
|
||||
{hasEmailService ? " If you don't see it in your inbox, check your spam folder." : ''}
|
||||
</p>
|
||||
|
||||
<div className='flex justify-center'>
|
||||
@@ -192,7 +192,7 @@ function VerificationForm({
|
||||
{isLoading ? 'Verifying...' : 'Verify Email'}
|
||||
</Button>
|
||||
|
||||
{hasResendKey && (
|
||||
{hasEmailService && (
|
||||
<div className='text-center'>
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
Didn't receive a code?{' '}
|
||||
@@ -245,10 +245,10 @@ function VerificationFormFallback() {
|
||||
)
|
||||
}
|
||||
|
||||
export function VerifyContent({ hasResendKey, isProduction }: VerifyContentProps) {
|
||||
export function VerifyContent({ hasEmailService, isProduction }: VerifyContentProps) {
|
||||
return (
|
||||
<Suspense fallback={<VerificationFormFallback />}>
|
||||
<VerificationForm hasResendKey={hasResendKey} isProduction={isProduction} />
|
||||
<VerificationForm hasEmailService={hasEmailService} isProduction={isProduction} />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
handleInvoicePaymentFailed,
|
||||
handleInvoicePaymentSucceeded,
|
||||
} from '@/lib/billing/webhooks/invoices'
|
||||
import { sendEmail } from '@/lib/email/mailer'
|
||||
import { hasEmailService, sendEmail } from '@/lib/email/mailer'
|
||||
import { getFromEmailAddress } from '@/lib/email/utils'
|
||||
import { quickValidateEmail } from '@/lib/email/validation'
|
||||
import { env, isTruthy } from '@/lib/env'
|
||||
@@ -147,7 +147,7 @@ export const auth = betterAuth({
|
||||
},
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
requireEmailVerification: isProd,
|
||||
requireEmailVerification: isProd && hasEmailService(),
|
||||
sendVerificationOnSignUp: false,
|
||||
throwOnMissingCredentials: true,
|
||||
throwOnInvalidCredentials: true,
|
||||
|
||||
@@ -69,6 +69,13 @@ const azureEmailClient =
|
||||
? new EmailClient(azureConnectionString)
|
||||
: null
|
||||
|
||||
/**
|
||||
* Check if any email service is configured and available
|
||||
*/
|
||||
export function hasEmailService(): boolean {
|
||||
return !!(resend || azureEmailClient)
|
||||
}
|
||||
|
||||
export async function sendEmail(options: EmailOptions): Promise<SendEmailResult> {
|
||||
try {
|
||||
// Check if user has unsubscribed (skip for critical transactional emails)
|
||||
|
||||
@@ -10,6 +10,7 @@ services:
|
||||
limits:
|
||||
memory: 8G
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
|
||||
- BETTER_AUTH_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
|
||||
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
|
||||
|
||||
@@ -9,6 +9,7 @@ services:
|
||||
limits:
|
||||
memory: 8G
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
|
||||
- BETTER_AUTH_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
|
||||
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
|
||||
|
||||
Reference in New Issue
Block a user