From 582a2cac6c44be6b5b6e803a201f2f1fed78e619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20R=C3=BCegg?= Date: Sat, 12 Aug 2023 14:40:38 +0200 Subject: [PATCH] Enh: Attempt to show better error messages when DB-Connection is invalid --- CHANGELOG-DEV.md | 4 + index.php | 10 +- .../humhub/commands/MigrateController.php | 15 +- .../humhub/components/console/Application.php | 2 +- protected/humhub/helpers/DatabaseHelper.php | 147 ++++++++++++++++++ protected/humhub/libs/BaseSettingsManager.php | 15 +- .../installer/commands/InstallController.php | 38 ++--- protected/yii | 13 +- 8 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 protected/humhub/helpers/DatabaseHelper.php diff --git a/CHANGELOG-DEV.md b/CHANGELOG-DEV.md index 8865c2f26c..21a275055b 100644 --- a/CHANGELOG-DEV.md +++ b/CHANGELOG-DEV.md @@ -1,6 +1,10 @@ HumHub Changelog ================ +1.16.0 (Unreleased) +-------------------------- +- Enh #6512: Show error messages when DB connection configuration is invalid + 1.15.0-beta.2 (Unreleased) -------------------------- - Enh #6478: Add pseudo test class to allow population of DB with standard test data diff --git a/index.php b/index.php index b36348a6d6..9de9a45159 100644 --- a/index.php +++ b/index.php @@ -7,6 +7,8 @@ */ // comment out the following two lines when deployed to production +use humhub\helpers\DatabaseHelper; + defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_ENV') or define('YII_ENV', 'dev'); @@ -22,4 +24,10 @@ $config = yii\helpers\ArrayHelper::merge( require(__DIR__ . '/protected/config/web.php') ); -(new humhub\components\Application($config))->run(); +try { + (new humhub\components\Application($config))->run(); +} catch (\Throwable $ex) { + if (null === DatabaseHelper::handleConnectionErrors($ex)) { + throw $ex; + } +} diff --git a/protected/humhub/commands/MigrateController.php b/protected/humhub/commands/MigrateController.php index 5e563f0a33..094fbb7707 100644 --- a/protected/humhub/commands/MigrateController.php +++ b/protected/humhub/commands/MigrateController.php @@ -9,6 +9,7 @@ namespace humhub\commands; use humhub\components\Module; +use humhub\helpers\DatabaseHelper; use Yii; use yii\console\Exception; use yii\web\Application; @@ -78,9 +79,17 @@ class MigrateController extends \yii\console\controllers\MigrateController */ public function beforeAction($action) { - // Make sure to define default table storage engine - if (in_array(Yii::$app->db->getDriverName(), ['mysql', 'mysqli'], true)) { - Yii::$app->db->pdo->exec('SET default_storage_engine=' . Yii::$app->params['databaseDefaultStorageEngine']); + // Make sure to define a default table storage engine + $db = Yii::$app->db; + + try { + $db->open(); + } catch (\Throwable $ex) { + DatabaseHelper::handleConnectionErrors($ex); + } + + if (in_array($db->getDriverName(), ['mysql', 'mysqli'], true)) { + $db->pdo->exec('SET default_storage_engine=' . Yii::$app->params['databaseDefaultStorageEngine']); } return parent::beforeAction($action); } diff --git a/protected/humhub/components/console/Application.php b/protected/humhub/components/console/Application.php index 6683daa95f..0c26ba00b8 100644 --- a/protected/humhub/components/console/Application.php +++ b/protected/humhub/components/console/Application.php @@ -59,7 +59,7 @@ class Application extends \yii\console\Application implements \humhub\interfaces )); } - if (BaseSettingsManager::isDatabaseInstalled()) { + if (BaseSettingsManager::isDatabaseInstalled(Yii::$app->params['databaseInstalled'] ?? false)) { $baseUrl = Yii::$app->settings->get('baseUrl'); if (!empty($baseUrl)) { if (Yii::getAlias('@web', false) === false) { diff --git a/protected/humhub/helpers/DatabaseHelper.php b/protected/humhub/helpers/DatabaseHelper.php new file mode 100644 index 0000000000..e4532cc4bd --- /dev/null +++ b/protected/humhub/helpers/DatabaseHelper.php @@ -0,0 +1,147 @@ +getCode()) { + case 2002: + $error = 'Hostname not found.'; + break; + + case 1044: + $error = 'Database not found or not accessible.'; + break; + + case 1049: + $error = 'Database not found.'; + break; + + default: + $error = $ex->getMessage(); + } + + /** + * @see https://www.php.net/manual/en/ref.pdo-odbc.connection.php + * @see https://www.php.net/manual/en/ref.pdo-ibm.connection.php + * @see https://www.php.net/manual/en/ref.pdo-pgsql.connection.php + */ + $dsn = preg_replace( + '@((?<=:|;)(?:user|uid|User ID|pwd|password)=)(.*?)(?=;(?:$|\w+=)|$)@i', + '$1****', + Yii::$app->db->dsn + ); + + try { + $additionalInfo = json_encode([get_class($ex), ...$ex->errorInfo], JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + $additionalInfo = 'N/A'; + } + + while ($ex->getPrevious()) { + $ex = $ex->getPrevious(); + } + + $htmlMessage = defined('YII_DEBUG') && YII_DEBUG + ? sprintf(' +

