From 98d30cbc589745e8acb040e7b2850e5da117d05f Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Fri, 11 Sep 2026 11:42:21 -0400 Subject: [PATCH] Route index.php and create_ticket_api.php through Database::getConnection() (#103, #104) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both files opened their own raw new mysqli(...) connection instead of using Database::getConnection(), missing the charset/timezone sync every other connection gets. index.php's connection serves nearly all non-API web traffic (dashboard, ticket view, ticket create) — any NOW()/CURDATE()-based query through it used the DB server's default session timezone instead of the app's configured TIMEZONE, and no explicit utf8mb4 charset meant multi-byte characters typed into a ticket via the non-JS POST fallback could get corrupted at write time. create_ticket_api.php (the hwmonDaemon Bearer endpoint) had the same timezone gap, risking created_at landing on the wrong 'day' relative to every other ticket-creation path. index.php's raw die("Connection failed: " . $conn->connect_error) also leaked raw mysqli error text (host/user/failure reason) to any unauthenticated visitor on a DB outage; it now logs via error_log() and shows a generic message instead. create_ticket_api.php already handled this correctly (JSON error + error_log, no leak) and needed no behavior change there beyond the connection source. Verified against real MariaDB: the new connection path reports the configured -04:00 session time_zone and utf8mb4 charset, vs. SYSTEM tz on the old raw-mysqli path; a simulated connection failure (bad DB_NAME) confirmed only the generic message reaches the response body while the raw driver error goes to error_log(). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Lhz7pGMaoTfL5sdYS5XiKv --- create_ticket_api.php | 56 +++++++++++++------------------------------ index.php | 18 +++++++------- 2 files changed, 25 insertions(+), 49 deletions(-) diff --git a/create_ticket_api.php b/create_ticket_api.php index e4a85a9..17d35be 100644 --- a/create_ticket_api.php +++ b/create_ticket_api.php @@ -8,9 +8,10 @@ ini_set('display_errors', 0); require_once __DIR__ . '/middleware/RateLimitMiddleware.php'; RateLimitMiddleware::apply('api'); -// Load environment variables with error check -$envFile = __DIR__ . '/.env'; -if (!file_exists($envFile)) { +// Early friendly JSON error if .env is missing, before config.php's own +// (plain-text die()) handling would otherwise run — this is a JSON API +// endpoint and must always respond with a JSON body. +if (!file_exists(__DIR__ . '/.env')) { echo json_encode([ 'success' => false, 'error' => 'Configuration file not found' @@ -18,37 +19,17 @@ if (!file_exists($envFile)) { exit; } -$envVars = parse_ini_file($envFile, false, INI_SCANNER_TYPED); -if (!$envVars) { - echo json_encode([ - 'success' => false, - 'error' => 'Invalid configuration file' - ]); - exit; -} +// Load application config so UrlHelper can resolve APP_DOMAIN, and so the +// DB connection below (via Database::getConnection()) gets the same +// charset/timezone sync as every other endpoint instead of a hand-rolled +// second connection. +require_once __DIR__ . '/config/config.php'; +require_once __DIR__ . '/helpers/Database.php'; -// Strip quotes from values if present (parse_ini_file may include them) -foreach ($envVars as $key => $value) { - if (is_string($value)) { - if ( - (substr($value, 0, 1) === '"' && substr($value, -1) === '"') || - (substr($value, 0, 1) === "'" && substr($value, -1) === "'") - ) { - $envVars[$key] = substr($value, 1, -1); - } - } -} - -// Database connection with detailed error handling -$conn = new mysqli( - $envVars['DB_HOST'], - $envVars['DB_USER'], - $envVars['DB_PASS'], - $envVars['DB_NAME'] -); - -if ($conn->connect_error) { - error_log('create_ticket_api: DB connection failed: ' . $conn->connect_error); +try { + $conn = Database::getConnection(); +} catch (\Throwable $e) { + error_log('create_ticket_api: DB connection failed: ' . $e->getMessage()); http_response_code(500); echo json_encode([ 'success' => false, @@ -57,9 +38,6 @@ if ($conn->connect_error) { exit; } -// Load application config so UrlHelper can resolve APP_DOMAIN -require_once __DIR__ . '/config/config.php'; - // Authenticate via API key require_once __DIR__ . '/middleware/ApiKeyAuth.php'; require_once __DIR__ . '/models/AuditLogModel.php'; @@ -349,7 +327,7 @@ if ($existing) { (new StatsModel($conn))->invalidateCache(); } - $conn->close(); + Database::close(); echo json_encode([ 'success' => true, 'ticket_id' => $existingId, @@ -386,7 +364,7 @@ if ($existing) { // Ticket reopened (Closed → Open) — refresh dashboard stats. (new StatsModel($conn))->invalidateCache(); - $conn->close(); + Database::close(); require_once __DIR__ . '/helpers/NotificationHelper.php'; NotificationHelper::sendTicketNotification($existingId, [ @@ -484,7 +462,7 @@ if ($inserted) { // New ticket created — refresh dashboard stats. (new StatsModel($conn))->invalidateCache(); - $conn->close(); + Database::close(); require_once __DIR__ . '/helpers/NotificationHelper.php'; NotificationHelper::sendTicketNotification($ticket_id, [ diff --git a/index.php b/index.php index d5982c0..248f1cd 100644 --- a/index.php +++ b/index.php @@ -5,6 +5,7 @@ require_once 'config/config.php'; require_once 'middleware/SecurityHeadersMiddleware.php'; require_once 'middleware/AuthMiddleware.php'; require_once 'models/AuditLogModel.php'; +require_once 'helpers/Database.php'; // Apply security headers early SecurityHeadersMiddleware::apply(); @@ -17,15 +18,12 @@ $requestPath = strtok($request, '?'); // Create database connection for non-API routes if (!str_starts_with($requestPath, '/api/')) { - $conn = new mysqli( - $GLOBALS['config']['DB_HOST'], - $GLOBALS['config']['DB_USER'], - $GLOBALS['config']['DB_PASS'], - $GLOBALS['config']['DB_NAME'] - ); - - if ($conn->connect_error) { - die("Connection failed: " . $conn->connect_error); + try { + $conn = Database::getConnection(); + } catch (\Throwable $e) { + error_log('index.php: database connection failed: ' . $e->getMessage()); + http_response_code(500); + die('Sorry, something went wrong. Please try again shortly.'); } // Authenticate user via Authelia forward auth @@ -444,5 +442,5 @@ switch (true) { // Close database connection if it was opened if (isset($conn)) { - $conn->close(); + Database::close(); }