init
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
email VARCHAR(100) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
roles JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW ()
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS users;
|
||||
@@ -0,0 +1,14 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS refresh_tokens (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
jti VARCHAR(255) NOT NULL UNIQUE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
|
||||
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS refresh_tokens;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS scenarios (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
image TEXT,
|
||||
scenario JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
author_id INTEGER NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'draft',
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
published_at TIMESTAMP,
|
||||
is_deleted BOOLEAN DEFAULT FALSE,
|
||||
|
||||
CONSTRAINT fk_scenarios_author FOREIGN KEY (author_id) REFERENCES users (id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS scenarios;
|
||||
@@ -0,0 +1,19 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS games (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
start_at TIMESTAMP NOT NULL,
|
||||
scenario_id INTEGER NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'draft',
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
started_at TIMESTAMP,
|
||||
ended_at TIMESTAMP,
|
||||
|
||||
CONSTRAINT fk_games_scenario FOREIGN KEY (scenario_id) REFERENCES scenarios (id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS games;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS teams (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'register',
|
||||
creator_id INTEGER NOT NULL,
|
||||
game_id INTEGER NOT NULL,
|
||||
password TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
|
||||
CONSTRAINT fk_teams_game FOREIGN KEY (game_id) REFERENCES games (id) ON DELETE RESTRICT,
|
||||
CONSTRAINT fk_teams_creator FOREIGN KEY (creator_id) REFERENCES users (id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS teams;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS actions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
code TEXT,
|
||||
team_id INTEGER,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
|
||||
CONSTRAINT fk_actions_team FOREIGN KEY (team_id) REFERENCES teams (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS actions;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS applications (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT,
|
||||
image TEXT,
|
||||
team_id INTEGER,
|
||||
status VARCHAR(50) DEFAULT 'need',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
|
||||
CONSTRAINT fk_applications_team FOREIGN KEY (team_id) REFERENCES teams (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS applications;
|
||||
@@ -0,0 +1,26 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS user_agreements (
|
||||
id SERIAL PRIMARY KEY,
|
||||
-- user_id NULL-able: при удалении аккаунта FK-ограничение обнуляет
|
||||
-- ссылку (ON DELETE SET NULL), а сама запись об акцепте остаётся в
|
||||
-- обезличенном виде (факт согласия, ст. 9 ч. 4 152-ФЗ — бремя
|
||||
-- доказывания на операторе). Персональные данные внутри записи
|
||||
-- (ip, user_agent) обнуляет users_repo.DeleteUser.
|
||||
user_id INTEGER,
|
||||
agreement_type VARCHAR(50) NOT NULL,
|
||||
version VARCHAR(50) NOT NULL,
|
||||
accepted_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
ip VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
|
||||
CONSTRAINT fk_user_agreements_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL,
|
||||
CONSTRAINT uq_user_agreement UNIQUE (user_id, agreement_type, version),
|
||||
CONSTRAINT chk_user_agreement_type CHECK (agreement_type IN ('terms', 'privacy'))
|
||||
);
|
||||
|
||||
-- Отдельный индекс по user_id не нужен: UNIQUE (user_id, agreement_type, version)
|
||||
-- уже покрывает выборки по префиксу user_id.
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS user_agreements;
|
||||
@@ -0,0 +1,39 @@
|
||||
-- +goose Up
|
||||
-- Сценарии и команды сохраняются при удалении аккаунта автора:
|
||||
-- привязка к пользователю обнуляется (обезличивание, ст. 3 152-ФЗ),
|
||||
-- сам контент остаётся в Сервисе (лицензия по п. 7.2 Пользовательского соглашения).
|
||||
ALTER TABLE scenarios ALTER COLUMN author_id DROP NOT NULL;
|
||||
|
||||
ALTER TABLE scenarios DROP CONSTRAINT IF EXISTS fk_scenarios_author;
|
||||
ALTER TABLE scenarios ADD CONSTRAINT fk_scenarios_author
|
||||
FOREIGN KEY (author_id) REFERENCES users (id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE teams ALTER COLUMN creator_id DROP NOT NULL;
|
||||
|
||||
ALTER TABLE teams DROP CONSTRAINT IF EXISTS fk_teams_creator;
|
||||
ALTER TABLE teams ADD CONSTRAINT fk_teams_creator
|
||||
FOREIGN KEY (creator_id) REFERENCES users (id) ON DELETE SET NULL;
|
||||
|
||||
-- status у сценариев всегда задан (default 'draft'): NULL-значение сломало бы
|
||||
-- guard'ы вида "status <> 'public'" в scenarios_repo (NULL даёт NULL, т.е. ложь).
|
||||
-- На БД с историческими NULL-данными сначала приводим их к default'у.
|
||||
UPDATE scenarios SET status = 'draft' WHERE status IS NULL;
|
||||
ALTER TABLE scenarios ALTER COLUMN status SET NOT NULL;
|
||||
|
||||
-- +goose Down
|
||||
-- Восстановление прежней схемы: автор обязателен, при удалении пользователя —
|
||||
-- удаление его контента запрещено (RESTRICT).
|
||||
DELETE FROM scenarios WHERE author_id IS NULL;
|
||||
DELETE FROM teams WHERE creator_id IS NULL;
|
||||
|
||||
ALTER TABLE scenarios ALTER COLUMN status DROP NOT NULL;
|
||||
|
||||
ALTER TABLE scenarios DROP CONSTRAINT IF EXISTS fk_scenarios_author;
|
||||
ALTER TABLE scenarios ADD CONSTRAINT fk_scenarios_author
|
||||
FOREIGN KEY (author_id) REFERENCES users (id) ON DELETE RESTRICT;
|
||||
ALTER TABLE scenarios ALTER COLUMN author_id SET NOT NULL;
|
||||
|
||||
ALTER TABLE teams DROP CONSTRAINT IF EXISTS fk_teams_creator;
|
||||
ALTER TABLE teams ADD CONSTRAINT fk_teams_creator
|
||||
FOREIGN KEY (creator_id) REFERENCES users (id) ON DELETE RESTRICT;
|
||||
ALTER TABLE teams ALTER COLUMN creator_id SET NOT NULL;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE applications
|
||||
ADD COLUMN IF NOT EXISTS file_type TEXT NOT NULL DEFAULT '';
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE applications
|
||||
DROP COLUMN IF EXISTS file_type;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE
|
||||
IF NOT EXISTS team_answers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
team_id INTEGER NOT NULL,
|
||||
question_code TEXT NOT NULL,
|
||||
answer TEXT NOT NULL DEFAULT '',
|
||||
score INTEGER,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW (),
|
||||
|
||||
CONSTRAINT fk_team_answers_team FOREIGN KEY (team_id) REFERENCES teams (id) ON DELETE CASCADE,
|
||||
CONSTRAINT uq_team_answer UNIQUE (team_id, question_code)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS team_answers;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE actions ADD COLUMN via_mcp BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE actions DROP COLUMN via_mcp;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS screen_sessions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
access_token_hash TEXT NOT NULL UNIQUE,
|
||||
game_id INTEGER REFERENCES games (id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS screen_sessions;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE screen_sessions
|
||||
ADD COLUMN IF NOT EXISTS sound_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS last_seen_at TIMESTAMP NOT NULL DEFAULT NOW();
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE screen_sessions
|
||||
DROP COLUMN IF EXISTS last_seen_at,
|
||||
DROP COLUMN IF EXISTS sound_enabled;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS game_map_items (
|
||||
game_id INTEGER NOT NULL REFERENCES games(id) ON DELETE CASCADE,
|
||||
kind TEXT NOT NULL,
|
||||
item_id INTEGER NOT NULL,
|
||||
x INTEGER NOT NULL DEFAULT 40,
|
||||
y INTEGER NOT NULL DEFAULT 40,
|
||||
hidden BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY (game_id, kind, item_id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS game_map_items;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE screen_sessions
|
||||
ADD COLUMN IF NOT EXISTS mode INTEGER NOT NULL DEFAULT 1,
|
||||
ADD COLUMN IF NOT EXISTS state_version BIGINT NOT NULL DEFAULT 1,
|
||||
ADD COLUMN IF NOT EXISTS position_ms BIGINT NOT NULL DEFAULT 0;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE screen_sessions
|
||||
DROP COLUMN IF EXISTS position_ms,
|
||||
DROP COLUMN IF EXISTS state_version,
|
||||
DROP COLUMN IF EXISTS mode;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE screen_sessions
|
||||
ADD COLUMN IF NOT EXISTS command INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS volume INTEGER NOT NULL DEFAULT 25;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE screen_sessions
|
||||
DROP COLUMN IF EXISTS volume,
|
||||
DROP COLUMN IF EXISTS command;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE screen_sessions
|
||||
ALTER COLUMN volume SET DEFAULT 100;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE screen_sessions
|
||||
ALTER COLUMN volume SET DEFAULT 25;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE games ADD COLUMN format TEXT NOT NULL DEFAULT 'online'
|
||||
CHECK (format IN ('online', 'offline'));
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE games DROP COLUMN format;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE teams
|
||||
ADD COLUMN captain_email TEXT NOT NULL DEFAULT '',
|
||||
ADD COLUMN participants_count INTEGER NOT NULL DEFAULT 0 CHECK (participants_count >= 0),
|
||||
ADD COLUMN registration_delivery_id TEXT,
|
||||
ADD CONSTRAINT teams_registration_delivery_unique UNIQUE (game_id, registration_delivery_id);
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE teams
|
||||
DROP CONSTRAINT teams_registration_delivery_unique,
|
||||
DROP COLUMN registration_delivery_id,
|
||||
DROP COLUMN participants_count,
|
||||
DROP COLUMN captain_email;
|
||||
@@ -0,0 +1,23 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE teams
|
||||
ADD COLUMN participation_status TEXT NOT NULL DEFAULT 'pending' CHECK (participation_status IN ('pending', 'confirmed', 'cancelled')),
|
||||
ADD COLUMN participation_source TEXT NOT NULL DEFAULT '',
|
||||
ADD COLUMN participation_answered_at TIMESTAMPTZ,
|
||||
ADD COLUMN participation_token_hash BYTEA,
|
||||
ADD COLUMN participation_sent_at TIMESTAMPTZ,
|
||||
ADD COLUMN participation_attempts INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN participation_next_attempt_at TIMESTAMPTZ,
|
||||
ADD COLUMN participation_email_error TEXT NOT NULL DEFAULT '';
|
||||
CREATE UNIQUE INDEX teams_participation_token ON teams (participation_token_hash);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX teams_participation_token;
|
||||
ALTER TABLE teams
|
||||
DROP COLUMN participation_status,
|
||||
DROP COLUMN participation_source,
|
||||
DROP COLUMN participation_answered_at,
|
||||
DROP COLUMN participation_token_hash,
|
||||
DROP COLUMN participation_sent_at,
|
||||
DROP COLUMN participation_attempts,
|
||||
DROP COLUMN participation_next_attempt_at,
|
||||
DROP COLUMN participation_email_error;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE game_reviews (
|
||||
id SERIAL PRIMARY KEY,
|
||||
game_id INTEGER NOT NULL REFERENCES games (id) ON DELETE CASCADE,
|
||||
team_id INTEGER NOT NULL REFERENCES teams (id) ON DELETE CASCADE,
|
||||
rating INTEGER NOT NULL CHECK (rating BETWEEN 1 AND 5),
|
||||
text TEXT NOT NULL CHECK (length(btrim(text)) > 0),
|
||||
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'rejected', 'hidden')),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
moderated_at TIMESTAMPTZ,
|
||||
UNIQUE (game_id, team_id)
|
||||
);
|
||||
|
||||
CREATE INDEX game_reviews_moderation ON game_reviews (status, created_at);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE game_reviews;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE teams
|
||||
ADD COLUMN review_reminder_sent_at TIMESTAMPTZ,
|
||||
ADD COLUMN review_reminder_attempts INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN review_reminder_next_attempt_at TIMESTAMPTZ,
|
||||
ADD COLUMN review_reminder_error TEXT NOT NULL DEFAULT '';
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE teams
|
||||
DROP COLUMN review_reminder_sent_at,
|
||||
DROP COLUMN review_reminder_attempts,
|
||||
DROP COLUMN review_reminder_next_attempt_at,
|
||||
DROP COLUMN review_reminder_error;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE games ADD COLUMN location TEXT;
|
||||
|
||||
CREATE TABLE tickets (
|
||||
id SERIAL PRIMARY KEY,
|
||||
game_id INTEGER NOT NULL REFERENCES games (id) ON DELETE CASCADE,
|
||||
token_hash BYTEA NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'used')),
|
||||
used_at TIMESTAMPTZ,
|
||||
used_by INTEGER REFERENCES users (id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX tickets_game_id ON tickets (game_id);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS tickets;
|
||||
ALTER TABLE games DROP COLUMN location;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE screen_sessions
|
||||
ADD COLUMN IF NOT EXISTS timer_label TEXT NOT NULL DEFAULT 'Перерыв',
|
||||
ADD COLUMN IF NOT EXISTS timer_duration_seconds BIGINT NOT NULL DEFAULT 600,
|
||||
ADD COLUMN IF NOT EXISTS timer_remaining_seconds BIGINT NOT NULL DEFAULT 600,
|
||||
ADD COLUMN IF NOT EXISTS timer_state INTEGER NOT NULL DEFAULT 1,
|
||||
ADD COLUMN IF NOT EXISTS timer_ends_at TIMESTAMP;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE screen_sessions
|
||||
DROP COLUMN IF EXISTS timer_ends_at,
|
||||
DROP COLUMN IF EXISTS timer_state,
|
||||
DROP COLUMN IF EXISTS timer_remaining_seconds,
|
||||
DROP COLUMN IF EXISTS timer_duration_seconds,
|
||||
DROP COLUMN IF EXISTS timer_label;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE screen_sessions
|
||||
ALTER COLUMN timer_ends_at TYPE TIMESTAMPTZ
|
||||
USING timer_ends_at AT TIME ZONE 'Asia/Barnaul';
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE screen_sessions
|
||||
ALTER COLUMN timer_ends_at TYPE TIMESTAMP
|
||||
USING timer_ends_at AT TIME ZONE 'Asia/Barnaul';
|
||||
@@ -0,0 +1,6 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE game_reviews
|
||||
ADD COLUMN difficulty INTEGER NOT NULL DEFAULT 2 CHECK (difficulty BETWEEN 1 AND 3);
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE game_reviews DROP COLUMN difficulty;
|
||||
Reference in New Issue
Block a user