WHAT WAS DONE: - Built browser dashboard (dashboard.html) showing installed vs latest version for all Pterodactyl game servers - Built PHP proxy (proxy.php + config.php) for Billing VPS deployment - Created isolated Nginx server block (version-proxy.conf) - Created full deployment guide (DEPLOYMENT-GUIDE.md) ARCHITECTURE: - PHP proxy at /var/www/version-proxy on Billing VPS (38.68.14.188) - Isolated from Paymenter/Laravel routing — separate directory + port - API keys (Pterodactyl ptlc_, CurseForge) live server-side only - FTB packs: fully automatic via .manifest.json + FTB public API - CurseForge packs: reads manifest.json, needs CF Project ID + API key - config.php blocked from direct web access via Nginx PENDING AT DEPLOYMENT: - Verify port 8080 is free (ss -tlnp) before enabling Nginx block - Fill real API keys into config.php on server - Enter CurseForge Project IDs for CF packs (saved in localStorage) COLLABORATION: - PHP proxy architecture designed by Gemini (consultation session 2026-03-29) - Dashboard HTML and detection logic by Chronicler #47 - Gemini identified Laravel routing conflict and content-type gotcha WHY: - Interim solution before full Blueprint extension (post-launch) - Hands-off modpack update monitoring for staff - Zero manual checking required after initial CF Project ID setup Signed-off-by: claude@firefrostgaming.com
72 lines
2.5 KiB
PHP
72 lines
2.5 KiB
PHP
<?php
|
|
// proxy.php
|
|
// Deploy to /var/www/version-proxy/ on Billing VPS (38.68.14.188)
|
|
// Serves as CORS-safe API bridge between the dashboard HTML and:
|
|
// - Pterodactyl Panel API (panel.firefrostgaming.com)
|
|
// - CurseForge API (api.curseforge.com)
|
|
|
|
$config = require 'config.php';
|
|
$action = $_GET['action'] ?? '';
|
|
|
|
// Allow requests from any origin (dashboard can be hosted anywhere)
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET');
|
|
header('Access-Control-Allow-Headers: Content-Type');
|
|
|
|
function makeRequest($url, $headers) {
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
|
$result = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode >= 400) {
|
|
http_response_code($httpCode);
|
|
echo json_encode(['error' => "HTTP $httpCode"]);
|
|
exit;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
$panelHeaders = [
|
|
"Authorization: Bearer {$config['panel_key']}",
|
|
"Accept: application/json"
|
|
];
|
|
|
|
if ($action === 'servers') {
|
|
// List all servers
|
|
header('Content-Type: application/json');
|
|
echo makeRequest("{$config['panel_url']}/api/client/servers", $panelHeaders);
|
|
|
|
} elseif ($action === 'files') {
|
|
// List files in server root
|
|
header('Content-Type: application/json');
|
|
$id = urlencode($_GET['server'] ?? '');
|
|
echo makeRequest("{$config['panel_url']}/api/client/servers/{$id}/files/list", $panelHeaders);
|
|
|
|
} elseif ($action === 'read') {
|
|
// Read a specific file from server filesystem
|
|
// Note: Pterodactyl returns raw text here, not JSON
|
|
header('Content-Type: text/plain');
|
|
$id = urlencode($_GET['server'] ?? '');
|
|
$file = urlencode($_GET['file'] ?? '');
|
|
echo makeRequest("{$config['panel_url']}/api/client/servers/{$id}/files/contents?file={$file}", $panelHeaders);
|
|
|
|
} elseif ($action === 'curseforge') {
|
|
// Get latest CurseForge mod files (API key stays server-side)
|
|
header('Content-Type: application/json');
|
|
$projectId = urlencode($_GET['project'] ?? '');
|
|
$cfHeaders = [
|
|
"x-api-key: {$config['cf_key']}",
|
|
"Accept: application/json"
|
|
];
|
|
echo makeRequest("https://api.curseforge.com/v1/mods/{$projectId}/files?pageSize=1&sortField=5&sortOrder=desc", $cfHeaders);
|
|
|
|
} else {
|
|
header('Content-Type: application/json');
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Invalid action']);
|
|
}
|