mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
Merge branch 'dev' into swiftyos/connect-db-directly
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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 />;
|
||||
}
|
||||
|
||||
@@ -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 />;
|
||||
}
|
||||
|
||||
@@ -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.",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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.",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
"use client";
|
||||
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user