Перейти к содержанию

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
);
create table pro_players (...);
create table 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').
RLS для redux_items
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-политиками на стороне БД.

Дальше: R2 layout →