fix: login redirect, admin auth guard, JWT_SECRET env
This commit is contained in:
+46
-1
@@ -1481,6 +1481,8 @@ export default function AdminPage() {
|
|||||||
const [activeSection, setActiveSection] = useState('dashboard');
|
const [activeSection, setActiveSection] = useState('dashboard');
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
const [toasts, setToasts] = useState<{ id: number; message: string; type: string }[]>([]);
|
const [toasts, setToasts] = useState<{ id: number; message: string; type: string }[]>([]);
|
||||||
|
const [authChecked, setAuthChecked] = useState(false);
|
||||||
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
|
||||||
const [slides, setSlides] = useState<Slide[]>([]);
|
const [slides, setSlides] = useState<Slide[]>([]);
|
||||||
const [rooms, setRooms] = useState<Room[]>([]);
|
const [rooms, setRooms] = useState<Room[]>([]);
|
||||||
@@ -1504,8 +1506,20 @@ export default function AdminPage() {
|
|||||||
setToasts(prev => prev.filter(t => t.id !== id));
|
setToasts(prev => prev.filter(t => t.id !== id));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
/* ── Auth check ── */
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/auth/me', { credentials: 'include' })
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.authenticated) setIsAuthenticated(true);
|
||||||
|
setAuthChecked(true);
|
||||||
|
})
|
||||||
|
.catch(() => setAuthChecked(true));
|
||||||
|
}, []);
|
||||||
|
|
||||||
/* ── Data fetching ── */
|
/* ── Data fetching ── */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isAuthenticated) return;
|
||||||
const fetchAll = async () => {
|
const fetchAll = async () => {
|
||||||
try {
|
try {
|
||||||
const [slidesRes, roomsRes, calRes, socialRes, menuRes, placesRes, reviewsRes, imagesRes] = await Promise.all([
|
const [slidesRes, roomsRes, calRes, socialRes, menuRes, placesRes, reviewsRes, imagesRes] = await Promise.all([
|
||||||
@@ -1529,7 +1543,7 @@ export default function AdminPage() {
|
|||||||
} catch (err) { console.error('Failed to load admin data:', err); }
|
} catch (err) { console.error('Failed to load admin data:', err); }
|
||||||
};
|
};
|
||||||
fetchAll();
|
fetchAll();
|
||||||
}, []);
|
}, [isAuthenticated]);
|
||||||
|
|
||||||
const deleteImage = async (id: number) => {
|
const deleteImage = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
@@ -1567,6 +1581,35 @@ export default function AdminPage() {
|
|||||||
{/* Toasts */}
|
{/* Toasts */}
|
||||||
{toasts.map(t => <Toast key={t.id} message={t.message} type={t.type} onClose={() => removeToast(t.id)} />)}
|
{toasts.map(t => <Toast key={t.id} message={t.message} type={t.type} onClose={() => removeToast(t.id)} />)}
|
||||||
|
|
||||||
|
{/* Auth loading */}
|
||||||
|
{!authChecked && (
|
||||||
|
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<div style={{ fontSize: '32px', marginBottom: '16px' }}>⏳</div>
|
||||||
|
<div style={{ fontSize: '16px', color: C.text }}>Checking authentication...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Not authenticated - show login prompt */}
|
||||||
|
{authChecked && !isAuthenticated && (
|
||||||
|
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
<div style={{ textAlign: 'center', maxWidth: 400, padding: '40px', background: '#fff', borderRadius: 16, boxShadow: C.shadow }}>
|
||||||
|
<div style={{ fontSize: '48px', marginBottom: '16px' }}>🔒</div>
|
||||||
|
<h2 style={{ margin: '0 0 12px', fontSize: '24px', color: C.navy }}>Admin Access Required</h2>
|
||||||
|
<p style={{ margin: '0 0 24px', fontSize: '14px', color: C.textLight }}>
|
||||||
|
Please log in to access the admin panel.
|
||||||
|
</p>
|
||||||
|
<a href="/login" style={{ display: 'inline-block', padding: '12px 32px', background: C.gold, color: '#fff', borderRadius: 8, textDecoration: 'none', fontWeight: 600, fontSize: '15px' }}>
|
||||||
|
Go to Login
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Authenticated - show admin panel */}
|
||||||
|
{authChecked && isAuthenticated && (
|
||||||
|
<>
|
||||||
{/* Mobile sidebar toggle */}
|
{/* Mobile sidebar toggle */}
|
||||||
<div style={{ display: 'none' }} /> {/* placeholder */}
|
<div style={{ display: 'none' }} /> {/* placeholder */}
|
||||||
|
|
||||||
@@ -1608,6 +1651,8 @@ export default function AdminPage() {
|
|||||||
{renderSection()}
|
{renderSection()}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-20
@@ -6,29 +6,14 @@ import type { NextRequest } from 'next/server';
|
|||||||
// In a production environment, you would want to verify the token
|
// In a production environment, you would want to verify the token
|
||||||
export function middleware(request: NextRequest) {
|
export function middleware(request: NextRequest) {
|
||||||
const { pathname } = request.nextUrl;
|
const { pathname } = request.nextUrl;
|
||||||
|
|
||||||
// Protect admin routes
|
|
||||||
if (pathname.startsWith('/admin')) {
|
|
||||||
// Get session token from cookies
|
|
||||||
const token = request.cookies.get('session')?.value;
|
const token = request.cookies.get('session')?.value;
|
||||||
|
|
||||||
// If no token, redirect to login
|
// Protect admin routes - let page handle auth UI
|
||||||
if (!token) {
|
// No redirect here; the admin page shows login prompt if not authenticated
|
||||||
return NextResponse.redirect(new URL('/login', request.url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: In a real implementation, you would verify the token here
|
// If logged in and trying to access login, redirect to admin
|
||||||
// For now, we're just checking that a token exists
|
if (pathname === '/login' && token) {
|
||||||
}
|
return NextResponse.redirect(new URL('/admin', request.url));
|
||||||
|
|
||||||
// Redirect authenticated users from login page to home
|
|
||||||
if (pathname === '/login') {
|
|
||||||
const token = request.cookies.get('session')?.value;
|
|
||||||
if (token) {
|
|
||||||
// In a real implementation, you would verify the token and check role
|
|
||||||
// For now, we'll just redirect to home
|
|
||||||
return NextResponse.redirect(new URL('/', request.url));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
|
|||||||
Reference in New Issue
Block a user