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 />
 | 
			
		||||
            <b>Maintenance Mode! </b>
 | 
			
		||||
            <p>
 | 
			
		||||
                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.
 | 
			
		||||
            </p>
 | 
			
		||||
        </StyledDiv>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        config.flagResolver = {
 | 
			
		||||
            isEnabled: jest.fn().mockResolvedValue(true),
 | 
			
		||||
            getAll: jest.fn(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        stores.accessStore = {
 | 
			
		||||
            ...stores.accessStore,
 | 
			
		||||
            addUserToRole: jest.fn(),
 | 
			
		||||
 | 
			
		||||
@ -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(),
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user