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
|
||||
app.use(helmet());
|
||||
app.use(cors({
|
||||
origin: process.env.FRONTEND_URL || 'http://localhost:3001',
|
||||
origin: process.env.FRONTEND_URL || 'http://localhost:3005',
|
||||
credentials: true
|
||||
}));
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ services:
|
||||
- NODE_ENV=development
|
||||
- PORT=3000
|
||||
- JWT_SECRET=local-dev-secret-key-123
|
||||
- ADMIN_URL=http://localhost:3001
|
||||
- FRONTEND_URL=http://localhost:3000
|
||||
- ADMIN_URL=http://localhost:3005
|
||||
- FRONTEND_URL=http://localhost:3005
|
||||
# Email - uses Mailhog
|
||||
- SMTP_HOST=mailhog
|
||||
- SMTP_PORT=1025
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
BrandingPage
|
||||
} from './pages/admin';
|
||||
import { AdminLayout } from './components/admin';
|
||||
import { PageErrorBoundary, OfflineIndicator, SkipLink } from './components/common';
|
||||
import { PageErrorBoundary, ErrorBoundary, OfflineIndicator, SkipLink } from './components/common';
|
||||
|
||||
// Create a client
|
||||
const queryClient = new QueryClient({
|
||||
@@ -68,7 +68,7 @@ function App() {
|
||||
<Routes>
|
||||
<Route path="login" element={<AdminLoginPage />} />
|
||||
<Route element={<AdminLayout />}>
|
||||
<Route path="dashboard" element={<AdminDashboard />} />
|
||||
<Route path="dashboard" element={<ErrorBoundary><AdminDashboard /></ErrorBoundary>} />
|
||||
<Route path="events" element={<EventsListPage />} />
|
||||
<Route path="events/new" element={<CreateEventPage />} />
|
||||
<Route path="events/:id" element={<EventDetailsPage />} />
|
||||
|
||||
@@ -6,9 +6,20 @@ import { AdminSidebar } from './AdminSidebar';
|
||||
import { AdminHeader } from './AdminHeader';
|
||||
|
||||
export const AdminLayout: React.FC = () => {
|
||||
const { isAuthenticated } = useAdminAuth();
|
||||
const { isAuthenticated, isLoading } = useAdminAuth();
|
||||
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) {
|
||||
return <Navigate to="/admin/login" replace />;
|
||||
}
|
||||
|
||||
@@ -35,15 +35,21 @@ export const AdminAuthProvider: React.FC<AdminAuthProviderProps> = ({ children }
|
||||
|
||||
useEffect(() => {
|
||||
// Check if user has a valid token on mount
|
||||
const token = getAuthToken(true);
|
||||
if (token) {
|
||||
// TODO: Validate token with backend and get user info
|
||||
setIsAuthenticated(true);
|
||||
}
|
||||
setIsLoading(false);
|
||||
const checkAuth = async () => {
|
||||
const token = getAuthToken(true);
|
||||
if (token) {
|
||||
// For now, just assume the token is valid
|
||||
// TODO: Validate token with backend and get user info
|
||||
setIsAuthenticated(true);
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
checkAuth();
|
||||
}, []);
|
||||
|
||||
const login = (_token: string, user: AdminUser) => {
|
||||
// Token is already stored in cookie by authService
|
||||
setUser(user);
|
||||
setIsAuthenticated(true);
|
||||
setError(null);
|
||||
|
||||
@@ -4,7 +4,11 @@ import type { LoginResponse, GalleryAuthResponse } from '../types';
|
||||
export const authService = {
|
||||
// Admin authentication
|
||||
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);
|
||||
return response.data;
|
||||
|
||||
Reference in New Issue
Block a user