1
0
forked from BRT/arc

- Add database migration for screenshot column in general_logs.

- Implement `cleanupOldData` in `DatabaseManager` to remove outdated entries and optimize storage.
- Introduce `unlockScreen` in `AdbUtils` and integrate it into common scripts for screen unlocking.
- Enhance `GeneralLogDAO` to support screenshot storage and retrieval.
- Expand `FileLogWindow` with screenshot viewer and horizontal layout.
- Add device ID migration logic in `BankProfileDAO` and `MaterialDAO`.
- Improve error handling in `EventHandler` by saving and logging screenshots for critical events.
This commit is contained in:
slava 2026-03-31 13:08:02 +07:00
parent de8c2e84d0
commit a6509b5653
18 changed files with 245 additions and 65 deletions

View File

@ -469,3 +469,33 @@ QSet<QString> AdbUtils::getListApps(const QString &deviceId) {
}
return apps;
}
void AdbUtils::unlockScreen(const QString &deviceId, const int screenWidth, const int screenHeight) {
const QString adb = adbPath();
// Проверяем, включён ли экран (mWakefulness=Awake)
QString powerState;
startProcess(adb, {"-s", deviceId, "shell", "dumpsys", "power"}, &powerState);
const bool isAwake = powerState.contains("mWakefulness=Awake");
if (!isAwake) {
// Включаем экран
startProcess(adb, {"-s", deviceId, "shell", "input", "keyevent", "KEYCODE_WAKEUP"});
QThread::msleep(500);
qDebug() << "[AdbUtils] unlockScreen: woke up device" << deviceId;
}
// Проверяем, залочен ли экран (mDreamingSleepToken / mShowingLockscreen)
QString wmState;
startProcess(adb, {"-s", deviceId, "shell", "dumpsys", "window"}, &wmState);
const bool isLocked = wmState.contains("mDreamingSleepToken") || wmState.contains("mShowingLockscreen=true")
|| wmState.contains("StatusBar");
if (isLocked) {
// Свайп вверх для разблокировки
const int x = screenWidth / 2;
makeSwipe(deviceId, x, screenHeight * 3 / 4, x, screenHeight / 4);
QThread::msleep(500);
qDebug() << "[AdbUtils] unlockScreen: swiped to unlock" << deviceId;
}
}

View File

@ -44,6 +44,8 @@ public:
static bool goBack(const QString &deviceId);
static void unlockScreen(const QString &deviceId, int screenWidth, int screenHeight);
static QSet<QString> getListApps(const QString &deviceId);
private:

View File

