togethere.cloud/private_html/api/loadUsers.php

213 lines
7.3 KiB
PHP

<?php
// Włączenie raportowania błędów dla debugowania
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
// Funkcja do zwracania błędów jako JSON
function returnError($message, $code = 500) {
http_response_code($code);
echo json_encode([
'success' => false,
'error' => $message
], JSON_UNESCAPED_UNICODE);
exit;
}
// Konfiguracja bazy danych
$host = "localhost";
$db = "togethere_cloud";
$user = "root";
$pass = "HasloDoSQL";
try {
$pdo->exec("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci");
// Parametry z requestu
$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$limit = isset($_GET['limit']) ? min(100, max(1, (int)$_GET['limit'])) : 50;
$offset = ($page - 1) * $limit;
// Sortowanie
$sortBy = isset($_GET['sortBy']) ? $_GET['sortBy'] : 'id';
$sortOrder = isset($_GET['sortOrder']) && strtoupper($_GET['sortOrder']) === 'DESC' ? 'DESC' : 'ASC';
// Dozwolone kolumny do sortowania (bezpieczeństwo)
$allowedSortColumns = ['id', 'username', 'email', 'created_at', 'role'];
if (!in_array($sortBy, $allowedSortColumns)) {
$sortBy = 'id';
}
// Filtrowanie
$filters = [];
$params = [];
// Filtr po username
if (isset($_GET['username']) && $_GET['username'] !== '') {
$filters[] = "u.username LIKE :username";
$params[':username'] = '%' . $_GET['username'] . '%';
}
// Filtr po email
if (isset($_GET['email']) && $_GET['email'] !== '') {
$filters[] = "u.email LIKE :email";
$params[':email'] = '%' . $_GET['email'] . '%';
}
// Filtr po roli
if (isset($_GET['role']) && $_GET['role'] !== '') {
$filters[] = "u.role = :role";
$params[':role'] = $_GET['role'];
}
// Filtr po statusie weryfikacji
if (isset($_GET['email_verified'])) {
$filters[] = "u.email_verified = :email_verified";
$params[':email_verified'] = (int)$_GET['email_verified'];
}
// Filtr po dacie rejestracji (od)
if (isset($_GET['created_from']) && $_GET['created_from'] !== '') {
$filters[] = "u.created_at >= :created_from";
$params[':created_from'] = $_GET['created_from'];
}
// Filtr po dacie rejestracji (do)
if (isset($_GET['created_to']) && $_GET['created_to'] !== '') {
$filters[] = "u.created_at <= :created_to";
$params[':created_to'] = $_GET['created_to'];
}
// Wyklucz użytkowników z disabled = 1
$filters[] = "(u.disabled IS NULL OR u.disabled = 0)";
// Budowanie WHERE clause
$whereClause = '';
if (count($filters) > 0) {
$whereClause = 'WHERE ' . implode(' AND ', $filters);
}
// OPTYMALIZACJA: Fast approximate count z limitem 100k
// Sprawdzenie czy count jest w cache (ważny 5 minut)
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/session_bootstrap.php';
$cacheKey = 'users_count_' . md5(serialize($params));
$totalRecords = 0;
$isApproximate = false;
if (isset($_SESSION[$cacheKey]) &&
isset($_SESSION[$cacheKey . '_time']) &&
(time() - $_SESSION[$cacheKey . '_time']) < 300) {
// Cache hit - użyj zapisanej wartości
$totalRecords = $_SESSION[$cacheKey];
$isApproximate = $_SESSION[$cacheKey . '_approx'] ?? false;
} else {
// Cache miss - policz z limitem
// OPTYMALIZACJA: Limit count do 100k dla wydajności
$countSql = "SELECT COUNT(*) as total FROM (
SELECT 1 FROM users u $whereClause LIMIT 100000
) as limited_count";
$countStmt = $pdo->prepare($countSql);
try {
$countStmt->execute($params);
$totalRecords = $countStmt->fetch(PDO::FETCH_ASSOC)['total'];
// Jeśli osiągnięto limit, sprawdź czy jest więcej
if ($totalRecords >= 100000) {
$checkMoreSql = "SELECT EXISTS(
SELECT 1 FROM users u $whereClause LIMIT 100001
) as has_more";
$checkStmt = $pdo->prepare($checkMoreSql);
$checkStmt->execute($params);
if ($checkStmt->fetch(PDO::FETCH_ASSOC)['has_more']) {
$isApproximate = true;
$totalRecords = 100000; // Pokazuj 100k+
}
}
// Zapisz w cache na 5 minut
$_SESSION[$cacheKey] = $totalRecords;
$_SESSION[$cacheKey . '_time'] = time();
$_SESSION[$cacheKey . '_approx'] = $isApproximate;
} catch (PDOException $e) {
returnError('Błąd podczas zliczania rekordów: ' . $e->getMessage());
}
}
$totalPages = $totalRecords > 0 ? ceil($totalRecords / $limit) : 1;
// Pobieranie użytkowników + podstawowe statystyki salda (lekki LEFT JOIN)
$sql = "SELECT
u.id,
u.username,
u.email,
u.first_name,
u.last_name,
u.role,
u.email_verified,
u.created_at,
COALESCE(us.balance, 0) as balance,
COALESCE(us.matches_played, 0) as matches_played,
COALESCE(us.matches_won, 0) as matches_won,
COALESCE(us.matches_lost, 0) as matches_lost,
us.account_status
FROM users u
LEFT JOIN user_stats us ON u.id = us.user_id
$whereClause
ORDER BY u.$sortBy $sortOrder
LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($sql);
// Bindowanie parametrów filtrów
foreach ($params as $key => $value) {
$stmt->bindValue($key, $value);
}
// Bindowanie limit i offset
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
try {
$stmt->execute();
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
returnError('Błąd podczas pobierania użytkowników: ' . $e->getMessage());
}
// Formatowanie odpowiedzi
$response = [
'success' => true,
'data' => $users,
'pagination' => [
'currentPage' => $page,
'totalPages' => $totalPages,
'totalRecords' => (int)$totalRecords,
'totalRecordsApproximate' => $isApproximate,
'totalRecordsDisplay' => $isApproximate ? '100,000+' : number_format($totalRecords, 0, ',', ' '),
'recordsPerPage' => $limit,
'hasNextPage' => $page < $totalPages,
'hasPreviousPage' => $page > 1
],
'filters' => [
'sortBy' => $sortBy,
'sortOrder' => $sortOrder,
'appliedFilters' => array_keys($params)
]
];
echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
} catch (PDOException $e) {
returnError('Błąd połączenia z bazą danych: ' . $e->getMessage());
} catch (Exception $e) {
returnError('Nieoczekiwany błąd: ' . $e->getMessage());
}
?>