etc/rtt: UPSERT node_update_verified; gui: account delegate (rtt+nat subline), context menu member props (admin/supernode), cleanOrphanedPeers, refreshRtt timer 2s
This commit is contained in:
1 parent
c367144776
commit
090acc1f00
14 files changed
+496
-32
No files matched your search
@@ -0,0 +1,168 @@
|
||||
#include "memberpropsdialog.h"
|
||||
#include "../db/db_manager.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
MemberPropsDialog::MemberPropsDialog(DbManager* db, const QString& channelId,
|
||||
quint64 nodeId, const QString& memberName,
|
||||
QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_db(db)
|
||||
, m_channelId(channelId)
|
||||
, m_nodeId(nodeId)
|
||||
{
|
||||
setWindowTitle("Member Properties");
|
||||
setMinimumWidth(340);
|
||||
setModal(true);
|
||||
|
||||
// читаем ed25519_privkey канала
|
||||
auto chs = m_db->getChannels();
|
||||
for (const auto& ch : chs) {
|
||||
if (ch.channelId == channelId) {
|
||||
m_edPrivkey = ch.ed25519_privkey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool haveKey = (m_edPrivkey.size() == 32);
|
||||
|
||||
// читаем текущий adm_tags
|
||||
bool isAdmin = false, isSuper = false;
|
||||
if (m_db->m_db) {
|
||||
QString tbl = m_db->peersTableName(channelId);
|
||||
QByteArray sql = QString(
|
||||
"SELECT adm_tags FROM \"%1\" WHERE node_id=?"
|
||||
).arg(tbl).toUtf8();
|
||||
sqlite3_stmt* st = nullptr;
|
||||
if (sqlite3_prepare_v2(m_db->m_db, sql.constData(), -1, &st, nullptr) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)nodeId);
|
||||
if (sqlite3_step(st) == SQLITE_ROW) {
|
||||
const char* tags = (const char*)sqlite3_column_text(st, 0);
|
||||
if (tags) {
|
||||
if (strstr(tags, "admin=yes")) isAdmin = true;
|
||||
if (strstr(tags, "supernode=yes")) isSuper = true;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
}
|
||||
// страховка: добавить колонки если нет
|
||||
QByteArray alt = QString(
|
||||
"ALTER TABLE \"%1\" ADD COLUMN adm_tags TEXT"
|
||||
).arg(tbl).toUtf8();
|
||||
sqlite3_exec(m_db->m_db, alt.constData(), nullptr, nullptr, nullptr);
|
||||
alt = QString(
|
||||
"ALTER TABLE \"%1\" ADD COLUMN adm_tags_sig BLOB"
|
||||
).arg(tbl).toUtf8();
|
||||
sqlite3_exec(m_db->m_db, alt.constData(), nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(12);
|
||||
layout->setContentsMargins(20, 16, 20, 16);
|
||||
|
||||
auto* title = new QLabel(
|
||||
QString("Node: 0x%1").arg(nodeId, 16, 16, QChar('0')) +
|
||||
(memberName.isEmpty() ? "" : QString(" (%1)").arg(memberName)),
|
||||
this);
|
||||
title->setStyleSheet("font-weight: bold; font-size: 13px;");
|
||||
layout->addWidget(title);
|
||||
|
||||
m_adminCb = new QCheckBox("Admin", this);
|
||||
m_adminCb->setChecked(isAdmin);
|
||||
m_adminCb->setEnabled(haveKey);
|
||||
layout->addWidget(m_adminCb);
|
||||
|
||||
m_superCb = new QCheckBox("Supernode", this);
|
||||
m_superCb->setChecked(isSuper);
|
||||
m_superCb->setEnabled(haveKey);
|
||||
layout->addWidget(m_superCb);
|
||||
|
||||
if (!haveKey) {
|
||||
auto* hint = new QLabel("(requires channel private key)", this);
|
||||
hint->setStyleSheet("color: gray; font-size: 11px;");
|
||||
layout->addWidget(hint);
|
||||
}
|
||||
|
||||
layout->addStretch();
|
||||
|
||||
auto* btnLayout = new QHBoxLayout();
|
||||
btnLayout->addStretch();
|
||||
|
||||
auto* cancelBtn = new QPushButton("Cancel", this);
|
||||
cancelBtn->setFixedWidth(90);
|
||||
cancelBtn->setStyleSheet(
|
||||
"QPushButton { border: 1px solid palette(mid); border-radius: 4px;"
|
||||
" padding: 6px 16px; font-size: 13px; background: palette(button); }"
|
||||
"QPushButton:hover { background: palette(light); }");
|
||||
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
|
||||
btnLayout->addWidget(cancelBtn);
|
||||
|
||||
auto* saveBtn = new QPushButton("Save", this);
|
||||
saveBtn->setFixedWidth(90);
|
||||
saveBtn->setDefault(true);
|
||||
saveBtn->setEnabled(haveKey);
|
||||
saveBtn->setStyleSheet(
|
||||
"QPushButton { border: none; border-radius: 4px;"
|
||||
" padding: 6px 16px; font-size: 13px; font-weight: bold;"
|
||||
" background: #3390EC; color: white; }"
|
||||
"QPushButton:hover { background: #2B7ED4; }"
|
||||
"QPushButton:disabled { background: palette(mid); color: palette(midlight); }");
|
||||
connect(saveBtn, &QPushButton::clicked, this, &MemberPropsDialog::onSave);
|
||||
btnLayout->addWidget(saveBtn);
|
||||
|
||||
layout->addLayout(btnLayout);
|
||||
}
|
||||
|
||||
void MemberPropsDialog::onSave() {
|
||||
if (m_edPrivkey.size() != 32 || !m_db->m_db) return;
|
||||
|
||||
// собираем adm_tags
|
||||
QString tags;
|
||||
if (m_adminCb->isChecked()) tags += "admin=yes";
|
||||
if (m_superCb->isChecked()) {
|
||||
if (!tags.isEmpty()) tags += ",";
|
||||
tags += "supernode=yes";
|
||||
}
|
||||
|
||||
// подписываем: tags || node_id(8 LE)
|
||||
QByteArray msg = tags.toUtf8();
|
||||
uint64_t le = htole64(m_nodeId);
|
||||
msg.append((const char*)&le, 8);
|
||||
|
||||
uint8_t sig[64] = {};
|
||||
EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr,
|
||||
(const uint8_t*)m_edPrivkey.constData(), 32);
|
||||
if (pkey) {
|
||||
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
|
||||
if (mdctx) {
|
||||
size_t siglen = 64;
|
||||
if (EVP_DigestSignInit(mdctx, nullptr, nullptr, nullptr, pkey) == 1)
|
||||
EVP_DigestSign(mdctx, sig, &siglen, (const uint8_t*)msg.constData(), msg.size());
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
}
|
||||
EVP_PKEY_free(pkey);
|
||||
}
|
||||
|
||||
// пишем в БД
|
||||
QString tbl = m_db->peersTableName(m_channelId);
|
||||
QByteArray sql = QString(
|
||||
"UPDATE \"%1\" SET adm_tags=?, adm_tags_sig=?, node_type=? WHERE node_id=?"
|
||||
).arg(tbl).toUtf8();
|
||||
sqlite3_stmt* st = nullptr;
|
||||
if (sqlite3_prepare_v2(m_db->m_db, sql.constData(), -1, &st, nullptr) == SQLITE_OK) {
|
||||
sqlite3_bind_text(st, 1, tags.isEmpty() ? nullptr : tags.toUtf8().constData(),
|
||||
-1, tags.isEmpty() ? SQLITE_STATIC : SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(st, 2, sig, 64, SQLITE_TRANSIENT);
|
||||
int nt = m_superCb->isChecked() ? 4 : 1;
|
||||
sqlite3_bind_int(st, 3, nt);
|
||||
sqlite3_bind_int64(st, 4, (sqlite3_int64)m_nodeId);
|
||||
sqlite3_step(st);
|
||||
sqlite3_finalize(st);
|
||||
}
|
||||
|
||||
accept();
|
||||
}
|
||||
Reference in new issue
Block a user