From ebeeffa6a362c732cf045b1ac1be826213d57456 Mon Sep 17 00:00:00 2001 From: Vladimir Zagainov Date: Sat, 14 Jun 2025 20:17:17 +0300 Subject: [PATCH] feat: add initial database schema migration --- scripts/migrations/001_initial_schema.sql | 120 ++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 scripts/migrations/001_initial_schema.sql diff --git a/scripts/migrations/001_initial_schema.sql b/scripts/migrations/001_initial_schema.sql new file mode 100644 index 0000000..0afa30f --- /dev/null +++ b/scripts/migrations/001_initial_schema.sql @@ -0,0 +1,120 @@ +-- Устанавливаем таймзону для сессии, чтобы все timestamp были в UTC +SET TIMEZONE = 'UTC'; + +-- Таблица пользователей. Основная сущность, хранящая данные для входа. +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + uuid UUID NOT NULL UNIQUE, + username VARCHAR(16) NOT NULL UNIQUE, + email VARCHAR(255) NOT NULL UNIQUE, + password_hash VARCHAR(60) NOT NULL, -- bcrypt хеши имеют длину 60 символов + role VARCHAR(20) NOT NULL DEFAULT 'user', -- 'user' или 'admin' + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- Индекс для быстрого поиска по имени пользователя (хотя UNIQUE уже создает индекс) +CREATE INDEX idx_users_username ON users(username); + +-- Таблица профилей. Связана с пользователями и хранит игровые данные (скины, плащи). +CREATE TABLE profiles ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL UNIQUE, + skin_hash VARCHAR(40), -- SHA1 хеш скина (40 символов) + cape_hash VARCHAR(40), -- SHA1 хеш плаща (40 символов) + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + CONSTRAINT fk_user + FOREIGN KEY(user_id) + REFERENCES users(id) + ON DELETE CASCADE -- Если пользователь удален, его профиль тоже удаляется +); + +-- Таблица для хранения активных access-токенов (Yggdrasil) +CREATE TABLE access_tokens ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + access_token VARCHAR(36) NOT NULL UNIQUE, -- UUIDv4 + client_token VARCHAR(255) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + CONSTRAINT fk_user + FOREIGN KEY(user_id) + REFERENCES users(id) + ON DELETE CASCADE +); + +-- Таблица игровых серверов, которые отображаются в мониторинге +CREATE TABLE game_servers ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL, + address VARCHAR(255) NOT NULL UNIQUE, -- Например, "mc.example.com:25565" + is_enabled BOOLEAN NOT NULL DEFAULT TRUE, + -- Данные, получаемые поллером + status_json TEXT, -- Полный JSON-ответ от сервера Minecraft + last_polled_at TIMESTAMPTZ, + -- Данные, которые мы будем извлекать из JSON для удобства + motd TEXT, + player_count INTEGER, + max_players INTEGER, + version_name VARCHAR(100), + ping_backend_server INTEGER -- Пинг от бэкенда до игрового сервера в мс +); + +-- Таблица модпаков (сборок) +CREATE TABLE modpacks ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL UNIQUE, -- Уникальное имя, например, "TechnoMagic" + display_name VARCHAR(255) NOT NULL, -- Человекочитаемое имя, "TechnoMagic SkyBlock" + minecraft_version VARCHAR(50) NOT NULL, + is_active BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- Таблица файлов, принадлежащих модпакам. +-- Хранит метаданные о каждом файле в модпаке. +CREATE TABLE modpack_files ( + id BIGSERIAL PRIMARY KEY, + modpack_id INTEGER NOT NULL, + -- Относительный путь файла в клиенте, например, "mods/industrial-craft.jar" + relative_path TEXT NOT NULL, + file_hash VARCHAR(40) NOT NULL, -- SHA1 хеш файла + file_size BIGINT NOT NULL, + -- URL для скачивания (если файл импортирован с CurseForge/Modrinth) + download_url TEXT, + + CONSTRAINT fk_modpack + FOREIGN KEY(modpack_id) + REFERENCES modpacks(id) + ON DELETE CASCADE, + + -- Уникальный ключ, чтобы в одном модпаке не было двух файлов с одинаковым путем + UNIQUE (modpack_id, relative_path) +); + +-- Индекс для быстрого поиска файлов по хешу +CREATE INDEX idx_modpack_files_hash ON modpack_files(file_hash); + +-- Функция для автоматического обновления поля updated_at +CREATE OR REPLACE FUNCTION trigger_set_timestamp() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Триггеры для таблиц users и profiles +CREATE TRIGGER set_timestamp +BEFORE UPDATE ON users +FOR EACH ROW +EXECUTE FUNCTION trigger_set_timestamp(); + +CREATE TRIGGER set_timestamp +BEFORE UPDATE ON profiles +FOR EACH ROW +EXECUTE FUNCTION trigger_set_timestamp(); + +-- Сообщение об успешном завершении миграции +\echo 'Initial schema migration applied successfully.'