Files
firefrost-services/services/arbiter-3.0/src/views/admin/scheduler/audit-modal.ejs
Claude (Chronicler #61) 5e8201fd22 feat: Task #94 Global Restart Scheduler
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>
2026-04-05 09:58:52 +00:00

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>