fix: 优化客户端错误提示和校验诊断信息
This commit is contained in:
+76
-40
@@ -3,9 +3,10 @@
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QProcess>
|
||||
#include <QProgressDialog>
|
||||
#include <QSaveFile>
|
||||
@@ -164,14 +165,19 @@ int main(int argc, char* argv[])
|
||||
if (offlinePackagePath.isEmpty())
|
||||
logic.reportResult(deviceId, fromVersion, targetVersion, success);
|
||||
};
|
||||
const auto fail = [&](const QString& title, const QString& message,
|
||||
const QString& errorCode = QString("update_failed")) {
|
||||
transaction.markFailed(errorCode, message);
|
||||
reportUpdateResult(false);
|
||||
progress.close();
|
||||
QMessageBox::critical(nullptr, title, message);
|
||||
return -1;
|
||||
};
|
||||
const auto fail = [&](const QString& title, const QString& message,
|
||||
const QString& errorCode = QString("update_failed")) {
|
||||
transaction.markFailed(errorCode, message);
|
||||
reportUpdateResult(false);
|
||||
progress.close();
|
||||
QMessageBox::critical(nullptr, title, message);
|
||||
return -1;
|
||||
};
|
||||
const auto withDetails = [](const QString& message, const QString& details) {
|
||||
return details.trimmed().isEmpty()
|
||||
? message
|
||||
: message + QCoreApplication::translate("Updater", "\n\nDetails:\n%1").arg(details);
|
||||
};
|
||||
const auto configuredName = [&](const QString& key, const QString& fallback) {
|
||||
return ConfigHelper::executableNameForCurrentPlatform(
|
||||
config.getValue("Runtime", key), fallback);
|
||||
@@ -182,25 +188,44 @@ int main(int argc, char* argv[])
|
||||
bool timeoutOk = false;
|
||||
int healthCheckTimeoutMs = config.getValue("Runtime", "health_check_timeout_ms").toInt(&timeoutOk);
|
||||
if (!timeoutOk || healthCheckTimeoutMs < 1000) healthCheckTimeoutMs = 15000;
|
||||
const QString mainAppPath = QDir(runtimeDir).filePath(mainExecutable);
|
||||
const QString updaterPath = QDir(runtimeDir).filePath(updaterExecutable);
|
||||
const QString bootstrapPath = QDir(runtimeDir).filePath(bootstrapExecutable);
|
||||
const QString launchToken = config.getValue("App", "launch_token");
|
||||
const auto launchMainApp = [&](const QString& healthFile = QString()) {
|
||||
QString ticketPath;
|
||||
QString ticketError;
|
||||
const QString launchVersion = config.getValue("App", "current_version");
|
||||
if (!TicketHelper::createTicket(appId, deviceId, launchVersion, launchToken,
|
||||
&ticketPath, &ticketError)) {
|
||||
qDebug() << "Cannot create launch ticket:" << ticketError;
|
||||
return false;
|
||||
}
|
||||
QStringList args{QString("--ticket-file=%1").arg(ticketPath)};
|
||||
if (!healthFile.isEmpty()) args.append(QString("--health-file=%1").arg(healthFile));
|
||||
const bool started = QProcess::startDetached(mainAppPath, args);
|
||||
if (!started) QFile::remove(ticketPath);
|
||||
return started;
|
||||
};
|
||||
const QString mainAppPath = QDir(runtimeDir).filePath(mainExecutable);
|
||||
const QString updaterPath = QDir(runtimeDir).filePath(updaterExecutable);
|
||||
const QString bootstrapPath = QDir(runtimeDir).filePath(bootstrapExecutable);
|
||||
const QString launchToken = config.getValue("App", "launch_token");
|
||||
QString mainStartupError;
|
||||
const auto launchMainApp = [&](const QString& healthFile = QString()) {
|
||||
mainStartupError.clear();
|
||||
if (!QFileInfo::exists(mainAppPath)) {
|
||||
mainStartupError = QCoreApplication::translate(
|
||||
"Updater",
|
||||
"Cannot start the main application because the executable file does not exist.\nExecutable: %1\nCheck main_executable and install_root in the generated client configuration.")
|
||||
.arg(mainAppPath);
|
||||
return false;
|
||||
}
|
||||
QString ticketPath;
|
||||
QString ticketError;
|
||||
const QString launchVersion = config.getValue("App", "current_version");
|
||||
if (!TicketHelper::createTicket(appId, deviceId, launchVersion, launchToken,
|
||||
&ticketPath, &ticketError)) {
|
||||
qDebug() << "Cannot create launch ticket:" << ticketError;
|
||||
mainStartupError = QCoreApplication::translate(
|
||||
"Updater",
|
||||
"Cannot start the main application because the one-time launch ticket could not be created.\nExecutable: %1\nDetails: %2")
|
||||
.arg(mainAppPath, ticketError);
|
||||
return false;
|
||||
}
|
||||
QStringList args{QString("--ticket-file=%1").arg(ticketPath)};
|
||||
if (!healthFile.isEmpty()) args.append(QString("--health-file=%1").arg(healthFile));
|
||||
const bool started = QProcess::startDetached(mainAppPath, args);
|
||||
if (!started) {
|
||||
QFile::remove(ticketPath);
|
||||
mainStartupError = QCoreApplication::translate(
|
||||
"Updater",
|
||||
"Cannot start the main application process.\nExecutable: %1\nTicket file: %2\nHealth file: %3\nCheck file permissions, dependent DLLs/shared libraries, and whether the executable can run independently.")
|
||||
.arg(mainAppPath, ticketPath, healthFile.isEmpty() ? QCoreApplication::translate("Updater", "<not used>") : healthFile);
|
||||
}
|
||||
return started;
|
||||
};
|
||||
const auto bootstrapPlanFile = [&]() {
|
||||
return QDir(updateDir).filePath("bootstrap_plan_" + transaction.transactionId() + ".txt");
|
||||
};
|
||||
@@ -282,16 +307,19 @@ int main(int argc, char* argv[])
|
||||
if (resumingFromBootstrap) {
|
||||
if (!logic.loadManifestCache(manifestCacheDir, targetVersion))
|
||||
return delegateRollback(QCoreApplication::translate("Updater", "Manifest Cache Failed"),
|
||||
QCoreApplication::translate("Updater", "Cannot read the signed manifest cache after Bootstrap installation."));
|
||||
withDetails(QCoreApplication::translate("Updater", "Cannot read the signed manifest cache after Bootstrap installation."),
|
||||
logic.errorString()));
|
||||
} else if (offlinePackagePath.isEmpty()) {
|
||||
logic.getManifest(appId, channel, targetVersion, targetVersionId);
|
||||
}
|
||||
if (!logic.verifyManifestSignature()) {
|
||||
if (resumingFromBootstrap)
|
||||
return delegateRollback(QCoreApplication::translate("Updater", "Security Verification Failed"),
|
||||
QCoreApplication::translate("Updater", "Cannot reverify the manifest signature after Bootstrap installation."));
|
||||
withDetails(QCoreApplication::translate("Updater", "Cannot reverify the manifest signature after Bootstrap installation."),
|
||||
logic.errorString()));
|
||||
return fail(QCoreApplication::translate("Updater", "Security Verification Failed"),
|
||||
QCoreApplication::translate("Updater", "The version manifest signature is invalid. The update has stopped. Please contact the administrator."),
|
||||
withDetails(QCoreApplication::translate("Updater", "The version manifest signature is invalid. The update has stopped. Please contact the administrator."),
|
||||
logic.errorString()),
|
||||
"manifest_signature_invalid");
|
||||
}
|
||||
QStringList obsoletePaths;
|
||||
@@ -309,9 +337,11 @@ int main(int argc, char* argv[])
|
||||
if (!logic.saveManifestCache(manifestCacheDir)) {
|
||||
if (resumingFromBootstrap)
|
||||
return delegateRollback(QCoreApplication::translate("Updater", "Manifest Cache Failed"),
|
||||
QCoreApplication::translate("Updater", "Cannot save the new version manifest cache."));
|
||||
withDetails(QCoreApplication::translate("Updater", "Cannot save the new version manifest cache."),
|
||||
logic.errorString()));
|
||||
return fail(QCoreApplication::translate("Updater", "Manifest Cache Failed"),
|
||||
QCoreApplication::translate("Updater", "Cannot save the new version manifest cache. The update has stopped."),
|
||||
withDetails(QCoreApplication::translate("Updater", "Cannot save the new version manifest cache. The update has stopped."),
|
||||
logic.errorString()),
|
||||
"manifest_cache_failed");
|
||||
}
|
||||
|
||||
@@ -324,7 +354,8 @@ int main(int argc, char* argv[])
|
||||
logic.getDownloadUrl(appId, channel, targetVersion, targetVersionId);
|
||||
if (logic.getFileList().isEmpty())
|
||||
return fail(QCoreApplication::translate("Updater", "No Files to Update"),
|
||||
QCoreApplication::translate("Updater", "The server did not return any version files. The update has stopped."),
|
||||
withDetails(QCoreApplication::translate("Updater", "The server did not return any version files. The update has stopped."),
|
||||
logic.errorString()),
|
||||
"empty_file_list");
|
||||
|
||||
QStorageInfo storage(updateDir);
|
||||
@@ -353,7 +384,8 @@ int main(int argc, char* argv[])
|
||||
if (!logic.downloadAllFiles(stagingDir, targetDir)) {
|
||||
logic.reportDownloadResult(appId, channel, targetVersion, false);
|
||||
return fail(QCoreApplication::translate("Updater", "Download Failed"),
|
||||
QCoreApplication::translate("Updater", "Some files failed to download or failed SHA-256 verification. Please check the network and try again."),
|
||||
withDetails(QCoreApplication::translate("Updater", "Some files failed to download or failed SHA-256 verification. Please check the network, update cache, or server release files and try again."),
|
||||
logic.errorString()),
|
||||
"download_failed");
|
||||
}
|
||||
logic.reportDownloadResult(appId, channel, targetVersion, true);
|
||||
@@ -362,7 +394,8 @@ int main(int argc, char* argv[])
|
||||
setProgress(58, QCoreApplication::translate("Updater", "Verifying complete version files..."));
|
||||
if (!logic.validateLocalFiles(stagingDir, targetDir))
|
||||
return fail(QCoreApplication::translate("Updater", "File Verification Failed"),
|
||||
QCoreApplication::translate("Updater", "The staged files do not match the version manifest. The update has stopped."),
|
||||
withDetails(QCoreApplication::translate("Updater", "The downloaded/staged files do not match the target version manifest. The update has stopped before replacing installed files."),
|
||||
logic.errorString()),
|
||||
"staging_verify_failed");
|
||||
|
||||
QStringList changedPaths;
|
||||
@@ -441,7 +474,8 @@ int main(int argc, char* argv[])
|
||||
setProgress(82, QCoreApplication::translate("Updater", "Verifying Bootstrap installation result..."));
|
||||
if (!transaction.markPostVerify() || !logic.validateLocalFiles(targetDir))
|
||||
return delegateRollback(QCoreApplication::translate("Updater", "Installation Verification Failed"),
|
||||
QCoreApplication::translate("Updater", "New version files failed verification after installation."));
|
||||
withDetails(QCoreApplication::translate("Updater", "New version files failed verification after installation. The updater will roll back to the previous version."),
|
||||
logic.errorString()));
|
||||
for (const QString& path : transaction.obsoletePaths()) {
|
||||
if (QFile::exists(QDir(targetDir).filePath(path)))
|
||||
return delegateRollback(QCoreApplication::translate("Updater", "Obsolete File Cleanup Failed"),
|
||||
@@ -459,7 +493,9 @@ int main(int argc, char* argv[])
|
||||
setProgress(94, QCoreApplication::translate("Updater", "Starting the new version and waiting for health confirmation..."));
|
||||
if (!launchMainApp(healthFile))
|
||||
return delegateRollback(QCoreApplication::translate("Updater", "Startup Failed"),
|
||||
QCoreApplication::translate("Updater", "%1 cannot be started.").arg(mainExecutable));
|
||||
mainStartupError.isEmpty()
|
||||
? QCoreApplication::translate("Updater", "%1 cannot be started.").arg(mainExecutable)
|
||||
: mainStartupError);
|
||||
|
||||
QElapsedTimer healthTimer;
|
||||
healthTimer.start();
|
||||
|
||||
Reference in New Issue
Block a user