diff --git a/components/AdminProtected.js b/components/AdminProtected.js index 2b85f1b..6e66036 100644 --- a/components/AdminProtected.js +++ b/components/AdminProtected.js @@ -14,7 +14,19 @@ export default function AdminProtected({ children }) { const checkAdminAccess = async () => { try { - const response = await fetch('/api/auth/verify'); + // Get token from localStorage + const token = localStorage.getItem('auth_token'); + + const headers = { + 'Content-Type': 'application/json', + }; + + // Add authorization header if token exists + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const response = await fetch('/api/auth/verify', { headers }); if (response.ok) { const userData = await response.json(); if (userData.role === 'admin') { @@ -25,6 +37,10 @@ export default function AdminProtected({ children }) { } } else { setAccessDenied(true); + // Clear invalid token + if (token) { + localStorage.removeItem('auth_token'); + } } } catch (error) { console.error('Admin auth check failed:', error); diff --git a/lib/admin-auth.js b/lib/admin-auth.js index 23f9e78..4a90b65 100644 --- a/lib/admin-auth.js +++ b/lib/admin-auth.js @@ -13,12 +13,28 @@ export function AdminProvider({ children }) { const checkAdminAuth = async () => { try { - const response = await fetch('/api/auth/verify'); + // Get token from localStorage + const token = localStorage.getItem('auth_token'); + + const headers = { + 'Content-Type': 'application/json', + }; + + // Add authorization header if token exists + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const response = await fetch('/api/auth/verify', { headers }); if (response.ok) { const userData = await response.json(); setUser(userData); } else { setUser(null); + // Clear invalid token + if (token) { + localStorage.removeItem('auth_token'); + } } } catch (error) { console.error('Auth check failed:', error); @@ -67,12 +83,28 @@ export function useIsAdmin() { useEffect(() => { const checkAdmin = async () => { try { - const response = await fetch('/api/auth/verify'); + // Get token from localStorage + const token = localStorage.getItem('auth_token'); + + const headers = { + 'Content-Type': 'application/json', + }; + + // Add authorization header if token exists + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const response = await fetch('/api/auth/verify', { headers }); if (response.ok) { const userData = await response.json(); setIsAdmin(userData.role === 'admin'); } else { setIsAdmin(false); + // Clear invalid token + if (token) { + localStorage.removeItem('auth_token'); + } } } catch (error) { setIsAdmin(false); diff --git a/pages/api/auth/verify.js b/pages/api/auth/verify.js index 87ba4b8..174c6b7 100644 --- a/pages/api/auth/verify.js +++ b/pages/api/auth/verify.js @@ -1,4 +1,7 @@ -import { verifyToken, getUserById } from '../auth-utils.js'; +import { sql } from '@vercel/postgres'; +import jwt from 'jsonwebtoken'; + +const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production'; export default async function handler(req, res) { // Set CORS headers @@ -17,20 +20,50 @@ export default async function handler(req, res) { } try { - // For development purposes, return mock admin user - // In production, implement proper JWT/session verification - const mockAdminUser = { - id: 1, - email: 'admin@tcgvault.com', - role: 'admin', - name: 'Admin User', - created_at: new Date().toISOString() - }; + const authHeader = req.headers.authorization; + + if (!authHeader || !authHeader.startsWith('Bearer ')) { + // For development, return admin user if no token provided + // In production, this should return 401 + const result = await sql` + SELECT id, email, role, created_at + FROM users + WHERE email = 'admin@tcgvault.com' + `; - res.status(200).json(mockAdminUser); + if (result.rows.length > 0) { + return res.status(200).json(result.rows[0]); + } else { + return res.status(401).json({ error: 'No admin user found' }); + } + } + + const token = authHeader.substring(7); + + try { + const decoded = jwt.verify(token, JWT_SECRET); + + // Get user data from database + const result = await sql` + SELECT id, email, role, created_at + FROM users + WHERE id = ${decoded.userId} + `; + + if (result.rows.length === 0) { + return res.status(401).json({ error: 'User not found' }); + } + + const user = result.rows[0]; + res.status(200).json(user); + + } catch (jwtError) { + console.error('JWT verification error:', jwtError); + return res.status(401).json({ error: 'Invalid token' }); + } } catch (error) { - console.error('Token verification error:', error); + console.error('Auth verification error:', error); res.status(500).json({ error: 'Internal server error' }); } } \ No newline at end of file diff --git a/pages/login.js b/pages/login.js index 03f2027..e29b10b 100644 --- a/pages/login.js +++ b/pages/login.js @@ -1,132 +1,208 @@ import { useState } from 'react'; import { useRouter } from 'next/router'; -import { useAuth } from '../lib/auth-context.js'; +import Layout from '../components/Layout'; export default function Login() { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [loading, setLoading] = useState(false); - const [isRegister, setIsRegister] = useState(false); - - const { login, register } = useAuth(); const router = useRouter(); + const [formData, setFormData] = useState({ + email: '', + password: '' + }); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + const handleInputChange = (field, value) => { + setFormData(prev => ({ + ...prev, + [field]: value + })); + // Clear error when user starts typing + if (error) setError(''); + }; const handleSubmit = async (e) => { e.preventDefault(); - setError(''); setLoading(true); + setError(''); try { - const result = isRegister - ? await register(email, password) - : await login(email, password); + const response = await fetch('/api/auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData) + }); - if (result.success) { - router.push('/dashboard'); + const data = await response.json(); + + if (response.ok) { + // Store the JWT token in localStorage + localStorage.setItem('auth_token', data.token); + + // Redirect to admin panel or dashboard + if (data.user.role === 'admin') { + router.push('/admin/card-editor'); + } else { + router.push('/dashboard'); + } } else { - setError(result.error); + setError(data.error || 'Login failed'); } } catch (error) { - setError('An unexpected error occurred'); + console.error('Login error:', error); + setError('Network error. Please try again.'); } finally { setLoading(false); } }; + const handleQuickAdminLogin = () => { + setFormData({ + email: 'admin@tcgvault.com', + password: 'admin123' + }); + }; + return ( -
-
-

- {isRegister ? 'Create your account' : 'Sign in to your account'} -

-

- {isRegister ? 'Already have an account?' : "Don't have an account?"}{' '} - -

-
+ +
+
+
+
+
🃏
+

+ Sign in to TCG Vault +

+

+ Access your trading card collection +

+
+
-
-
-
- {error && ( -
- {error} -
- )} +
+ + {error && ( +
+

{error}

+
+ )} -
- -
+
+ setEmail(e.target.value)} - className="input-field" + value={formData.email} + onChange={(e) => handleInputChange('email', e.target.value)} + className="w-full px-4 py-3 rounded-lg border transition-all duration-200 focus:ring-2 focus:ring-purple-500 focus:border-transparent" + style={{ + backgroundColor: 'var(--bg-primary)', + borderColor: 'var(--border)', + color: 'var(--text-primary)' + }} placeholder="Enter your email" />
-
-
- -
+
+ setPassword(e.target.value)} - className="input-field" + value={formData.password} + onChange={(e) => handleInputChange('password', e.target.value)} + className="w-full px-4 py-3 rounded-lg border transition-all duration-200 focus:ring-2 focus:ring-purple-500 focus:border-transparent" + style={{ + backgroundColor: 'var(--bg-primary)', + borderColor: 'var(--border)', + color: 'var(--text-primary)' + }} placeholder="Enter your password" />
-
-
- -
- +
+ +
- {!isRegister && ( -
-
-
-
-
-
- Demo Account + {/* Quick Admin Login for Development */} +
+
+

+ Development Quick Login: +

+
-
-

Email: admin@tcgvault.com

-

Password: admin123

+ + +
+

+ Don't have an account?{' '} + +

+
+
+ + {/* Admin Info Card */} +
+
+

+ 🛡️ Admin Access +

+
+

Email: admin@tcgvault.com

+

Password: admin123

- )} +
-
+ ); } \ No newline at end of file diff --git a/pages/logout.js b/pages/logout.js index 5d44669..21f1c7c 100644 --- a/pages/logout.js +++ b/pages/logout.js @@ -1,20 +1,36 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; +import Layout from '../components/Layout'; export default function Logout() { const router = useRouter(); useEffect(() => { - // In a real app, you would clear auth tokens here - router.push('/login'); + // Clear the auth token + localStorage.removeItem('auth_token'); + + // Redirect to login after a short delay + setTimeout(() => { + router.push('/login'); + }, 2000); }, [router]); return ( -
-
-
-

Logging out...

+ +
+
+
👋
+

+ Signing you out... +

+

+ You will be redirected to the login page shortly. +

+
+
+
+
-
+ ); } \ No newline at end of file