mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: make maintenance-related 503s more intuitive (#5018)
This makes maintenance-related 503s more intuitive on our UI by mentioning that maintenance banner is currently enabled. 
This commit is contained in:
		
							parent
							
								
									c41f23ae54
								
							
						
					
					
						commit
						6c21ed5f74
					
				@ -4,3 +4,4 @@ export const CREATED = 201;
 | 
				
			|||||||
export const NOT_FOUND = 404;
 | 
					export const NOT_FOUND = 404;
 | 
				
			||||||
export const FORBIDDEN = 403;
 | 
					export const FORBIDDEN = 403;
 | 
				
			||||||
export const UNAUTHORIZED = 401;
 | 
					export const UNAUTHORIZED = 401;
 | 
				
			||||||
 | 
					export const UNAVAILABLE = 503;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ import {
 | 
				
			|||||||
    NOT_FOUND,
 | 
					    NOT_FOUND,
 | 
				
			||||||
    OK,
 | 
					    OK,
 | 
				
			||||||
    UNAUTHORIZED,
 | 
					    UNAUTHORIZED,
 | 
				
			||||||
 | 
					    UNAVAILABLE,
 | 
				
			||||||
} from 'constants/statusCodes';
 | 
					} from 'constants/statusCodes';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    AuthenticationError,
 | 
					    AuthenticationError,
 | 
				
			||||||
@ -12,6 +13,7 @@ import {
 | 
				
			|||||||
    ForbiddenError,
 | 
					    ForbiddenError,
 | 
				
			||||||
    headers,
 | 
					    headers,
 | 
				
			||||||
    NotFoundError,
 | 
					    NotFoundError,
 | 
				
			||||||
 | 
					    UnavailableError,
 | 
				
			||||||
} from 'utils/apiUtils';
 | 
					} from 'utils/apiUtils';
 | 
				
			||||||
import { formatApiPath } from 'utils/formatPath';
 | 
					import { formatApiPath } from 'utils/formatPath';
 | 
				
			||||||
import { ACCESS_DENIED_TEXT } from 'utils/formatAccessText';
 | 
					import { ACCESS_DENIED_TEXT } from 'utils/formatAccessText';
 | 
				
			||||||
