connect_error) { die("Connection failed: " . $conn->connect_error); } // Authenticate user via Authelia forward auth $authMiddleware = new AuthMiddleware($conn); $currentUser = $authMiddleware->authenticate(); // Store current user in globals for controllers $GLOBALS['currentUser'] = $currentUser; // Initialize audit log model $GLOBALS['auditLog'] = new AuditLogModel($conn); // Check if user has a timezone preference and apply it if ($currentUser && isset($currentUser['user_id'])) { require_once 'models/UserPreferencesModel.php'; $prefsModel = new UserPreferencesModel($conn); $userTimezone = $prefsModel->getPreference($currentUser['user_id'], 'timezone', null); if ($userTimezone) { // Override system timezone with user preference date_default_timezone_set($userTimezone); $GLOBALS['config']['TIMEZONE'] = $userTimezone; $now = new DateTime('now', new DateTimeZone($userTimezone)); $GLOBALS['config']['TIMEZONE_OFFSET'] = $now->getOffset() / 60; $GLOBALS['config']['TIMEZONE_ABBREV'] = $now->format('T'); } } } // Simple router switch (true) { case $requestPath == '/' || $requestPath == '': require_once 'controllers/DashboardController.php'; $controller = new DashboardController($conn); $controller->index(); break; case preg_match('/^\/ticket\/(\d+)$/', $requestPath, $matches): require_once 'controllers/TicketController.php'; $controller = new TicketController($conn); $controller->view($matches[1]); break; case $requestPath == '/ticket/create': require_once 'controllers/TicketController.php'; $controller = new TicketController($conn); $controller->create(); break; // API Routes - these handle their own database connections case $requestPath == '/api/update_ticket.php': require_once 'api/update_ticket.php'; break; case $requestPath == '/api/add_comment.php': require_once 'api/add_comment.php'; break; case $requestPath == '/api/ticket_dependencies.php': require_once 'api/ticket_dependencies.php'; break; case $requestPath == '/api/upload_attachment.php': require_once 'api/upload_attachment.php'; break; case $requestPath == '/api/delete_attachment.php': require_once 'api/delete_attachment.php'; break; case $requestPath == '/api/get_users.php': require_once 'api/get_users.php'; break; case $requestPath == '/api/assign_ticket.php': require_once 'api/assign_ticket.php'; break; case $requestPath == '/api/get_template.php': require_once 'api/get_template.php'; break; case $requestPath == '/api/bulk_operation.php': require_once 'api/bulk_operation.php'; break; case $requestPath == '/api/export_tickets.php': require_once 'api/export_tickets.php'; break; case $requestPath == '/api/generate_api_key.php': require_once 'api/generate_api_key.php'; break; case $requestPath == '/api/revoke_api_key.php': require_once 'api/revoke_api_key.php'; break; case $requestPath == '/api/manage_templates.php': require_once 'api/manage_templates.php'; break; case $requestPath == '/api/manage_workflows.php': require_once 'api/manage_workflows.php'; break; case $requestPath == '/api/manage_recurring.php': require_once 'api/manage_recurring.php'; break; case $requestPath == '/api/check_duplicates.php': require_once 'api/check_duplicates.php'; break; // Admin Routes - require admin privileges case $requestPath == '/admin/recurring-tickets': if (!$currentUser || !$currentUser['is_admin']) { header("HTTP/1.0 403 Forbidden"); echo 'Admin access required'; break; } require_once 'models/RecurringTicketModel.php'; $recurringModel = new RecurringTicketModel($conn); $recurringTickets = $recurringModel->getAll(true); include 'views/admin/RecurringTicketsView.php'; break; case $requestPath == '/admin/custom-fields': if (!$currentUser || !$currentUser['is_admin']) { header("HTTP/1.0 403 Forbidden"); echo 'Admin access required'; break; } require_once 'models/CustomFieldModel.php'; $fieldModel = new CustomFieldModel($conn); $customFields = $fieldModel->getAllDefinitions(null, false); include 'views/admin/CustomFieldsView.php'; break; case $requestPath == '/admin/workflow': if (!$currentUser || !$currentUser['is_admin']) { header("HTTP/1.0 403 Forbidden"); echo 'Admin access required'; break; } $result = $conn->query("SELECT * FROM status_transitions ORDER BY from_status, to_status"); $workflows = []; while ($row = $result->fetch_assoc()) { $workflows[] = $row; } include 'views/admin/WorkflowDesignerView.php'; break; case $requestPath == '/admin/templates': if (!$currentUser || !$currentUser['is_admin']) { header("HTTP/1.0 403 Forbidden"); echo 'Admin access required'; break; } $result = $conn->query("SELECT * FROM ticket_templates ORDER BY template_name"); $templates = []; while ($row = $result->fetch_assoc()) { $templates[] = $row; } include 'views/admin/TemplatesView.php'; break; case $requestPath == '/admin/audit-log': if (!$currentUser || !$currentUser['is_admin']) { header("HTTP/1.0 403 Forbidden"); echo 'Admin access required'; break; } $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; $perPage = 50; $offset = ($page - 1) * $perPage; $filters = []; $whereConditions = []; $params = []; $types = ''; if (!empty($_GET['action_type'])) { $whereConditions[] = "al.action_type = ?"; $params[] = $_GET['action_type']; $types .= 's'; $filters['action_type'] = $_GET['action_type']; } if (!empty($_GET['user_id'])) { $whereConditions[] = "al.user_id = ?"; $params[] = (int)$_GET['user_id']; $types .= 'i'; $filters['user_id'] = $_GET['user_id']; } if (!empty($_GET['date_from'])) { $whereConditions[] = "DATE(al.created_at) >= ?"; $params[] = $_GET['date_from']; $types .= 's'; $filters['date_from'] = $_GET['date_from']; } if (!empty($_GET['date_to'])) { $whereConditions[] = "DATE(al.created_at) <= ?"; $params[] = $_GET['date_to']; $types .= 's'; $filters['date_to'] = $_GET['date_to']; } $where = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : ''; $countSql = "SELECT COUNT(*) as total FROM audit_log al $where"; if (!empty($params)) { $stmt = $conn->prepare($countSql); $stmt->bind_param($types, ...$params); $stmt->execute(); $countResult = $stmt->get_result(); } else { $countResult = $conn->query($countSql); } $totalLogs = $countResult->fetch_assoc()['total']; $totalPages = ceil($totalLogs / $perPage); $sql = "SELECT al.*, u.display_name, u.username FROM audit_log al LEFT JOIN users u ON al.user_id = u.user_id $where ORDER BY al.created_at DESC LIMIT $perPage OFFSET $offset"; if (!empty($params)) { $stmt = $conn->prepare($sql); $stmt->bind_param($types, ...$params); $stmt->execute(); $result = $stmt->get_result(); } else { $result = $conn->query($sql); } $auditLogs = []; while ($row = $result->fetch_assoc()) { $auditLogs[] = $row; } $usersResult = $conn->query("SELECT user_id, username, display_name FROM users ORDER BY display_name"); $users = []; while ($row = $usersResult->fetch_assoc()) { $users[] = $row; } include 'views/admin/AuditLogView.php'; break; case $requestPath == '/admin/api-keys': if (!$currentUser || !$currentUser['is_admin']) { header("HTTP/1.0 403 Forbidden"); echo 'Admin access required'; break; } require_once 'models/ApiKeyModel.php'; $apiKeyModel = new ApiKeyModel($conn); $apiKeys = $apiKeyModel->getAllKeys(); include 'views/admin/ApiKeysView.php'; break; case $requestPath == '/admin/user-activity': if (!$currentUser || !$currentUser['is_admin']) { header("HTTP/1.0 403 Forbidden"); echo 'Admin access required'; break; } $dateRange = [ 'from' => $_GET['date_from'] ?? date('Y-m-d', strtotime('-30 days')), 'to' => $_GET['date_to'] ?? date('Y-m-d') ]; $sql = "SELECT u.user_id, u.username, u.display_name, u.is_admin, (SELECT COUNT(*) FROM tickets t WHERE t.created_by = u.user_id AND DATE(t.created_at) BETWEEN ? AND ?) as tickets_created, (SELECT COUNT(*) FROM tickets t WHERE t.assigned_to = u.user_id AND t.status = 'Closed' AND DATE(t.updated_at) BETWEEN ? AND ?) as tickets_resolved, (SELECT COUNT(*) FROM ticket_comments tc WHERE tc.user_id = u.user_id AND DATE(tc.created_at) BETWEEN ? AND ?) as comments_added, (SELECT COUNT(*) FROM tickets t WHERE t.assigned_to = u.user_id AND DATE(t.created_at) BETWEEN ? AND ?) as tickets_assigned, (SELECT MAX(al.created_at) FROM audit_log al WHERE al.user_id = u.user_id) as last_activity FROM users u ORDER BY tickets_created DESC, tickets_resolved DESC"; $stmt = $conn->prepare($sql); $stmt->bind_param('ssssssss', $dateRange['from'], $dateRange['to'], $dateRange['from'], $dateRange['to'], $dateRange['from'], $dateRange['to'], $dateRange['from'], $dateRange['to'] ); $stmt->execute(); $result = $stmt->get_result(); $userStats = []; while ($row = $result->fetch_assoc()) { $userStats[] = $row; } $stmt->close(); include 'views/admin/UserActivityView.php'; break; // Legacy support for old URLs case $requestPath == '/dashboard.php': header("Location: /"); exit; case preg_match('/^\/ticket\.php/', $requestPath) && isset($_GET['id']): header("Location: /ticket/" . $_GET['id']); exit; default: // 404 Not Found header("HTTP/1.0 404 Not Found"); echo '404 Page Not Found'; break; } // Close database connection if it was opened if (isset($conn)) { $conn->close(); } ?>