2026-03-06 20:50:02 -06:00
<!doctype html>
< html lang = "en" class = "h-full" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< title > Settings - Overlord</ title >
< link rel = "icon" type = "image/x-icon" href = "/assets/favicon.ico" />
< link rel = "stylesheet" href = "/assets/tailwind.css" />
2026-04-18 21:16:29 -05:00
< link rel = "stylesheet" href = "/vendor/fontawesome/css/all.min.css" />
2026-03-06 20:50:02 -06:00
< link rel = "stylesheet" href = "/assets/main.css" />
2026-04-01 13:49:41 -05:00
< link rel = "stylesheet" href = "/assets/custom.css" />
2026-04-29 00:22:37 -05:00
< style >
. settings-nav-link {
display : flex ;
align-items : center ;
gap : 0.625 rem ;
padding : 0.5 rem 0.75 rem ;
border-radius : 0.5 rem ;
color : rgb ( 203 213 225 ); /* slate-300 */
cursor : pointer ;
transition : background-color 120 ms , color 120 ms ;
}
. settings-nav-link : hover {
background-color : rgb ( 30 41 59 / 0.7 ); /* slate-800/70 */
color : rgb ( 241 245 249 ); /* slate-100 */
}
/* Active state pulls its color from --accent (set inline per link to
match the link's icon). Falls back to sky-400 if not set. */
. settings-nav-link . active {
background-color : rgb ( var ( -- accent , 56 189 248 ) / 0.18 );
color : rgb ( 241 245 249 );
box-shadow :
inset 2 px 0 0 0 rgb ( var ( -- accent , 56 189 248 )),
0 0 14 px -2 px rgb ( var ( -- accent , 56 189 248 ) / 0.45 );
}
. settings-nav-group-label {
display : block ;
padding : 0.5 rem 0.75 rem 0.25 rem ;
font-size : 0.625 rem ;
font-weight : 600 ;
text-transform : uppercase ;
letter-spacing : 0.08 em ;
color : rgb ( 100 116 139 ); /* slate-500 */
}
/* Scroll offset for #section anchors. In topbar nav mode this is set to
the topbar's actual rendered height + a small buffer at runtime; in
sidebar mode it's just a small visual margin. The settings-section
class applies scroll-margin-top, which both `scrollIntoView` and
browser hash navigation honor. */
: root { --settings-scroll-mt : 1 rem ; }
. settings-section { scroll-margin-top : var ( -- settings - scroll - mt ); }
/* Also pin the sticky sidebar below the topbar so it doesn't slide
under the nav when the page is scrolled. */
# settings-sidebar { top : var ( -- settings - scroll - mt ); }
</ style >
2026-03-06 20:50:02 -06:00
</ head >
< body class = "min-h-screen bg-slate-950 text-slate-100" >
< header id = "top-nav" ></ header >
2026-04-29 00:22:37 -05:00
< main class = "max-w-7xl w-full mx-auto px-4 py-8" >
< div class = "mb-6" >
2026-03-06 20:50:02 -06:00
< h1 class = "text-2xl font-bold text-slate-100" > Settings</ h1 >
< p class = "text-slate-400 text-sm" >
2026-04-29 00:22:37 -05:00
Manage your account preferences, operational tools, and admin configuration.
2026-03-06 20:50:02 -06:00
</ p >
</ div >
2026-04-29 00:22:37 -05:00
< div class = "flex flex-col lg:flex-row gap-6" >
<!-- Sidebar nav -->
< aside class = "lg:w-56 lg:flex-shrink-0" >
< nav id = "settings-sidebar"
class = "lg:sticky bg-slate-900/60 border border-slate-800 rounded-xl p-2 space-y-0.5 text-sm" >
< span class = "settings-nav-group-label" > Personal</ span >
< a data-target = "section-account" class = "settings-nav-link" style = "--accent: 56 189 248" >< i class = "fa-solid fa-user w-4 text-sky-400" ></ i >< span > Account</ span ></ a >
< a data-target = "section-sessions" class = "settings-nav-link" style = "--accent: 52 211 153" >< i class = "fa-solid fa-desktop w-4 text-emerald-400" ></ i >< span > Active Sessions</ span ></ a >
< a data-target = "section-prefs" class = "settings-nav-link" style = "--accent: 167 139 250" >< i class = "fa-solid fa-sliders w-4 text-violet-400" ></ i >< span > Dashboard Preferences</ span ></ a >
< a data-target = "telegram-settings" class = "settings-nav-link" style = "--accent: 52 211 153" >< i class = "fa-solid fa-bell w-4 text-emerald-400" ></ i >< span > Notification Delivery</ span ></ a >
< a data-target = "section-news" class = "settings-nav-link" style = "--accent: 56 189 248" >< i class = "fa-brands fa-telegram w-4 text-sky-400" ></ i >< span > News & Updates</ span ></ a >
< span class = "settings-nav-group-label" data-admin-or-operator > Operations</ span >
< a data-target = "section-bans" class = "settings-nav-link" style = "--accent: 248 113 113" >< i class = "fa-solid fa-ban w-4 text-red-400" ></ i >< span > Banned IPs</ span ></ a >
< a data-target = "section-builds" class = "settings-nav-link" data-admin-or-operator style = "--accent: 251 146 60" >< i class = "fa-solid fa-hammer w-4 text-orange-400" ></ i >< span > Build IDs</ span ></ a >
< a data-target = "wipe-offline-section" class = "settings-nav-link" data-admin-or-operator style = "--accent: 248 113 113" >< i class = "fa-solid fa-circle-minus w-4 text-red-400" ></ i >< span > Wipe Offline Clients</ span ></ a >
< span class = "settings-nav-group-label" data-admin-only > Admin</ span >
< a data-target = "section-security" class = "settings-nav-link" data-admin-only style = "--accent: 129 140 248" >< i class = "fa-solid fa-shield-halved w-4 text-indigo-400" ></ i >< span > Security Policy</ span ></ a >
< a data-target = "section-tls" class = "settings-nav-link" data-admin-only style = "--accent: 34 211 238" >< i class = "fa-solid fa-certificate w-4 text-cyan-400" ></ i >< span > TLS Certificates</ span ></ a >
< a data-target = "chat-settings-section" class = "settings-nav-link" data-admin-only style = "--accent: 56 189 248" >< i class = "fa-solid fa-comments w-4 text-sky-400" ></ i >< span > Team Chat</ span ></ a >
< a data-target = "registration-section" class = "settings-nav-link" data-admin-only style = "--accent: 52 211 153" >< i class = "fa-solid fa-user-plus w-4 text-emerald-400" ></ i >< span > Registration</ span ></ a >
< a data-target = "build-rate-limit-section" class = "settings-nav-link" data-admin-only style = "--accent: 251 146 60" >< i class = "fa-solid fa-gauge-high w-4 text-orange-400" ></ i >< span > Build Rate Limit</ span ></ a >
< a data-target = "section-appearance" class = "settings-nav-link" data-admin-only style = "--accent: 232 121 249" >< i class = "fa-solid fa-paintbrush w-4 text-fuchsia-400" ></ i >< span > Custom CSS</ span ></ a >
< a data-target = "export-import-section" class = "settings-nav-link" data-admin-only style = "--accent: 251 191 36" >< i class = "fa-solid fa-file-export w-4 text-amber-400" ></ i >< span > Export / Import</ span ></ a >
</ nav >
</ aside >
<!-- Sections -->
< div class = "flex-1 min-w-0 space-y-6" >
<!-- ========== PERSONAL ========== -->
<!-- Account -->
< section id = "section-account" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-03-24 20:42:54 -05:00
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-sky-500/10 border border-sky-500/30" >
2026-04-29 00:22:37 -05:00
< i class = "fa-solid fa-user text-sky-400" ></ i >
2026-03-24 20:42:54 -05:00
</ div >
< div >
2026-04-29 00:22:37 -05:00
< h2 class = "text-lg font-semibold" > Account</ h2 >
< p class = "text-sm text-slate-400" > View your account info and update your password.</ p >
2026-03-25 10:18:22 -05:00
</ div >
</ div >
2026-04-29 00:22:37 -05:00
< div class = "grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm" >
< div class = "rounded-lg bg-slate-900 border border-slate-800 px-3 py-2" >
< div class = "text-slate-400" > Username</ div >
< div id = "settings-username" class = "font-medium" > -</ div >
</ div >
< div class = "rounded-lg bg-slate-900 border border-slate-800 px-3 py-2" >
< div class = "text-slate-400" > Role</ div >
< div id = "settings-role" class = "font-medium" > -</ div >
</ div >
</ div >
< form id = "password-form" class = "space-y-3 mt-2 pt-4 border-t border-slate-800" >
< p class = "text-xs font-semibold uppercase tracking-wider text-slate-500 pb-1" > Change Password</ p >
< input
id = "current-password"
type = "password"
autocomplete = "current-password"
placeholder = "Current password"
class = "w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
required
/>
< input
id = "new-password"
type = "password"
autocomplete = "new-password"
minlength = "6"
placeholder = "New password (min 6 chars)"
class = "w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
required
/>
< input
id = "confirm-password"
type = "password"
autocomplete = "new-password"
minlength = "6"
placeholder = "Confirm new password"
class = "w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
required
/>
< div class = "pt-1" >
< p id = "password-policy-hint" class = "text-xs text-slate-400 mb-3" ></ p >
2026-03-25 10:18:22 -05:00
</ div >
< button
2026-04-29 00:22:37 -05:00
type = "submit"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-sky-600 hover:bg-sky-500 transition-colors text-white text-sm font-medium"
2026-03-25 10:18:22 -05:00
>
2026-04-29 00:22:37 -05:00
< i class = "fa-solid fa-key" ></ i > Update Password
2026-03-25 10:18:22 -05:00
</ button >
2026-04-29 00:22:37 -05:00
</ form >
2026-03-25 10:18:22 -05:00
2026-04-29 00:22:37 -05:00
< p id = "settings-message" class = "hidden text-sm rounded-lg px-3 py-2 border" ></ p >
2026-03-06 20:50:02 -06:00
</ section >
2026-04-13 17:15:06 -05:00
<!-- Active Sessions -->
2026-04-29 00:22:37 -05:00
< section id = "section-sessions" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-04-13 17:15:06 -05:00
< div class = "flex items-center justify-between gap-3" >
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-emerald-500/10 border border-emerald-500/30" >
< i class = "fa-solid fa-desktop text-emerald-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Active Sessions</ h2 >
< p class = "text-sm text-slate-400" > View and manage your active login sessions.</ p >
</ div >
</ div >
< button
id = "refresh-sessions-btn"
class = "inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 border border-slate-700 text-sm transition-colors"
type = "button"
>
< i class = "fa-solid fa-rotate" ></ i > Refresh
</ button >
2026-04-14 10:23:59 -05:00
< button
id = "remove-inactive-sessions-btn"
class = "inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-red-900/40 hover:bg-red-800/60 text-red-100 border border-red-700/60 text-sm transition-colors"
type = "button"
>
< i class = "fa-solid fa-broom" ></ i > Remove Inactive
</ button >
2026-04-13 17:15:06 -05:00
</ div >
< div class = "overflow-x-auto border border-slate-800 rounded-lg" >
< table class = "w-full text-sm" >
< thead class = "bg-slate-800/60 text-slate-300" >
< tr >
< th class = "text-left px-3 py-2" > IP</ th >
< th class = "text-left px-3 py-2" > Browser / Client</ th >
< th class = "text-left px-3 py-2" > Created</ th >
< th class = "text-left px-3 py-2" > Last Active</ th >
< th class = "text-left px-3 py-2" > Status</ th >
< th class = "text-right px-3 py-2" > Action</ th >
</ tr >
</ thead >
< tbody id = "sessions-table-body" class = "divide-y divide-slate-800" >
< tr >
< td colspan = "6" class = "px-3 py-6 text-center text-slate-400" > Loading...</ td >
</ tr >
</ tbody >
</ table >
</ div >
< p id = "sessions-message" class = "hidden text-sm rounded-lg px-3 py-2 border" ></ p >
</ section >
2026-03-24 20:42:54 -05:00
2026-04-05 21:39:22 -05:00
<!-- Dashboard Preferences -->
2026-04-29 00:22:37 -05:00
< section id = "section-prefs" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-04-05 21:39:22 -05:00
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-violet-500/10 border border-violet-500/30" >
< i class = "fa-solid fa-sliders text-violet-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Dashboard Preferences</ h2 >
< p class = "text-sm text-slate-400" > Configure notifications, client refresh rate, and navigation layout.</ p >
</ div >
</ div >
2026-03-06 20:50:02 -06:00
2026-04-05 21:39:22 -05:00
< form id = "prefs-form" class = "space-y-4" >
< div class = "grid grid-cols-1 sm:grid-cols-2 gap-4" >
< div class = "space-y-2" >
< p class = "text-xs font-semibold uppercase tracking-wider text-slate-500" > Notifications</ p >
< label class = "flex items-center gap-2 text-sm" >
< input id = "pref-notifications" type = "checkbox" class = "h-4 w-4" />
< span > Enable notification badge updates</ span >
</ label >
< label class = "flex items-center gap-2 text-sm" >
< input id = "pref-desktop-notifications" type = "checkbox" class = "h-4 w-4 accent-emerald-500" />
< span > Enable desktop (OS) notifications</ span >
</ label >
< p id = "pref-desktop-notifications-hint" class = "hidden text-xs text-amber-400" >
< i class = "fa-solid fa-triangle-exclamation mr-1" ></ i >
Browser permission was denied. Reset notification permissions for this site in your browser settings, then try again.
</ p >
</ div >
< div class = "space-y-2" >
< p class = "text-xs font-semibold uppercase tracking-wider text-slate-500" > Refresh Interval</ p >
< label class = "block" >
< span class = "text-sm text-slate-300" > Client refresh interval (seconds)</ span >
< input
id = "pref-refresh-seconds"
type = "number"
min = "3"
max = "120"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
</ div >
</ div >
< div >
< p class = "text-xs font-semibold uppercase tracking-wider text-slate-500 mb-2" > Navigation Layout</ p >
< div class = "flex gap-2" >
< button type = "button" id = "pref-nav-sidebar"
class = "nav-layout-btn flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-colors" >
< i class = "fa-solid fa-table-columns" ></ i > Sidebar
</ button >
< button type = "button" id = "pref-nav-topbar"
class = "nav-layout-btn flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-lg border text-sm font-medium transition-colors" >
< i class = "fa-solid fa-bars" ></ i > Top Bar
</ button >
</ div >
< p class = "text-xs text-slate-500 mt-1" > Changes apply immediately on save.</ p >
</ div >
2026-04-17 18:43:16 -05:00
< div class = "space-y-2" >
< p class = "text-xs font-semibold uppercase tracking-wider text-slate-500" > Visibility</ p >
< label class = "flex items-center gap-2 text-sm" >
< input id = "pref-nav-hidden" type = "checkbox" class = "h-4 w-4 accent-indigo-500" />
< span > Hide navigation bar</ span >
</ label >
< p class = "text-xs text-slate-500" > Press < kbd class = "px-1 py-0.5 rounded bg-slate-800 border border-slate-700 text-slate-400 text-[10px]" > Ctrl+\</ kbd > to toggle anytime.</ p >
</ div >
2026-04-05 21:39:22 -05:00
< button
type = "submit"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-violet-700 hover:bg-violet-600 transition-colors text-white text-sm font-medium"
>
< i class = "fa-solid fa-floppy-disk" ></ i > Save Preferences
</ button >
</ form >
</ section >
<!-- Notification Delivery -->
2026-04-29 00:22:37 -05:00
< section id = "telegram-settings" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-04-05 21:39:22 -05:00
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-emerald-500/10 border border-emerald-500/30" >
< i class = "fa-solid fa-bell text-emerald-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Notification Delivery</ h2 >
< p class = "text-sm text-slate-400" > Configure where alerts and notifications are sent.</ p >
</ div >
</ div >
< div class = "rounded-lg border border-slate-700 bg-slate-900 px-4 py-4" >
< p class = "text-sm text-slate-300" >
Webhook and Telegram Bot settings have moved to the
< a href = "/notifications" class = "text-sky-400 hover:underline font-medium" > Notifications page</ a > .
Configure your personal webhook and Telegram bot there.
</ p >
</ div >
</ section >
2026-04-29 00:22:37 -05:00
<!-- News & Updates -->
< section id = "section-news" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-sky-500/10 border border-sky-500/30" >
< i class = "fa-brands fa-telegram text-sky-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > News & Updates</ h2 >
< p class = "text-sm text-slate-400" > Stay up to date with the newest features, releases, and announcements.</ p >
</ div >
</ div >
< div class = "rounded-lg border border-slate-700 bg-slate-900 px-4 py-4 flex flex-col sm:flex-row sm:items-center gap-4" >
< div class = "flex-1" >
< p class = "text-sm text-slate-300" >
Follow the official Overlord Telegram channel for the latest updates, changelogs, and feature announcements.
</ p >
< p class = "text-xs text-slate-500 mt-1" > Channel: < span class = "text-slate-400 font-mono" > @kunta</ span ></ p >
</ div >
< a
href = "https://t.me/kunta"
target = "_blank"
rel = "noopener noreferrer"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-sky-600 hover:bg-sky-500 transition-colors text-white text-sm font-medium whitespace-nowrap"
>
< i class = "fa-brands fa-telegram" ></ i >
Open on Telegram
</ a >
</ div >
</ section >
<!-- ========== OPERATIONS ========== -->
<!-- Banned IPs -->
< section id = "section-bans" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
< div class = "flex items-center justify-between gap-3" >
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-red-500/10 border border-red-500/30" >
< i class = "fa-solid fa-ban text-red-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Banned IPs</ h2 >
< p class = "text-sm text-slate-400" > Unban blocked agent source IPs.</ p >
</ div >
</ div >
< button
id = "refresh-bans-btn"
class = "inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 border border-slate-700 text-sm transition-colors"
type = "button"
>
< i class = "fa-solid fa-rotate" ></ i > Refresh
</ button >
</ div >
< div id = "bans-permission-note" class = "hidden rounded-lg border border-amber-800 bg-amber-900/20 text-amber-200 px-3 py-2 text-sm" >
Your role does not allow IP unban actions.
</ div >
< div class = "overflow-x-auto border border-slate-800 rounded-lg" >
< table class = "w-full text-sm" >
< thead class = "bg-slate-800/60 text-slate-300" >
< tr >
< th class = "text-left px-3 py-2" > IP</ th >
< th class = "text-left px-3 py-2" > Reason</ th >
< th class = "text-left px-3 py-2" > Banned At</ th >
< th class = "text-right px-3 py-2" > Action</ th >
</ tr >
</ thead >
< tbody id = "bans-table-body" class = "divide-y divide-slate-800" >
< tr >
< td colspan = "4" class = "px-3 py-6 text-center text-slate-400" > Loading...</ td >
</ tr >
</ tbody >
</ table >
</ div >
</ section >
<!-- Build IDs (operator/admin) -->
< section id = "section-builds" class = "hidden bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
< div class = "flex items-center justify-between gap-3" >
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-orange-500/10 border border-orange-500/30" >
< i class = "fa-solid fa-hammer text-orange-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Build IDs</ h2 >
< p class = "text-sm text-slate-400" > Review your builds, see how many agents have claimed each, and block a build to immediately disconnect any agent that connects with its tag.</ p >
</ div >
</ div >
< div class = "flex items-center gap-2" >
< label class = "hidden lg:flex items-center gap-2 text-xs text-slate-400" id = "builds-show-all-wrap" >
< input id = "builds-show-all" type = "checkbox" class = "h-3.5 w-3.5" />
< span > Show all users' builds</ span >
</ label >
< button
id = "refresh-builds-btn"
class = "inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-slate-800 hover:bg-slate-700 border border-slate-700 text-sm transition-colors"
type = "button"
>
< i class = "fa-solid fa-rotate" ></ i > Refresh
</ button >
</ div >
</ div >
< p class = "text-xs text-slate-500" >
< strong > Claim count</ strong > is the number of unique agent fingerprints that have ever connected using a build's tag. Each unique host produces one claim. A claim count higher than the number of machines you actually deployed to suggests the build tag may have leaked.
</ p >
< div class = "overflow-x-auto border border-slate-800 rounded-lg" >
< table class = "w-full text-sm" >
< thead class = "bg-slate-800/60 text-slate-300" >
< tr >
< th class = "text-left px-3 py-2" > Build ID</ th >
< th class = "text-left px-3 py-2" > Tag</ th >
< th class = "text-left px-3 py-2" > Status</ th >
< th class = "text-left px-3 py-2" > Created</ th >
< th class = "text-right px-3 py-2" > Claims</ th >
< th class = "text-right px-3 py-2" > Action</ th >
</ tr >
</ thead >
< tbody id = "builds-table-body" class = "divide-y divide-slate-800" >
< tr >
< td colspan = "6" class = "px-3 py-6 text-center text-slate-400" > Loading...</ td >
</ tr >
</ tbody >
</ table >
</ div >
< p id = "builds-message" class = "hidden text-sm rounded-lg px-3 py-2 border" ></ p >
</ section >
<!-- Wipe Offline Clients -->
< section id = "wipe-offline-section" class = "hidden bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-red-500/10 border border-red-500/30" >
< i class = "fa-solid fa-circle-minus text-red-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Wipe Offline Clients</ h2 >
< p class = "text-sm text-slate-400" > Permanently remove all offline clients from the dashboard in one click.</ p >
</ div >
</ div >
< div class = "rounded-lg border border-slate-700 bg-slate-900 px-4 py-4 flex flex-col sm:flex-row sm:items-center gap-4" >
< div class = "flex-1" >
< p class = "text-sm text-slate-300" >
Deletes every client currently marked as offline. If any of them reconnect later they will reappear automatically.
</ p >
</ div >
< button
id = "wipe-offline-btn"
type = "button"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-red-800/70 hover:bg-red-700 transition-colors text-red-100 border border-red-700 text-sm font-medium whitespace-nowrap"
>
< i class = "fa-solid fa-circle-minus" ></ i >
Wipe Offline
</ button >
</ div >
< p id = "wipe-offline-message" class = "hidden text-sm rounded-lg px-3 py-2 border" ></ p >
</ section >
<!-- ========== ADMIN ========== -->
2026-04-05 21:39:22 -05:00
<!-- Security Policy -->
2026-04-29 00:22:37 -05:00
< section id = "section-security" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-04-05 21:39:22 -05:00
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-indigo-500/10 border border-indigo-500/30" >
< i class = "fa-solid fa-shield-halved text-indigo-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Security Policy</ h2 >
< p class = "text-sm text-slate-400" > Admin-only global security controls.</ p >
</ div >
</ div >
< form id = "security-form" class = "space-y-4" >
< div id = "security-permission-note" class = "hidden rounded-lg border border-amber-800 bg-amber-900/20 text-amber-200 px-3 py-2 text-sm" >
Only admins can update security policy settings.
</ div >
< div class = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3" >
< label class = "block" >
< span class = "text-sm text-slate-300" > Session TTL (hours)</ span >
< input
id = "security-session-ttl"
type = "number"
min = "1"
max = "720"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Max login attempts</ span >
< input
id = "security-login-max-attempts"
type = "number"
min = "1"
max = "50"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Login window (minutes)</ span >
< input
id = "security-login-window"
type = "number"
min = "1"
max = "1440"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Lockout duration (minutes)</ span >
< input
id = "security-lockout"
type = "number"
min = "1"
max = "1440"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Minimum password length</ span >
< input
id = "security-password-min"
type = "number"
min = "6"
max = "128"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
</ div >
< div >
< p class = "text-xs font-semibold uppercase tracking-wider text-slate-500 mb-2" > Password Requirements</ p >
< div class = "grid grid-cols-2 sm:grid-cols-4 gap-2 text-sm" >
< label class = "flex items-center gap-2" >
< input id = "security-require-uppercase" type = "checkbox" class = "h-4 w-4" />
< span > Uppercase</ span >
</ label >
< label class = "flex items-center gap-2" >
< input id = "security-require-lowercase" type = "checkbox" class = "h-4 w-4" />
< span > Lowercase</ span >
</ label >
< label class = "flex items-center gap-2" >
< input id = "security-require-number" type = "checkbox" class = "h-4 w-4" />
< span > Number</ span >
</ label >
< label class = "flex items-center gap-2" >
< input id = "security-require-symbol" type = "checkbox" class = "h-4 w-4" />
< span > Symbol</ span >
</ label >
</ div >
</ div >
< button
type = "submit"
id = "security-save-btn"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-indigo-700 hover:bg-indigo-600 transition-colors text-white text-sm font-medium"
>
< i class = "fa-solid fa-shield-halved" ></ i > Save Security Policy
</ button >
</ form >
</ section >
<!-- TLS Certificates -->
2026-04-29 00:22:37 -05:00
< section id = "section-tls" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-04-05 21:39:22 -05:00
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-cyan-500/10 border border-cyan-500/30" >
< i class = "fa-solid fa-certificate text-cyan-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > TLS Certificates</ h2 >
< p class = "text-sm text-slate-400" > Admin-only TLS source configuration. Changes apply after server restart.</ p >
</ div >
</ div >
< form id = "tls-form" class = "space-y-4" >
< div id = "tls-permission-note" class = "hidden rounded-lg border border-amber-800 bg-amber-900/20 text-amber-200 px-3 py-2 text-sm" >
Only admins can update TLS settings.
</ div >
< label class = "flex items-center gap-2 text-sm" >
< input id = "tls-certbot-enabled" type = "checkbox" class = "h-4 w-4" />
< span > Use certbot certificates in production runtime (NODE_ENV=production)</ span >
</ label >
< div class = "grid grid-cols-1 sm:grid-cols-2 gap-3" >
< label class = "block" >
< span class = "text-sm text-slate-300" > Let's Encrypt account email</ span >
< input
id = "tls-certbot-email"
type = "email"
placeholder = "admin@example.com"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Certbot live base path</ span >
< input
id = "tls-certbot-live-path"
type = "text"
placeholder = "/etc/letsencrypt/live"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Certbot domain folder</ span >
< input
id = "tls-certbot-domain"
type = "text"
placeholder = "example.com"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Cert file name</ span >
< input
id = "tls-certbot-cert-file"
type = "text"
placeholder = "fullchain.pem"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Key file name</ span >
< input
id = "tls-certbot-key-file"
type = "text"
placeholder = "privkey.pem"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > CA chain file name</ span >
< input
id = "tls-certbot-ca-file"
type = "text"
placeholder = "chain.pem"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500"
/>
</ label >
</ div >
< div class = "rounded-lg border border-slate-700 bg-slate-900 px-4 py-4 flex flex-col sm:flex-row sm:items-center gap-4" >
< div class = "flex-1" >
< p class = "text-sm text-slate-300" > One-click setup requirements: DNS for the domain must point to this server and inbound port 80 must be reachable.</ p >
</ div >
< button
type = "button"
id = "tls-certbot-auto-btn"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-emerald-700 hover:bg-emerald-600 transition-colors text-white text-sm font-medium whitespace-nowrap"
>
< i class = "fa-solid fa-wand-magic-sparkles" ></ i > Auto Setup Free SSL
</ button >
</ div >
< button
type = "submit"
id = "tls-save-btn"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-cyan-700 hover:bg-cyan-600 transition-colors text-white text-sm font-medium"
>
< i class = "fa-solid fa-certificate" ></ i > Save TLS Settings
</ button >
</ form >
</ section >
2026-04-14 21:56:18 -05:00
<!-- Team Chat -->
2026-04-29 00:22:37 -05:00
< section id = "chat-settings-section" class = "hidden bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-04-14 21:56:18 -05:00
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-sky-500/10 border border-sky-500/30" >
< i class = "fa-solid fa-comments text-sky-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Team Chat</ h2 >
< p class = "text-sm text-slate-400" > Admin-only. Configure message retention for the team chat.</ p >
</ div >
</ div >
< form id = "chat-settings-form" class = "space-y-4" >
< label class = "block" >
< span class = "text-sm text-slate-300 font-medium" > Message Retention (days)</ span >
< input
type = "number"
id = "chat-retention-days"
min = "0"
max = "365"
step = "1"
class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500 text-sm"
/>
< p class = "text-xs text-slate-500 mt-1" > Messages older than this will be automatically deleted. Set to 0 to keep messages forever. Max 365 days.</ p >
</ label >
< button
type = "submit"
id = "chat-settings-save-btn"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-sky-700 hover:bg-sky-600 transition-colors text-white text-sm font-medium"
>
< i class = "fa-solid fa-floppy-disk" ></ i > Save Chat Settings
</ button >
</ form >
</ section >
2026-04-19 18:24:50 -05:00
<!-- Registration Settings (admin only) -->
2026-04-29 00:22:37 -05:00
< section id = "registration-section" class = "hidden bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-04-19 18:24:50 -05:00
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-emerald-500/10 border border-emerald-500/30" >
< i class = "fa-solid fa-user-plus text-emerald-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Registration</ h2 >
< p class = "text-sm text-slate-400" > Admin-only. Control how new users can register.</ p >
</ div >
</ div >
< form id = "registration-form" class = "space-y-4" >
< div class = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3" >
< label class = "block" >
< span class = "text-sm text-slate-300" > Registration Mode</ span >
< select id = "reg-mode" class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" >
< option value = "off" > Off</ option >
< option value = "open" > Open</ option >
< option value = "key" > Key Required</ option >
< option value = "approval" > Approval Required</ option >
</ select >
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Default Role</ span >
< select id = "reg-default-role" class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" >
< option value = "operator" > Operator</ option >
< option value = "viewer" > Viewer</ option >
</ select >
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Max Users (0 = unlimited)</ span >
< input id = "reg-max-users" type = "number" min = "0" max = "10000" class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" />
</ label >
</ div >
< button type = "submit" class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-emerald-700 hover:bg-emerald-600 transition-colors text-white text-sm font-medium" >
< i class = "fa-solid fa-floppy-disk" ></ i > Save Registration Settings
</ button >
</ form >
< p id = "reg-settings-msg" class = "hidden text-sm rounded-lg px-3 py-2 border" ></ p >
<!-- Key Management (visible when mode=key) -->
< div id = "reg-key-section" class = "hidden space-y-3 border-t border-slate-700 pt-4" >
< h3 class = "text-md font-semibold text-slate-200" > Registration Keys</ h3 >
< div class = "flex flex-wrap items-end gap-3" >
< label class = "block" >
< span class = "text-sm text-slate-300" > Count</ span >
< input id = "reg-key-count" type = "number" value = "1" min = "1" max = "100" class = "mt-1 w-24 px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" />
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Label (optional)</ span >
< input id = "reg-key-label" type = "text" maxlength = "64" placeholder = "e.g. Batch 1" class = "mt-1 w-40 px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" />
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Expires (hours, 0=never)</ span >
< input id = "reg-key-expires" type = "number" value = "0" min = "0" max = "8760" class = "mt-1 w-28 px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" />
</ label >
< button id = "reg-key-generate-btn" type = "button" class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-emerald-700 hover:bg-emerald-600 transition-colors text-white text-sm font-medium" >
< i class = "fa-solid fa-plus" ></ i > Generate
</ button >
< a id = "reg-key-export-btn" href = "/api/registration/keys/export" class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 transition-colors text-white text-sm font-medium" >
< i class = "fa-solid fa-file-export" ></ i > Export
</ a >
</ div >
< div class = "overflow-x-auto" >
< table class = "w-full text-sm text-left text-slate-300" >
< thead class = "text-xs uppercase text-slate-500 border-b border-slate-700" >
< tr >
< th class = "px-3 py-2" > Key</ th >
< th class = "px-3 py-2" > Label</ th >
< th class = "px-3 py-2" > Status</ th >
< th class = "px-3 py-2" > Created</ th >
< th class = "px-3 py-2" > Expires</ th >
< th class = "px-3 py-2" ></ th >
</ tr >
</ thead >
< tbody id = "reg-keys-tbody" ></ tbody >
</ table >
</ div >
</ div >
<!-- Pending Registrations (visible when mode=approval) -->
< div id = "reg-pending-section" class = "hidden space-y-3 border-t border-slate-700 pt-4" >
< h3 class = "text-md font-semibold text-slate-200" > Pending Registrations</ h3 >
< div class = "overflow-x-auto" >
< table class = "w-full text-sm text-left text-slate-300" >
< thead class = "text-xs uppercase text-slate-500 border-b border-slate-700" >
< tr >
< th class = "px-3 py-2" > Username</ th >
< th class = "px-3 py-2" > Requested</ th >
< th class = "px-3 py-2" > Status</ th >
< th class = "px-3 py-2" > Actions</ th >
</ tr >
</ thead >
< tbody id = "reg-pending-tbody" ></ tbody >
</ table >
</ div >
< p id = "reg-pending-empty" class = "text-sm text-slate-500" > No pending registrations.</ p >
</ div >
</ section >
<!-- Build Rate Limit (admin only) -->
2026-04-29 00:22:37 -05:00
< section id = "build-rate-limit-section" class = "hidden bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
2026-04-19 18:24:50 -05:00
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-orange-500/10 border border-orange-500/30" >
< i class = "fa-solid fa-gauge-high text-orange-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Build Rate Limit</ h2 >
< p class = "text-sm text-slate-400" > Admin-only. Prevent abuse by limiting build frequency.</ p >
</ div >
</ div >
< form id = "build-rate-limit-form" class = "space-y-4" >
< div class = "grid grid-cols-1 sm:grid-cols-3 gap-3" >
< label class = "block" >
< span class = "text-sm text-slate-300" > Max Builds per Hour (per user)</ span >
< input id = "brl-max-per-hour" type = "number" min = "1" max = "100" class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" />
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Max Concurrent (per user)</ span >
< input id = "brl-max-concurrent-user" type = "number" min = "1" max = "10" class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" />
</ label >
< label class = "block" >
< span class = "text-sm text-slate-300" > Global Max Concurrent</ span >
< input id = "brl-global-concurrent" type = "number" min = "1" max = "50" class = "mt-1 w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500" />
</ label >
</ div >
< button type = "submit" class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-orange-700 hover:bg-orange-600 transition-colors text-white text-sm font-medium" >
< i class = "fa-solid fa-floppy-disk" ></ i > Save Rate Limit Settings
</ button >
</ form >
< p id = "brl-settings-msg" class = "hidden text-sm rounded-lg px-3 py-2 border" ></ p >
</ section >
2026-04-29 00:22:37 -05:00
<!-- Custom CSS -->
< section id = "section-appearance" class = "bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-fuchsia-500/10 border border-fuchsia-500/30" >
< i class = "fa-solid fa-paintbrush text-fuchsia-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Custom CSS</ h2 >
< p class = "text-sm text-slate-400" > Admin-only. Paste custom CSS to override the default theme across all pages.</ p >
</ div >
</ div >
< form id = "appearance-form" class = "space-y-4" >
< div id = "appearance-permission-note" class = "hidden rounded-lg border border-amber-800 bg-amber-900/20 text-amber-200 px-3 py-2 text-sm" >
Only admins can update custom CSS.
</ div >
< label class = "block" >
< textarea
id = "appearance-custom-css"
rows = "12"
spellcheck = "false"
placeholder = "/* Paste your custom CSS here */ :root { --color-primary: #f59e0b; }"
class = "w-full px-3 py-2 rounded-lg bg-slate-900 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-sky-500 font-mono text-xs resize-y"
></ textarea >
< p class = "text-xs text-slate-500 mt-1" > Max 50 KB. Reload any open page after saving to see the new styles.</ p >
</ label >
< button
type = "submit"
id = "appearance-save-btn"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-fuchsia-700 hover:bg-fuchsia-600 transition-colors text-white text-sm font-medium"
>
< i class = "fa-solid fa-paintbrush" ></ i > Save Custom CSS
</ button >
</ form >
</ section >
<!-- Export & Import -->
< section id = "export-import-section" class = "hidden bg-slate-900/60 border border-slate-800 rounded-xl p-5 space-y-4 settings-section" >
< div class = "flex items-center gap-3" >
< div class = "flex items-center justify-center w-9 h-9 rounded-lg bg-amber-500/10 border border-amber-500/30" >
< i class = "fa-solid fa-file-export text-amber-400" ></ i >
</ div >
< div >
< h2 class = "text-lg font-semibold" > Export & Import Settings</ h2 >
< p class = "text-sm text-slate-400" > Backup or restore your Overlord configuration (notifications, security, TLS, appearance, auth tokens).</ p >
</ div >
</ div >
< div class = "rounded-lg border border-slate-700 bg-slate-900 px-4 py-4 flex flex-col sm:flex-row sm:items-center gap-4" >
< div class = "flex-1 space-y-1" >
< p class = "text-sm text-slate-300" >
< strong > Export</ strong > — Downloads a JSON file with all current settings and auth tokens.
</ p >
< p class = "text-xs text-slate-500" > Keep this file secure — it contains sensitive tokens.</ p >
</ div >
< button
id = "export-settings-btn"
type = "button"
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-amber-600 hover:bg-amber-500 transition-colors text-white text-sm font-medium whitespace-nowrap"
>
< i class = "fa-solid fa-download" ></ i >
Export Settings
</ button >
</ div >
< div class = "rounded-lg border border-slate-700 bg-slate-900 px-4 py-4 flex flex-col sm:flex-row sm:items-center gap-4" >
< div class = "flex-1 space-y-1" >
< p class = "text-sm text-slate-300" >
< strong > Import</ strong > — Restore settings from a previously exported JSON file.
</ p >
< p class = "text-xs text-slate-500" > Auth token changes require a server restart. Environment variables will still take priority.</ p >
</ div >
< label
class = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 transition-colors text-white text-sm font-medium whitespace-nowrap cursor-pointer"
>
< i class = "fa-solid fa-upload" ></ i >
Import Settings
< input id = "import-settings-file" type = "file" accept = ".json,application/json" class = "hidden" />
</ label >
</ div >
< p id = "export-import-message" class = "hidden text-sm rounded-lg px-3 py-2 border" ></ p >
</ section >
</ div >
<!-- /Sections -->
</ div >
<!-- /flex wrapper -->
2026-04-13 13:49:41 -05:00
< footer class = "text-center text-sm text-slate-500 py-4 border-t border-slate-800 mt-4" >
< p > Created by KDot — < a href = "https://t.me/kunta" target = "_blank" rel = "noopener noreferrer" class = "text-sky-400 hover:text-sky-300 transition-colors" >< i class = "fa-brands fa-telegram" ></ i > t.me/kunta</ a ></ p >
</ footer >
2026-03-28 22:18:33 -05:00
</ main >
2026-03-06 20:50:02 -06:00
< script type = "module" src = "/assets/nav.js" ></ script >
< script type = "module" src = "/assets/settings.js" ></ script >
</ body >
</ html >