Merge branch 'dev' into swiftyos/connect-db-directly

This commit is contained in:
Swifty
2025-11-17 12:31:22 +01:00
committed by GitHub
17 changed files with 316 additions and 319 deletions

View File

@@ -1,4 +1,11 @@
"use client";
import { StoreAgentDetails } from "@/lib/autogpt-server-api";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
import { isEmptyOrWhitespace } from "@/lib/utils";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useOnboarding } from "../../../../providers/onboarding/onboarding-provider";
import OnboardingAgentCard from "../components/OnboardingAgentCard";
import OnboardingButton from "../components/OnboardingButton";
import {
OnboardingFooter,
@@ -6,28 +13,22 @@ import {
OnboardingStep,
} from "../components/OnboardingStep";
import { OnboardingText } from "../components/OnboardingText";
import OnboardingAgentCard from "../components/OnboardingAgentCard";
import { useEffect, useState } from "react";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
import { StoreAgentDetails } from "@/lib/autogpt-server-api";
import { isEmptyOrWhitespace } from "@/lib/utils";
import { useOnboarding } from "../../../../providers/onboarding/onboarding-provider";
import { finishOnboarding } from "../6-congrats/actions";
export default function Page() {
const { state, updateState } = useOnboarding(4, "INTEGRATIONS");
const { state, updateState, completeStep } = useOnboarding(4, "INTEGRATIONS");
const [agents, setAgents] = useState<StoreAgentDetails[]>([]);
const api = useBackendAPI();
const router = useRouter();
useEffect(() => {
api.getOnboardingAgents().then((agents) => {
if (agents.length < 2) {
finishOnboarding();
completeStep("CONGRATS");
router.replace("/");
}
setAgents(agents);
});
}, [api, setAgents]);
}, []);
useEffect(() => {
// Deselect agent if it's not in the list of agents

View File

@@ -1,18 +0,0 @@
"use server";
import BackendAPI from "@/lib/autogpt-server-api";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
export async function finishOnboarding() {
const api = new BackendAPI();
const onboarding = await api.getUserOnboarding();
const listingId = onboarding?.selectedStoreListingVersionId;
if (listingId) {
const libraryAgent = await api.addMarketplaceAgentToLibrary(listingId);
revalidatePath(`/library/agents/${libraryAgent.id}`, "layout");
redirect(`/library/agents/${libraryAgent.id}`);
} else {
revalidatePath("/library", "layout");
redirect("/library");
}
}

View File

@@ -1,12 +1,15 @@
"use client";
import { useEffect, useRef, useState } from "react";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
import { cn } from "@/lib/utils";
import { finishOnboarding } from "./actions";
import { useOnboarding } from "../../../../providers/onboarding/onboarding-provider";
import { useRouter } from "next/navigation";
import * as party from "party-js";
import { useEffect, useRef, useState } from "react";
import { useOnboarding } from "../../../../providers/onboarding/onboarding-provider";
export default function Page() {
const { completeStep } = useOnboarding(7, "AGENT_INPUT");
const router = useRouter();
const api = useBackendAPI();
const [showText, setShowText] = useState(false);
const [showSubtext, setShowSubtext] = useState(false);
const divRef = useRef(null);
@@ -30,9 +33,28 @@ export default function Page() {
setShowSubtext(true);
}, 500);
const timer2 = setTimeout(() => {
const timer2 = setTimeout(async () => {
completeStep("CONGRATS");
finishOnboarding();
try {
const onboarding = await api.getUserOnboarding();
if (onboarding?.selectedStoreListingVersionId) {
try {
const libraryAgent = await api.addMarketplaceAgentToLibrary(
onboarding.selectedStoreListingVersionId,
);
router.replace(`/library/agents/${libraryAgent.id}`);
} catch (error) {
console.error("Failed to add agent to library:", error);
router.replace("/library");
}
} else {
router.replace("/library");
}
} catch (error) {
console.error("Failed to get onboarding data:", error);
router.replace("/library");
}
}, 3000);
return () => {
@@ -40,7 +62,7 @@ export default function Page() {
clearTimeout(timer1);
clearTimeout(timer2);
};
}, []);
}, [completeStep, router, api]);
return (
<div className="flex h-screen w-screen flex-col items-center justify-center bg-violet-100">

View File

@@ -1,37 +1,72 @@
import BackendAPI from "@/lib/autogpt-server-api";
import { redirect } from "next/navigation";
import { finishOnboarding } from "./6-congrats/actions";
import { shouldShowOnboarding } from "@/app/api/helpers";
"use client";
import { LoadingSpinner } from "@/components/atoms/LoadingSpinner/LoadingSpinner";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
// Force dynamic rendering to avoid static generation issues with cookies
export const dynamic = "force-dynamic";
export default function OnboardingPage() {
const router = useRouter();
const api = useBackendAPI();
export default async function OnboardingPage() {
const api = new BackendAPI();
const isOnboardingEnabled = await shouldShowOnboarding();
useEffect(() => {
async function redirectToStep() {
try {
// Check if onboarding is enabled
const isEnabled = await api.isOnboardingEnabled();
if (!isEnabled) {
router.replace("/");
return;
}
if (!isOnboardingEnabled) {
redirect("/marketplace");
}
const onboarding = await api.getUserOnboarding();
const onboarding = await api.getUserOnboarding();
// Handle completed onboarding
if (onboarding.completedSteps.includes("GET_RESULTS")) {
router.replace("/");
return;
}
// CONGRATS is the last step in intro onboarding
if (onboarding.completedSteps.includes("GET_RESULTS"))
redirect("/marketplace");
else if (onboarding.completedSteps.includes("CONGRATS")) finishOnboarding();
else if (onboarding.completedSteps.includes("AGENT_INPUT"))
redirect("/onboarding/5-run");
else if (onboarding.completedSteps.includes("AGENT_NEW_RUN"))
redirect("/onboarding/5-run");
else if (onboarding.completedSteps.includes("AGENT_CHOICE"))
redirect("/onboarding/5-run");
else if (onboarding.completedSteps.includes("INTEGRATIONS"))
redirect("/onboarding/4-agent");
else if (onboarding.completedSteps.includes("USAGE_REASON"))
redirect("/onboarding/3-services");
else if (onboarding.completedSteps.includes("WELCOME"))
redirect("/onboarding/2-reason");
// Redirect to appropriate step based on completed steps
if (onboarding.completedSteps.includes("AGENT_INPUT")) {
router.push("/onboarding/5-run");
return;
}
redirect("/onboarding/1-welcome");
if (onboarding.completedSteps.includes("AGENT_NEW_RUN")) {
router.push("/onboarding/5-run");
return;
}
if (onboarding.completedSteps.includes("AGENT_CHOICE")) {
router.push("/onboarding/5-run");
return;
}
if (onboarding.completedSteps.includes("INTEGRATIONS")) {
router.push("/onboarding/4-agent");
return;
}
if (onboarding.completedSteps.includes("USAGE_REASON")) {
router.push("/onboarding/3-services");
return;
}
if (onboarding.completedSteps.includes("WELCOME")) {
router.push("/onboarding/2-reason");
return;
}
// Default: redirect to first step
router.push("/onboarding/1-welcome");
} catch (error) {
console.error("Failed to determine onboarding step:", error);
router.replace("/");
}
}
redirectToStep();
}, [api, router]);
return <LoadingSpinner size="large" cover />;
}

View File

@@ -2,11 +2,12 @@
import { postV1ResetOnboardingProgress } from "@/app/api/__generated__/endpoints/onboarding/onboarding";
import { LoadingSpinner } from "@/components/atoms/LoadingSpinner/LoadingSpinner";
import { useToast } from "@/components/molecules/Toast/use-toast";
import { redirect } from "next/navigation";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
export default function OnboardingResetPage() {
const { toast } = useToast();
const router = useRouter();
useEffect(() => {
postV1ResetOnboardingProgress()
@@ -17,7 +18,7 @@ export default function OnboardingResetPage() {
variant: "success",
});
redirect("/onboarding/1-welcome");
router.push("/onboarding");
})
.catch(() => {
toast({
@@ -26,7 +27,7 @@ export default function OnboardingResetPage() {
variant: "destructive",
});
});
}, []);
}, [toast, router]);
return <LoadingSpinner cover />;
}

