Supabase schema¶
Вся персистентность Miami Graphics живёт в одном Supabase проекте — huntergraphics. Не разнесён по микросервисам, не нужно. Всё в одной PostgreSQL базе с RLS-политиками.
Архитектура¶
flowchart TB
Lаунчер[Лаунчер Miami Graphics] --> AppBridge[AppBridge на C#]
AppBridge --> Repos[Repository classes]
Repos -->|REST API| Supa[Supabase REST]
Supa --> PG[(PostgreSQL)]
Supa --> RPC[RPC functions]
Web[Веб админка
будущая] -.-> Supa
Доступ из лаунчера — только через C# repository classes, которые делают REST-запросы на Supabase. UI не имеет прямого Supabase-клиента (см. admin index — это специально, чтобы централизованно следить за валидацией).
Основные таблицы¶
Каталог моддинга¶
-- Redux моды (главное)
create table redux_items (
id uuid primary key default gen_random_uuid(),
name text not null,
author text,
description text,
preview_url text,
glb_url text,
tags text[],
popularity int default 0,
is_deleted bool default false,
created_at timestamptz default now()
);
create table redux_versions (
id uuid primary key default gen_random_uuid(),
redux_id uuid references redux_items(id) on delete cascade,
version_label text not null, -- "v1", "v2-fix"
patch_url text not null,
sha256 text,
size_bytes bigint,
component_urls jsonb, -- {"minimap": "url", "tracers": "url"}
created_at timestamptz default now(),
unique (redux_id, version_label)
);
create table redux_featured_picks (
id uuid primary key default gen_random_uuid(),
redux_id uuid references redux_items(id) on delete cascade,
display_order int default 0,
badge_text text,
added_at timestamptz default now()
);
-- Gunpack'и
create table gunpacks (
id uuid primary key default gen_random_uuid(),
name text not null,
author text,
cover_url text,
rpf_url text not null,
rpf_sha256 text,
popularity int default 0,
is_whitelist_allowed bool default true,
is_deleted bool default false,
created_at timestamptz default now()
);
create table gunpack_guns (
id uuid primary key default gen_random_uuid(),
gunpack_id uuid references gunpacks(id) on delete cascade,
internal_name text not null,
display_name text,
model_glb_url text,
preview_png_url text,
meta_patch jsonb
);
-- Армор
create table armor_packs (...);
create table armor_pieces (...);
-- DLC импорты
create table dlc_imports (...);
-- Library (отдельные компоненты)
create table library_components (...);
-- GTA Presets
create table gta_presets (...);
HNT-коды и user-builds¶
create table hnt_codes (
code text primary key,
payload jsonb not null,
created_by text,
created_at timestamptz default now(),
last_downloaded_at timestamptz,
downloads_count int default 0,
expires_at timestamptz
);
create table user_builds (
id uuid primary key default gen_random_uuid(),
name text not null,
description text,
cover_url text,
author_user_id text not null,
author_username text,
hnt_code text references hnt_codes(code),
payload jsonb not null,
status text default 'pending',
review_notes text,
popularity int default 0,
created_at timestamptz default now(),
is_deleted bool default false
);
Поддержка билдов GTA¶
create table gta_versions (
id uuid primary key default gen_random_uuid(),
version_string text not null unique,
display_name text,
release_date date,
is_supported bool default true,
notes text,
display_order int default 0
);
Pro players и popular choices¶
Auto-update¶
create table app_versions (
id uuid primary key default gen_random_uuid(),
version text not null, -- "1.0.2"
installer_url text not null, -- R2 link на .exe
release_notes text,
is_required bool default false,
released_at timestamptz default now(),
is_latest bool default false -- only one row at a time
);
Подробнее в Обновления → Inno + Supabase.
Пользователи¶
-- Не auth.users (это Supabase встроенная), а наша own
create table app_users (
id uuid primary key, -- = auth.users.id
login text unique not null,
username text,
role text default 'User', -- 'Admin' | 'Moderator' | 'User' | 'Guest'
password_hash text not null,
totp_secret text,
is_banned bool default false,
created_at timestamptz default now()
);
RLS политики¶
Row-Level Security включён на всех каталог-таблицах. Дефолтные политики:
- SELECT разрешён для всех (anonymous и authenticated).
- INSERT/UPDATE/DELETE только для
role IN ('Admin', 'Moderator').
alter table redux_items enable row level security;
create policy "Public read"
on redux_items for select
using (is_deleted = false);
create policy "Admin write"
on redux_items for all
using (
exists (
select 1 from app_users
where app_users.id = auth.uid()
and app_users.role in ('Admin', 'Moderator')
)
);
hnt_codes имеет особый policy:
- INSERT — only authenticated (любой залогиненный юзер может create).
- SELECT — by code (для preview) или by
created_by = auth.uid()(для «my codes»). - DELETE — only
created_by = auth.uid()(только создатель удаляет).
RPC functions¶
Часть бизнес-логики живёт в RPC functions (PostgreSQL plpgsql) для атомарности и security:
| Function | Что делает |
|---|---|
generate_hnt_code() |
Genероры уникальный код, инсёрт row, return |
import_hnt_code(code) |
Atomic «найти + инкремент downloads_count + return» |
increment_redux_popularity(redux_id) |
+= 1 с security definer |
daily_popularity_decay() |
popularity = floor(popularity * 0.99) по всем таблицам |
Migrations¶
История schema-изменений в supabase/migrations/. Каждое изменение это .sql файл с timestamp prefix'ом:
supabase/migrations/
├── 20240301000000_initial_schema.sql
├── 20240315000000_add_redux_versions.sql
├── 20240502000000_add_hnt_codes.sql
├── 20240620000000_add_pro_players.sql
└── 20250408000000_add_app_versions.sql
Применяются через supabase db push локально или через Supabase Studio в production.
URL и креды¶
- Project URL:
https://<projectref>.supabase.co - REST endpoint:
https://<projectref>.supabase.co/rest/v1/ - Anon key: вшит в C# код (public, безопасно с RLS).
- Service key: только на VPS для batch-job'ов (decay, cleanup).
Лаунчер использует anon key. Все sensitive операции защищены RLS-политиками на стороне БД.