yamiclaws's picture
Upload folder using huggingface_hub
935f504 verified
Raw
History Blame Contribute Delete
9.68 kB
import { useState, useEffect, useCallback, useRef } from "react"
import "./App.css"
const API = window.location.hostname === "localhost" ? "http://localhost:8000" : ""
function App() {
const [page, setPage] = useState("login")
const [token, setToken] = useState(localStorage.getItem("access_token"))
const [user, setUser] = useState(null)
const [form, setForm] = useState({ username: "", email: "", password: "" })
const [otp, setOtp] = useState("")
const [msg, setMsg] = useState("")
const hasCheckedAuth = useRef(false)
const refreshAccessToken = useCallback(async () => {
const refreshToken = localStorage.getItem("refresh_token")
if (!refreshToken) { logout(); return null }
try {
const res = await fetch(`${API}/auth/refresh`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token: refreshToken })
})
if (res.ok) {
const data = await res.json()
localStorage.setItem("access_token", data.access_token)
localStorage.setItem("refresh_token", data.refresh_token)
setToken(data.access_token)
return data.access_token
} else { logout(); return null }
} catch { logout(); return null }
}, [])
const apiCall = useCallback(async (url, options = {}) => {
let currentToken = localStorage.getItem("access_token")
let res = await fetch(url, {
...options,
headers: { "Content-Type": "application/json", ...options.headers, Authorization: `Bearer ${currentToken}` }
})
if (res.status === 401) {
const newToken = await refreshAccessToken()
if (newToken) {
res = await fetch(url, {
...options,
headers: { "Content-Type": "application/json", ...options.headers, Authorization: `Bearer ${newToken}` }
})
}
}
return res
}, [refreshAccessToken])
function navigateTo(newPage) {
setPage(newPage)
setMsg("")
setOtp("")
}
function resetAll() {
setPage("login")
setMsg("")
setOtp("")
setForm({ username: "", email: "", password: "" })
}
useEffect(() => {
if (!hasCheckedAuth.current) { hasCheckedAuth.current = true; if (token) getMe() }
}, [])
async function getMe() {
const res = await apiCall(`${API}/me`)
if (res.ok) { setUser(await res.json()); navigateTo("dashboard") }
else logout()
}
async function register() {
try {
if (!/^[a-zA-Z0-9_]{3,20}$/.test(form.username)) { setMsg("Username: 3-20 chars, letters/numbers/_ only"); return }
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) { setMsg("Invalid email format"); return }
if (form.password.length < 8) { setMsg("Password must be at least 8 characters"); return }
const res = await fetch(`${API}/auth/register`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: form.username, email: form.email, password: form.password })
})
const data = await res.json()
if (res.ok) {
setPage("verify-register")
setOtp("")
setMsg("OTP sent to your email. Enter it to verify.")
} else {
setMsg(data.detail || "Registration failed")
}
} catch (err) {
setMsg("Network error. Is the backend running?")
}
}
async function verifyRegister() {
try {
if (otp.length !== 6) { setMsg("Enter the 6-digit OTP"); return }
const res = await fetch(`${API}/auth/verify-email`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: form.email, code: otp })
})
const data = await res.json()
if (res.ok) {
setMsg("Account verified! Now login")
navigateTo("login")
} else {
setMsg(data.detail || "Verification failed")
}
} catch (err) {
setMsg("Network error. Is the backend running?")
}
}
async function login() {
try {
const body = new URLSearchParams()
body.append("username", form.username)
body.append("password", form.password)
const res = await fetch(`${API}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: body.toString()
})
const data = await res.json()
if (res.ok) {
localStorage.setItem("access_token", data.access_token)
localStorage.setItem("refresh_token", data.refresh_token)
setToken(data.access_token)
const meRes = await apiCall(`${API}/me`)
if (meRes.ok) setUser(await meRes.json())
navigateTo("dashboard")
} else {
setMsg(data.detail || "Login failed")
}
} catch (err) {
setMsg("Network error. Is the backend running?")
}
}
function logout() {
localStorage.removeItem("access_token")
localStorage.removeItem("refresh_token")
setToken(null); setUser(null); resetAll()
}
async function forgotPassword() {
try {
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) { setMsg("Enter a valid email"); return }
const res = await fetch(`${API}/auth/forgot-password`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: form.email })
})
const data = await res.json()
if (res.ok) {
setPage("verify-forgot")
setOtp("")
setMsg("OTP sent to your email")
} else {
setMsg(data.detail || "Failed to send OTP")
}
} catch (err) {
setMsg("Network error. Is the backend running?")
}
}
async function resetPassword() {
try {
if (otp.length !== 6) { setMsg("Enter the 6-digit OTP"); return }
if (form.password.length < 8) { setMsg("Password must be at least 8 characters"); return }
const res = await fetch(`${API}/auth/reset-password`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: form.email, code: otp, new_password: form.password })
})
const data = await res.json()
if (res.ok) {
setMsg("Password reset successful!")
navigateTo("login")
} else {
setMsg(data.detail || "Reset failed")
}
} catch (err) {
setMsg("Network error. Is the backend running?")
}
}
return (
<div className="container">
{page === "login" && (
<div className="card">
<h2>Login</h2>
{msg && <p className="msg">{msg}</p>}
<input placeholder="Username or Email" value={form.username} onChange={e => setForm({ ...form, username: e.target.value })} />
<input type="password" placeholder="Password" value={form.password} onChange={e => setForm({ ...form, password: e.target.value })} />
<button onClick={login}>Login</button>
<p className="link" onClick={() => navigateTo("register")}>No account? Register</p>
<p className="link" onClick={() => navigateTo("forgot-password")}>Forgot password?</p>
</div>
)}
{page === "register" && (
<div className="card">
<h2>Register</h2>
{msg && <p className="msg">{msg}</p>}
<input placeholder="Username" value={form.username} onChange={e => setForm({ ...form, username: e.target.value })} />
<input type="email" placeholder="Email" value={form.email} onChange={e => setForm({ ...form, email: e.target.value })} />
<input type="password" placeholder="Password (min 8 chars)" value={form.password} onChange={e => setForm({ ...form, password: e.target.value })} />
<button onClick={register}>Register</button>
<p className="link" onClick={() => navigateTo("login")}>Have account? Login</p>
</div>
)}
{page === "verify-register" && (
<div className="card">
<h2>Verify Account</h2>
{msg && <p className="msg">{msg}</p>}
<p className="info">OTP sent to: {form.email || "your email"}</p>
<input placeholder="Enter 6-digit OTP" value={otp} onChange={e => setOtp(e.target.value)} />
<button onClick={verifyRegister}>Verify</button>
</div>
)}
{page === "forgot-password" && (
<div className="card">
<h2>Forgot Password</h2>
{msg && <p className="msg">{msg}</p>}
<input type="email" placeholder="Email" value={form.email} onChange={e => setForm({ ...form, email: e.target.value })} />
<button onClick={forgotPassword}>Send OTP</button>
<p className="link" onClick={() => navigateTo("login")}>Back to Login</p>
</div>
)}
{page === "verify-forgot" && (
<div className="card">
<h2>Reset Password</h2>
{msg && <p className="msg">{msg}</p>}
<p className="info">OTP sent to: {form.email || "your email"}</p>
<input placeholder="Enter 6-digit OTP" value={otp} onChange={e => setOtp(e.target.value)} />
<input type="password" placeholder="New Password (min 8 chars)" value={form.password} onChange={e => setForm({ ...form, password: e.target.value })} />
<button onClick={resetPassword}>Reset Password</button>
<p className="link" onClick={() => navigateTo("forgot-password")}>Back</p>
</div>
)}
{page === "dashboard" && (
<div className="card">
<h2>Dashboard</h2>
<p>ID: {user?.id}</p>
<p>Username: {user?.username}</p>
<p>Email: {user?.email}</p>
<button onClick={logout}>Logout</button>
</div>
)}
</div>
)
}
export default App