1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-18 12:52:42 +01:00

Add a friendlier "in flight" error page

Summary:
Ref T11589. When we hit a fatal setup issue (essentially always a connection failure) //after// we've already survived them on at least one request, we can be pretty sure a server went down and that the problem is not a setup/configuration issue.

In this case, show a friendlier error page instead of the fairly detailed technical one.

Test Plan:
  - Broke MySQL config.
  - Restarted Apache.
  - Got the "admin/setup" error page:

{F1803268}

  - Fixed the MySQL config.
  - Loaded any page, to put us "in flight".
  - Broke MySQL config.
  - Loaded any page.
  - Got the friendly "in flight" error page:

{F1803271}

If you want to design this better, easiest way to get to it is:

  - Set `mysql.port` to `9999` in `conf/local/local.json`.
  - Reload any page while already running (don't restart).

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11589

Differential Revision: https://secure.phabricator.com/D16503
This commit is contained in:
epriestley 2016-09-06 13:48:22 -07:00
parent 0e8ceeb690
commit f82adbf0c8
7 changed files with 104 additions and 4 deletions

View file

@ -41,7 +41,7 @@ return array(
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4', 'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
'rsrc/css/application/config/config-options.css' => '0ede4c9b', 'rsrc/css/application/config/config-options.css' => '0ede4c9b',
'rsrc/css/application/config/config-page.css' => '8798e14f', 'rsrc/css/application/config/config-page.css' => '8798e14f',
'rsrc/css/application/config/config-template.css' => '8e6c6fcd', 'rsrc/css/application/config/config-template.css' => '8f18fa41',
'rsrc/css/application/config/setup-issue.css' => 'f794cfc3', 'rsrc/css/application/config/setup-issue.css' => 'f794cfc3',
'rsrc/css/application/config/unhandled-exception.css' => '4c96257a', 'rsrc/css/application/config/unhandled-exception.css' => '4c96257a',
'rsrc/css/application/conpherence/durable-column.css' => '86396117', 'rsrc/css/application/conpherence/durable-column.css' => '86396117',
@ -777,7 +777,7 @@ return array(
'phabricator-dashboard-css' => 'bc6f2127', 'phabricator-dashboard-css' => 'bc6f2127',
'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-drag-and-drop-file-upload' => '58dea2fa',
'phabricator-draggable-list' => '5a13c79f', 'phabricator-draggable-list' => '5a13c79f',
'phabricator-fatal-config-template-css' => '8e6c6fcd', 'phabricator-fatal-config-template-css' => '8f18fa41',
'phabricator-feed-css' => 'ecd4ec57', 'phabricator-feed-css' => 'ecd4ec57',
'phabricator-file-upload' => '680ea2c8', 'phabricator-file-upload' => '680ea2c8',
'phabricator-filetree-view-css' => 'fccf9f82', 'phabricator-filetree-view-css' => 'fccf9f82',

View file

@ -2685,6 +2685,7 @@ phutil_register_library_map(array(
'PhabricatorImageMacroRemarkupRule' => 'applications/macro/markup/PhabricatorImageMacroRemarkupRule.php', 'PhabricatorImageMacroRemarkupRule' => 'applications/macro/markup/PhabricatorImageMacroRemarkupRule.php',
'PhabricatorImageTransformer' => 'applications/files/PhabricatorImageTransformer.php', 'PhabricatorImageTransformer' => 'applications/files/PhabricatorImageTransformer.php',
'PhabricatorImagemagickSetupCheck' => 'applications/config/check/PhabricatorImagemagickSetupCheck.php', 'PhabricatorImagemagickSetupCheck' => 'applications/config/check/PhabricatorImagemagickSetupCheck.php',
'PhabricatorInFlightErrorView' => 'applications/config/view/PhabricatorInFlightErrorView.php',
'PhabricatorIndexEngine' => 'applications/search/index/PhabricatorIndexEngine.php', 'PhabricatorIndexEngine' => 'applications/search/index/PhabricatorIndexEngine.php',
'PhabricatorIndexEngineExtension' => 'applications/search/index/PhabricatorIndexEngineExtension.php', 'PhabricatorIndexEngineExtension' => 'applications/search/index/PhabricatorIndexEngineExtension.php',
'PhabricatorIndexEngineExtensionModule' => 'applications/search/index/PhabricatorIndexEngineExtensionModule.php', 'PhabricatorIndexEngineExtensionModule' => 'applications/search/index/PhabricatorIndexEngineExtensionModule.php',
@ -7509,6 +7510,7 @@ phutil_register_library_map(array(
'PhabricatorImageMacroRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorImageMacroRemarkupRule' => 'PhutilRemarkupRule',
'PhabricatorImageTransformer' => 'Phobject', 'PhabricatorImageTransformer' => 'Phobject',
'PhabricatorImagemagickSetupCheck' => 'PhabricatorSetupCheck', 'PhabricatorImagemagickSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorInFlightErrorView' => 'AphrontView',
'PhabricatorIndexEngine' => 'Phobject', 'PhabricatorIndexEngine' => 'Phobject',
'PhabricatorIndexEngineExtension' => 'Phobject', 'PhabricatorIndexEngineExtension' => 'Phobject',
'PhabricatorIndexEngineExtensionModule' => 'PhabricatorConfigModule', 'PhabricatorIndexEngineExtensionModule' => 'PhabricatorConfigModule',

View file

@ -192,6 +192,21 @@ abstract class PhabricatorSetupCheck extends Phobject {
} }
} }
/**
* Test if we've survived through setup on at least one normal request
* without fataling.
*
* If we've made it through setup without hitting any fatals, we switch
* to render a more friendly error page when encountering issues like
* database connection failures. This gives users a smoother experience in
* the face of intermittent failures.
*
* @return bool True if we've made it through setup since the last restart.
*/
final public static function isInFlight() {
return (self::getOpenSetupIssueKeys() !== null);
}
final public static function loadAllChecks() { final public static function loadAllChecks() {
return id(new PhutilClassMapQuery()) return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__) ->setAncestorClass(__CLASS__)

View file

@ -25,11 +25,21 @@ final class PhabricatorConfigResponse extends AphrontStandaloneHTMLResponse {
} }
protected function getResponseBodyClass() { protected function getResponseBodyClass() {
return 'setup-fatal'; if (PhabricatorSetupCheck::isInFlight()) {
return 'setup-fatal in-flight';
} else {
return 'setup-fatal';
}
} }
protected function getResponseBody() { protected function getResponseBody() {
return $this->view->render(); $view = $this->view;
if (PhabricatorSetupCheck::isInFlight()) {
return $view->renderInFlight();
} else {
return $view->render();
}
} }
protected function buildPlainTextResponseString() { protected function buildPlainTextResponseString() {

View file

@ -0,0 +1,41 @@
<?php
final class PhabricatorInFlightErrorView extends AphrontView {
private $message;
public function setMessage($message) {
$this->message = $message;
return $this;
}
public function getMessage() {
return $this->message;
}
public function render() {
return phutil_tag(
'div',
array(
'class' => 'in-flight-error-detail',
),
array(
phutil_tag(
'h1',
array(
'class' => 'in-flight-error-title',
),
pht('A Troublesome Encounter!')),
phutil_tag(
'div',
array(
'class' => 'in-flight-error-body',
),
pht(
'Woe! This request had its journey cut short by unexpected '.
'circumstances (%s).',
$this->getMessage())),
));
}
}

View file

@ -13,6 +13,14 @@ final class PhabricatorSetupIssueView extends AphrontView {
return $this->issue; return $this->issue;
} }
public function renderInFlight() {
$issue = $this->getIssue();
return id(new PhabricatorInFlightErrorView())
->setMessage($issue->getName())
->render();
}
public function render() { public function render() {
$issue = $this->getIssue(); $issue = $this->getIssue();

View file

@ -11,3 +11,27 @@ body {
text-align: left; text-align: left;
-webkit-text-size-adjust: none; -webkit-text-size-adjust: none;
} }
body.in-flight {
background: #41506e;
color: #e0e0e0;
}
.in-flight-error-detail {
max-width: 760px;
margin: 72px auto;
background: rgba(255, 255, 255, 0.25);
border-radius: 3px;
padding: 8px 16px;
}
.in-flight-error-title {
padding: 12px 8px;
font-size: 24px;
font-weight: 500;
margin: 0;
}
.in-flight-error-body {
padding: 4px 12px 12px;
}