feat(Nav): Refactor navigation structure to support nested items and improve visibility logic

feat(Radio):
-  Redesigned Queue modal, added drag & drop capabilities
- Added stream quality selector, currently offering: AAC @ 128kbps, AAC @ 320kbps & FLAC (lossless)

fix(middleware): Import API_URL from config and remove hardcoded API_URL definition

security(api): Enhance discord image and video caching with improved signature verification and error handling, updated image proxy to include production checks for signing secret
This commit is contained in:
2026-02-22 13:53:43 -05:00
parent b5bf5fd5a7
commit ef15b646cc
12 changed files with 1188 additions and 192 deletions

View File

@@ -2,60 +2,121 @@
import { metaData, API_URL } from "../config";
import { Icon } from "astro-icon/components";
import { requireAuthHook } from "@/hooks/requireAuthHook";
import { padlockIconSvg, userIconSvg, externalLinkIconSvg } from "@/utils/navAssets";
import { userIconSvg, externalLinkIconSvg } from "@/utils/navAssets";
import "@/assets/styles/nav.css";
const user = await requireAuthHook(Astro);
const hostHeader = Astro.request?.headers?.get('host') || '';
const host = hostHeader.split(':')[0];
import { getSubsiteByHost } from '../utils/subsites.js';
import { getSubsiteByPath } from '../utils/subsites.js';
const isReq = getSubsiteByHost(host)?.short === 'req' || getSubsiteByPath(Astro.url.pathname)?.short === 'req';
// Nav is the standard site navigation — whitelabel logic belongs in SubNav
const isLoggedIn = Boolean(user);
const userDisplayName = user?.user ?? null;
const isAdmin = user?.roles?.includes('admin') ?? false;
const navItems = [
type NavItem = {
label: string;
href: string;
icon?: string;
auth?: boolean;
adminOnly?: boolean;
guestOnly?: boolean;
onclick?: string;
children?: NavItem[];
};
const baseNavItems: NavItem[] = [
{ label: "Home", href: "/" },
{ label: "Radio", href: "/radio" },
{ label: "Memes", href: "/memes" },
{ label: "TRip", href: "/TRip", auth: true, icon: "pirate" },
{ label: "Discord Logs", href: "/discord-logs", auth: true },
{ label: "Lighting", href: "/lighting", auth: true, adminOnly: true },
{ label: "Memes", href: "/memes" },
{ label: "Git", href: "https://kode.boatson.boats", icon: "external" },
{ label: "Glances", href: "https://_gl.codey.horse", auth: true, icon: "external",
adminOnly: true },
{ label: "PSQL", href: "https://_pg.codey.horse", auth: true, icon: "external",
adminOnly: true },
{ label: "qBitTorrent", href: "https://_qb.codey.horse", auth: true, icon: "external",
adminOnly: true },
{ label: "RQ", href: "https://_rq.codey.horse", auth: true, icon: "external",
adminOnly: true },
{ label: "RI", href: "https://_r0.codey.horse", auth: true, icon: "external",
adminOnly: true },
// { label: "Status", href: "https://status.boatson.boats", icon: "external" },
{
label: "TRip",
href: "/TRip",
auth: true,
icon: "pirate",
children: [
{ label: "Submit Request", href: "/TRip", auth: true },
{ label: "Manage Requests", href: "/TRip/requests", auth: true },
],
},
{
label: "Admin",
href: "javascript:void(0)",
auth: true,
adminOnly: true,
children: [
{ label: "Lighting", href: "/lighting", auth: true, adminOnly: true },
{ label: "Discord Logs", href: "/discord-logs", auth: true, adminOnly: true },
{ label: "Glances", href: "https://_gl.codey.horse", auth: true, icon: "external", adminOnly: true },
{ label: "PSQL", href: "https://_pg.codey.horse", auth: true, icon: "external", adminOnly: true },
{ label: "SQLite", href: "https://_sqlite.codey.horse", auth: true, icon: "external", adminOnly: true },
{ label: "qBitTorrent", href: "https://_qb.codey.horse", auth: true, icon: "external", adminOnly: true },
{ label: "RQ", href: "https://_rq.codey.horse", auth: true, icon: "external", adminOnly: true },
{ label: "RI", href: "https://_r0.codey.horse", auth: true, icon: "external", adminOnly: true },
],
},
{ label: "Login", href: "/login", guestOnly: true },
...(isLoggedIn ? [{ label: "Logout", href: "#logout", onclick: "handleLogout()" }] : []),
];
const visibleNavItems = navItems.filter((item) => {
if (item.auth && !isLoggedIn) {
return false;
}
// Fold any adminOnly root items into the Admin group automatically
const adminContainerIndex = baseNavItems.findIndex((item) => item.label === "Admin");
const adminContainer: NavItem = adminContainerIndex >= 0
? baseNavItems[adminContainerIndex]
: { label: "Admin", href: "#admin", auth: true, adminOnly: true, children: [] };
if (item.adminOnly && !isAdmin) {
return false;
}
const adminChildren: NavItem[] = [...(adminContainer.children ?? [])];
const groupedNavItems: NavItem[] = [];
if (item.guestOnly && isLoggedIn) {
return false;
}
baseNavItems.forEach((item, idx) => {
if (item.label === "Admin") return; // defer insertion
return true;
if (item.adminOnly) {
adminChildren.push(item);
} else {
groupedNavItems.push(item);
}
});
const currentPath = Astro.url.pathname;
if (adminChildren.length > 0) {
const adminItem: NavItem = { ...adminContainer, children: adminChildren };
// Insert Admin before Login/Logout (which are always last)
const loginIdx = groupedNavItems.findIndex((item) => item.label === "Login" || item.label === "Logout");
const insertAt = loginIdx >= 0 ? loginIdx : groupedNavItems.length;
groupedNavItems.splice(insertAt, 0, adminItem);
}
const isItemVisible = (item: NavItem): boolean => {
if (item.auth && !isLoggedIn) return false;
if (item.adminOnly && !isAdmin) return false;
if (item.guestOnly && isLoggedIn) return false;
return true;
};
const visibleNavItems = groupedNavItems
.filter(isItemVisible)
.map((item): NavItem => {
const visibleChildren = item.children?.filter(isItemVisible);
return visibleChildren ? { ...item, children: visibleChildren } : item;
})
.filter((item) => !item.children || item.children.length > 0);
const normalize = (url: string) => (url || '/').replace(/\/+$/, '') || '/';
const normalizedCurrent = normalize(Astro.url.pathname);
// For parent items: active if exact match OR if path starts with href (prefix matching)
const isPathActive = (href: string) => {
const normalizedHref = normalize(href);
if (!href || href.startsWith('http')) return false;
return normalizedHref === '/'
? normalizedCurrent === '/'
: normalizedCurrent === normalizedHref || normalizedCurrent.startsWith(`${normalizedHref}/`);
};
// For dropdown children: active only on exact match (no prefix matching)
const isPathActiveExact = (href: string) => {
const normalizedHref = normalize(href);
if (!href || href.startsWith('http')) return false;
return normalizedCurrent === normalizedHref;
};
---
@@ -75,40 +136,69 @@ const currentPath = Astro.url.pathname;
<div class="desktop-nav flex items-center">
<ul class="desktop-nav-list">
{visibleNavItems.map((item) => {
const hasChildren = Array.isArray(item.children) && item.children.length > 0;
const isExternal = item.href?.startsWith("http");
const isAuthedPath = item.auth ?? false;
const normalize = (url) => (url || '/').replace(/\/+$/, '') || '/';
const normalizedCurrent = normalize(currentPath);
const normalizedHref = normalize(item.href);
const isActive = !isExternal && (
normalizedHref === '/'
? normalizedCurrent === '/'
: normalizedCurrent === normalizedHref || normalizedCurrent.startsWith(normalizedHref + '/')
);
const childActive = hasChildren && item.children?.some((child) => isPathActive(child.href));
const isActive = childActive || (!isExternal && isPathActive(item.href));
return (
<li>
<li class:list={["nav-item", hasChildren && "nav-item--has-children"]}>
<a
href={item.href}
class={isActive
? "flex items-center gap-0.5 px-3 py-1.5 rounded-lg text-[13px] font-semibold transition-all duration-200 text-white bg-neutral-900 dark:bg-white dark:text-neutral-900 shadow-sm font-['IBM_Plex_Sans',sans-serif]"
: "flex items-center gap-0.5 px-3 py-1.5 rounded-lg text-[13px] font-medium transition-all duration-200 text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800/60 font-['IBM_Plex_Sans',sans-serif]"
? "flex items-center gap-1 px-3 py-1.5 rounded-lg text-[13px] font-semibold transition-all duration-200 text-white bg-neutral-900 dark:bg-white dark:text-neutral-900 shadow-sm font-['IBM_Plex_Sans',sans-serif]"
: "flex items-center gap-1 px-3 py-1.5 rounded-lg text-[13px] font-medium transition-all duration-200 text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800/60 font-['IBM_Plex_Sans',sans-serif]"
}
target={isExternal ? "_blank" : undefined}
rel={(isExternal || isAuthedPath) ? "external" : undefined}
onclick={item.onclick}
aria-haspopup={hasChildren ? "true" : undefined}
aria-expanded={hasChildren ? isActive : undefined}
>
{item.label}
{hasChildren && <span class="nav-caret" aria-hidden="true">▼</span>}
{item.icon === "external" && (
<span class="inline-flex ml-0.5" aria-hidden="true" set:html={externalLinkIconSvg}></span>
)}
{item.icon === "padlock" && (
<span class="inline-flex" aria-hidden="true" set:html={padlockIconSvg}></span>
)}
{item.icon === "pirate" && (
<span class="inline-flex ml-1" role="img" aria-label="Pirate flag">🏴‍☠️</span>
)}
</a>
{hasChildren && (
<div class="nav-dropdown" role="menu">
<ul>
{item.children?.map((child) => {
const childExternal = child.href?.startsWith("http");
const childAuthedPath = child.auth ?? false;
const childIsActive = !childExternal && isPathActiveExact(child.href);
return (
<li>
<a
href={child.href}
class={childIsActive
? "flex items-center gap-1 px-3 py-2 rounded-md text-sm font-semibold transition-all duration-150 text-white bg-neutral-900 dark:text-neutral-900 dark:bg-white"
: "flex items-center gap-1 px-3 py-2 rounded-md text-sm font-medium transition-all duration-150 text-neutral-700 dark:text-neutral-200 hover:bg-neutral-100 dark:hover:bg-neutral-800/80"}
target={childExternal ? "_blank" : undefined}
rel={(childExternal || childAuthedPath) ? "external" : undefined}
onclick={childIsActive ? "event.preventDefault()" : undefined}
>
{child.label}
{child.icon === "external" && (
<span class="inline-flex ml-0.5" aria-hidden="true" set:html={externalLinkIconSvg}></span>
)}
{child.icon === "pirate" && (
<span class="inline-flex ml-1" role="img" aria-label="Pirate flag">🏴‍☠️</span>
)}
</a>
</li>
);
})}
</ul>
</div>
)}
</li>
);
})}
@@ -170,6 +260,7 @@ const currentPath = Astro.url.pathname;
<div
id="mobile-menu"
class="mobile-menu-dropdown xl:hidden"
data-lenis-prevent
>
{isLoggedIn && userDisplayName && (
<div class:list={['nav-user-inline', 'nav-user-inline--mobile', isAdmin && 'nav-user-inline--admin']} title={`Logged in as ${userDisplayName}`}>
@@ -179,16 +270,11 @@ const currentPath = Astro.url.pathname;
)}
<ul class="flex flex-col gap-1 py-4">
{visibleNavItems.map((item) => {
const hasChildren = Array.isArray(item.children) && item.children.length > 0;
const isExternal = item.href?.startsWith("http");
const isAuthedPath = item.auth ?? false;
const normalize = (url) => (url || '/').replace(/\/+$/, '') || '/';
const normalizedCurrent = normalize(currentPath);
const normalizedHref = normalize(item.href);
const isActive = !isExternal && (
normalizedHref === '/'
? normalizedCurrent === '/'
: normalizedCurrent === normalizedHref || normalizedCurrent.startsWith(normalizedHref + '/')
);
const childActive = hasChildren && item.children?.some((child) => isPathActive(child.href));
const isActive = childActive || (!isExternal && isPathActive(item.href));
return (
<li>
@@ -204,16 +290,46 @@ const currentPath = Astro.url.pathname;
onclick={item.onclick}
>
<span style="color:inherit;">{item.label}</span>
{hasChildren && <span class="mobile-caret" aria-hidden="true"></span>}
{item.icon === "external" && (
<span class="inline-flex ml-0.5" aria-hidden="true" set:html={externalLinkIconSvg}></span>
)}
{item.icon === "padlock" && (
<span class="inline-flex" aria-hidden="true" set:html={padlockIconSvg}></span>
)}
{item.icon === "pirate" && (
<span class="inline-flex ml-1" role="img" aria-label="Pirate flag">🏴‍☠️</span>
)}
</a>
{hasChildren && (
<ul class="mobile-subnav" role="menu">
{item.children?.map((child) => {
const childExternal = child.href?.startsWith("http");
const childAuthedPath = child.auth ?? false;
const childIsActive = !childExternal && isPathActiveExact(child.href);
return (
<li>
<a
href={child.href}
class={childIsActive
? "flex items-center gap-1 px-6 py-2 rounded-lg text-base font-semibold transition-all duration-200 text-white bg-neutral-900 dark:bg-white dark:text-neutral-900"
: "flex items-center gap-1 px-6 py-2 rounded-lg text-base font-medium transition-all duration-200 text-neutral-700 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800"}
target={childExternal ? "_blank" : undefined}
rel={(childExternal || childAuthedPath) ? "external" : undefined}
onclick={childIsActive ? "event.preventDefault()" : undefined}
>
<span style="color:inherit;">{child.label}</span>
{child.icon === "external" && (
<span class="inline-flex ml-0.5" aria-hidden="true" set:html={externalLinkIconSvg}></span>
)}
{child.icon === "pirate" && (
<span class="inline-flex ml-1" role="img" aria-label="Pirate flag">🏴‍☠️</span>
)}
</a>
</li>
);
})}
</ul>
)}
</li>
);
})}