Tweak setup wizard to enhance whole process

This commit is contained in:
Théophile Diot 2024-10-02 15:51:03 +02:00
parent f8827f31ea
commit 0bf3af7089
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
3 changed files with 165 additions and 85 deletions

View file

@ -104,7 +104,7 @@ location /setup/check {
local http = require "resty.http".new()
local res, err = http:request_uri("https://" .. args["server_name"] .. "/setup/check", {ssl_verify = false})
if not res then
ngx.print("ko")
ngx.print("error")
logger:log(ngx.ERR, "Server name check failed : " .. err)
return
end

View file

@ -33,22 +33,22 @@ $(document).ready(() => {
isValid = validateCondition(
password.length >= 8,
"#length-check i",
isValid,
isValid
);
isValid = validateCondition(
/[A-Z]/.test(password),
"#uppercase-check i",
isValid,
isValid
);
isValid = validateCondition(
/\d/.test(password),
"#number-check i",
isValid,
isValid
);
isValid = validateCondition(
/[ -~]/.test(password),
"#special-check i",
isValid,
isValid
); // Check for special characters
return isValid;
@ -121,10 +121,12 @@ $(document).ready(() => {
const text = (await response.text()).trim().toLowerCase();
if (response.status === 200 && text === "ok") {
return true;
} else if (text === "error") {
return "Server name check failed";
}
return false;
} catch (error) {
return false;
return error.message;
}
};
@ -146,30 +148,50 @@ $(document).ready(() => {
const primaryURL = `https://${serverName}/setup/check`;
const fallbackURL = `${window.location.origin}/setup/check?server_name=${serverName}`;
let isSuccess = await fetchCheck(primaryURL);
let result = await fetchCheck(primaryURL);
if (!isSuccess) {
isSuccess = await fetchCheck(fallbackURL);
if (!result || typeof result === "string") {
result = await fetchCheck(fallbackURL);
}
$overviewUniqueServerName
.find("i")
.toggleClass("bx-question-mark text-warning", false);
const $input = $("#SERVER_NAME");
const isValid = result && typeof result !== "string";
// Toggle valid/invalid classes
$input.toggleClass("is-invalid", !isValid);
// Manage the invalid-feedback element
let $feedback = $input.siblings(".invalid-feedback");
if (!$feedback.length) {
const $textSpan = $input.parent().find("span.input-group-text");
$feedback = $('<div class="invalid-feedback"></div>').insertAfter(
$textSpan.length ? $textSpan : $input
);
}
const feedbackToast = $("#feedback-toast").clone(); // Clone the feedback toast
feedbackToast.attr("id", `feedback-toast-${toastNum++}`); // Corrected to set the ID for the toast
if (!isSuccess) {
if (!isValid) {
$feedback.text("Server name is not unique.");
feedbackToast.removeClass("bg-primary");
feedbackToast.addClass("bg-danger");
feedbackToast.find("span").text("Server name is not unique.");
feedbackToast
.find("div.toast-body")
.text("Please choose a different server name.");
$overviewUniqueServerName
.find("i")
.toggleClass("bx-check text-success", false)
.toggleClass("bx-x text-danger", true);
if (typeof result !== "string")
$overviewUniqueServerName
.find("i")
.toggleClass("bx-check text-success", false)
.toggleClass("bx-x text-danger", true);
} else {
$feedback.text("");
feedbackToast.removeClass("bg-danger");
feedbackToast.addClass("bg-primary");
feedbackToast.find("span").text("Server name is unique.");
feedbackToast
.find("div.toast-body")
@ -179,8 +201,11 @@ $(document).ready(() => {
.toggleClass("bx-check text-success", true)
.toggleClass("bx-x text-danger", false);
}
feedbackToast.appendTo("#feedback-toast-container"); // Ensure the toast is appended to the container
feedbackToast.toast("show");
return result;
};
/**
@ -231,7 +256,7 @@ $(document).ready(() => {
if (!$feedback.length) {
const $textSpan = $input.parent().find("span.input-group-text");
$feedback = $('<div class="invalid-feedback"></div>').insertAfter(
$textSpan.length ? $textSpan : $input,
$textSpan.length ? $textSpan : $input
);
}
@ -324,14 +349,34 @@ $(document).ready(() => {
*/
const populateOverview = () => {
if (!uiUser) {
$("#overview_username").val($("#username").val());
$("#overview_password").val($("#password").val());
const $overviewEmail = $("#overview_email");
const $overviewUsername = $("#overview_username");
const $overviewPassword = $("#overview_password");
const adminEmail = $("#email").val();
if (adminEmail) {
$overviewEmail.val(adminEmail);
$overviewEmail.closest(".col-12").removeClass("d-none");
$overviewUsername.closest(".col-12").addClass("col-md-4");
$overviewPassword
.closest(".col-12")
.addClass("col-md-4")
.removeClass("col-sm-6");
} else {
$overviewEmail.closest(".col-12").addClass("d-none");
$overviewUsername.closest(".col-12").removeClass("col-md-4");
$overviewPassword
.closest(".col-12")
.removeClass("col-md-4")
.addClass("col-sm-6");
}
$overviewUsername.val($("#username").val());
$overviewPassword.val($("#password").val());
}
if (!uiReverseProxy) {
$("#overview_service_url").val(
`https://${getServerName()}${$("#REVERSE_PROXY_URL").val()}`,
`https://${getServerName()}${$("#REVERSE_PROXY_URL").val()}`
);
$("#overview_email_lets_encrypt").val($("#EMAIL_LETS_ENCRYPT").val());
}
};
@ -339,11 +384,11 @@ $(document).ready(() => {
* Handles navigation to the next or previous step.
* @param {boolean} isNext - True if navigating forward, false if backward.
*/
const handleStepNavigation = (isNext) => {
const handleStepNavigation = async (isNext, force = false) => {
const newStep = isNext ? currentStep + 1 : currentStep - 1;
const $currentStepContainer = $(`#navs-steps-${currentStep}`);
if (isNext) {
if (!force && isNext) {
let isStepValid = validateCurrentStepInputs($currentStepContainer);
// Additional validation for step 1 (password confirmation)
@ -359,7 +404,7 @@ $(document).ready(() => {
.parent()
.find("span.input-group-text");
$feedback = $(
'<div class="invalid-feedback">Passwords do not match.</div>',
'<div class="invalid-feedback">Passwords do not match.</div>'
).insertAfter($textSpan.length ? $textSpan : $confirmPasswordInput);
} else {
$feedback.text("Passwords do not match.");
@ -369,6 +414,29 @@ $(document).ready(() => {
$confirmPasswordInput.removeClass("is-invalid");
$confirmPasswordInput.siblings(".invalid-feedback").text("");
}
} else if (!uiReverseProxy && currentStep === 2) {
const result = await checkDNS();
const modal = $("#modal-confirm-dns");
const $checkUrl = $("#check-url");
const serverName = getServerName();
$checkUrl.attr("href", `https://${serverName}/setup/check`);
isStepValid = false;
if (typeof result === "string") {
$("#dns-check-title").text("Error");
$("#dns-check-result").html(
`Are you sure you want to proceed to the next step?<br/>Error: ${result}`
);
modal.modal("show");
} else if (!result) {
$("#dns-check-title").text("Server name is not unique");
$("#dns-check-result").html(
`Are you sure you want to proceed to the next step?<br/>Server name "${serverName}" is not unique.`
);
modal.modal("show");
} else {
isStepValid = true;
}
}
if (!isStepValid) return;
@ -389,7 +457,7 @@ $(document).ready(() => {
debounce(function () {
const isValid = validatePassword();
updateValidationState(this, isValid);
}, 100),
}, 100)
);
// Real-time validation for other plugin settings
@ -404,7 +472,7 @@ $(document).ready(() => {
$this
.toggleClass("is-valid", isValid)
.toggleClass("is-invalid", !isValid);
}, 100),
}, 100)
);
// Remove validation state on focus out
@ -442,11 +510,11 @@ $(document).ready(() => {
formData.append("ui_url", ui_url);
formData.append(
"auto_lets_encrypt",
$("#AUTO_LETS_ENCRYPT").prop("checked") ? "yes" : "no",
$("#AUTO_LETS_ENCRYPT").prop("checked") ? "yes" : "no"
);
formData.append(
"lets_encrypt_staging",
$("#LETS_ENCRYPT_STAGING").prop("checked") ? "yes" : "no",
$("#LETS_ENCRYPT_STAGING").prop("checked") ? "yes" : "no"
);
formData.append("email_lets_encrypt", $("#EMAIL_LETS_ENCRYPT").val());
}
@ -501,17 +569,8 @@ $(document).ready(() => {
e.preventDefault();
const isNext = $(this).hasClass("next-step");
handleStepNavigation(isNext);
});
// DNS Check Button Click
$("#check-dns").on("click", function (e) {
e.preventDefault();
if (uiReverseProxy) return;
if (currentStep !== CHECK_STEP) return;
checkDNS();
const confirmDNS = this.id === "confirm-dns";
handleStepNavigation(isNext, confirmDNS);
});
$2faInput.on("input", function () {

View file

@ -423,7 +423,7 @@
{% if not ui_user %}
<h6 class="mt-2 mb-1 fw-bold">Admin User</h6>
{% set input_readonly = true %}
<div class="col-12 col-sm-6 pb-3">
<div class="col-12 col-sm-6 col-md-4 pb-3">
<div class="d-flex justify-content-between align-items-center">
<label id="label-overview_username"
for="overview_username"
@ -435,7 +435,17 @@
{% set setting_data = {"type": "text", "id": "overview_username", "regex": "^.{1,256}$"} %}
{% include "models/input_setting.html" %}
</div>
<div class="col-12 col-sm-6 pb-3">
<div class="col-12 col-sm-6 col-md-4 pb-3">
<div class="d-flex justify-content-between align-items-center">
<label id="label-overview_email"
for="overview_email"
class="form-label fw-semibold text-truncate">Admin Email</label>
</div>
{% set setting = "email" %}
{% set setting_data = {"type": "email", "id": "overview_email", "regex": "^.*$"} %}
{% include "models/input_setting.html" %}
</div>
<div class="col-12 col-md-4 pb-3">
<div class="d-flex justify-content-between align-items-center">
<label id="label-overview_password"
for="overview_password"
@ -449,52 +459,21 @@
</div>
{% endif %}
{% if not ui_reverse_proxy %}
<h6 class="mt-2 mb-1 fw-bold">UI specific Settings</h6>
<div class="col-12 col-sm-6 pb-3">
<div class="d-flex justify-content-between align-items-center">
<label id="label-overview_service_url"
for="overview_service_url"
class="form-label fw-semibold text-truncate">
BunkerWeb UI final URL
</label>
<div class="row justify-content-center">
<div class="col-6 pb-3">
<h6 class="mt-2 mb-1 fw-bold">BunkerWeb UI final URL</h6>
{% set setting = "SERVER_NAME + REVERSE_PROXY_HOST" %}
{% set setting_data = {"type": "text", "id": "overview_service_url", "regex": "^.+$"} %}
{% include "models/input_setting.html" %}
</div>
{% set setting = "SERVER_NAME + REVERSE_PROXY_HOST" %}
{% set setting_data = {"type": "text", "id": "overview_service_url", "regex": "^.+$"} %}
{% include "models/input_setting.html" %}
</div>
<div class="col-sm-6 pb-3">
<div class="d-flex justify-content-between align-items-center">
<label id="label-overview_email_lets_encrypt"
for="overview_email_lets_encrypt"
class="form-label fw-semibold text-truncate">
Email for Let's Encrypt
</label>
<div class="d-flex align-items-center">
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-original-title="The email address that will be used for Let's Encrypt">
<span class="bx bx-question-mark bx-xs"></span>
</span>
</div>
</div>
{% set setting = "EMAIL_LETS_ENCRYPT" %}
{% set setting_data = {"type": "text", "id": "overview_email_lets_encrypt", "regex": "^.*$"} %}
{% include "models/input_setting.html" %}
</div>
<div class="text-center pt-2 mb-6">
<h6 class="mb-1 fw-bold">Check server name DNS</h6>
<button id="check-dns" class="btn btn-primary">Check DNS</button>
<p class="mt-1 mb-0">
In case of issues, you can also click <a id="check_url"
class="fw-semibold"
href="https://www.example.com/setup/check"
target="_blank"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-original-title='If the shown text is "ok", that means that the server name is available'>here</a> to perform a manual check.
</p>
</div>
<!-- <div class="text-center pt-2 mb-6">
<h6 class="mb-1 fw-bold">Check server name DNS</h6>
<button id="check-dns" class="btn btn-primary">Check DNS</button>
<p class="mt-1 mb-0">
In case of issues, you can also click <a id="check_url" class="fw-semibold" href="https://www.example.com/setup/check" target="_blank" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title='If the shown text is "ok", that means that the server name is available'>here</a> to perform a manual check.
</p>
</div> -->
{% endif %}
<div class="row p-0">
<div class="col-md-{% if not ui_user %}4{% else %}6{% endif %}">
@ -593,12 +572,12 @@
</div>
</div>
<div id="feedback-toast"
class="bs-toast toast fade bg-primary text-white"
class="bs-toast toast fade"
role="alert"
aria-live="assertive"
aria-atomic="true"
data-bs-autohide="true"
data-bs-delay="8000">
data-bs-delay="10000">
<div class="toast-header">
<i class="d-block w-px-20 h-auto rounded me-2 tf-icons bx bx-bell"></i>
<span class="fw-medium me-auto">BunkerWeb Forever</span>
@ -610,6 +589,48 @@
</div>
<div class="toast-body">If you read this, it means that you're curious 👀</div>
</div>
{% if not ui_reverse_proxy %}
<div class="modal fade"
id="modal-confirm-dns"
data-bs-backdrop="static"
tabindex="-1"
aria-hidden="true"
role="dialog">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 id="dns-check-title" class="modal-title">Server name is not unique</h5>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body">
<div id="dns-check-result"
class="alert alert-danger text-center"
role="alert">Are you sure you want to proceed to the next step?</div>
<p class="mt-1 mb-0 text-center">
In case of issues, you can also click <a id="check-url"
class="fw-semibold"
href="https://www.example.com/setup/check"
target="_blank"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-original-title='If the shown text is "ok", that means that the server name is available'>here</a> to perform a manual check.
</p>
</div>
<div class="modal-footer justify-content-center">
<button id="confirm-dns"
class="btn btn-outline-danger next-step me-2"
data-bs-dismiss="modal">Proceed</button>
<button type="reset"
class="btn btn-outline-secondary"
data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
{% endif %}
<div class="modal fade"
id="loadingModal"
data-bs-backdrop="static"