From 567164ef7dbcb03fe666d52184f3c8051c6abd96 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Apr 2026 05:44:00 +0000 Subject: [PATCH] Add servers-api Cloudflare Worker to version control - Retrieved from Cloudflare dashboard via MCP connector - Was 'dashboard only, not in any git repo' - gap now closed - Original creation: April 3, 2026 by Chronicler #56 (The Velocity) - Proxies Pterodactyl API for live server status on website Chronicler #68 --- cloudflare-workers/servers-api/README.md | 78 +++++++++++++++++ cloudflare-workers/servers-api/index.js | 103 +++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 cloudflare-workers/servers-api/README.md create mode 100644 cloudflare-workers/servers-api/index.js diff --git a/cloudflare-workers/servers-api/README.md b/cloudflare-workers/servers-api/README.md new file mode 100644 index 0000000..531a6dd --- /dev/null +++ b/cloudflare-workers/servers-api/README.md @@ -0,0 +1,78 @@ +# Servers API - Cloudflare Worker + +**Purpose:** Proxies Pterodactyl Panel API to provide live server status for firefrostgaming.com + +**Deployed URL:** https://servers-api.firefrostgaming.workers.dev +**Created:** April 3, 2026 by Chronicler #56 (The Velocity) +**Retrieved from Cloudflare:** April 8, 2026 by Chronicler #68 + +--- + +## What It Does + +1. Receives request from website (firefrostgaming.com or pages.dev preview) +2. Fetches server list from Pterodactyl Panel API +3. Fetches live resource stats for each server +4. Returns JSON with server name, status (Online/Offline), player count, description +5. Caches response for 60 seconds + +--- + +## Environment Variables + +Configure these in Cloudflare Workers dashboard: + +| Variable | Description | +|----------|-------------| +| `PANEL_URL` | Pterodactyl panel URL (https://panel.firefrostgaming.com) | +| `CLIENT_API_KEY` | Pterodactyl client API key for webuser_api account | + +--- + +## CORS Configuration + +Allowed origins: +- `https://firefrostgaming.com` +- `https://firefrost-website.pages.dev` + +--- + +## Response Format + +```json +{ + "servers": [ + { + "id": "abc123", + "name": "All The Mods 10", + "status": "Online", + "players": 3, + "description": "ATM10 modpack server" + } + ] +} +``` + +--- + +## Deployment + +This Worker is deployed via Cloudflare dashboard. To update: + +1. Edit code in Cloudflare Workers dashboard, OR +2. Use Wrangler CLI: `wrangler deploy` + +**Note:** This file is the source of truth. If editing in dashboard, sync changes back here. + +--- + +## History + +| Date | Change | By | +|------|--------|-----| +| 2026-04-03 | Initial creation | Chronicler #56 (The Velocity) | +| 2026-04-08 | Added to git (was dashboard-only) | Chronicler #68 | + +--- + +**Fire + Frost + Foundation = Where Love Builds Legacy** 💙🔥❄️ diff --git a/cloudflare-workers/servers-api/index.js b/cloudflare-workers/servers-api/index.js new file mode 100644 index 0000000..aa7a586 --- /dev/null +++ b/cloudflare-workers/servers-api/index.js @@ -0,0 +1,103 @@ +/** + * Firefrost Gaming - Servers API Worker + * + * Cloudflare Worker that proxies Pterodactyl Panel API + * to provide live server status for the website. + * + * Deployed: https://servers-api.firefrostgaming.workers.dev + * Created: April 3, 2026 (Chronicler #56 - The Velocity) + * + * Environment Variables Required: + * PANEL_URL - Pterodactyl panel URL (https://panel.firefrostgaming.com) + * CLIENT_API_KEY - Pterodactyl client API key + */ + +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 + } + }); + } + } +}