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';
|
require_once __DIR__ . '/middleware/RateLimitMiddleware.php';
|
||||||
RateLimitMiddleware::apply('api');
|
RateLimitMiddleware::apply('api');
|
||||||
|
|
||||||
// Load environment variables with error check
|
// Early friendly JSON error if .env is missing, before config.php's own
|
||||||
$envFile = __DIR__ . '/.env';
|
// (plain-text die()) handling would otherwise run — this is a JSON API
|
||||||
if (!file_exists($envFile)) {
|
// endpoint and must always respond with a JSON body.
|
||||||
|
if (!file_exists(__DIR__ . '/.env')) {
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'Configuration file not found'
|
'error' => 'Configuration file not found'
|
||||||
@@ -18,37 +19,17 @@ if (!file_exists($envFile)) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$envVars = parse_ini_file($envFile, false, INI_SCANNER_TYPED);
|
// Load application config so UrlHelper can resolve APP_DOMAIN, and so the
|
||||||
if (!$envVars) {
|
// DB connection below (via Database::getConnection()) gets the same
|
||||||
echo json_encode([
|
// charset/timezone sync as every other endpoint instead of a hand-rolled
|
||||||
'success' => false,
|
// second connection.
|
||||||
'error' => 'Invalid configuration file'
|
require_once __DIR__ . '/config/config.php';
|
||||||
]);
|
require_once __DIR__ . '/helpers/Database.php';
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip quotes from values if present (parse_ini_file may include them)
|
try {
|
||||||
foreach ($envVars as $key => $value) {
|
$conn = Database::getConnection();
|
||||||
if (is_string($value)) {
|
} catch (\Throwable $e) {
|
||||||
if (
|
error_log('create_ticket_api: DB connection failed: ' . $e->getMessage());
|
||||||
(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);
|
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@@ -57,9 +38,6 @@ if ($conn->connect_error) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load application config so UrlHelper can resolve APP_DOMAIN
|
|
||||||
require_once __DIR__ . '/config/config.php';
|
|
||||||
|
|
||||||
// Authenticate via API key
|
// Authenticate via API key
|
||||||
require_once __DIR__ . '/middleware/ApiKeyAuth.php';
|
require_once __DIR__ . '/middleware/ApiKeyAuth.php';
|
||||||
require_once __DIR__ . '/models/AuditLogModel.php';
|
require_once __DIR__ . '/models/AuditLogModel.php';
|
||||||
@@ -349,7 +327,7 @@ if ($existing) {
|
|||||||
(new StatsModel($conn))->invalidateCache();
|
(new StatsModel($conn))->invalidateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
$conn->close();
|
Database::close();
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'ticket_id' => $existingId,
|
'ticket_id' => $existingId,
|
||||||
@@ -386,7 +364,7 @@ if ($existing) {
|
|||||||
// Ticket reopened (Closed → Open) — refresh dashboard stats.
|
// Ticket reopened (Closed → Open) — refresh dashboard stats.
|
||||||
(new StatsModel($conn))->invalidateCache();
|
(new StatsModel($conn))->invalidateCache();
|
||||||
|
|
||||||
$conn->close();
|
Database::close();
|
||||||
|
|
||||||
require_once __DIR__ . '/helpers/NotificationHelper.php';
|
require_once __DIR__ . '/helpers/NotificationHelper.php';
|
||||||
NotificationHelper::sendTicketNotification($existingId, [
|
NotificationHelper::sendTicketNotification($existingId, [
|
||||||
@@ -484,7 +462,7 @@ if ($inserted) {
|
|||||||
// New ticket created — refresh dashboard stats.
|
// New ticket created — refresh dashboard stats.
|
||||||
(new StatsModel($conn))->invalidateCache();
|
(new StatsModel($conn))->invalidateCache();
|
||||||
|
|
||||||
$conn->close();
|
Database::close();
|
||||||
|
|
||||||
require_once __DIR__ . '/helpers/NotificationHelper.php';
|
require_once __DIR__ . '/helpers/NotificationHelper.php';
|
||||||
NotificationHelper::sendTicketNotification($ticket_id, [
|
NotificationHelper::sendTicketNotification($ticket_id, [
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ require_once 'config/config.php';
|
|||||||
require_once 'middleware/SecurityHeadersMiddleware.php';
|
require_once 'middleware/SecurityHeadersMiddleware.php';
|
||||||
require_once 'middleware/AuthMiddleware.php';
|
require_once 'middleware/AuthMiddleware.php';
|
||||||
require_once 'models/AuditLogModel.php';
|
require_once 'models/AuditLogModel.php';
|
||||||
|
require_once 'helpers/Database.php';
|
||||||
|
|
||||||
// Apply security headers early
|
// Apply security headers early
|
||||||
SecurityHeadersMiddleware::apply();
|
SecurityHeadersMiddleware::apply();
|
||||||
@@ -17,15 +18,12 @@ $requestPath = strtok($request, '?');
|
|||||||
|
|
||||||
// Create database connection for non-API routes
|
// Create database connection for non-API routes
|
||||||
if (!str_starts_with($requestPath, '/api/')) {
|
if (!str_starts_with($requestPath, '/api/')) {
|
||||||
$conn = new mysqli(
|
try {
|
||||||
$GLOBALS['config']['DB_HOST'],
|
$conn = Database::getConnection();
|
||||||
$GLOBALS['config']['DB_USER'],
|
} catch (\Throwable $e) {
|
||||||
$GLOBALS['config']['DB_PASS'],
|
error_log('index.php: database connection failed: ' . $e->getMessage());
|
||||||
$GLOBALS['config']['DB_NAME']
|
http_response_code(500);
|
||||||
);
|
die('Sorry, something went wrong. Please try again shortly.');
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
die("Connection failed: " . $conn->connect_error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate user via Authelia forward auth
|
// Authenticate user via Authelia forward auth
|
||||||
@@ -444,5 +442,5 @@ switch (true) {
|
|||||||
|
|
||||||
// Close database connection if it was opened
|
// Close database connection if it was opened
|
||||||
if (isset($conn)) {
|
if (isset($conn)) {
|
||||||
$conn->close();
|
Database::close();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user