Spaces:
Running
Running
Deploy Aureon OS (deploy/selfhost stack: nginx edge + fusion API x2 + console + HNC organ + redis + postgres; paper mode) (part 18)
f568a4f verified Download frontend/src/components/AuthForm.tsx from GaryLeckey/aureon-os: direct link, hf CLI and curl.
- Browser
- Download file 10.5 kB
-
https://huggingface.co/spaces/GaryLeckey/aureon-os/resolve/main/frontend/src/components/AuthForm.tsx
- Command line
-
hf download hf://spaces/GaryLeckey/aureon-os/frontend/src/components/AuthForm.tsx
-
curl -L -o AuthForm.tsx https://huggingface.co/spaces/GaryLeckey/aureon-os/resolve/main/frontend/src/components/AuthForm.tsx
10.5 kB
| import { useState } from "react"; | |
| import { supabase } from "@/integrations/supabase/client"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Input } from "@/components/ui/input"; | |
| import { Label } from "@/components/ui/label"; | |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; | |
| import { toast } from "sonner"; | |
| import { z } from "zod"; | |
| import { Sparkles, Eye, EyeOff } from "lucide-react"; | |
| const emailSchema = z.string().email("Invalid email address"); | |
| const passwordSchema = z.string().min(8, "Password must be at least 8 characters"); | |
| interface AuthFormProps { | |
| onSuccess: () => void; | |
| } | |
| export function AuthForm({ onSuccess }: AuthFormProps) { | |
| const [loading, setLoading] = useState(false); | |
| const [showPassword, setShowPassword] = useState(false); | |
| // Sign In State | |
| const [email, setEmail] = useState(""); | |
| const [password, setPassword] = useState(""); | |
| // Sign Up State | |
| const [signUpEmail, setSignUpEmail] = useState(""); | |
| const [signUpPassword, setSignUpPassword] = useState(""); | |
| const [confirmPassword, setConfirmPassword] = useState(""); | |
| const [binanceApiKey, setBinanceApiKey] = useState(""); | |
| const [binanceApiSecret, setBinanceApiSecret] = useState(""); | |
| const [showApiSecret, setShowApiSecret] = useState(false); | |
| const handleSignUp = async (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| try { | |
| emailSchema.parse(signUpEmail); | |
| passwordSchema.parse(signUpPassword); | |
| if (signUpPassword !== confirmPassword) { | |
| toast.error("Passwords do not match"); | |
| return; | |
| } | |
| setLoading(true); | |
| const { data: authData, error: authError } = await supabase.auth.signUp({ | |
| email: signUpEmail, | |
| password: signUpPassword, | |
| options: { | |
| emailRedirectTo: `${window.location.origin}/`, | |
| } | |
| }); | |
| if (authError) throw authError; | |
| if (!authData.user) throw new Error("Failed to create account"); | |
| const { data: sessionData, error: sessionError } = await supabase.functions.invoke('create-aureon-session', { | |
| body: { | |
| userId: authData.user.id, | |
| apiKey: binanceApiKey || null, | |
| apiSecret: binanceApiSecret || null, | |
| }, | |
| }); | |
| if (sessionError || !sessionData?.success) { | |
| throw new Error(sessionError?.message || sessionData?.error || 'Session creation failed'); | |
| } | |
| toast.success("Account created! Welcome."); | |
| onSuccess(); | |
| } catch (error) { | |
| if (error instanceof z.ZodError) { | |
| toast.error(error.errors[0].message); | |
| } else if (error instanceof Error) { | |
| toast.error(error.message); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handleSignIn = async (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| try { | |
| emailSchema.parse(email); | |
| passwordSchema.parse(password); | |
| setLoading(true); | |
| const { error } = await supabase.auth.signInWithPassword({ | |
| email, | |
| password | |
| }); | |
| if (error) throw error; | |
| toast.success("Welcome back!"); | |
| onSuccess(); | |
| } catch (error) { | |
| if (error instanceof z.ZodError) { | |
| toast.error(error.errors[0].message); | |
| } else if (error instanceof Error) { | |
| toast.error(error.message); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return ( | |
| <Card className="w-full max-w-md mx-auto border-border/50"> | |
| <CardHeader className="text-center space-y-2"> | |
| <div className="flex justify-center"> | |
| <div className="h-12 w-12 rounded-xl bg-primary flex items-center justify-center"> | |
| <Sparkles className="h-6 w-6 text-primary-foreground" /> | |
| </div> | |
| </div> | |
| <CardTitle className="text-2xl font-bold">AUREON</CardTitle> | |
| <CardDescription>Sign in to access trade feeds</CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <Tabs defaultValue="signin" className="w-full"> | |
| <TabsList className="grid w-full grid-cols-2 mb-4"> | |
| <TabsTrigger value="signin">Sign In</TabsTrigger> | |
| <TabsTrigger value="signup">Sign Up</TabsTrigger> | |
| </TabsList> | |
| <TabsContent value="signin"> | |
| <form onSubmit={handleSignIn} className="space-y-4"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="signin-email">Email</Label> | |
| <Input | |
| id="signin-email" | |
| type="email" | |
| placeholder="your@email.com" | |
| value={email} | |
| onChange={(e) => setEmail(e.target.value)} | |
| required | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="signin-password">Password</Label> | |
| <div className="relative"> | |
| <Input | |
| id="signin-password" | |
| type={showPassword ? "text" : "password"} | |
| placeholder="••••••••" | |
| value={password} | |
| onChange={(e) => setPassword(e.target.value)} | |
| required | |
| /> | |
| <button | |
| type="button" | |
| onClick={() => setShowPassword(!showPassword)} | |
| className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground" | |
| > | |
| {showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />} | |
| </button> | |
| </div> | |
| </div> | |
| <Button type="submit" className="w-full" disabled={loading}> | |
| {loading ? "Signing in..." : "Sign In"} | |
| </Button> | |
| </form> | |
| </TabsContent> | |
| <TabsContent value="signup"> | |
| <form onSubmit={handleSignUp} className="space-y-4"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="signup-email">Email</Label> | |
| <Input | |
| id="signup-email" | |
| type="email" | |
| placeholder="your@email.com" | |
| value={signUpEmail} | |
| onChange={(e) => setSignUpEmail(e.target.value)} | |
| required | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="signup-password">Password</Label> | |
| <div className="relative"> | |
| <Input | |
| id="signup-password" | |
| type={showPassword ? "text" : "password"} | |
| placeholder="Min. 8 characters" | |
| value={signUpPassword} | |
| onChange={(e) => setSignUpPassword(e.target.value)} | |
| required | |
| /> | |
| <button | |
| type="button" | |
| onClick={() => setShowPassword(!showPassword)} | |
| className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground" | |
| > | |
| {showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />} | |
| </button> | |
| </div> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="confirm-password">Confirm Password</Label> | |
| <Input | |
| id="confirm-password" | |
| type={showPassword ? "text" : "password"} | |
| placeholder="Re-enter password" | |
| value={confirmPassword} | |
| onChange={(e) => setConfirmPassword(e.target.value)} | |
| required | |
| className={confirmPassword && signUpPassword !== confirmPassword ? "border-destructive" : ""} | |
| /> | |
| {confirmPassword && signUpPassword !== confirmPassword && ( | |
| <p className="text-xs text-destructive">Passwords do not match</p> | |
| )} | |
| </div> | |
| <div className="border-t border-border/50 pt-4 mt-4"> | |
| <p className="text-xs text-muted-foreground mb-3"> | |
| Binance API (optional - add later in Settings) | |
| </p> | |
| <div className="space-y-3"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="binance-api-key">API Key</Label> | |
| <Input | |
| id="binance-api-key" | |
| type="text" | |
| placeholder="Your Binance API Key" | |
| value={binanceApiKey} | |
| onChange={(e) => setBinanceApiKey(e.target.value)} | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="binance-api-secret">API Secret</Label> | |
| <div className="relative"> | |
| <Input | |
| id="binance-api-secret" | |
| type={showApiSecret ? "text" : "password"} | |
| placeholder="Your Binance API Secret" | |
| value={binanceApiSecret} | |
| onChange={(e) => setBinanceApiSecret(e.target.value)} | |
| /> | |
| <button | |
| type="button" | |
| onClick={() => setShowApiSecret(!showApiSecret)} | |
| className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground" | |
| > | |
| {showApiSecret ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />} | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <Button type="submit" className="w-full" disabled={loading}> | |
| {loading ? "Creating account..." : "Sign Up"} | |
| </Button> | |
| </form> | |
| </TabsContent> | |
| </Tabs> | |
| </CardContent> | |
| </Card> | |
| ); | |
| } | |