5.5 KiB
База данных децентрализованного чата (chatgui)
Сторона сервиса (gui):
Архитектура хранения
Чат децентрализованный — нет центрального сервера. Каждый узел (нода uTun) хранит свою локальную SQLite БД. В ней:
- Информация о узлах (и о себе) (как подключиться: адреса, порты, ключи — всё из NODEINFO). плюс nickname. - и список узлов = список участников. каждый узел имеет parent node (дерево).
- История сообщений —
Данные о других узлах поступают из NODEINFO gossip-протокола uTun и синхронизируются в фоне.
Сообщения приходят через msg_transport (TCP, локальный IPC).
аттачи - загружают и кешируют узлы в соответствии со своими настройками.
Структура таблиц SQLite
local_identity — наш собственный узел
my_nodes — все мои устройства)
между моими устройствами синхронизируется контент - список чатов и сообщения. ... todo
CREATE TABLE IF NOT EXISTS nodes (
id INTEGER PRIMARY KEY,
node_id INTEGER NOT NULL UNIQUE, // id из utun
name TEXT NOT NULL, // ник
pubkey (ed+x)
last_seen INTEGER DEFAULT 0,
online_rating INTEGER DEFAULT 0, // рейтинг доступности узла. чем больше число тем чаще узел онлайн
speed_rating INTEGER DEFAULT 0, // рейтинг скорости обмена с узлом
rtt_rating INTEGER DEFAULT 0, // rtt до узла (через промежуточные узлы если есть)
connectivity_json TEXT, // как связаться с узлом - адреса, (пока пусто)
parent_node_id INTEGER DEFAULT NULL, // каждая нода должна иметь родителя (кроме корневой)
main_node_id INTEGER DEFAULT NULL, // если несколько устройств у клиента, id следующей ноды (циклический список)
created_at TEXT DEFAULT (datetime('now')),
);
channels — чаты (группы узлов)
CREATE TABLE IF NOT EXISTS channels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
last_msg_at TEXT,
);
messages — список пользователей в чате
CREATE TABLE IF NOT EXISTS channel_members (
channel_id INTEGER,
node_id INTEGER, // если несколько устройств то ноды всех стройств
);
messages — история сообщений (для каждой группы создаем новую таблицу)
CREATE TABLE IF NOT EXISTS messages_ch<n> (
id INTEGER PRIMARY KEY AUTOINCREMENT,
channel_id TEXT NOT NULL REFERENCES channels(channel_id) ON DELETE CASCADE,
author_node_id INTEGER NOT NULL,
content TEXT NOT NULL,
content_type TEXT DEFAULT 'text/plain',
timestamp INTEGER NOT NULL,
local_seq INTEGER DEFAULT 0,
signature BLOB,
is_outgoing INTEGER DEFAULT 0,
is_read INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_msg_channel_time ON messages(channel_id, timestamp);
CREATE INDEX IF NOT EXISTS idx_msg_author ON messages(author_node_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_msg_dedup ON messages(channel_id, author_node_id, timestamp);
Пояснение: каждое сообщение привязано к каналу. author_node_id — кто автор
(из nodes.node_id). timestamp — unix время в миллисекундах по часам отправителя
(в децентрализованной системе нет глобальных часов, но для порядка в UI достаточно).
local_seq — монотонно возрастающий номер в пределах канала для детерминированной
сортировки при одинаковых timestamp.
Дедупликация: сообщение идентифицируется тройкой (channel_id, author_node_id, timestamp). Уникальный индекс idx_msg_dedup предотвращает дубликаты при
повторной доставке через разные пути.
| Поле | Тип | Описание |
|---|---|---|
channel_id |
TEXT | Ссылка на канал |
author_node_id |
INTEGER | Кто отправил (node_id) |
content |
TEXT | Текст сообщения |
content_type |
TEXT | MIME-тип: text/plain, в будущем image/png и т.д. |
timestamp |
INTEGER | Unix timestamp (мс) — когда отправлено |
local_seq |
INTEGER | Локальный порядковый номер в канале |
signature |
BLOB | Ed25519 подпись (64 байта), NULL если без подписи |
is_outgoing |
INTEGER | 1 = отправлено нами |
is_read |
INTEGER | 1 = прочитано (для галочек в UI) |