SESSION_LIFETIME, 'path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax', ]); session_start(); } // ── Auth checks ────────────────────────────────────────────── function isLoggedIn(): bool { return isset($_SESSION['user_id']) && !empty($_SESSION['user_id']); } function requireLogin(string $redirect = '/auth/login.php'): void { if (!isLoggedIn()) { header('Location: ' . APP_URL . $redirect); exit; } } function requireAdmin(): void { requireLogin('/auth/login.php'); if (!in_array($_SESSION['user_role'] ?? '', ['super_admin', 'manager'])) { header('Location: ' . APP_URL . '/dashboard.php'); exit; } } function requireSuperAdmin(): void { requireLogin('/auth/login.php'); if (($_SESSION['user_role'] ?? '') !== 'super_admin') { header('Location: ' . APP_URL . '/admin/index.php'); exit; } } function currentUser(bool $refresh = false): ?array { if (!isLoggedIn()) return null; static $user = null; if ($user === null || $refresh) { $user = DB::row( "SELECT u.*, p.name AS plan_name, p.surveys_limit, p.responses_monthly, p.questions_limit, p.file_uploads, p.custom_branding, p.api_access, p.export_formats FROM users u JOIN plans p ON u.plan_id = p.id WHERE u.id = ? AND u.is_active = 1", [$_SESSION['user_id']] ); } return $user; } function loginUser(array $user): void { session_regenerate_id(true); $_SESSION['user_id'] = $user['id']; $_SESSION['user_role'] = $user['role']; $_SESSION['user_name'] = $user['name']; DB::query("UPDATE users SET last_login = NOW() WHERE id = ?", [$user['id']]); } function logoutUser(): void { $_SESSION = []; if (ini_get('session.use_cookies')) { $p = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $p['path'], $p['domain'], $p['secure'], $p['httponly']); } session_destroy(); } // ── CSRF ───────────────────────────────────────────────────── function csrfToken(): string { if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['csrf_token']; } function verifyCsrf(string $token): bool { return hash_equals($_SESSION['csrf_token'] ?? '', $token); } function csrfField(): string { return ''; } // ── Plan / quota helpers ────────────────────────────────────── function canCreateSurvey(): bool { $user = currentUser(); if (!$user) return false; if (in_array($user['role'], ['super_admin', 'manager'])) return true; if ($user['surveys_limit'] === -1) return true; return $user['surveys_created'] < $user['surveys_limit']; } function canCollectResponse(int $survey_id): bool { $survey = DB::row("SELECT user_id FROM surveys WHERE id = ?", [$survey_id]); if (!$survey) return false; $user = DB::row( "SELECT u.role, u.responses_used_this_month, u.responses_reset_date, p.responses_monthly FROM users u JOIN plans p ON u.plan_id = p.id WHERE u.id = ?", [$survey['user_id']] ); if (!$user) return false; if (in_array($user['role'], ['super_admin', 'manager'])) return true; if ($user['responses_monthly'] === -1) return true; // Reset monthly count if needed if ($user['responses_reset_date'] && $user['responses_reset_date'] < date('Y-m-01')) { DB::query("UPDATE users SET responses_used_this_month=0, responses_reset_date=? WHERE id=?", [date('Y-m-01'), $survey['user_id']]); return true; } return $user['responses_used_this_month'] < $user['responses_monthly']; } function incrementResponseCount(int $survey_id): void { $survey = DB::row("SELECT user_id FROM surveys WHERE id = ?", [$survey_id]); if ($survey) { DB::query("UPDATE users SET responses_used_this_month = responses_used_this_month + 1 WHERE id = ?", [$survey['user_id']]); DB::query("UPDATE surveys SET response_count = response_count + 1 WHERE id = ?", [$survey_id]); } }