94 lines
3.5 KiB
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.
|
|
]);
|