UNPKG

6.95 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.createTempUpdateFile = exports.DownloadedUpdateHelper = void 0;
4const crypto_1 = require("crypto");
5const fs_1 = require("fs");
6// @ts-ignore
7const isEqual = require("lodash.isequal");
8const fs_extra_1 = require("fs-extra");
9const path = require("path");
10/** @private **/
11class DownloadedUpdateHelper {
12 constructor(cacheDir) {
13 this.cacheDir = cacheDir;
14 this._file = null;
15 this._packageFile = null;
16 this.versionInfo = null;
17 this.fileInfo = null;
18 this._downloadedFileInfo = null;
19 }
20 get downloadedFileInfo() {
21 return this._downloadedFileInfo;
22 }
23 get file() {
24 return this._file;
25 }
26 get packageFile() {
27 return this._packageFile;
28 }
29 get cacheDirForPendingUpdate() {
30 return path.join(this.cacheDir, "pending");
31 }
32 async validateDownloadedPath(updateFile, updateInfo, fileInfo, logger) {
33 if (this.versionInfo != null && this.file === updateFile && this.fileInfo != null) {
34 // update has already been downloaded from this running instance
35 // check here only existence, not checksum
36 if (isEqual(this.versionInfo, updateInfo) && isEqual(this.fileInfo.info, fileInfo.info) && (await fs_extra_1.pathExists(updateFile))) {
37 return updateFile;
38 }
39 else {
40 return null;
41 }
42 }
43 // update has already been downloaded from some previous app launch
44 const cachedUpdateFile = await this.getValidCachedUpdateFile(fileInfo, logger);
45 if (cachedUpdateFile === null) {
46 return null;
47 }
48 logger.info(`Update has already been downloaded to ${updateFile}).`);
49 this._file = cachedUpdateFile;
50 return cachedUpdateFile;
51 }
52 async setDownloadedFile(downloadedFile, packageFile, versionInfo, fileInfo, updateFileName, isSaveCache) {
53 this._file = downloadedFile;
54 this._packageFile = packageFile;
55 this.versionInfo = versionInfo;
56 this.fileInfo = fileInfo;
57 this._downloadedFileInfo = {
58 fileName: updateFileName,
59 sha512: fileInfo.info.sha512,
60 isAdminRightsRequired: fileInfo.info.isAdminRightsRequired === true,
61 };
62 if (isSaveCache) {
63 await fs_extra_1.outputJson(this.getUpdateInfoFile(), this._downloadedFileInfo);
64 }
65 }
66 async clear() {
67 this._file = null;
68 this._packageFile = null;
69 this.versionInfo = null;
70 this.fileInfo = null;
71 await this.cleanCacheDirForPendingUpdate();
72 }
73 async cleanCacheDirForPendingUpdate() {
74 try {
75 // remove stale data
76 await fs_extra_1.emptyDir(this.cacheDirForPendingUpdate);
77 }
78 catch (ignore) {
79 // ignore
80 }
81 }
82 /**
83 * Returns "update-info.json" which is created in the update cache directory's "pending" subfolder after the first update is downloaded. If the update file does not exist then the cache is cleared and recreated. If the update file exists then its properties are validated.
84 * @param fileInfo
85 * @param logger
86 */
87 async getValidCachedUpdateFile(fileInfo, logger) {
88 var _a;
89 const updateInfoFilePath = this.getUpdateInfoFile();
90 const doesUpdateInfoFileExist = await fs_extra_1.pathExists(updateInfoFilePath);
91 if (!doesUpdateInfoFileExist) {
92 return null;
93 }
94 let cachedInfo;
95 try {
96 cachedInfo = await fs_extra_1.readJson(updateInfoFilePath);
97 }
98 catch (error) {
99 let message = `No cached update info available`;
100 if (error.code !== "ENOENT") {
101 await this.cleanCacheDirForPendingUpdate();
102 message += ` (error on read: ${error.message})`;
103 }
104 logger.info(message);
105 return null;
106 }
107 const isCachedInfoFileNameValid = (_a = (cachedInfo === null || cachedInfo === void 0 ? void 0 : cachedInfo.fileName) !== null) !== null && _a !== void 0 ? _a : false;
108 if (!isCachedInfoFileNameValid) {
109 logger.warn(`Cached update info is corrupted: no fileName, directory for cached update will be cleaned`);
110 await this.cleanCacheDirForPendingUpdate();
111 return null;
112 }
113 if (fileInfo.info.sha512 !== cachedInfo.sha512) {
114 logger.info(`Cached update sha512 checksum doesn't match the latest available update. New update must be downloaded. Cached: ${cachedInfo.sha512}, expected: ${fileInfo.info.sha512}. Directory for cached update will be cleaned`);
115 await this.cleanCacheDirForPendingUpdate();
116 return null;
117 }
118 const updateFile = path.join(this.cacheDirForPendingUpdate, cachedInfo.fileName);
119 if (!(await fs_extra_1.pathExists(updateFile))) {
120 logger.info("Cached update file doesn't exist");
121 return null;
122 }
123 const sha512 = await hashFile(updateFile);
124 if (fileInfo.info.sha512 !== sha512) {
125 logger.warn(`Sha512 checksum doesn't match the latest available update. New update must be downloaded. Cached: ${sha512}, expected: ${fileInfo.info.sha512}`);
126 await this.cleanCacheDirForPendingUpdate();
127 return null;
128 }
129 this._downloadedFileInfo = cachedInfo;
130 return updateFile;
131 }
132 getUpdateInfoFile() {
133 return path.join(this.cacheDirForPendingUpdate, "update-info.json");
134 }
135}
136exports.DownloadedUpdateHelper = DownloadedUpdateHelper;
137function hashFile(file, algorithm = "sha512", encoding = "base64", options) {
138 return new Promise((resolve, reject) => {
139 const hash = crypto_1.createHash(algorithm);
140 hash.on("error", reject).setEncoding(encoding);
141 fs_1.createReadStream(file, { ...options, highWaterMark: 1024 * 1024 /* better to use more memory but hash faster */ })
142 .on("error", reject)
143 .on("end", () => {
144 hash.end();
145 resolve(hash.read());
146 })
147 .pipe(hash, { end: false });
148 });
149}
150async function createTempUpdateFile(name, cacheDir, log) {
151 // https://github.com/electron-userland/electron-builder/pull/2474#issuecomment-366481912
152 let nameCounter = 0;
153 let result = path.join(cacheDir, name);
154 for (let i = 0; i < 3; i++) {
155 try {
156 await fs_extra_1.unlink(result);
157 return result;
158 }
159 catch (e) {
160 if (e.code === "ENOENT") {
161 return result;
162 }
163 log.warn(`Error on remove temp update file: ${e}`);
164 result = path.join(cacheDir, `${nameCounter++}-${name}`);
165 }
166 }
167 return result;
168}
169exports.createTempUpdateFile = createTempUpdateFile;
170//# sourceMappingURL=DownloadedUpdateHelper.js.map
\No newline at end of file