View File

@@ -0,0 +1,66 @@
"use server";
import BackendAPI from "@/lib/autogpt-server-api";
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
import { verifyTurnstileToken } from "@/lib/turnstile";
import { environment } from "@/services/environment";
import { loginFormSchema } from "@/types/auth";
import * as Sentry from "@sentry/nextjs";
import { shouldShowOnboarding } from "../../api/helpers";
export async function login(
email: string,
password: string,
turnstileToken?: string,
) {
try {
const parsed = loginFormSchema.safeParse({ email, password });
if (!parsed.success) {
return {
success: false,
error: "Invalid email or password",
};
}
const captchaOk = await verifyTurnstileToken(turnstileToken ?? "", "login");
if (!captchaOk && !environment.isVercelPreview()) {
return {
success: false,
error: "CAPTCHA verification failed. Please try again.",
};
}
const supabase = await getServerSupabase();
if (!supabase) {
return {
success: false,
error: "Authentication service unavailable",
};
}
const { error } = await supabase.auth.signInWithPassword(parsed.data);
if (error) {
return {
success: false,
error: error.message,
};
}
const api = new BackendAPI();
await api.createUser();
const onboarding = await shouldShowOnboarding();
return {
success: true,
onboarding,
};
} catch (err) {
Sentry.captureException(err);
return {
success: false,
error: "Failed to login. Please try again.",
};
}
}

