1
0
forked from BRT/arc
arc/services/scripting/ScriptCache.cpp
slava cae345cf61 Implement JS-based remote scripting framework for dynamic script updates:
- Add Proof of Concept (PoC) for executing JavaScript scripts fetched remotely.
- Integrate `Black::GetProfileInfoScript` logic into a JS version with fallback to C++ on errors.
- Extend `ScreenXmlParser` with additional JS-to-C++ bridge methods.
- Update `config.ini` for scripting configuration.
- Ensure scripts are securely fetched with SHA-256 validation and use an atomic caching mechanism.
- Add developer tools for debugging JS scripts and enable easier script updates without app re-deployment.
2026-05-13 15:42:10 +07:00

89 lines
2.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ScriptCache.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QSaveFile>
#include <QString>
#include "ScriptingConfig.h"
namespace Scripting {
namespace {
QString scriptRelPath(const QString &bank, const QString &scriptName) {
return bank + "/" + scriptName + ".js";
}
QString versionRelPath(const QString &bank, const QString &scriptName) {
return bank + "/" + scriptName + ".version";
}
} // namespace
bool ScriptCache::resolve(const QString &bank, const QString &scriptName,
QString *outPath, QString *outError) {
const QString rel = scriptRelPath(bank, scriptName);
const QString cached = QDir(ScriptingConfig::cacheDir()).filePath(rel);
if (QFileInfo::exists(cached)) {
if (outPath) *outPath = cached;
return true;
}
const QString bundled = QDir(ScriptingConfig::bundledDir()).filePath(rel);
if (QFileInfo::exists(bundled)) {
if (outPath) *outPath = bundled;
return true;
}
if (outError) {
*outError = "not found in cache (" + cached + ") nor bundled (" + bundled + ")";
}
return false;
}
bool ScriptCache::installFromBytes(const QString &bank, const QString &scriptName,
const QString &version, const QByteArray &body,
QString *outError) {
const QString dirPath = QDir(ScriptingConfig::cacheDir()).filePath(bank);
QDir().mkpath(dirPath);
const QString fullPath = QDir(ScriptingConfig::cacheDir()).filePath(scriptRelPath(bank, scriptName));
QSaveFile f(fullPath);
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
if (outError) *outError = "cannot open for write: " + fullPath + "" + f.errorString();
return false;
}
if (f.write(body) != body.size()) {
if (outError) *outError = "short write to " + fullPath;
return false;
}
if (!f.commit()) {
if (outError) *outError = "commit failed for " + fullPath + ": " + f.errorString();
return false;
}
// Sidecar с версией. Не критичен — если упал, скрипт всё равно установлен.
const QString verPath = QDir(ScriptingConfig::cacheDir()).filePath(versionRelPath(bank, scriptName));
QFile vf(verPath);
if (vf.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
vf.write(version.toUtf8());
vf.close();
}
return true;
}
QString ScriptCache::installedVersion(const QString &bank, const QString &scriptName) {
const QString verPath = QDir(ScriptingConfig::cacheDir()).filePath(versionRelPath(bank, scriptName));
QFile vf(verPath);
if (!vf.open(QIODevice::ReadOnly)) return {};
const QByteArray b = vf.readAll();
vf.close();
return QString::fromUtf8(b).trimmed();
}
} // namespace Scripting