From: Alexander Ebert Date: Tue, 5 Sep 2023 14:26:22 +0000 (+0200) Subject: Block parallel requests to install a package X-Git-Tag: 6.0.0_Beta_4~6^2~4 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=de651d589cb6e8ba55d0cb0421b4f78397b7f9f5;p=GitHub%2FWoltLab%2FWCF.git Block parallel requests to install a package --- diff --git a/ts/WoltLabSuite/Core/Acp/Component/License.ts b/ts/WoltLabSuite/Core/Acp/Component/License.ts index 07d8ea608c..718d4e59fc 100644 --- a/ts/WoltLabSuite/Core/Acp/Component/License.ts +++ b/ts/WoltLabSuite/Core/Acp/Component/License.ts @@ -1,14 +1,25 @@ +/** + * Offers to install packages from the list of licensed products. + * + * @author Alexander Ebert + * @copyright 2001-2023 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.0 + */ + +import { promiseMutex } from "WoltLabSuite/Core/Helper/PromiseMutex"; import AcpUiPackagePrepareInstallation from "../Ui/Package/PrepareInstallation"; -function installPackage(button: HTMLButtonElement): void { +function installPackage(button: HTMLButtonElement): Promise { const installation = new AcpUiPackagePrepareInstallation(); - installation.start(button.dataset.package!, button.dataset.packageVersion!); + return installation.start(button.dataset.package!, button.dataset.packageVersion!); } export function setup(): void { + const callback = promiseMutex((button: HTMLButtonElement) => installPackage(button)); document.querySelectorAll(".jsInstallPackage").forEach((button) => { button.addEventListener("click", () => { - installPackage(button); + callback(button); }); }); } diff --git a/ts/WoltLabSuite/Core/Acp/Ui/Package/PrepareInstallation.ts b/ts/WoltLabSuite/Core/Acp/Ui/Package/PrepareInstallation.ts index e7557fcaad..5fcf8530ba 100644 --- a/ts/WoltLabSuite/Core/Acp/Ui/Package/PrepareInstallation.ts +++ b/ts/WoltLabSuite/Core/Acp/Ui/Package/PrepareInstallation.ts @@ -24,12 +24,21 @@ interface AjaxResponse { class AcpUiPackagePrepareInstallation { private identifier = ""; private version = ""; + #resolve?: () => void; + + start(identifier: string, version: string): Promise { + if (this.#resolve !== undefined) { + throw new Error("There is already a pending installation."); + } - start(identifier: string, version: string): void { this.identifier = identifier; this.version = version; - this.prepare({}); + return new Promise((resolve) => { + this.#resolve = resolve; + + this.prepare({}); + }); } private prepare(authData: ArbitraryObject): void { @@ -82,6 +91,9 @@ class AcpUiPackagePrepareInstallation { } else if (data.returnValues.template) { UiDialog.open(this, data.returnValues.template); } + + this.#resolve!(); + this.#resolve = undefined; } _ajaxSetup(): ReturnType { diff --git a/ts/WoltLabSuite/Core/Acp/Ui/Package/Search.ts b/ts/WoltLabSuite/Core/Acp/Ui/Package/Search.ts index 428d2882a6..be8e914d17 100644 --- a/ts/WoltLabSuite/Core/Acp/Ui/Package/Search.ts +++ b/ts/WoltLabSuite/Core/Acp/Ui/Package/Search.ts @@ -10,6 +10,7 @@ import AcpUiPackagePrepareInstallation from "./PrepareInstallation"; import * as Ajax from "../../../Ajax"; import AjaxRequest from "../../../Ajax/Request"; import { AjaxCallbackObject, AjaxCallbackSetup } from "../../../Ajax/Data"; +import { promiseMutex } from "WoltLabSuite/Core/Helper/PromiseMutex"; interface AjaxResponse { actionName: string; @@ -120,12 +121,16 @@ class AcpUiPackageSearch implements AjaxCallbackObject { this.setStatus("showResults"); + const callback = promiseMutex((button: HTMLAnchorElement) => { + return this.installation.start(button.dataset.package!, button.dataset.packageVersion!); + }); this.resultList.querySelectorAll(".jsInstallPackage").forEach((button: HTMLAnchorElement) => { button.addEventListener("click", (event) => { event.preventDefault(); - button.blur(); - this.installation.start(button.dataset.package!, button.dataset.packageVersion!); + if (callback(button)) { + button.blur(); + } }); }); } else { diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Component/License.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Component/License.js index 10cce723f8..7663b1bbd8 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Component/License.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Component/License.js @@ -1,16 +1,25 @@ -define(["require", "exports", "tslib", "../Ui/Package/PrepareInstallation"], function (require, exports, tslib_1, PrepareInstallation_1) { +/** + * Offers to install packages from the list of licensed products. + * + * @author Alexander Ebert + * @copyright 2001-2023 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.0 + */ +define(["require", "exports", "tslib", "WoltLabSuite/Core/Helper/PromiseMutex", "../Ui/Package/PrepareInstallation"], function (require, exports, tslib_1, PromiseMutex_1, PrepareInstallation_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setup = void 0; PrepareInstallation_1 = tslib_1.__importDefault(PrepareInstallation_1); function installPackage(button) { const installation = new PrepareInstallation_1.default(); - installation.start(button.dataset.package, button.dataset.packageVersion); + return installation.start(button.dataset.package, button.dataset.packageVersion); } function setup() { + const callback = (0, PromiseMutex_1.promiseMutex)((button) => installPackage(button)); document.querySelectorAll(".jsInstallPackage").forEach((button) => { button.addEventListener("click", () => { - installPackage(button); + callback(button); }); }); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/PrepareInstallation.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/PrepareInstallation.js index 7ec4a005f8..97f0da58ba 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/PrepareInstallation.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/PrepareInstallation.js @@ -15,10 +15,17 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Language", ".. class AcpUiPackagePrepareInstallation { identifier = ""; version = ""; + #resolve; start(identifier, version) { + if (this.#resolve !== undefined) { + throw new Error("There is already a pending installation."); + } this.identifier = identifier; this.version = version; - this.prepare({}); + return new Promise((resolve) => { + this.#resolve = resolve; + this.prepare({}); + }); } prepare(authData) { const packages = {}; @@ -66,6 +73,8 @@ define(["require", "exports", "tslib", "../../../Ajax", "../../../Language", ".. else if (data.returnValues.template) { Dialog_1.default.open(this, data.returnValues.template); } + this.#resolve(); + this.#resolve = undefined; } _ajaxSetup() { return { diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/Search.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/Search.js index 20b3b74001..a4a4c9c005 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/Search.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/Package/Search.js @@ -5,7 +5,7 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License */ -define(["require", "exports", "tslib", "./PrepareInstallation", "../../../Ajax"], function (require, exports, tslib_1, PrepareInstallation_1, Ajax) { +define(["require", "exports", "tslib", "./PrepareInstallation", "../../../Ajax", "WoltLabSuite/Core/Helper/PromiseMutex"], function (require, exports, tslib_1, PrepareInstallation_1, Ajax, PromiseMutex_1) { "use strict"; PrepareInstallation_1 = tslib_1.__importDefault(PrepareInstallation_1); Ajax = tslib_1.__importStar(Ajax); @@ -86,11 +86,15 @@ define(["require", "exports", "tslib", "./PrepareInstallation", "../../../Ajax"] this.resultList.innerHTML = data.returnValues.template; this.resultCounter.textContent = data.returnValues.count.toString(); this.setStatus("showResults"); + const callback = (0, PromiseMutex_1.promiseMutex)((button) => { + return this.installation.start(button.dataset.package, button.dataset.packageVersion); + }); this.resultList.querySelectorAll(".jsInstallPackage").forEach((button) => { button.addEventListener("click", (event) => { event.preventDefault(); - button.blur(); - this.installation.start(button.dataset.package, button.dataset.packageVersion); + if (callback(button)) { + button.blur(); + } }); }); }