223 lines
11 KiB
SQL
223 lines
11 KiB
SQL
-- =====================================================================
|
|
-- 1. ТАБЛИЦА ПОЛЬЗОВАТЕЛЕЙ (С учетом Ghost Protocol)
|
|
-- =====================================================================
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id UUID PRIMARY KEY,
|
|
tg_id BIGINT UNIQUE, -- NULL для анонимов, заходящих по Seed
|
|
tg_username VARCHAR(255),
|
|
role VARCHAR(50) NOT NULL DEFAULT 'user',
|
|
balance BIGINT NOT NULL DEFAULT 0,
|
|
game_tickets INT NOT NULL DEFAULT 0,
|
|
has_used_trial BOOLEAN NOT NULL DEFAULT FALSE,
|
|
seed_hash VARCHAR(255) UNIQUE, -- Хеш сид-фразы анонима
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_hack_at TIMESTAMPTZ
|
|
);
|
|
|
|
-- =====================================================================
|
|
-- 2. СИНДИКАТ И РЕФЕРАЛЫ (RevShare / CPA контур)
|
|
-- =====================================================================
|
|
CREATE TABLE IF NOT EXISTS referrals (
|
|
referrer_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
referred_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
earned_bonus BIGINT NOT NULL DEFAULT 0,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
|
total_earned BIGINT NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (referrer_id, referred_id),
|
|
UNIQUE (referred_id)
|
|
);
|
|
|
|
-- =====================================================================
|
|
-- 3. ТАРИФНЫЕ ПЛАНЫ И РЕГИОНАЛЬНЫЕ ЦЕНЫ (Биллинг V2)
|
|
-- =====================================================================
|
|
CREATE TABLE IF NOT EXISTS plans (
|
|
name VARCHAR(50) PRIMARY KEY,
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS plan_prices (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
plan_name VARCHAR(50) NOT NULL REFERENCES plans(name) ON DELETE CASCADE,
|
|
currency VARCHAR(10) NOT NULL, -- 'USD', 'RUB', 'EUR', 'TON'
|
|
amount BIGINT NOT NULL, -- В центах, копейках, nanoTON
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
UNIQUE(plan_name, currency)
|
|
);
|
|
|
|
-- =====================================================================
|
|
-- 4. ТАБЛИЦА ПОДПИСОК (Состояния Конечного Автомата)
|
|
-- =====================================================================
|
|
CREATE TABLE IF NOT EXISTS subscriptions (
|
|
user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
|
plan_name VARCHAR(50),
|
|
expires_at TIMESTAMPTZ,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active, grace, expired
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- =====================================================================
|
|
-- 5. ИНВОЙСЫ И ЧЕКИ ОПЛАТ
|
|
-- =====================================================================
|
|
CREATE TABLE IF NOT EXISTS invoices (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
plan_name VARCHAR(50) NOT NULL REFERENCES plans(name),
|
|
provider VARCHAR(50) NOT NULL, -- 'TON', 'STRIPE', 'YOOKASSA'
|
|
currency VARCHAR(10) NOT NULL,
|
|
amount BIGINT NOT NULL,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending, paid, failed, expired
|
|
external_id VARCHAR(255) UNIQUE, -- Хеш транзакции в блокчейне или ID в эквайринге
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_invoices_user ON invoices(user_id);
|
|
CREATE INDEX idx_invoices_status ON invoices(status);
|
|
|
|
-- =====================================================================
|
|
-- 6. ПРОМОКОДЫ И СИСТЕМА ИХ АКТИВИЗАЦИИ
|
|
-- =====================================================================
|
|
CREATE TABLE IF NOT EXISTS promo_codes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
code VARCHAR(50) UNIQUE NOT NULL,
|
|
reward_amount BIGINT NOT NULL,
|
|
max_uses INT,
|
|
current_uses INT NOT NULL DEFAULT 0,
|
|
expires_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS promo_usages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
promo_id UUID NOT NULL REFERENCES promo_codes(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
used_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(promo_id, user_id)
|
|
);
|
|
|
|
CREATE INDEX idx_promo_code ON promo_codes(code);
|
|
|
|
-- =====================================================================
|
|
-- 7. СЕССИИ АВТОРИЗАЦИИ И VPN УЗЛЫ
|
|
-- =====================================================================
|
|
-- TODO: Таблица объявлена, но нигде не используется в коде (нет ни одного
|
|
-- запроса к auth_sessions в src/). Это недореализованный флоу Magic Link
|
|
-- (одноразовый код из Telegram-бота -> обмен на JWT на сайте, см. things.txt).
|
|
-- Либо доделать флоу (repository + auth/service.rs + controller), либо убрать
|
|
-- таблицу из схемы, чтобы не вводить в заблуждение.
|
|
CREATE TABLE IF NOT EXISTS auth_sessions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID,
|
|
auth_code VARCHAR(8) NOT NULL UNIQUE,
|
|
is_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
|
expires_at TIMESTAMPTZ NOT NULL
|
|
);
|
|
|
|
-- TODO: Не хватает столбцов public_key и sni_domain, необходимых для формирования
|
|
-- полноценного TunnelConfig в GET /nodes/routing (см. TODO в
|
|
-- src/modules/nodes/controller.rs::get_node_routing).
|
|
CREATE TABLE IF NOT EXISTS vpn_nodes (
|
|
id UUID PRIMARY KEY,
|
|
name VARCHAR(100) NOT NULL,
|
|
country_code VARCHAR(10) NOT NULL,
|
|
ip_address VARCHAR(50) NOT NULL,
|
|
port INT NOT NULL DEFAULT 443,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'offline',
|
|
current_load INT NOT NULL DEFAULT 0,
|
|
tunnel_address VARCHAR(50) NOT NULL DEFAULT '10.0.0.1',
|
|
tunnel_prefix INT NOT NULL DEFAULT 24,
|
|
tunnel_dns VARCHAR(50) NOT NULL DEFAULT '10.0.0.2',
|
|
tunnel_mtu INT NOT NULL DEFAULT 1380,
|
|
last_seen TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- =====================================================================
|
|
-- 8. СИСТЕМНЫЙ СИД (Наполнение справочников начальными данными)
|
|
-- =====================================================================
|
|
-- Регистрируем все варианты планов по длительности (если их ещё нет)
|
|
INSERT INTO plans (name) VALUES
|
|
('GHOST_1M'), ('GHOST_6M'), ('GHOST_12M'),
|
|
('NETRUNNER_1M'), ('NETRUNNER_6M'), ('NETRUNNER_12M'),
|
|
('MAINFRAME_1M'), ('MAINFRAME_6M'), ('MAINFRAME_12M')
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Заливаем полную международную и региональную сетку цен
|
|
INSERT INTO plan_prices (plan_name, currency, amount) VALUES
|
|
-- =================================================================
|
|
-- ГЛОБАЛЬНЫЙ КОНТУР (USD) — Базовый прайс с сайта
|
|
-- =================================================================
|
|
('GHOST_1M', 'USD', 500), -- $5.00
|
|
('GHOST_6M', 'USD', 2400), -- $24.00
|
|
('GHOST_12M', 'USD', 3600), -- $36.00 (Сайт: $3/mo)
|
|
|
|
('NETRUNNER_1M', 'USD', 900), -- $9.00
|
|
('NETRUNNER_6M', 'USD', 4500), -- $45.00
|
|
('NETRUNNER_12M', 'USD', 6000), -- $60.00 (Сайт: $5/mo)
|
|
|
|
('MAINFRAME_1M', 'USD', 2000), -- $20.00
|
|
('MAINFRAME_6M', 'USD', 9600), -- $96.00
|
|
('MAINFRAME_12M', 'USD', 14400), -- $144.00 (Сайт: $12/mo)
|
|
|
|
-- =================================================================
|
|
-- РОССИЯ И СНГ (RUB) — Локальный прайс с сайта
|
|
-- =================================================================
|
|
('GHOST_1M', 'RUB', 24900), -- 249.00 ₽
|
|
('GHOST_6M', 'RUB', 129000), -- 1 290.00 ₽
|
|
('GHOST_12M', 'RUB', 299000), -- 2 990.00 ₽ (Сайт: 249/мес)
|
|
|
|
('NETRUNNER_1M', 'RUB', 69000), -- 690.00 ₽
|
|
('NETRUNNER_6M', 'RUB', 349000), -- 3 490.00 ₽
|
|
('NETRUNNER_12M', 'RUB', 549000), -- 5 490.00 ₽ (Сайт: 458/мес)
|
|
|
|
('MAINFRAME_1M', 'RUB', 149000), -- 1 490.00 ₽
|
|
('MAINFRAME_6M', 'RUB', 749000), -- 7 490.00 ₽
|
|
('MAINFRAME_12M', 'RUB', 1199000), -- 11 990.00 ₽ (Сайт: 999/мес)
|
|
|
|
-- =================================================================
|
|
-- КИТАЙ (CNY) — Адаптировано под конкуренцию с GFW-панелями
|
|
-- =================================================================
|
|
('GHOST_1M', 'CNY', 2200), -- 22.00 ¥
|
|
('GHOST_6M', 'CNY', 11000), -- 110.00 ¥
|
|
('GHOST_12M', 'CNY', 22000), -- 220.00 ¥
|
|
|
|
('NETRUNNER_1M', 'CNY', 4900), -- 49.00 ¥
|
|
('NETRUNNER_6M', 'CNY', 24500), -- 245.00 ¥
|
|
('NETRUNNER_12M', 'CNY', 43000), -- 430.00 ¥
|
|
|
|
('MAINFRAME_1M', 'CNY', 10000), -- 100.00 ¥
|
|
('MAINFRAME_6M', 'CNY', 49000), -- 490.00 ¥
|
|
('MAINFRAME_12M', 'CNY', 86000), -- 860.00 ¥
|
|
|
|
-- =================================================================
|
|
-- ТУРЦИЯ (TRY) — С учётом девальвации лиры
|
|
-- =================================================================
|
|
('GHOST_1M', 'TRY', 9900), -- 99.00 ₺
|
|
('GHOST_6M', 'TRY', 49900), -- 499.00 ₺
|
|
('GHOST_12M', 'TRY', 89900), -- 899.00 ₺
|
|
|
|
('NETRUNNER_1M', 'TRY', 24900), -- 249.00 ₺
|
|
('NETRUNNER_6M', 'TRY', 119900), -- 1 199.00 ₺
|
|
('NETRUNNER_12M', 'TRY', 219000), -- 2 190.00 ₺
|
|
|
|
('MAINFRAME_1M', 'TRY', 59900), -- 599.00 ₺
|
|
('MAINFRAME_6M', 'TRY', 289000), -- 2 890.00 ₺
|
|
('MAINFRAME_12M', 'TRY', 499000), -- 4 990.00 ₺
|
|
|
|
-- =================================================================
|
|
-- ИРАН (IRR / Цены указаны в РИАЛАХ, 1 Томан = 10 Риалов)
|
|
-- =================================================================
|
|
('GHOST_1M', 'IRR', 15000000), -- 1,500,000 Томанов (15 млн риалов)
|
|
('GHOST_6M', 'IRR', 75000000), -- 7,500,000 Томанов
|
|
('GHOST_12M', 'IRR', 135000000), -- 13,500,000 Томанов
|
|
|
|
('NETRUNNER_1M', 'IRR', 40000000), -- 4,000,000 Томанов
|
|
('NETRUNNER_6M', 'IRR', 190000000), -- 19,000,000 Томанов
|
|
('NETRUNNER_12M', 'IRR', 320000000), -- 32,000,000 Томанов
|
|
|
|
('MAINFRAME_1M', 'IRR', 90000000), -- 9,000,000 Томанов
|
|
('MAINFRAME_6M', 'IRR', 430000000), -- 43,000,000 Томанов
|
|
('MAINFRAME_12M', 'IRR', 750000000) -- 75,000,000 Томанов
|
|
ON CONFLICT (plan_name, currency) DO UPDATE
|
|
SET amount = EXCLUDED.amount; |