feat: 优化客户端跨平台配置和运行态管理
This commit is contained in:
@@ -35,9 +35,11 @@ bool UpdateTransaction::copyOverwrite(const QString& source, const QString& dest
|
||||
return QFile::copy(source, destination);
|
||||
}
|
||||
|
||||
bool UpdateTransaction::writeState(const QString& status, const QString& errorCode, const QString& message)
|
||||
{
|
||||
m_state["transaction_id"] = m_transactionId;
|
||||
bool UpdateTransaction::writeState(const QString& status, const QString& errorCode, const QString& message)
|
||||
{
|
||||
// upgrade_state.json 是升级事务的“黑匣子”。
|
||||
// 如果替换文件时断电或崩溃,Bootstrap/Updater 会根据这里的状态继续提交或回滚。
|
||||
m_state["transaction_id"] = m_transactionId;
|
||||
m_state["from_version"] = m_fromVersion;
|
||||
m_state["to_version"] = m_toVersion;
|
||||
m_state["status"] = status;
|
||||
@@ -146,9 +148,11 @@ bool UpdateTransaction::recordVerifiedFiles(const QStringList& changedPaths,
|
||||
return writeState("verified");
|
||||
}
|
||||
|
||||
bool UpdateTransaction::backupCurrentFiles()
|
||||
{
|
||||
if (!writeState("waiting_mainapp_exit")) return false;
|
||||
bool UpdateTransaction::backupCurrentFiles()
|
||||
{
|
||||
// 替换前先备份所有将被修改或删除的文件。
|
||||
// 后续健康检查失败时,可以用这些备份恢复到升级前版本。
|
||||
if (!writeState("waiting_mainapp_exit")) return false;
|
||||
QStringList paths = m_changedPaths;
|
||||
paths.append(m_obsoletePaths);
|
||||
for (const QString& path : paths) {
|
||||
@@ -161,9 +165,11 @@ bool UpdateTransaction::backupCurrentFiles()
|
||||
return writeState("backed_up");
|
||||
}
|
||||
|
||||
bool UpdateTransaction::installStagedFiles(QString* failedPath)
|
||||
{
|
||||
if (!writeState("replacing")) return false;
|
||||
bool UpdateTransaction::installStagedFiles(QString* failedPath)
|
||||
{
|
||||
// staging 目录里只放已经下载并校验过 hash 的新文件。
|
||||
// 真正覆盖安装目录时如果任意一个文件失败,就进入 rollback_required。
|
||||
if (!writeState("replacing")) return false;
|
||||
for (const QString& path : m_changedPaths) {
|
||||
const QString source = QDir(m_stagingDir).filePath(path);
|
||||
const QString destination = QDir(m_installDir).filePath(path);
|
||||
@@ -223,10 +229,11 @@ QString UpdateTransaction::backupDir() const { return m_backupDir; }
|
||||
QStringList UpdateTransaction::obsoletePaths() const { return m_obsoletePaths; }
|
||||
QString UpdateTransaction::healthFile() const { return QDir(m_updateDir).filePath("health_" + m_transactionId + ".ok"); }
|
||||
|
||||
bool UpdateTransaction::recoverInterrupted(const QString& installDir, const QString& updateDir,
|
||||
QString* restoredVersion, QString* errorMessage)
|
||||
{
|
||||
QString stateFile = QDir(updateDir.isEmpty() ? QDir(installDir).filePath("update") : updateDir)
|
||||
bool UpdateTransaction::recoverInterrupted(const QString& installDir, const QString& updateDir,
|
||||
QString* restoredVersion, QString* errorMessage)
|
||||
{
|
||||
// 启动时恢复未完成事务:如果上次升级停在替换/验证/回滚中间,优先恢复到可启动状态。
|
||||
QString stateFile = QDir(updateDir.isEmpty() ? QDir(installDir).filePath("update") : updateDir)
|
||||
.filePath("upgrade_state.json");
|
||||
const QString legacyStateFile = QDir(installDir).filePath("update/upgrade_state.json");
|
||||
if (!QFile::exists(stateFile) && QFile::exists(legacyStateFile))
|
||||
|
||||
@@ -31,9 +31,11 @@ UpdaterLogic::UpdaterLogic(QObject* parent)
|
||||
m_serverAddr = ConfigHelper::instance().getValue("Server", "api_base_url");
|
||||
}
|
||||
|
||||
void UpdaterLogic::getManifest(const QString& appId, const QString& channel, const QString& targetVer, int versionId)
|
||||
{
|
||||
QString url = m_serverAddr + "/api/v1/update/manifest";
|
||||
void UpdaterLogic::getManifest(const QString& appId, const QString& channel, const QString& targetVer, int versionId)
|
||||
{
|
||||
// Manifest 由服务端按版本动态生成,描述目标版本包含哪些文件以及每个文件的 SHA256。
|
||||
// Updater 先拿到 Manifest,再请求下载 URL,最后按 Manifest 校验本地文件。
|
||||
QString url = m_serverAddr + "/api/v1/update/manifest";
|
||||
QJsonObject body;
|
||||
body["app_id"] = appId;
|
||||
body["channel"] = channel;
|
||||
@@ -145,9 +147,11 @@ bool UpdaterLogic::verifySignature(const QByteArray& payload, const QString& sig
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UpdaterLogic::verifyManifestSignature(const QString& publicKeyPath) const
|
||||
{
|
||||
if (m_manifest.isEmpty() || m_manifestText.isEmpty())
|
||||
bool UpdaterLogic::verifyManifestSignature(const QString& publicKeyPath) const
|
||||
{
|
||||
// 验签用的是客户端随 SDK 分发的公钥。
|
||||
// 只要服务端私钥没有泄漏,客户端就能发现被篡改的 Manifest。
|
||||
if (m_manifest.isEmpty() || m_manifestText.isEmpty())
|
||||
{
|
||||
qDebug() << "No manifest available to verify";
|
||||
return false;
|
||||
@@ -167,9 +171,11 @@ bool UpdaterLogic::verifyManifestSignature(const QString& publicKeyPath) const
|
||||
return verifySignature(m_manifestText.toUtf8(), signature, path);
|
||||
}
|
||||
|
||||
bool UpdaterLogic::saveManifestCache(const QString& cacheDir) const
|
||||
{
|
||||
if (m_manifest.isEmpty())
|
||||
bool UpdaterLogic::saveManifestCache(const QString& cacheDir) const
|
||||
{
|
||||
// 启动后的完整性检查依赖本地 Manifest 缓存。
|
||||
// 升级成功后缓存签名清单,下一次离线启动也能校验当前版本文件。
|
||||
if (m_manifest.isEmpty())
|
||||
return false;
|
||||
|
||||
QDir dir(cacheDir);
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include <QApplication>
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
|
||||
Reference in New Issue
Block a user