- Add findSumEditText to ScreenXmlParser for enhanced sum field detection.
- Update `PayByPhoneScript` and `PayByCardScript` to use `findSumEditText` as a fallback. - Enhance `FileLogWindow` to export logs and database files to backend via `NetworkService`. - Add `networkService` getter in `TaskDumpRecorder` for centralizing access to `NetworkService`.
This commit is contained in:
parent
558e881c75
commit
9c2ec74f76
@ -103,6 +103,10 @@ void TaskDumpRecorder::setNetworkService(NetworkService *service) {
|
|||||||
networkServiceRef() = service;
|
networkServiceRef() = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkService *TaskDumpRecorder::networkService() {
|
||||||
|
return networkServiceRef().data();
|
||||||
|
}
|
||||||
|
|
||||||
TaskDumpRecorder *TaskDumpRecorder::current() {
|
TaskDumpRecorder *TaskDumpRecorder::current() {
|
||||||
return tlsRecorder().hasLocalData() ? tlsRecorder().localData() : nullptr;
|
return tlsRecorder().hasLocalData() ? tlsRecorder().localData() : nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,10 @@ public:
|
|||||||
// но не отправляет его — только пишет warning в лог.
|
// но не отправляет его — только пишет warning в лог.
|
||||||
static void setNetworkService(NetworkService *service);
|
static void setNetworkService(NetworkService *service);
|
||||||
|
|
||||||
|
// Доступ к зарегистрированному NetworkService для других потребителей,
|
||||||
|
// которые тоже хотят дернуть /monitoring/dump (отправка ручных логов).
|
||||||
|
static NetworkService* networkService();
|
||||||
|
|
||||||
// Возвращает рекордер текущего потока (nullptr если задача не начата).
|
// Возвращает рекордер текущего потока (nullptr если задача не начата).
|
||||||
static TaskDumpRecorder* current();
|
static TaskDumpRecorder* current();
|
||||||
|
|
||||||
|
|||||||
@ -243,6 +243,7 @@ void PayByCardScript::makePayment(
|
|||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||||||
closeGooglePlayBannerIfVisible(deviceId, width, height);
|
closeGooglePlayBannerIfVisible(deviceId, width, height);
|
||||||
sumEditText = xmlScreenParser.findEditTextByHint(QString::fromUtf8("Сумма"));
|
sumEditText = xmlScreenParser.findEditTextByHint(QString::fromUtf8("Сумма"));
|
||||||
|
if (sumEditText.isEmpty()) sumEditText = xmlScreenParser.findSumEditText();
|
||||||
if (!sumEditText.isEmpty()) break;
|
if (!sumEditText.isEmpty()) break;
|
||||||
qDebug() << "[Dushanbe::PayByCard] Waiting for sum input, attempt" << attempt + 1 << "/ 10";
|
qDebug() << "[Dushanbe::PayByCard] Waiting for sum input, attempt" << attempt + 1 << "/ 10";
|
||||||
QThread::msleep(1500);
|
QThread::msleep(1500);
|
||||||
|
|||||||
@ -244,6 +244,7 @@ void PayByPhoneScript::makePayment(
|
|||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||||||
closeGooglePlayBannerIfVisible(deviceId, width, height);
|
closeGooglePlayBannerIfVisible(deviceId, width, height);
|
||||||
sumEditText = xmlScreenParser.findEditTextByHint(QString::fromUtf8("Сумма"));
|
sumEditText = xmlScreenParser.findEditTextByHint(QString::fromUtf8("Сумма"));
|
||||||
|
if (sumEditText.isEmpty()) sumEditText = xmlScreenParser.findSumEditText();
|
||||||
if (!sumEditText.isEmpty()) break;
|
if (!sumEditText.isEmpty()) break;
|
||||||
qDebug() << "[Dushanbe::PayByPhone] Waiting for sum input, attempt" << attempt + 1 << "/ 10";
|
qDebug() << "[Dushanbe::PayByPhone] Waiting for sum input, attempt" << attempt + 1 << "/ 10";
|
||||||
QThread::msleep(1500);
|
QThread::msleep(1500);
|
||||||
|
|||||||
@ -526,6 +526,27 @@ Node ScreenXmlParser::findEditTextByHint(const QString &hint) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node ScreenXmlParser::findSumEditText() {
|
||||||
|
// Якорь: View "Мин. сумма: ..." расположен сразу под полем суммы.
|
||||||
|
Node anchor;
|
||||||
|
for (const Node &node : m_nodes) {
|
||||||
|
if (node.contentDesc.contains(QString::fromUtf8("Мин. сумма"))) {
|
||||||
|
anchor = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (anchor.x1() < 0) return {};
|
||||||
|
|
||||||
|
// Ближайший EditText, который заканчивается выше якоря.
|
||||||
|
Node best;
|
||||||
|
for (const Node &node : m_nodes) {
|
||||||
|
if (node.className != "android.widget.EditText") continue;
|
||||||
|
if (node.y2() > anchor.y1()) continue;
|
||||||
|
if (best.x1() < 0 || node.y2() > best.y2()) best = node;
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
Node ScreenXmlParser::tryToFindSystemPermissionDenyButton() {
|
Node ScreenXmlParser::tryToFindSystemPermissionDenyButton() {
|
||||||
for (const Node &node : m_nodes) {
|
for (const Node &node : m_nodes) {
|
||||||
if (node.resourceId == "com.android.permissioncontroller:id/permission_deny_button"
|
if (node.resourceId == "com.android.permissioncontroller:id/permission_deny_button"
|
||||||
|
|||||||
@ -119,6 +119,11 @@ public:
|
|||||||
// Находит EditText по hint
|
// Находит EditText по hint
|
||||||
Node findEditTextByHint(const QString &hint);
|
Node findEditTextByHint(const QString &hint);
|
||||||
|
|
||||||
|
// Поле ввода суммы на экране перевода. У EditText'а нет hint/content-desc
|
||||||
|
// (NAF), поэтому ищем по якорю — View "Мин. сумма: ..." под полем —
|
||||||
|
// и берём ближайший EditText выше якоря.
|
||||||
|
Node findSumEditText();
|
||||||
|
|
||||||
// Находит кнопку закрытия Google Play in-app update bottom sheet
|
// Находит кнопку закрытия Google Play in-app update bottom sheet
|
||||||
// ("com.android.vending" оверлей "Доступно обновление"), если он показан
|
// ("com.android.vending" оверлей "Доступно обновление"), если он показан
|
||||||
Node tryToFindGooglePlayBanner(int screenWidth, int screenHeight);
|
Node tryToFindGooglePlayBanner(int screenWidth, int screenHeight);
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
#include "FileLogWindow.h"
|
#include "FileLogWindow.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QDateTime>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QHttpMultiPart>
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QNetworkAccessManager>
|
#include <QMetaObject>
|
||||||
#include <QNetworkReply>
|
#include <QSettings>
|
||||||
#include <QSslSocket>
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QTemporaryFile>
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "dao/EventDAO.h"
|
#include "dump/TaskDumpRecorder.h"
|
||||||
#include "dao/GeneralLogDAO.h"
|
#include "dao/GeneralLogDAO.h"
|
||||||
#include "dao/SettingsDAO.h"
|
#include "dao/SettingsDAO.h"
|
||||||
#include "log/LogDetailWindow.h"
|
#include "log/LogDetailWindow.h"
|
||||||
|
#include "net/NetworkService.h"
|
||||||
|
|
||||||
static constexpr int COL_ID = 0;
|
static constexpr int COL_ID = 0;
|
||||||
static constexpr int COL_TIME = 1;
|
static constexpr int COL_TIME = 1;
|
||||||
@ -105,7 +105,7 @@ FileLogWindow::FileLogWindow(QWidget *parent) : QDialog(parent) {
|
|||||||
detail->show();
|
detail->show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(m_sendBtn, &QPushButton::clicked, this, &FileLogWindow::sendLogToTelegram);
|
connect(m_sendBtn, &QPushButton::clicked, this, &FileLogWindow::sendLogsToBackend);
|
||||||
|
|
||||||
loadPage();
|
loadPage();
|
||||||
}
|
}
|
||||||
@ -163,114 +163,84 @@ void FileLogWindow::updatePagination() {
|
|||||||
m_nextBtn->setEnabled(m_page < m_totalPages - 1);
|
m_nextBtn->setEnabled(m_page < m_totalPages - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLogWindow::sendLogToTelegram() {
|
void FileLogWindow::sendLogsToBackend() {
|
||||||
qDebug() << "[sendLogToTelegram] step 1: checking log file";
|
qDebug() << "[sendLogsToBackend] step 1: checking log file";
|
||||||
const QString logPath = QCoreApplication::applicationDirPath() + "/application.log";
|
const QString logPath = QCoreApplication::applicationDirPath() + "/application.log";
|
||||||
if (!QFile::exists(logPath)) {
|
if (!QFile::exists(logPath)) {
|
||||||
QMessageBox::warning(this, "Ошибка", "Файл application.log не найден");
|
QMessageBox::warning(this, "Ошибка", "Файл application.log не найден");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Резолвим путь к БД из config.ini (как DatabaseManager): [db]/dbPath,
|
||||||
|
// с разворотом '~' в домашнюю директорию и приведением к абсолютному пути
|
||||||
|
// относительно applicationDirPath, чтобы не зависеть от текущего CWD.
|
||||||
|
const QSettings settings("config.ini", QSettings::IniFormat);
|
||||||
|
QString dbPath = settings.value("db/dbPath").toString();
|
||||||
|
if (dbPath.startsWith("~")) {
|
||||||
|
dbPath.replace(0, 1, QDir::homePath());
|
||||||
|
}
|
||||||
|
if (!dbPath.isEmpty() && QFileInfo(dbPath).isRelative()) {
|
||||||
|
dbPath = QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(dbPath);
|
||||||
|
}
|
||||||
|
if (dbPath.isEmpty() || !QFile::exists(dbPath)) {
|
||||||
|
QMessageBox::warning(this, "Ошибка",
|
||||||
|
"Файл БД не найден: " + (dbPath.isEmpty() ? QStringLiteral("<пусто>") : dbPath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkService *svc = TaskDumpRecorder::networkService();
|
||||||
|
if (!svc) {
|
||||||
|
QMessageBox::warning(this, "Ошибка",
|
||||||
|
"NetworkService не инициализирован — отправка на бэкенд недоступна.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_sendBtn->setEnabled(false);
|
m_sendBtn->setEnabled(false);
|
||||||
m_sendBtn->setText("Подготовка...");
|
m_sendBtn->setText("Подготовка...");
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] step 2: creating tmp dir";
|
qDebug() << "[sendLogsToBackend] step 2: creating tmp dir";
|
||||||
// Собираем временную папку для архива
|
|
||||||
const QString tmpDir = QDir::tempPath() + "/arc_logs_export";
|
const QString tmpDir = QDir::tempPath() + "/arc_logs_export";
|
||||||
|
QDir(tmpDir).removeRecursively();
|
||||||
QDir().mkpath(tmpDir);
|
QDir().mkpath(tmpDir);
|
||||||
|
|
||||||
// 1. Копируем application.log
|
// 1. application.log целиком
|
||||||
QFile::remove(tmpDir + "/application.log");
|
|
||||||
QFile::copy(logPath, tmpDir + "/application.log");
|
QFile::copy(logPath, tmpDir + "/application.log");
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] step 3: exporting errors";
|
// 2. БД + WAL/SHM-сайдкары (если есть) — иначе при включённом WAL
|
||||||
// 2. Экспортируем events с ошибками в errors.txt
|
// снимок будет рассогласованным.
|
||||||
const QList<EventInfo> errors = EventDAO::getAllEvents(EventStatus::Error);
|
const QString dbBaseName = QFileInfo(dbPath).fileName();
|
||||||
if (!errors.isEmpty()) {
|
QFile::copy(dbPath, tmpDir + "/" + dbBaseName);
|
||||||
QFile errFile(tmpDir + "/errors.txt");
|
for (const QString &suffix : {QStringLiteral("-wal"), QStringLiteral("-shm")}) {
|
||||||
if (errFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
const QString sidecar = dbPath + suffix;
|
||||||
QTextStream out(&errFile);
|
if (QFile::exists(sidecar)) {
|
||||||
for (const EventInfo &e : errors) {
|
QFile::copy(sidecar, tmpDir + "/" + dbBaseName + suffix);
|
||||||
out << "--- Event #" << e.id << " ---\n"
|
|
||||||
<< "Type: " << eventTypeToString(e.type) << "\n"
|
|
||||||
<< "Bank: " << e.bankName << "\n"
|
|
||||||
<< "Device: " << e.deviceId << "\n"
|
|
||||||
<< "Amount: " << e.amount << "\n"
|
|
||||||
<< "Phone: " << e.phone << "\n"
|
|
||||||
<< "Comment: " << e.comment << "\n"
|
|
||||||
<< "Time: " << e.timestamp.toString(Qt::ISODate) << "\n"
|
|
||||||
<< "JSON: " << e.json << "\n\n";
|
|
||||||
}
|
|
||||||
errFile.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] step 4: exporting event screenshots";
|
qDebug() << "[sendLogsToBackend] step 3: creating archive";
|
||||||
// 3. Экспортируем скриншоты ошибок из events
|
|
||||||
QDir().mkpath(tmpDir + "/screenshots");
|
|
||||||
int screenshotCount = 0;
|
|
||||||
for (const EventInfo &e : errors) {
|
|
||||||
const QByteArray screenshot = EventDAO::getScreenshot(e.id);
|
|
||||||
if (screenshot.isEmpty()) continue;
|
|
||||||
const QString fileName = QString("screenshots/%1_%2_%3.png")
|
|
||||||
.arg(e.id)
|
|
||||||
.arg(eventTypeToString(e.type))
|
|
||||||
.arg(e.timestamp.toString("yyyyMMdd_HHmmss"));
|
|
||||||
QFile imgFile(tmpDir + "/" + fileName);
|
|
||||||
if (imgFile.open(QIODevice::WriteOnly)) {
|
|
||||||
imgFile.write(screenshot);
|
|
||||||
imgFile.close();
|
|
||||||
++screenshotCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] step 5: exporting log screenshots";
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] step 6: creating archive with powershell";
|
|
||||||
// 4. Создаём архив
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
const QString archivePath = QDir::tempPath() + "/arc_logs.zip";
|
const QString archivePath = QDir::tempPath() + "/arc_logs.zip";
|
||||||
QFile::remove(archivePath);
|
QFile::remove(archivePath);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
QProcess archiver;
|
QProcess archiver;
|
||||||
archiver.setWorkingDirectory(tmpDir);
|
archiver.setWorkingDirectory(tmpDir);
|
||||||
archiver.start("powershell", QStringList()
|
archiver.start("powershell", QStringList()
|
||||||
<< "-NoProfile" << "-Command"
|
<< "-NoProfile" << "-Command"
|
||||||
<< QString("Compress-Archive -Path '%1/*' -DestinationPath '%2' -Force").arg(tmpDir, archivePath));
|
<< QString("Compress-Archive -Path '%1/*' -DestinationPath '%2' -Force").arg(tmpDir, archivePath));
|
||||||
if (!archiver.waitForFinished(30000)) {
|
if (!archiver.waitForFinished(30000)) {
|
||||||
qWarning() << "[sendLogToTelegram] powershell archiver timeout/error:" << archiver.errorString();
|
qWarning() << "[sendLogsToBackend] powershell archiver timeout/error:" << archiver.errorString();
|
||||||
}
|
}
|
||||||
qDebug() << "[sendLogToTelegram] step 6.1: archiver exit code:" << archiver.exitCode()
|
qDebug() << "[sendLogsToBackend] archiver exit code:" << archiver.exitCode()
|
||||||
<< "stdout:" << archiver.readAllStandardOutput()
|
<< "stdout:" << archiver.readAllStandardOutput()
|
||||||
<< "stderr:" << archiver.readAllStandardError();
|
<< "stderr:" << archiver.readAllStandardError();
|
||||||
#else
|
#else
|
||||||
const QString archivePath = QDir::tempPath() + "/arc_logs.zip";
|
|
||||||
QFile::remove(archivePath);
|
|
||||||
QProcess archiver;
|
QProcess archiver;
|
||||||
archiver.setWorkingDirectory(tmpDir);
|
archiver.setWorkingDirectory(tmpDir);
|
||||||
archiver.start("zip", QStringList() << "-r" << archivePath << ".");
|
archiver.start("zip", QStringList() << "-r" << archivePath << ".");
|
||||||
archiver.waitForFinished(30000);
|
archiver.waitForFinished(30000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Чистим временную папку
|
|
||||||
QDir(tmpDir).removeRecursively();
|
QDir(tmpDir).removeRecursively();
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] step 7: archive exists:" << QFile::exists(archivePath)
|
|
||||||
<< "path:" << archivePath;
|
|
||||||
|
|
||||||
if (!QFile::exists(archivePath)) {
|
if (!QFile::exists(archivePath)) {
|
||||||
QMessageBox::warning(this, "Ошибка", "Не удалось создать архив");
|
QMessageBox::warning(this, "Ошибка", "Не удалось создать архив");
|
||||||
m_sendBtn->setEnabled(true);
|
m_sendBtn->setEnabled(true);
|
||||||
@ -278,88 +248,46 @@ void FileLogWindow::sendLogToTelegram() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sendBtn->setText("Отправка...");
|
QFile zipFile(archivePath);
|
||||||
|
if (!zipFile.open(QIODevice::ReadOnly)) {
|
||||||
QFile *zipFile = new QFile(archivePath);
|
|
||||||
if (!zipFile->open(QIODevice::ReadOnly)) {
|
|
||||||
QMessageBox::warning(this, "Ошибка", "Не удалось открыть архив");
|
QMessageBox::warning(this, "Ошибка", "Не удалось открыть архив");
|
||||||
delete zipFile;
|
QFile::remove(archivePath);
|
||||||
m_sendBtn->setEnabled(true);
|
m_sendBtn->setEnabled(true);
|
||||||
m_sendBtn->setText("Отправить");
|
m_sendBtn->setText("Отправить");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const QByteArray archiveBytes = zipFile.readAll();
|
||||||
// Проверяем поддержку SSL
|
zipFile.close();
|
||||||
if (!QSslSocket::supportsSsl()) {
|
|
||||||
qWarning() << "[sendLogToTelegram] SSL not supported! Build:" << QSslSocket::sslLibraryBuildVersionString()
|
|
||||||
<< "Runtime:" << QSslSocket::sslLibraryVersionString();
|
|
||||||
QMessageBox::warning(this, "Ошибка", "SSL не поддерживается.\nНевозможно отправить логи через HTTPS.");
|
|
||||||
delete zipFile;
|
|
||||||
m_sendBtn->setEnabled(true);
|
|
||||||
m_sendBtn->setText("Отправить");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] SSL OK, sending...";
|
|
||||||
|
|
||||||
// Telegram Bot API
|
|
||||||
const QString botToken = "8256716069:AAFgZRB_0Y6KTpqFQmCxIlZiWdY5m2dR2D8";
|
|
||||||
const QString chatId = "-5177086220";
|
|
||||||
const QString desktopId = SettingsDAO::get("desktop_id");
|
|
||||||
const QString caption = "Logs from desktop: " + desktopId
|
|
||||||
+ "\nErrors: " + QString::number(errors.size())
|
|
||||||
+ "\nScreenshots: " + QString::number(screenshotCount);
|
|
||||||
|
|
||||||
const QUrl url("https://api.telegram.org/bot" + botToken + "/sendDocument");
|
|
||||||
|
|
||||||
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
|
||||||
|
|
||||||
QHttpPart chatPart;
|
|
||||||
chatPart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="chat_id")");
|
|
||||||
chatPart.setBody(chatId.toUtf8());
|
|
||||||
multiPart->append(chatPart);
|
|
||||||
|
|
||||||
QHttpPart captionPart;
|
|
||||||
captionPart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="caption")");
|
|
||||||
captionPart.setBody(caption.toUtf8());
|
|
||||||
multiPart->append(captionPart);
|
|
||||||
|
|
||||||
QHttpPart filePart;
|
|
||||||
filePart.setHeader(QNetworkRequest::ContentTypeHeader, "application/zip");
|
|
||||||
filePart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
|
||||||
R"(form-data; name="document"; filename="arc_logs.zip")");
|
|
||||||
filePart.setBodyDevice(zipFile);
|
|
||||||
zipFile->setParent(multiPart);
|
|
||||||
multiPart->append(filePart);
|
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] step 8: creating network request";
|
|
||||||
auto *manager = new QNetworkAccessManager(this);
|
|
||||||
|
|
||||||
// Принудительно используем Schannel (нативный Windows TLS) вместо OpenSSL
|
|
||||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
|
||||||
sslConfig.setBackendConfigurationOption("SslProtocol", "TlsV1_2OrLater");
|
|
||||||
QNetworkRequest req(url);
|
|
||||||
req.setSslConfiguration(sslConfig);
|
|
||||||
|
|
||||||
qDebug() << "[sendLogToTelegram] step 9: posting to telegram..."
|
|
||||||
<< "SSL backend:" << QSslSocket::activeBackend();
|
|
||||||
QNetworkReply *reply = manager->post(req, multiPart);
|
|
||||||
qDebug() << "[sendLogToTelegram] step 10: post sent, waiting for reply";
|
|
||||||
multiPart->setParent(reply);
|
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::finished, this, [this, reply, manager, archivePath]() {
|
|
||||||
reply->deleteLater();
|
|
||||||
manager->deleteLater();
|
|
||||||
QFile::remove(archivePath);
|
QFile::remove(archivePath);
|
||||||
|
|
||||||
|
if (archiveBytes.isEmpty()) {
|
||||||
|
QMessageBox::warning(this, "Ошибка", "Архив пуст — отправлять нечего");
|
||||||
m_sendBtn->setEnabled(true);
|
m_sendBtn->setEnabled(true);
|
||||||
m_sendBtn->setText("Отправить");
|
m_sendBtn->setText("Отправить");
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
QMessageBox::warning(this, "Ошибка", "Не удалось отправить: " + reply->errorString());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox::information(this, "Готово", "Логи отправлены в Telegram");
|
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||||
});
|
const QString text = QStringLiteral("kind: manual_logs\ndesktop: %1\ntime: %2")
|
||||||
|
.arg(desktopId, QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||||
|
|
||||||
|
qDebug() << "[sendLogsToBackend] dispatching to NetworkService::postMonitoringDump"
|
||||||
|
<< "archive.size=" << archiveBytes.size();
|
||||||
|
const bool dispatched = QMetaObject::invokeMethod(svc, "postMonitoringDump",
|
||||||
|
Qt::QueuedConnection,
|
||||||
|
Q_ARG(QString, text),
|
||||||
|
Q_ARG(QByteArray, archiveBytes),
|
||||||
|
Q_ARG(QByteArray, QByteArray()));
|
||||||
|
|
||||||
|
m_sendBtn->setEnabled(true);
|
||||||
|
m_sendBtn->setText("Отправить");
|
||||||
|
|
||||||
|
if (!dispatched) {
|
||||||
|
QMessageBox::warning(this, "Ошибка",
|
||||||
|
"Не удалось поставить отправку в очередь NetworkService.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::information(this, "Готово",
|
||||||
|
"Логи поставлены в очередь на отправку на бэкенд.");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user