@ -27,6 +29,7 @@ interface IUseAPI {
 | 
				
			|||||||
    handleNotFound?: ApiErrorHandler;
 | 
					    handleNotFound?: ApiErrorHandler;
 | 
				
			||||||
    handleUnauthorized?: ApiErrorHandler;
 | 
					    handleUnauthorized?: ApiErrorHandler;
 | 
				
			||||||
    handleForbidden?: ApiErrorHandler;
 | 
					    handleForbidden?: ApiErrorHandler;
 | 
				
			||||||
 | 
					    handleUnavailable?: ApiErrorHandler;
 | 
				
			||||||
    propagateErrors?: boolean;
 | 
					    propagateErrors?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -35,6 +38,7 @@ const useAPI = ({
 | 
				
			|||||||
    handleNotFound,
 | 
					    handleNotFound,
 | 
				
			||||||
    handleForbidden,
 | 
					    handleForbidden,
 | 
				
			||||||
    handleUnauthorized,
 | 
					    handleUnauthorized,
 | 
				
			||||||
 | 
					    handleUnavailable,
 | 
				
			||||||
    propagateErrors = false,
 | 
					    propagateErrors = false,
 | 
				
			||||||
}: IUseAPI) => {
 | 
					}: IUseAPI) => {
 | 
				
			||||||
    const [errors, setErrors] = useState<Record<string, string>>({});
 | 
					    const [errors, setErrors] = useState<Record<string, string>>({});
 | 
				
			||||||
@ -104,6 +108,22 @@ const useAPI = ({
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (res.status === UNAVAILABLE) {
 | 
				
			||||||
 | 
					                if (handleUnavailable) {
 | 
				
			||||||
 | 
					                    return handleUnavailable(setErrors, res, requestId);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    setErrors((prev) => ({
 | 
				
			||||||
 | 
					                        ...prev,
 | 
				
			||||||
 | 
					                        unavailable: 'This operation is unavailable',
 | 
				
			||||||
 | 
					                    }));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (propagateErrors) {
 | 
				
			||||||
 | 
					                    const response = await res.json();
 | 
				
			||||||
 | 
					                    throw new UnavailableError(res.status, response);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (res.status > 399) {
 | 
					            if (res.status > 399) {
 | 
				
			||||||
                const response = await res.json();
 | 
					                const response = await res.json();
 | 
				
			||||||
                if (response?.details?.length > 0 && propagateErrors) {
 | 
					                if (response?.details?.length > 0 && propagateErrors) {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,12 +3,17 @@ import {
 | 
				
			|||||||
    FORBIDDEN,
 | 
					    FORBIDDEN,
 | 
				
			||||||
    NOT_FOUND,
 | 
					    NOT_FOUND,
 | 
				
			||||||
    UNAUTHORIZED,
 | 
					    UNAUTHORIZED,
 | 
				
			||||||
 | 
					    UNAVAILABLE,
 | 
				
			||||||
} from 'constants/statusCodes';
 | 
					} from 'constants/statusCodes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IErrorBody {
 | 
					export interface IErrorBody {
 | 
				
			||||||
 | 
					    message?: string;
 | 
				
			||||||
    details?: { message: string }[];
 | 
					    details?: { message: string }[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getErrorMessage = (body: IErrorBody) =>
 | 
				
			||||||
 | 
					    body.details?.[0]?.message || body.message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AuthenticationError extends Error {
 | 
					export class AuthenticationError extends Error {
 | 
				
			||||||
    statusCode: number;
 | 
					    statusCode: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,23 +29,31 @@ export class ForbiddenError extends Error {
 | 
				
			|||||||
    body: IErrorBody;
 | 
					    body: IErrorBody;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(statusCode: number = FORBIDDEN, body: IErrorBody = {}) {
 | 
					    constructor(statusCode: number = FORBIDDEN, body: IErrorBody = {}) {
 | 
				
			||||||
        super(
 | 
					        super(getErrorMessage(body) || 'You cannot perform this action');
 | 
				
			||||||
            body.details?.length
 | 
					 | 
				
			||||||
                ? body.details[0].message
 | 
					 | 
				
			||||||
                : 'You cannot perform this action',
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        this.name = 'ForbiddenError';
 | 
					        this.name = 'ForbiddenError';
 | 
				
			||||||
        this.statusCode = statusCode;
 | 
					        this.statusCode = statusCode;
 | 
				
			||||||
        this.body = body;
 | 
					        this.body = body;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UnavailableError extends Error {
 | 
				
			||||||
 | 
					    statusCode: number;
 | 
				
			||||||
 | 
					    body: IErrorBody;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(statusCode: number = UNAVAILABLE, body: IErrorBody = {}) {
 | 
				
			||||||
 | 
					        super(getErrorMessage(body) || 'This operation is unavailable');
 | 
				
			||||||
 | 
					        this.name = 'UnavailableError';
 | 
				
			||||||
 | 
					        this.statusCode = statusCode;
 | 
				
			||||||
 | 
					        this.body = body;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class BadRequestError extends Error {
 | 
					export class BadRequestError extends Error {
 | 
				
			||||||
    statusCode: number;
 | 
					    statusCode: number;
 | 
				
			||||||
    body: IErrorBody;
 | 
					    body: IErrorBody;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(statusCode: number = BAD_REQUEST, body: IErrorBody = {}) {
 | 
					    constructor(statusCode: number = BAD_REQUEST, body: IErrorBody = {}) {
 | 
				
			||||||
        super(body.details?.length ? body.details[0].message : 'Bad request');
 | 
					        super(getErrorMessage(body) || 'Bad request');
 | 
				
			||||||
        this.name = 'BadRequestError';
 | 
					        this.name = 'BadRequestError';
 | 
				
			||||||
        this.statusCode = statusCode;
 | 
					        this.statusCode = statusCode;
 | 
				
			||||||
        this.body = body;
 | 
					        this.body = body;
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,9 @@ import { IUnleashConfig } from '../types';
 | 
				
			|||||||
import MaintenanceService from '../services/maintenance-service';
 | 
					import MaintenanceService from '../services/maintenance-service';
 | 
				
			||||||
import { IAuthRequest } from '../routes/unleash-types';
 | 
					import { IAuthRequest } from '../routes/unleash-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MAINTENANCE_MODE_ENABLED =
 | 
				
			||||||
 | 
					    'Unleash is currently in maintenance mode.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const maintenanceMiddleware = (
 | 
					const maintenanceMiddleware = (
 | 
				
			||||||
    { getLogger }: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
 | 
					    { getLogger }: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
 | 
				
			||||||
    maintenanceService: MaintenanceService,
 | 
					    maintenanceService: MaintenanceService,
 | 
				
			||||||
@ -17,7 +20,9 @@ const maintenanceMiddleware = (
 | 
				
			|||||||
            writeMethod &&
 | 
					            writeMethod &&
 | 
				
			||||||
            (await maintenanceService.isMaintenanceMode())
 | 
					            (await maintenanceService.isMaintenanceMode())
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            res.status(503).send({});
 | 
					            res.status(503).send({
 | 
				
			||||||
 | 
					                message: MAINTENANCE_MODE_ENABLED,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            next();
 | 
					            next();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user