UNPKG

@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
"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