diff --git a/frontend/src/component/maintenance/Maintenance.tsx b/frontend/src/component/maintenance/Maintenance.tsx
index 2fbc0337aa..862daa96a5 100644
--- a/frontend/src/component/maintenance/Maintenance.tsx
+++ b/frontend/src/component/maintenance/Maintenance.tsx
@@ -25,8 +25,9 @@ const Maintenance = () => {
Maintenance Mode!
- Any changes you make during maintenance mode will not be saved.
- We apologize for any inconvenience this may cause.
+ During maintenance mode, any changes made will not be saved and
+ you may receive an error. We apologize for any inconvenience
+ this may cause.
);
diff --git a/frontend/src/hooks/api/getters/useAuth/useAuthPermissions.ts b/frontend/src/hooks/api/getters/useAuth/useAuthPermissions.ts
index 084a113034..47b4849b16 100644
--- a/frontend/src/hooks/api/getters/useAuth/useAuthPermissions.ts
+++ b/frontend/src/hooks/api/getters/useAuth/useAuthPermissions.ts
@@ -15,9 +15,14 @@ const getPermissions = (
uiConfig: IUiConfig
): IPermission[] | undefined => {
let permissions =
- auth.data && 'permissions' in auth.data && !uiConfig?.flags?.maintenance
+ auth.data && 'permissions' in auth.data
? auth.data.permissions
: undefined;
+ if (permissions && uiConfig?.flags?.maintenance) {
+ permissions = permissions.filter(
+ permission => permission.permission === 'ADMIN'
+ );
+ }
return permissions;
};
diff --git a/src/lib/app.ts b/src/lib/app.ts
index 0c39f246f9..aab8048495 100644
--- a/src/lib/app.ts
+++ b/src/lib/app.ts
@@ -25,6 +25,7 @@ import { findPublicFolder } from './util/findPublicFolder';
import { conditionalMiddleware } from './middleware/conditional-middleware';
import patMiddleware from './middleware/pat-middleware';
import { Knex } from 'knex';
+import maintenanceMiddleware from './middleware/maintenance-middleware';
export default async function getApp(
config: IUnleashConfig,
@@ -138,6 +139,8 @@ export default async function getApp(
rbacMiddleware(config, stores, services.accessService),
);
+ app.use('/api/admin', maintenanceMiddleware(config));
+
if (typeof config.preRouterHook === 'function') {
config.preRouterHook(app, config, services, stores, db);
}
diff --git a/src/lib/middleware/maintenance-middleware.ts b/src/lib/middleware/maintenance-middleware.ts
new file mode 100644
index 0000000000..595aee9bbb
--- /dev/null
+++ b/src/lib/middleware/maintenance-middleware.ts
@@ -0,0 +1,21 @@
+import { IUnleashConfig } from '../types';
+import { Request } from 'express';
+
+const maintenanceMiddleware = ({
+ getLogger,
+ flagResolver,
+}: Pick): any => {
+ const logger = getLogger('/middleware/maintenance-middleware.ts');
+ logger.debug('Enabling Maintenance middleware');
+
+ return async (req: Request, res, next) => {
+ const writeMethod = ['POST', 'PUT', 'DELETE'].includes(req.method);
+ if (writeMethod && flagResolver.isEnabled('maintenance')) {
+ res.status(503).send({});
+ } else {
+ next();
+ }
+ };
+};
+
+export default maintenanceMiddleware;
diff --git a/src/lib/routes/admin-api/public-signup.test.ts b/src/lib/routes/admin-api/public-signup.test.ts
index 945f36711e..8462330180 100644
--- a/src/lib/routes/admin-api/public-signup.test.ts
+++ b/src/lib/routes/admin-api/public-signup.test.ts
@@ -14,11 +14,6 @@ describe('Public Signup API', () => {
preRouterHook: perms.hook,
});
- config.flagResolver = {
- isEnabled: jest.fn().mockResolvedValue(true),
- getAll: jest.fn(),
- };
-
stores.accessStore = {
...stores.accessStore,
addUserToRole: jest.fn(),
diff --git a/src/lib/routes/public-invite.test.ts b/src/lib/routes/public-invite.test.ts
index 1e4412394f..e7015194e6 100644
--- a/src/lib/routes/public-invite.test.ts
+++ b/src/lib/routes/public-invite.test.ts
@@ -14,11 +14,6 @@ describe('Public Signup API', () => {
preRouterHook: perms.hook,
});
- config.flagResolver = {
- isEnabled: jest.fn().mockResolvedValue(true),
- getAll: jest.fn(),
- };
-
stores.accessStore = {
...stores.accessStore,
addUserToRole: jest.fn(),
diff --git a/src/test/e2e/api/admin/feature.e2e.test.ts b/src/test/e2e/api/admin/feature.e2e.test.ts
index c31e73fd3f..0d2a62e1b1 100644
--- a/src/test/e2e/api/admin/feature.e2e.test.ts
+++ b/src/test/e2e/api/admin/feature.e2e.test.ts
@@ -837,3 +837,21 @@ test('should have access to the get all features endpoint even if api is disable
.get('/api/admin/features')
.expect(200);
});
+
+test('should not allow creation of feature toggle in maintenance mode', async () => {
+ const appWithMaintenanceMode = await setupAppWithCustomConfig(db.stores, {
+ experimental: {
+ flags: {
+ maintenance: true,
+ },
+ },
+ });
+
+ return appWithMaintenanceMode.request
+ .post('/api/admin/features')
+ .send({
+ name: 'maintenance-feature',
+ })
+ .set('Content-Type', 'application/json')
+ .expect(503);
+});
diff --git a/src/test/e2e/api/admin/public-signup-token.e2e.test.ts b/src/test/e2e/api/admin/public-signup-token.e2e.test.ts
index b0b274301e..27ce35c89e 100644
--- a/src/test/e2e/api/admin/public-signup-token.e2e.test.ts
+++ b/src/test/e2e/api/admin/public-signup-token.e2e.test.ts
@@ -7,15 +7,6 @@ import { PublicSignupTokenCreateSchema } from '../../../../lib/openapi/spec/publ
let stores;
let db;
-jest.mock('../../../../lib/util/flag-resolver', () => {
- return jest.fn().mockImplementation(() => {
- return {
- getAll: jest.fn(),
- isEnabled: jest.fn().mockResolvedValue(true),
- };
- });
-});
-
beforeEach(async () => {
db = await dbInit('test', getLogger);
stores = db.stores;