Cut Auth.js DB load with cached jwtVersion checks, surface Redis in /api/health and deploy warnings, add admin help-center ticket reply UI, rate-limit API tickets/reactions/referral claims, and revoke PATs on sign-out-everywhere. Co-authored-by: Cursor <[email protected]>
464 lines
11 KiB
TypeScript
464 lines
11 KiB
TypeScript
import type { LucideIcon } from "lucide-react";
|
|
import {
|
|
Activity,
|
|
AlertTriangle,
|
|
BadgeCheck,
|
|
Ban,
|
|
Calendar,
|
|
CalendarDays,
|
|
ClipboardList,
|
|
Cog,
|
|
Compass,
|
|
FileText,
|
|
Filter,
|
|
Gavel,
|
|
HelpCircle,
|
|
Image,
|
|
Import,
|
|
KeyRound,
|
|
Languages,
|
|
LayoutDashboard,
|
|
Megaphone,
|
|
Monitor,
|
|
Newspaper,
|
|
Package,
|
|
Radio,
|
|
Server,
|
|
Settings,
|
|
Shield,
|
|
ShoppingCart,
|
|
Sparkles,
|
|
Store,
|
|
Tags,
|
|
Terminal,
|
|
Ticket,
|
|
Trophy,
|
|
Users,
|
|
Volume2,
|
|
Vote,
|
|
Wifi,
|
|
Wrench,
|
|
} from "lucide-react";
|
|
|
|
export interface AdminHubTab {
|
|
href: string;
|
|
/** Key under pages.admin.hubs.tabs.* */
|
|
labelKey: string;
|
|
match?: string[];
|
|
/** Optional group id for multi-row tab strips (e.g. Radio primary/tools). */
|
|
group?: string;
|
|
}
|
|
|
|
export interface AdminHubDefinition {
|
|
id: string;
|
|
/** Key under pages.admin.hubs.* for title/subtitle */
|
|
titleKey: string;
|
|
subtitleKey: string;
|
|
icon: LucideIcon;
|
|
/** Path prefixes belonging to this hub (for chrome + sidebar active). */
|
|
prefixes: string[];
|
|
tabs: AdminHubTab[];
|
|
}
|
|
|
|
export interface AdminNavItem {
|
|
href: string;
|
|
labelKey: string;
|
|
icon: LucideIcon;
|
|
/** When set, sidebar item is active if pathname matches any prefix. */
|
|
matchPrefixes?: string[];
|
|
/** Prefixes that must NOT count as active (e.g. /admin/users vs multi-accounts). */
|
|
matchExcludePrefixes?: string[];
|
|
}
|
|
|
|
export interface AdminNavGroup {
|
|
labelKey: string;
|
|
icon: LucideIcon;
|
|
items: AdminNavItem[];
|
|
}
|
|
|
|
/**
|
|
* Hubs with real sibling tabs only. Standalone features have no hub chrome
|
|
* (sidebar links go straight to the page).
|
|
*/
|
|
export const ADMIN_HUBS: AdminHubDefinition[] = [
|
|
{
|
|
id: "events",
|
|
titleKey: "events",
|
|
subtitleKey: "eventsSubtitle",
|
|
icon: Calendar,
|
|
prefixes: ["/admin/events"],
|
|
tabs: [
|
|
{ href: "/admin/events", labelKey: "events", match: ["/admin/events"] },
|
|
{ href: "/admin/events/types", labelKey: "eventTypes" },
|
|
],
|
|
},
|
|
{
|
|
id: "tickets",
|
|
titleKey: "tickets",
|
|
subtitleKey: "ticketsSubtitle",
|
|
icon: Ticket,
|
|
prefixes: ["/admin/tickets", "/admin/help-tickets"],
|
|
tabs: [
|
|
{
|
|
href: "/admin/tickets",
|
|
labelKey: "tickets",
|
|
match: ["/admin/tickets"],
|
|
},
|
|
{
|
|
href: "/admin/help-tickets",
|
|
labelKey: "helpTickets",
|
|
match: ["/admin/help-tickets"],
|
|
},
|
|
{ href: "/admin/tickets/templates", labelKey: "templates" },
|
|
],
|
|
},
|
|
{
|
|
id: "staff-access",
|
|
titleKey: "staffAccess",
|
|
subtitleKey: "staffAccessSubtitle",
|
|
icon: KeyRound,
|
|
prefixes: ["/admin/teams", "/admin/permissions", "/admin/housekeeping"],
|
|
tabs: [
|
|
{ href: "/admin/teams", labelKey: "teams" },
|
|
{
|
|
href: "/admin/permissions",
|
|
labelKey: "permissions",
|
|
match: ["/admin/permissions"],
|
|
},
|
|
{ href: "/admin/housekeeping", labelKey: "housekeeping" },
|
|
],
|
|
},
|
|
{
|
|
id: "moderation",
|
|
titleKey: "moderation",
|
|
subtitleKey: "moderationSubtitle",
|
|
icon: Gavel,
|
|
prefixes: ["/admin/moderation"],
|
|
tabs: [
|
|
{ href: "/admin/moderation", labelKey: "overview" },
|
|
{ href: "/admin/moderation/actions", labelKey: "actions" },
|
|
{
|
|
href: "/admin/moderation/cfh",
|
|
labelKey: "cfh",
|
|
match: ["/admin/moderation/cfh"],
|
|
},
|
|
{ href: "/admin/moderation/team", labelKey: "team" },
|
|
],
|
|
},
|
|
{
|
|
id: "radio",
|
|
titleKey: "radio",
|
|
subtitleKey: "radioSubtitle",
|
|
icon: Radio,
|
|
prefixes: ["/admin/radio"],
|
|
tabs: [
|
|
{ href: "/admin/radio", labelKey: "overview", group: "primary" },
|
|
{ href: "/admin/radio/history", labelKey: "history", group: "primary" },
|
|
{
|
|
href: "/admin/radio/moderation",
|
|
labelKey: "moderationTab",
|
|
group: "primary",
|
|
},
|
|
{
|
|
href: "/admin/radio/monitoring",
|
|
labelKey: "monitoring",
|
|
group: "primary",
|
|
},
|
|
{
|
|
href: "/admin/radio/settings",
|
|
labelKey: "settingsTab",
|
|
group: "primary",
|
|
},
|
|
{ href: "/admin/radio/banners", labelKey: "banners", group: "tools" },
|
|
{ href: "/admin/radio/embed", labelKey: "embed", group: "tools" },
|
|
{ href: "/admin/radio/api-keys", labelKey: "apiKeys", group: "tools" },
|
|
{ href: "/admin/radio/points", labelKey: "points", group: "tools" },
|
|
{ href: "/admin/radio/ranks", labelKey: "ranks", group: "tools" },
|
|
{ href: "/admin/radio/autodj", labelKey: "autoDj", group: "tools" },
|
|
],
|
|
},
|
|
{
|
|
id: "analytics",
|
|
titleKey: "analytics",
|
|
subtitleKey: "analyticsSubtitle",
|
|
icon: Activity,
|
|
prefixes: ["/admin/analytics"],
|
|
tabs: [
|
|
{
|
|
href: "/admin/analytics",
|
|
labelKey: "analytics",
|
|
match: ["/admin/analytics"],
|
|
},
|
|
{ href: "/admin/analytics/activity", labelKey: "activity" },
|
|
{ href: "/admin/analytics/economy", labelKey: "economy" },
|
|
],
|
|
},
|
|
{
|
|
id: "devops",
|
|
titleKey: "devops",
|
|
subtitleKey: "devopsSubtitle",
|
|
icon: Server,
|
|
prefixes: ["/admin/devops"],
|
|
tabs: [
|
|
{ href: "/admin/devops", labelKey: "devops", match: ["/admin/devops"] },
|
|
{ href: "/admin/devops/errors", labelKey: "errors" },
|
|
],
|
|
},
|
|
];
|
|
|
|
/** Longest-prefix match so nested routes (e.g. /admin/logs/audit) win. */
|
|
export function findAdminHub(pathname: string): AdminHubDefinition | null {
|
|
let best: AdminHubDefinition | null = null;
|
|
let bestLen = -1;
|
|
for (const hub of ADMIN_HUBS) {
|
|
for (const prefix of hub.prefixes) {
|
|
const hit = pathname === prefix || pathname.startsWith(`${prefix}/`);
|
|
if (hit && prefix.length > bestLen) {
|
|
best = hub;
|
|
bestLen = prefix.length;
|
|
}
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
/** Feature-first sidebar: one entry per feature; hubs only for sibling tabs. */
|
|
export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
|
{
|
|
labelKey: "overview",
|
|
icon: LayoutDashboard,
|
|
items: [
|
|
{
|
|
href: "/admin",
|
|
labelKey: "dashboard",
|
|
icon: LayoutDashboard,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
labelKey: "content",
|
|
icon: Newspaper,
|
|
items: [
|
|
{
|
|
href: "/admin/articles",
|
|
labelKey: "articles",
|
|
icon: Newspaper,
|
|
matchPrefixes: ["/admin/articles"],
|
|
},
|
|
{ href: "/admin/photos", labelKey: "photos", icon: Image },
|
|
{ href: "/admin/banners", labelKey: "banners", icon: Megaphone },
|
|
{
|
|
href: "/admin/ads",
|
|
labelKey: "advertisements",
|
|
icon: FileText,
|
|
matchPrefixes: ["/admin/ads"],
|
|
},
|
|
{ href: "/admin/media", labelKey: "media", icon: Image },
|
|
{ href: "/admin/navigation", labelKey: "navigator", icon: Compass },
|
|
{
|
|
href: "/admin/help-questions",
|
|
labelKey: "helpCenter",
|
|
icon: HelpCircle,
|
|
matchPrefixes: ["/admin/help-questions"],
|
|
},
|
|
{
|
|
href: "/admin/writeable-boxes",
|
|
labelKey: "writeableBoxes",
|
|
icon: Package,
|
|
},
|
|
{ href: "/admin/tags", labelKey: "tags", icon: Tags },
|
|
{ href: "/admin/prefixes", labelKey: "prefixes", icon: Sparkles },
|
|
],
|
|
},
|
|
{
|
|
labelKey: "community",
|
|
icon: Calendar,
|
|
items: [
|
|
{
|
|
href: "/admin/events",
|
|
labelKey: "events",
|
|
icon: Calendar,
|
|
matchPrefixes: ["/admin/events"],
|
|
},
|
|
{
|
|
href: "/admin/polls",
|
|
labelKey: "polls",
|
|
icon: Vote,
|
|
matchPrefixes: ["/admin/polls"],
|
|
},
|
|
{
|
|
href: "/admin/tickets",
|
|
labelKey: "tickets",
|
|
icon: Ticket,
|
|
matchPrefixes: ["/admin/tickets", "/admin/help-tickets"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
labelKey: "usersAndAccess",
|
|
icon: Users,
|
|
items: [
|
|
{
|
|
href: "/admin/users",
|
|
labelKey: "users",
|
|
icon: Users,
|
|
matchPrefixes: ["/admin/users"],
|
|
matchExcludePrefixes: ["/admin/users/multi-accounts"],
|
|
},
|
|
{
|
|
href: "/admin/users/multi-accounts",
|
|
labelKey: "multiAccounts",
|
|
icon: Users,
|
|
},
|
|
{ href: "/admin/online", labelKey: "onlineUsers", icon: Wifi },
|
|
{
|
|
href: "/admin/applications",
|
|
labelKey: "applications",
|
|
icon: ClipboardList,
|
|
},
|
|
{
|
|
href: "/admin/teams",
|
|
labelKey: "staffAccess",
|
|
icon: KeyRound,
|
|
matchPrefixes: [
|
|
"/admin/teams",
|
|
"/admin/permissions",
|
|
"/admin/housekeeping",
|
|
],
|
|
},
|
|
{ href: "/admin/bans", labelKey: "bans", icon: Ban },
|
|
{ href: "/admin/ip", labelKey: "ipManagement", icon: Shield },
|
|
{ href: "/admin/vpn", labelKey: "vpn", icon: Shield },
|
|
{ href: "/admin/wordfilter", labelKey: "wordFilter", icon: Filter },
|
|
{
|
|
href: "/admin/moderation",
|
|
labelKey: "moderation",
|
|
icon: Gavel,
|
|
matchPrefixes: ["/admin/moderation"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
labelKey: "economy",
|
|
icon: ShoppingCart,
|
|
items: [
|
|
{
|
|
href: "/admin/catalog",
|
|
labelKey: "catalog",
|
|
icon: Store,
|
|
matchPrefixes: ["/admin/catalog"],
|
|
},
|
|
{ href: "/admin/rare-values", labelKey: "rareValues", icon: Sparkles },
|
|
{ href: "/admin/badges", labelKey: "badges", icon: BadgeCheck },
|
|
{ href: "/admin/achievements", labelKey: "achievements", icon: Trophy },
|
|
{ href: "/admin/sounds", labelKey: "sounds", icon: Volume2 },
|
|
{ href: "/admin/shop", labelKey: "shop", icon: ShoppingCart },
|
|
{ href: "/admin/transactions", labelKey: "transactions", icon: Activity },
|
|
{ href: "/admin/vouchers", labelKey: "vouchers", icon: Ticket },
|
|
{
|
|
href: "/admin/subscriptions",
|
|
labelKey: "subscriptions",
|
|
icon: ClipboardList,
|
|
},
|
|
{
|
|
href: "/admin/calendar",
|
|
labelKey: "calendar",
|
|
icon: CalendarDays,
|
|
matchPrefixes: ["/admin/calendar"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
labelKey: "radio",
|
|
icon: Radio,
|
|
items: [
|
|
{
|
|
href: "/admin/radio",
|
|
labelKey: "radio",
|
|
icon: Radio,
|
|
matchPrefixes: ["/admin/radio"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
labelKey: "system",
|
|
icon: Settings,
|
|
items: [
|
|
{ href: "/admin/settings", labelKey: "settings", icon: Cog },
|
|
{ href: "/admin/theme", labelKey: "theme", icon: Sparkles },
|
|
{ href: "/admin/maintenance", labelKey: "maintenance", icon: Wrench },
|
|
{ href: "/admin/favicon", labelKey: "favicon", icon: Image },
|
|
{ href: "/admin/emulator", labelKey: "emulator", icon: Server },
|
|
{
|
|
href: "/admin/email-templates",
|
|
labelKey: "emailTemplates",
|
|
icon: FileText,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
labelKey: "tools",
|
|
icon: Wrench,
|
|
items: [
|
|
{
|
|
href: "/admin/commandocentrum",
|
|
labelKey: "commandocentrum",
|
|
icon: Terminal,
|
|
},
|
|
{
|
|
href: "/admin/rooms",
|
|
labelKey: "rooms",
|
|
icon: Store,
|
|
matchPrefixes: ["/admin/rooms"],
|
|
},
|
|
{
|
|
href: "/admin/import",
|
|
labelKey: "import",
|
|
icon: Import,
|
|
matchPrefixes: ["/admin/import"],
|
|
},
|
|
{
|
|
href: "/admin/translations",
|
|
labelKey: "translations",
|
|
icon: Languages,
|
|
matchPrefixes: ["/admin/translations"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
labelKey: "monitoring",
|
|
icon: Monitor,
|
|
items: [
|
|
{
|
|
href: "/admin/logs",
|
|
labelKey: "logs",
|
|
icon: FileText,
|
|
matchPrefixes: ["/admin/logs"],
|
|
matchExcludePrefixes: [
|
|
"/admin/logs/audit",
|
|
"/admin/logs/chat",
|
|
"/admin/logs/commands",
|
|
"/admin/logs/trades",
|
|
],
|
|
},
|
|
{ href: "/admin/logs/audit", labelKey: "auditLog", icon: ClipboardList },
|
|
{ href: "/admin/logs/chat", labelKey: "chatLog", icon: FileText },
|
|
{ href: "/admin/logs/commands", labelKey: "commandLog", icon: Terminal },
|
|
{ href: "/admin/logs/trades", labelKey: "tradeLog", icon: Activity },
|
|
{
|
|
href: "/admin/analytics",
|
|
labelKey: "analytics",
|
|
icon: Activity,
|
|
matchPrefixes: ["/admin/analytics"],
|
|
},
|
|
{
|
|
href: "/admin/devops",
|
|
labelKey: "devops",
|
|
icon: Server,
|
|
matchPrefixes: ["/admin/devops"],
|
|
},
|
|
{ href: "/admin/alerts", labelKey: "alerts", icon: AlertTriangle },
|
|
],
|
|
},
|
|
];
|