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:
2026-01-30 14:39:13 -05:00
parent c3f7593f3c
commit 7575d6a277
31 changed files with 825 additions and 398 deletions

View File

@@ -83,7 +83,7 @@ class BulkOperationsModel {
// Get current ticket from pre-loaded batch
$currentTicket = $ticketsById[$ticketId] ?? null;
if ($currentTicket) {
$success = $ticketModel->updateTicket([
$updateResult = $ticketModel->updateTicket([
'ticket_id' => $ticketId,
'title' => $currentTicket['title'],
'description' => $currentTicket['description'],
@@ -92,6 +92,7 @@ class BulkOperationsModel {
'status' => 'Closed',
'priority' => $currentTicket['priority']
], $operation['performed_by']);
$success = $updateResult['success'];
if ($success) {
$auditLogModel->log($operation['performed_by'], 'update', 'ticket', $ticketId,
@@ -114,7 +115,7 @@ class BulkOperationsModel {
if (isset($parameters['priority'])) {
$currentTicket = $ticketsById[$ticketId] ?? null;
if ($currentTicket) {
$success = $ticketModel->updateTicket([
$updateResult = $ticketModel->updateTicket([
'ticket_id' => $ticketId,
'title' => $currentTicket['title'],
'description' => $currentTicket['description'],
@@ -123,6 +124,7 @@ class BulkOperationsModel {
'status' => $currentTicket['status'],
'priority' => $parameters['priority']
], $operation['performed_by']);
$success = $updateResult['success'];
if ($success) {
$auditLogModel->log($operation['performed_by'], 'update', 'ticket', $ticketId,
@@ -136,7 +138,7 @@ class BulkOperationsModel {
if (isset($parameters['status'])) {
$currentTicket = $ticketsById[$ticketId] ?? null;
if ($currentTicket) {
$success = $ticketModel->updateTicket([
$updateResult = $ticketModel->updateTicket([
'ticket_id' => $ticketId,
'title' => $currentTicket['title'],
'description' => $currentTicket['description'],
@@ -145,6 +147,7 @@ class BulkOperationsModel {
'status' => $parameters['status'],
'priority' => $currentTicket['priority']
], $operation['performed_by']);
$success = $updateResult['success'];
if ($success) {
$auditLogModel->log($operation['performed_by'], 'update', 'ticket', $ticketId,