togethere.cloud/private_html/api/matches/ping-pong/1v1/index.php

94 lines
3.5 KiB
PHP

<?php
// Rewards queue endpoint for ping-pong 1v1.
// Called by the Node match server after match ends.
require_once __DIR__ . '/internal/respond.php';
require_once __DIR__ . '/internal/env.php';
require_once __DIR__ . '/internal/hmac.php';
require_once __DIR__ . '/../../../administration/includes/config.php';
if (!isset($pdo) || !($pdo instanceof PDO)) {
og_respond(['success' => false, 'error' => 'Database connection not initialized'], 500);
}
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit(0);
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
og_respond(['success' => false, 'error' => 'Method not allowed'], 405);
}
$secret = og_env('PINGPONG_1V1_SHARED_SECRET');
if (!$secret) {
og_respond(['success' => false, 'error' => 'Server not configured (missing PINGPONG_1V1_SHARED_SECRET)'], 500);
}
$raw = file_get_contents('php://input');
$check = og_require_node_signature($secret, $raw, 60000);
if (empty($check['ok'])) {
og_respond(['success' => false, 'error' => 'Invalid signature', 'code' => $check['error']], 401);
}
$payload = json_decode($raw, true);
if (!$payload || !is_array($payload)) {
og_respond(['success' => false, 'error' => 'Invalid JSON'], 400);
}
$matchId = isset($payload['matchId']) ? (int) $payload['matchId'] : 0;
$winnerUserId = isset($payload['winnerUserId']) ? (int) $payload['winnerUserId'] : 0;
$loserUserId = isset($payload['loserUserId']) ? (int) $payload['loserUserId'] : 0;
$score = isset($payload['score']) ? (string) $payload['score'] : null;
if ($matchId <= 0 || $winnerUserId <= 0 || $loserUserId <= 0 || !$score) {
og_respond(['success' => false, 'error' => 'Missing required fields'], 400);
}
// Ensure table exists (idempotent)
$pdo->exec(
"CREATE TABLE IF NOT EXISTS rewards_jobs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
discipline VARCHAR(50) NOT NULL,
mode VARCHAR(50) NOT NULL,
match_id BIGINT UNSIGNED NOT NULL,
payload_json LONGTEXT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'queued',
attempts INT NOT NULL DEFAULT 0,
result_json LONGTEXT NULL,
last_error TEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uniq_match (discipline, mode, match_id),
INDEX idx_status_created (status, created_at),
INDEX idx_match (match_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
);
// idempotent: avoid duplicate jobs if Node retries
$stmt = $pdo->prepare(
'INSERT IGNORE INTO rewards_jobs (discipline, mode, match_id, payload_json, status) VALUES (:discipline, :mode, :match_id, :payload_json, :status)'
);
$stmt->execute([
':discipline' => 'ping-pong',
':mode' => '1v1',
':match_id' => $matchId,
':payload_json' => json_encode($payload, JSON_UNESCAPED_UNICODE),
':status' => 'queued',
]);
$stmt2 = $pdo->prepare('SELECT id, status FROM rewards_jobs WHERE discipline = :d AND mode = :m AND match_id = :mid');
$stmt2->execute([':d' => 'ping-pong', ':m' => '1v1', ':mid' => $matchId]);
$row = $stmt2->fetch(PDO::FETCH_ASSOC);
if (!$row) {
og_respond(['success' => false, 'error' => 'Failed to enqueue rewards job'], 500);
}
$jobId = (int) $row['id'];
og_respond([
'success' => true,
'jobId' => $jobId,
'status' => 'queued',
// UI can show "processing" animation and poll status endpoint.
]);