Админ-панель¶
Админ-панель — это отдельное окно в Miami Graphics доступное только аккаунтам с ролью Admin или Moderator в Supabase. Здесь делается вся работа по управлению каталогом: загрузка новых модов, разбор, генерация превью, утверждение пользовательских заявок.

Кто видит¶
public async Task<AuthResultDto> AuthenticateUserAsync(string login, string password, string? totp)
{
var user = await _userRepo.FindByLoginAsync(login);
var ok = VerifyPassword(password, user.PasswordHash);
if (!ok) throw new UnauthorizedException();
// ... TOTP check если включен ...
return new AuthResultDto(
token: GenerateLocalToken(user.Id),
role: user.Role, // 'Admin' / 'Moderator' / 'User' / 'Guest'
username: user.Username
);
}
UI скрывает админ-панель если role != 'Admin' && role != 'Moderator':
const hasAdmin = auth?.role === 'Admin' || auth?.role === 'Moderator';
{hasAdmin && <SidebarItem icon={ShieldIcon} label="Админ" onClick={() => navigate('admin')} />}
Это client-side hide, не security. Реальная защита на стороне Supabase RLS — все admin-операции требуют JWT с правильным role claim.
Структура¶
Админ-панель это single-page wizard с боковой навигацией. 12 секций:
flowchart LR
AdminSidebar[Sidebar] --> Settings[Settings — глобальные настройки]
AdminSidebar --> Redux[Redux — каталог модов]
AdminSidebar --> Guns[Guns — оружейные паки]
AdminSidebar --> Armor[Бронежилеты]
AdminSidebar --> DlcImport[DLC Import]
AdminSidebar --> Injector[Injector — manual install]
AdminSidebar --> Database[Database]
AdminSidebar --> GtaVersions[GTA Versions]
AdminSidebar --> Library[Library — отдельные компоненты]
AdminSidebar --> Presets[GTA Presets — графика]
AdminSidebar --> Builds[Заявки на сборки]
AdminSidebar --> ProPlayers[PRO Players]
AdminSidebar --> Popularity[Popularity]
Каждая секция — отдельный экран с своим CRUD UI и pipeline'ом операций.
Главное правило¶
Все записи в БД и R2 идут через Bridge handlers в AppBridge. UI не имеет прямого доступа к Supabase или R2 — все запросы идут через C# код где включена авторизация и валидация.
// плохо — никогда не делаем так:
await supabase.from('redux_items').insert(...);
// хорошо:
await bridge.adminCatalogUpdateAsync(item);
Это даёт:
- Single point control над permissions;
- Возможность включить server-side валидацию (SHA проверки, формат файлов);
- Audit trail (логирование в debug-log что и кто менял).
Workflow upload нового мода¶
Самый типичный для админа сценарий:
sequenceDiagram
participant A as Admin (UI)
participant Bridge as AppBridge
participant Parser as ReduxParserPipeline
participant R2 as Cloudflare R2
participant Supa as Supabase
A->>Bridge: adminReduxAnalyze(modRpfPath)
Bridge->>Parser: ParseRedux(cleanRpf, modRpf, name)
Parser->>Parser: open archives, diff, scan components
Parser-->>Bridge: ReduxAnalysisDto {actions, components, glb}
Bridge-->>A: показать preview
A->>Bridge: adminQueueAdd(item)
Bridge->>R2: upload patch.zip
Bridge->>R2: upload preview.png
Bridge->>R2: upload armor.glb (если есть)
Bridge->>R2: upload per-component files
Bridge->>Supa: insert redux_items + redux_versions rows
Bridge-->>A: success, новый мод в каталоге
Это происходит за 30 секунд – 2 минуты в зависимости от размера мода. Тяжёлая работа — это R2 upload (200-500 МБ типично) и parse pipeline.