false, 'error' => 'Authentication required']); exit; } // Check admin privileges if (!$_SESSION['user']['is_admin']) { http_response_code(403); echo json_encode(['success' => false, 'error' => 'Admin privileges required']); exit; } $currentUserId = $_SESSION['user']['user_id']; // CSRF Protection for write operations if ($_SERVER['REQUEST_METHOD'] !== 'GET') { require_once dirname(__DIR__) . '/middleware/CsrfMiddleware.php'; $csrfToken = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''; if (!CsrfMiddleware::validateToken($csrfToken)) { http_response_code(403); echo json_encode(['success' => false, 'error' => 'Invalid CSRF token']); exit; } } // Use centralized database connection $conn = Database::getConnection(); header('Content-Type: application/json'); $model = new RecurringTicketModel($conn); $method = $_SERVER['REQUEST_METHOD']; $id = isset($_GET['id']) ? (int)$_GET['id'] : null; $action = isset($_GET['action']) ? $_GET['action'] : null; switch ($method) { case 'GET': if ($id) { $recurring = $model->getById($id); echo json_encode(['success' => (bool)$recurring, 'recurring' => $recurring]); } else { $all = $model->getAll(true); echo json_encode(['success' => true, 'recurring_tickets' => $all]); } break; case 'POST': if ($action === 'toggle' && $id) { $result = $model->toggleActive($id); echo json_encode($result); } else { $data = json_decode(file_get_contents('php://input'), true); // Calculate next run time $nextRun = calculateNextRun( $data['schedule_type'], $data['schedule_day'] ?? null, $data['schedule_time'] ?? '09:00' ); $data['next_run_at'] = $nextRun; $data['is_active'] = isset($data['is_active']) ? (int)$data['is_active'] : 1; $data['created_by'] = $currentUserId; $result = $model->create($data); echo json_encode($result); } break; case 'PUT': if (!$id) { echo json_encode(['success' => false, 'error' => 'ID required']); exit; } $data = json_decode(file_get_contents('php://input'), true); // Recalculate next run time if schedule changed $nextRun = calculateNextRun( $data['schedule_type'], $data['schedule_day'] ?? null, $data['schedule_time'] ?? '09:00' ); $data['next_run_at'] = $nextRun; $data['is_active'] = isset($data['is_active']) ? (int)$data['is_active'] : 1; $result = $model->update($id, $data); echo json_encode($result); break; case 'DELETE': if (!$id) { echo json_encode(['success' => false, 'error' => 'ID required']); exit; } $result = $model->delete($id); echo json_encode($result); break; default: http_response_code(405); echo json_encode(['success' => false, 'error' => 'Method not allowed']); } } catch (Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => $e->getMessage()]); } function calculateNextRun($scheduleType, $scheduleDay, $scheduleTime) { $now = new DateTime(); $time = $scheduleTime ?: '09:00'; switch ($scheduleType) { case 'daily': $next = new DateTime('tomorrow ' . $time); break; case 'weekly': $days = ['', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; $dayName = $days[$scheduleDay] ?? 'Monday'; $next = new DateTime("next {$dayName} " . $time); break; case 'monthly': $day = max(1, min(28, (int)$scheduleDay)); $next = new DateTime(); $next->modify('first day of next month'); $next->setDate($next->format('Y'), $next->format('m'), $day); list($h, $m) = explode(':', $time); $next->setTime((int)$h, (int)$m, 0); break; default: $next = new DateTime('tomorrow ' . $time); } return $next->format('Y-m-d H:i:s'); }