Files
EventHubFrontAdmin/src/config/adminAccess.ts
T
aleksey 63323c1ca4
CI / test (push) Failing after 3m46s
CI / push-image (push) Has been skipped
CI / deploy-ift (push) Has been skipped
CI / ci-done (push) Failing after 0s
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped
Добавить UI настроек и inbox автомодерации.
Refs EventHub/EventHubFrontAdmin#40
Refs EventHub/EventHubFrontAdmin#41
2026-07-17 18:09:38 +03:00

155 lines
5.9 KiB
TypeScript

/** Роли доступа к разделам админки (согласовано с меню и маршрутами). */
export type AdminRole = 'superadmin' | 'admin' | 'moderator' | 'support';
export const ROUTE_ACCESS: Record<string, AdminRole[] | undefined> = {
'/dashboard': undefined,
'/profile': undefined,
'/monitoring': undefined,
'/inbox/reports': ['superadmin', 'admin', 'moderator'],
'/inbox/tickets': ['superadmin', 'admin', 'support'],
'/inbox/automod': ['superadmin', 'admin', 'moderator'],
'/users': ['superadmin', 'admin'],
'/calendars': ['superadmin', 'admin'],
'/events': ['superadmin', 'admin'],
'/reports': ['superadmin', 'admin', 'moderator'],
'/reviews': ['superadmin', 'admin', 'moderator'],
'/banned-words': ['superadmin', 'admin'],
'/automod-settings': ['superadmin', 'admin'],
'/tickets': ['superadmin', 'admin', 'support'],
'/subscriptions': ['superadmin', 'admin'],
'/admins': ['superadmin'],
'/audit': ['superadmin'],
};
export type NavItem = {
key: string;
label: string;
roles?: AdminRole[];
};
export type NavGroup = {
id: string;
label: string;
items: NavItem[];
};
/** Группы навигации Control Center (label = i18n key). */
export const NAV_GROUPS: NavGroup[] = [
{
id: 'work',
label: 'navGroup.work',
items: [
{ key: '/inbox/reports', label: 'nav.inboxReports', roles: ['superadmin', 'admin', 'moderator'] },
{ key: '/inbox/tickets', label: 'nav.inboxTickets', roles: ['superadmin', 'admin', 'support'] },
{ key: '/inbox/automod', label: 'nav.inboxAutomod', roles: ['superadmin', 'admin', 'moderator'] },
],
},
{
id: 'overview',
label: 'navGroup.overview',
items: [
{ key: '/dashboard', label: 'nav.dashboard' },
{ key: '/monitoring', label: 'nav.monitoring' },
],
},
{
id: 'content',
label: 'navGroup.content',
items: [
{ key: '/users', label: 'nav.users', roles: ['superadmin', 'admin'] },
{ key: '/calendars', label: 'nav.calendars', roles: ['superadmin', 'admin'] },
{ key: '/events', label: 'nav.events', roles: ['superadmin', 'admin'] },
{ key: '/subscriptions', label: 'nav.subscriptions', roles: ['superadmin', 'admin'] },
],
},
{
id: 'moderation',
label: 'navGroup.moderation',
items: [
{ key: '/reports', label: 'nav.reports', roles: ['superadmin', 'admin', 'moderator'] },
{ key: '/reviews', label: 'nav.reviews', roles: ['superadmin', 'admin', 'moderator'] },
{ key: '/banned-words', label: 'nav.bannedWords', roles: ['superadmin', 'admin'] },
{ key: '/automod-settings', label: 'nav.automodSettings', roles: ['superadmin', 'admin'] },
{ key: '/tickets', label: 'nav.tickets', roles: ['superadmin', 'admin', 'support'] },
],
},
{
id: 'system',
label: 'navGroup.system',
items: [
{ key: '/admins', label: 'nav.admins', roles: ['superadmin'] },
{ key: '/audit', label: 'nav.audit', roles: ['superadmin'] },
],
},
];
/** Плоский список для совместимости и getMenuSelectedKey. */
export const MENU_ITEMS: NavItem[] = NAV_GROUPS.flatMap((g) => g.items);
export function canAccessRoute(pathname: string, role: string | undefined): boolean {
if (!role) return false;
const base = pathname.split('/').slice(0, 2).join('/') || '/';
const inboxBase = pathname.startsWith('/inbox/') ? pathname.split('/').slice(0, 3).join('/') : null;
const check = inboxBase ?? base;
const allowed = ROUTE_ACCESS[check] ?? ROUTE_ACCESS[base];
if (allowed === undefined) {
return ROUTE_ACCESS[pathname] === undefined || ROUTE_ACCESS[pathname]!.includes(role as AdminRole);
}
return allowed.includes(role as AdminRole);
}
export function filterMenuByRole<T extends { roles?: AdminRole[] }>(
items: T[],
role: string | undefined
): T[] {
return items.filter((item) => !item.roles || (role && item.roles.includes(role as AdminRole)));
}
export function filterNavGroupsByRole(groups: NavGroup[], role: string | undefined): NavGroup[] {
return groups
.map((group) => ({
...group,
items: filterMenuByRole(group.items, role),
}))
.filter((group) => group.items.length > 0);
}
/** Ключ пункта меню для detail-маршрутов (/users/:id → /users). */
export function getMenuSelectedKey(pathname: string): string {
if (MENU_ITEMS.some((item) => item.key === pathname)) {
return pathname;
}
if (pathname.startsWith('/inbox/')) {
const parts = pathname.split('/').filter(Boolean);
if (parts.length >= 2) return `/inbox/${parts[1]}`;
}
const segment = pathname.split('/').filter(Boolean)[0];
if (segment) {
const parent = `/${segment}`;
if (MENU_ITEMS.some((item) => item.key === parent)) {
return parent;
}
}
return pathname;
}
const WS_NOTIFICATION_ROUTES: Record<'report_created' | 'ticket_created' | 'automod_hit', string> = {
report_created: '/inbox/reports',
ticket_created: '/inbox/tickets',
automod_hit: '/inbox/automod',
};
export function canReceiveWsNotification(
type: 'report_created' | 'ticket_created' | 'automod_hit',
role: string | undefined
): boolean {
return canAccessRoute(WS_NOTIFICATION_ROUTES[type], role);
}
/** Стартовая страница по роли после логина. */
export function getDefaultRouteForRole(role: AdminRole | undefined): string {
if (role === 'moderator') return '/inbox/reports';
if (role === 'support') return '/inbox/tickets';
return '/dashboard';
}