467363d651
Made-with: Cursor
177 lines
6.4 KiB
PHP
177 lines
6.4 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Proxy ค่าคำถาม quiz-settings ระหว่างแผง Admin กับ Node (Game/server.js)
|
|
* แก้กรณี nginx ส่ง /Game/ เป็น static แล้ว PUT ไม่ถึง Node — เบราว์เซอร์เรียก PHP แทน
|
|
*
|
|
* รองรับ Node เวอร์ชันเก่าที่ GET ไม่ส่ง battleQuizMcq / PUT เขียนทับไฟล์แล้วตัดฟิลด์:
|
|
* - GET: ดึง battleQuizMcq จากไฟล์บนดิสก์มาแปะในคำตอบ
|
|
* - PUT: merge body ลง quiz-settings.json บนดิสก์เสมอ (หลัง proxy Node) เพื่อให้ข้อมูลคงอยู่หลังรีเฟรช
|
|
*/
|
|
require __DIR__ . '/_common.php';
|
|
|
|
require_login();
|
|
|
|
if (!function_exists('curl_init')) {
|
|
json_response(['ok' => false, 'error' => 'ต้องเปิด PHP extension curl (ติดตั้งแพ็กเกจ php-curl แล้วรีสตาร์ท PHP-FPM)'], 500);
|
|
}
|
|
|
|
$port = preg_replace('/[^0-9]/', '', (string)(getenv('GAME_NODE_PORT') ?: '3001')) ?: '3001';
|
|
$host = getenv('GAME_NODE_INTERNAL_HOST') ?: '127.0.0.1';
|
|
$target = 'http://' . $host . ':' . $port . '/Game/api/quiz-settings';
|
|
|
|
function quiz_settings_disk_path(): string
|
|
{
|
|
return dirname(__DIR__, 2) . '/Game/data/quiz-settings.json';
|
|
}
|
|
|
|
/**
|
|
* รวมฟิลด์ที่ส่งมาใน PUT เข้ากับไฟล์บนดิสก์ (ไม่ทับฟิลด์ที่ไม่ได้ส่งมา)
|
|
*/
|
|
function merge_quiz_settings_disk_from_put(string $rawBody): bool
|
|
{
|
|
$patch = json_decode($rawBody, true);
|
|
if (!is_array($patch)) {
|
|
return false;
|
|
}
|
|
$path = quiz_settings_disk_path();
|
|
$base = [];
|
|
if (is_file($path)) {
|
|
$prev = @file_get_contents($path);
|
|
if ($prev !== false && $prev !== '') {
|
|
$d = json_decode($prev, true);
|
|
if (is_array($d)) {
|
|
$base = $d;
|
|
}
|
|
}
|
|
}
|
|
$mergeKeys = ['readMs', 'answerMs', 'betweenMs', 'questions', 'carryQuestions', 'battleQuizMcq'];
|
|
foreach ($mergeKeys as $k) {
|
|
if (array_key_exists($k, $patch)) {
|
|
$base[$k] = $patch[$k];
|
|
}
|
|
}
|
|
$dir = dirname($path);
|
|
if (!is_dir($dir)) {
|
|
if (!@mkdir($dir, 0755, true)) {
|
|
return false;
|
|
}
|
|
}
|
|
$json = json_encode($base, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|
if ($json === false) {
|
|
return false;
|
|
}
|
|
return @file_put_contents($path, $json) !== false;
|
|
}
|
|
|
|
/**
|
|
* Node เก่าอาจไม่ใส่ battleQuizMcq ใน JSON — อ่านจากดิสก์มาแปะให้แอดมินโหลดได้หลังรีเฟรช
|
|
*/
|
|
function overlay_battle_quiz_mcq_from_disk(string $nodeBody): string
|
|
{
|
|
$j = json_decode($nodeBody, true);
|
|
if (!is_array($j)) {
|
|
return $nodeBody;
|
|
}
|
|
$path = quiz_settings_disk_path();
|
|
if (!is_file($path)) {
|
|
return $nodeBody;
|
|
}
|
|
$raw = @file_get_contents($path);
|
|
if ($raw === false || $raw === '') {
|
|
return $nodeBody;
|
|
}
|
|
$disk = json_decode($raw, true);
|
|
if (!is_array($disk) || !array_key_exists('battleQuizMcq', $disk)) {
|
|
return $nodeBody;
|
|
}
|
|
$j['battleQuizMcq'] = $disk['battleQuizMcq'];
|
|
$enc = json_encode($j, JSON_UNESCAPED_UNICODE);
|
|
return $enc !== false ? $enc : $nodeBody;
|
|
}
|
|
|
|
function proxy_quiz_curl(string $method, ?string $body = null): void
|
|
{
|
|
global $target;
|
|
$ch = curl_init($target);
|
|
if ($ch === false) {
|
|
json_response(['ok' => false, 'error' => 'curl init failed'], 500);
|
|
}
|
|
$headers = ['Accept: application/json'];
|
|
if ($body !== null && $method !== 'GET') {
|
|
$headers[] = 'Content-Type: application/json';
|
|
}
|
|
$opts = [
|
|
CURLOPT_CUSTOMREQUEST => $method,
|
|
CURLOPT_HTTPHEADER => $headers,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 15,
|
|
];
|
|
if ($body !== null && $method !== 'GET') {
|
|
$opts[CURLOPT_POSTFIELDS] = $body;
|
|
}
|
|
curl_setopt_array($ch, $opts);
|
|
$out = curl_exec($ch);
|
|
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$err = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($method === 'PUT' && $body !== null && $body !== '') {
|
|
merge_quiz_settings_disk_from_put($body);
|
|
}
|
|
|
|
if ($out === false || $out === '') {
|
|
if ($method === 'GET') {
|
|
$disk = quiz_settings_disk_path();
|
|
if (is_file($disk)) {
|
|
$rawFile = @file_get_contents($disk);
|
|
if ($rawFile !== false && $rawFile !== '') {
|
|
http_response_code(200);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('X-Content-Type-Options: nosniff');
|
|
header('Cache-Control: no-store');
|
|
echo $rawFile;
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
if ($method === 'PUT' && $body !== null && $body !== '' && is_file(quiz_settings_disk_path())) {
|
|
http_response_code(200);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('X-Content-Type-Options: nosniff');
|
|
header('Cache-Control: no-store');
|
|
echo json_encode(['ok' => true, 'diskFallback' => true], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
json_response([
|
|
'ok' => false,
|
|
'error' => 'ไม่ต่อถึงเซิร์ฟเวอร์เกม (Node) — ตรวจว่า pm2/systemd รัน Game/server.js อยู่ที่พอร์ต ' . (getenv('GAME_NODE_PORT') ?: '3001') . ' · ' . $err,
|
|
], 502);
|
|
}
|
|
http_response_code($code > 0 ? $code : 500);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('X-Content-Type-Options: nosniff');
|
|
header('Cache-Control: no-store');
|
|
if ($method === 'GET' && $code >= 200 && $code < 300) {
|
|
echo overlay_battle_quiz_mcq_from_disk($out);
|
|
} else {
|
|
echo $out;
|
|
}
|
|
exit;
|
|
}
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
|
if ($method === 'GET') {
|
|
proxy_quiz_curl('GET', null);
|
|
}
|
|
if ($method === 'PUT') {
|
|
$raw = file_get_contents('php://input');
|
|
if ($raw === false) {
|
|
$raw = '';
|
|
}
|
|
proxy_quiz_curl('PUT', $raw);
|
|
}
|
|
|
|
json_response(['ok' => false, 'error' => 'Method not allowed'], 405);
|