Invalid database configuration

+

%s

+

The following connection string was used:
%s

+
+

Technical information

+

%s

+

%s

+', $error, $dsn, $additionalInfo, $ex) + : sprintf(' +

Invalid database configuration

+

%s

+', $error); + + $txtMessage = defined('YII_DEBUG') && YII_DEBUG + ? sprintf(' +Invalid database configuration +============================== + +%s + +The following connection string was used: +%s + + +Technical information +--------------------- +%s + +%s + +', $error, $dsn, $additionalInfo, $ex) + : sprintf(' +Invalid database configuration +============================== + +%s + +The following connection string was used: +%s + + +Technical information +--------------------- +%s + +', $error, $dsn, $additionalInfo); + + if ($print) { + if ($forcePlainText) { + echo $txtMessage; + } elseif (Yii::$app instanceof \yii\console\Application && Yii::$app->controller instanceof \yii\console\Controller) { + Yii::$app->controller->stderr($txtMessage); + } else { + header("HTTP/1.1 500 Internal Server Error"); + echo $htmlMessage; + } + } + + if (!$die) { + return $txtMessage; + } + + die(1); + } +} diff --git a/protected/humhub/libs/BaseSettingsManager.php b/protected/humhub/libs/BaseSettingsManager.php index 33dc82254e..38f04f48be 100644 --- a/protected/humhub/libs/BaseSettingsManager.php +++ b/protected/humhub/libs/BaseSettingsManager.php @@ -10,12 +10,12 @@ namespace humhub\libs; use humhub\components\SettingActiveRecord; use humhub\exceptions\InvalidArgumentTypeException; +use humhub\helpers\DatabaseHelper; use Stringable; use Yii; use yii\base\Component; use yii\base\InvalidArgumentException; use yii\base\InvalidConfigException; -use yii\db\conditions\LikeCondition; use yii\db\StaleObjectException; use yii\helpers\Json; @@ -303,19 +303,20 @@ abstract class BaseSettingsManager extends Component /** * Checks if settings table exists or application is not installed yet * - * @return bool * @since 1.3 */ - public static function isDatabaseInstalled() + public static function isDatabaseInstalled(bool $dieOnError = false): bool { try { - if (in_array('setting', Yii::$app->db->schema->getTableNames())) { - return true; - } + $db = Yii::$app->db; + $db->open(); } catch (\Exception $ex) { + if ($dieOnError) { + DatabaseHelper::handleConnectionErrors($ex); + } return false; } - return false; + return in_array('setting', $db->schema->getTableNames()); } } diff --git a/protected/humhub/modules/installer/commands/InstallController.php b/protected/humhub/modules/installer/commands/InstallController.php index c6bcca630f..580280eb3f 100644 --- a/protected/humhub/modules/installer/commands/InstallController.php +++ b/protected/humhub/modules/installer/commands/InstallController.php @@ -8,27 +8,28 @@ namespace humhub\modules\installer\commands; +use humhub\helpers\DatabaseHelper; +use humhub\libs\DynamicConfig; +use humhub\libs\UUID; +use humhub\modules\installer\libs\InitialData; +use humhub\modules\user\models\Group; +use humhub\modules\user\models\Password; +use humhub\modules\user\models\User; use Yii; +use yii\base\Exception; use yii\console\Controller; use yii\console\ExitCode; use yii\helpers\Console; -use yii\base\Exception; -use humhub\modules\user\models\User; -use humhub\modules\user\models\Password; -use humhub\modules\user\models\Group; -use humhub\modules\installer\libs\InitialData; -use humhub\libs\UUID; -use humhub\libs\DynamicConfig; /** * Console Install - * + * * Example usage: * php yii installer/write-db-config "$HUMHUB_DB_HOST" "$HUMHUB_DB_NAME" "$HUMHUB_DB_USER" "$HUMHUB_DB_PASSWORD" * php yii installer/install-db * php yii installer/write-site-config "$HUMHUB_NAME" "$HUMHUB_EMAIL" * php yii installer/create-admin-account - * + * */ class InstallController extends Controller { @@ -42,9 +43,9 @@ class InstallController extends Controller return ExitCode::OK; } - + /** - * Tries to open a connection to given db. + * Tries to open a connection to given db. * On success: Writes given settings to config-file and reloads it. * On failure: Throws exception */ @@ -80,12 +81,12 @@ class InstallController extends Controller $this->stdout("Install DB:\n\n", Console::FG_YELLOW); $this->stdout(" * Checking Database Connection\n", Console::FG_YELLOW); - if(!$this->checkDBConnection()){ - throw new Exception("Could not connect to DB!"); + if (true !== $message = $this->checkDBConnection()) { + throw new Exception($message ?? "Could not connect to DB!"); } $this->stdout(" * Installing Database\n", Console::FG_YELLOW); - + Yii::$app->cache->flush(); // Disable max execution time to avoid timeouts during migrations @ini_set('max_execution_time', 0); @@ -119,7 +120,7 @@ class InstallController extends Controller $user->profile->firstname = 'Sys'; $user->profile->lastname = 'Admin'; $user->profile->save(); - + $password = new Password(); $password->user_id = $user->id; $password->setPassword($admin_pass); @@ -179,6 +180,8 @@ class InstallController extends Controller /** * Tries to open global db connection and checks result. + * + * @return true|null|string */ private function checkDBConnection() { @@ -186,10 +189,9 @@ class InstallController extends Controller // call setActive with true to open connection. Yii::$app->db->open(); // return the current connection state. - return Yii::$app->db->getIsActive(); + return Yii::$app->db->getIsActive() ?: null; } catch (Exception $e) { - $this->stderr($e->getMessage()); + return DatabaseHelper::handleConnectionErrors($e, false, false, true); } - return false; } } diff --git a/protected/yii b/protected/yii index ab69eda696..c4378c4c3b 100755 --- a/protected/yii +++ b/protected/yii @@ -8,6 +8,8 @@ * @license http://www.yiiframework.com/license/ */ +use humhub\helpers\DatabaseHelper; + defined('YII_DEBUG') or define('YII_DEBUG', true); // fcgi doesn't have STDIN and STDOUT defined by default @@ -25,6 +27,11 @@ $config = yii\helpers\ArrayHelper::merge( require(__DIR__ . '/config/console.php') ); -$application = new humhub\components\console\Application($config); -$exitCode = $application->run(); -exit($exitCode); +try { + $exitCode = (new humhub\components\console\Application($config))->run(); + exit($exitCode); +} catch (\Throwable $ex) { + if (null === DatabaseHelper::handleConnectionErrors($ex)) { + throw $ex; + } +}