Worker proxies Pterodactyl client API for website server list. Live status + player counts, CORS-locked, 60s cache. Was only in Cloudflare Dashboard — known gap now closed. Recovered via Cloudflare MCP connector audit. Chronicler #78 | firefrost-services
100 lines
3.0 KiB
JavaScript
100 lines
3.0 KiB
JavaScript
/**
|
|
* servers-api — Cloudflare Worker
|
|
*
|
|
* Proxies Pterodactyl client API for the firefrostgaming.com website.
|
|
* Returns live server status + player counts.
|
|
*
|
|
* Deployed: April 3, 2026
|
|
* Location: Cloudflare Workers (servers-api)
|
|
* Env vars: PANEL_URL, CLIENT_API_KEY
|
|
* CORS: firefrostgaming.com, firefrost-website.pages.dev
|
|
* Cache: 60 seconds
|
|
*
|
|
* NOT in any git repo until now — recovered by Chronicler #78 via Cloudflare MCP.
|
|
*/
|
|
|
|
export default {
|
|
async fetch(request, env) {
|
|
// Determine allowed origin
|
|
const origin = request.headers.get('Origin');
|
|
const allowedOrigins = [
|
|
'https://firefrostgaming.com',
|
|
'https://firefrost-website.pages.dev'
|
|
];
|
|
const allowedOrigin = allowedOrigins.includes(origin) ? origin : 'https://firefrostgaming.com';
|
|
|
|
// Handle CORS preflight
|
|
if (request.method === 'OPTIONS') {
|
|
return new Response(null, {
|
|
headers: {
|
|
'Access-Control-Allow-Origin': allowedOrigin,
|
|
'Access-Control-Allow-Methods': 'GET, OPTIONS',
|
|
'Access-Control-Allow-Headers': 'Content-Type'
|
|
}
|
|
});
|
|
}
|
|
|
|
const PANEL_URL = env.PANEL_URL;
|
|
const API_KEY = env.CLIENT_API_KEY;
|
|
|
|
try {
|
|
// Fetch server list
|
|
const listResponse = await fetch(`${PANEL_URL}/api/client`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${API_KEY}`,
|
|
'Accept': 'application/json'
|
|
}
|
|
});
|
|
|
|
const listData = await listResponse.json();
|
|
|
|
if (!listData.data) throw new Error("Failed to fetch server list");
|
|
|
|
// Fetch live stats for all servers
|
|
const serverPromises = listData.data.map(async (server) => {
|
|
const id = server.attributes.identifier;
|
|
|
|
const statsResponse = await fetch(`${PANEL_URL}/api/client/servers/${id}/resources`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${API_KEY}`,
|
|
'Accept': 'application/json'
|
|
}
|
|
});
|
|
|
|
const stats = await statsResponse.json();
|
|
const isRunning = stats.attributes?.current_state === 'running';
|
|
|
|
return {
|
|
id: id,
|
|
name: server.attributes.name,
|
|
status: isRunning ? 'Online' : 'Offline',
|
|
players: isRunning ? (stats.attributes?.resources?.players || 0) : 0,
|
|
description: server.attributes.description
|
|
};
|
|
});
|
|
|
|
const finalServers = await Promise.all(serverPromises);
|
|
|
|
return new Response(JSON.stringify({ servers: finalServers }), {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': allowedOrigin,
|
|
'Cache-Control': 'public, s-maxage=60, max-age=60'
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
return new Response(JSON.stringify({
|
|
error: "Servers temporarily unreachable",
|
|
servers: []
|
|
}), {
|
|
status: 500,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': allowedOrigin
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|