2026-05-18 13:21:22 -04:00
|
|
|
/**
|
|
|
|
|
* auth-guard.js
|
|
|
|
|
* Included on every protected page.
|
|
|
|
|
*
|
|
|
|
|
* - If /api/v1/auth/me returns 401 → redirect to login.html immediately.
|
|
|
|
|
* (When AUTH_ENABLED=false the endpoint returns a synthetic guest user,
|
|
|
|
|
* so the redirect only fires in production auth-enabled mode.)
|
|
|
|
|
* - On success, populate the sidebar user widget and wire up the logout button.
|
2026-05-21 09:59:29 -04:00
|
|
|
* - Side effect: marks any sidebar link pointing at editor.html with an
|
|
|
|
|
* "IN DEV" badge, so we don't have to touch every HTML file individually.
|
2026-05-18 13:21:22 -04:00
|
|
|
*/
|
2026-05-21 09:59:29 -04:00
|
|
|
|
|
|
|
|
// ── Cross-page IN-DEV markers ─────────────────────────────────────────────
|
|
|
|
|
// Add (page-name → label) here to flag a sidebar item without editing all
|
|
|
|
|
// 13 HTML files. The CSS + DOM patch runs once on every page.
|
|
|
|
|
const IN_DEV_PAGES = {
|
|
|
|
|
'editor.html': 'IN DEV',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(function tagInDevNavItems() {
|
|
|
|
|
// 1. Inject the badge styles once. Kept inline so we don't add another
|
|
|
|
|
// HTTP request per page — and so the rule can't get out of sync with
|
|
|
|
|
// the auth-guard that toggles it.
|
|
|
|
|
if (!document.getElementById('in-dev-style')) {
|
|
|
|
|
const style = document.createElement('style');
|
|
|
|
|
style.id = 'in-dev-style';
|
|
|
|
|
style.textContent = `
|
|
|
|
|
.nav-item.is-in-dev { position: relative; }
|
|
|
|
|
.nav-item.is-in-dev .nav-dev-badge {
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
font-size: 9px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: oklch(28% 0.14 80 / 0.45);
|
|
|
|
|
color: oklch(85% 0.16 85);
|
|
|
|
|
border: 1px solid oklch(50% 0.16 80 / 0.55);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
.nav-item.is-in-dev:hover .nav-dev-badge {
|
|
|
|
|
background: oklch(32% 0.16 80 / 0.55);
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
document.head.appendChild(style);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Walk every sidebar nav-item link and tag the ones whose href matches
|
|
|
|
|
// a known in-dev page. Href is matched case-insensitively against the
|
|
|
|
|
// final path segment so '/editor.html', 'editor.html', and absolute
|
|
|
|
|
// URLs all hit.
|
|
|
|
|
const links = document.querySelectorAll('.nav-item');
|
|
|
|
|
links.forEach((a) => {
|
|
|
|
|
const href = (a.getAttribute('href') || '').toLowerCase();
|
|
|
|
|
const last = href.split('/').pop().split('?')[0];
|
|
|
|
|
const label = IN_DEV_PAGES[last];
|
|
|
|
|
if (!label) return;
|
|
|
|
|
if (a.querySelector('.nav-dev-badge')) return; // idempotent
|
|
|
|
|
a.classList.add('is-in-dev');
|
|
|
|
|
const badge = document.createElement('span');
|
|
|
|
|
badge.className = 'nav-dev-badge';
|
|
|
|
|
badge.textContent = label;
|
|
|
|
|
a.appendChild(badge);
|
|
|
|
|
});
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
// ── Auth + user widget ────────────────────────────────────────────────────
|
2026-05-18 13:21:22 -04:00
|
|
|
(async () => {
|
|
|
|
|
try {
|
|
|
|
|
const r = await fetch('/api/v1/auth/me', { credentials: 'include' });
|
|
|
|
|
if (r.status === 401) {
|
|
|
|
|
location.replace('login.html');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (r.ok) {
|
|
|
|
|
const u = await r.json();
|
|
|
|
|
const name = u.display_name || u.username || 'User';
|
|
|
|
|
const userNameEl = document.getElementById('userName');
|
|
|
|
|
const userAvatarEl = document.getElementById('userAvatar');
|
|
|
|
|
const userRoleEl = document.getElementById('userRole');
|
|
|
|
|
if (userNameEl) userNameEl.textContent = name;
|
|
|
|
|
if (userAvatarEl) userAvatarEl.textContent = name[0].toUpperCase();
|
|
|
|
|
if (userRoleEl) userRoleEl.textContent = u.role || '';
|
|
|
|
|
}
|
|
|
|
|
} catch (_) {
|
|
|
|
|
// Network error — don't redirect; the user may be on a dev build without auth.
|
|
|
|
|
}
|
|
|
|
|
const logoutBtn = document.getElementById('logoutBtn');
|
|
|
|
|
if (logoutBtn) {
|
|
|
|
|
logoutBtn.onclick = async () => {
|
|
|
|
|
try { await fetch('/api/v1/auth/logout', { method: 'POST', credentials: 'include' }); } catch (_) {}
|
|
|
|
|
location.href = 'login.html';
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
})();
|