a5205cc054
- миграция 00002: users, sessions, totp_secrets, webauthn_credentials, security_events, guilds, guild_members, roles, member_roles, channels, channel_overrides, audit_log + дефолтные instance_settings (AGENT.md 6.1) - internal/store: Snowflake-идентификаторы, CRUD пользователей и сессий (ротация, step-up, logout-all), TOTP и события безопасности, серверы, участники, роли, комнаты и оверрайды, настройки инстанса и аудит - internal/permissions: 37 прав битмаской, вычисление по правилам §6.2 (баз role @user → оверрайды ролей → оверрайд пользователя → ADMINISTRATOR), иерархия ролей и участников, тайм-ауты, обход для инстанс-админа, LRU-кэш с инвалидацией - internal/source: адаптер permissions.Source поверх store - тесты: 18 unit-тестов прав + интеграционный набор на реальной SQLite (приватная комната, модератор, владелец, инстанс-админ, тайм-аут) - golangci: обоснованное исключение gosec для пакета store (конверсии Snowflake и сборка SQL из константных шаблонов)
190 lines
7.7 KiB
SQL
190 lines
7.7 KiB
SQL
-- +goose Up
|
|
-- Фаза 1: аккаунты, инстанс, серверы, роли, комнаты и права (AGENT.md 6.1).
|
|
|
|
CREATE TABLE users (
|
|
id INTEGER PRIMARY KEY,
|
|
username TEXT NOT NULL,
|
|
username_lower TEXT NOT NULL UNIQUE,
|
|
display_name TEXT NOT NULL DEFAULT '',
|
|
email_enc TEXT NOT NULL,
|
|
email_index TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
avatar_file_id INTEGER,
|
|
banner_file_id INTEGER,
|
|
bio TEXT NOT NULL DEFAULT '',
|
|
status TEXT NOT NULL DEFAULT 'online',
|
|
custom_status TEXT NOT NULL DEFAULT '',
|
|
custom_status_emoji TEXT NOT NULL DEFAULT '',
|
|
flags INTEGER NOT NULL DEFAULT 0,
|
|
is_instance_admin INTEGER NOT NULL DEFAULT 0,
|
|
badges_json TEXT NOT NULL DEFAULT '[]',
|
|
locale TEXT NOT NULL DEFAULT 'ru',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
deleted_at TEXT
|
|
);
|
|
CREATE INDEX users_created_idx ON users (created_at);
|
|
CREATE INDEX users_admin_idx ON users (is_instance_admin) WHERE is_instance_admin = 1;
|
|
|
|
CREATE TABLE sessions (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
user_agent TEXT NOT NULL DEFAULT '',
|
|
ip TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
expires_at TEXT NOT NULL,
|
|
last_seen TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
stepped_up_at TEXT
|
|
);
|
|
CREATE INDEX sessions_user_idx ON sessions (user_id);
|
|
CREATE INDEX sessions_expires_idx ON sessions (expires_at);
|
|
|
|
CREATE TABLE totp_secrets (
|
|
user_id INTEGER PRIMARY KEY REFERENCES users (id) ON DELETE CASCADE,
|
|
secret_encrypted TEXT NOT NULL,
|
|
enabled INTEGER NOT NULL DEFAULT 0,
|
|
recovery_codes_json TEXT NOT NULL DEFAULT '[]',
|
|
confirmed_at TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
|
|
-- Таблица создаётся сразу, используется с Фазы 7 (AGENT.md 6.1).
|
|
CREATE TABLE webauthn_credentials (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
|
credential_id TEXT NOT NULL UNIQUE,
|
|
public_key BLOB NOT NULL,
|
|
sign_count INTEGER NOT NULL DEFAULT 0,
|
|
transports TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
|
|
CREATE TABLE security_events (
|
|
id INTEGER PRIMARY KEY,
|
|
user_id INTEGER REFERENCES users (id) ON DELETE SET NULL,
|
|
type TEXT NOT NULL,
|
|
ip TEXT NOT NULL DEFAULT '',
|
|
user_agent TEXT NOT NULL DEFAULT '',
|
|
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
CREATE INDEX security_events_user_idx ON security_events (user_id, id DESC);
|
|
|
|
CREATE TABLE guilds (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
owner_id INTEGER NOT NULL REFERENCES users (id),
|
|
icon_file_id INTEGER,
|
|
banner_file_id INTEGER,
|
|
splash_file_id INTEGER,
|
|
accent_color INTEGER NOT NULL DEFAULT 0,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
is_main INTEGER NOT NULL DEFAULT 0,
|
|
is_discoverable INTEGER NOT NULL DEFAULT 0,
|
|
limits_json TEXT NOT NULL DEFAULT '{}',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
CREATE INDEX guilds_owner_idx ON guilds (owner_id);
|
|
CREATE INDEX guilds_discoverable_idx ON guilds (is_discoverable) WHERE is_discoverable = 1;
|
|
|
|
CREATE TABLE guild_members (
|
|
guild_id INTEGER NOT NULL REFERENCES guilds (id) ON DELETE CASCADE,
|
|
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
|
nickname TEXT,
|
|
joined_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
timeout_until TEXT,
|
|
PRIMARY KEY (guild_id, user_id)
|
|
);
|
|
CREATE INDEX guild_members_user_idx ON guild_members (user_id);
|
|
|
|
CREATE TABLE roles (
|
|
id INTEGER PRIMARY KEY,
|
|
guild_id INTEGER NOT NULL REFERENCES guilds (id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
color INTEGER NOT NULL DEFAULT 0,
|
|
position INTEGER NOT NULL DEFAULT 0,
|
|
permissions INTEGER NOT NULL DEFAULT 0,
|
|
is_default INTEGER NOT NULL DEFAULT 0,
|
|
mentionable INTEGER NOT NULL DEFAULT 0,
|
|
hoist INTEGER NOT NULL DEFAULT 0,
|
|
cosmetic_frame_id INTEGER,
|
|
cosmetic_badge_id INTEGER,
|
|
nick_color INTEGER,
|
|
nick_effect TEXT NOT NULL DEFAULT 'none',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
CREATE INDEX roles_guild_idx ON roles (guild_id, position DESC);
|
|
|
|
CREATE TABLE member_roles (
|
|
guild_id INTEGER NOT NULL,
|
|
user_id INTEGER NOT NULL,
|
|
role_id INTEGER NOT NULL REFERENCES roles (id) ON DELETE CASCADE,
|
|
PRIMARY KEY (guild_id, user_id, role_id),
|
|
FOREIGN KEY (guild_id, user_id) REFERENCES guild_members (guild_id, user_id) ON DELETE CASCADE
|
|
);
|
|
CREATE INDEX member_roles_user_idx ON member_roles (user_id);
|
|
CREATE INDEX member_roles_role_idx ON member_roles (role_id);
|
|
|
|
CREATE TABLE channels (
|
|
id INTEGER PRIMARY KEY,
|
|
guild_id INTEGER REFERENCES guilds (id) ON DELETE CASCADE,
|
|
type TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
position INTEGER NOT NULL DEFAULT 0,
|
|
parent_id INTEGER REFERENCES channels (id) ON DELETE CASCADE,
|
|
slowmode_seconds INTEGER NOT NULL DEFAULT 0,
|
|
nsfw INTEGER NOT NULL DEFAULT 0,
|
|
background TEXT,
|
|
voice_status TEXT NOT NULL DEFAULT '',
|
|
user_limit INTEGER NOT NULL DEFAULT 0,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
CREATE INDEX channels_guild_idx ON channels (guild_id, position);
|
|
CREATE INDEX channels_parent_idx ON channels (parent_id);
|
|
|
|
CREATE TABLE channel_overrides (
|
|
channel_id INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE,
|
|
target_type TEXT NOT NULL,
|
|
target_id INTEGER NOT NULL,
|
|
allow INTEGER NOT NULL DEFAULT 0,
|
|
deny INTEGER NOT NULL DEFAULT 0,
|
|
PRIMARY KEY (channel_id, target_type, target_id)
|
|
);
|
|
|
|
CREATE TABLE audit_log (
|
|
id INTEGER PRIMARY KEY,
|
|
guild_id INTEGER REFERENCES guilds (id) ON DELETE CASCADE,
|
|
actor_id INTEGER,
|
|
actor_instance_admin INTEGER NOT NULL DEFAULT 0,
|
|
action TEXT NOT NULL,
|
|
target_type TEXT NOT NULL DEFAULT '',
|
|
target_id INTEGER,
|
|
reason TEXT NOT NULL DEFAULT '',
|
|
changes_json TEXT NOT NULL DEFAULT '{}',
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
);
|
|
CREATE INDEX audit_log_guild_idx ON audit_log (guild_id, id DESC);
|
|
|
|
INSERT INTO instance_settings (key, value) VALUES
|
|
('max_guilds_per_user', '10'),
|
|
('max_members_per_guild', '250'),
|
|
('allow_guild_creation', 'true'),
|
|
('main_guild_id', '')
|
|
ON CONFLICT (key) DO NOTHING;
|
|
|
|
-- +goose Down
|
|
DROP TABLE audit_log;
|
|
DROP TABLE channel_overrides;
|
|
DROP TABLE channels;
|
|
DROP TABLE member_roles;
|
|
DROP TABLE roles;
|
|
DROP TABLE guild_members;
|
|
DROP TABLE guilds;
|
|
DROP TABLE security_events;
|
|
DROP TABLE webauthn_credentials;
|
|
DROP TABLE totp_secrets;
|
|
DROP TABLE sessions;
|
|
DROP TABLE users;
|