-- +goose Up CREATE TYPE user_role AS ENUM ('admin', 'moderator', 'user'); CREATE TABLE users ( id uuid PRIMARY KEY, username citext NOT NULL UNIQUE, display_name text, password_hash text NOT NULL, role user_role NOT NULL DEFAULT 'user', locale text NOT NULL DEFAULT 'ru', ui_prefs jsonb NOT NULL DEFAULT '{}', must_change_password boolean NOT NULL DEFAULT false, must_change_credentials boolean NOT NULL DEFAULT false, is_blocked boolean NOT NULL DEFAULT false, token_version int NOT NULL DEFAULT 0, failed_login_count int NOT NULL DEFAULT 0, locked_until timestamptz, created_by uuid REFERENCES users(id) ON DELETE SET NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), last_login_at timestamptz ); CREATE TABLE refresh_tokens ( id uuid PRIMARY KEY, user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, family_id uuid NOT NULL, family_started_at timestamptz NOT NULL, token_hash bytea NOT NULL UNIQUE, device_name text, client_type text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), expires_at timestamptz NOT NULL, used_at timestamptz, revoked_at timestamptz ); CREATE INDEX refresh_tokens_user_active_idx ON refresh_tokens (user_id) WHERE revoked_at IS NULL; CREATE INDEX refresh_tokens_family_idx ON refresh_tokens (family_id); CREATE TABLE audit_log ( id uuid PRIMARY KEY, actor_id uuid REFERENCES users(id) ON DELETE SET NULL, action text NOT NULL, target_type text, target_id uuid, payload jsonb, created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX audit_log_created_idx ON audit_log (created_at DESC, id DESC); -- +goose Down DROP TABLE audit_log; DROP TABLE refresh_tokens; DROP TABLE users; DROP TYPE user_role;