View File

@@ -1,4 +1,5 @@
"use client";
import { Form, FormField } from "@/components/__legacy__/ui/form";
import { Button } from "@/components/atoms/Button/Button";
import { Input } from "@/components/atoms/Input/Input";
import { Link } from "@/components/atoms/Link/Link";
@@ -7,10 +8,9 @@ import AuthFeedback from "@/components/auth/AuthFeedback";
import { EmailNotAllowedModal } from "@/components/auth/EmailNotAllowedModal";
import { GoogleOAuthButton } from "@/components/auth/GoogleOAuthButton";
import Turnstile from "@/components/auth/Turnstile";
import { Form, FormField } from "@/components/__legacy__/ui/form";
import { environment } from "@/services/environment";
import { LoadingLogin } from "./components/LoadingLogin";
import { useLoginPage } from "./useLoginPage";
import { environment } from "@/services/environment";
export default function LoginPage() {
const {
@@ -20,9 +20,9 @@ export default function LoginPage() {
turnstile,
captchaKey,
isLoading,
isGoogleLoading,
isCloudEnv,
isUserLoading,
isGoogleLoading,
showNotAllowedModal,
isSupabaseAvailable,
handleSubmit,
@@ -101,6 +101,7 @@ export default function LoginPage() {
<Button
variant="primary"
loading={isLoading}
disabled={isGoogleLoading}
type="submit"
className="mt-6 w-full"
>

View File

@@ -4,18 +4,17 @@ import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { environment } from "@/services/environment";
import { loginFormSchema, LoginProvider } from "@/types/auth";
import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useCallback, useState } from "react";
import { useForm } from "react-hook-form";
import z from "zod";
import { login as loginAction } from "./actions";
export function useLoginPage() {
const { supabase, user, isUserLoading } = useSupabase();
const [feedback, setFeedback] = useState<string | null>(null);
const [captchaKey, setCaptchaKey] = useState(0);
const router = useRouter();
const searchParams = useSearchParams();
const returnUrl = searchParams.get("returnUrl");
const { toast } = useToast();
const [isLoading, setIsLoading] = useState(false);
const [isGoogleLoading, setIsGoogleLoading] = useState(false);
@@ -42,10 +41,6 @@ export function useLoginPage() {
turnstile.reset();
}, [turnstile]);
useEffect(() => {
if (user) router.push("/");
}, [user]);
async function handleProviderLogin(provider: LoginProvider) {
setIsGoogleLoading(true);
@@ -69,14 +64,7 @@ export function useLoginPage() {
if (!response.ok) {
const { error } = await response.json();
if (error === "not_allowed") {
setShowNotAllowedModal(true);
} else {
setFeedback(error || "Failed to start OAuth flow");
}
resetCaptcha();
setIsGoogleLoading(false);
return;
throw new Error(error || "Failed to start OAuth flow");
}
const { url } = await response.json();
@@ -92,6 +80,7 @@ export function useLoginPage() {
async function handleLogin(data: z.infer<typeof loginFormSchema>) {
setIsLoading(true);
if (isCloudEnv && !turnstile.verified && !isVercelPreview) {
toast({
title: "Please complete the CAPTCHA challenge.",
@@ -115,39 +104,21 @@ export function useLoginPage() {
}
try {
const response = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: data.email,
password: data.password,
turnstileToken: turnstile.token,
}),
});
const result = await loginAction(
data.email,
data.password,
turnstile.token ?? undefined,
);
const result = await response.json();
if (!response.ok) {
toast({
title: result?.error || "Login failed",
variant: "destructive",
});
setIsLoading(false);
resetCaptcha();
turnstile.reset();
return;
if (!result.success) {
throw new Error(result.error || "Login failed");
}
await supabase?.auth.refreshSession();
setIsLoading(false);
setFeedback(null);
// Prioritize returnUrl from query params over backend's onboarding logic
const next = returnUrl
? returnUrl
: (result?.next as string) ||
(result?.onboarding ? "/onboarding" : "/");
if (next) router.push(next);
if (result.onboarding) {
router.replace("/onboarding");
} else {
router.replace("/marketplace");
}
} catch (error) {
toast({
title:
@@ -169,9 +140,9 @@ export function useLoginPage() {
captchaKey,
user,
isLoading,
isGoogleLoading,
isCloudEnv,
isUserLoading,
isGoogleLoading,
showNotAllowedModal,
isSupabaseAvailable: !!supabase,
handleSubmit: form.handleSubmit(handleLogin),

View File

@@ -0,0 +1,86 @@
"use server";
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
import { verifyTurnstileToken } from "@/lib/turnstile";
import { environment } from "@/services/environment";
import { signupFormSchema } from "@/types/auth";
import * as Sentry from "@sentry/nextjs";
import { isWaitlistError, logWaitlistError } from "../../api/auth/utils";
import { shouldShowOnboarding } from "../../api/helpers";
export async function signup(
email: string,
password: string,
confirmPassword: string,
agreeToTerms: boolean,
turnstileToken?: string,
) {
try {
const parsed = signupFormSchema.safeParse({
email,
password,
confirmPassword,
agreeToTerms,
});
if (!parsed.success) {
return {
success: false,
error: "Invalid signup payload",
};
}
const captchaOk = await verifyTurnstileToken(
turnstileToken ?? "",
"signup",
);
if (!captchaOk && !environment.isVercelPreview()) {
return {
success: false,
error: "CAPTCHA verification failed. Please try again.",
};
}
const supabase = await getServerSupabase();
if (!supabase) {
return {
success: false,
error: "Authentication service unavailable",
};
}
const { data, error } = await supabase.auth.signUp(parsed.data);
if (error) {
if (isWaitlistError(error?.code, error?.message)) {
logWaitlistError("Signup", error.message);
return { success: false, error: "not_allowed" };
}
if ((error as any).code === "user_already_exists") {
return { success: false, error: "user_already_exists" };
}
return {
success: false,
error: error.message,
};
}
if (data.session) {
await supabase.auth.setSession(data.session);
}
const isOnboardingEnabled = await shouldShowOnboarding();
const next = isOnboardingEnabled ? "/onboarding" : "/";
return { success: true, next };
} catch (err) {
Sentry.captureException(err);
return {
success: false,
error: "Failed to sign up. Please try again.",
};
}
}

View File

@@ -1,5 +1,13 @@
"use client";
import { Checkbox } from "@/components/__legacy__/ui/checkbox";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
} from "@/components/__legacy__/ui/form";
import { Button } from "@/components/atoms/Button/Button";
import { Input } from "@/components/atoms/Input/Input";
import { Link } from "@/components/atoms/Link/Link";
@@ -9,18 +17,10 @@ import AuthFeedback from "@/components/auth/AuthFeedback";
import { EmailNotAllowedModal } from "@/components/auth/EmailNotAllowedModal";
import { GoogleOAuthButton } from "@/components/auth/GoogleOAuthButton";
import Turnstile from "@/components/auth/Turnstile";
import { Checkbox } from "@/components/__legacy__/ui/checkbox";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
} from "@/components/__legacy__/ui/form";
import { environment } from "@/services/environment";
import { WarningOctagonIcon } from "@phosphor-icons/react/dist/ssr";
import { LoadingSignup } from "./components/LoadingSignup";
import { useSignupPage } from "./useSignupPage";
import { environment } from "@/services/environment";
export default function SignupPage() {
const {
@@ -30,9 +30,9 @@ export default function SignupPage() {
captchaKey,
isLoggedIn,
isLoading,
isGoogleLoading,
isCloudEnv,
isUserLoading,
isGoogleLoading,
showNotAllowedModal,
isSupabaseAvailable,
handleSubmit,
@@ -178,6 +178,7 @@ export default function SignupPage() {
<Button
variant="primary"
loading={isLoading}
disabled={isGoogleLoading}
type="submit"
className="mt-6 w-full"
>

View File

@@ -1,13 +1,14 @@
import { useToast } from "@/components/molecules/Toast/use-toast";
import { useTurnstile } from "@/hooks/useTurnstile";
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { environment } from "@/services/environment";
import { LoginProvider, signupFormSchema } from "@/types/auth";
import { zodResolver } from "@hookform/resolvers/zod";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useState } from "react";
import { useForm } from "react-hook-form";
import z from "zod";
import { useToast } from "@/components/molecules/Toast/use-toast";
import { environment } from "@/services/environment";
import { signup as signupAction } from "./actions";
export function useSignupPage() {
const { supabase, user, isUserLoading } = useSupabase();
@@ -18,7 +19,6 @@ export function useSignupPage() {
const [isLoading, setIsLoading] = useState(false);
const [isGoogleLoading, setIsGoogleLoading] = useState(false);
const [showNotAllowedModal, setShowNotAllowedModal] = useState(false);
const isCloudEnv = environment.isCloud();
const isVercelPreview = process.env.NEXT_PUBLIC_VERCEL_ENV === "preview";
@@ -43,10 +43,6 @@ export function useSignupPage() {
},
});
useEffect(() => {
if (user) router.push("/");
}, [user]);
async function handleProviderSignup(provider: LoginProvider) {
setIsGoogleLoading(true);
@@ -69,24 +65,17 @@ export function useSignupPage() {
if (!response.ok) {
const { error } = await response.json();
setIsGoogleLoading(false);
resetCaptcha();
if (error === "not_allowed") {
setShowNotAllowedModal(true);
return;
}
toast({
title: error || "Failed to start OAuth flow",
variant: "destructive",
});
return;
throw new Error(error || "Failed to start OAuth flow");
}
const { url } = await response.json();
if (url) window.location.href = url as string;
setFeedback(null);
} catch (error) {
setIsGoogleLoading(false);
resetCaptcha();
@@ -124,34 +113,29 @@ export function useSignupPage() {
}
try {
const response = await fetch("/api/auth/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: data.email,
password: data.password,
confirmPassword: data.confirmPassword,
agreeToTerms: data.agreeToTerms,
turnstileToken: turnstile.token,
}),
});
const result = await signupAction(
data.email,
data.password,
data.confirmPassword,
data.agreeToTerms,
turnstile.token ?? undefined,
);
const result = await response.json();
setIsLoading(false);
if (!response.ok) {
if (result?.error === "user_already_exists") {
if (!result.success) {
if (result.error === "user_already_exists") {
setFeedback("User with this email already exists");
turnstile.reset();
return;
}
if (result?.error === "not_allowed") {
if (result.error === "not_allowed") {
setShowNotAllowedModal(true);
return;
}
toast({
title: result?.error || "Signup failed",
title: result.error || "Signup failed",
variant: "destructive",
});
resetCaptcha();
@@ -159,9 +143,8 @@ export function useSignupPage() {
return;
}
setFeedback(null);
const next = (result?.next as string) || "/";
router.push(next);
const next = result.next || "/";
if (next) router.replace(next);
} catch (error) {
setIsLoading(false);
toast({
@@ -183,9 +166,9 @@ export function useSignupPage() {
captchaKey,
isLoggedIn: !!user,
isLoading,
isGoogleLoading,
isCloudEnv,
isUserLoading,
isGoogleLoading,
showNotAllowedModal,
isSupabaseAvailable: !!supabase,
handleSubmit: form.handleSubmit(handleSignup),

View File

@@ -1,67 +0,0 @@
import BackendAPI from "@/lib/autogpt-server-api";
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
import { verifyTurnstileToken } from "@/lib/turnstile";
import { environment } from "@/services/environment";
import { loginFormSchema } from "@/types/auth";
import * as Sentry from "@sentry/nextjs";
import { NextResponse } from "next/server";
import { shouldShowOnboarding } from "../../helpers";
export async function POST(request: Request) {
try {
const body = await request.json();
const parsed = loginFormSchema.safeParse({
email: body?.email,
password: body?.password,
});
if (!parsed.success) {
return NextResponse.json(
{ error: "Invalid email or password" },
{ status: 400 },
);
}
const turnstileToken: string | undefined = body?.turnstileToken;
// Verify Turnstile token if provided
const captchaOk = await verifyTurnstileToken(turnstileToken ?? "", "login");
if (!captchaOk && !environment.isVercelPreview()) {
return NextResponse.json(
{ error: "CAPTCHA verification failed. Please try again." },
{ status: 400 },
);
}
const supabase = await getServerSupabase();
if (!supabase) {
return NextResponse.json(
{ error: "Authentication service unavailable" },
{ status: 500 },
);
}
const { error } = await supabase.auth.signInWithPassword(parsed.data);
if (error) {
return NextResponse.json({ error: error.message }, { status: 400 });
}
const api = new BackendAPI();
await api.createUser();
const onboarding = await shouldShowOnboarding();
return NextResponse.json({
success: true,
onboarding,
next: onboarding ? "/onboarding" : "/",
});
} catch (err) {
Sentry.captureException(err);
return NextResponse.json(
{ error: "Failed to login. Please try again." },
{ status: 500 },
);
}
}

View File

@@ -1,83 +0,0 @@
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
import { verifyTurnstileToken } from "@/lib/turnstile";
import { environment } from "@/services/environment";
import { signupFormSchema } from "@/types/auth";
import * as Sentry from "@sentry/nextjs";
import { NextResponse } from "next/server";
import { shouldShowOnboarding } from "../../helpers";
import { isWaitlistError, logWaitlistError } from "../utils";
export async function POST(request: Request) {
try {
const body = await request.json();
const parsed = signupFormSchema.safeParse({
email: body?.email,
password: body?.password,
confirmPassword: body?.confirmPassword,
agreeToTerms: body?.agreeToTerms,
});
if (!parsed.success) {
return NextResponse.json(
{ error: "Invalid signup payload" },
{ status: 400 },
);
}
const turnstileToken: string | undefined = body?.turnstileToken;
const captchaOk = await verifyTurnstileToken(
turnstileToken ?? "",
"signup",
);
if (!captchaOk && !environment.isVercelPreview()) {
return NextResponse.json(
{ error: "CAPTCHA verification failed. Please try again." },
{ status: 400 },
);
}
const supabase = await getServerSupabase();
if (!supabase) {
return NextResponse.json(
{ error: "Authentication service unavailable" },
{ status: 500 },
);
}
const { data, error } = await supabase.auth.signUp(parsed.data);
if (error) {
if (isWaitlistError(error?.code, error?.message)) {
logWaitlistError("Signup", error.message);
return NextResponse.json({ error: "not_allowed" }, { status: 403 });
}
if ((error as any).code === "user_already_exists") {
return NextResponse.json(
{ error: "user_already_exists" },
{ status: 409 },
);
}
return NextResponse.json({ error: error.message }, { status: 400 });
}
if (data.session) {
await supabase.auth.setSession(data.session);
}
const isOnboardingEnabled = await shouldShowOnboarding();
const next = isOnboardingEnabled ? "/onboarding" : "/";
return NextResponse.json({ success: true, next });
} catch (err) {
Sentry.captureException(err);
return NextResponse.json(
{ error: "Failed to sign up. Please try again." },
{ status: 500 },
);
}
}

View File

@@ -1,5 +1,3 @@
"use client";
import { redirect } from "next/navigation";
export default function Page() {

View File

@@ -1,7 +1,7 @@
import { environment } from "@/services/environment";
import { Key, storage } from "@/services/storage/local-storage";
import { type CookieOptions } from "@supabase/ssr";
import { SupabaseClient } from "@supabase/supabase-js";
import { Key, storage } from "@/services/storage/local-storage";
import { environment } from "@/services/environment";
export const PROTECTED_PAGES = [
"/monitor",

View File

@@ -1,7 +1,7 @@
import { environment } from "@/services/environment";
import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
import { getCookieSettings, isAdminPage, isProtectedPage } from "./helpers";
import { environment } from "@/services/environment";
export async function updateSession(request: NextRequest) {
let supabaseResponse = NextResponse.next({