diff --git a/EnvExample.txt b/EnvExample.txt new file mode 100644 index 0000000..4cafc9f --- /dev/null +++ b/EnvExample.txt @@ -0,0 +1,10 @@ +EMAIL_USER=Your App Email +EMAIL_PASSWORD=Your Google App Password +NEXT_PUBLIC_GA_ID=Your Google Analytics ID + +NEXT_PUBLIC_SUPABASE_URL=Your supabase Url +NEXT_PUBLIC_SUPABASE_ANON_KEY=Your Anon Key +NEXT_PUBLIC_TURNSTILE_SITE_KEY=Your Cloudfare Captcha Key + +TURNSTILE_SECRET_KEY=Your Cloudfare backend route api key +SUPABASE_SERVICE_KEY=Your supabase service key \ No newline at end of file diff --git a/app/api/auth/route.js b/app/api/auth/route.js new file mode 100644 index 0000000..6633ff2 --- /dev/null +++ b/app/api/auth/route.js @@ -0,0 +1,52 @@ +import { createClient } from '@supabase/supabase-js' + +const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL, + process.env.SUPABASE_SERVICE_KEY +) + +export async function POST(req) { + try { + const { email, password, captchaToken, action } = await req.json() + + if (!email || !password) + return new Response(JSON.stringify({ message: 'Email and password required' }), { status: 400 }) + + if (action === 'signup') { + if (!captchaToken) + return new Response(JSON.stringify({ message: 'Captcha token missing' }), { status: 400 }) + + // Verify Turnstile token + const verifyRes = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + secret: process.env.TURNSTILE_SECRET_KEY, + response: captchaToken, + }), + }) + + const data = await verifyRes.json() + if (!data.success) + return new Response(JSON.stringify({ message: 'Captcha verification failed' }), { status: 400 }) + + // Create Supabase user + const { user, error } = await supabase.auth.admin.createUser({ email, password }) + if (error) + return new Response(JSON.stringify({ message: error.message }), { status: 400 }) + + return new Response(JSON.stringify({ message: 'Signup successful! Check your email.' }), { status: 200 }) + } + + else if (action === 'login') { + return new Response(JSON.stringify({ message: 'Use frontend login with anon key' }), { status: 400 }) + } + + else { + return new Response(JSON.stringify({ message: 'Invalid action' }), { status: 400 }) + } + } catch (err) { + console.error(err) + return new Response(JSON.stringify({ message: 'Internal server error' }), { status: 500 }) + } +} \ No newline at end of file diff --git a/app/login/page.jsx b/app/login/page.jsx index 76213ce..ac91dde 100644 --- a/app/login/page.jsx +++ b/app/login/page.jsx @@ -5,12 +5,12 @@ import { useRouter } from 'next/navigation' import { FiMail, FiLock, FiUser, FiLogIn, FiUserPlus, FiSun, FiMoon } from 'react-icons/fi' import { motion } from 'framer-motion' import Link from 'next/link' -import dynamic from "next/dynamic"; +import dynamic from 'next/dynamic' const Turnstile = dynamic( - () => import("@marsidev/react-turnstile").then((mod) => mod.Turnstile), + () => import('@marsidev/react-turnstile').then((mod) => mod.Turnstile), { ssr: false } -); +) export default function LoginPage() { const [email, setEmail] = useState('') @@ -38,17 +38,28 @@ export default function LoginPage() { const handleAuth = async () => { setLoading(true) setError('') - + try { if (isLogin) { + // Login with frontend anon key const { error } = await supabase.auth.signInWithPassword({ email, password }) if (error) throw error router.push('/dashboard') } else { + // Signup via API route with Turnstile if (!captchaToken) throw new Error('Please complete captcha') - const { error } = await supabase.auth.signUp({ email, password, options: { captchaToken } }) - if (error) throw error - alert('Check your email for confirmation!') + + const res = await fetch('/auth', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password, captchaToken, action: 'signup' }), + }) + + const data = await res.json() + if (!res.ok) throw new Error(data.message) + + alert(data.message) + setIsLogin(true) // switch to login after signup } } catch (err) { setError(err.message) @@ -57,21 +68,19 @@ export default function LoginPage() { } } - const handleGoogleSignIn = async () => { - const { error } = await supabase.auth.signInWithOAuth({ - provider: 'google', - }) - if (error) console.error('Error signing in with Google:', error.message) + const handleGoogleSignIn = async () => { + const { error } = await supabase.auth.signInWithOAuth({ provider: 'google' }) + if (error) console.error('Google sign-in error:', error.message) } return ( -
{error}
+
Don't have an account?{' '}
-
Already have an account?{' '}
-