Adds installed version display, edit form, save/cancel buttons, and version history viewer to each server card. Uses var assignment pattern to avoid single quotes inside EJS attribute tags. EJS validates clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
475 lines
28 KiB
Plaintext
475 lines
28 KiB
Plaintext
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
<div>
|
|
<h2 class="text-lg font-bold mb-4 flex items-center gap-2 text-gray-800 dark:text-gray-200">
|
|
<span>🔥</span> Dallas Node (TX1)
|
|
</h2>
|
|
<div class="space-y-4">
|
|
<% txServers.forEach(function(server) { %>
|
|
<%
|
|
const isOnline = server.log.is_online;
|
|
const hasError = !!server.log.last_error;
|
|
const config = server.config;
|
|
const shortName = config ? config.short_name : null;
|
|
const locked = config ? config.short_name_locked : false;
|
|
const discordComplete = server.discord?.complete;
|
|
const unconfigured = server.discord?.unconfigured;
|
|
const nodeBadge = server.node === 'TX1' ? '🔥 TX1' : '❄️ NC1';
|
|
|
|
let borderClass = 'border-gray-200 dark:border-gray-700';
|
|
if (isOnline && !hasError) borderClass = 'border-green-500 shadow-[0_0_10px_rgba(34,197,94,0.2)]';
|
|
if (hasError) borderClass = 'border-red-500 shadow-[0_0_10px_rgba(239,68,68,0.2)]';
|
|
%>
|
|
|
|
<div class="bg-white dark:bg-darkcard rounded-lg border-l-4 <%= borderClass %> p-4 relative overflow-hidden">
|
|
|
|
<!-- Header -->
|
|
<div class="flex justify-between items-start mb-2">
|
|
<div>
|
|
<h3 class="font-bold text-gray-900 dark:text-white"><%= server.name %></h3>
|
|
<span class="text-xs font-medium px-1.5 py-0.5 rounded bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400"><%= nodeBadge %></span>
|
|
</div>
|
|
<span class="inline-flex items-center gap-1.5 text-xs font-medium px-2 py-1 rounded-full <%= isOnline ? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400' : 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400' %>">
|
|
<span class="w-1.5 h-1.5 rounded-full <%= isOnline ? 'bg-green-500 animate-pulse' : 'bg-gray-400' %>"></span>
|
|
<%= isOnline ? 'Online' : 'Offline' %>
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Short Name Section -->
|
|
<div class="mb-3" id="shortname-<%= server.identifier %>">
|
|
<% if (locked) { %>
|
|
<span class="inline-flex items-center gap-1 text-xs font-mono bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 px-2 py-1 rounded">
|
|
🔒 <%= shortName %>
|
|
</span>
|
|
<% } else { %>
|
|
<div class="flex items-center gap-2">
|
|
<input type="text" name="short_name" value="<%= shortName || '' %>"
|
|
placeholder="e.g. atm10-tts"
|
|
class="text-xs font-mono border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded px-2 py-1 w-36"
|
|
id="sn-input-<%= server.identifier %>">
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/set-short-name"
|
|
hx-include="#sn-input-<%= server.identifier %>"
|
|
hx-target="#shortname-<%= server.identifier %>"
|
|
class="text-xs bg-gray-200 dark:bg-gray-700 dark:text-white px-2 py-1 rounded hover:bg-gray-300 dark:hover:bg-gray-600">
|
|
Save
|
|
</button>
|
|
<% if (shortName) { %>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/lock-short-name"
|
|
hx-confirm="⚠️ Once locked, this cannot be changed. Continue?"
|
|
hx-target="#shortname-<%= server.identifier %>"
|
|
class="text-xs bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400 px-2 py-1 rounded hover:bg-red-200 dark:hover:bg-red-900/50">
|
|
Lock In
|
|
</button>
|
|
<% } %>
|
|
</div>
|
|
<p class="text-[10px] text-gray-400 mt-1">⚠️ Once locked, this cannot be changed</p>
|
|
<% } %>
|
|
</div>
|
|
|
|
<!-- Stats Row -->
|
|
<div class="grid grid-cols-2 gap-4 text-sm mb-3">
|
|
<div>
|
|
<span class="text-gray-500 dark:text-gray-400 block text-xs">Whitelist</span>
|
|
<span class="font-medium dark:text-gray-200">
|
|
<%= server.whitelistEnabled ? '✅ Enabled' : '🔓 Disabled' %>
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<span class="text-gray-500 dark:text-gray-400 block text-xs">Last Sync</span>
|
|
<span class="font-medium dark:text-gray-200 text-xs">
|
|
<%= server.log.last_successful_sync ? new Date(server.log.last_successful_sync).toLocaleString() : 'Never' %>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Subdomain -->
|
|
<div class="mb-3" id="subdomain-<%= server.identifier %>">
|
|
<span class="text-gray-500 dark:text-gray-400 block text-xs mb-1">Subdomain</span>
|
|
<% if (config && config.subdomain) { %>
|
|
<div class="text-xs">
|
|
<span class="text-green-600 dark:text-green-400 font-mono">🌐 <%= config.subdomain %>.firefrostgaming.com</span>
|
|
<span class="text-gray-400 ml-2"><%= config.server_ip || '' %>:<%= config.server_port || 25565 %></span>
|
|
</div>
|
|
<% } else if (config && config.server_ip) { %>
|
|
<div class="text-xs">
|
|
<span class="text-gray-400">🌐 No subdomain</span>
|
|
<span class="text-gray-400 ml-2"><%= config.server_ip %>:<%= config.server_port || 25565 %></span>
|
|
</div>
|
|
<div class="flex items-center gap-2 mt-1">
|
|
<input type="text" name="subdomain" placeholder="e.g. stoneblock4"
|
|
class="text-xs font-mono border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded px-2 py-1 w-32"
|
|
id="sd-input-<%= server.identifier %>">
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/provision-subdomain"
|
|
hx-include="#sd-input-<%= server.identifier %>"
|
|
hx-target="#subdomain-<%= server.identifier %>"
|
|
class="text-xs bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 px-2 py-1 rounded hover:bg-purple-200 dark:hover:bg-purple-900/50">
|
|
+ Provision
|
|
</button>
|
|
</div>
|
|
<% } else { %>
|
|
<span class="text-gray-400 dark:text-gray-500 text-xs italic">No server config</span>
|
|
<% } %>
|
|
</div>
|
|
|
|
<!-- Discord Channel Status -->
|
|
<div class="mb-3">
|
|
<span class="text-gray-500 dark:text-gray-400 block text-xs mb-1">Discord Channels</span>
|
|
<% if (unconfigured) { %>
|
|
<span class="text-gray-400 dark:text-gray-500 text-xs italic">Set short name to enable channel detection</span>
|
|
<% } else if (discordComplete) { %>
|
|
<span class="text-green-600 dark:text-green-400 text-sm font-medium">✅ All 5 channels configured</span>
|
|
<% } else if (server.discord?.found?.length > 0 || server.discord?.missing?.length > 0) { %>
|
|
<div class="text-xs space-y-0.5">
|
|
<% if (server.discord.found.length > 0) { %>
|
|
<div class="text-green-600 dark:text-green-400">✅ <%= server.discord.found.join(', ') %></div>
|
|
<% } %>
|
|
<% if (server.discord.missing.length > 0) { %>
|
|
<div class="text-yellow-600 dark:text-yellow-400">❌ Missing: <%= server.discord.missing.join(', ') %></div>
|
|
<% } %>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
|
|
<% if (hasError) { %>
|
|
<div class="bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 p-2 rounded text-xs mb-3 break-words">
|
|
<strong>Error:</strong> <%= server.log.last_error %>
|
|
</div>
|
|
<% } %>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="flex flex-wrap items-center gap-2 border-t border-gray-100 dark:border-gray-700 pt-3 mt-2" id="controls-<%= server.identifier %>">
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/sync"
|
|
hx-target="#controls-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
<%= !isOnline ? 'disabled' : '' %>
|
|
class="text-xs font-medium bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-white px-2.5 py-1.5 rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed">
|
|
⚡ Sync
|
|
</button>
|
|
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/toggle-whitelist"
|
|
hx-confirm="Changing the whitelist requires a server restart. Continue?"
|
|
hx-target="#controls-<%= server.identifier %>"
|
|
hx-swap="beforeend"
|
|
class="text-xs font-medium text-blue-600 dark:text-blue-400 hover:underline">
|
|
Toggle WL
|
|
</button>
|
|
|
|
<% if (locked && !discordComplete) { %>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/createserver"
|
|
hx-target="#controls-<%= server.identifier %>"
|
|
hx-swap="beforeend"
|
|
class="text-xs font-medium bg-green-100 hover:bg-green-200 dark:bg-green-900/30 dark:hover:bg-green-900/50 text-green-700 dark:text-green-400 px-2.5 py-1.5 rounded">
|
|
🚀 Create Server
|
|
</button>
|
|
<% } %>
|
|
|
|
<% if (locked && server.discord?.found?.length > 0) { %>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/delserver"
|
|
hx-confirm="⚠️ This will delete ALL Discord channels for this server. Are you sure?"
|
|
hx-target="#controls-<%= server.identifier %>"
|
|
hx-swap="beforeend"
|
|
class="text-xs font-medium bg-red-100 hover:bg-red-200 dark:bg-red-900/30 dark:hover:bg-red-900/50 text-red-700 dark:text-red-400 px-2.5 py-1.5 rounded">
|
|
🗑️ Delete
|
|
</button>
|
|
<% } %>
|
|
|
|
<!-- Power Controls -->
|
|
<div class="flex items-center gap-1 ml-auto" id="power-<%= server.identifier %>">
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/power"
|
|
hx-vals='{"signal":"start"}'
|
|
hx-target="#power-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
class="text-xs px-2 py-1 rounded bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 hover:bg-green-200 dark:hover:bg-green-900/50"
|
|
title="Start">▶️</button>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/power"
|
|
hx-vals='{"signal":"stop"}'
|
|
hx-target="#power-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
class="text-xs px-2 py-1 rounded bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
|
|
title="Stop">⏹</button>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/power"
|
|
hx-vals='{"signal":"restart"}'
|
|
hx-target="#power-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
class="text-xs px-2 py-1 rounded bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400 hover:bg-yellow-200 dark:hover:bg-yellow-900/50"
|
|
title="Restart">🔄</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Installed Version -->
|
|
<% var currentVersion = config && config.current_version ? config.current_version : null; %>
|
|
<div class="px-4 pb-4" style="border-top:1px solid #333;padding-top:10px;margin-top:4px;">
|
|
<div style="font-size:10px;color:#666;text-transform:uppercase;letter-spacing:0.1em;margin-bottom:6px;">📦 Installed Version</div>
|
|
<div style="display:flex;gap:6px;align-items:center;" id="version-display-<%= server.identifier %>">
|
|
<span style="font-size:13px;font-weight:600;color:<%= currentVersion ? '#4ade80' : '#555' %>;" id="version-text-<%= server.identifier %>">
|
|
<%= currentVersion || 'Not set' %>
|
|
</span>
|
|
<button id="version-edit-btn-<%= server.identifier %>"
|
|
onclick="toggleVersionForm('<%= server.identifier %>')"
|
|
style="font-size:10px;background:#333;border:1px solid #555;color:#aaa;padding:2px 8px;border-radius:4px;cursor:pointer;">
|
|
✏️ Edit
|
|
</button>
|
|
</div>
|
|
<div id="version-form-<%= server.identifier %>" style="display:none;margin-top:6px;">
|
|
<div style="display:flex;gap:6px;align-items:center;">
|
|
<input type="text" id="version-input-<%= server.identifier %>"
|
|
placeholder="e.g. 1.4.2"
|
|
style="font-size:12px;background:#1a1a1a;border:1px solid #555;color:#e0e0e0;padding:4px 8px;border-radius:4px;width:140px;" />
|
|
<button onclick="saveVersion('<%= server.identifier %>')"
|
|
style="font-size:11px;background:#2563eb;color:#fff;border:none;padding:4px 10px;border-radius:4px;cursor:pointer;">Save</button>
|
|
<button onclick="hideVersionForm('<%= server.identifier %>')"
|
|
style="font-size:11px;background:#333;border:1px solid #555;color:#aaa;padding:4px 8px;border-radius:4px;cursor:pointer;">Cancel</button>
|
|
</div>
|
|
<div id="version-result-<%= server.identifier %>" style="margin-top:4px;font-size:11px;"></div>
|
|
<div style="margin-top:6px;">
|
|
<button hx-get="/admin/servers/<%= server.identifier %>/version-history"
|
|
hx-target="#version-history-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
style="font-size:10px;background:transparent;border:none;color:#555;cursor:pointer;padding:0;text-decoration:underline;">View history</button>
|
|
</div>
|
|
<div id="version-history-<%= server.identifier %>" style="margin-top:4px;background:#1a1a1a;border-radius:4px;padding:4px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<% }) %>
|
|
<% if (txServers.length === 0) { %><p class="text-gray-500 text-sm">No servers found.</p><% } %>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h2 class="text-lg font-bold mb-4 flex items-center gap-2 text-gray-800 dark:text-gray-200">
|
|
<span>❄️</span> Charlotte Node (NC1)
|
|
</h2>
|
|
<div class="space-y-4">
|
|
<% ncServers.forEach(function(server) { %>
|
|
<%
|
|
const isOnline = server.log.is_online;
|
|
const hasError = !!server.log.last_error;
|
|
const config = server.config;
|
|
const shortName = config ? config.short_name : null;
|
|
const locked = config ? config.short_name_locked : false;
|
|
const discordComplete = server.discord?.complete;
|
|
const unconfigured = server.discord?.unconfigured;
|
|
const nodeBadge = server.node === 'TX1' ? '🔥 TX1' : '❄️ NC1';
|
|
|
|
let borderClass = 'border-gray-200 dark:border-gray-700';
|
|
if (isOnline && !hasError) borderClass = 'border-green-500 shadow-[0_0_10px_rgba(34,197,94,0.2)]';
|
|
if (hasError) borderClass = 'border-red-500 shadow-[0_0_10px_rgba(239,68,68,0.2)]';
|
|
%>
|
|
|
|
<div class="bg-white dark:bg-darkcard rounded-lg border-l-4 <%= borderClass %> p-4 relative overflow-hidden">
|
|
|
|
<!-- Header -->
|
|
<div class="flex justify-between items-start mb-2">
|
|
<div>
|
|
<h3 class="font-bold text-gray-900 dark:text-white"><%= server.name %></h3>
|
|
<span class="text-xs font-medium px-1.5 py-0.5 rounded bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400"><%= nodeBadge %></span>
|
|
</div>
|
|
<span class="inline-flex items-center gap-1.5 text-xs font-medium px-2 py-1 rounded-full <%= isOnline ? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400' : 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400' %>">
|
|
<span class="w-1.5 h-1.5 rounded-full <%= isOnline ? 'bg-green-500 animate-pulse' : 'bg-gray-400' %>"></span>
|
|
<%= isOnline ? 'Online' : 'Offline' %>
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Short Name Section -->
|
|
<div class="mb-3" id="shortname-<%= server.identifier %>">
|
|
<% if (locked) { %>
|
|
<span class="inline-flex items-center gap-1 text-xs font-mono bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 px-2 py-1 rounded">
|
|
🔒 <%= shortName %>
|
|
</span>
|
|
<% } else { %>
|
|
<div class="flex items-center gap-2">
|
|
<input type="text" name="short_name" value="<%= shortName || '' %>"
|
|
placeholder="e.g. atm10-tts"
|
|
class="text-xs font-mono border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded px-2 py-1 w-36"
|
|
id="sn-input-<%= server.identifier %>">
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/set-short-name"
|
|
hx-include="#sn-input-<%= server.identifier %>"
|
|
hx-target="#shortname-<%= server.identifier %>"
|
|
class="text-xs bg-gray-200 dark:bg-gray-700 dark:text-white px-2 py-1 rounded hover:bg-gray-300 dark:hover:bg-gray-600">
|
|
Save
|
|
</button>
|
|
<% if (shortName) { %>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/lock-short-name"
|
|
hx-confirm="⚠️ Once locked, this cannot be changed. Continue?"
|
|
hx-target="#shortname-<%= server.identifier %>"
|
|
class="text-xs bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400 px-2 py-1 rounded hover:bg-red-200 dark:hover:bg-red-900/50">
|
|
Lock In
|
|
</button>
|
|
<% } %>
|
|
</div>
|
|
<p class="text-[10px] text-gray-400 mt-1">⚠️ Once locked, this cannot be changed</p>
|
|
<% } %>
|
|
</div>
|
|
|
|
<!-- Stats Row -->
|
|
<div class="grid grid-cols-2 gap-4 text-sm mb-3">
|
|
<div>
|
|
<span class="text-gray-500 dark:text-gray-400 block text-xs">Whitelist</span>
|
|
<span class="font-medium dark:text-gray-200">
|
|
<%= server.whitelistEnabled ? '✅ Enabled' : '🔓 Disabled' %>
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<span class="text-gray-500 dark:text-gray-400 block text-xs">Last Sync</span>
|
|
<span class="font-medium dark:text-gray-200 text-xs">
|
|
<%= server.log.last_successful_sync ? new Date(server.log.last_successful_sync).toLocaleString() : 'Never' %>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Subdomain -->
|
|
<div class="mb-3" id="subdomain-<%= server.identifier %>">
|
|
<span class="text-gray-500 dark:text-gray-400 block text-xs mb-1">Subdomain</span>
|
|
<% if (config && config.subdomain) { %>
|
|
<div class="text-xs">
|
|
<span class="text-green-600 dark:text-green-400 font-mono">🌐 <%= config.subdomain %>.firefrostgaming.com</span>
|
|
<span class="text-gray-400 ml-2"><%= config.server_ip || '' %>:<%= config.server_port || 25565 %></span>
|
|
</div>
|
|
<% } else if (config && config.server_ip) { %>
|
|
<div class="text-xs">
|
|
<span class="text-gray-400">🌐 No subdomain</span>
|
|
<span class="text-gray-400 ml-2"><%= config.server_ip %>:<%= config.server_port || 25565 %></span>
|
|
</div>
|
|
<div class="flex items-center gap-2 mt-1">
|
|
<input type="text" name="subdomain" placeholder="e.g. stoneblock4"
|
|
class="text-xs font-mono border border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded px-2 py-1 w-32"
|
|
id="sd-input-<%= server.identifier %>">
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/provision-subdomain"
|
|
hx-include="#sd-input-<%= server.identifier %>"
|
|
hx-target="#subdomain-<%= server.identifier %>"
|
|
class="text-xs bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 px-2 py-1 rounded hover:bg-purple-200 dark:hover:bg-purple-900/50">
|
|
+ Provision
|
|
</button>
|
|
</div>
|
|
<% } else { %>
|
|
<span class="text-gray-400 dark:text-gray-500 text-xs italic">No server config</span>
|
|
<% } %>
|
|
</div>
|
|
|
|
<!-- Discord Channel Status -->
|
|
<div class="mb-3">
|
|
<span class="text-gray-500 dark:text-gray-400 block text-xs mb-1">Discord Channels</span>
|
|
<% if (unconfigured) { %>
|
|
<span class="text-gray-400 dark:text-gray-500 text-xs italic">Set short name to enable channel detection</span>
|
|
<% } else if (discordComplete) { %>
|
|
<span class="text-green-600 dark:text-green-400 text-sm font-medium">✅ All 5 channels configured</span>
|
|
<% } else if (server.discord?.found?.length > 0 || server.discord?.missing?.length > 0) { %>
|
|
<div class="text-xs space-y-0.5">
|
|
<% if (server.discord.found.length > 0) { %>
|
|
<div class="text-green-600 dark:text-green-400">✅ <%= server.discord.found.join(', ') %></div>
|
|
<% } %>
|
|
<% if (server.discord.missing.length > 0) { %>
|
|
<div class="text-yellow-600 dark:text-yellow-400">❌ Missing: <%= server.discord.missing.join(', ') %></div>
|
|
<% } %>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
|
|
<% if (hasError) { %>
|
|
<div class="bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 p-2 rounded text-xs mb-3 break-words">
|
|
<strong>Error:</strong> <%= server.log.last_error %>
|
|
</div>
|
|
<% } %>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="flex flex-wrap items-center gap-2 border-t border-gray-100 dark:border-gray-700 pt-3 mt-2" id="controls-<%= server.identifier %>">
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/sync"
|
|
hx-target="#controls-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
<%= !isOnline ? 'disabled' : '' %>
|
|
class="text-xs font-medium bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-white px-2.5 py-1.5 rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed">
|
|
⚡ Sync
|
|
</button>
|
|
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/toggle-whitelist"
|
|
hx-confirm="Changing the whitelist requires a server restart. Continue?"
|
|
hx-target="#controls-<%= server.identifier %>"
|
|
hx-swap="beforeend"
|
|
class="text-xs font-medium text-blue-600 dark:text-blue-400 hover:underline">
|
|
Toggle WL
|
|
</button>
|
|
|
|
<% if (locked && !discordComplete) { %>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/createserver"
|
|
hx-target="#controls-<%= server.identifier %>"
|
|
hx-swap="beforeend"
|
|
class="text-xs font-medium bg-green-100 hover:bg-green-200 dark:bg-green-900/30 dark:hover:bg-green-900/50 text-green-700 dark:text-green-400 px-2.5 py-1.5 rounded">
|
|
🚀 Create Server
|
|
</button>
|
|
<% } %>
|
|
|
|
<% if (locked && server.discord?.found?.length > 0) { %>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/delserver"
|
|
hx-confirm="⚠️ This will delete ALL Discord channels for this server. Are you sure?"
|
|
hx-target="#controls-<%= server.identifier %>"
|
|
hx-swap="beforeend"
|
|
class="text-xs font-medium bg-red-100 hover:bg-red-200 dark:bg-red-900/30 dark:hover:bg-red-900/50 text-red-700 dark:text-red-400 px-2.5 py-1.5 rounded">
|
|
🗑️ Delete
|
|
</button>
|
|
<% } %>
|
|
|
|
<!-- Power Controls -->
|
|
<div class="flex items-center gap-1 ml-auto" id="power-<%= server.identifier %>">
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/power"
|
|
hx-vals='{"signal":"start"}'
|
|
hx-target="#power-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
class="text-xs px-2 py-1 rounded bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 hover:bg-green-200 dark:hover:bg-green-900/50"
|
|
title="Start">▶️</button>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/power"
|
|
hx-vals='{"signal":"stop"}'
|
|
hx-target="#power-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
class="text-xs px-2 py-1 rounded bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
|
|
title="Stop">⏹</button>
|
|
<button hx-post="/admin/servers/<%= server.identifier %>/power"
|
|
hx-vals='{"signal":"restart"}'
|
|
hx-target="#power-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
class="text-xs px-2 py-1 rounded bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400 hover:bg-yellow-200 dark:hover:bg-yellow-900/50"
|
|
title="Restart">🔄</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Installed Version -->
|
|
<% var currentVersion = config && config.current_version ? config.current_version : null; %>
|
|
<div class="px-4 pb-4" style="border-top:1px solid #333;padding-top:10px;margin-top:4px;">
|
|
<div style="font-size:10px;color:#666;text-transform:uppercase;letter-spacing:0.1em;margin-bottom:6px;">📦 Installed Version</div>
|
|
<div style="display:flex;gap:6px;align-items:center;" id="version-display-<%= server.identifier %>">
|
|
<span style="font-size:13px;font-weight:600;color:<%= currentVersion ? '#4ade80' : '#555' %>;" id="version-text-<%= server.identifier %>">
|
|
<%= currentVersion || 'Not set' %>
|
|
</span>
|
|
<button id="version-edit-btn-<%= server.identifier %>"
|
|
onclick="toggleVersionForm('<%= server.identifier %>')"
|
|
style="font-size:10px;background:#333;border:1px solid #555;color:#aaa;padding:2px 8px;border-radius:4px;cursor:pointer;">
|
|
✏️ Edit
|
|
</button>
|
|
</div>
|
|
<div id="version-form-<%= server.identifier %>" style="display:none;margin-top:6px;">
|
|
<div style="display:flex;gap:6px;align-items:center;">
|
|
<input type="text" id="version-input-<%= server.identifier %>"
|
|
placeholder="e.g. 1.4.2"
|
|
style="font-size:12px;background:#1a1a1a;border:1px solid #555;color:#e0e0e0;padding:4px 8px;border-radius:4px;width:140px;" />
|
|
<button onclick="saveVersion('<%= server.identifier %>')"
|
|
style="font-size:11px;background:#2563eb;color:#fff;border:none;padding:4px 10px;border-radius:4px;cursor:pointer;">Save</button>
|
|
<button onclick="hideVersionForm('<%= server.identifier %>')"
|
|
style="font-size:11px;background:#333;border:1px solid #555;color:#aaa;padding:4px 8px;border-radius:4px;cursor:pointer;">Cancel</button>
|
|
</div>
|
|
<div id="version-result-<%= server.identifier %>" style="margin-top:4px;font-size:11px;"></div>
|
|
<div style="margin-top:6px;">
|
|
<button hx-get="/admin/servers/<%= server.identifier %>/version-history"
|
|
hx-target="#version-history-<%= server.identifier %>"
|
|
hx-swap="innerHTML"
|
|
style="font-size:10px;background:transparent;border:none;color:#555;cursor:pointer;padding:0;text-decoration:underline;">View history</button>
|
|
</div>
|
|
<div id="version-history-<%= server.identifier %>" style="margin-top:4px;background:#1a1a1a;border-radius:4px;padding:4px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<% }) %>
|
|
<% if (ncServers.length === 0) { %><p class="text-gray-500 text-sm">No servers found.</p><% } %>
|
|
</div>
|
|
</div>
|
|
</div>
|