@fdm-monster/server
Version:
FDM Monster is a bulk OctoPrint manager to set up, configure and monitor 3D printers. Our aim is to provide extremely optimized websocket performance and reliability.
172 lines (171 loc) • 8.65 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "ClientBundleService", {
enumerable: true,
get: function() {
return ClientBundleService;
}
});
const _admzip = /*#__PURE__*/ _interop_require_default(require("adm-zip"));
const _path = require("path");
const _nodefs = require("node:fs");
const _promises = require("node:fs/promises");
const _fsutils = require("../../utils/fs.utils");
const _semverutils = require("../../utils/semver.utils");
const _serverconstants = require("../../server.constants");
const _semver = require("semver");
const _errorutils = require("../../utils/error.utils");
const _runtimeexceptions = require("../../exceptions/runtime.exceptions");
const _octokit = require("octokit");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
class ClientBundleService {
githubService;
logger;
githubOwner;
githubRepo;
storageRoot;
minVersion;
constructor(loggerFactory, githubService){
this.githubService = githubService;
this.githubOwner = _serverconstants.AppConstants.orgName;
this.githubRepo = _serverconstants.AppConstants.clientRepoName;
this.storageRoot = (0, _fsutils.superRootPath)();
this.minVersion = _serverconstants.AppConstants.defaultClientMinimum;
this.logger = loggerFactory(ClientBundleService.name);
}
get clientPackageJsonPath() {
return (0, _path.join)((0, _fsutils.superRootPath)(), _serverconstants.AppConstants.defaultClientBundleStorage, "package.json");
}
get clientIndexHtmlPath() {
return (0, _path.join)((0, _fsutils.superRootPath)(), _serverconstants.AppConstants.defaultClientBundleStorage, "dist/index.html");
}
async getReleases() {
try {
const [releases, latestRelease] = await Promise.all([
this.githubService.getReleases(this.githubOwner, this.githubRepo),
this.githubService.getLatestRelease(this.githubOwner, this.githubRepo)
]);
return {
minimum: {
tag_name: this.minVersion
},
current: {
tag_name: this.getClientVersion()
},
latest: latestRelease.data,
releases: releases.data
};
} catch (e) {
if (e instanceof _octokit.RequestError) {
this.logger.error(`Github OctoKit error ${(0, _errorutils.errorSummary)(e)}`);
throw new _runtimeexceptions.ExternalServiceError({
error: "Github OctoKit error: " + e.message
}, "GitHub");
}
throw new _runtimeexceptions.InternalServerException("Something went wrong with the request to Github");
}
}
async shouldUpdateWithReason(overrideAutoUpdate, minimumVersion, requestedVersion, allowDowngrade) {
const clientAutoUpdate = _serverconstants.AppConstants.enableClientDistAutoUpdateKey;
const existingVersion = this.getClientVersion();
minimumVersion ??= this.minVersion;
if (!clientAutoUpdate && !overrideAutoUpdate) {
return this.createResponse(false, existingVersion, minimumVersion, requestedVersion, null, "Client auto-update disabled, skipping");
}
if (!existingVersion || !this.doesClientIndexHtmlExist()) {
const reason = !existingVersion ? "Client package.json does not exist" : "Client index.html could not be found";
return this.createResponse(true, existingVersion, minimumVersion, requestedVersion, (0, _semverutils.getMaximumOfVersionsSafe)(minimumVersion, requestedVersion), `${reason}, downloading new release`);
}
const meetsMinimum = (0, _semverutils.checkVersionSatisfiesMinimum)(existingVersion, minimumVersion);
if (!meetsMinimum) {
return this.createResponse(true, existingVersion, minimumVersion, requestedVersion, (0, _semverutils.getMaximumOfVersionsSafe)(minimumVersion, requestedVersion), `Client version ${existingVersion} below minimum ${minimumVersion}, downloading new release`);
}
if (requestedVersion) {
return this.evaluateRequestedVersion(existingVersion, minimumVersion, requestedVersion, allowDowngrade);
}
return this.createResponse(false, existingVersion, minimumVersion, requestedVersion, (0, _semverutils.getMaximumOfVersionsSafe)(minimumVersion, requestedVersion), `Client already satisfies minimum version ${minimumVersion}`);
}
async downloadClientUpdate(releaseTag) {
const result = await this.githubService.getReleaseByTag(this.githubOwner, this.githubRepo, releaseTag);
const release = result.data;
const assetName = `dist-client-${release.tag_name}.zip`;
const asset = release.assets.find((a)=>a.name === assetName);
if (!asset) {
throw new _runtimeexceptions.NotFoundException(`Asset ${assetName} not found in release ${release.tag_name}`);
}
const zipPath = await this.downloadZip(asset.id, asset.name);
await this.extractZip(zipPath);
return release.tag_name;
}
evaluateRequestedVersion(currentVersion, minimumVersion, requestedVersion, allowDowngrade) {
if ((0, _semver.compare)(requestedVersion, minimumVersion) === -1) {
return this.createResponse(false, currentVersion, minimumVersion, requestedVersion, requestedVersion, `Requested version ${requestedVersion} below minimum ${minimumVersion}, skipping`);
}
const versionComparison = (0, _semver.compare)(requestedVersion, currentVersion);
if (versionComparison === 0) {
return this.createResponse(false, currentVersion, minimumVersion, requestedVersion, requestedVersion, `Requested version ${requestedVersion} same as current, skipping`);
} else if (versionComparison === -1) {
return this.createResponse(!!allowDowngrade, currentVersion, minimumVersion, requestedVersion, requestedVersion, allowDowngrade ? `Downgrading to ${requestedVersion} (above minimum ${minimumVersion})` : `Downgrade not allowed, skipping`);
} else {
return this.createResponse(true, currentVersion, minimumVersion, requestedVersion, requestedVersion, `Upgrading from ${currentVersion} to ${requestedVersion}`);
}
}
createResponse(shouldUpdate, currentVersion, minimumVersion, requestedVersion, targetVersion, reason) {
return {
shouldUpdate,
requestedVersion,
currentVersion,
minimumVersion,
targetVersion,
reason
};
}
async downloadZip(assetId, assetName) {
const assetResult = await this.githubService.requestAsset(this.githubOwner, this.githubRepo, assetId);
const dir = (0, _path.join)(this.storageRoot, _serverconstants.AppConstants.defaultClientBundleZipsStorage);
(0, _fsutils.ensureDirExists)(dir);
const path = (0, _path.join)(dir, assetName);
(0, _nodefs.writeFileSync)(path, Buffer.from(assetResult.data));
this.logger.log(`Downloaded client ZIP to ${dir}`);
return path;
}
async extractZip(zipPath) {
const distPath = (0, _path.join)(this.storageRoot, _serverconstants.AppConstants.defaultClientBundleStorage);
(0, _fsutils.ensureDirExists)(distPath);
this.logger.debug(`Clearing contents of ${distPath}`);
for (const item of (await (0, _promises.readdir)(distPath))){
const itemPath = (0, _path.join)(distPath, item);
await (0, _promises.rm)(itemPath, {
force: true,
recursive: true
}).catch((e)=>this.logger.error(`Failed to remove ${itemPath}: ${e.message}`));
}
try {
const zip = new _admzip.default(zipPath);
zip.extractAllTo(distPath);
this.logger.log(`Successfully extracted client to ${distPath}`);
} catch (e) {
this.logger.error(`Extraction failed: ${e.message}`);
throw e;
}
}
doesClientIndexHtmlExist() {
return (0, _nodefs.existsSync)(this.clientIndexHtmlPath);
}
getClientVersion() {
const path = this.clientPackageJsonPath;
if (!(0, _nodefs.existsSync)(path)) {
return null;
}
require.cache[path] = undefined;
const json = require(path);
return json?.version ?? null;
}
}
//# sourceMappingURL=client-bundle.service.js.map