#include "db_manager.h" #include "../src/debug_ui.h" DbManager::DbManager(const QString& path, QObject* parent) : QObject(parent) { int rc = sqlite3_open(path.toUtf8().constData(), &m_db); if (rc != SQLITE_OK) { GUI_ERROR("Cannot open database: %s", sqlite3_errmsg(m_db)); sqlite3_close(m_db); m_db = nullptr; return; } sqlite3_exec(m_db, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr); initTables(); GUI_INFO("Database opened: %s", path.toUtf8().constData()); } DbManager::~DbManager() { if (m_db) { sqlite3_close(m_db); m_db = nullptr; GUI_INFO("Database closed"); } } void DbManager::initTables() { sqlite3_exec(m_db, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr); const char* sql = "CREATE TABLE IF NOT EXISTS local_identity (" " id INTEGER PRIMARY KEY CHECK (id = 1)," " node_id INTEGER NOT NULL UNIQUE," " name TEXT NOT NULL," " x25519_pubkey BLOB NOT NULL," " ed25519_pubkey BLOB," " created_at TEXT DEFAULT (datetime('now'))," " updated_at TEXT DEFAULT (datetime('now'))" ");" "CREATE TABLE IF NOT EXISTS nodes (" " node_id INTEGER PRIMARY KEY," " name TEXT," " x25519_pubkey BLOB," " ed25519_pubkey BLOB," " hop_count INTEGER DEFAULT 0," " is_online INTEGER DEFAULT 0," " last_seen_ver INTEGER DEFAULT 0," " connectivity_json TEXT," " created_at TEXT DEFAULT (datetime('now'))," " updated_at TEXT DEFAULT (datetime('now'))" ");" "CREATE TABLE IF NOT EXISTS node_addresses (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " node_id INTEGER NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE," " family INTEGER NOT NULL DEFAULT 4," " ip BLOB NOT NULL," " port INTEGER NOT NULL," " addr_type INTEGER NOT NULL DEFAULT 0," " socket_type INTEGER DEFAULT 0," " priority INTEGER DEFAULT 0" ");" "CREATE INDEX IF NOT EXISTS idx_node_addr_node ON node_addresses(node_id);" "CREATE TABLE IF NOT EXISTS node_subnets (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " node_id INTEGER NOT NULL REFERENCES nodes(node_id) ON DELETE CASCADE," " family INTEGER NOT NULL DEFAULT 4," " network BLOB NOT NULL," " prefix_length INTEGER NOT NULL" ");" "CREATE INDEX IF NOT EXISTS idx_subnet_node ON node_subnets(node_id);" "CREATE TABLE IF NOT EXISTS channels (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " channel_id TEXT NOT NULL UNIQUE," " name TEXT NOT NULL," " member_ids_json TEXT NOT NULL," " is_dm INTEGER DEFAULT 0," " last_message TEXT," " last_msg_at TEXT," " created_at TEXT DEFAULT (datetime('now'))" ");" "CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_cid ON channels(channel_id);" "CREATE TABLE IF NOT EXISTS messages (" " 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);"; char* errMsg = nullptr; int rc = sqlite3_exec(m_db, sql, nullptr, nullptr, &errMsg); if (rc != SQLITE_OK) { GUI_ERROR("Failed to create tables: %s", errMsg); sqlite3_free(errMsg); return; } GUI_INFO("Database tables initialized (6 tables, 6 indexes)"); }