protected function maintenanceMode($force = false, $sTitle = '', $sBody = '')
    {
        if ($force || file_exists(NAILS_APP_PATH . '.MAINTENANCE')) {

            /**
             * We're in maintenance mode. This can happen very early so we need to
             * make sure that we've loaded everything we need to load as we're
             * exiting whether we like it or not
             */

            $oInput = Factory::service('Input');
            $oUri   = Factory::service('Uri');

            try {

                //  Load the encryption service. Set the package path so it is loaded correctly
                //  (this runs early, before the paths are added)
                $this->load->add_package_path(NAILS_COMMON_PATH);
                Factory::service('encrypt');

                $whitelistIp   = (array) appSetting('maintenance_mode_whitelist', 'site');
                $isWhiteListed = isIpInRange($oInput->ipAddress(), $whitelistIp);

                //  Customisations
                $sMaintenanceTitle = $sTitle ? $sTitle : appSetting('maintenance_mode_title', 'site');
                $sMaintenanceBody  = $sBody ? $sBody : appSetting('maintenance_mode_body', 'site');

            } catch (\Exception $e) {

                //  No database, or it failed, defaults
                $isWhiteListed     = false;
                $sMaintenanceTitle = $sTitle;
                $sMaintenanceBody  = $sBody;
            }

            // --------------------------------------------------------------------------

            if (!$isWhiteListed) {

                if (!$oInput::isCli()) {

                    header($oInput->server('SERVER_PROTOCOL') . ' 503 Service Temporarily Unavailable');
                    header('Status: 503 Service Temporarily Unavailable');
                    header('Retry-After: 7200');

                    // --------------------------------------------------------------------------

                    //  If the request is an AJAX request, or the URL is on the API then spit back JSON
                    if ($oInput::isAjax() || $oUri->segment(1) == 'api') {

                        header('Cache-Control: no-store, no-cache, must-revalidate');
                        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
                        header('Content-Type: application/json');
                        header('Pragma: no-cache');

                        $_out = ['status' => 503, 'error' => 'Down for Maintenance'];

                        echo json_encode($_out);

                    } else {
                        //  Otherwise, render some HTML
                        if (file_exists(NAILS_APP_PATH . 'application/views/errors/html/maintenance.php')) {
                            //  Look for an app override
                            require NAILS_APP_PATH . 'application/views/errors/html/maintenance.php';
                        } else {
                            //  Fall back to the Nails maintenance page
                            require NAILS_COMMON_PATH . 'views/errors/html/maintenance.php';
                        }
                    }

                } else {
                    if (file_exists(NAILS_APP_PATH . 'application/views/errors/cli/maintenance.php')) {
                        //  Look for an app override
                        require NAILS_APP_PATH . 'application/views/errors/cli/maintenance.php';
                    } else {
                        //  Fall back to the Nails maintenance page
                        require NAILS_COMMON_PATH . 'views/errors/cli/maintenance.php';
                    }
                }
                exit(0);
            }
        }
    }