fix(admin): P0 integrity — permanent bans, ACL sidebar, rank guards
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
11004626c7
commit
75cdfdebe4
14 files changed
+458
-68
No files matched your search
+231
-28
@@ -39,6 +39,7 @@ import {
|
||||
Wifi,
|
||||
Wrench,
|
||||
} from "lucide-react";
|
||||
import { PERMS } from "@/lib/permission-slugs";
|
||||
|
||||
export interface AdminHubTab {
|
||||
href: string;
|
||||
@@ -64,6 +65,11 @@ export interface AdminNavItem {
|
||||
href: string;
|
||||
labelKey: string;
|
||||
icon: LucideIcon;
|
||||
/**
|
||||
* ACL slug(s) required to show this item. Any match is enough.
|
||||
* Omit only for the dashboard (already gated by admin.dashboard).
|
||||
*/
|
||||
permission?: string | readonly string[];
|
||||
/** 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). */
|
||||
@@ -232,6 +238,7 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin",
|
||||
labelKey: "dashboard",
|
||||
icon: LayoutDashboard,
|
||||
permission: PERMS.ADMIN_DASHBOARD,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -243,31 +250,65 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin/articles",
|
||||
labelKey: "articles",
|
||||
icon: Newspaper,
|
||||
permission: PERMS.NEWS_VIEW,
|
||||
matchPrefixes: ["/admin/articles"],
|
||||
},
|
||||
{ href: "/admin/photos", labelKey: "photos", icon: Image },
|
||||
{ href: "/admin/banners", labelKey: "banners", icon: Megaphone },
|
||||
{
|
||||
href: "/admin/photos",
|
||||
labelKey: "photos",
|
||||
icon: Image,
|
||||
permission: PERMS.PAGES_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/banners",
|
||||
labelKey: "banners",
|
||||
icon: Megaphone,
|
||||
permission: PERMS.BANNERS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/ads",
|
||||
labelKey: "advertisements",
|
||||
icon: FileText,
|
||||
permission: PERMS.PAGES_VIEW,
|
||||
matchPrefixes: ["/admin/ads"],
|
||||
},
|
||||
{ href: "/admin/media", labelKey: "media", icon: Image },
|
||||
{ href: "/admin/navigation", labelKey: "navigator", icon: Compass },
|
||||
{
|
||||
href: "/admin/media",
|
||||
labelKey: "media",
|
||||
icon: Image,
|
||||
permission: PERMS.PAGES_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/navigation",
|
||||
labelKey: "navigator",
|
||||
icon: Compass,
|
||||
permission: PERMS.PAGES_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/help-questions",
|
||||
labelKey: "helpCenter",
|
||||
icon: HelpCircle,
|
||||
permission: PERMS.PAGES_VIEW,
|
||||
matchPrefixes: ["/admin/help-questions"],
|
||||
},
|
||||
{
|
||||
href: "/admin/writeable-boxes",
|
||||
labelKey: "writeableBoxes",
|
||||
icon: Package,
|
||||
permission: PERMS.PAGES_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/tags",
|
||||
labelKey: "tags",
|
||||
icon: Tags,
|
||||
permission: PERMS.PAGES_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/prefixes",
|
||||
labelKey: "prefixes",
|
||||
icon: Sparkles,
|
||||
permission: PERMS.PREFIXES_VIEW,
|
||||
},
|
||||
{ href: "/admin/tags", labelKey: "tags", icon: Tags },
|
||||
{ href: "/admin/prefixes", labelKey: "prefixes", icon: Sparkles },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -278,18 +319,21 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin/events",
|
||||
labelKey: "events",
|
||||
icon: Calendar,
|
||||
permission: PERMS.EVENTS_VIEW,
|
||||
matchPrefixes: ["/admin/events"],
|
||||
},
|
||||
{
|
||||
href: "/admin/polls",
|
||||
labelKey: "polls",
|
||||
icon: Vote,
|
||||
permission: PERMS.POLLS_VIEW,
|
||||
matchPrefixes: ["/admin/polls"],
|
||||
},
|
||||
{
|
||||
href: "/admin/tickets",
|
||||
labelKey: "tickets",
|
||||
icon: Ticket,
|
||||
permission: PERMS.TICKETS_VIEW,
|
||||
matchPrefixes: ["/admin/tickets", "/admin/help-tickets"],
|
||||
},
|
||||
],
|
||||
@@ -302,6 +346,7 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin/users",
|
||||
labelKey: "users",
|
||||
icon: Users,
|
||||
permission: PERMS.USERS_VIEW,
|
||||
matchPrefixes: ["/admin/users"],
|
||||
matchExcludePrefixes: ["/admin/users/multi-accounts"],
|
||||
},
|
||||
@@ -309,31 +354,64 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin/users/multi-accounts",
|
||||
labelKey: "multiAccounts",
|
||||
icon: Users,
|
||||
permission: PERMS.USERS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/online",
|
||||
labelKey: "onlineUsers",
|
||||
icon: Wifi,
|
||||
permission: PERMS.USERS_VIEW,
|
||||
},
|
||||
{ href: "/admin/online", labelKey: "onlineUsers", icon: Wifi },
|
||||
{
|
||||
href: "/admin/applications",
|
||||
labelKey: "applications",
|
||||
icon: ClipboardList,
|
||||
permission: PERMS.USERS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/teams",
|
||||
labelKey: "staffAccess",
|
||||
icon: KeyRound,
|
||||
permission: [
|
||||
PERMS.USERS_VIEW,
|
||||
PERMS.PERMISSIONS_MANAGE,
|
||||
PERMS.SETTINGS_VIEW,
|
||||
],
|
||||
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/bans",
|
||||
labelKey: "bans",
|
||||
icon: Ban,
|
||||
permission: PERMS.BANS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/ip",
|
||||
labelKey: "ipManagement",
|
||||
icon: Shield,
|
||||
permission: PERMS.SETTINGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/vpn",
|
||||
labelKey: "vpn",
|
||||
icon: Shield,
|
||||
permission: PERMS.SETTINGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/wordfilter",
|
||||
labelKey: "wordFilter",
|
||||
icon: Filter,
|
||||
permission: PERMS.WORDFILTER_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/moderation",
|
||||
labelKey: "moderation",
|
||||
icon: Gavel,
|
||||
permission: PERMS.MODERATION_VIEW,
|
||||
matchPrefixes: ["/admin/moderation"],
|
||||
},
|
||||
],
|
||||
@@ -346,24 +424,62 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin/catalog",
|
||||
labelKey: "catalog",
|
||||
icon: Store,
|
||||
permission: PERMS.CATALOG_VIEW,
|
||||
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/rare-values",
|
||||
labelKey: "rareValues",
|
||||
icon: Sparkles,
|
||||
permission: PERMS.SHOP_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/badges",
|
||||
labelKey: "badges",
|
||||
icon: BadgeCheck,
|
||||
permission: PERMS.CATALOG_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/achievements",
|
||||
labelKey: "achievements",
|
||||
icon: Trophy,
|
||||
permission: PERMS.CATALOG_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/sounds",
|
||||
labelKey: "sounds",
|
||||
icon: Volume2,
|
||||
permission: PERMS.CATALOG_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/shop",
|
||||
labelKey: "shop",
|
||||
icon: ShoppingCart,
|
||||
permission: PERMS.SHOP_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/transactions",
|
||||
labelKey: "transactions",
|
||||
icon: Activity,
|
||||
permission: PERMS.SHOP_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/vouchers",
|
||||
labelKey: "vouchers",
|
||||
icon: Ticket,
|
||||
permission: PERMS.SHOP_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/subscriptions",
|
||||
labelKey: "subscriptions",
|
||||
icon: ClipboardList,
|
||||
permission: PERMS.SHOP_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/calendar",
|
||||
labelKey: "calendar",
|
||||
icon: CalendarDays,
|
||||
permission: PERMS.SHOP_VIEW,
|
||||
matchPrefixes: ["/admin/calendar"],
|
||||
},
|
||||
],
|
||||
@@ -376,6 +492,7 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin/radio",
|
||||
labelKey: "radio",
|
||||
icon: Radio,
|
||||
permission: PERMS.RADIO_VIEW,
|
||||
matchPrefixes: ["/admin/radio"],
|
||||
},
|
||||
],
|
||||
@@ -384,15 +501,41 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
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/settings",
|
||||
labelKey: "settings",
|
||||
icon: Cog,
|
||||
permission: PERMS.SETTINGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/theme",
|
||||
labelKey: "theme",
|
||||
icon: Sparkles,
|
||||
permission: PERMS.SETTINGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/maintenance",
|
||||
labelKey: "maintenance",
|
||||
icon: Wrench,
|
||||
permission: PERMS.SETTINGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/favicon",
|
||||
labelKey: "favicon",
|
||||
icon: Image,
|
||||
permission: PERMS.SETTINGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/emulator",
|
||||
labelKey: "emulator",
|
||||
icon: Server,
|
||||
permission: PERMS.SETTINGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/email-templates",
|
||||
labelKey: "emailTemplates",
|
||||
icon: FileText,
|
||||
permission: PERMS.PAGES_VIEW,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -404,23 +547,27 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin/commandocentrum",
|
||||
labelKey: "commandocentrum",
|
||||
icon: Terminal,
|
||||
permission: PERMS.RCON_EXECUTE,
|
||||
},
|
||||
{
|
||||
href: "/admin/rooms",
|
||||
labelKey: "rooms",
|
||||
icon: Store,
|
||||
permission: PERMS.ROOMS_VIEW,
|
||||
matchPrefixes: ["/admin/rooms"],
|
||||
},
|
||||
{
|
||||
href: "/admin/import",
|
||||
labelKey: "import",
|
||||
icon: Import,
|
||||
permission: PERMS.ASSETS_IMPORT,
|
||||
matchPrefixes: ["/admin/import"],
|
||||
},
|
||||
{
|
||||
href: "/admin/translations",
|
||||
labelKey: "translations",
|
||||
icon: Languages,
|
||||
permission: PERMS.SETTINGS_VIEW,
|
||||
matchPrefixes: ["/admin/translations"],
|
||||
},
|
||||
],
|
||||
@@ -433,6 +580,7 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
href: "/admin/logs",
|
||||
labelKey: "logs",
|
||||
icon: FileText,
|
||||
permission: PERMS.LOGS_VIEW,
|
||||
matchPrefixes: ["/admin/logs"],
|
||||
matchExcludePrefixes: [
|
||||
"/admin/logs/audit",
|
||||
@@ -441,23 +589,78 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [
|
||||
"/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/logs/audit",
|
||||
labelKey: "auditLog",
|
||||
icon: ClipboardList,
|
||||
permission: PERMS.LOGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/logs/chat",
|
||||
labelKey: "chatLog",
|
||||
icon: FileText,
|
||||
permission: PERMS.LOGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/logs/commands",
|
||||
labelKey: "commandLog",
|
||||
icon: Terminal,
|
||||
permission: PERMS.LOGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/logs/trades",
|
||||
labelKey: "tradeLog",
|
||||
icon: Activity,
|
||||
permission: PERMS.LOGS_VIEW,
|
||||
},
|
||||
{
|
||||
href: "/admin/analytics",
|
||||
labelKey: "analytics",
|
||||
icon: Activity,
|
||||
permission: PERMS.ANALYTICS_VIEW,
|
||||
matchPrefixes: ["/admin/analytics"],
|
||||
},
|
||||
{
|
||||
href: "/admin/devops",
|
||||
labelKey: "devops",
|
||||
icon: Server,
|
||||
permission: PERMS.DEVOPS_VIEW,
|
||||
matchPrefixes: ["/admin/devops"],
|
||||
},
|
||||
{ href: "/admin/alerts", labelKey: "alerts", icon: AlertTriangle },
|
||||
{
|
||||
href: "/admin/alerts",
|
||||
labelKey: "alerts",
|
||||
icon: AlertTriangle,
|
||||
permission: PERMS.NOTIFICATIONS_VIEW,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/** Whether a nav item should be visible for the given permission set. */
|
||||
export function navItemIsAllowed(
|
||||
item: AdminNavItem,
|
||||
opts: { isSuperAdmin: boolean; has: (slug: string) => boolean },
|
||||
): boolean {
|
||||
if (opts.isSuperAdmin) return true;
|
||||
if (!item.permission) return true;
|
||||
const needed = Array.isArray(item.permission)
|
||||
? item.permission
|
||||
: [item.permission];
|
||||
return needed.some((slug) => opts.has(slug));
|
||||
}
|
||||
|
||||
/** Collect every ACL slug referenced by the sidebar (for layout gating). */
|
||||
export function collectNavPermissionSlugs(): string[] {
|
||||
const slugs = new Set<string>();
|
||||
for (const group of ADMIN_NAV_GROUPS) {
|
||||
for (const item of group.items) {
|
||||
if (!item.permission) continue;
|
||||
const needed = Array.isArray(item.permission)
|
||||
? item.permission
|
||||
: [item.permission];
|
||||
for (const slug of needed) slugs.add(slug);
|
||||
}
|
||||
}
|
||||
return [...slugs];
|
||||
}
|
||||
Reference in new issue
Block a user