215 lines
7.6 KiB
PHP
215 lines
7.6 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;
|
|
}
|
|
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/session_bootstrap.php';
|
|
|
|
try {
|
|
$pdo = og_session_get_pdo();
|
|
if (!$pdo instanceof PDO) {
|
|
throw new PDOException('Nie udało się zainicjalizować połączenia z bazą danych.');
|
|
}
|
|
|
|
// 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)
|
|
$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(u.account_suspended, 0) as account_suspended,
|
|
u.suspension_reason,
|
|
u.suspended_until,
|
|
a.username AS suspended_by_username,
|
|
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
|
|
LEFT JOIN users a ON a.id = u.suspended_by
|
|
$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());
|
|
}
|
|
?>
|
|
|