2026-07-10 02:45:13 +00:00
|
|
|
#include "UpdateLogic.h"
|
|
|
|
|
#include "ConfigHelper.h"
|
|
|
|
|
#include <QDebug>
|
2026-07-14 01:37:06 +00:00
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QSaveFile>
|
|
|
|
|
#include "PolicyHelper.h"
|
|
|
|
|
#include "LocalStateHelper.h"
|
2026-07-10 02:45:13 +00:00
|
|
|
|
|
|
|
|
UpdateLogic::UpdateLogic(QObject* parent)
|
|
|
|
|
: QObject(parent)
|
|
|
|
|
{
|
|
|
|
|
ConfigHelper& cfg = ConfigHelper::instance();
|
|
|
|
|
m_serverAddr = cfg.getValue("Server", "api_base_url");
|
|
|
|
|
m_appId = cfg.getValue("App", "app_id");
|
|
|
|
|
m_curVer = cfg.getValue("App", "current_version");
|
|
|
|
|
m_channel = cfg.getValue("App", "channel");
|
|
|
|
|
|
|
|
|
|
// Debug print config
|
|
|
|
|
qDebug() << "Read server addr:" << m_serverAddr;
|
|
|
|
|
qDebug() << "Read app id:" << m_appId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateLogic::checkUpdate()
|
|
|
|
|
{
|
|
|
|
|
if (m_serverAddr.isEmpty() || m_appId.isEmpty() || m_curVer.isEmpty() || m_channel.isEmpty())
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "Config incomplete, abort update check";
|
|
|
|
|
m_needUpdate = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QString url = m_serverAddr + "/api/v1/update/check";
|
|
|
|
|
QJsonObject body;
|
|
|
|
|
body["app_id"] = m_appId;
|
|
|
|
|
body["current_version"] = m_curVer;
|
|
|
|
|
body["channel"] = m_channel;
|
|
|
|
|
const int configuredProtocol = ConfigHelper::instance().getValue("App", "client_protocol").toInt();
|
|
|
|
|
body["client_protocol"] = qMax(3, configuredProtocol);
|
|
|
|
|
|
|
|
|
|
m_http.postRequest(url, body, [this](int code, const QJsonObject& resp)
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "Check update HTTP code:" << code;
|
|
|
|
|
m_lastStatusCode = code;
|
|
|
|
|
m_checkResp = resp;
|
|
|
|
|
m_networkOk = (code == 200);
|
|
|
|
|
if (code == 200)
|
|
|
|
|
{
|
|
|
|
|
const QString appDir = QApplication::applicationDirPath();
|
|
|
|
|
PolicyHelper onlinePolicy(appDir);
|
|
|
|
|
LocalStateHelper state(appDir);
|
|
|
|
|
const bool stateLoaded = state.loadState();
|
|
|
|
|
const bool policyValid = onlinePolicy.loadPolicyObject(
|
|
|
|
|
resp.value("policy").toObject(), resp.value("policy_text").toString());
|
|
|
|
|
if (!policyValid || !stateLoaded
|
|
|
|
|
|| state.isPolicySeqRolledBack(onlinePolicy.policySeq())
|
|
|
|
|
|| !onlinePolicy.savePolicy())
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "Online policy rejected:" << onlinePolicy.errorString();
|
|
|
|
|
m_networkOk = false;
|
|
|
|
|
m_needUpdate = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
state.updateOnlineVerified(onlinePolicy.policySeq());
|
|
|
|
|
if (!state.saveState())
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "Cannot persist online policy state";
|
|
|
|
|
m_networkOk = false;
|
|
|
|
|
m_needUpdate = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_needUpdate = resp["need_update"].toBool();
|
|
|
|
|
m_latestVer = resp["latest_version"].toString();
|
|
|
|
|
qDebug() << "Need update:" << m_needUpdate;
|
|
|
|
|
qDebug() << "Latest version:" << m_latestVer;
|
|
|
|
|
qDebug() << "Policy seq:" << onlinePolicy.policySeq();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_needUpdate = false;
|
|
|
|
|
qDebug() << "Check update api failed";
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-14 01:37:06 +00:00
|
|
|
void UpdateLogic::getDownloadUrl(const QString& appId, const QString& channel, const QString& ver, int verId)
|
|
|
|
|
{
|
2026-07-10 02:45:13 +00:00
|
|
|
QString url = m_serverAddr + "/api/v1/update/download-url";
|
|
|
|
|
QJsonObject body;
|
|
|
|
|
body["app_id"] = appId;
|
|
|
|
|
body["channel"] = channel;
|
|
|
|
|
body["version"] = ver;
|
|
|
|
|
body["version_id"] = verId;
|
|
|
|
|
QJsonArray files;
|
|
|
|
|
body["files"] = files;
|
|
|
|
|
|
|
|
|
|
m_http.postRequest(url, body, [](int code, const QJsonObject& resp)
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "\n========== File Download Url ==========";
|
|
|
|
|
qDebug() << resp["files"].toArray();
|
|
|
|
|
});
|
2026-07-14 01:37:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UpdateLogic::cacheManifest(const QString& appId, const QString& channel, const QString& version,
|
|
|
|
|
int versionId, const QString& cacheDir)
|
|
|
|
|
{
|
|
|
|
|
m_error.clear();
|
|
|
|
|
if (appId.isEmpty() || channel.isEmpty() || version.isEmpty() || versionId <= 0) {
|
|
|
|
|
m_error = "manifest identity is incomplete";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QJsonObject response;
|
|
|
|
|
int statusCode = 0;
|
|
|
|
|
QJsonObject body;
|
|
|
|
|
body["app_id"] = appId;
|
|
|
|
|
body["channel"] = channel;
|
|
|
|
|
body["version"] = version;
|
|
|
|
|
body["version_id"] = versionId;
|
|
|
|
|
m_http.postRequest(m_serverAddr + "/api/v1/update/manifest", body,
|
|
|
|
|
[&](int code, const QJsonObject& resp) {
|
|
|
|
|
statusCode = code;
|
|
|
|
|
response = resp;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (statusCode != 200) {
|
|
|
|
|
m_error = QString("manifest request failed (HTTP %1)").arg(statusCode);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QJsonObject manifest = response.value("manifest").toObject();
|
|
|
|
|
const QString manifestText = response.value("manifest_text").toString();
|
|
|
|
|
if (manifest.isEmpty() || manifestText.isEmpty()) {
|
|
|
|
|
m_error = "manifest response is incomplete";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (manifest.value("app_id").toString() != appId
|
|
|
|
|
|| manifest.value("channel").toString() != channel
|
|
|
|
|
|| manifest.value("version").toString() != version) {
|
|
|
|
|
m_error = "manifest identity does not match current version";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDir dir(cacheDir);
|
|
|
|
|
if (!dir.exists() && !dir.mkpath(".")) {
|
|
|
|
|
m_error = "cannot create manifest cache directory";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QJsonObject wrapper;
|
|
|
|
|
wrapper["manifest"] = manifest;
|
|
|
|
|
wrapper["manifest_text"] = manifestText;
|
|
|
|
|
QSaveFile file(dir.filePath("manifest_" + version + ".json"));
|
|
|
|
|
const QByteArray bytes = QJsonDocument(wrapper).toJson(QJsonDocument::Indented);
|
|
|
|
|
if (!file.open(QIODevice::WriteOnly) || file.write(bytes) != bytes.size() || !file.commit()) {
|
|
|
|
|
m_error = "cannot save signed manifest cache";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
qDebug() << "Current version manifest cached to" << file.fileName();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateLogic::reportUpdateResult(const QString& deviceId, const QString& fromVer, const QString& toVer, bool success)
|
|
|
|
|
{
|
2026-07-10 02:45:13 +00:00
|
|
|
QString url = m_serverAddr + "/api/v1/update/report";
|
|
|
|
|
QJsonObject body;
|
|
|
|
|
body["app_id"] = m_appId;
|
|
|
|
|
body["device_id"] = deviceId;
|
|
|
|
|
body["from_version"] = fromVer;
|
|
|
|
|
body["to_version"] = toVer;
|
|
|
|
|
body["result"] = success ? "success" : "fail";
|
|
|
|
|
|
|
|
|
|
m_http.postRequest(url, body, [](int code, const QJsonObject& resp)
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "\n========== Update Report Result ==========";
|
|
|
|
|
qDebug() << resp;
|
|
|
|
|
});
|
2026-07-14 01:37:06 +00:00
|
|
|
}
|