Fix admin login and CORS issues
- Update CORS configuration to allow frontend on port 3005 - Fix auth service to map email field to username for backend compatibility - Add loading state handling in AdminLayout - Add error boundary to dashboard route - Fix unused parameter warning in login function 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -21,7 +21,7 @@ const PORT = process.env.PORT || 3000;
|
|||||||
// Security middleware
|
// Security middleware
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: process.env.FRONTEND_URL || 'http://localhost:3001',
|
origin: process.env.FRONTEND_URL || 'http://localhost:3005',
|
||||||
credentials: true
|
credentials: true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ services:
|
|||||||
- NODE_ENV=development
|
- NODE_ENV=development
|
||||||
- PORT=3000
|
- PORT=3000
|
||||||
- JWT_SECRET=local-dev-secret-key-123
|
- JWT_SECRET=local-dev-secret-key-123
|
||||||
- ADMIN_URL=http://localhost:3001
|
- ADMIN_URL=http://localhost:3005
|
||||||
- FRONTEND_URL=http://localhost:3000
|
- FRONTEND_URL=http://localhost:3005
|
||||||
# Email - uses Mailhog
|
# Email - uses Mailhog
|
||||||
- SMTP_HOST=mailhog
|
- SMTP_HOST=mailhog
|
||||||
- SMTP_PORT=1025
|
- SMTP_PORT=1025
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
BrandingPage
|
BrandingPage
|
||||||
} from './pages/admin';
|
} from './pages/admin';
|
||||||
import { AdminLayout } from './components/admin';
|
import { AdminLayout } from './components/admin';
|
||||||
import { PageErrorBoundary, OfflineIndicator, SkipLink } from './components/common';
|
import { PageErrorBoundary, ErrorBoundary, OfflineIndicator, SkipLink } from './components/common';
|
||||||
|
|
||||||
// Create a client
|
// Create a client
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
@@ -68,7 +68,7 @@ function App() {
|
|||||||
<Routes>
|
<Routes>
|
||||||
<Route path="login" element={<AdminLoginPage />} />
|
<Route path="login" element={<AdminLoginPage />} />
|
||||||
<Route element={<AdminLayout />}>
|
<Route element={<AdminLayout />}>
|
||||||
<Route path="dashboard" element={<AdminDashboard />} />
|
<Route path="dashboard" element={<ErrorBoundary><AdminDashboard /></ErrorBoundary>} />
|
||||||
<Route path="events" element={<EventsListPage />} />
|
<Route path="events" element={<EventsListPage />} />
|
||||||
<Route path="events/new" element={<CreateEventPage />} />
|
<Route path="events/new" element={<CreateEventPage />} />
|
||||||
<Route path="events/:id" element={<EventDetailsPage />} />
|
<Route path="events/:id" element={<EventDetailsPage />} />
|
||||||
|
|||||||
@@ -6,9 +6,20 @@ import { AdminSidebar } from './AdminSidebar';
|
|||||||
import { AdminHeader } from './AdminHeader';
|
import { AdminHeader } from './AdminHeader';
|
||||||
|
|
||||||
export const AdminLayout: React.FC = () => {
|
export const AdminLayout: React.FC = () => {
|
||||||
const { isAuthenticated } = useAdminAuth();
|
const { isAuthenticated, isLoading } = useAdminAuth();
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-neutral-50 flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="w-16 h-16 border-4 border-primary-600 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||||||
|
<p className="text-neutral-600">Loading...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
return <Navigate to="/admin/login" replace />;
|
return <Navigate to="/admin/login" replace />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,15 +35,21 @@ export const AdminAuthProvider: React.FC<AdminAuthProviderProps> = ({ children }
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Check if user has a valid token on mount
|
// Check if user has a valid token on mount
|
||||||
const token = getAuthToken(true);
|
const checkAuth = async () => {
|
||||||
if (token) {
|
const token = getAuthToken(true);
|
||||||
// TODO: Validate token with backend and get user info
|
if (token) {
|
||||||
setIsAuthenticated(true);
|
// For now, just assume the token is valid
|
||||||
}
|
// TODO: Validate token with backend and get user info
|
||||||
setIsLoading(false);
|
setIsAuthenticated(true);
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkAuth();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const login = (_token: string, user: AdminUser) => {
|
const login = (_token: string, user: AdminUser) => {
|
||||||
|
// Token is already stored in cookie by authService
|
||||||
setUser(user);
|
setUser(user);
|
||||||
setIsAuthenticated(true);
|
setIsAuthenticated(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import type { LoginResponse, GalleryAuthResponse } from '../types';
|
|||||||
export const authService = {
|
export const authService = {
|
||||||
// Admin authentication
|
// Admin authentication
|
||||||
async adminLogin(credentials: { email: string; password: string }): Promise<LoginResponse> {
|
async adminLogin(credentials: { email: string; password: string }): Promise<LoginResponse> {
|
||||||
const response = await api.post<LoginResponse>('/api/auth/admin/login', credentials);
|
// Backend expects 'username' field, but we accept email
|
||||||
|
const response = await api.post<LoginResponse>('/api/auth/admin/login', {
|
||||||
|
username: credentials.email,
|
||||||
|
password: credentials.password
|
||||||
|
});
|
||||||
|
|
||||||
setAuthToken(response.data.token, true);
|
setAuthToken(response.data.token, true);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|||||||
Reference in New Issue
Block a user