diff --git a/package.json b/package.json index 266c3d5..9a72402 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "open-seo", "private": true, "sideEffects": false, - "version": "0.0.16", + "version": "0.0.17", "type": "module", "packageManager": "pnpm@10.30.1", "scripts": { diff --git a/release-notes/v0.0.17.md b/release-notes/v0.0.17.md new file mode 100644 index 0000000..3f30162 --- /dev/null +++ b/release-notes/v0.0.17.md @@ -0,0 +1,11 @@ +Bug fixes and an internal code cleanup. + +## Fixed + +- Fixed an infinite redirect loop between `/verify-email` and the app when `BYPASS_EMAIL_VERIFICATION=true` (local dev). The email-verification bypass is now honored consistently across the auth route guard, onboarding redirect, and verify-email page. Production behavior is unchanged. + +## Improved + +- Simplified complex client code across audit, backlinks, domain, keywords, and rank-tracking features (removed single-use wrappers, dead helpers, and redundant guards) while preserving type safety and behavior. + +Full Changelog: https://github.com/every-app/open-seo/compare/v0.0.16...v0.0.17 diff --git a/src/client/features/auth/useHostedAuthRouteGuard.ts b/src/client/features/auth/useHostedAuthRouteGuard.ts index f7fb62e..a203ad7 100644 --- a/src/client/features/auth/useHostedAuthRouteGuard.ts +++ b/src/client/features/auth/useHostedAuthRouteGuard.ts @@ -1,7 +1,10 @@ import { useNavigate } from "@tanstack/react-router"; import { useEffect } from "react"; import { useSession } from "@/lib/auth-client"; -import { isHostedClientAuthMode } from "@/lib/auth-mode"; +import { + isEmailVerificationBypassed, + isHostedClientAuthMode, +} from "@/lib/auth-mode"; import { getCurrentAuthRedirectFromHref, getSignInSearch, @@ -12,6 +15,8 @@ export function useHostedAuthRouteGuard() { const navigate = useNavigate(); const { data: session, isPending } = useSession(); const isHostedMode = isHostedClientAuthMode(); + const emailVerified = + session?.user?.emailVerified === true || isEmailVerificationBypassed(); useEffect(() => { if (isPending || !isHostedMode) { @@ -29,7 +34,7 @@ export function useHostedAuthRouteGuard() { return; } - if (!session.user.emailVerified) { + if (!emailVerified) { void navigate({ to: "/verify-email", search: getVerifyEmailSearch(session.user.email, redirectTo), @@ -39,16 +44,14 @@ export function useHostedAuthRouteGuard() { }, [ isPending, isHostedMode, + emailVerified, session?.user?.email, - session?.user?.emailVerified, session?.user?.id, navigate, ]); const hasVerifiedHostedSession = - !isPending && - Boolean(session?.user?.id) && - session?.user?.emailVerified === true; + !isPending && Boolean(session?.user?.id) && emailVerified; return { isHostedMode, diff --git a/src/client/features/onboarding/useOnboardingRedirect.ts b/src/client/features/onboarding/useOnboardingRedirect.ts index fd80843..b182a0d 100644 --- a/src/client/features/onboarding/useOnboardingRedirect.ts +++ b/src/client/features/onboarding/useOnboardingRedirect.ts @@ -3,13 +3,17 @@ import { useNavigate } from "@tanstack/react-router"; import { useEffect } from "react"; import { onboardingAnswersQueryOptions } from "@/client/features/onboarding/onboardingModel"; import { useSession } from "@/lib/auth-client"; -import { isHostedClientAuthMode } from "@/lib/auth-mode"; +import { + isEmailVerificationBypassed, + isHostedClientAuthMode, +} from "@/lib/auth-mode"; export function useOnboardingRedirect() { const navigate = useNavigate(); const { data: session } = useSession(); const isHostedMode = isHostedClientAuthMode(); - const isEmailVerified = session?.user?.emailVerified === true; + const isEmailVerified = + session?.user?.emailVerified === true || isEmailVerificationBypassed(); const onboardingQuery = useQuery({ ...onboardingAnswersQueryOptions(), enabled: isHostedMode && Boolean(session?.user?.id) && isEmailVerified, diff --git a/src/lib/auth-mode.ts b/src/lib/auth-mode.ts index 602c42a..f8053b3 100644 --- a/src/lib/auth-mode.ts +++ b/src/lib/auth-mode.ts @@ -22,3 +22,7 @@ export function isHostedClientAuthMode() { // AUTH_MODE=hosted in both the client build environment and the runtime. return isHostedAuthMode(import.meta.env.AUTH_MODE); } + +export function isEmailVerificationBypassed() { + return import.meta.env.BYPASS_EMAIL_VERIFICATION === "true"; +} diff --git a/src/routes/verify-email.tsx b/src/routes/verify-email.tsx index cd4615a..f6c8625 100644 --- a/src/routes/verify-email.tsx +++ b/src/routes/verify-email.tsx @@ -8,7 +8,10 @@ import { } from "@/client/features/auth/AuthPage"; import { captureClientEvent } from "@/client/lib/posthog"; import { authClient, useSession } from "@/lib/auth-client"; -import { isHostedClientAuthMode } from "@/lib/auth-mode"; +import { + isEmailVerificationBypassed, + isHostedClientAuthMode, +} from "@/lib/auth-mode"; import { getSignInSearch, normalizeAuthRedirect } from "@/lib/auth-redirect"; import { z } from "zod"; @@ -102,8 +105,7 @@ function VerifyEmailPage() { const redirectTo = normalizeAuthRedirect(search.redirect); const isHostedMode = isHostedClientAuthMode(); const { data: session, isPending } = useSession(); - const bypassEmailVerification = - import.meta.env.BYPASS_EMAIL_VERIFICATION === "true"; + const bypassEmailVerification = isEmailVerificationBypassed(); const errorMessage = getVerificationErrorMessage(search.error); const verificationIssueType = search.error ? verificationIssueSchema.parse(search.error)