chatgui: create group, unified logging, fix uasync_post epoll bug
- Add CreateGroupDialog with group name input
- Generate X25519+Ed25519 keypairs, random uint64 channel_id,
Ed25519 signature, write to DB via uasync thread
- chat_core_create_channel(): create msg table, topo_node_sqlite_channel_put()
- GUI_EVT_CHANNEL_UPDATED notification to reload channel list
- Per-category debug config: debug_level= + debug_categories= in [gui]
- Unified logging via debug_config: all threads write to same file/console
- debug_enable_console(1) to mirror file output to stderr
- DEBUG_LEVEL_DISABLED=-1: truly disable category, NONE=0 fallback to global
- Fix uasync_post under epoll: process_posted_tasks was never called
- Fix MainWindow constructor parameter ordering (dbPath vs debugFile)
- debug_ui.h: append mode ('a' instead of 'w')
This commit is contained in:
1 parent
a9a6b6bb10
commit
de7841f33a
26 files changed
+761
-265
No files matched your search
+19
-151
@@ -8,7 +8,6 @@
|
||||
/* ── helpers ── */
|
||||
|
||||
static const char kZero64[64] = {};
|
||||
static const char kZero32[32] = {};
|
||||
|
||||
QString DbManager::sanitizeTableName(const QString& chId) {
|
||||
QString s;
|
||||
@@ -119,28 +118,6 @@ void DbManager::initTables() {
|
||||
" updated_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS nodes ("
|
||||
" node_id INTEGER PRIMARY KEY,"
|
||||
" name TEXT,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_pubkey BLOB,"
|
||||
" last_seen_at INTEGER,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
|
||||
"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 CHECK(family IN (4, 6)),"
|
||||
" protocol INTEGER NOT NULL DEFAULT 1,"
|
||||
" address BLOB NOT NULL,"
|
||||
" port INTEGER NOT NULL CHECK(port > 0 AND port <= 65535),"
|
||||
" rtt INTEGER,"
|
||||
" is_nat INTEGER DEFAULT 0,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
"CREATE INDEX IF NOT EXISTS idx_na_node ON node_addresses(node_id);"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS accounts ("
|
||||
" node_id INTEGER PRIMARY KEY REFERENCES nodes(node_id),"
|
||||
" display_name TEXT NOT NULL,"
|
||||
@@ -150,21 +127,6 @@ void DbManager::initTables() {
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS channels ("
|
||||
" channel_id TEXT PRIMARY KEY,"
|
||||
" name TEXT NOT NULL,"
|
||||
" owner_node_id INTEGER,"
|
||||
" is_dm INTEGER DEFAULT 0,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" x25519_privkey BLOB,"
|
||||
" ed25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_privkey BLOB,"
|
||||
" signature BLOB NOT NULL,"
|
||||
" last_read_msg_id INTEGER,"
|
||||
" last_pos_msg_id INTEGER,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS ui_state ("
|
||||
" key TEXT PRIMARY KEY,"
|
||||
" value TEXT"
|
||||
@@ -213,12 +175,11 @@ bool DbManager::createChannel(const QString& chId, const QString& name,
|
||||
quint64 ownerNodeId,
|
||||
const QByteArray& x25519pub, const QByteArray& x25519priv,
|
||||
const QByteArray& ed25519pub, const QByteArray& ed25519priv) {
|
||||
GUI_INFO("createChannel: chId='%s' name='%s'", chId.toUtf8().constData(), name.toUtf8().constData());
|
||||
QByteArray sig = kZero64; Q_UNUSED(sig);
|
||||
GUI_INFO("createChannel (msg table only): chId='%s' name='%s'", chId.toUtf8().constData(), name.toUtf8().constData());
|
||||
(void)ownerNodeId; (void)x25519pub; (void)x25519priv; (void)ed25519pub; (void)ed25519priv;
|
||||
|
||||
QString msgt = msgTableName(chId), pt = peersTableName(chId);
|
||||
QString msgt = msgTableName(chId);
|
||||
|
||||
/* per-channel message table (DDL — до INSERT, чтобы не ломать транзакцию) */
|
||||
QString csql = QStringLiteral(
|
||||
"CREATE TABLE IF NOT EXISTS \"%1\" ("
|
||||
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
@@ -237,41 +198,7 @@ bool DbManager::createChannel(const QString& chId, const QString& name,
|
||||
csql = QStringLiteral("CREATE INDEX IF NOT EXISTS idx_%1_ts ON \"%1\"(timestamp, datahash)").arg(msgt);
|
||||
sqlite3_exec(m_db, csql.toUtf8().constData(), nullptr, nullptr, nullptr);
|
||||
|
||||
/* per-channel peers table */
|
||||
csql = QStringLiteral(
|
||||
"CREATE TABLE IF NOT EXISTS \"%1\" ("
|
||||
" node_id INTEGER NOT NULL,"
|
||||
" join_sig BLOB NOT NULL,"
|
||||
" creator_sig BLOB,"
|
||||
" comment TEXT,"
|
||||
" joined_at INTEGER DEFAULT (unixepoch()),"
|
||||
" PRIMARY KEY (node_id))").arg(pt);
|
||||
sqlite3_exec(m_db, csql.toUtf8().constData(), nullptr, nullptr, nullptr);
|
||||
|
||||
/* вставка записи channels (после DDL, корректно внутри транзакции) */
|
||||
sqlite3_stmt* stmt = prepareOrNull(
|
||||
"INSERT INTO channels(channel_id,name,owner_node_id,is_dm,"
|
||||
" x25519_pubkey,x25519_privkey,ed25519_pubkey,ed25519_privkey,signature)"
|
||||
" VALUES(?,?,?,0,?,?,?,?,?)");
|
||||
if (!stmt) return false;
|
||||
sqlite3_bind_text(stmt, 1, chId.toUtf8().constData(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 2, name.toUtf8().constData(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int64(stmt, 3, (sqlite3_int64)ownerNodeId);
|
||||
sqlite3_bind_blob(stmt, 4, x25519pub.isEmpty() ? kZero32 : x25519pub.constData(),
|
||||
x25519pub.isEmpty() ? 32 : x25519pub.size(), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(stmt, 5, x25519priv.isEmpty() ? nullptr : x25519priv.constData(),
|
||||
x25519priv.size(), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(stmt, 6, ed25519pub.isEmpty() ? kZero32 : ed25519pub.constData(),
|
||||
ed25519pub.isEmpty() ? 32 : ed25519pub.size(), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(stmt, 7, ed25519priv.isEmpty() ? nullptr : ed25519priv.constData(),
|
||||
ed25519priv.size(), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(stmt, 8, sig.constData(), sig.size(), SQLITE_TRANSIENT);
|
||||
bool ok = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
if (!ok) { GUI_ERROR("createChannel %s failed: %s", chId.toUtf8().constData(), sqlite3_errmsg(m_db)); return false; }
|
||||
|
||||
GUI_INFO("Channel created: %s (msg=%s, peers=%s)",
|
||||
chId.toUtf8().constData(), msgt.toUtf8().constData(), pt.toUtf8().constData());
|
||||
GUI_INFO("Message table ensured: %s", msgt.toUtf8().constData());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -284,6 +211,7 @@ bool DbManager::deleteChannel(const QString& chId) {
|
||||
sqlite3_bind_text(stmt, 1, chId.toUtf8().constData(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
GUI_INFO("Channel deleted: %s", chId.toUtf8().constData());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -349,73 +277,32 @@ bool DbManager::insertMessage(const QString& chId, quint64 nodeId,
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* ── peers ── */
|
||||
/* ── peers (запись теперь через topo_node_sqlite) ── */
|
||||
|
||||
bool DbManager::addPeer(const QString& chId, quint64 nodeId,
|
||||
const QByteArray& joinSig, const QByteArray& creatorSig,
|
||||
const QString& comment) {
|
||||
QString sql = QStringLiteral(
|
||||
"INSERT OR REPLACE INTO \"%1\"(node_id, join_sig, creator_sig, comment)"
|
||||
" VALUES(?,?,?,?)").arg(peersTableName(chId));
|
||||
sqlite3_stmt* stmt = prepareOrNull(sql.toUtf8().constData());
|
||||
if (!stmt) return false;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)nodeId);
|
||||
sqlite3_bind_blob(stmt, 2, joinSig.constData(), joinSig.size(), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(stmt, 3, creatorSig.isEmpty() ? nullptr : creatorSig.constData(),
|
||||
creatorSig.size(), SQLITE_TRANSIENT);
|
||||
if (!comment.isEmpty())
|
||||
sqlite3_bind_text(stmt, 4, comment.toUtf8().constData(), -1, SQLITE_TRANSIENT);
|
||||
else
|
||||
sqlite3_bind_null(stmt, 4);
|
||||
bool ok = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DbManager::removePeer(const QString& chId, quint64 nodeId) {
|
||||
QString sql = QStringLiteral(
|
||||
"DELETE FROM \"%1\" WHERE node_id=?").arg(peersTableName(chId));
|
||||
sqlite3_stmt* stmt = prepareOrNull(sql.toUtf8().constData());
|
||||
if (!stmt) return false;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)nodeId);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
(void)chId; (void)nodeId; (void)joinSig; (void)creatorSig; (void)comment;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ── nodes ── */
|
||||
bool DbManager::removePeer(const QString& chId, quint64 nodeId) {
|
||||
(void)chId; (void)nodeId;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ── nodes (запись теперь через topo_node_sqlite) ── */
|
||||
|
||||
bool DbManager::upsertNode(quint64 nodeId, const QString& name,
|
||||
const QByteArray& x25519pub, const QByteArray& ed25519pub) {
|
||||
sqlite3_stmt* stmt = prepareOrNull(
|
||||
"INSERT OR REPLACE INTO nodes(node_id, name, x25519_pubkey, ed25519_pubkey, last_seen_at)"
|
||||
" VALUES(?,?,?,?,unixepoch())");
|
||||
if (!stmt) return false;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)nodeId);
|
||||
sqlite3_bind_text(stmt, 2, name.toUtf8().constData(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(stmt, 3, x25519pub.constData(), x25519pub.size(), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(stmt, 4, ed25519pub.isEmpty() ? nullptr : ed25519pub.constData(),
|
||||
ed25519pub.size(), SQLITE_TRANSIENT);
|
||||
bool ok = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
return ok;
|
||||
(void)nodeId; (void)name; (void)x25519pub; (void)ed25519pub;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DbManager::addNodeAddress(quint64 nodeId, int family, int protocol,
|
||||
const QByteArray& addr, quint16 port, bool isNat) {
|
||||
sqlite3_stmt* stmt = prepareOrNull(
|
||||
"INSERT OR IGNORE INTO node_addresses(node_id, family, protocol, address, port, is_nat)"
|
||||
" VALUES(?,?,?,?,?,?)");
|
||||
if (!stmt) return false;
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)nodeId);
|
||||
sqlite3_bind_int(stmt, 2, family);
|
||||
sqlite3_bind_int(stmt, 3, protocol);
|
||||
sqlite3_bind_blob(stmt, 4, addr.constData(), addr.size(), SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int(stmt, 5, port);
|
||||
sqlite3_bind_int(stmt, 6, isNat ? 1 : 0);
|
||||
bool ok = sqlite3_step(stmt) == SQLITE_DONE;
|
||||
sqlite3_finalize(stmt);
|
||||
return ok;
|
||||
(void)nodeId; (void)family; (void)protocol; (void)addr; (void)port; (void)isNat;
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray DbManager::loadNodeInfo(quint64 nodeId) const {
|
||||
@@ -755,7 +642,7 @@ void DbManager::setNodeOnline(quint64 nodeId, bool online) {
|
||||
/* ── seed stub data ── */
|
||||
|
||||
void DbManager::seedStubData() {
|
||||
GUI_INFO("Seeding stub data...");
|
||||
GUI_INFO("Seeding stub data (accounts only)...");
|
||||
sqlite3_exec(m_db, "BEGIN", nullptr, nullptr, nullptr);
|
||||
|
||||
unsigned char pub_dummy[32] = {};
|
||||
@@ -773,14 +660,6 @@ void DbManager::seedStubData() {
|
||||
{5, "Eve", "E", "#00B894"},
|
||||
};
|
||||
for (auto& a : accs) {
|
||||
ins = prepareOrNull("INSERT OR IGNORE INTO nodes(node_id,name,x25519_pubkey,ed25519_pubkey,last_seen_at)"
|
||||
" VALUES(?,?,?,?,unixepoch())");
|
||||
sqlite3_bind_int64(ins, 1, (sqlite3_int64)a.id);
|
||||
sqlite3_bind_text(ins, 2, a.name, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(ins, 3, pub_dummy, 32, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(ins, 4, pub_dummy, 32, SQLITE_TRANSIENT);
|
||||
sqlite3_step(ins); sqlite3_finalize(ins);
|
||||
|
||||
ins = prepareOrNull("INSERT OR IGNORE INTO accounts(node_id,display_name,avatar_color,avatar_letter)"
|
||||
" VALUES(?,?,?,?)");
|
||||
sqlite3_bind_int64(ins, 1, (sqlite3_int64)a.id);
|
||||
@@ -790,17 +669,6 @@ void DbManager::seedStubData() {
|
||||
sqlite3_step(ins); sqlite3_finalize(ins);
|
||||
}
|
||||
|
||||
/* channels (1 демо-канал) */
|
||||
QByteArray dummyPub(32, '\0'), dummySig(64, '\0'), dummyCH(32, '\0');
|
||||
createChannel("ch:general", "#general", 0, dummyPub, QByteArray(), dummyPub, QByteArray());
|
||||
|
||||
/* peers */
|
||||
for (auto& a : accs) {
|
||||
addPeer("ch:general", a.id, dummySig, dummySig, a.name);
|
||||
}
|
||||
|
||||
/* messages — 0 для простоты, добавим позже в seed v2 */
|
||||
|
||||
sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
|
||||
GUI_INFO("Seed data inserted (5 accounts, 1 channel)");
|
||||
GUI_INFO("Seed data inserted (5 accounts)");
|
||||
}
|
||||
Reference in new issue
Block a user