Newer
Older
tgclient-mcp / backend / app / schema.sql
-- tgclient-mcp: схема SQLite.
-- Время — UTC ISO-8601 (строки, лексикографически сортируются корректно).

-- Незавершённые OAuth-потоки gnexus-auth (state → pkce_verifier, 10 минут)
CREATE TABLE IF NOT EXISTS oauth_states (
    state          TEXT PRIMARY KEY,
    pkce_verifier  TEXT NOT NULL,
    return_to      TEXT NOT NULL DEFAULT '/',
    expires_at     TEXT NOT NULL
);

-- Браузерные сессии gnexus-auth (cookie ↔ профиль; TTL по expires_at)
CREATE TABLE IF NOT EXISTS sessions (
    id           TEXT PRIMARY KEY,
    user_id      TEXT NOT NULL,
    email        TEXT NOT NULL,
    display_name TEXT NOT NULL DEFAULT '',
    avatar_url   TEXT NOT NULL DEFAULT '',
    expires_at   TEXT NOT NULL,
    created_at   TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);

-- Пользователи gnexus-auth: пер-сервисные настройки + снейпшоты для гейтов.
-- profile = словарь профиля verbatim (язык аккаунта — profile.locale);
-- locale  = override из настроек сервиса (NULL = следовать аккаунту).
CREATE TABLE IF NOT EXISTS users (
    user_id      TEXT PRIMARY KEY,          -- sub из gnexus-auth (= sessions.user_id)
    email        TEXT NOT NULL,
    display_name TEXT NOT NULL DEFAULT '',
    profile      TEXT NOT NULL DEFAULT '{}',  -- JSON профиля gnexus-auth verbatim
    locale       TEXT,                        -- override 'en'|'uk'|'ru'; NULL = авто
    system_role  TEXT,                        -- снейпшот system_role gnexus-auth
    blocked      INTEGER NOT NULL DEFAULT 0,  -- вебхук user.blocked/deleted/archived
    created_at   TEXT NOT NULL,
    updated_at   TEXT NOT NULL
);

-- Персональные MCP-ключи (handbook 10-platform/mcp.md, канон = gnexus-synapse):
-- plaintext показывается один раз, в БД — sha256-хэш (unique) + хвост-хинт;
-- снейпшот роли владельца на момент выпуска; вместо архива — ревок.
CREATE TABLE IF NOT EXISTS mcp_tokens (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id      TEXT NOT NULL,               -- sub gnexus-auth (выдаёт сам себе)
    name         TEXT NOT NULL DEFAULT '',    -- «агент Navi rei»
    user_email   TEXT NOT NULL DEFAULT '',    -- снейпшот для списков
    system_role  TEXT NOT NULL DEFAULT 'user',-- снейпшот роли выпуска
    token_hash   TEXT NOT NULL UNIQUE,        -- sha256 plaintext-ключа
    token_hint   TEXT NOT NULL DEFAULT '',    -- хвост 8 символов для опознания в списке
    created_at   TEXT NOT NULL,
    last_used_at TEXT,
    revoked_at   TEXT
);

CREATE INDEX IF NOT EXISTS idx_mcp_tokens_user ON mcp_tokens(user_id);

-- Telegram-аккаунты (MTProto юзер-клиенты). Владелец = sub gnexus-auth
-- (режим без SSO — служебный 'local'). Сессия Telethon — только StringSession,
-- сериализуется в session_data (SQLiteSession запретён: файл-конфликт с БД).
CREATE TABLE IF NOT EXISTS accounts (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id      TEXT NOT NULL REFERENCES users(user_id),
    phone        TEXT NOT NULL,                 -- E.164, telethon.utils.parse_phone
    label        TEXT NOT NULL DEFAULT '',      -- заметка владельца («личный»)
    tg_user_id   INTEGER,                       -- get_me().id после логина
    username     TEXT NOT NULL DEFAULT '',
    display_name TEXT NOT NULL DEFAULT '',
    session_data TEXT,                          -- StringSession.serialize(); NULL = не залогинен
    api_id       INTEGER,                       -- NULL → дефолт из env (задел под per-account creds)
    api_hash     TEXT,
    status       TEXT NOT NULL DEFAULT 'pending', -- pending|active|logged_out|error
    error        TEXT NOT NULL DEFAULT '',
    created_at   TEXT NOT NULL,
    updated_at   TEXT NOT NULL,
    last_used_at TEXT
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_accounts_owner_phone ON accounts(user_id, phone);
CREATE UNIQUE INDEX IF NOT EXISTS idx_accounts_owner_tgid ON accounts(user_id, tg_user_id) WHERE tg_user_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_accounts_owner ON accounts(user_id);

-- Незавершённые логины Telegram — общее состояние для SPA и MCP-тулов
-- (человек может начать добавление в UI и продолжить агентом, и наоборот).
-- Живой TelegramClient pending-логина живёт в RAM (AccountManager._pending);
-- код и пароль 2FA в БД никогда не пишутся — сразу в sign_in.
CREATE TABLE IF NOT EXISTS login_sessions (
    id              TEXT PRIMARY KEY,            -- uuid hex
    user_id         TEXT NOT NULL REFERENCES users(user_id),
    phone           TEXT NOT NULL,               -- нормализованный E.164
    label           TEXT NOT NULL DEFAULT '',    -- заметка из UI (доедет на аккаунт)
    phone_code_hash TEXT NOT NULL DEFAULT '',    -- из send_code_request; стирается
    step            TEXT NOT NULL DEFAULT 'awaiting_code', -- awaiting_code|awaiting_password
    attempts        INTEGER NOT NULL DEFAULT 0,  -- неверный код; >=3 → отмена
    error           TEXT NOT NULL DEFAULT '',    -- текст последней ошибки шага
    expires_at      TEXT NOT NULL,               -- +15 мин; чистит общий GC-цикл
    created_at      TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_login_sessions_user ON login_sessions(user_id);