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:
Greg Sarjeant 2025-08-04 01:54:45 +00:00 committed by greg
parent 7816581216
commit 51abf33ad1
4 changed files with 80 additions and 69 deletions

View File

@ -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))) {

View File

@ -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;
}
}
}

View File

@ -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
);

View File

@ -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;
}
}