mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-09 11:03:17 +00:00
Add better maintenance handling
This commit is contained in:
@@ -76,6 +76,7 @@ use Alchemy\Phrasea\Controller\User\Preferences;
|
||||
use Alchemy\Phrasea\Core\PhraseaExceptionHandler;
|
||||
use Alchemy\Phrasea\Core\Event\Subscriber\LogoutSubscriber;
|
||||
use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaLocaleSubscriber;
|
||||
use Alchemy\Phrasea\Core\Event\Subscriber\MaintenanceSubscriber;
|
||||
use Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider;
|
||||
use Alchemy\Phrasea\Core\Provider\BrowserServiceProvider;
|
||||
use Alchemy\Phrasea\Core\Provider\BorderManagerServiceProvider;
|
||||
@@ -369,6 +370,7 @@ class Application extends SilexApplication
|
||||
$dispatcher->addListener(KernelEvents::RESPONSE, array($app, 'disableCookiesIfRequired'), -256);
|
||||
$dispatcher->addSubscriber(new LogoutSubscriber());
|
||||
$dispatcher->addSubscriber(new PhraseaLocaleSubscriber($app));
|
||||
$dispatcher->addSubscriber(new MaintenanceSubscriber($app));
|
||||
|
||||
return $dispatcher;
|
||||
})
|
||||
|
@@ -57,13 +57,6 @@ class Login implements ControllerProviderInterface
|
||||
if ($request->getPathInfo() == $app->path('homepage')) {
|
||||
return;
|
||||
}
|
||||
if ($app['phraseanet.registry']->get('GV_maintenance')) {
|
||||
$app->addFlash('warning', _('login::erreur: maintenance en cours, merci de nous excuser pour la gene occasionee'));
|
||||
|
||||
return $app->redirect($app->path('homepage', array(
|
||||
'redirect' => ltrim($request->get('redirect'), '/'),
|
||||
)));
|
||||
}
|
||||
});
|
||||
|
||||
// Displays the homepage
|
||||
@@ -697,9 +690,7 @@ class Login implements ControllerProviderInterface
|
||||
$feeds = $public_feeds->get_feeds();
|
||||
array_unshift($feeds, $public_feeds->get_aggregate());
|
||||
|
||||
$form = $app->form(new PhraseaAuthenticationForm(), null, array(
|
||||
'disabled' => $app['phraseanet.registry']->get('GV_maintenance')
|
||||
));
|
||||
$form = $app->form(new PhraseaAuthenticationForm());
|
||||
|
||||
return $app['twig']->render('login/index.html.twig', array(
|
||||
'module_name' => _('Accueil'),
|
||||
|
@@ -112,7 +112,7 @@ class Session implements ControllerProviderInterface
|
||||
}
|
||||
|
||||
if (in_array($app['session']->get('phraseanet.message'), array('1', null))) {
|
||||
if ($app['phraseanet.registry']->get('GV_maintenance')) {
|
||||
if ($app['phraseanet.configuration']['main']['maintenance']) {
|
||||
$ret['message'] .= _('The application is going down for maintenance, please logout.');
|
||||
}
|
||||
|
||||
|
@@ -15,9 +15,13 @@ use Alchemy\Phrasea\Application;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
|
||||
class ApiExceptionHandlerSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
@@ -44,6 +48,8 @@ class ApiExceptionHandlerSubscriber implements EventSubscriberInterface
|
||||
$code = \API_V1_result::ERROR_METHODNOTALLOWED;
|
||||
} elseif ($e instanceof MethodNotAllowedHttpException) {
|
||||
$code = \API_V1_result::ERROR_METHODNOTALLOWED;
|
||||
} elseif ($e instanceof BadRequestHttpException) {
|
||||
$code = \API_V1_result::ERROR_BAD_REQUEST;
|
||||
} elseif ($e instanceof \API_V1_exception_badrequest) {
|
||||
$code = \API_V1_result::ERROR_BAD_REQUEST;
|
||||
} elseif ($e instanceof \API_V1_exception_forbidden) {
|
||||
@@ -52,13 +58,23 @@ class ApiExceptionHandlerSubscriber implements EventSubscriberInterface
|
||||
$code = \API_V1_result::ERROR_UNAUTHORIZED;
|
||||
} elseif ($e instanceof \API_V1_exception_internalservererror) {
|
||||
$code = \API_V1_result::ERROR_INTERNALSERVERERROR;
|
||||
} elseif ($e instanceof AccessDeniedHttpException) {
|
||||
$code = \API_V1_result::ERROR_FORBIDDEN;
|
||||
} elseif ($e instanceof UnauthorizedHttpException) {
|
||||
$code = \API_V1_result::ERROR_UNAUTHORIZED;
|
||||
} elseif ($e instanceof NotFoundHttpException) {
|
||||
$code = \API_V1_result::ERROR_NOTFOUND;
|
||||
} elseif ($e instanceof HttpExceptionInterface) {
|
||||
if (503 === $e->getStatusCode()) {
|
||||
$code = \API_V1_result::ERROR_MAINTENANCE;
|
||||
} else {
|
||||
$code = \API_V1_result::ERROR_INTERNALSERVERERROR;
|
||||
}
|
||||
} else {
|
||||
$code = \API_V1_result::ERROR_INTERNALSERVERERROR;
|
||||
}
|
||||
|
||||
if ($e instanceof HttpException) {
|
||||
if ($e instanceof HttpExceptionInterface) {
|
||||
$headers = $e->getHeaders();
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2013 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Core\Event\Subscriber;
|
||||
|
||||
use Silex\Application;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
|
||||
class MaintenanceSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private $app;
|
||||
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::REQUEST => array('checkForMaintenance', 0),
|
||||
);
|
||||
}
|
||||
|
||||
public function checkForMaintenance(GetResponseEvent $event)
|
||||
{
|
||||
if ($this->app['phraseanet.configuration']['main']['maintenance']) {
|
||||
$this->app->abort(503, 'Service Temporarily Unavailable', array('Retry-After' => 3600));
|
||||
}
|
||||
}
|
||||
}
|
21
lib/classes/API/V1/exception/maintenance.php
Normal file
21
lib/classes/API/V1/exception/maintenance.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2013 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @package APIv1
|
||||
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
|
||||
* @link www.phraseanet.com
|
||||
*/
|
||||
class API_V1_exception_maintenance extends API_V1_exception_abstract
|
||||
{
|
||||
protected static $details = 'Server is offline for maintenance, try again soon.';
|
||||
}
|
@@ -89,6 +89,7 @@ class API_V1_result
|
||||
const ERROR_UNAUTHORIZED = 'Unauthorized';
|
||||
const ERROR_FORBIDDEN = 'Forbidden';
|
||||
const ERROR_NOTFOUND = 'Not Found';
|
||||
const ERROR_MAINTENANCE = 'Service Temporarily Unavailable';
|
||||
const ERROR_METHODNOTALLOWED = 'Method Not Allowed';
|
||||
const ERROR_INTERNALSERVERERROR = 'Internal Server Error';
|
||||
|
||||
@@ -295,6 +296,11 @@ class API_V1_result
|
||||
$this->error_type = $const;
|
||||
$this->error_message = API_V1_exception_internalservererror::get_details();
|
||||
break;
|
||||
case self::ERROR_MAINTENANCE:
|
||||
$this->http_code = 503;
|
||||
$this->error_type = $const;
|
||||
$this->error_message = API_V1_exception_maintenance::get_details();
|
||||
break;
|
||||
case OAUTH2_ERROR_INVALID_REQUEST:
|
||||
$this->error_type = $const;
|
||||
break;
|
||||
|
@@ -50,7 +50,6 @@ class registry implements registryInterface
|
||||
if ($app['phraseanet.configuration-tester']->isInstalled()) {
|
||||
$this->cache->save('GV_ServerName', $app['phraseanet.configuration']['main']['servername']);
|
||||
$this->cache->save('GV_debug', $app['debug']);
|
||||
$this->cache->save('GV_maintenance', $app['phraseanet.configuration']['main']['maintenance']);
|
||||
|
||||
$config = $app['phraseanet.configuration']->getConfig();
|
||||
|
||||
@@ -91,7 +90,7 @@ class registry implements registryInterface
|
||||
|
||||
}
|
||||
foreach ($rs as $row) {
|
||||
if (in_array($row['key'], array('GV_ServerName', 'GV_sit', 'GV_debug', 'GV_maintenance'))) {
|
||||
if (in_array($row['key'], array('GV_ServerName', 'GV_sit', 'GV_debug'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@@ -20,7 +20,7 @@
|
||||
<div class="control-group">
|
||||
<label class="control-label">Maintenance : </label>
|
||||
<div class="controls">
|
||||
<input type="checkbox" readonly="readonly" disabled="disabled" {{ app['phraseanet.registry'].get('GV_maintenance') == true ? "checked='checked'" : '' }} />
|
||||
<input type="checkbox" readonly="readonly" disabled="disabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
|
@@ -302,6 +302,32 @@ class ApplicationTest extends \PhraseanetPHPUnitAbstract
|
||||
$this->assertEquals('cat.turbocat.com', $app['url_generator']->getContext()->getHost());
|
||||
}
|
||||
|
||||
public function testMaintenanceModeTriggers503s()
|
||||
{
|
||||
$app = new Application('test');
|
||||
|
||||
$app['phraseanet.configuration.config-path'] = __DIR__ . '/Core/Event/Subscriber/Fixtures/configuration-maintenance.yml';
|
||||
$app['phraseanet.configuration.config-compiled-path'] = __DIR__ . '/Core/Event/Subscriber/Fixtures/configuration-maintenance.php';
|
||||
|
||||
if (is_file($app['phraseanet.configuration.config-compiled-path'])) {
|
||||
unlink($app['phraseanet.configuration.config-compiled-path']);
|
||||
}
|
||||
|
||||
$app->get('/', function(Application $app, Request $request) {
|
||||
return 'Hello';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', '/');
|
||||
|
||||
$this->assertEquals(503, $client->getResponse()->getStatusCode());
|
||||
$this->assertNotEquals('Hello', $client->getResponse()->getContent());
|
||||
|
||||
if (is_file($app['phraseanet.configuration.config-compiled-path'])) {
|
||||
unlink($app['phraseanet.configuration.config-compiled-path']);
|
||||
}
|
||||
}
|
||||
|
||||
private function getAppThatReturnLocale()
|
||||
{
|
||||
$app = new Application('test');
|
||||
|
@@ -1287,44 +1287,6 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
||||
self::$DI['user']->set_mail_locked(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Alchemy\Phrasea\Controller\Root\Login::authenticate
|
||||
*/
|
||||
public function testAuthenticateUnavailable()
|
||||
{
|
||||
self::$DI['app']['authentication']->closeAccount();
|
||||
$password = \random::generatePassword();
|
||||
self::$DI['app']['phraseanet.registry']->set('GV_maintenance', true , \registry::TYPE_BOOLEAN);
|
||||
|
||||
self::$DI['client'] = new Client(self::$DI['app'], array());
|
||||
|
||||
self::$DI['client']->request('POST', '/login/authenticate/', array(
|
||||
'login' => self::$DI['user']->get_login(),
|
||||
'password' => $password,
|
||||
'_token' => 'token'
|
||||
));
|
||||
self::$DI['app']['phraseanet.registry']->set('GV_maintenance', false, \registry::TYPE_BOOLEAN);
|
||||
$this->assertTrue(self::$DI['client']->getResponse()->isRedirect());
|
||||
$this->assertFlashMessagePopulated(self::$DI['app'], 'warning', 1);
|
||||
$this->assertFalse(self::$DI['app']['authentication']->isAuthenticated());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Alchemy\Phrasea\Controller\Root\Login::authenticate
|
||||
*/
|
||||
public function testMaintenanceOnLoginDoesNotRedirect()
|
||||
{
|
||||
self::$DI['app']['authentication']->closeAccount();
|
||||
self::$DI['app']['phraseanet.registry']->set('GV_maintenance', true , \registry::TYPE_BOOLEAN);
|
||||
|
||||
self::$DI['client'] = new Client(self::$DI['app'], array());
|
||||
|
||||
self::$DI['client']->request('GET', '/login/');
|
||||
self::$DI['app']['phraseanet.registry']->set('GV_maintenance', false, \registry::TYPE_BOOLEAN);
|
||||
$this->assertFalse(self::$DI['client']->getResponse()->isRedirect());
|
||||
}
|
||||
|
||||
public function testAuthenticateWithProvider()
|
||||
{
|
||||
$provider = $this->getMock('Alchemy\Phrasea\Authentication\Provider\ProviderInterface');
|
||||
|
@@ -0,0 +1,141 @@
|
||||
main:
|
||||
servername: 'http://local.phrasea/'
|
||||
maintenance: true
|
||||
database:
|
||||
host: sql-host
|
||||
port: '3306'
|
||||
user: sql-user
|
||||
password: sql-password
|
||||
dbname: ab_phraseanet
|
||||
driver: pdo_mysql
|
||||
charset: UTF8
|
||||
database-test:
|
||||
driver: pdo_sqlite
|
||||
path: /tmp/db.sqlite
|
||||
charset: UTF8
|
||||
api-timers: true
|
||||
cache:
|
||||
type: MemcacheCache
|
||||
options:
|
||||
host: localhost
|
||||
port: 11211
|
||||
opcodecache:
|
||||
type: ArrayCache
|
||||
options: { }
|
||||
search-engine:
|
||||
type: Alchemy\Phrasea\SearchEngine\Phrasea\PhraseaEngine
|
||||
options: { }
|
||||
task-manager:
|
||||
options: ''
|
||||
trusted-proxies: { }
|
||||
debugger:
|
||||
allowed-ips: { }
|
||||
binaries: { }
|
||||
border-manager:
|
||||
enabled: true
|
||||
checkers:
|
||||
-
|
||||
type: Checker\Sha256
|
||||
enabled: true
|
||||
-
|
||||
type: Checker\UUID
|
||||
enabled: true
|
||||
-
|
||||
type: Checker\Colorspace
|
||||
enabled: false
|
||||
options:
|
||||
colorspaces:
|
||||
- cmyk
|
||||
- grayscale
|
||||
- rgb
|
||||
-
|
||||
type: Checker\Dimension
|
||||
enabled: false
|
||||
options:
|
||||
width: 80
|
||||
height: 160
|
||||
-
|
||||
type: Checker\Extension
|
||||
enabled: false
|
||||
options:
|
||||
extensions:
|
||||
- jpg
|
||||
- jpeg
|
||||
- bmp
|
||||
- tif
|
||||
- gif
|
||||
- png
|
||||
- pdf
|
||||
- doc
|
||||
- odt
|
||||
- mpg
|
||||
- mpeg
|
||||
- mov
|
||||
- avi
|
||||
- xls
|
||||
- flv
|
||||
- mp3
|
||||
- mp2
|
||||
-
|
||||
type: Checker\Filename
|
||||
enabled: false
|
||||
options:
|
||||
sensitive: true
|
||||
-
|
||||
type: Checker\MediaType
|
||||
enabled: false
|
||||
options:
|
||||
mediatypes:
|
||||
- Audio
|
||||
- Document
|
||||
- Flash
|
||||
- Image
|
||||
- Video
|
||||
authentication:
|
||||
auto-create:
|
||||
enabled: false
|
||||
templates: { }
|
||||
captcha:
|
||||
enabled: true
|
||||
trials-before-failure: 9
|
||||
providers:
|
||||
facebook:
|
||||
enabled: false
|
||||
options:
|
||||
app-id: ''
|
||||
secret: ''
|
||||
twitter:
|
||||
enabled: false
|
||||
options:
|
||||
consumer-key: ''
|
||||
consumer-secret: ''
|
||||
google-plus:
|
||||
enabled: false
|
||||
options:
|
||||
client-id: ''
|
||||
client-secret: ''
|
||||
github:
|
||||
enabled: false
|
||||
options:
|
||||
client-id: ''
|
||||
client-secret: ''
|
||||
viadeo:
|
||||
enabled: false
|
||||
options:
|
||||
client-id: ''
|
||||
client-secret: ''
|
||||
linkedin:
|
||||
enabled: false
|
||||
options:
|
||||
client-id: ''
|
||||
client-secret: ''
|
||||
registration-fields:
|
||||
-
|
||||
name: company
|
||||
required: true
|
||||
-
|
||||
name: firstname
|
||||
required: true
|
||||
-
|
||||
name: geonameid
|
||||
required: true
|
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Alchemy\Tests\Phrasea\Core\Event\Subscriber;
|
||||
|
||||
use Alchemy\Phrasea\Application;
|
||||
use Alchemy\Phrasea\Core\Event\Subscriber\MaintenanceSubscriber;
|
||||
use Symfony\Component\HttpKernel\Client;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class MaintenanceSubscriberTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
if (is_file(__DIR__ . '/Fixtures/configuration-maintenance.php')) {
|
||||
unlink(__DIR__ . '/Fixtures/configuration-maintenance.php');
|
||||
}
|
||||
}
|
||||
|
||||
public function testCheckNegative()
|
||||
{
|
||||
$app = new Application();
|
||||
unset($app['exception_handler']);
|
||||
$app['dispatcher']->addSubscriber(new MaintenanceSubscriber($app));
|
||||
$app->get('/', function () {
|
||||
return 'Hello';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', '/');
|
||||
|
||||
$this->assertEquals(200, $client->getResponse()->getStatusCode());
|
||||
$this->assertEquals('Hello', $client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testCheckPositive()
|
||||
{
|
||||
$app = new Application();
|
||||
|
||||
$app['phraseanet.configuration.config-path'] = __DIR__ . '/Fixtures/configuration-maintenance.yml';
|
||||
$app['phraseanet.configuration.config-compiled-path'] = __DIR__ . '/Fixtures/configuration-maintenance.php';
|
||||
|
||||
if (is_file($app['phraseanet.configuration.config-compiled-path'])) {
|
||||
unlink($app['phraseanet.configuration.config-compiled-path']);
|
||||
}
|
||||
|
||||
unset($app['exception_handler']);
|
||||
$app['dispatcher']->addSubscriber(new MaintenanceSubscriber($app));
|
||||
$app->get('/', function () {
|
||||
return 'Hello';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
try {
|
||||
$client->request('GET', '/');
|
||||
$this->fail('An exception should have been raised');
|
||||
} catch (HttpException $e) {
|
||||
$this->assertEquals(503, $e->getStatusCode());
|
||||
$this->assertEquals(array('Retry-After' => 3600), $e->getHeaders());
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user