mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Maintenance mode middleware (#2707)
This commit is contained in:
		
							parent
							
								
									ff9bc1d10c
								
							
						
					
					
						commit
						2d5455d203
					
				@ -25,8 +25,9 @@ const Maintenance = () => {
 | 
				
			|||||||
            <StyledErrorRoundedIcon />
 | 
					            <StyledErrorRoundedIcon />
 | 
				
			||||||
            <b>Maintenance Mode! </b>
 | 
					            <b>Maintenance Mode! </b>
 | 
				
			||||||
            <p>
 | 
					            <p>
 | 
				
			||||||
                Any changes you make during maintenance mode will not be saved.
 | 
					                During maintenance mode, any changes made will not be saved and
 | 
				
			||||||
                We apologize for any inconvenience this may cause.
 | 
					                you may receive an error. We apologize for any inconvenience
 | 
				
			||||||
 | 
					                this may cause.
 | 
				
			||||||
            </p>
 | 
					            </p>
 | 
				
			||||||
        </StyledDiv>
 | 
					        </StyledDiv>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
				
			|||||||
@ -15,9 +15,14 @@ const getPermissions = (
 | 
				
			|||||||
    uiConfig: IUiConfig
 | 
					    uiConfig: IUiConfig
 | 
				
			||||||
): IPermission[] | undefined => {
 | 
					): IPermission[] | undefined => {
 | 
				
			||||||
    let permissions =
 | 
					    let permissions =
 | 
				
			||||||
        auth.data && 'permissions' in auth.data && !uiConfig?.flags?.maintenance
 | 
					        auth.data && 'permissions' in auth.data
 | 
				
			||||||
            ? auth.data.permissions
 | 
					            ? auth.data.permissions
 | 
				
			||||||
            : undefined;
 | 
					            : undefined;
 | 
				
			||||||
 | 
					    if (permissions && uiConfig?.flags?.maintenance) {
 | 
				
			||||||
 | 
					        permissions = permissions.filter(
 | 
				
			||||||
 | 
					            permission => permission.permission === 'ADMIN'
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return permissions;
 | 
					    return permissions;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ import { findPublicFolder } from './util/findPublicFolder';
 | 
				
			|||||||
import { conditionalMiddleware } from './middleware/conditional-middleware';
 | 
					import { conditionalMiddleware } from './middleware/conditional-middleware';
 | 
				
			||||||
import patMiddleware from './middleware/pat-middleware';
 | 
					import patMiddleware from './middleware/pat-middleware';
 | 
				
			||||||
import { Knex } from 'knex';
 | 
					import { Knex } from 'knex';
 | 
				
			||||||
 | 
					import maintenanceMiddleware from './middleware/maintenance-middleware';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function getApp(
 | 
					export default async function getApp(
 | 
				
			||||||
    config: IUnleashConfig,
 | 
					    config: IUnleashConfig,
 | 
				
			||||||
@ -138,6 +139,8 @@ export default async function getApp(
 | 
				
			|||||||
        rbacMiddleware(config, stores, services.accessService),
 | 
					        rbacMiddleware(config, stores, services.accessService),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app.use('/api/admin', maintenanceMiddleware(config));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (typeof config.preRouterHook === 'function') {
 | 
					    if (typeof config.preRouterHook === 'function') {
 | 
				
			||||||
        config.preRouterHook(app, config, services, stores, db);
 | 
					        config.preRouterHook(app, config, services, stores, db);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										21
									
								
								src/lib/middleware/maintenance-middleware.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/lib/middleware/maintenance-middleware.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					import { IUnleashConfig } from '../types';
 | 
				
			||||||
 | 
					import { Request } from 'express';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const maintenanceMiddleware = ({
 | 
				
			||||||
 | 
					    getLogger,
 | 
				
			||||||
 | 
					    flagResolver,
 | 
				
			||||||
 | 
					}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>): 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;
 | 
				
			||||||
@ -14,11 +14,6 @@ describe('Public Signup API', () => {
 | 
				
			|||||||
            preRouterHook: perms.hook,
 | 
					            preRouterHook: perms.hook,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        config.flagResolver = {
 | 
					 | 
				
			||||||
            isEnabled: jest.fn().mockResolvedValue(true),
 | 
					 | 
				
			||||||
            getAll: jest.fn(),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        stores.accessStore = {
 | 
					        stores.accessStore = {
 | 
				
			||||||
            ...stores.accessStore,
 | 
					            ...stores.accessStore,
 | 
				
			||||||
            addUserToRole: jest.fn(),
 | 
					            addUserToRole: jest.fn(),
 | 
				
			||||||
 | 
				
			|||||||
@ -14,11 +14,6 @@ describe('Public Signup API', () => {
 | 
				
			|||||||
            preRouterHook: perms.hook,
 | 
					            preRouterHook: perms.hook,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        config.flagResolver = {
 | 
					 | 
				
			||||||
            isEnabled: jest.fn().mockResolvedValue(true),
 | 
					 | 
				
			||||||
            getAll: jest.fn(),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        stores.accessStore = {
 | 
					        stores.accessStore = {
 | 
				
			||||||
            ...stores.accessStore,
 | 
					            ...stores.accessStore,
 | 
				
			||||||
            addUserToRole: jest.fn(),
 | 
					            addUserToRole: jest.fn(),
 | 
				
			||||||
 | 
				
			|||||||
@ -837,3 +837,21 @@ test('should have access to the get all features endpoint even if api is disable
 | 
				
			|||||||
        .get('/api/admin/features')
 | 
					        .get('/api/admin/features')
 | 
				
			||||||
        .expect(200);
 | 
					        .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);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -7,15 +7,6 @@ import { PublicSignupTokenCreateSchema } from '../../../../lib/openapi/spec/publ
 | 
				
			|||||||
let stores;
 | 
					let stores;
 | 
				
			||||||
let db;
 | 
					let db;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jest.mock('../../../../lib/util/flag-resolver', () => {
 | 
					 | 
				
			||||||
    return jest.fn().mockImplementation(() => {
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            getAll: jest.fn(),
 | 
					 | 
				
			||||||
            isEnabled: jest.fn().mockResolvedValue(true),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
beforeEach(async () => {
 | 
					beforeEach(async () => {
 | 
				
			||||||
    db = await dbInit('test', getLogger);
 | 
					    db = await dbInit('test', getLogger);
 | 
				
			||||||
    stores = db.stores;
 | 
					    stores = db.stores;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user