false, 'error' => 'ต้องเปิด PHP extension curl'], 500); } function room_moderation_disk_path(): string { return dirname(__DIR__, 2) . '/Game/data/room-moderation.json'; } function room_approval_history_path(): string { return ADMIN_PRIVATE_DIR . '/room-approval-history.json'; } function sync_room_moderation_disk(bool $enabled): void { $path = room_moderation_disk_path(); $dir = dirname($path); if (!is_dir($dir)) { @mkdir($dir, 0755, true); } $json = json_encode(['enabled' => $enabled], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); if ($json !== false) { @file_put_contents($path, $json, LOCK_EX); } } function read_room_approval_history(): array { $path = room_approval_history_path(); if (!is_file($path)) { return []; } $raw = @file_get_contents($path); $j = json_decode($raw ?: '[]', true); return is_array($j) ? $j : []; } function append_room_approval_history(array $row): void { $rows = read_room_approval_history(); array_unshift($rows, $row); if (count($rows) > 500) { $rows = array_slice($rows, 0, 500); } if (!is_dir(ADMIN_PRIVATE_DIR)) { @mkdir(ADMIN_PRIVATE_DIR, 0750, true); } $json = json_encode($rows, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); if ($json !== false) { @file_put_contents(room_approval_history_path(), $json, LOCK_EX); } } function node_room_approvals_url(string $suffix = ''): string { $port = preg_replace('/[^0-9]/', '', (string)(getenv('GAME_NODE_PORT') ?: '13010')) ?: '13010'; $host = getenv('GAME_NODE_INTERNAL_HOST') ?: '127.0.0.1'; return 'http://' . $host . ':' . $port . '/Game/api/admin/room-approvals' . $suffix; } function proxy_node_room_approvals(string $method, ?string $body = null, string $suffix = ''): array { $ch = curl_init(node_room_approvals_url($suffix)); if ($ch === false) { return ['ok' => false, 'error' => 'curl init failed', 'httpCode' => 0]; } $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 => 12, ]; 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 ($out === false || $out === '') { return ['ok' => false, 'error' => 'ไม่ต่อถึงเซิร์ฟเวอร์เกม (Node): ' . $err, 'httpCode' => $code]; } $j = json_decode($out, true); if (!is_array($j)) { return ['ok' => false, 'error' => 'ตอบกลับจาก Node ไม่ถูกต้อง', 'httpCode' => $code]; } $j['httpCode'] = $code; return $j; } $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; $admin = current_admin(); if ($method === 'GET') { $action = isset($_GET['action']) ? (string)$_GET['action'] : 'pending'; $store = read_store(); $enabled = !empty($store['roomModeration']['enabled']); sync_room_moderation_disk($enabled); if ($action === 'settings') { json_response([ 'ok' => true, 'enabled' => $enabled, ]); } if ($action === 'history') { json_response([ 'ok' => true, 'history' => read_room_approval_history(), 'enabled' => $enabled, ]); } $node = proxy_node_room_approvals('GET'); if (empty($node['ok'])) { json_response([ 'ok' => true, 'enabled' => $enabled, 'pending' => [], 'nodeError' => $node['error'] ?? 'Node unavailable', ]); } json_response([ 'ok' => true, 'enabled' => $enabled, 'nodeModerationEnabled' => !empty($node['moderationEnabled']), 'pending' => $node['pending'] ?? [], ]); } if ($method === 'PATCH') { $raw = file_get_contents('php://input') ?: '{}'; $body = json_decode($raw, true); if (!is_array($body)) { json_response(['ok' => false, 'error' => 'ข้อมูลไม่ถูกต้อง'], 400); } $store = read_store(); if (!isset($store['roomModeration']) || !is_array($store['roomModeration'])) { $store['roomModeration'] = ['enabled' => true]; } if (array_key_exists('enabled', $body)) { $store['roomModeration']['enabled'] = !!$body['enabled']; } if (!write_store($store)) { json_response(['ok' => false, 'error' => 'บันทึกการตั้งค่าไม่สำเร็จ'], 500); } sync_room_moderation_disk(!empty($store['roomModeration']['enabled'])); json_response(['ok' => true, 'enabled' => !empty($store['roomModeration']['enabled'])]); } if ($method === 'POST') { $raw = file_get_contents('php://input') ?: '{}'; $body = json_decode($raw, true); if (!is_array($body)) { json_response(['ok' => false, 'error' => 'ข้อมูลไม่ถูกต้อง'], 400); } $spaceId = isset($body['spaceId']) ? trim((string)$body['spaceId']) : ''; $act = isset($body['action']) ? trim((string)$body['action']) : ''; $note = isset($body['note']) ? trim((string)$body['note']) : ''; if ($spaceId === '' || !in_array($act, ['approve', 'reject', 'dismiss'], true)) { json_response(['ok' => false, 'error' => 'ต้องระบุ spaceId และ action (reject|dismiss)'], 400); } $meta = isset($body['meta']) && is_array($body['meta']) ? $body['meta'] : []; $payload = json_encode([ 'spaceId' => $spaceId, 'action' => $act, 'note' => $note, ], JSON_UNESCAPED_UNICODE); $result = proxy_node_room_approvals('POST', $payload, ''); if (empty($result['ok'])) { json_response(['ok' => false, 'error' => $result['error'] ?? 'ดำเนินการไม่สำเร็จ'], !empty($result['httpCode']) && $result['httpCode'] >= 400 ? (int)$result['httpCode'] : 400); } append_room_approval_history([ 'at' => gmdate('c'), 'action' => $act, 'spaceId' => $spaceId, 'spaceName' => $meta['spaceName'] ?? $spaceId, 'creatorDisplayName' => $meta['creatorDisplayName'] ?? '', 'creatorPlayerKey' => $meta['creatorPlayerKey'] ?? '', 'adminUsername' => is_array($admin) ? ($admin['username'] ?? '') : '', 'note' => $note, 'kicked' => isset($result['kicked']) ? (int)$result['kicked'] : null, ]); json_response([ 'ok' => true, 'action' => $act, 'spaceId' => $spaceId, 'kicked' => $result['kicked'] ?? null, ]); } json_response(['ok' => false, 'error' => 'Method not allowed'], 405);