Skip to content

Commit

Permalink
Added install state for better update of plugins (#1058)
Browse files Browse the repository at this point in the history
  • Loading branch information
alMukaafih authored Oct 20, 2024
1 parent 4883085 commit 9cb0012
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 1 deletion.
43 changes: 42 additions & 1 deletion src/lib/installPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fsOperation from "fileSystem";
import JSZip from "jszip";
import Url from "utils/Url";
import constants from "./constants";
import InstallState from "./installState";
import loadPlugin from "./loadPlugin";

/**
Expand Down Expand Up @@ -70,6 +71,8 @@ export default async function installPlugin(id, name, purchaseToken) {
pluginDir = Url.join(PLUGIN_DIR, id);
}

const state = await InstallState.new(id);

if (!(await fsOperation(pluginDir).exists())) {
await fsOperation(PLUGIN_DIR).createDirectory(id);
}
Expand All @@ -81,7 +84,7 @@ export default async function installPlugin(id, name, purchaseToken) {
}

const fileUrl = Url.join(pluginDir, correctFile);
if (!(await fsOperation(fileUrl).exists())) {
if (!state.exists(correctFile)) {
await createFileRecursive(pluginDir, correctFile);
}

Expand All @@ -93,11 +96,18 @@ export default async function installPlugin(id, name, purchaseToken) {
data = JSON.stringify(pluginJson);
}

if (!(await state.isUpdated(correctFile, data))) {
return;
}

await fsOperation(fileUrl).writeFile(data);
return;
});

await Promise.all(promises);
await loadPlugin(id, true);
await state.save();
deleteRedundantFiles(pluginDir, state);
}
} catch (err) {
try {
Expand Down Expand Up @@ -139,3 +149,34 @@ async function createFileRecursive(parent, dir) {
await createFileRecursive(newParent, dir);
}
}
/**
*
* @param {string} dir
* @param {Array<string>} files
*/
async function listFileRecursive(dir, files) {
for (const child of await fsOperation(dir).lsDir()) {
const fileUrl = Url.join(dir, child.name);
if (child.isDirectory) {
await listFileRecursive(fileUrl, files);
} else {
files.push(fileUrl);
}
}
}

/**
*
* @param {Record<string, boolean>} files
*/
async function deleteRedundantFiles(pluginDir, state) {
/** @type string[] */
let files = [];
await listFileRecursive(pluginDir, files);

for (const file of files) {
if (!state.exists(file.replace(`${pluginDir}/`, ""))) {
fsOperation(file).delete();
}
}
}
99 changes: 99 additions & 0 deletions src/lib/installState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import fsOperation from "fileSystem";
import Url from "utils/Url";

const INSTALL_STATE_STORAGE = Url.join(DATA_STORAGE, ".install-state");

export default class InstallState {
/** @type Record<string, string> */
store;
/** @type Record<string, string> */
updatedStore;

/**
*
* @param {string} id
* @returns
*/
static async new(id) {
const state = new InstallState();
state.id = await checksumText(id);
state.updatedStore = {};

if (!(await fsOperation(INSTALL_STATE_STORAGE).exists())) {
await fsOperation(DATA_STORAGE).createDirectory(".install-state");
}

state.storeUrl = Url.join(INSTALL_STATE_STORAGE, state.id);
if (await fsOperation(state.storeUrl).exists()) {
state.store = JSON.parse(
await fsOperation(state.storeUrl).readFile("utf-8"),
);
} else {
state.store = {};
await fsOperation(INSTALL_STATE_STORAGE).createFile(state.id);
}

return state;
}

/**
*
* @param {string} url
* @param {ArrayBuffer | string} content
* @param {boolean} isString
* @returns
*/
async isUpdated(url, content) {
const current = this.store[url];
const update =
typeof content === "string"
? await checksumText(content)
: await checksum(content);
this.updatedStore[url] = update;

if (current === update) {
return false;
} else {
return true;
}
}

exists(url) {
if (typeof this.store[url] !== "undefined") {
return true;
} else {
return false;
}
}

async save() {
this.store = this.updatedStore;
await fsOperation(this.storeUrl).writeFile(
JSON.stringify(this.updatedStore),
);
}
}

/**
* Derives the checksum of a Buffer
* @param {BufferSource} data
* @returns the derived checksum
*/
async function checksum(data) {
const hashBuffer = await window.crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
return hashHex;
}

/**
*
* @param {string} text
* @returns
*/
async function checksumText(text) {
const textUint8 = new TextEncoder().encode(text);
return await checksum(textUint8);
}

0 comments on commit 9cb0012

Please sign in to comment.