Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions deploy/nginx/rustadmin.conf
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,6 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
}

location = /superuser {
return 302 /superuser/;
}

location = /superuser/ {
try_files /superuser/ui/index.html =404;
}

location /superuser/ {
try_files $uri $uri/ /superuser/ui/index.html;
}

location / {
try_files $uri /index.html;
}
Comment on lines 36 to 38

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve assets when hitting legacy /superuser/ path

Removing the dedicated /superuser locations means /superuser/… now falls through to the catch‑all location / (lines 36‑38), which serves index.html from that subpath. The SPA still references its JS/CSS with relative URLs like assets/app.js, so when the document is loaded at /superuser/ the browser requests /superuser/assets/..., which Nginx does not serve and instead returns HTML. Any operator following an existing /superuser/ bookmark now gets a broken page with no scripts or styles. Redirecting /superuser/* to / or making the asset URLs absolute is needed to keep the legacy entry point usable.

Useful? React with 👍 / 👎.

Expand Down
80 changes: 23 additions & 57 deletions frontend/assets/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,9 @@
lastUpdate: 'Last update timestamp'
};

const superuserUi = Boolean(typeof window !== 'undefined' && window.SUPERUSER_MODE);
const defaultPanel = superuserUi
? 'admin'
: (typeof window !== 'undefined' && window.DEFAULT_PANEL)
? String(window.DEFAULT_PANEL)
: 'dashboard';
const defaultPanel = (typeof window !== 'undefined' && window.DEFAULT_PANEL)
? String(window.DEFAULT_PANEL)
: 'dashboard';
const state = {
API: '',
TOKEN: localStorage.getItem('token') || '',
Expand Down Expand Up @@ -159,7 +156,6 @@
selectedUserId: null,
loading: false
},
superuserUi
};
let adminUserFilter = '';
const loginUsername = $('#username');
Expand Down Expand Up @@ -4794,12 +4790,11 @@
}

function applyPermissionGates() {
const adminOnlyMode = state.superuserUi === true;
const canManageServers = adminOnlyMode ? false : hasGlobalPermission('manageServers');
const canManageUsers = adminOnlyMode ? false : hasGlobalPermission('manageUsers');
const canManageRoles = adminOnlyMode ? false : hasGlobalPermission('manageRoles');
const canAccessTeam = adminOnlyMode ? false : (canManageUsers || canManageRoles);
const canAccessLinked = adminOnlyMode ? false : canAccessTeam;
const canManageServers = hasGlobalPermission('manageServers');
const canManageUsers = hasGlobalPermission('manageUsers');
const canManageRoles = hasGlobalPermission('manageRoles');
const canAccessTeam = canManageUsers || canManageRoles;
const canAccessLinked = canAccessTeam;
const canAccessAdminPanel = isSuperuser() && hasGlobalPermission('manageUsers');
if (addServerPrompt) {
addServerPrompt.classList.toggle('hidden', !canManageServers);
Expand Down Expand Up @@ -4897,19 +4892,14 @@
}

function switchPanel(panel = 'dashboard') {
const adminOnlyMode = state.superuserUi === true;
const superuser = isSuperuser();
const canAccessTeam = adminOnlyMode ? false : (hasGlobalPermission('manageUsers') || hasGlobalPermission('manageRoles'));
const canAccessLinked = adminOnlyMode ? false : canAccessTeam;
const canAccessAdmin = adminOnlyMode ? superuser : (superuser && hasGlobalPermission('manageUsers'));
const canAccessDiscord = adminOnlyMode ? false : canManageTeamDiscord();
const canAccessTeam = hasGlobalPermission('manageUsers') || hasGlobalPermission('manageRoles');
const canAccessLinked = canAccessTeam;
const canAccessAdmin = superuser && hasGlobalPermission('manageUsers');
const canAccessDiscord = canManageTeamDiscord();
const canAccessSettings = Boolean(state.currentUser);
const fallbackPanel = adminOnlyMode
? (canAccessAdmin ? 'admin' : (canAccessSettings ? 'settings' : 'dashboard'))
: 'dashboard';
let nextPanel = adminOnlyMode
? (panel === 'settings' ? 'settings' : 'admin')
: panel;
const fallbackPanel = 'dashboard';
let nextPanel = panel;
if (
(nextPanel === 'team' && !canAccessTeam)
|| (nextPanel === 'discord' && !canAccessDiscord)
Expand Down Expand Up @@ -6712,12 +6702,6 @@
}

async function refreshServers() {
if (state.superuserUi) {
if (serversEl) serversEl.innerHTML = '';
if (serversEmpty) serversEmpty.classList.add('hidden');
hideAddServerCard({ force: true });
return;
}
try {
const list = await api('/servers');
syncServerPermissions(list);
Expand Down Expand Up @@ -7439,7 +7423,7 @@
if (user.superuser) {
const badge = document.createElement('span');
badge.className = 'badge strong';
badge.textContent = 'Superuser';
badge.textContent = 'Global admin';
trailing.appendChild(badge);
}
const chevron = document.createElement('span');
Expand Down Expand Up @@ -7607,7 +7591,7 @@
const roleChanged = !!role && role !== user.role;
const superuserChanged = Boolean(desiredSuperuser) !== Boolean(user.superuser);
if (!roleChanged && !superuserChanged) {
showNotice(adminUserRoleStatus, 'Select a different role or superuser state before updating.', 'error');
showNotice(adminUserRoleStatus, 'Select a different role or global admin state before updating.', 'error');
return;
}
try {
Expand Down Expand Up @@ -8223,9 +8207,8 @@
}

function toggleUserCard() {
const adminOnlyMode = state.superuserUi === true;
const canUsers = adminOnlyMode ? false : hasGlobalPermission('manageUsers');
const canRoles = adminOnlyMode ? false : hasGlobalPermission('manageRoles');
const canUsers = hasGlobalPermission('manageUsers');
const canRoles = hasGlobalPermission('manageRoles');
if (canUsers || canRoles) {
userCard.classList.remove('hidden');
if (canUsers) {
Expand Down Expand Up @@ -8865,22 +8848,14 @@
}

function updateTeamAccessView({ refreshUsers = false, refreshAdmin = false } = {}) {
const adminOnlyMode = state.superuserUi === true;
const superuser = isSuperuser();
const canUsers = adminOnlyMode ? false : hasGlobalPermission('manageUsers');
const canRoles = adminOnlyMode ? false : hasGlobalPermission('manageRoles');
const canAccessTeam = adminOnlyMode ? false : (canUsers || canRoles);
const canManageDiscord = adminOnlyMode ? false : canManageTeamDiscord();
const canAccessLinked = adminOnlyMode ? false : (canUsers || canRoles);
const canUsers = hasGlobalPermission('manageUsers');
const canRoles = hasGlobalPermission('manageRoles');
const canAccessTeam = canUsers || canRoles;
const canManageDiscord = canManageTeamDiscord();
const canAccessLinked = canUsers || canRoles;
const canAccessAdmin = superuser && hasGlobalPermission('manageUsers');

if (navDashboard) {
navDashboard.classList.toggle('hidden', adminOnlyMode);
navDashboard.setAttribute('aria-hidden', adminOnlyMode ? 'true' : 'false');
navDashboard.disabled = adminOnlyMode;
if (adminOnlyMode) navDashboard.classList.remove('active');
}

if (navTeam) {
navTeam.classList.toggle('hidden', !canAccessTeam);
navTeam.setAttribute('aria-hidden', canAccessTeam ? 'false' : 'true');
Expand Down Expand Up @@ -8909,11 +8884,6 @@
if (!canAccessAdmin) navAdmin.classList.remove('active');
}

if (teamSwitcher && adminOnlyMode) {
teamSwitcher.classList.add('hidden');
teamSwitcher.setAttribute('aria-hidden', 'true');
}

if (!canAccessTeam) {
if (teamPanel) teamPanel.classList.add('hidden');
if (state.activePanel === 'team') switchPanel('dashboard');
Expand Down Expand Up @@ -9482,10 +9452,6 @@
});
navAdmin?.addEventListener('click', () => {
if (navAdmin.disabled) return;
if (!state.superuserUi) {
window.location.href = '/superuser/ui/';
return;
}
hideWorkspace('nav');
switchPanel('admin');
closeProfileMenu();
Expand Down
51 changes: 0 additions & 51 deletions frontend/assets/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1010,35 +1010,6 @@ label.inline input[type="checkbox"]:focus-visible,
.team-section-head h4 { font-size: 1.15rem; }
.team-section-head p { margin: 0; }

.superuser-ui {
background: radial-gradient(420px 320px at 10% 10%, rgba(59, 130, 246, 0.16), transparent 70%),
radial-gradient(520px 400px at 80% 0%, rgba(248, 113, 113, 0.16), transparent 70%),
#0e1118;
}

.mode-pill {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
border-radius: 999px;
font-size: 0.9rem;
font-weight: 700;
letter-spacing: 0.01em;
color: #e0f2fe;
background: linear-gradient(120deg, rgba(59, 130, 246, 0.2), rgba(45, 212, 191, 0.16));
border: 1px solid rgba(124, 221, 252, 0.35);
box-shadow: 0 12px 28px rgba(14, 165, 233, 0.18);
width: fit-content;
}

.mode-pill.subtle {
color: #c7d2fe;
background: rgba(148, 163, 184, 0.12);
border-color: rgba(148, 163, 184, 0.32);
box-shadow: none;
}

.global-admin { display: flex; flex-direction: column; gap: 24px; }

.global-admin-head { display: flex; justify-content: space-between; gap: 18px; align-items: flex-start; flex-wrap: wrap; }
Expand Down Expand Up @@ -1095,28 +1066,6 @@ label.inline input[type="checkbox"]:focus-visible,
gap: 6px;
}

.superuser-guide {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 18px;
padding: 16px 18px;
border-radius: var(--radius-md);
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: inset 0 0 0 1px rgba(45, 212, 191, 0.14);
align-items: center;
}

.superuser-checklist {
display: grid;
gap: 10px;
padding-left: 18px;
margin: 0;
color: #e5e7eb;
}

.superuser-checklist li { line-height: 1.4; }

.admin-stat-card h3 { font-size: 1.8rem; }

.admin-teams { gap: 14px; }
Expand Down
10 changes: 5 additions & 5 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ <h4 id="teamAuthTitle">Discord authentication</h4>
<section id="adminPanel" class="hidden admin-panel global-admin" aria-labelledby="adminPanelTitle">
<div class="section-head global-admin-head">
<div>
<p class="badge strong">Superuser tools</p>
<p class="badge strong">Global admin tools</p>
<h2 id="adminPanelTitle">Global administration</h2>
<p class="muted">Edit every organization without switching teams. Changes apply everywhere.</p>
</div>
Expand All @@ -376,7 +376,7 @@ <h2 id="adminPanelTitle">Global administration</h2>

<div class="admin-global-banner">
<div>
<p class="muted small">Superuser workspace</p>
<p class="muted small">Global admin workspace</p>
<h3>System-wide user controls</h3>
<p class="muted">Promote, demote, or reset accounts without leaving the global view.</p>
</div>
Expand All @@ -393,7 +393,7 @@ <h3 id="adminUserCount">0</h3>
<p class="muted small">Global users</p>
</article>
<article class="admin-stat-card">
<p class="muted small">Superusers</p>
<p class="muted small">Global admins</p>
<h3 id="adminSuperuserCount">0</h3>
<p class="muted small">Full control</p>
</article>
Expand Down Expand Up @@ -428,7 +428,7 @@ <h3>Create a user</h3>
<select id="adminNewUserRole"></select>
</label>
<label class="inline">
<input id="adminNewUserSuperuser" type="checkbox"> Grant superuser access
<input id="adminNewUserSuperuser" type="checkbox"> Grant global admin access
</label>
<label>Assign to organization (optional)
<select id="adminNewUserTeam"></select>
Expand Down Expand Up @@ -477,7 +477,7 @@ <h4>Selected user</h4>
<button id="adminUserSaveRole" type="button" class="ghost">Update role</button>
</div>
<label class="inline">
<input id="adminUserSuperuser" type="checkbox"> Superuser access (all organizations)
<input id="adminUserSuperuser" type="checkbox"> Global admin access (all organizations)
</label>
<p id="adminUserRoleStatus" class="notice small hidden"></p>
<div class="grid2 stack-sm">
Expand Down
Loading