Inital build
Some checks failed
Automated Container Build / build-and-push (push) Failing after 12s

This commit is contained in:
Elijah 2026-05-22 12:29:43 -07:00
parent fa2be029a2
commit 724d70e58b
3339 changed files with 1075535 additions and 0 deletions

View file

@ -0,0 +1,221 @@
'use client';
import { useState } from 'react';
import { motion } from 'framer-motion';
import { HardDrive, Lock, User, LogIn, Shield } from 'lucide-react';
import api from '@/lib/api';
interface LoginPageProps {
onSuccess: () => void;
}
export default function LoginPage({ onSuccess }: LoginPageProps) {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [totpCode, setTotpCode] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const [needs2FA, setNeeds2FA] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError('');
setLoading(true);
try {
const { ok, status, data } = await api.login(username, password, needs2FA ? totpCode : undefined);
if (ok) {
onSuccess();
} else if (status === 428 && data.totp_required) {
setNeeds2FA(true);
} else {
setError(data.error || 'Login failed');
}
} catch {
setError('Connection failed. Is the server running?');
} finally {
setLoading(false);
}
}
return (
<div
className="flex items-center justify-center min-h-screen p-4"
style={{ backgroundColor: 'var(--color-bg-primary)' }}
>
{/* Subtle background gradient orbs */}
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div
className="absolute -top-40 -right-40 w-96 h-96 rounded-full opacity-20 blur-3xl"
style={{ background: 'radial-gradient(circle, var(--color-accent), transparent)' }}
/>
<div
className="absolute -bottom-40 -left-40 w-96 h-96 rounded-full opacity-10 blur-3xl"
style={{ background: 'radial-gradient(circle, #8b5cf6, transparent)' }}
/>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: 'easeOut' }}
className="w-full max-w-md relative z-10"
>
{/* Logo & Title */}
<div className="text-center mb-8">
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.2, type: 'spring', stiffness: 200 }}
className="inline-flex items-center justify-center w-16 h-16 rounded-2xl mb-4"
style={{
background: 'linear-gradient(135deg, var(--color-accent), #8b5cf6)',
boxShadow: 'var(--shadow-glow)',
}}
>
<HardDrive className="w-8 h-8 text-white" />
</motion.div>
<h1 className="text-2xl font-bold" style={{ color: 'var(--color-text-primary)' }}>
Welcome Back
</h1>
<p className="mt-2 text-sm" style={{ color: 'var(--color-text-secondary)' }}>
Sign in to access your files
</p>
</div>
{/* Login Card */}
<div
className="rounded-2xl p-8"
style={{
backgroundColor: 'var(--color-bg-elevated)',
border: '1px solid var(--color-border)',
boxShadow: 'var(--shadow-lg)',
backdropFilter: 'blur(20px)',
}}
>
<form onSubmit={handleSubmit} className="space-y-4">
{!needs2FA ? (
<>
<label className="block">
<span className="text-xs font-medium uppercase tracking-wider" style={{ color: 'var(--color-text-secondary)' }}>
Username
</span>
<div className="relative mt-1.5">
<User className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4" style={{ color: 'var(--color-text-tertiary)' }} />
<input
id="login-username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Your username"
autoFocus
className="w-full pl-10 pr-4 py-3 rounded-xl text-sm outline-none transition-all"
style={{
backgroundColor: 'var(--color-bg-tertiary)',
border: '1px solid var(--color-border)',
color: 'var(--color-text-primary)',
}}
/>
</div>
</label>
<label className="block">
<span className="text-xs font-medium uppercase tracking-wider" style={{ color: 'var(--color-text-secondary)' }}>
Password
</span>
<div className="relative mt-1.5">
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4" style={{ color: 'var(--color-text-tertiary)' }} />
<input
id="login-password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Your password"
className="w-full pl-10 pr-4 py-3 rounded-xl text-sm outline-none transition-all"
style={{
backgroundColor: 'var(--color-bg-tertiary)',
border: '1px solid var(--color-border)',
color: 'var(--color-text-primary)',
}}
/>
</div>
</label>
</>
) : (
<motion.div initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }}>
<div
className="flex items-center gap-2 p-3 rounded-lg mb-4"
style={{
backgroundColor: 'var(--color-accent-subtle)',
border: '1px solid var(--color-accent)',
}}
>
<Shield className="w-4 h-4" style={{ color: 'var(--color-accent)' }} />
<span className="text-xs" style={{ color: 'var(--color-accent)' }}>
Two-factor authentication required
</span>
</div>
<label className="block">
<span className="text-xs font-medium uppercase tracking-wider" style={{ color: 'var(--color-text-secondary)' }}>
Authentication Code
</span>
<input
id="login-totp"
type="text"
inputMode="numeric"
maxLength={6}
value={totpCode}
onChange={(e) => setTotpCode(e.target.value.replace(/\D/g, ''))}
placeholder="000000"
autoFocus
className="w-full mt-1.5 px-4 py-3 rounded-xl text-sm text-center tracking-[0.5em] font-mono outline-none transition-all"
style={{
backgroundColor: 'var(--color-bg-tertiary)',
border: '1px solid var(--color-border)',
color: 'var(--color-text-primary)',
}}
/>
</label>
</motion.div>
)}
{error && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-sm px-3 py-2 rounded-lg"
style={{
color: 'var(--color-danger)',
backgroundColor: 'rgba(239, 68, 68, 0.1)',
}}
>
{error}
</motion.p>
)}
<button
id="login-submit"
type="submit"
disabled={loading}
className="w-full mt-2 py-3 rounded-xl text-sm font-semibold text-white flex items-center justify-center gap-2 transition-all duration-200 hover:opacity-90 disabled:opacity-50"
style={{
background: 'linear-gradient(135deg, var(--color-accent), #8b5cf6)',
boxShadow: 'var(--shadow-glow)',
}}
>
{loading ? (
<div className="w-5 h-5 rounded-full border-2 border-white border-t-transparent" style={{ animation: 'spin 0.8s linear infinite' }} />
) : (
<>
<LogIn className="w-4 h-4" />
{needs2FA ? 'Verify' : 'Sign In'}
</>
)}
</button>
</form>
</div>
</motion.div>
</div>
);
}