-- 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);