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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lhz7pGMaoTfL5sdYS5XiKv
This commit is contained in:
+17
-39
@@ -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, [
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user