@ -67,6 +67,9 @@ bool CommonScript::runAppAndGoToHomeScreen(
const int width,
const int height
) {
// Разблокируем экран перед запуском
AdbUtils::unlockScreen(deviceId, width, height);
// Убить приложение если запущено
if (const QString pid = AdbUtils::tryToGetRunningAppPid(deviceId, packageName); !pid.isEmpty()) {
qDebug() << "[Black] Process already running, pid:" << pid;

View File

@ -59,6 +59,9 @@ bool CommonScript::runAppAndGoToHomeScreen(
const int width,
const int height
) {
// Разблокируем экран перед запуском
AdbUtils::unlockScreen(deviceId, width, height);
// Запустить устройство
if (const QString pid = AdbUtils::tryToGetRunningAppPid(deviceId, packageName); !pid.isEmpty()) {
qDebug() << "Process already running, pid: " << pid;

View File

@ -212,6 +212,10 @@ void DatabaseManager::initSchema() {
q.exec("CREATE INDEX IF NOT EXISTS idx_general_logs_id ON general_logs (id DESC)");
// Миграция: добавляем колонку screenshot если её нет
q.exec("ALTER TABLE general_logs ADD COLUMN screenshot BLOB");
// Ошибка = колонка уже существует — это нормально
q.exec(R"(CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT
@ -220,6 +224,49 @@ void DatabaseManager::initSchema() {
db.commit();
}
void DatabaseManager::cleanupOldData(int days) {
QSqlDatabase db = database();
QSqlQuery q(db);
const QString cutoff = QDateTime::currentDateTimeUtc().addDays(-days).toString(Qt::ISODate);
db.transaction();
// general_logs
q.prepare("DELETE FROM general_logs WHERE timestamp < :cutoff");
q.bindValue(":cutoff", cutoff);
if (q.exec()) {
qDebug() << "[cleanup] general_logs: deleted" << q.numRowsAffected() << "rows older than" << days << "days";
}
// app_logs
q.prepare("DELETE FROM app_logs WHERE timestamp < :cutoff");
q.bindValue(":cutoff", cutoff);
if (q.exec()) {
qDebug() << "[cleanup] app_logs: deleted" << q.numRowsAffected() << "rows older than" << days << "days";
}
// events (завершённые/ошибочные)
q.prepare("DELETE FROM events WHERE timestamp < :cutoff AND status IN ('complete', 'error')");
q.bindValue(":cutoff", cutoff);
if (q.exec()) {
qDebug() << "[cleanup] events: deleted" << q.numRowsAffected() << "rows older than" << days << "days";
}
// transactions
q.prepare("DELETE FROM transactions WHERE timestamp < :cutoff");
q.bindValue(":cutoff", cutoff);
if (q.exec()) {
qDebug() << "[cleanup] transactions: deleted" << q.numRowsAffected() << "rows older than" << days << "days";
}
db.commit();
// Освобождаем место
q.exec("VACUUM");
qDebug() << "[cleanup] VACUUM done";
}
void DatabaseManager::closeConnection() {
QMutexLocker<QRecursiveMutex> locker(&mutex);
if (const QString name = connectionName(); connectionPool.contains(name)) {

View File

@ -12,6 +12,7 @@ public:
QSqlDatabase database();
void initSchema();
void cleanupOldData(int days = 5);
void closeConnection();
private:

View File

@ -10,22 +10,24 @@ bool BankProfileDAO::upsertApplication(const BankProfileInfo &info) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
INSERT INTO bank_profiles (id, device_id, pin_code, name, code, package, status, comment, install, pin_code_checked)
VALUES (:id, :device_id, :pin_code, :name, :code, :package, :status, :comment, :install, :pin_code_checked)
INSERT INTO bank_profiles (device_id, pin_code, name, code, package, status, comment, install, pin_code_checked,
bank_profile_id, full_name, phone)
VALUES (:device_id, :pin_code, :name, :code, :package, :status, :comment, :install, :pin_code_checked,
:bank_profile_id, :full_name, :phone)
ON CONFLICT(device_id, code)
DO UPDATE SET
device_id = excluded.device_id,
pin_code = excluded.pin_code,
pin_code = CASE WHEN excluded.pin_code != '' THEN excluded.pin_code ELSE bank_profiles.pin_code END,
name = excluded.name,
code = excluded.code,
package = excluded.package,
package = CASE WHEN excluded.package != '' THEN excluded.package ELSE bank_profiles.package END,
status = excluded.status,
comment = excluded.comment,
install = excluded.install,
pin_code_checked = excluded.pin_code_checked
pin_code_checked = excluded.pin_code_checked,
bank_profile_id = CASE WHEN excluded.bank_profile_id != '' THEN excluded.bank_profile_id ELSE bank_profiles.bank_profile_id END,
full_name = CASE WHEN excluded.full_name != '' THEN excluded.full_name ELSE bank_profiles.full_name END,
phone = CASE WHEN excluded.phone != '' THEN excluded.phone ELSE bank_profiles.phone END
)");
query.bindValue(":id", info.id);
query.bindValue(":device_id", info.deviceId);
query.bindValue(":pin_code", info.pinCode);
query.bindValue(":name", info.name);
@ -35,6 +37,9 @@ bool BankProfileDAO::upsertApplication(const BankProfileInfo &info) {
query.bindValue(":comment", info.comment);
query.bindValue(":install", info.install ? "yes" : "no");
query.bindValue(":pin_code_checked", info.pinCodeChecked ? "yes" : "no");
query.bindValue(":bank_profile_id", info.bankProfileId);
query.bindValue(":full_name", info.fullName);
query.bindValue(":phone", info.phone);
if (!query.exec()) {
qWarning() << "Ошибка при вставке/обновлении application:" << query.lastError().text();
@ -367,3 +372,16 @@ bool BankProfileDAO::checkInstalledApps(const QString &deviceId, const QSet<QStr
return true;
}
bool BankProfileDAO::migrateDeviceId(const QString &oldDeviceId, const QString &newDeviceId) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare("UPDATE bank_profiles SET device_id = :new_id WHERE device_id = :old_id");
query.bindValue(":new_id", newDeviceId);
query.bindValue(":old_id", oldDeviceId);
if (!query.exec()) {
qWarning() << "[BankProfileDAO] migrateDeviceId failed:" << query.lastError().text();
return false;
}
qDebug() << "[BankProfileDAO] migrated" << query.numRowsAffected() << "profiles from" << oldDeviceId << "to" << newDeviceId;
return true;
}

View File

@ -47,4 +47,6 @@ public:
[[nodiscard]] static bool activateAppsForDevice(const QString &deviceId);
[[nodiscard]] static bool deactivateAppsForDevice(const QString &deviceId);
[[nodiscard]] static bool migrateDeviceId(const QString &oldDeviceId, const QString &newDeviceId);
};

View File

@ -7,15 +7,21 @@
#include "time/DateUtils.h"
bool GeneralLogDAO::insert(const QString &level, const QString &source, const QString &message) {
return insert(level, source, message, {});
}
bool GeneralLogDAO::insert(const QString &level, const QString &source, const QString &message,
const QByteArray &screenshot) {
QSqlQuery q(DatabaseManager::instance().database());
q.prepare(R"(
INSERT INTO general_logs (level, source, message, timestamp)
VALUES (:level, :source, :message, :timestamp)
INSERT INTO general_logs (level, source, message, timestamp, screenshot)
VALUES (:level, :source, :message, :timestamp, :screenshot)
)");
q.bindValue(":level", level);
q.bindValue(":source", source);
q.bindValue(":message", message);
q.bindValue(":timestamp", DateUtils::toUtcString(QDateTime::currentDateTimeUtc()));
q.bindValue(":screenshot", screenshot.isEmpty() ? QVariant(QMetaType(QMetaType::QByteArray)) : screenshot);
return q.exec();
}
@ -36,7 +42,7 @@ QList<GeneralLogEntry> GeneralLogDAO::getLogs(int page, int pageSize, const QStr
QList<GeneralLogEntry> result;
QSqlQuery q(DatabaseManager::instance().database());
q.prepare(
"SELECT id, level, source, message, timestamp FROM general_logs"
"SELECT id, level, source, message, timestamp, screenshot FROM general_logs"
+ buildWhereClause(levels)
+ " ORDER BY id DESC LIMIT :limit OFFSET :offset"
);
@ -51,6 +57,7 @@ QList<GeneralLogEntry> GeneralLogDAO::getLogs(int page, int pageSize, const QStr
e.source = q.value("source").toString();
e.message = q.value("message").toString();
e.timestamp = DateUtils::fromUtcString(q.value("timestamp").toString());
e.screenshot = q.value("screenshot").toByteArray();
result.append(e);
}
return result;

View File

@ -10,11 +10,14 @@ struct GeneralLogEntry {
QString source;
QString message;
QDateTime timestamp;
QByteArray screenshot;
};
class GeneralLogDAO {
public:
static bool insert(const QString &level, const QString &source, const QString &message);
static bool insert(const QString &level, const QString &source, const QString &message,
const QByteArray &screenshot);
[[nodiscard]] static QList<GeneralLogEntry> getLogs(int page, int pageSize,
const QStringList &levels = {});

View File

@ -19,17 +19,18 @@ bool MaterialDAO::upsertAccount(const MaterialInfo &info) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
INSERT INTO materials (device_id, app_code, last_numbers, card_number, amount, update_time, status, description, currency)
VALUES (:device_id, :app_code, :last_numbers, :card_number, :amount, :update_time, :status, :description, :currency)
INSERT INTO materials (device_id, app_code, last_numbers, card_number, amount, update_time, status, description, currency, material_id)
VALUES (:device_id, :app_code, :last_numbers, :card_number, :amount, :update_time, :status, :description, :currency, :material_id)
ON CONFLICT(device_id, app_code, last_numbers)
DO UPDATE SET
amount = excluded.amount,
update_time = excluded.update_time,
last_numbers = excluded.last_numbers,
card_number = excluded.card_number,
card_number = CASE WHEN excluded.card_number != '' THEN excluded.card_number ELSE materials.card_number END,
status = excluded.status,
description = excluded.description,
currency = excluded.currency
currency = excluded.currency,
material_id = CASE WHEN excluded.material_id != '' THEN excluded.material_id ELSE materials.material_id END
)");
query.bindValue(":device_id", info.deviceId);
@ -41,6 +42,7 @@ bool MaterialDAO::upsertAccount(const MaterialInfo &info) {
query.bindValue(":amount", info.amount);
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":status", materialStatusToString(info.status));
query.bindValue(":material_id", info.materialId);
if (!query.exec()) {
qWarning() << "Ошибка при вставке/обновлении account:" << query.lastError().text();
@ -289,3 +291,16 @@ MaterialInfo MaterialDAO::findByMaterialId(const QString &materialId) {
return {};
}
bool MaterialDAO::migrateDeviceId(const QString &oldDeviceId, const QString &newDeviceId) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare("UPDATE materials SET device_id = :new_id WHERE device_id = :old_id");
query.bindValue(":new_id", newDeviceId);
query.bindValue(":old_id", oldDeviceId);
if (!query.exec()) {
qWarning() << "[MaterialDAO] migrateDeviceId failed:" << query.lastError().text();
return false;
}
qDebug() << "[MaterialDAO] migrated" << query.numRowsAffected() << "materials from" << oldDeviceId << "to" << newDeviceId;
return true;
}

View File

@ -36,4 +36,6 @@ public:
[[nodiscard]] static QStringList getActiveMaterialIds();
[[nodiscard]] static QStringList getActiveMaterialIdsExcludingDevices(const QSet<QString> &excludeDevices);
[[nodiscard]] static bool migrateDeviceId(const QString &oldDeviceId, const QString &newDeviceId);
};

View File

@ -268,44 +268,18 @@ int main(int argc, char *argv[]) {
qInstallMessageHandler(messageHandler);
// Проверяем наличие файла БД, если нет — предлагаем загрузить
// Создаём директорию для БД, если её нет (initSchema создаст файл автоматически)
{
QSettings settings("config.ini", QSettings::IniFormat);
QString dbPath = settings.value("db/dbPath").toString();
if (dbPath.startsWith("~"))
dbPath.replace(0, 1, QDir::homePath());
if (!QFile::exists(dbPath)) {
QMessageBox msgBox;
msgBox.setWindowTitle("База данных не найдена");
msgBox.setText("Файл базы данных не найден:\n" + dbPath);
QPushButton *selectBtn = msgBox.addButton("Выбрать файл", QMessageBox::AcceptRole);
QPushButton *createBtn = msgBox.addButton("Создать новую", QMessageBox::ActionRole);
QPushButton *exitBtn = msgBox.addButton("Выход", QMessageBox::RejectRole);
msgBox.setDefaultButton(selectBtn);
msgBox.exec();
if (msgBox.clickedButton() == selectBtn) {
QString selected = QFileDialog::getOpenFileName(
nullptr, "Выберите файл базы данных", QDir::homePath(), "SQLite (*.db *.sqlite);;Все файлы (*)");
if (selected.isEmpty()) {
return 0;
}
QDir().mkpath(QFileInfo(dbPath).absolutePath());
if (!QFile::copy(selected, dbPath)) {
QMessageBox::critical(nullptr, "Ошибка", "Не удалось скопировать файл базы данных.");
return 1;
}
} else if (msgBox.clickedButton() == createBtn) {
// Просто продолжаем — initSchema создаст новую БД
} else {
return 0;
}
}
}
// Миграции БД
DatabaseManager::instance().initSchema();
DatabaseManager::instance().cleanupOldData(5);
dbReady.store(true);
// 1. Если есть api_key — логинимся и проверяем/создаём desktop

View File

@ -337,8 +337,16 @@ void DeviceScreener::start() {
} else {
// Маппинг с восстановленным устройством — только по name (точное совпадение)
const DeviceInfo restored = DeviceDAO::findByName(device.name);
qDebug() << "[DeviceScreener] findByName(" << device.name << ") →"
<< "id:" << restored.id << "apiId:" << restored.apiId
<< "match:" << (!restored.id.isEmpty() && restored.id != device.id && !restored.apiId.isEmpty());
if (!restored.id.isEmpty() && restored.id != device.id && !restored.apiId.isEmpty()) {
device.apiId = restored.apiId;
// Сначала создаём новое устройство (FK target), затем мигрируем, затем удаляем старое
DeviceDAO::upsertDevice(device);
const bool bpOk = BankProfileDAO::migrateDeviceId(restored.id, device.id);
const bool matOk = MaterialDAO::migrateDeviceId(restored.id, device.id);
qDebug() << "[DeviceScreener] migration result: bp=" << bpOk << "mat=" << matOk;
DeviceDAO::deleteDevice(restored.id);
qDebug() << "[DeviceScreener] matched restored device:" << restored.id << "->" << device.id
<< "apiId:" << device.apiId;

View File

@ -18,6 +18,7 @@
#include "dao/TransactionDAO.h"
#include "db/BankProfileInfo.h"
#include "adb/AdbUtils.h"
#include "dao/GeneralLogDAO.h"
#include "net/NetworkService.h"
#include "black/GetLastTransactionsScript.h"
#include "black/LoginAndCheckAccountsScript.h"
@ -256,14 +257,20 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
} else {
if (newStatus == EventStatus::Error) {
// Сохраняем скриншот устройства при ошибке
QByteArray screenshot;
if (!event.deviceId.isEmpty()) {
const DeviceInfo device = DeviceDAO::getDeviceById(event.deviceId);
const QString adbSerial = device.adbSerial.isEmpty() ? event.deviceId : device.adbSerial;
const QByteArray screenshot = AdbUtils::captureScreenshotBytes(adbSerial);
screenshot = AdbUtils::captureScreenshotBytes(adbSerial);
if (!screenshot.isEmpty()) {
EventDAO::updateScreenshot(event.id, screenshot);
}
}
// Дублируем ошибку в general_logs со скриншотом
const QString logMsg = QString("[%1] %2 bank=%3: %4")
.arg(eventTypeToString(event.type), event.externalId, event.bankName, result);
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg, screenshot);
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, result);
EventDAO::markSentToServer(event.id);
}

View File

@ -11,6 +11,7 @@
#include <QNetworkReply>
#include <QProcess>
#include <QTemporaryFile>
#include <QPixmap>
#include <QVBoxLayout>
#include "dao/EventDAO.h"
@ -25,7 +26,7 @@ static constexpr int COL_MESSAGE = 4;
FileLogWindow::FileLogWindow(QWidget *parent) : QDialog(parent) {
setWindowTitle("Логи");
resize(1100, 600);
resize(1200, 650);
m_table = new QTableWidget(this);
m_table->setColumnCount(5);
@ -40,6 +41,19 @@ FileLogWindow::FileLogWindow(QWidget *parent) : QDialog(parent) {
m_table->setColumnWidth(COL_LEVEL, 80);
m_table->setColumnWidth(COL_SOURCE, 250);
// Панель скриншота
m_screenshotLabel = new QLabel(this);
m_screenshotLabel->setAlignment(Qt::AlignCenter);
m_screenshotLabel->setMinimumWidth(300);
m_screenshotLabel->setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc;");
m_screenshotLabel->setText("Выберите запись\nдля просмотра скриншота");
auto *splitter = new QSplitter(Qt::Horizontal, this);
splitter->addWidget(m_table);
splitter->addWidget(m_screenshotLabel);
splitter->setStretchFactor(0, 3);
splitter->setStretchFactor(1, 1);
m_showAllCheck = new QCheckBox("Показать все (включая DEBUG/INFO)", this);
m_prevBtn = new QPushButton("", this);
@ -62,7 +76,7 @@ FileLogWindow::FileLogWindow(QWidget *parent) : QDialog(parent) {
paginationLayout->addWidget(m_sendBtn);
auto *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(m_table, 1);
mainLayout->addWidget(splitter, 1);
mainLayout->addLayout(paginationLayout);
connect(m_prevBtn, &QPushButton::clicked, this, [this]() {
@ -79,6 +93,9 @@ FileLogWindow::FileLogWindow(QWidget *parent) : QDialog(parent) {
m_page = 0;
loadPage();
});
connect(m_table, &QTableWidget::cellClicked, this, [this](int row, int) {
showScreenshot(row);
});
connect(m_sendBtn, &QPushButton::clicked, this, &FileLogWindow::sendLogToTelegram);
loadPage();
@ -96,11 +113,11 @@ void FileLogWindow::loadPage() {
m_totalPages = qMax(1, (total + m_pageSize - 1) / m_pageSize);
m_page = qBound(0, m_page, m_totalPages - 1);
const QList<GeneralLogEntry> entries = GeneralLogDAO::getLogs(m_page, m_pageSize, levels);
m_entries = GeneralLogDAO::getLogs(m_page, m_pageSize, levels);
m_table->setRowCount(static_cast<int>(entries.size()));
for (int i = 0; i < entries.size(); ++i) {
const auto &e = entries[i];
m_table->setRowCount(static_cast<int>(m_entries.size()));
for (int i = 0; i < m_entries.size(); ++i) {
const auto &e = m_entries[i];
m_table->setItem(i, COL_ID, new QTableWidgetItem(QString::number(e.id)));
m_table->setItem(i, COL_TIME, new QTableWidgetItem(
e.timestamp.toLocalTime().toString("dd.MM.yy HH:mm:ss")));
@ -112,6 +129,25 @@ void FileLogWindow::loadPage() {
updatePagination();
}
void FileLogWindow::showScreenshot(int row) {
if (row < 0 || row >= m_entries.size()) return;
const QByteArray &data = m_entries[row].screenshot;
if (data.isEmpty()) {
m_screenshotLabel->setText("Нет скриншота");
return;
}
QPixmap pixmap;
pixmap.loadFromData(data);
if (pixmap.isNull()) {
m_screenshotLabel->setText("Ошибка загрузки");
return;
}
m_screenshotLabel->setPixmap(pixmap.scaled(
m_screenshotLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
void FileLogWindow::updatePagination() {
m_pageLabel->setText(QString("Страница %1 из %2").arg(m_page + 1).arg(m_totalPages));
m_prevBtn->setEnabled(m_page > 0);
@ -175,6 +211,21 @@ void FileLogWindow::sendLogToTelegram() {
}
}
// 3.5. Экспортируем скриншоты из general_logs (ошибки скриптов)
const QList<GeneralLogEntry> logErrors = GeneralLogDAO::getLogs(0, 100, {"WARNING", "CRITICAL", "FATAL"});
for (const GeneralLogEntry &gl : logErrors) {
if (gl.screenshot.isEmpty()) continue;
const QString fileName = QString("screenshots/log_%1_%2.png")
.arg(gl.id)
.arg(gl.timestamp.toString("yyyyMMdd_HHmmss"));
QFile imgFile(tmpDir + "/" + fileName);
if (imgFile.open(QIODevice::WriteOnly)) {
imgFile.write(gl.screenshot);
imgFile.close();
++screenshotCount;
}
}
// 4. Создаём ZIP
const QString zipPath = QDir::tempPath() + "/arc_logs.zip";
QFile::remove(zipPath);

View File

@ -3,8 +3,11 @@
#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QSplitter>
#include <QTableWidget>
#include "dao/GeneralLogDAO.h"
class FileLogWindow final : public QDialog {
Q_OBJECT
@ -13,6 +16,7 @@ public:
private:
QTableWidget *m_table;
QLabel *m_screenshotLabel;
QLabel *m_pageLabel;
QPushButton *m_prevBtn;
QPushButton *m_nextBtn;
@ -23,8 +27,11 @@ private:
int m_pageSize = 20;
int m_totalPages = 1;
QList<GeneralLogEntry> m_entries;
QStringList currentLevelFilter() const;
void loadPage();
void updatePagination();
void showScreenshot(int row);
void sendLogToTelegram();
};

View File

@ -19,10 +19,10 @@ RegistrationWindow::RegistrationWindow(QWidget *parent): QWidget(parent) {
auto *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("Введите API ключ:"));
layout->addWidget(new QLabel("Введите ваш идентификатор:"));
QLineEdit *apiKeyEdit = new QLineEdit();
apiKeyEdit->setPlaceholderText("API ключ");
apiKeyEdit->setPlaceholderText("Идентификатор");
layout->addWidget(apiKeyEdit);
QHBoxLayout *buttonLayout = new QHBoxLayout();
@ -39,7 +39,7 @@ RegistrationWindow::RegistrationWindow(QWidget *parent): QWidget(parent) {
connect(loginButton, &QPushButton::clicked, this, [=]() {
const QString apiKey = apiKeyEdit->text().trimmed();
if (apiKey.isEmpty()) {
QMessageBox::warning(this, "Ошибка", "Введите API ключ");
QMessageBox::warning(this, "Ошибка", "Введите ваш идентификатор");
return;
}
@ -66,7 +66,7 @@ RegistrationWindow::RegistrationWindow(QWidget *parent): QWidget(parent) {
emit onRegisteredSuccess();
} else if (result == 0) {
SettingsDAO::remove("api_key");
QMessageBox::critical(this, "Ошибка входа", "Неверный API ключ. Проверьте и попробуйте снова.");
QMessageBox::critical(this, "Ошибка входа", "Неверный идентификатор. Проверьте и попробуйте снова.");
} else {
QMessageBox::critical(this, "Ошибка подключения",
"Нет подключения к интернету или сервер не отвечает.\n\nПожалуйста, попробуйте позже.");