fix(maintenance): enabling maintenance mode no longer locks admins out
Turning on maintenance mode locked out every admin — including ones already logged in — with no way back in from the browser. Two causes: 1. Backend (middleware/maintenance.js): the skipPaths allow-list pointed at /api/admin/login and /api/admin/auth/login, but the real admin auth routes live under /api/auth (POST /api/auth/admin/login, GET /api/auth/session). So during maintenance both the login POST and the session check 503'd. The 503 on /auth/session made the frontend read every admin as logged-out, and also tripped the axios interceptor that force-enables maintenance globally. Fixed the allow-list to the actual endpoints. 2. Frontend (MaintenanceWrapper.tsx): the maintenance screen rendered over every /admin/* route unless an admin session already existed — covering the /admin/login page itself. A logged-out admin could never reach the form to get a session (catch-22). /admin/login is now always allowed through. With both: a logged-in admin keeps working (session check passes), and a logged-out admin can reach /admin/login and sign back in, all while maintenance mode correctly blocks customers.
This commit is contained in:
@@ -64,10 +64,15 @@ async function checkMaintenanceMode() {
|
|||||||
|
|
||||||
// Middleware to enforce maintenance mode
|
// Middleware to enforce maintenance mode
|
||||||
async function maintenanceMiddleware(req, res, next) {
|
async function maintenanceMiddleware(req, res, next) {
|
||||||
// Skip maintenance check for certain paths
|
// Skip maintenance check for certain paths. Admin auth MUST work during
|
||||||
|
// maintenance — otherwise enabling it locks every admin out, including
|
||||||
|
// already-logged-in ones (their /auth/session check would 503 and read as
|
||||||
|
// logged-out). These are the REAL endpoints: the admin login + session
|
||||||
|
// routes live under /api/auth, NOT /api/admin (the old /api/admin/login
|
||||||
|
// entries here matched nothing, which is exactly why the lockout happened).
|
||||||
const skipPaths = [
|
const skipPaths = [
|
||||||
'/api/admin/login',
|
'/api/auth/admin/login',
|
||||||
'/api/admin/auth/login',
|
'/api/auth/session',
|
||||||
'/api/public/settings',
|
'/api/public/settings',
|
||||||
'/health'
|
'/health'
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ export const MaintenanceWrapper: React.FC<MaintenanceWrapperProps> = ({ children
|
|||||||
const [hasAdminSession, setHasAdminSession] = useState(false);
|
const [hasAdminSession, setHasAdminSession] = useState(false);
|
||||||
|
|
||||||
const isAdminRoute = location.pathname.startsWith('/admin');
|
const isAdminRoute = location.pathname.startsWith('/admin');
|
||||||
|
// The admin login page must ALWAYS render during maintenance — it's how an
|
||||||
|
// admin gets a session to bypass it. Without this exemption a logged-out
|
||||||
|
// admin sees the maintenance screen over the login form (catch-22: needs a
|
||||||
|
// session to get past maintenance, but the login page that grants one is
|
||||||
|
// hidden).
|
||||||
|
const isAdminLoginRoute = location.pathname.startsWith('/admin/login');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
@@ -54,7 +60,7 @@ export const MaintenanceWrapper: React.FC<MaintenanceWrapperProps> = ({ children
|
|||||||
});
|
});
|
||||||
}, [setMaintenanceMode]);
|
}, [setMaintenanceMode]);
|
||||||
|
|
||||||
if (isMaintenanceMode && (!isAdminRoute || !hasAdminSession)) {
|
if (isMaintenanceMode && !isAdminLoginRoute && (!isAdminRoute || !hasAdminSession)) {
|
||||||
return <MaintenanceMode />;
|
return <MaintenanceMode />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user