Complete implementation of staggered restart scheduler for Trinity Console. Database: - global_restart_config: Node-wide settings (TX1 @ 04:00 UTC, NC1 @ 04:30 UTC) - server_restart_schedules: Per-server state with sort order - sync_logs: Audit trail for all sync operations Backend: - src/utils/scheduler.js: Stagger calculation with date-fns - src/lib/ptero-sync.js: Pterodactyl API integration (create/update/delete/audit) - src/routes/admin/scheduler.js: All CRUD + import + sync + audit routes Frontend: - Drag-and-drop server ordering (SortableJS) - Per-node config cards with base time + interval - Audit modal to detect and nuke rogue schedules - Skip toggle for maintenance mode - Visual sync status indicators Features: - Import servers from Pterodactyl discovery - Recalculate effective times on reorder - Rate-limited API calls (200ms delay) - [Trinity] Daily Restart naming convention Signed-off-by: Claude (Chronicler #61) <claude@firefrostgaming.com>
56 lines
3.2 KiB
Plaintext
56 lines
3.2 KiB
Plaintext
<div id="audit-modal" class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-70 backdrop-blur-sm">
|
|
<div class="bg-white dark:bg-darkcard border <%= totalRogue > 0 ? 'border-red-500' : 'border-green-500' %> rounded-lg shadow-2xl w-full max-w-2xl p-6 relative">
|
|
|
|
<% if (totalRogue > 0) { %>
|
|
<h2 class="text-2xl font-bold text-red-500 mb-2">⚠ Conflicts Detected</h2>
|
|
<p class="text-gray-600 dark:text-gray-300 mb-4">
|
|
Found <strong class="text-gray-900 dark:text-white"><%= totalRogue %></strong> rogue restart schedule(s) across
|
|
<strong class="text-gray-900 dark:text-white"><%= serverCount %></strong> server(s) on <%= node %>.
|
|
These must be removed before Trinity can take control.
|
|
</p>
|
|
|
|
<div class="bg-gray-100 dark:bg-darkbg rounded p-4 mb-6 max-h-64 overflow-y-auto border border-gray-200 dark:border-gray-700">
|
|
<ul class="space-y-3">
|
|
<% results.forEach(result => { %>
|
|
<li class="border-b border-gray-200 dark:border-gray-700 pb-2 last:border-0">
|
|
<span class="text-fire font-semibold"><%= result.serverName %></span>
|
|
<ul class="ml-4 mt-1 text-sm text-gray-500 dark:text-gray-400">
|
|
<% result.rogueSchedules.forEach(sched => { %>
|
|
<li>- "<%= sched.name %>" (Cron: <%= sched.cron %>)</li>
|
|
<% }) %>
|
|
</ul>
|
|
</li>
|
|
<% }) %>
|
|
</ul>
|
|
</div>
|
|
|
|
<%
|
|
const nukePayload = [];
|
|
results.forEach(r => r.rogueSchedules.forEach(s => nukePayload.push({
|
|
serverId: r.serverId, scheduleId: s.id, scheduleName: s.name
|
|
})));
|
|
%>
|
|
|
|
<form hx-post="/admin/scheduler/audit/nuke/<%= node %>" hx-target="#audit-modal" hx-swap="outerHTML">
|
|
<input type="hidden" name="nukeData" value='<%- JSON.stringify(nukePayload) %>'>
|
|
|
|
<div class="flex justify-end gap-4 mt-6">
|
|
<button type="button" onclick="document.getElementById('audit-modal').remove()"
|
|
class="px-4 py-2 text-gray-500 hover:text-gray-900 dark:hover:text-white transition">Cancel</button>
|
|
<button type="submit"
|
|
class="bg-red-600 hover:bg-red-500 text-white px-6 py-2 rounded font-bold transition">
|
|
🔥 Nuke <%= totalRogue %> Schedules
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<% } else { %>
|
|
<h2 class="text-2xl font-bold text-green-500 mb-2">✓ All Clear</h2>
|
|
<p class="text-gray-600 dark:text-gray-300 mb-6">No conflicts found on <%= node %>. Trinity is ready to take control.</p>
|
|
<div class="flex justify-end">
|
|
<button type="button" onclick="document.getElementById('audit-modal').remove()"
|
|
class="bg-green-600 hover:bg-green-500 text-white px-6 py-2 rounded transition">Close</button>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
</div> |