You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5.4 KiB

База данных децентрализованного чата (chatgui)

Архитектура хранения

Чат децентрализованный — нет центрального сервера. Каждый узел (нода uTun) хранит свою локальную SQLite БД. В ней:

  • Информация о узлах (и о себе) (как подключиться: адреса, порты, ключи — всё из NODEINFO). плюс nickname. - и список узлов = список участников. каждый узел имеет parent node (дерево).
  • История сообщений —

Данные о других узлах поступают из NODEINFO gossip-протокола uTun и синхронизируются в фоне. Сообщения приходят через msg_transport (TCP, локальный IPC). аттачи - загружают и кешируют узлы в соответствии со своими настройками.

Структура таблиц SQLite

local_identity — наш собственный узел (точнее все мои устройства)

между моими устройствами синхронизируется контент - список чатов и сообщения.

CREATE TABLE IF NOT EXISTS nodes (
    id              INTEGER PRIMARY KEY,
    node_id         INTEGER NOT NULL UNIQUE, // id из utun
    name            TEXT NOT NULL, // ник
    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)