Add performance, security, and reliability improvements
- Consolidate all 20 API files to use centralized Database helper - Add optimistic locking to ticket updates to prevent concurrent conflicts - Add caching to StatsModel (60s TTL) for dashboard performance - Add health check endpoint (api/health.php) for monitoring - Improve rate limit cleanup with cron script and efficient DirectoryIterator - Enable rate limit response headers (X-RateLimit-*) - Add audit logging for workflow transitions - Log Discord webhook failures instead of silencing - Fix visibility check on export_tickets.php - Add database migration system with performance indexes - Fix cron recurring tickets to use assignTicket method Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
110
api/health.php
Normal file
110
api/health.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* Health Check Endpoint
|
||||
*
|
||||
* Returns system health status for monitoring tools.
|
||||
* Does not require authentication - suitable for load balancer health checks.
|
||||
*
|
||||
* Returns:
|
||||
* - 200 OK: System is healthy
|
||||
* - 503 Service Unavailable: System has issues
|
||||
*/
|
||||
|
||||
// Don't apply rate limiting to health checks - they should always respond
|
||||
header('Content-Type: application/json');
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||
|
||||
$startTime = microtime(true);
|
||||
$checks = [];
|
||||
$healthy = true;
|
||||
|
||||
// Check 1: Database connectivity
|
||||
try {
|
||||
require_once dirname(__DIR__) . '/config/config.php';
|
||||
require_once dirname(__DIR__) . '/helpers/Database.php';
|
||||
|
||||
$conn = Database::getConnection();
|
||||
|
||||
// Quick query to verify connection is actually working
|
||||
$result = $conn->query('SELECT 1');
|
||||
if ($result && $result->fetch_row()) {
|
||||
$checks['database'] = [
|
||||
'status' => 'ok',
|
||||
'message' => 'Connected'
|
||||
];
|
||||
} else {
|
||||
$checks['database'] = [
|
||||
'status' => 'error',
|
||||
'message' => 'Query failed'
|
||||
];
|
||||
$healthy = false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$checks['database'] = [
|
||||
'status' => 'error',
|
||||
'message' => 'Connection failed'
|
||||
];
|
||||
$healthy = false;
|
||||
}
|
||||
|
||||
// Check 2: File system (uploads directory writable)
|
||||
$uploadDir = $GLOBALS['config']['UPLOAD_DIR'] ?? dirname(__DIR__) . '/uploads';
|
||||
if (is_dir($uploadDir) && is_writable($uploadDir)) {
|
||||
$checks['filesystem'] = [
|
||||
'status' => 'ok',
|
||||
'message' => 'Writable'
|
||||
];
|
||||
} else {
|
||||
$checks['filesystem'] = [
|
||||
'status' => 'warning',
|
||||
'message' => 'Upload directory not writable'
|
||||
];
|
||||
// Don't mark as unhealthy - this might be intentional
|
||||
}
|
||||
|
||||
// Check 3: Session storage
|
||||
$sessionPath = session_save_path() ?: sys_get_temp_dir();
|
||||
if (is_dir($sessionPath) && is_writable($sessionPath)) {
|
||||
$checks['sessions'] = [
|
||||
'status' => 'ok',
|
||||
'message' => 'Writable'
|
||||
];
|
||||
} else {
|
||||
$checks['sessions'] = [
|
||||
'status' => 'error',
|
||||
'message' => 'Session storage not writable'
|
||||
];
|
||||
$healthy = false;
|
||||
}
|
||||
|
||||
// Check 4: Rate limit storage
|
||||
$rateLimitDir = sys_get_temp_dir() . '/tinker_tickets_ratelimit';
|
||||
if (!is_dir($rateLimitDir)) {
|
||||
@mkdir($rateLimitDir, 0755, true);
|
||||
}
|
||||
if (is_dir($rateLimitDir) && is_writable($rateLimitDir)) {
|
||||
$checks['rate_limit'] = [
|
||||
'status' => 'ok',
|
||||
'message' => 'Writable'
|
||||
];
|
||||
} else {
|
||||
$checks['rate_limit'] = [
|
||||
'status' => 'warning',
|
||||
'message' => 'Rate limit storage not writable'
|
||||
];
|
||||
}
|
||||
|
||||
// Calculate response time
|
||||
$responseTime = round((microtime(true) - $startTime) * 1000, 2);
|
||||
|
||||
// Set status code
|
||||
http_response_code($healthy ? 200 : 503);
|
||||
|
||||
// Return response
|
||||
echo json_encode([
|
||||
'status' => $healthy ? 'healthy' : 'unhealthy',
|
||||
'timestamp' => date('c'),
|
||||
'response_time_ms' => $responseTime,
|
||||
'checks' => $checks,
|
||||
'version' => '1.0.0'
|
||||
], JSON_PRETTY_PRINT);
|
||||
Reference in New Issue
Block a user