Files
firefrost-website/servers.njk
Claude (Chronicler #56) ac99b90fdd feat: dynamic server status with Cloudflare Worker integration
WHAT WAS DONE:
Replaced static Minecraft servers section with dynamic real-time status
display powered by Cloudflare Workers + Pterodactyl Client API.

FEATURES IMPLEMENTED:
- Real-time server status (Online/Offline) with pulse animation
- Live player counts
- Auto-refresh every 60 seconds
- Graceful error handling
- NO IP addresses displayed (Discord-gated security)
- 'Join Discord' CTA for connection details

TECHNICAL DETAILS:
- Fetches from: https://servers-api.michael-b25.workers.dev
- Edge-cached (60 second TTL)
- Zero manual updates required
- IPs stripped at Worker level (never touch frontend)

SECURITY:
- Server IPs available only in Discord (Awakened+ tier)
- Drives community engagement and subscription conversion
- Prevents port scanning and unauthorized access

When servers added/removed in Pterodactyl, website auto-updates
within 60 seconds. Zero code changes needed.

Fire + Frost + Foundation = Where Love Builds Legacy 💙

Signed-off-by: Claude (Chronicler #56) <claude@firefrostgaming.com>
2026-04-03 05:10:54 +00:00

138 lines
8.3 KiB
Plaintext

---
layout: layouts/base.njk
title: Our Servers
description: Nine Minecraft experiences. One community built to last. All servers available to all subscribers.
---
<!-- SERVERS PAGE - HERO -->
<div style="padding: 100px 40px; background: linear-gradient(135deg, #0f0f1e 0%, #1a1a2e 100%); text-align: center;">
<h1 style="font-size: 4rem; font-weight: 900; margin-bottom: 30px; color: #ffffff; text-shadow: 2px 2px 8px rgba(0,0,0,0.7);">Our Servers</h1>
<p style="font-size: 1.5rem; color: #a8dadc; max-width: 900px; margin: 0 auto 40px; line-height: 1.8;">Nine Minecraft experiences. One community built to last.</p>
<p style="font-size: 1.8rem; color: #FFD700; font-weight: 700;">All servers available to all subscribers</p>
</div>
<!-- MINECRAFT SERVERS - DYNAMIC -->
<div style="padding: 100px 60px; background: linear-gradient(135deg, #1a1a2e 0%, #0f0f1e 100%);">
<div style="max-width: 1400px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 80px;">
<h2 style="font-size: 3rem; margin-bottom: 20px; color: #e8f4f8; font-weight: 800;">Live Server Status</h2>
<p style="font-size: 1.3rem; color: #a8dadc; line-height: 1.7;">Real-time status for all Firefrost Gaming servers.</p>
</div>
<div id="server-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); gap: 40px;">
<p style="color: #a8dadc; text-align: center; font-size: 1.2rem;">Connecting to the fleet...</p>
</div>
<!-- Discord Access Notice -->
<div style="margin-top: 60px; background: linear-gradient(135deg, rgba(168, 85, 247, 0.15) 0%, rgba(168, 85, 247, 0.05) 100%); border: 2px solid #A855F7; border-radius: 15px; padding: 40px; text-align: center;">
<div style="font-size: 2.5rem; margin-bottom: 15px;">🔒</div>
<h3 style="color: #A855F7; font-size: 1.8rem; margin-bottom: 15px; font-weight: 700;">Server IPs Available in Discord</h3>
<p style="color: #e8f4f8; font-size: 1.2rem; line-height: 1.7; margin-bottom: 25px;">Connection details are available to Awakened+ subscribers in our Discord community.</p>
<a href="/discord" style="display: inline-block; background: linear-gradient(135deg, #A855F7 0%, #4ECDC4 100%); color: white; padding: 18px 45px; text-decoration: none; border-radius: 10px; font-weight: 700; font-size: 1.1rem; transition: transform 0.3s;">Join Discord →</a>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
const grid = document.getElementById('server-grid');
const WORKER_URL = 'https://servers-api.michael-b25.workers.dev';
function fetchServers() {
fetch(WORKER_URL)
.then(res => res.json())
.then(data => {
if (data.error || !data.servers || data.servers.length === 0) {
grid.innerHTML = `<p style="color: #ff6b35; text-align: center; font-size: 1.2rem;">Unable to connect to server status. Please try again later.</p>`;
return;
}
grid.innerHTML = data.servers.map(server => {
const isOnline = server.status === 'Online';
const statusColor = isOnline ? '#4ecdc4' : '#ff6b35';
const playerText = isOnline ? `${server.players} Players` : 'Offline';
return `
<div style="background: linear-gradient(135deg, rgba(78, 205, 196, 0.1) 0%, rgba(78, 205, 196, 0.05) 100%); border: 2px solid ${statusColor}; border-radius: 15px; padding: 35px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
<h3 style="color: #e8f4f8; font-size: 1.8rem; font-weight: 700; margin: 0;">${server.name}</h3>
<span class="status-badge ${server.status.toLowerCase()}" style="background: ${isOnline ? 'rgba(78, 205, 196, 0.2)' : 'rgba(255, 107, 53, 0.2)'}; color: ${statusColor}; padding: 8px 16px; border-radius: 20px; font-size: 0.9rem; font-weight: 600; display: flex; align-items: center; gap: 8px;">
${isOnline ? '<span class="pulse-dot"></span>' : ''} ${server.status}
</span>
</div>
<p style="color: #a8dadc; font-size: 1rem; margin-bottom: 15px;">${server.description || 'Minecraft Server'}</p>
<p style="color: ${statusColor}; font-size: 1.1rem; font-weight: 600;">${playerText}</p>
</div>
`;
}).join('');
})
.catch(err => {
grid.innerHTML = `<p style="color: #ff6b35; text-align: center; font-size: 1.2rem;">Connection error. Please check back shortly.</p>`;
});
}
// Initial fetch
fetchServers();
// Auto-refresh every 60 seconds
setInterval(fetchServers, 60000);
});
</script>
<style>
.pulse-dot {
display: inline-block;
width: 8px;
height: 8px;
background-color: #4ecdc4;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(78, 205, 196, 0.7); }
70% { box-shadow: 0 0 0 6px rgba(78, 205, 196, 0); }
100% { box-shadow: 0 0 0 0 rgba(78, 205, 196, 0); }
}
</style>
<!-- SPECIAL SERVICES -->
<div style="padding: 100px 60px; background: linear-gradient(135deg, #0f0f1e 0%, #1a1a2e 100%);">
<div style="max-width: 1400px; margin: 0 auto;">
<div style="text-align: center; margin-bottom: 80px;">
<div style="font-size: 4rem; margin-bottom: 20px;">⚡</div>
<h2 style="font-size: 3rem; margin-bottom: 20px; color: #A855F7; font-weight: 800;">Premium Add-Ons</h2>
<p style="font-size: 1.3rem; color: #a8dadc; line-height: 1.7;">Optional services available for subscribers.</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 50px;">
<!-- FoundryVTT -->
<div style="background: linear-gradient(135deg, rgba(168, 85, 247, 0.15) 0%, rgba(168, 85, 247, 0.05) 100%); border: 3px solid #A855F7; border-radius: 20px; padding: 45px; text-align: center;">
<h3 style="color: #A855F7; font-size: 2rem; margin-bottom: 15px; font-weight: 700;">FoundryVTT</h3>
<p style="color: #A855F7; font-size: 1.2rem; margin-bottom: 25px; font-weight: 600;">GM Time Add-On</p>
<p style="color: #4ecdc4; font-size: 1.1rem; margin-bottom: 20px; font-weight: 600;">foundry.firefrostgaming.com</p>
<p style="color: #e8f4f8; line-height: 1.8; margin-bottom: 20px;">Virtual tabletop for running epic D&D campaigns. Available as a separate purchase for any subscriber. Run your own world, your own rules.</p>
<p style="color: #a8dadc; font-size: 0.95rem; font-style: italic;">Separate add-on purchase • 14-day grace period on subscription lapse</p>
</div>
<!-- Hytale -->
<div style="background: linear-gradient(135deg, rgba(168, 85, 247, 0.15) 0%, rgba(168, 85, 247, 0.05) 100%); border: 3px solid #A855F7; border-radius: 20px; padding: 45px; text-align: center;">
<h3 style="color: #A855F7; font-size: 2rem; margin-bottom: 15px; font-weight: 700;">Hytale</h3>
<p style="color: #A855F7; font-size: 1.2rem; margin-bottom: 25px; font-weight: 600;">Coming Soon</p>
<p style="color: #4ecdc4; font-size: 1.1rem; margin-bottom: 20px; font-weight: 600;">hytale.firefrostgaming.com</p>
<p style="color: #e8f4f8; line-height: 1.8; margin-bottom: 20px;">The next generation of voxel gaming. We're ready for launch day. When Hytale releases, Firefrost will be there.</p>
<p style="color: #a8dadc; font-size: 0.95rem; font-style: italic;">Launching when the game releases • Subscription-based access</p>
</div>
</div>
</div>
</div>
<!-- JOIN CTA -->
<div style="padding: 100px 60px; background: linear-gradient(135deg, rgba(78, 205, 196, 0.1) 0%, rgba(168, 85, 247, 0.1) 50%, rgba(255, 107, 53, 0.1) 100%); text-align: center;">
<h2 style="font-size: 3rem; margin-bottom: 30px; color: #e8f4f8; font-weight: 800;">Ready to Play?</h2>
<p style="font-size: 1.4rem; color: #a8dadc; margin-bottom: 50px; max-width: 800px; margin-left: auto; margin-right: auto; line-height: 1.7;">Join a community built to last. All servers available to all subscribers.</p>
<a href="/subscribe" style="display: inline-block; background: linear-gradient(135deg, #FF6B35 0%, #A855F7 50%, #4ECDC4 100%); color: white; padding: 25px 70px; text-decoration: none; border-radius: 12px; font-weight: 700; font-size: 1.3rem; box-shadow: 0 8px 25px rgba(78, 205, 196, 0.5); transition: transform 0.3s;">🔥⚡❄️ Start Your Journey</a>
</div>