172 lines
6.3 KiB
C++
172 lines
6.3 KiB
C++
/* SQLite-представление данных для desktop UI: каналы/сообщения/узлы, локальные имена и диагностика БД.
|
|
* setDb заимствует handle ядра, destructor закрывает свои statement-курсоры, но не SQLite.
|
|
* Перед уничтожением БД закрыть курсоры и сбросить ссылку. Сетевые и подписанные изменения идут через chat_core;
|
|
* методы чтения возвращают самостоятельные Qt-значения. */
|
|
#ifndef DB_MANAGER_H
|
|
#define DB_MANAGER_H
|
|
|
|
#include <sqlite3.h>
|
|
#include <QString>
|
|
#include <QList>
|
|
#include <QMap>
|
|
#include <QObject>
|
|
#include <cstdint>
|
|
|
|
enum class ChannelSortMode { Alphabetical, Recent };
|
|
|
|
struct ChannelRow {
|
|
QString channelId;
|
|
QString name;
|
|
quint64 ownerNodeId = 0;
|
|
QByteArray x25519_pubkey;
|
|
QByteArray x25519_privkey;
|
|
QByteArray ed25519_pubkey;
|
|
QByteArray ed25519_privkey;
|
|
QByteArray signature; // Ed25519 подпись всех полей записи privkey канала
|
|
|
|
qint64 lastReadMsgId = 0;
|
|
qint64 lastPosMsgId = 0;
|
|
qint64 lastMsgAt = 0;
|
|
qint64 createdAt = 0;
|
|
int unreadCount = 0;
|
|
quint64 avatarVersion = 0;
|
|
QByteArray avatarHash;
|
|
};
|
|
|
|
struct MessageRow {
|
|
qint64 id = 0;
|
|
quint64 authorNodeId = 0;
|
|
QString contentType;
|
|
QByteArray data;
|
|
qint64 timestamp = 0;
|
|
uint64_t datahash = 0;
|
|
QByteArray chainHash;
|
|
QByteArray signature;
|
|
QByteArray messageId; // общий ID подписанного сообщения, 32 байта
|
|
QByteArray localAttrs;
|
|
bool isOutgoing = false;
|
|
bool isRead = false;
|
|
};
|
|
|
|
struct AccountRow {
|
|
quint64 nodeId = 0;
|
|
QString displayName;
|
|
QString avatarColor;
|
|
QString avatarLetter;
|
|
bool isContact = true;
|
|
};
|
|
|
|
struct NodeAddr {
|
|
quint64 nodeId = 0;
|
|
int family = 0;
|
|
int protocol = 0; // битмаска: 1=UDP, 2=TCP, 4=REALITY (6=TCP+REALITY)
|
|
QByteArray address;
|
|
quint16 port = 0;
|
|
int rtt = 0;
|
|
int addrType = 0; // ADDR_TYPE_*
|
|
int socketId = 0; // socket_id из ETCP
|
|
};
|
|
|
|
struct IntegrityResult {
|
|
bool ok = false;
|
|
QString message;
|
|
QStringList errors;
|
|
int elapsedMs = 0;
|
|
};
|
|
|
|
struct DmConversation {
|
|
QString convId;
|
|
quint64 peerNodeId = 0;
|
|
QString peerName;
|
|
qint64 lastTs = 0;
|
|
qint64 lastOutSeq = 0;
|
|
qint64 lastInSeq = 0;
|
|
};
|
|
|
|
class DbManager : public QObject {
|
|
Q_OBJECT
|
|
public:
|
|
explicit DbManager(QObject* parent = nullptr);
|
|
~DbManager() override;
|
|
|
|
bool isOpen() const { return m_db != nullptr; }
|
|
void setDb(sqlite3* db) { m_db = db; }
|
|
void setDbPath(const QString& path) { m_dbPath = path; }
|
|
QString dbPath() const { return m_dbPath; }
|
|
quint64 myNodeId() const { return m_myNodeId; }
|
|
void setMyNodeId(quint64 nodeId) { m_myNodeId = nodeId; }
|
|
|
|
/* ── Каналы ── */
|
|
QList<ChannelRow> getChannels(ChannelSortMode sort = ChannelSortMode::Alphabetical) const;
|
|
QByteArray getChannelAvatar(const QString& channelId) const;
|
|
QString getChannelLastMessage(const QString& channelId) const;
|
|
int getUnreadCount(const QString& channelId, qint64 lastReadMsgId) const;
|
|
|
|
/* ── UI state for channel read/scroll ── */
|
|
qint64 getUiStateInt64(const QString& key, qint64 defaultVal = 0) const;
|
|
|
|
/* ── Сообщения (UI) ── */
|
|
QList<MessageRow> getMessages(const QString& chId, int limit = 50) const;
|
|
QList<MessageRow> getMessagesSince(const QString& chId, qint64 sinceId) const;
|
|
MessageRow getMessageById(const QString& chId, qint64 msgId) const;
|
|
|
|
/* ── DM-беседы (UI, read-only) ── */
|
|
QList<DmConversation> getDmConversations() const;
|
|
|
|
/* ── Участники канала (UI) ── */
|
|
void cleanOrphanedPeers(const QString& chId) const;
|
|
QString getDisplayName(quint64 nodeId) const;
|
|
QMap<quint64, QString> getLocalNicknames(const QString& chId) const;
|
|
bool isChannelMember(const QString& chId, quint64 nodeId) const;
|
|
|
|
/* ── Узлы ── */
|
|
QByteArray loadNodeInfo(quint64 nodeId) const;
|
|
QList<quint64> getOnlinePeers() const;
|
|
QByteArray getNodeEdPub(quint64 nodeId) const;
|
|
QList<NodeAddr> getOnlinePeerAddresses(quint64 excludeNodeId) const;
|
|
|
|
/* ── Аккаунты ── */
|
|
QList<AccountRow> getAccounts(bool contactsOnly = false) const;
|
|
AccountRow getAccount(quint64 nodeId) const;
|
|
|
|
/* ── Чтение UI state из SQLite ── */
|
|
QString getUiState(const QString& key) const;
|
|
int getUiStateInt(const QString& key, int defaultVal) const;
|
|
|
|
/* ── Sync-операции (read-only, для gui_bridge/inline) ── */
|
|
QByteArray syncListChannels() const;
|
|
QByteArray syncCount(const QString& chId) const;
|
|
QByteArray syncChainHashAt(const QString& chId, uint32_t pos) const;
|
|
QByteArray syncCursorOpen(const QString& chId);
|
|
QByteArray syncCursorNext(uint32_t cursorId);
|
|
void syncCursorClose(uint32_t cursorId);
|
|
QByteArray syncListPeers(const QString& chId) const;
|
|
|
|
|
|
sqlite3_stmt* prepareOrNull(const char* sql) const;
|
|
static QString colText(sqlite3_stmt* stmt, int col);
|
|
static QByteArray colBlob(sqlite3_stmt* stmt, int col);
|
|
|
|
QString msgTableName(const QString& chId) const;
|
|
QString peersTableName(const QString& chId) const;
|
|
static QString sanitizeTableName(const QString& chId);
|
|
static uint64_t computeDatahash(const QByteArray& data);
|
|
static QByteArray computeChainHash(const QByteArray& prevChain,
|
|
qint64 timestamp, uint64_t datahash);
|
|
QByteArray getLastChainHash(const QString& chId) const;
|
|
|
|
IntegrityResult integrityCheckQuick() const;
|
|
IntegrityResult integrityCheckFull() const;
|
|
IntegrityResult integrityOptimize() const;
|
|
|
|
sqlite3* m_db = nullptr;
|
|
QString m_dbPath;
|
|
quint64 m_myNodeId = 0;
|
|
|
|
/* Кэш открытых курсоров для sync-операций */
|
|
mutable QMap<uint32_t, sqlite3_stmt*> m_cursors;
|
|
mutable uint32_t m_nextCursorId = 1;
|
|
};
|
|
|
|
#endif
|