Add SettingsDAO implementation, schema migration, and related updates
Implemented `SettingsDAO` with `get`, `set`, `remove`, and related methods for key-value storage. Added database schema migration for a new `settings` table. Updated `EventDAO` with error handling improvements and `AbstractDAO` with helper utilities. Enhanced Ozon scripts with ad banner detection and handling.
This commit is contained in:
parent
8eedd07ffd
commit
3d3ed9cbf6
@ -15,14 +15,10 @@ QString AdbUtils::adbPath() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdbUtils::startProcess(const QString &cmd, QString *stdOut = nullptr, QString *stdErr = nullptr) {
|
bool AdbUtils::startProcess(const QString &program, const QStringList &args,
|
||||||
|
QString *stdOut, QString *stdErr) {
|
||||||
QProcess process;
|
QProcess process;
|
||||||
|
process.start(program, args);
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
process.start("cmd.exe", {"/C", cmd});
|
|
||||||
#else
|
|
||||||
process.start("sh", {"-c", cmd});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!process.waitForFinished() ||
|
if (!process.waitForFinished() ||
|
||||||
process.exitStatus() != QProcess::NormalExit ||
|
process.exitStatus() != QProcess::NormalExit ||
|
||||||
@ -130,41 +126,34 @@ QString AdbUtils::getScreenDumpAsXml(const QString &deviceId, int idx, bool scre
|
|||||||
QString output;
|
QString output;
|
||||||
|
|
||||||
// 1. Dump UI hierarchy
|
// 1. Dump UI hierarchy
|
||||||
QString dumpCmd = QString("%1 -s %2 shell uiautomator dump /sdcard/uidump.xml").arg(adb, deviceId);
|
if (!startProcess(adb, {"-s", deviceId, "shell", "uiautomator", "dump", "/sdcard/uidump.xml"})) {
|
||||||
if (!startProcess(dumpCmd)) {
|
|
||||||
qWarning() << "Failed to dump UI hierarchy.";
|
qWarning() << "Failed to dump UI hierarchy.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
// 2. Optionally pull and save the XML file
|
// 2. Optionally pull and save the XML file
|
||||||
if (screenshot) {
|
if (screenshot) {
|
||||||
QString pullCmd = QString("%1 -s %2 pull /sdcard/uidump.xml step_%3.xml").arg(adb, deviceId).arg(idx);
|
if (!startProcess(adb, {"-s", deviceId, "pull", "/sdcard/uidump.xml",
|
||||||
if (!startProcess(pullCmd)) {
|
QString("step_%1.xml").arg(idx)})) {
|
||||||
qWarning() << "Failed to pull UIDump XML.";
|
qWarning() << "Failed to pull UIDump XML.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 3. Read the XML content
|
// 3. Read the XML content
|
||||||
QString catCmd = QString("%1 -s %2 shell cat /sdcard/uidump.xml").arg(adb, deviceId);
|
|
||||||
QString xmlOut;
|
QString xmlOut;
|
||||||
if (!startProcess(catCmd, &xmlOut)) {
|
if (!startProcess(adb, {"-s", deviceId, "shell", "cat", "/sdcard/uidump.xml"}, &xmlOut)) {
|
||||||
qWarning() << "Failed to read UIDump XML.";
|
qWarning() << "Failed to read UIDump XML.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Remove temp XML file
|
// 4. Remove temp XML file
|
||||||
QString rmCmd = QString("%1 -s %2 shell rm /sdcard/uidump.xml").arg(adb, deviceId);
|
startProcess(adb, {"-s", deviceId, "shell", "rm", "/sdcard/uidump.xml"}); // неважно, успешна ли команда
|
||||||
startProcess(rmCmd); // неважно, успешна ли команда
|
|
||||||
|
|
||||||
// 5. Screenshot (if needed)
|
// 5. Screenshot (if needed)
|
||||||
if (screenshot) {
|
if (screenshot) {
|
||||||
QString shotCmds[] = {
|
startProcess(adb, {"-s", deviceId, "shell", "screencap", "-p", "/sdcard/screen.png"});
|
||||||
QString("%1 -s %2 shell screencap -p /sdcard/screen.png").arg(adb, deviceId),
|
startProcess(adb, {"-s", deviceId, "pull", "/sdcard/screen.png",
|
||||||
QString("%1 -s %2 pull /sdcard/screen.png screen_step_%3.png").arg(adb, deviceId).arg(idx),
|
QString("screen_step_%1.png").arg(idx)});
|
||||||
QString("%1 -s %2 shell rm /sdcard/screen.png").arg(adb, deviceId)
|
startProcess(adb, {"-s", deviceId, "shell", "rm", "/sdcard/screen.png"});
|
||||||
};
|
|
||||||
for (const QString &cmd: shotCmds) {
|
|
||||||
startProcess(cmd); // ошибки игнорируем для скриншота
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Обрезка к началу XML
|
// 6. Обрезка к началу XML
|
||||||
@ -177,16 +166,17 @@ QString AdbUtils::getScreenDumpAsXml(const QString &deviceId, int idx, bool scre
|
|||||||
|
|
||||||
bool AdbUtils::takeScreenshot(const QString &deviceId, const QString &name) {
|
bool AdbUtils::takeScreenshot(const QString &deviceId, const QString &name) {
|
||||||
const QString adb = adbPath();
|
const QString adb = adbPath();
|
||||||
QStringList commands = {
|
struct Step { QStringList args; };
|
||||||
QString("%1 -s %2 shell screencap -p /sdcard/screen.png").arg(adb, deviceId),
|
Step steps[] = {
|
||||||
QString("%1 -s %2 pull /sdcard/screen.png %3.png").arg(adb, deviceId, name),
|
{{"-s", deviceId, "shell", "screencap", "-p", "/sdcard/screen.png"}},
|
||||||
QString("%1 -s %2 shell rm /sdcard/screen.png").arg(adb, deviceId)
|
{{"-s", deviceId, "pull", "/sdcard/screen.png", name + ".png"}},
|
||||||
|
{{"-s", deviceId, "shell", "rm", "/sdcard/screen.png"}}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const QString &cmd: commands) {
|
for (const auto &step : steps) {
|
||||||
QString stdErr;
|
QString stdErr;
|
||||||
if (!startProcess(cmd, nullptr, &stdErr)) {
|
if (!startProcess(adb, step.args, nullptr, &stdErr)) {
|
||||||
qWarning() << "ADB command failed:" << cmd;
|
qWarning() << "ADB command failed:" << step.args.join(' ');
|
||||||
qWarning() << "Error:" << stdErr;
|
qWarning() << "Error:" << stdErr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -195,13 +185,8 @@ bool AdbUtils::takeScreenshot(const QString &deviceId, const QString &name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QByteArray AdbUtils::captureScreenshotBytes(const QString &deviceId) {
|
QByteArray AdbUtils::captureScreenshotBytes(const QString &deviceId) {
|
||||||
const QString cmd = QString("%1 -s %2 exec-out screencap -p").arg(adbPath(), deviceId);
|
|
||||||
QProcess process;
|
QProcess process;
|
||||||
#ifdef Q_OS_WIN
|
process.start(adbPath(), {"-s", deviceId, "exec-out", "screencap", "-p"});
|
||||||
process.start("cmd.exe", {"/C", cmd});
|
|
||||||
#else
|
|
||||||
process.start("sh", {"-c", cmd});
|
|
||||||
#endif
|
|
||||||
if (!process.waitForFinished(10000) || process.exitCode() != 0) {
|
if (!process.waitForFinished(10000) || process.exitCode() != 0) {
|
||||||
qWarning() << "[AdbUtils] captureScreenshotBytes failed for" << deviceId;
|
qWarning() << "[AdbUtils] captureScreenshotBytes failed for" << deviceId;
|
||||||
return {};
|
return {};
|
||||||
@ -211,17 +196,18 @@ QByteArray AdbUtils::captureScreenshotBytes(const QString &deviceId) {
|
|||||||
|
|
||||||
bool AdbUtils::takeXmlDump(const QString &deviceId, const QString &name) {
|
bool AdbUtils::takeXmlDump(const QString &deviceId, const QString &name) {
|
||||||
const QString adb = adbPath();
|
const QString adb = adbPath();
|
||||||
QStringList commands = {
|
struct Step { QStringList args; };
|
||||||
QString("%1 -s %2 shell uiautomator dump /sdcard/uidump.xml").arg(adb, deviceId),
|
Step steps[] = {
|
||||||
QString("%1 -s %2 pull /sdcard/uidump.xml %3.xml").arg(adb, deviceId, name),
|
{{"-s", deviceId, "shell", "uiautomator", "dump", "/sdcard/uidump.xml"}},
|
||||||
QString("%1 -s %2 shell cat /sdcard/uidump.xml").arg(adb, deviceId),
|
{{"-s", deviceId, "pull", "/sdcard/uidump.xml", name + ".xml"}},
|
||||||
QString("%1 -s %2 shell rm /sdcard/uidump.xml").arg(adb, deviceId)
|
{{"-s", deviceId, "shell", "cat", "/sdcard/uidump.xml"}},
|
||||||
|
{{"-s", deviceId, "shell", "rm", "/sdcard/uidump.xml"}}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const QString &cmd: commands) {
|
for (const auto &step : steps) {
|
||||||
QString stdErr;
|
QString stdErr;
|
||||||
if (!startProcess(cmd, nullptr, &stdErr)) {
|
if (!startProcess(adb, step.args, nullptr, &stdErr)) {
|
||||||
qWarning() << "ADB command failed:" << cmd;
|
qWarning() << "ADB command failed:" << step.args.join(' ');
|
||||||
qWarning() << "Error:" << stdErr;
|
qWarning() << "Error:" << stdErr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -242,15 +228,16 @@ bool AdbUtils::makeTap(const QString &deviceId, const int x, const int y) {
|
|||||||
|
|
||||||
bool AdbUtils::enableAdbIMEKeyboard(const QString &deviceId) {
|
bool AdbUtils::enableAdbIMEKeyboard(const QString &deviceId) {
|
||||||
const QString adb = adbPath();
|
const QString adb = adbPath();
|
||||||
QStringList commands = {
|
struct Step { QStringList args; };
|
||||||
QString("%1 -s %2 shell ime enable com.android.adbkeyboard/.AdbIME").arg(adb, deviceId),
|
Step steps[] = {
|
||||||
QString("%1 -s %2 shell ime set com.android.adbkeyboard/.AdbIME").arg(adb, deviceId)
|
{{"-s", deviceId, "shell", "ime", "enable", "com.android.adbkeyboard/.AdbIME"}},
|
||||||
|
{{"-s", deviceId, "shell", "ime", "set", "com.android.adbkeyboard/.AdbIME"}}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const QString &cmd: commands) {
|
for (const auto &step : steps) {
|
||||||
QString stdErr;
|
QString stdErr;
|
||||||
if (!startProcess(cmd, nullptr, &stdErr)) {
|
if (!startProcess(adb, step.args, nullptr, &stdErr)) {
|
||||||
qWarning() << "ADB command failed:" << cmd;
|
qWarning() << "ADB command failed:" << step.args.join(' ');
|
||||||
qWarning() << "Error:" << stdErr;
|
qWarning() << "Error:" << stdErr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -275,10 +262,8 @@ bool AdbUtils::inputAdbIMEText(const QString &deviceId, const QString &text) {
|
|||||||
|
|
||||||
bool AdbUtils::disableAdbIMEKeyboard(const QString &deviceId) {
|
bool AdbUtils::disableAdbIMEKeyboard(const QString &deviceId) {
|
||||||
QProcess process;
|
QProcess process;
|
||||||
QString command = QString(
|
process.start(adbPath(), {"-s", deviceId, "shell", "ime", "disable",
|
||||||
"%1 -s %2 shell ime disable com.android.adbkeyboard/.AdbIME"
|
"com.android.adbkeyboard/.AdbIME"});
|
||||||
).arg(adbPath(), deviceId);
|
|
||||||
process.start(command);
|
|
||||||
process.waitForFinished();
|
process.waitForFinished();
|
||||||
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
|
return process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0;
|
||||||
}
|
}
|
||||||
@ -290,7 +275,9 @@ bool AdbUtils::makeSwipe(const QString &deviceId, const int x1, const int y1, co
|
|||||||
{
|
{
|
||||||
"-s", deviceId,
|
"-s", deviceId,
|
||||||
"shell", "input",
|
"shell", "input",
|
||||||
"swipe", QString("%1 %2 %3 %4").arg(x1).arg(y1).arg(x2).arg(y2)
|
"swipe",
|
||||||
|
QString::number(x1), QString::number(y1),
|
||||||
|
QString::number(x2), QString::number(y2)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
process.waitForFinished();
|
process.waitForFinished();
|
||||||
|
|||||||
@ -47,5 +47,6 @@ public:
|
|||||||
static QSet<QString> getListApps(const QString &deviceId);
|
static QSet<QString> getListApps(const QString &deviceId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool startProcess(const QString &cmd, QString *stdOut, QString *stdErr);
|
static bool startProcess(const QString &program, const QStringList &args,
|
||||||
|
QString *stdOut = nullptr, QString *stdErr = nullptr);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -110,6 +110,14 @@ bool CommonScript::runAppAndGoToHomeScreen(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем на рекламный bottom sheet (ипотека и т.п.)
|
||||||
|
if (Node adNode = xmlScreenParser.tryToFindAdBanner(); !adNode.isEmpty()) {
|
||||||
|
qDebug() << "[runAppAndGoToHomeScreen] Ad bottom sheet detected, closing...";
|
||||||
|
AdbUtils::makeTap(deviceId, adNode.x(), adNode.y());
|
||||||
|
QThread::msleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Проверяем, что это экран ввода пин-кода
|
// Проверяем, что это экран ввода пин-кода
|
||||||
if (!findAndInputPincode) {
|
if (!findAndInputPincode) {
|
||||||
if (const QList<Node> pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН");
|
if (const QList<Node> pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН");
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include "dao/AccountDAO.h"
|
#include "dao/AccountDAO.h"
|
||||||
#include "dao/ApplicationDAO.h"
|
#include "dao/ApplicationDAO.h"
|
||||||
#include "dao/DeviceDAO.h"
|
#include "dao/DeviceDAO.h"
|
||||||
|
#include "dao/SettingsDAO.h"
|
||||||
#include "net/NetworkService.h"
|
#include "net/NetworkService.h"
|
||||||
#include "AppLogger.h"
|
#include "AppLogger.h"
|
||||||
|
|
||||||
@ -155,7 +156,7 @@ void GetCardInfoScript::doStart() {
|
|||||||
// 1. Создаём девайс если нет api_id
|
// 1. Создаём девайс если нет api_id
|
||||||
if (device.apiId.isEmpty()) {
|
if (device.apiId.isEmpty()) {
|
||||||
QJsonObject deviceBody;
|
QJsonObject deviceBody;
|
||||||
deviceBody["desktop_id"] = QSettings("config.ini", QSettings::IniFormat).value("common/desktop_id").toString();
|
deviceBody["desktop_id"] = SettingsDAO::get("desktop_id");
|
||||||
deviceBody["status"] = "ONLINE";
|
deviceBody["status"] = "ONLINE";
|
||||||
deviceBody["name"] = device.name;
|
deviceBody["name"] = device.name;
|
||||||
deviceBody["screen_width"] = device.screenWidth;
|
deviceBody["screen_width"] = device.screenWidth;
|
||||||
@ -261,12 +262,18 @@ void GetCardInfoScript::doStart() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Закрываем баннер "Понятно" если он перекрывает экран
|
// Закрываем баннер "Понятно" и повторно нажимаем "Показать"
|
||||||
Node bannerBtn = xmlScreenParser.findTextNode("Понятно");
|
Node bannerBtn = xmlScreenParser.findTextNode("Понятно");
|
||||||
if (!bannerBtn.isEmpty()) {
|
if (!bannerBtn.isEmpty()) {
|
||||||
qDebug() << "[GetCardInfo] Banner 'Понятно' detected, closing...";
|
qDebug() << "[GetCardInfo] Banner 'Понятно' detected, closing...";
|
||||||
AdbUtils::makeTap(m_deviceId, bannerBtn.x(), bannerBtn.y());
|
AdbUtils::makeTap(m_deviceId, bannerBtn.x(), bannerBtn.y());
|
||||||
QThread::msleep(1000);
|
QThread::msleep(1500);
|
||||||
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
|
Node retryShowBtn = xmlScreenParser.findButtonNode("Показать", false);
|
||||||
|
if (!retryShowBtn.isEmpty()) {
|
||||||
|
qDebug() << "[GetCardInfo] Re-tapping 'Показать' after banner close";
|
||||||
|
AdbUtils::makeTap(m_deviceId, retryShowBtn.x(), retryShowBtn.y());
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -412,22 +412,46 @@ QList<Node> ScreenXmlParser::findPinCodeNodes(const QString &pinCode) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Способ 2 (fallback): анонимные FrameLayout — ищем сетку 4x3
|
// Способ 2 (fallback): группируем clickable-ноды одинакового размера,
|
||||||
QList<Node> clickable;
|
// ищем сетку 4x3 по паттерну (не привязываясь к координатам экрана)
|
||||||
|
QMap<QPair<int,int>, QList<Node>> sizeGroups;
|
||||||
for (const Node &node : m_nodes) {
|
for (const Node &node : m_nodes) {
|
||||||
if (node.clickable && node.width() > 0 && node.height() > 0)
|
if (!node.clickable || node.width() <= 0 || node.height() <= 0) continue;
|
||||||
clickable.append(node);
|
if (node.width() == node.height()) { // кнопки пин-пада квадратные
|
||||||
|
sizeGroups[{node.width(), node.height()}].append(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (clickable.isEmpty()) return {};
|
|
||||||
|
|
||||||
int minH = INT_MAX;
|
|
||||||
for (const Node &n : clickable) minH = std::min(minH, n.height());
|
|
||||||
|
|
||||||
|
// Ищем группу из >= 12 одинаковых кнопок (пин-пад: 3x4 = 12)
|
||||||
QList<Node> candidates;
|
QList<Node> candidates;
|
||||||
for (const Node &n : clickable) {
|
for (auto it = sizeGroups.begin(); it != sizeGroups.end(); ++it) {
|
||||||
if (n.height() <= minH * 3) candidates.append(n);
|
if (it.value().size() >= 12 &&
|
||||||
|
(candidates.isEmpty() || it.value().size() < candidates.size())) {
|
||||||
|
candidates = it.value();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (candidates.size() < 12) {
|
||||||
|
// Попробуем без ограничения на квадрат, но с >= 12
|
||||||
|
sizeGroups.clear();
|
||||||
|
for (const Node &node : m_nodes) {
|
||||||
|
if (!node.clickable || node.width() <= 0 || node.height() <= 0) continue;
|
||||||
|
sizeGroups[{node.width(), node.height()}].append(node);
|
||||||
|
}
|
||||||
|
for (auto it = sizeGroups.begin(); it != sizeGroups.end(); ++it) {
|
||||||
|
if (it.value().size() >= 12 &&
|
||||||
|
(candidates.size() < 12 || it.value().size() < candidates.size())) {
|
||||||
|
candidates = it.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.size() < 12) {
|
||||||
|
qWarning() << "[findPinCodeNodes] grid not found: no group of 12+ same-sized clickable nodes";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сортируем по Y, затем группируем в строки
|
||||||
std::sort(candidates.begin(), candidates.end(), [](const Node &a, const Node &b) {
|
std::sort(candidates.begin(), candidates.end(), [](const Node &a, const Node &b) {
|
||||||
return a.y() < b.y();
|
return a.y() < b.y();
|
||||||
});
|
});
|
||||||
@ -457,7 +481,8 @@ QList<Node> ScreenXmlParser::findPinCodeNodes(const QString &pinCode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gridRows.size() < 4) {
|
if (gridRows.size() < 4) {
|
||||||
qWarning() << "[findPinCodeNodes] grid not found: gridRows=" << gridRows.size();
|
qWarning() << "[findPinCodeNodes] grid not found: gridRows=" << gridRows.size()
|
||||||
|
<< "candidates=" << candidates.size();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
assets/ozon/banner_ipoteka.png
Normal file
BIN
assets/ozon/banner_ipoteka.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
1
assets/ozon/banner_ipoteka.xml
Normal file
1
assets/ozon/banner_ipoteka.xml
Normal file
File diff suppressed because one or more lines are too long
@ -66,14 +66,189 @@
|
|||||||
focusable="false" focused="false" scrollable="false"
|
focusable="false" focused="false" scrollable="false"
|
||||||
long-clickable="false" password="false" selected="false"
|
long-clickable="false" password="false" selected="false"
|
||||||
bounds="[0,0][1080,2337]" drawing-order="1" hint="">
|
bounds="[0,0][1080,2337]" drawing-order="1" hint="">
|
||||||
<node NAF="true" index="0" text="" resource-id=""
|
<node index="0" text="" resource-id=""
|
||||||
class="android.webkit.WebView"
|
class="android.webkit.WebView"
|
||||||
package="ru.ozon.fintech.finance" content-desc=""
|
package="ru.ozon.fintech.finance" content-desc=""
|
||||||
checkable="false" checked="false" clickable="true"
|
checkable="false" checked="false" clickable="false"
|
||||||
enabled="true" focusable="true" focused="false"
|
enabled="true" focusable="false" focused="false"
|
||||||
scrollable="false" long-clickable="false" password="false"
|
scrollable="false" long-clickable="false" password="false"
|
||||||
selected="false" bounds="[0,0][1080,2337]" drawing-order="1"
|
selected="false" bounds="[0,0][1080,2337]" drawing-order="0"
|
||||||
hint=""/>
|
hint="">
|
||||||
|
<node index="0" text="⠀" resource-id=""
|
||||||
|
class="android.webkit.WebView"
|
||||||
|
package="ru.ozon.fintech.finance" content-desc=""
|
||||||
|
checkable="false" checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="true" focused="true"
|
||||||
|
scrollable="true" long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint="">
|
||||||
|
<node index="0" text="" resource-id=""
|
||||||
|
class="android.view.View"
|
||||||
|
package="ru.ozon.fintech.finance" content-desc=""
|
||||||
|
checkable="false" checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false" focused="false"
|
||||||
|
scrollable="false" long-clickable="false"
|
||||||
|
password="false" selected="false"
|
||||||
|
bounds="[0,0][1080,2337]" drawing-order="0" hint="">
|
||||||
|
<node index="0" text="" resource-id="svelte"
|
||||||
|
class="android.view.View"
|
||||||
|
package="ru.ozon.fintech.finance" content-desc=""
|
||||||
|
checkable="false" checked="false"
|
||||||
|
clickable="false" enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint="">
|
||||||
|
<node index="0" text="" resource-id=""
|
||||||
|
class="android.view.View"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,241]"
|
||||||
|
drawing-order="0" hint="">
|
||||||
|
<node index="0" text="" resource-id=""
|
||||||
|
class="android.widget.Button"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="base-go-back-button"
|
||||||
|
checkable="false" checked="false"
|
||||||
|
clickable="true" enabled="true"
|
||||||
|
focusable="true" focused="false"
|
||||||
|
scrollable="false" long-clickable="false"
|
||||||
|
password="false" selected="false"
|
||||||
|
bounds="[28,144][118,231]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
<node index="1" text="" resource-id=""
|
||||||
|
class="android.widget.Button"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="native-toolbar-close-button"
|
||||||
|
checkable="false" checked="false"
|
||||||
|
clickable="true" enabled="true"
|
||||||
|
focusable="true" focused="false"
|
||||||
|
scrollable="false" long-clickable="false"
|
||||||
|
password="false" selected="false"
|
||||||
|
bounds="[1036,186][1080,241]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
</node>
|
||||||
|
<node index="1" text="" resource-id=""
|
||||||
|
class="android.view.View"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[480,1349][598,1470]"
|
||||||
|
drawing-order="0" hint="">
|
||||||
|
<node index="0" text="" resource-id=""
|
||||||
|
class="android.widget.Image"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false"
|
||||||
|
bounds="[485,1354][593,1462]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
</node>
|
||||||
|
<node index="2" text=""
|
||||||
|
resource-id="modal-container"
|
||||||
|
class="android.widget.TextView"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,2337][1080,2337]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
<node index="3" text=""
|
||||||
|
resource-id="svelte-announcer"
|
||||||
|
class="android.widget.TextView"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][5,5]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
</node>
|
||||||
|
<node index="1" text="" resource-id=""
|
||||||
|
class="android.widget.TextView"
|
||||||
|
package="ru.ozon.fintech.finance" content-desc=""
|
||||||
|
checkable="false" checked="false"
|
||||||
|
clickable="false" enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
<node index="2" text="" resource-id=""
|
||||||
|
class="android.view.View"
|
||||||
|
package="ru.ozon.fintech.finance" content-desc=""
|
||||||
|
checkable="false" checked="false"
|
||||||
|
clickable="false" enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint="">
|
||||||
|
<node index="0" text="" resource-id=""
|
||||||
|
class="android.widget.TextView"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
<node index="1" text="" resource-id=""
|
||||||
|
class="android.widget.TextView"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
</node>
|
||||||
|
<node index="3" text="" resource-id=""
|
||||||
|
class="android.view.View"
|
||||||
|
package="ru.ozon.fintech.finance" content-desc=""
|
||||||
|
checkable="false" checked="false"
|
||||||
|
clickable="false" enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint="">
|
||||||
|
<node index="0" text="" resource-id=""
|
||||||
|
class="android.widget.TextView"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
<node index="1" text="" resource-id=""
|
||||||
|
class="android.widget.TextView"
|
||||||
|
package="ru.ozon.fintech.finance"
|
||||||
|
content-desc="" checkable="false"
|
||||||
|
checked="false" clickable="false"
|
||||||
|
enabled="true" focusable="false"
|
||||||
|
focused="false" scrollable="false"
|
||||||
|
long-clickable="false" password="false"
|
||||||
|
selected="false" bounds="[0,0][1080,2337]"
|
||||||
|
drawing-order="0" hint=""/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node index="1" text=""
|
<node index="1" text=""
|
||||||
|
|||||||
@ -2,13 +2,7 @@
|
|||||||
list=ozon
|
list=ozon
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjNDU1MjE1Ni1kMTc5LTRhNzktOWU5MS01ODcwMzhjOTVjZjAiLCJ0eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzcyODg4MjU2fQ.5WF3EQobCOQpuQr5wcCeYOJW1ER0RJG506WRhEB_Y5E
|
|
||||||
api_key=0eee77b62013253e02e1a69bc6a505bb37757fb7
|
|
||||||
currencies=USD, KRW, AZN, RUB
|
currencies=USD, KRW, AZN, RUB
|
||||||
desktop_id=b86eac8e-dc89-4a9e-a5e8-7f85db011391
|
|
||||||
profile_id=c4552156-d179-4a79-9e91-587038c95cf0
|
|
||||||
refresh_token=4c78f097-d70b-4bdb-b351-c839fa9eb442
|
|
||||||
token_expires_at=1772888256
|
|
||||||
version=0.0.1
|
version=0.0.1
|
||||||
|
|
||||||
[db]
|
[db]
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
DatabaseManager::DatabaseManager() = default;
|
DatabaseManager::DatabaseManager() = default;
|
||||||
|
|
||||||
@ -75,6 +76,13 @@ QString DatabaseManager::connectionName() {
|
|||||||
|
|
||||||
|
|
||||||
static bool columnExists(QSqlQuery &q, const QString &table, const QString &column) {
|
static bool columnExists(QSqlQuery &q, const QString &table, const QString &column) {
|
||||||
|
// Валидация: допускаем только [a-zA-Z0-9_] чтобы исключить SQL injection
|
||||||
|
static const QRegularExpression valid("^[a-zA-Z_][a-zA-Z0-9_]*$");
|
||||||
|
if (!valid.match(table).hasMatch() || !valid.match(column).hasMatch()) {
|
||||||
|
qCritical() << "[DB] columnExists: invalid identifier" << table << column;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
q.exec(QString("SELECT COUNT(*) FROM pragma_table_info('%1') WHERE name='%2'")
|
q.exec(QString("SELECT COUNT(*) FROM pragma_table_info('%1') WHERE name='%2'")
|
||||||
.arg(table, column));
|
.arg(table, column));
|
||||||
return q.next() && q.value(0).toInt() > 0;
|
return q.next() && q.value(0).toInt() > 0;
|
||||||
@ -88,7 +96,7 @@ void DatabaseManager::initSchema() {
|
|||||||
q.exec("PRAGMA user_version");
|
q.exec("PRAGMA user_version");
|
||||||
const int version = q.next() ? q.value(0).toInt() : 0;
|
const int version = q.next() ? q.value(0).toInt() : 0;
|
||||||
|
|
||||||
// ── Версия 1: начальная схема + api_id для devices ────────────────────────
|
// ── Версия 1: полная схема ─────────────────────────────────────────────────
|
||||||
if (version < 1) {
|
if (version < 1) {
|
||||||
db.transaction();
|
db.transaction();
|
||||||
|
|
||||||
@ -117,6 +125,11 @@ void DatabaseManager::initSchema() {
|
|||||||
status TEXT DEFAULT 'off',
|
status TEXT DEFAULT 'off',
|
||||||
install TEXT DEFAULT 'no',
|
install TEXT DEFAULT 'no',
|
||||||
comment TEXT,
|
comment TEXT,
|
||||||
|
phone TEXT DEFAULT '',
|
||||||
|
full_name TEXT DEFAULT '',
|
||||||
|
bank_profile_id TEXT DEFAULT '',
|
||||||
|
currency TEXT DEFAULT '',
|
||||||
|
amount REAL DEFAULT 0,
|
||||||
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE,
|
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE,
|
||||||
UNIQUE (device_id, code)
|
UNIQUE (device_id, code)
|
||||||
))");
|
))");
|
||||||
@ -131,6 +144,8 @@ void DatabaseManager::initSchema() {
|
|||||||
currency TEXT DEFAULT '',
|
currency TEXT DEFAULT '',
|
||||||
update_time DATETIME DEFAULT (strftime('%d.%m.%Y %H:%M:%S', 'now')),
|
update_time DATETIME DEFAULT (strftime('%d.%m.%Y %H:%M:%S', 'now')),
|
||||||
status TEXT DEFAULT 'active',
|
status TEXT DEFAULT 'active',
|
||||||
|
card_number TEXT DEFAULT '',
|
||||||
|
material_id TEXT DEFAULT '',
|
||||||
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE,
|
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE,
|
||||||
UNIQUE (device_id, app_code, last_numbers)
|
UNIQUE (device_id, app_code, last_numbers)
|
||||||
))");
|
))");
|
||||||
@ -155,185 +170,11 @@ void DatabaseManager::initSchema() {
|
|||||||
update_time DATETIME,
|
update_time DATETIME,
|
||||||
complete_time DATETIME,
|
complete_time DATETIME,
|
||||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
receipt_image BLOB,
|
||||||
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE
|
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE
|
||||||
))");
|
))");
|
||||||
|
|
||||||
q.exec(R"(CREATE TABLE IF NOT EXISTS events (
|
q.exec(R"(CREATE TABLE IF NOT EXISTS events (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
account_id INTEGER,
|
|
||||||
external_id INTEGER,
|
|
||||||
device_id TEXT,
|
|
||||||
amount REAL,
|
|
||||||
phone TEXT,
|
|
||||||
bank_name TEXT,
|
|
||||||
comment TEXT,
|
|
||||||
status TEXT,
|
|
||||||
type TEXT,
|
|
||||||
update_time DATETIME,
|
|
||||||
complete_time DATETIME,
|
|
||||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE,
|
|
||||||
UNIQUE (external_id)
|
|
||||||
))");
|
|
||||||
|
|
||||||
// Добавляем api_id в devices если ещё нет (для существующих БД)
|
|
||||||
if (!columnExists(q, "devices", "api_id")) {
|
|
||||||
q.exec("ALTER TABLE devices ADD COLUMN api_id TEXT");
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 1");
|
|
||||||
qDebug() << "[DB] Migration 1 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 2: currency в accounts + переименование app_name → app_code ───
|
|
||||||
if (version < 2) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
// Добавляем currency если ещё нет (существующие БД без этого поля)
|
|
||||||
if (!columnExists(q, "accounts", "currency")) {
|
|
||||||
q.exec("ALTER TABLE accounts ADD COLUMN currency TEXT DEFAULT ''");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Переименовываем app_name → app_code (SQLite 3.25+)
|
|
||||||
if (columnExists(q, "accounts", "app_name")) {
|
|
||||||
q.exec("ALTER TABLE accounts RENAME COLUMN app_name TO app_code");
|
|
||||||
if (q.lastError().isValid()) {
|
|
||||||
qWarning() << "[DB] Migration 2: rename app_name failed:" << q.lastError().text();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 2");
|
|
||||||
qDebug() << "[DB] Migration 2 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 3: phone и full_name в applications ────────────────────────────
|
|
||||||
if (version < 3) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
if (!columnExists(q, "applications", "phone")) {
|
|
||||||
q.exec("ALTER TABLE applications ADD COLUMN phone TEXT DEFAULT ''");
|
|
||||||
}
|
|
||||||
if (!columnExists(q, "applications", "full_name")) {
|
|
||||||
q.exec("ALTER TABLE applications ADD COLUMN full_name TEXT DEFAULT ''");
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 3");
|
|
||||||
qDebug() << "[DB] Migration 3 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 4: card_number в accounts ──────────────────────────────────────
|
|
||||||
if (version < 4) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
if (!columnExists(q, "accounts", "card_number")) {
|
|
||||||
q.exec("ALTER TABLE accounts ADD COLUMN card_number TEXT DEFAULT ''");
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 4");
|
|
||||||
qDebug() << "[DB] Migration 4 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 5: bank_profile_id в applications ──────────────────────────────
|
|
||||||
if (version < 5) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
if (!columnExists(q, "applications", "bank_profile_id")) {
|
|
||||||
q.exec("ALTER TABLE applications ADD COLUMN bank_profile_id TEXT DEFAULT ''");
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 5");
|
|
||||||
qDebug() << "[DB] Migration 5 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 6: currency и amount в applications ─────────────────────────────
|
|
||||||
if (version < 6) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
if (!columnExists(q, "applications", "currency")) {
|
|
||||||
q.exec("ALTER TABLE applications ADD COLUMN currency TEXT DEFAULT ''");
|
|
||||||
}
|
|
||||||
if (!columnExists(q, "applications", "amount")) {
|
|
||||||
q.exec("ALTER TABLE applications ADD COLUMN amount REAL DEFAULT 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 6");
|
|
||||||
qDebug() << "[DB] Migration 6 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 7: material_id в accounts ──────────────────────────────────────
|
|
||||||
if (version < 7) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
if (!columnExists(q, "accounts", "material_id")) {
|
|
||||||
q.exec("ALTER TABLE accounts ADD COLUMN material_id TEXT DEFAULT ''");
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 7");
|
|
||||||
qDebug() << "[DB] Migration 7 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 8: receipt_image в transactions ──────────────────────────────────
|
|
||||||
if (version < 8) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
if (!columnExists(q, "transactions", "receipt_image")) {
|
|
||||||
q.exec("ALTER TABLE transactions ADD COLUMN receipt_image BLOB");
|
|
||||||
}
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 8");
|
|
||||||
qDebug() << "[DB] Migration 8 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 9: таблица banks ───────────────────────────────────────────────
|
|
||||||
if (version < 9) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
q.exec(R"(CREATE TABLE IF NOT EXISTS banks (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
alias TEXT UNIQUE,
|
|
||||||
name TEXT,
|
|
||||||
nspk_name TEXT
|
|
||||||
))");
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 9");
|
|
||||||
qDebug() << "[DB] Migration 9 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 10: таблица app_logs ───────────────────────────────────────────
|
|
||||||
if (version < 10) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
q.exec(R"(CREATE TABLE IF NOT EXISTS app_logs (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
source TEXT NOT NULL DEFAULT '',
|
|
||||||
device_id TEXT NOT NULL DEFAULT '',
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
screenshot BLOB,
|
|
||||||
timestamp TEXT NOT NULL
|
|
||||||
))");
|
|
||||||
|
|
||||||
q.exec("CREATE INDEX IF NOT EXISTS idx_app_logs_ts ON app_logs (id DESC)");
|
|
||||||
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 10");
|
|
||||||
qDebug() << "[DB] Migration 10 applied";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 11: убираем UNIQUE(external_id) из events ─────────────────────
|
|
||||||
if (version < 11) {
|
|
||||||
db.transaction();
|
|
||||||
|
|
||||||
q.exec(R"(CREATE TABLE IF NOT EXISTS events_new (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
account_id INTEGER,
|
account_id INTEGER,
|
||||||
external_id TEXT,
|
external_id TEXT,
|
||||||
@ -347,29 +188,38 @@ void DatabaseManager::initSchema() {
|
|||||||
update_time DATETIME,
|
update_time DATETIME,
|
||||||
complete_time DATETIME,
|
complete_time DATETIME,
|
||||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
json TEXT DEFAULT '',
|
||||||
|
sent_to_server INTEGER DEFAULT 0,
|
||||||
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE,
|
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE
|
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE
|
||||||
))");
|
))");
|
||||||
|
|
||||||
q.exec(R"(INSERT INTO events_new
|
q.exec(R"(CREATE TABLE IF NOT EXISTS banks (
|
||||||
SELECT * FROM events)");
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
alias TEXT UNIQUE,
|
||||||
|
name TEXT,
|
||||||
|
nspk_name TEXT
|
||||||
|
))");
|
||||||
|
|
||||||
q.exec("DROP TABLE events");
|
q.exec(R"(CREATE TABLE IF NOT EXISTS app_logs (
|
||||||
q.exec("ALTER TABLE events_new RENAME TO events");
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
source TEXT NOT NULL DEFAULT '',
|
||||||
|
device_id TEXT NOT NULL DEFAULT '',
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
screenshot BLOB,
|
||||||
|
timestamp TEXT NOT NULL
|
||||||
|
))");
|
||||||
|
|
||||||
|
q.exec("CREATE INDEX IF NOT EXISTS idx_app_logs_ts ON app_logs (id DESC)");
|
||||||
|
|
||||||
|
q.exec(R"(CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT
|
||||||
|
))");
|
||||||
|
|
||||||
db.commit();
|
db.commit();
|
||||||
q.exec("PRAGMA user_version = 11");
|
q.exec("PRAGMA user_version = 1");
|
||||||
qDebug() << "[DB] Migration 11 applied";
|
qDebug() << "[DB] Migration 1 applied";
|
||||||
}
|
|
||||||
|
|
||||||
// ── Версия 12: json и sent_to_server в events ────────────────────────────
|
|
||||||
if (version < 12) {
|
|
||||||
db.transaction();
|
|
||||||
q.exec("ALTER TABLE events ADD COLUMN json TEXT DEFAULT ''");
|
|
||||||
q.exec("ALTER TABLE events ADD COLUMN sent_to_server INTEGER DEFAULT 0");
|
|
||||||
db.commit();
|
|
||||||
q.exec("PRAGMA user_version = 12");
|
|
||||||
qDebug() << "[DB] Migration 12 applied";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -58,7 +58,7 @@ bool AccountDAO::updateAccountById(
|
|||||||
|
|
||||||
query.bindValue(":status", status);
|
query.bindValue(":status", status);
|
||||||
query.bindValue(":description", description);
|
query.bindValue(":description", description);
|
||||||
query.bindValue(":last_numbers", description.right(4));
|
query.bindValue(":last_numbers", description.length() >= 4 ? description.right(4) : description);
|
||||||
query.bindValue(":currency", currency);
|
query.bindValue(":currency", currency);
|
||||||
query.bindValue(":id", id);
|
query.bindValue(":id", id);
|
||||||
|
|
||||||
|
|||||||
@ -203,7 +203,10 @@ QList<EventInfo> EventDAO::getEvents(int page, int pageSize) {
|
|||||||
|
|
||||||
int EventDAO::getTotalCount() {
|
int EventDAO::getTotalCount() {
|
||||||
QSqlQuery query(DatabaseManager::instance().database());
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
query.exec("SELECT COUNT(*) FROM events");
|
if (!query.exec("SELECT COUNT(*) FROM events")) {
|
||||||
|
qWarning() << "Ошибка при получении getTotalCount:" << query.lastError().text();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (query.next()) return query.value(0).toInt();
|
if (query.next()) return query.value(0).toInt();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
55
database/dao/SettingsDAO.cpp
Normal file
55
database/dao/SettingsDAO.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "SettingsDAO.h"
|
||||||
|
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "DatabaseManager.h"
|
||||||
|
|
||||||
|
QString SettingsDAO::get(const QString &key, const QString &defaultValue) {
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
query.prepare("SELECT value FROM settings WHERE key = :key LIMIT 1");
|
||||||
|
query.bindValue(":key", key);
|
||||||
|
if (!query.exec()) {
|
||||||
|
qWarning() << "SettingsDAO::get error:" << query.lastError().text();
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
if (query.next()) {
|
||||||
|
return query.value(0).toString();
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDAO::set(const QString &key, const QString &value) {
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
query.prepare(R"(
|
||||||
|
INSERT INTO settings (key, value) VALUES (:key, :value)
|
||||||
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
||||||
|
)");
|
||||||
|
query.bindValue(":key", key);
|
||||||
|
query.bindValue(":value", value);
|
||||||
|
if (!query.exec()) {
|
||||||
|
qWarning() << "SettingsDAO::set error:" << query.lastError().text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDAO::remove(const QString &key) {
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
query.prepare("DELETE FROM settings WHERE key = :key");
|
||||||
|
query.bindValue(":key", key);
|
||||||
|
if (!query.exec()) {
|
||||||
|
qWarning() << "SettingsDAO::remove error:" << query.lastError().text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 SettingsDAO::getLong(const QString &key, qint64 defaultValue) {
|
||||||
|
const QString val = get(key);
|
||||||
|
if (val.isEmpty()) return defaultValue;
|
||||||
|
bool ok;
|
||||||
|
const qint64 result = val.toLongLong(&ok);
|
||||||
|
return ok ? result : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDAO::setLong(const QString &key, qint64 value) {
|
||||||
|
set(key, QString::number(value));
|
||||||
|
}
|
||||||
12
database/dao/SettingsDAO.h
Normal file
12
database/dao/SettingsDAO.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class SettingsDAO {
|
||||||
|
public:
|
||||||
|
static QString get(const QString &key, const QString &defaultValue = {});
|
||||||
|
static void set(const QString &key, const QString &value);
|
||||||
|
static void remove(const QString &key);
|
||||||
|
static qint64 getLong(const QString &key, qint64 defaultValue = 0);
|
||||||
|
static void setLong(const QString &key, qint64 value);
|
||||||
|
};
|
||||||
15
main.cpp
15
main.cpp
@ -17,6 +17,7 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#include "AppLogger.h"
|
#include "AppLogger.h"
|
||||||
|
#include "dao/SettingsDAO.h"
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
#include "net/NetworkService.h"
|
#include "net/NetworkService.h"
|
||||||
#include "ozon/PayByPhoneScript.h"
|
#include "ozon/PayByPhoneScript.h"
|
||||||
@ -196,11 +197,11 @@ int main(int argc, char *argv[]) {
|
|||||||
// - неверный api_key [code 0] — показываем окно ввода
|
// - неверный api_key [code 0] — показываем окно ввода
|
||||||
// - всё ок [code 1] — запускаем приложение
|
// - всё ок [code 1] — запускаем приложение
|
||||||
// 2. Если нет api_key — показываем окно для ввода
|
// 2. Если нет api_key — показываем окно для ввода
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
const QString apiKey = SettingsDAO::get("api_key");
|
||||||
const QString apiKey = settings.value("common/api_key").toString();
|
|
||||||
|
|
||||||
auto showRegistration = [&app]() {
|
auto showRegistration = [&app]() {
|
||||||
auto *registrationWindow = new RegistrationWindow();
|
auto *registrationWindow = new RegistrationWindow();
|
||||||
|
registrationWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
registrationWindow->show();
|
registrationWindow->show();
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
registrationWindow, &RegistrationWindow::onRegisteredSuccess, registrationWindow,
|
registrationWindow, &RegistrationWindow::onRegisteredSuccess, registrationWindow,
|
||||||
@ -219,6 +220,7 @@ int main(int argc, char *argv[]) {
|
|||||||
showRegistration();
|
showRegistration();
|
||||||
} else {
|
} else {
|
||||||
auto *loader = new LoaderWindow();
|
auto *loader = new LoaderWindow();
|
||||||
|
loader->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
loader->show();
|
loader->show();
|
||||||
|
|
||||||
QObject::connect(loader, &LoaderWindow::loadingFinished, loader, [loader, &app, showRegistration](const int result) {
|
QObject::connect(loader, &LoaderWindow::loadingFinished, loader, [loader, &app, showRegistration](const int result) {
|
||||||
@ -227,10 +229,12 @@ int main(int argc, char *argv[]) {
|
|||||||
"Сервер не отвечает.\n\nПроверьте подключение к интернету и попробуйте снова.",
|
"Сервер не отвечает.\n\nПроверьте подключение к интернету и попробуйте снова.",
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
errorWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
errorWindow->show();
|
errorWindow->show();
|
||||||
QObject::connect(errorWindow, &ErrorWindow::retryRequested, errorWindow, [errorWindow, &app, showRegistration]() {
|
QObject::connect(errorWindow, &ErrorWindow::retryRequested, errorWindow, [errorWindow, &app, showRegistration]() {
|
||||||
errorWindow->close();
|
errorWindow->close();
|
||||||
auto *newLoader = new LoaderWindow();
|
auto *newLoader = new LoaderWindow();
|
||||||
|
newLoader->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
newLoader->show();
|
newLoader->show();
|
||||||
QObject::connect(newLoader, &LoaderWindow::loadingFinished, newLoader, [newLoader, &app, showRegistration](const int r) {
|
QObject::connect(newLoader, &LoaderWindow::loadingFinished, newLoader, [newLoader, &app, showRegistration](const int r) {
|
||||||
if (r == 1) {
|
if (r == 1) {
|
||||||
@ -241,14 +245,14 @@ int main(int argc, char *argv[]) {
|
|||||||
startEventHandler(app);
|
startEventHandler(app);
|
||||||
// startPayByPhoneTest();
|
// startPayByPhoneTest();
|
||||||
} else if (r == 0) {
|
} else if (r == 0) {
|
||||||
QSettings s("config.ini", QSettings::IniFormat);
|
SettingsDAO::remove("api_key");
|
||||||
s.remove("common/api_key");
|
|
||||||
showRegistration();
|
showRegistration();
|
||||||
} else {
|
} else {
|
||||||
auto *err2 = new ErrorWindow(
|
auto *err2 = new ErrorWindow(
|
||||||
"Сервер не отвечает.\n\nПроверьте подключение к интернету и попробуйте снова.",
|
"Сервер не отвечает.\n\nПроверьте подключение к интернету и попробуйте снова.",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
err2->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
err2->show();
|
err2->show();
|
||||||
}
|
}
|
||||||
newLoader->close();
|
newLoader->close();
|
||||||
@ -262,8 +266,7 @@ int main(int argc, char *argv[]) {
|
|||||||
startEventHandler(app);
|
startEventHandler(app);
|
||||||
// startPayByPhoneTest();
|
// startPayByPhoneTest();
|
||||||
} else if (result == 0) {
|
} else if (result == 0) {
|
||||||
QSettings s("config.ini", QSettings::IniFormat);
|
SettingsDAO::remove("api_key");
|
||||||
s.remove("common/api_key");
|
|
||||||
showRegistration();
|
showRegistration();
|
||||||
}
|
}
|
||||||
loader->close();
|
loader->close();
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <qeventloop.h>
|
#include <qeventloop.h>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QHttpMultiPart>
|
#include <QHttpMultiPart>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@ -18,6 +19,7 @@
|
|||||||
|
|
||||||
#include "adb/AdbUtils.h"
|
#include "adb/AdbUtils.h"
|
||||||
#include "dao/ApplicationDAO.h"
|
#include "dao/ApplicationDAO.h"
|
||||||
|
#include "dao/SettingsDAO.h"
|
||||||
|
|
||||||
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
const QSettings settings("config.ini", QSettings::IniFormat);
|
||||||
@ -77,14 +79,21 @@ void DeviceScreener::postDevicesData(const QList<DeviceInfo> &devices) {
|
|||||||
|
|
||||||
// локальный цикл — ждём, пока придёт finished()
|
// локальный цикл — ждём, пока придёт finished()
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
QObject::connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
QObject::connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
||||||
loop.quit();
|
loop.quit();
|
||||||
});
|
});
|
||||||
loop.exec(); // <- здесь и запускается event-loop потока
|
QObject::connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
// обрабатываем результат
|
// обрабатываем результат
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qWarning() << "Таймаут postDevicesData:" << m_urlAppUpdate;
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() == QNetworkReply::NoError) {
|
||||||
QByteArray raw = reply->readAll();
|
QByteArray raw = reply->readAll();
|
||||||
QJsonParseError err;
|
QJsonParseError err;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
||||||
@ -103,20 +112,26 @@ void DeviceScreener::postDevicesData(const QList<DeviceInfo> &devices) {
|
|||||||
QJsonValue DeviceScreener::apiPost(const QString &path, const QJsonObject &body) {
|
QJsonValue DeviceScreener::apiPost(const QString &path, const QJsonObject &body) {
|
||||||
if (!m_manager) m_manager = new QNetworkAccessManager(this);
|
if (!m_manager) m_manager = new QNetworkAccessManager(this);
|
||||||
|
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
|
||||||
QNetworkRequest request(QUrl(m_apiBase + path));
|
QNetworkRequest request(QUrl(m_apiBase + path));
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
request.setRawHeader("Authorization",
|
request.setRawHeader("Authorization",
|
||||||
("Bearer " + settings.value("common/access_token").toString()).toUtf8());
|
("Bearer " + SettingsDAO::get("access_token")).toUtf8());
|
||||||
|
|
||||||
QNetworkReply *reply = m_manager->post(request, QJsonDocument(body).toJson());
|
QNetworkReply *reply = m_manager->post(request, QJsonDocument(body).toJson());
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); });
|
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); });
|
||||||
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
QJsonValue result;
|
QJsonValue result;
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qCritical() << "DeviceScreener apiPost timeout:" << path;
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() == QNetworkReply::NoError) {
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
|
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
|
||||||
if (doc.isObject()) {
|
if (doc.isObject()) {
|
||||||
const QJsonObject root = doc.object();
|
const QJsonObject root = doc.object();
|
||||||
@ -133,27 +148,32 @@ QJsonValue DeviceScreener::apiPost(const QString &path, const QJsonObject &body)
|
|||||||
void DeviceScreener::apiPatch(const QString &path, const QJsonObject &body) {
|
void DeviceScreener::apiPatch(const QString &path, const QJsonObject &body) {
|
||||||
if (!m_manager) m_manager = new QNetworkAccessManager(this);
|
if (!m_manager) m_manager = new QNetworkAccessManager(this);
|
||||||
|
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
|
||||||
QNetworkRequest request(QUrl(m_apiBase + path));
|
QNetworkRequest request(QUrl(m_apiBase + path));
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
request.setRawHeader("Authorization",
|
request.setRawHeader("Authorization",
|
||||||
("Bearer " + settings.value("common/access_token").toString()).toUtf8());
|
("Bearer " + SettingsDAO::get("access_token")).toUtf8());
|
||||||
|
|
||||||
QNetworkReply *reply = m_manager->sendCustomRequest(request, "PATCH", QJsonDocument(body).toJson());
|
QNetworkReply *reply = m_manager->sendCustomRequest(request, "PATCH", QJsonDocument(body).toJson());
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); });
|
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); });
|
||||||
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qCritical() << "DeviceScreener apiPatch timeout:" << path;
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() != QNetworkReply::NoError) {
|
||||||
qCritical() << "DeviceScreener apiPatch network error:" << reply->errorString();
|
qCritical() << "DeviceScreener apiPatch network error:" << reply->errorString();
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DeviceScreener::createDeviceOnApi(const DeviceInfo &device) {
|
QString DeviceScreener::createDeviceOnApi(const DeviceInfo &device) {
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||||
const QString desktopId = settings.value("common/desktop_id").toString();
|
|
||||||
if (desktopId.isEmpty()) {
|
if (desktopId.isEmpty()) {
|
||||||
qWarning() << "createDeviceOnApi: desktop_id не задан";
|
qWarning() << "createDeviceOnApi: desktop_id не задан";
|
||||||
return {};
|
return {};
|
||||||
@ -177,11 +197,10 @@ QString DeviceScreener::createDeviceOnApi(const DeviceInfo &device) {
|
|||||||
void DeviceScreener::postScreenshotToApi(const QString &apiId, const QByteArray &image) {
|
void DeviceScreener::postScreenshotToApi(const QString &apiId, const QByteArray &image) {
|
||||||
if (!m_manager) m_manager = new QNetworkAccessManager(this);
|
if (!m_manager) m_manager = new QNetworkAccessManager(this);
|
||||||
|
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
|
||||||
const QString path = "/api/v1/device/add_screenshot/" + apiId;
|
const QString path = "/api/v1/device/add_screenshot/" + apiId;
|
||||||
QNetworkRequest request(QUrl(m_apiBase + path));
|
QNetworkRequest request(QUrl(m_apiBase + path));
|
||||||
request.setRawHeader("Authorization",
|
request.setRawHeader("Authorization",
|
||||||
("Bearer " + settings.value("common/access_token").toString()).toUtf8());
|
("Bearer " + SettingsDAO::get("access_token")).toUtf8());
|
||||||
|
|
||||||
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||||
QHttpPart filePart;
|
QHttpPart filePart;
|
||||||
@ -194,11 +213,18 @@ void DeviceScreener::postScreenshotToApi(const QString &apiId, const QByteArray
|
|||||||
QNetworkReply *reply = m_manager->post(request, multiPart);
|
QNetworkReply *reply = m_manager->post(request, multiPart);
|
||||||
multiPart->setParent(reply);
|
multiPart->setParent(reply);
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); });
|
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); });
|
||||||
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qWarning() << "postScreenshotToApi timeout for device" << apiId;
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() != QNetworkReply::NoError) {
|
||||||
qWarning() << "postScreenshotToApi network error:" << reply->errorString();
|
qWarning() << "postScreenshotToApi network error:" << reply->errorString();
|
||||||
} else {
|
} else {
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
|
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#include "dao/ApplicationDAO.h"
|
#include "dao/ApplicationDAO.h"
|
||||||
#include "dao/DeviceDAO.h"
|
#include "dao/DeviceDAO.h"
|
||||||
#include "dao/EventDAO.h"
|
#include "dao/EventDAO.h"
|
||||||
|
#include "dao/SettingsDAO.h"
|
||||||
#include "dao/TransactionDAO.h"
|
#include "dao/TransactionDAO.h"
|
||||||
#include "db/EventInfo.h"
|
#include "db/EventInfo.h"
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ NetworkService::NetworkService(QObject *parent) : QObject(parent) {
|
|||||||
m_pathMonitoringLog = settings.value("net/pathMonitoringLog", "/monitoring/log").toString();
|
m_pathMonitoringLog = settings.value("net/pathMonitoringLog", "/monitoring/log").toString();
|
||||||
m_manager = new QNetworkAccessManager(this);
|
m_manager = new QNetworkAccessManager(this);
|
||||||
m_token = settings.value("common/token").toString();
|
m_token = settings.value("common/token").toString();
|
||||||
m_accessToken = settings.value("common/access_token").toString();
|
m_accessToken = SettingsDAO::get("access_token");
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkService::~NetworkService() = default;
|
NetworkService::~NetworkService() = default;
|
||||||
@ -74,14 +75,21 @@ QJsonValue NetworkService::post(QHttpMultiPart *multiPart, const QString &path)
|
|||||||
multiPart->setParent(reply);
|
multiPart->setParent(reply);
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
||||||
loop.quit();
|
loop.quit();
|
||||||
});
|
});
|
||||||
loop.exec(); // <- здесь и запускается event-loop потока
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
QJsonValue result;
|
QJsonValue result;
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qCritical() << "Таймаут запроса:" << path;
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() == QNetworkReply::NoError) {
|
||||||
const QByteArray raw = reply->readAll();
|
const QByteArray raw = reply->readAll();
|
||||||
QJsonParseError err;
|
QJsonParseError err;
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
const QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
||||||
@ -113,14 +121,21 @@ QJsonArray NetworkService::get(const QString &path, const QMap<QString, QString>
|
|||||||
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> guard(reply);
|
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> guard(reply);
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
||||||
loop.quit();
|
loop.quit();
|
||||||
});
|
});
|
||||||
loop.exec(); // <- здесь и запускается event-loop потока
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
QJsonArray result;
|
QJsonArray result;
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qCritical() << "Таймаут запроса (GET):" << path;
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() == QNetworkReply::NoError) {
|
||||||
const QByteArray raw = reply->readAll();
|
const QByteArray raw = reply->readAll();
|
||||||
QJsonParseError err;
|
QJsonParseError err;
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
const QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
||||||
@ -205,14 +220,22 @@ QJsonValue NetworkService::postJson(const QString &path, const QJsonObject &body
|
|||||||
|
|
||||||
QNetworkReply *reply = m_manager->post(request, QJsonDocument(body).toJson());
|
QNetworkReply *reply = m_manager->post(request, QJsonDocument(body).toJson());
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
||||||
loop.quit();
|
loop.quit();
|
||||||
});
|
});
|
||||||
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
QJsonValue result;
|
QJsonValue result;
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qCritical() << "Таймаут запроса (postJson):" << path;
|
||||||
|
reply->abort();
|
||||||
|
result = QJsonValue::Undefined;
|
||||||
|
} else if (reply->error() != QNetworkReply::NoError) {
|
||||||
const QByteArray responseBody = reply->readAll();
|
const QByteArray responseBody = reply->readAll();
|
||||||
qCritical() << "Network error (POST):" << reply->errorString() << path
|
qCritical() << "Network error (POST):" << reply->errorString() << path
|
||||||
<< "\n response body:" << responseBody;
|
<< "\n response body:" << responseBody;
|
||||||
@ -236,8 +259,7 @@ QJsonValue NetworkService::postJson(const QString &path, const QJsonObject &body
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NetworkService::loginAndSetup() {
|
void NetworkService::loginAndSetup() {
|
||||||
QSettings settings("config.ini", QSettings::IniFormat);
|
const QString apiKey = SettingsDAO::get("api_key");
|
||||||
const QString apiKey = settings.value("common/api_key").toString();
|
|
||||||
|
|
||||||
// Шаг 1: логин
|
// Шаг 1: логин
|
||||||
QJsonObject loginBody;
|
QJsonObject loginBody;
|
||||||
@ -257,13 +279,19 @@ void NetworkService::loginAndSetup() {
|
|||||||
|
|
||||||
const QJsonObject loginData = loginResult.toObject();
|
const QJsonObject loginData = loginResult.toObject();
|
||||||
m_accessToken = loginData["access_token"].toString();
|
m_accessToken = loginData["access_token"].toString();
|
||||||
settings.setValue("common/access_token", m_accessToken);
|
if (m_accessToken.isEmpty() || !loginData.contains("refresh_token") || !loginData.contains("expires_in")) {
|
||||||
settings.setValue("common/refresh_token", loginData["refresh_token"].toString());
|
qCritical() << "loginAndSetup: ответ не содержит обязательных полей (access_token/refresh_token/expires_in)";
|
||||||
|
emit finishedWithResult(-1);
|
||||||
|
emit finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SettingsDAO::set("access_token", m_accessToken);
|
||||||
|
SettingsDAO::set("refresh_token", loginData["refresh_token"].toString());
|
||||||
const qint64 expiresIn = static_cast<qint64>(loginData["expires_in"].toDouble());
|
const qint64 expiresIn = static_cast<qint64>(loginData["expires_in"].toDouble());
|
||||||
settings.setValue("common/token_expires_at", QDateTime::currentSecsSinceEpoch() + expiresIn);
|
SettingsDAO::setLong("token_expires_at", QDateTime::currentSecsSinceEpoch() + expiresIn);
|
||||||
|
|
||||||
// Шаг 2: проверяем есть ли desktop_id
|
// Шаг 2: проверяем есть ли desktop_id
|
||||||
const QString desktopId = settings.value("common/desktop_id").toString();
|
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||||
if (!desktopId.isEmpty()) {
|
if (!desktopId.isEmpty()) {
|
||||||
emit finishedWithResult(1);
|
emit finishedWithResult(1);
|
||||||
emit finished();
|
emit finished();
|
||||||
@ -275,6 +303,7 @@ void NetworkService::loginAndSetup() {
|
|||||||
desktopBody["blue_token"] = QJsonValue::Null;
|
desktopBody["blue_token"] = QJsonValue::Null;
|
||||||
desktopBody["name"] = QJsonValue::Null;
|
desktopBody["name"] = QJsonValue::Null;
|
||||||
desktopBody["contact"] = QJsonValue::Null;
|
desktopBody["contact"] = QJsonValue::Null;
|
||||||
|
const QSettings settings("config.ini", QSettings::IniFormat);
|
||||||
desktopBody["app_version"] = settings.value("common/version").toString();
|
desktopBody["app_version"] = settings.value("common/version").toString();
|
||||||
const QJsonValue desktopResult = postJson(m_apiBase + m_pathDesktopCreate, desktopBody, true);
|
const QJsonValue desktopResult = postJson(m_apiBase + m_pathDesktopCreate, desktopBody, true);
|
||||||
|
|
||||||
@ -285,8 +314,8 @@ void NetworkService::loginAndSetup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QJsonObject desktopData = desktopResult.toObject();
|
const QJsonObject desktopData = desktopResult.toObject();
|
||||||
settings.setValue("common/desktop_id", desktopData["id"].toString());
|
SettingsDAO::set("desktop_id", desktopData["id"].toString());
|
||||||
settings.setValue("common/profile_id", desktopData["profile_id"].toString());
|
SettingsDAO::set("profile_id", desktopData["profile_id"].toString());
|
||||||
|
|
||||||
emit finishedWithResult(1);
|
emit finishedWithResult(1);
|
||||||
emit finished();
|
emit finished();
|
||||||
@ -350,12 +379,19 @@ QJsonValue NetworkService::patchJson(const QString &path, const QJsonObject &bod
|
|||||||
|
|
||||||
QNetworkReply *reply = m_manager->sendCustomRequest(request, "PATCH", QJsonDocument(body).toJson());
|
QNetworkReply *reply = m_manager->sendCustomRequest(request, "PATCH", QJsonDocument(body).toJson());
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); });
|
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { loop.quit(); });
|
||||||
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
QJsonValue result;
|
QJsonValue result;
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qCritical() << "Таймаут запроса (PATCH):" << path;
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() != QNetworkReply::NoError) {
|
||||||
const QByteArray responseBody = reply->readAll();
|
const QByteArray responseBody = reply->readAll();
|
||||||
qCritical() << "Network error (PATCH):" << reply->errorString() << path
|
qCritical() << "Network error (PATCH):" << reply->errorString() << path
|
||||||
<< "\n response body:" << responseBody;
|
<< "\n response body:" << responseBody;
|
||||||
@ -487,13 +523,20 @@ void NetworkService::postDeviceScreenshot() {
|
|||||||
multiPart->setParent(reply);
|
multiPart->setParent(reply);
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
||||||
loop.quit();
|
loop.quit();
|
||||||
});
|
});
|
||||||
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qWarning() << "[NetworkService] postDeviceScreenshot timeout for device" << device.apiId;
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() != QNetworkReply::NoError) {
|
||||||
qWarning() << "[NetworkService] postDeviceScreenshot failed:" << reply->errorString();
|
qWarning() << "[NetworkService] postDeviceScreenshot failed:" << reply->errorString();
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "[NetworkService] postDeviceScreenshot ok for device" << device.apiId;
|
qDebug() << "[NetworkService] postDeviceScreenshot ok for device" << device.apiId;
|
||||||
@ -503,8 +546,7 @@ void NetworkService::postDeviceScreenshot() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NetworkService::createDevice() {
|
void NetworkService::createDevice() {
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||||
const QString desktopId = settings.value("common/desktop_id").toString();
|
|
||||||
if (desktopId.isEmpty()) {
|
if (desktopId.isEmpty()) {
|
||||||
qWarning() << "createDevice: desktop_id not set";
|
qWarning() << "createDevice: desktop_id not set";
|
||||||
emit finished();
|
emit finished();
|
||||||
@ -540,8 +582,7 @@ void NetworkService::updateDevice() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkService::refreshToken() {
|
bool NetworkService::refreshToken() {
|
||||||
QSettings settings("config.ini", QSettings::IniFormat);
|
const QString storedRefreshToken = SettingsDAO::get("refresh_token");
|
||||||
const QString storedRefreshToken = settings.value("common/refresh_token").toString();
|
|
||||||
if (storedRefreshToken.isEmpty()) {
|
if (storedRefreshToken.isEmpty()) {
|
||||||
qWarning() << "refreshToken: refresh_token отсутствует";
|
qWarning() << "refreshToken: refresh_token отсутствует";
|
||||||
return false;
|
return false;
|
||||||
@ -562,10 +603,14 @@ bool NetworkService::refreshToken() {
|
|||||||
|
|
||||||
const QJsonObject data = result.toObject();
|
const QJsonObject data = result.toObject();
|
||||||
m_accessToken = data["access_token"].toString();
|
m_accessToken = data["access_token"].toString();
|
||||||
settings.setValue("common/access_token", m_accessToken);
|
if (m_accessToken.isEmpty() || !data.contains("refresh_token") || !data.contains("expires_in")) {
|
||||||
settings.setValue("common/refresh_token", data["refresh_token"].toString());
|
qCritical() << "refreshToken: ответ не содержит обязательных полей";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SettingsDAO::set("access_token", m_accessToken);
|
||||||
|
SettingsDAO::set("refresh_token", data["refresh_token"].toString());
|
||||||
const qint64 expiresIn = static_cast<qint64>(data["expires_in"].toDouble());
|
const qint64 expiresIn = static_cast<qint64>(data["expires_in"].toDouble());
|
||||||
settings.setValue("common/token_expires_at", QDateTime::currentSecsSinceEpoch() + expiresIn);
|
SettingsDAO::setLong("token_expires_at", QDateTime::currentSecsSinceEpoch() + expiresIn);
|
||||||
|
|
||||||
m_tokenErrorShown = false; // сбрасываем если снова заработало
|
m_tokenErrorShown = false; // сбрасываем если снова заработало
|
||||||
qDebug() << "refreshToken: токен обновлён, истекает через" << expiresIn << "сек";
|
qDebug() << "refreshToken: токен обновлён, истекает через" << expiresIn << "сек";
|
||||||
@ -573,8 +618,7 @@ bool NetworkService::refreshToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkService::ensureValidToken() {
|
bool NetworkService::ensureValidToken() {
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
const qint64 expiresAt = SettingsDAO::getLong("token_expires_at");
|
||||||
const qint64 expiresAt = settings.value("common/token_expires_at", 0).toLongLong();
|
|
||||||
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
||||||
|
|
||||||
// Обновляем заранее — за 30 минут до истечения
|
// Обновляем заранее — за 30 минут до истечения
|
||||||
@ -588,8 +632,7 @@ bool NetworkService::ensureValidToken() {
|
|||||||
void NetworkService::scheduleNextRefresh() {
|
void NetworkService::scheduleNextRefresh() {
|
||||||
if (!m_running) return;
|
if (!m_running) return;
|
||||||
|
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
const qint64 expiresAt = SettingsDAO::getLong("token_expires_at");
|
||||||
const qint64 expiresAt = settings.value("common/token_expires_at", 0).toLongLong();
|
|
||||||
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
||||||
|
|
||||||
// Обновляем за 30 минут до истечения; если уже пора — сразу
|
// Обновляем за 30 минут до истечения; если уже пора — сразу
|
||||||
@ -621,8 +664,7 @@ void NetworkService::getTasks() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
const QString accessToken = SettingsDAO::get("access_token");
|
||||||
const QString accessToken = settings.value("common/access_token").toString();
|
|
||||||
|
|
||||||
QUrl url(m_apiBase + m_pathGetTasks);
|
QUrl url(m_apiBase + m_pathGetTasks);
|
||||||
QUrlQuery urlQuery;
|
QUrlQuery urlQuery;
|
||||||
@ -702,10 +744,17 @@ void NetworkService::postMonitoringLog(const QString &type, const QString &event
|
|||||||
multiPart->setParent(reply);
|
multiPart->setParent(reply);
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
QTimer timeout;
|
||||||
|
timeout.setSingleShot(true);
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
|
connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||||
|
timeout.start(30000);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (!timeout.isActive()) {
|
||||||
|
qWarning() << "[NetworkService] postMonitoringLog timeout";
|
||||||
|
reply->abort();
|
||||||
|
} else if (reply->error() != QNetworkReply::NoError) {
|
||||||
qWarning() << "[NetworkService] postMonitoringLog failed:" << reply->errorString()
|
qWarning() << "[NetworkService] postMonitoringLog failed:" << reply->errorString()
|
||||||
<< "\n response body:" << reply->readAll();
|
<< "\n response body:" << reply->readAll();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
#include "LoaderDialog.h"
|
#include "LoaderDialog.h"
|
||||||
|
#include "dao/SettingsDAO.h"
|
||||||
#include "net/NetworkService.h"
|
#include "net/NetworkService.h"
|
||||||
|
|
||||||
RegistrationWindow::RegistrationWindow(QWidget *parent): QWidget(parent) {
|
RegistrationWindow::RegistrationWindow(QWidget *parent): QWidget(parent) {
|
||||||
@ -42,10 +43,7 @@ RegistrationWindow::RegistrationWindow(QWidget *parent): QWidget(parent) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
SettingsDAO::set("api_key", apiKey);
|
||||||
QSettings settings("config.ini", QSettings::IniFormat);
|
|
||||||
settings.setValue("common/api_key", apiKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *loader = new LoaderDialog(this);
|
auto *loader = new LoaderDialog(this);
|
||||||
loader->show();
|
loader->show();
|
||||||
@ -67,8 +65,7 @@ RegistrationWindow::RegistrationWindow(QWidget *parent): QWidget(parent) {
|
|||||||
if (result == 1) {
|
if (result == 1) {
|
||||||
emit onRegisteredSuccess();
|
emit onRegisteredSuccess();
|
||||||
} else if (result == 0) {
|
} else if (result == 0) {
|
||||||
QSettings s("config.ini", QSettings::IniFormat);
|
SettingsDAO::remove("api_key");
|
||||||
s.remove("common/api_key");
|
|
||||||
QMessageBox::critical(this, "Ошибка входа", "Неверный API ключ. Проверьте и попробуйте снова.");
|
QMessageBox::critical(this, "Ошибка входа", "Неверный API ключ. Проверьте и попробуйте снова.");
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(this, "Ошибка подключения",
|
QMessageBox::critical(this, "Ошибка подключения",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user