-- ===================================================================== -- 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;