Optimize initialization logic and simplify error handling. (#54)
Consolidate prerequisite tests - include database migrations. Remove SetupException and fold database initialization error handling into prereqs. Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/54 Co-authored-by: Greg Sarjeant <greg@subcultureofone.org> Co-committed-by: Greg Sarjeant <greg@subcultureofone.org>
This commit is contained in:
parent
7816581216
commit
51abf33ad1
@ -22,32 +22,15 @@ include_once(dirname(dirname(__FILE__)) . "/config/bootstrap.php");
|
||||
* Validate application state before processing request
|
||||
*/
|
||||
|
||||
// Check prerequisites
|
||||
// Check prerequisites (includes database connection and migrations)
|
||||
$prerequisites = new Prerequisites();
|
||||
$results = $prerequisites->validate();
|
||||
if (count($prerequisites->getErrors()) > 0) {
|
||||
$prerequisites->generateWebSummary($results);
|
||||
if (!$prerequisites->validate()) {
|
||||
$prerequisites->generateWebSummary();
|
||||
exit;
|
||||
}
|
||||
|
||||
// Connect to the database
|
||||
try {
|
||||
// SQLite will just create this if it doesn't exist.
|
||||
$db = new PDO("sqlite:" . DB_FILE);
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
throw new SetupException(
|
||||
"Database connection failed: " . $e->getMessage(),
|
||||
'database_connection',
|
||||
0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
// Do any necessary database migrations
|
||||
$migrator = new Migrator($db);
|
||||
$migrator->migrate();
|
||||
// Get the working database connection from prerequisites
|
||||
$db = $prerequisites->getDatabase();
|
||||
|
||||
// Make sure the initial setup is complete unless we're already heading to setup
|
||||
if (!(preg_match('/setup$/', $path))) {
|
||||
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
// Define an exception for validation errors
|
||||
class SetupException extends Exception {
|
||||
private $setupIssue;
|
||||
|
||||
public function __construct(string $message, string $setupIssue = '', int $code = 0, Throwable $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->setupIssue = $setupIssue;
|
||||
}
|
||||
|
||||
// Exception handler
|
||||
// Exceptions don't generally define their own handlers,
|
||||
// but this is a very specific case.
|
||||
public function handle(){
|
||||
// try to log the error, but keep going if it fails
|
||||
try {
|
||||
Log::error($this->setupIssue . ", " . $this->getMessage());
|
||||
} catch (Exception $e) {
|
||||
// Do nothing and move on to the normal error handling
|
||||
// We don't want to short-circuit this if there's a problem logging
|
||||
}
|
||||
|
||||
// TODO: This doesn't need to be a switch anymore
|
||||
// May not need to exist at all
|
||||
switch ($this->setupIssue){
|
||||
case 'database_connection':
|
||||
case 'db_migration':
|
||||
// Unrecoverable errors.
|
||||
// Show error message and exit
|
||||
http_response_code(500);
|
||||
echo "<h1>Configuration Error</h1>";
|
||||
echo "<p>" . Util::escape_html($this->setupIssue) . '-' . Util::escape_html($this->getMessage()) . "</p>";
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -18,9 +18,8 @@ class Migrator{
|
||||
$currentVersion = $this->getVersion();
|
||||
|
||||
if ($newVersion <= $currentVersion){
|
||||
throw new SetupException(
|
||||
"New version ($newVersion) must be greater than current version ($currentVersion)",
|
||||
'db_migration'
|
||||
throw new Exception(
|
||||
"New version ($newVersion) must be greater than current version ($currentVersion)"
|
||||
);
|
||||
}
|
||||
|
||||
@ -89,9 +88,8 @@ class Migrator{
|
||||
Log::info("Updated database version to " . $this->getVersion());
|
||||
} catch (Exception $e) {
|
||||
$this->db->rollBack();
|
||||
throw new SetupException(
|
||||
"Migration failed: $filename",
|
||||
'db_migration',
|
||||
throw new Exception(
|
||||
"Migration failed: $filename - " . $e->getMessage(),
|
||||
0,
|
||||
$e
|
||||
);
|
||||
|
@ -16,6 +16,7 @@ class Prerequisites {
|
||||
private $logFile;
|
||||
private $isCli;
|
||||
private $isWeb;
|
||||
private $database = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->isCli = php_sapi_name() === 'cli';
|
||||
@ -377,12 +378,68 @@ class Prerequisites {
|
||||
);
|
||||
}
|
||||
|
||||
return $canCreateDb;
|
||||
if (!$canCreateDb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test database connection
|
||||
try {
|
||||
$db = new PDO("sqlite:" . $dbFile);
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
|
||||
// Test basic query to ensure database is functional
|
||||
$db->query("SELECT 1")->fetchColumn();
|
||||
|
||||
$this->addCheck(
|
||||
'Database Connection',
|
||||
true,
|
||||
'Successfully connected to database'
|
||||
);
|
||||
|
||||
// Store working database connection
|
||||
$this->database = $db;
|
||||
|
||||
// Test migrations
|
||||
return $this->checkMigrations($db);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
$this->addCheck(
|
||||
'Database Connection',
|
||||
false,
|
||||
'Failed to connect: ' . $e->getMessage(),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function checkMigrations($db) {
|
||||
try {
|
||||
$migrator = new Migrator($db);
|
||||
$migrator->migrate();
|
||||
|
||||
$this->addCheck(
|
||||
'Database Migrations',
|
||||
true,
|
||||
'All database migrations applied successfully'
|
||||
);
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->addCheck(
|
||||
'Database Migrations',
|
||||
false,
|
||||
'Migration failed: ' . $e->getMessage(),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// validate prereqs
|
||||
// runs on each request and can be run from CLI
|
||||
public function validate() {
|
||||
public function validate(): bool {
|
||||
$this->log("=== tkr prerequisites check started at " . date('Y-m-d H:i:s') . " ===", true);
|
||||
|
||||
if ($this->isCli) {
|
||||
@ -406,7 +463,8 @@ class Prerequisites {
|
||||
$this->generateCliSummary($results);
|
||||
}
|
||||
|
||||
return $results;
|
||||
// Return true only if no errors occurred
|
||||
return count($this->errors) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -567,4 +625,14 @@ class Prerequisites {
|
||||
public function getWarnings() {
|
||||
return $this->warnings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get working database connection (only call after validate() returns true)
|
||||
*/
|
||||
public function getDatabase(): PDO {
|
||||
if ($this->database === null) {
|
||||
throw new RuntimeException('Database not available - call validate() first');
|
||||
}
|
||||
return $this->database;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user