togethere.cloud/public_html/api/admin_match_results.php

194 lines
7.6 KiB
PHP

<?php
ob_start();
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/session_bootstrap.php';
header('Content-Type: application/json; charset=utf-8');
// Admin only
if (empty($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true
|| empty($_SESSION['role']) || $_SESSION['role'] !== 'admin') {
ob_clean();
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
exit;
}
$pdo = og_session_get_pdo();
if (!$pdo) {
ob_clean();
echo json_encode(['success' => false, 'error' => 'DB unavailable']);
exit;
}
// ── Auto-create match_results if missing ─────────────────────────────────────
try {
$pdo->exec("CREATE TABLE IF NOT EXISTS match_results (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
match_key VARCHAR(100) NOT NULL DEFAULT '',
match_id BIGINT UNSIGNED NULL,
discipline VARCHAR(50) NOT NULL DEFAULT '',
mode VARCHAR(50) NOT NULL DEFAULT '1v1',
winner_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
loser_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
winner_username VARCHAR(100) NOT NULL DEFAULT '',
loser_username VARCHAR(100) NOT NULL DEFAULT '',
score VARCHAR(200) NOT NULL DEFAULT '',
sets_winner TINYINT NOT NULL DEFAULT 0,
sets_loser TINYINT NOT NULL DEFAULT 0,
reason VARCHAR(50) NOT NULL DEFAULT '',
ended_at DATETIME NULL,
payload_json LONGTEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uniq_match_key (discipline, mode, match_key),
INDEX idx_winner (winner_user_id),
INDEX idx_loser (loser_user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
} catch (Exception $e) {
// table may already exist with different schema -- ignore
}
// ── Check which optional columns exist in matches ────────────────────────────
function matchesHasColumn(PDO $pdo, string $col): bool {
static $cache = [];
if (!isset($cache[$col])) {
$cache[$col] = (int)$pdo->query(
"SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE() AND table_name = 'matches' AND column_name = " . $pdo->quote($col)
)->fetchColumn() > 0;
}
return $cache[$col];
}
// ── Helper: fetch matches by status with usernames ────────────────────────────
function fetchMatchesByStatus(PDO $pdo, string $status): array {
$disciplineExpr = matchesHasColumn($pdo, 'Discipline')
? "COALESCE(m.Discipline, 'ping-pong')"
: "'ping-pong'";
$sql = "SELECT
m.id,
{$disciplineExpr} AS discipline,
m.Status AS status,
COALESCE(m.Score, '0:0') AS score,
m.StartTime AS started_at,
m.EndTime AS ended_at,
m.Team1_ID AS user1_id,
m.Team2_ID AS user2_id,
COALESCE(u1.username, '') AS user1_username,
COALESCE(u2.username, '') AS user2_username
FROM matches m
LEFT JOIN users u1 ON u1.id = m.Team1_ID
LEFT JOIN users u2 ON u2.id = m.Team2_ID
WHERE m.Status = :status
ORDER BY m.StartTime DESC
LIMIT 200";
$stmt = $pdo->prepare($sql);
$stmt->execute([':status' => $status]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// ── Fetch live / planned / ended sessions ────────────────────────────────────
try {
$live = fetchMatchesByStatus($pdo, 'live');
$planned = fetchMatchesByStatus($pdo, 'planned');
$ended = fetchMatchesByStatus($pdo, 'end');
} catch (Exception $e) {
ob_clean();
echo json_encode(['success' => false, 'error' => 'matches_query: ' . $e->getMessage()]);
exit;
}
// ── Paginated / filtered match_results ───────────────────────────────────────
$page = max(1, (int)($_GET['page'] ?? 1));
$limit = min(100, max(1, (int)($_GET['limit'] ?? 25)));
$offset = ($page - 1) * $limit;
$allowedSort = ['id','discipline','mode','winner_username','loser_username','score','reason','ended_at','created_at'];
$sortBy = in_array($_GET['sortBy'] ?? '', $allowedSort) ? $_GET['sortBy'] : 'ended_at';
$sortOrder = strtoupper($_GET['sortOrder'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
// Filters
$filters = [];
$params = [];
if (!empty($_GET['user'])) {
$like = '%' . $_GET['user'] . '%';
$filters[] = '(r.winner_username LIKE :user1 OR r.loser_username LIKE :user2)';
$params[':user1'] = $like;
$params[':user2'] = $like;
}
if (!empty($_GET['discipline'])) {
$filters[] = 'r.discipline = :discipline';
$params[':discipline'] = $_GET['discipline'];
}
if (!empty($_GET['mode'])) {
$filters[] = 'r.mode = :mode';
$params[':mode'] = $_GET['mode'];
}
if (!empty($_GET['reason'])) {
$filters[] = 'r.reason = :reason';
$params[':reason'] = $_GET['reason'];
}
if (!empty($_GET['date_from'])) {
$filters[] = 'r.ended_at >= :date_from';
$params[':date_from'] = $_GET['date_from'] . ' 00:00:00';
}
if (!empty($_GET['date_to'])) {
$filters[] = 'r.ended_at <= :date_to';
$params[':date_to'] = $_GET['date_to'] . ' 23:59:59';
}
$where = $filters ? 'WHERE ' . implode(' AND ', $filters) : '';
$totalRecords = 0;
$totalPages = 1;
$results = [];
try {
// Count
$countSql = "SELECT COUNT(*) FROM match_results r $where";
$countStmt = $pdo->prepare($countSql);
$countStmt->execute($params);
$totalRecords = (int)$countStmt->fetchColumn();
$totalPages = $totalRecords > 0 ? (int)ceil($totalRecords / $limit) : 1;
// Rows
$rowsSql = "SELECT r.id, r.match_id, r.discipline, r.mode,
r.winner_user_id, r.winner_username,
r.loser_user_id, r.loser_username,
r.score, r.sets_winner, r.sets_loser,
r.reason, r.ended_at, r.created_at
FROM match_results r
$where
ORDER BY r.`$sortBy` $sortOrder
LIMIT :limit OFFSET :offset";
$rowsStmt = $pdo->prepare($rowsSql);
foreach ($params as $k => $v) {
$rowsStmt->bindValue($k, $v);
}
$rowsStmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$rowsStmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$rowsStmt->execute();
$results = $rowsStmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
ob_clean();
echo json_encode(['success' => false, 'error' => 'results_query: ' . $e->getMessage()]);
exit;
}
ob_clean();
echo json_encode([
'success' => true,
'live' => $live,
'planned' => $planned,
'ended_sessions' => $ended,
'results' => $results,
'pagination' => [
'currentPage' => $page,
'totalPages' => $totalPages,
'totalRecords' => $totalRecords,
'recordsPerPage' => $limit,
'hasNextPage' => $page < $totalPages,
'hasPreviousPage' => $page > 1,
],
], JSON_UNESCAPED_UNICODE);