Merge branch 'dev' of github.com:bunkerity/bunkerweb into dev

This commit is contained in:
fl0ppy-d1sk 2024-02-01 17:10:07 +01:00
commit bf5e3141b2
No known key found for this signature in database
GPG key ID: 93EE47CC3D061500
29 changed files with 623 additions and 4136 deletions

View file

@ -37,6 +37,7 @@ shopt -u globstar
# linux
sed -i "s@${OLD_VERSION}@${NEW_VERSION}@g" src/linux/scripts/*.sh
# db
sed -i "s@${OLD_VERSION}@${NEW_VERSION}@g" src/common/db/Database.py
sed -i "s@${OLD_VERSION}@${NEW_VERSION}@g" src/common/db/model.py
# github
sed -i "s@${OLD_VERSION}@${NEW_VERSION}@g" .github/ISSUE_TEMPLATE/bug_report.yml

View file

@ -61,211 +61,22 @@
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Anti-bot technology is designed to detect and mitigate suspicious or
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Anti-bot technology is designed to detect and mitigate suspicious or
malicious bots, preventing them from reaching an organization's websites
or IT ecosystem.`,
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
/* EXAMPLE
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(`[data-name="${nameKey}"]`).textContent =
nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove("bg-sky-500", "bg-green-500", "bg-red-500");
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch") return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
});
</script>
{% endblock %}

View file

@ -56,211 +56,22 @@
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Basic Auth is a method for an HTTP user agent
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Basic Auth is a method for an HTTP user agent
(e.g. a web browser) to provide a user name and password when making a
request.`,
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: `unknown`,
type: "text",
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: `unknown`,
type: "text",
},
});
</script>
</div>

View file

@ -105,216 +105,27 @@
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Ban IP generating too much 'bad' HTTP status
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Ban IP generating too much 'bad' HTTP status
code in a period of time.`,
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["code", "count"],
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["code", "count"],
},
});
</script>
{% endblock %}

View file

@ -219,231 +219,42 @@
</div>
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Deny access based on internal and external
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Deny access based on internal and external
IP/network/rDNS/ASN blacklists.`,
type: "text",
},
count_url: {
el: document.querySelector("[data-count-url]"),
value: "unknown",
type: "text",
},
count_ip: {
el: document.querySelector("[data-count-ip]"),
value: "unknown",
type: "text",
},
count_rdns: {
el: document.querySelector("[data-count-rdns]"),
value: "unknown",
type: "text",
},
count_asn: {
el: document.querySelector("[data-count-asn]"),
value: "unknown",
type: "text",
},
count_user_agent: {
el: document.querySelector("[data-count-user-agent]"),
value: "unknown",
type: "text",
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
count_url: {
el: document.querySelector("[data-count-url]"),
value: "unknown",
type: "text",
},
count_ip: {
el: document.querySelector("[data-count-ip]"),
value: "unknown",
type: "text",
},
count_rdns: {
el: document.querySelector("[data-count-rdns]"),
value: "unknown",
type: "text",
},
count_asn: {
el: document.querySelector("[data-count-asn]"),
value: "unknown",
type: "text",
},
count_user_agent: {
el: document.querySelector("[data-count-user-agent]"),
value: "unknown",
type: "text",
},
});
</script>
</div>
{% endblock %}

View file

@ -147,203 +147,23 @@
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `BunkerNet is a crowdsourced database of malicious
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `BunkerNet is a crowdsourced database of malicious
requests shared between all BunkerWeb instances over the world. `,
type: "text",
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status-svg]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status-svg]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
});
</script>
<!-- end test -->

View file

@ -58,210 +58,21 @@
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Cross-Origin Resource Sharing lets you manage how your
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Cross-Origin Resource Sharing lets you manage how your
service can be contacted from different origins.`,
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
});
</script>
</div>
{% endblock %}

View file

@ -99,216 +99,28 @@
</div>
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `The country security feature allows you to apply
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `The country security feature allows you to apply
policy based on the country of the IP address of clients (blacklist /
whitelist).`,
type: "text",
},
blacklist_count: {
el: document.querySelector("[data-count-blacklist]"),
value: "",
type: "text",
},
whitelist_count: {
el: document.querySelector("[data-count-whitelist]"),
value: "",
type: "text",
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
blacklist_count: {
el: document.querySelector("[data-count-blacklist]"),
value: "",
type: "text",
},
whitelist_count: {
el: document.querySelector("[data-count-whitelist]"),
value: "",
type: "text",
},
});
</script>
</div>
{% endblock %}

View file

@ -71,210 +71,21 @@
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Custom certificates allow you to get HTTPS / SSL / TLS on your requests.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Custom certificates allow you to get HTTPS / SSL / TLS on your requests.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
});
</script>
</div>

View file

@ -97,227 +97,33 @@
</div>
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `BunkerWeb securely stores its current configuration in
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `BunkerWeb securely stores its current configuration in
a backend database, which contains essential data for smooth
operation.`,
type: "text",
},
driver: {
el: document.querySelector("[data-driver]"),
value: "",
type: "text",
},
version: {
el: document.querySelector("[data-version]"),
value: "",
type: "text",
},
size: {
el: document.querySelector("[data-size]"),
value: "",
type: "text",
},
/* EXAMPLE
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
driver: {
el: document.querySelector("[data-driver]"),
value: "",
type: "text",
},
version: {
el: document.querySelector("[data-version]"),
value: "",
type: "text",
},
size: {
el: document.querySelector("[data-size]"),
value: "",
type: "text",
},
});
</script>
</div>
{% endblock %}

View file

@ -59,210 +59,22 @@
<!-- end list container-->
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Deny access based on external DNSBL servers.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "status"],
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Deny access based on external DNSBL servers.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "status"],
},
});
</script>
</div>

View file

@ -59,210 +59,22 @@
<!-- end list container-->
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Show number of occurences of each error code.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["code", "count"],
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Show number of occurences of each error code.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["code", "count"],
},
});
</script>
</div>

View file

@ -219,231 +219,42 @@
</div>
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Deny access based on internal and external
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Deny access based on internal and external
IP/network/rDNS/ASN greylists.`,
type: "text",
},
count_url: {
el: document.querySelector("[data-count-url]"),
value: "unknown",
type: "text",
},
count_ip: {
el: document.querySelector("[data-count-ip]"),
value: "unknown",
type: "text",
},
count_rdns: {
el: document.querySelector("[data-count-rdns]"),
value: "unknown",
type: "text",
},
count_asn: {
el: document.querySelector("[data-count-asn]"),
value: "unknown",
type: "text",
},
count_user_agent: {
el: document.querySelector("[data-count-user-agent]"),
value: "unknown",
type: "text",
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
count_url: {
el: document.querySelector("[data-count-url]"),
value: "unknown",
type: "text",
},
count_ip: {
el: document.querySelector("[data-count-ip]"),
value: "unknown",
type: "text",
},
count_rdns: {
el: document.querySelector("[data-count-rdns]"),
value: "unknown",
type: "text",
},
count_asn: {
el: document.querySelector("[data-count-asn]"),
value: "unknown",
type: "text",
},
count_user_agent: {
el: document.querySelector("[data-count-user-agent]"),
value: "unknown",
type: "text",
},
});
</script>
</div>
{% endblock %}

View file

@ -71,210 +71,21 @@
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Let's encrypt certificates allow you to get HTTPS / SSL / TLS on your requests.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Let's encrypt certificates allow you to get HTTPS / SSL / TLS on your requests.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
});
</script>
</div>

View file

@ -66,210 +66,21 @@
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Limit maximum number of requests and connections.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["url", "count"],
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Limit maximum number of requests and connections.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["url", "count"],
},
});
</script>
</div>
{% endblock %}

View file

@ -104,215 +104,26 @@
</div>
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Miscellaneous settings (methods, servers...).`,
type: "text",
},
count_disabled_servers: {
el: document.querySelector("[data-count-server-disabled]"),
value: "unknown",
type: "text",
},
count_disallowed_methods: {
el: document.querySelector("[data-count-disallowed-methods]"),
value: "unknown",
type: "text",
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Miscellaneous settings (methods, servers...).`,
type: "text",
},
count_disabled_servers: {
el: document.querySelector("[data-count-server-disabled]"),
value: "unknown",
type: "text",
},
count_disallowed_methods: {
el: document.querySelector("[data-count-disallowed-methods]"),
value: "unknown",
type: "text",
},
});
</script>
</div>
{% endblock %}

View file

@ -58,215 +58,22 @@
</div>
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `ModSecurity is an open source, cross platform web application firewall (WAF) engine for Apache, IIS and Nginx that is developed by Trustwave's SpiderLabs.`,
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: `ModSecurity is an open source, cross platform web application firewall (WAF) engine for Apache, IIS and Nginx that is developed by Trustwave's SpiderLabs.`,
type: "text",
},
/* EXAMPLE
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]"
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500"
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `ModSecurity is an open source, cross platform web application firewall (WAF) engine for Apache, IIS and Nginx that is developed by Trustwave's SpiderLabs.`,
type: "text",
},
count: {
el: document.querySelector("[data-count]"),
value: `ModSecurity is an open source, cross platform web application firewall (WAF) engine for Apache, IIS and Nginx that is developed by Trustwave's SpiderLabs.`,
type: "text",
},
});
</script>
</div>

View file

@ -144,203 +144,24 @@
</div>
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Redis server configuration when using BunkerWeb in
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Redis server configuration when using BunkerWeb in
cluster mode.`,
type: "text",
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status-svg]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status-svg]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
});
</script>
<script async>

View file

@ -61,213 +61,25 @@
<!-- end list container-->
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Reverse scan is a feature designed to detect
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Reverse scan is a feature designed to detect
open ports by establishing TCP connections with clients' IP addresses.
Consider adding this feature if you want to detect possible open proxies
or connections from servers.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["port", "count"],
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["port", "count"],
},
});
</script>
</div>
{% endblock %}

View file

@ -71,210 +71,21 @@
</div>
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Selfsigned certificates allow you to get HTTPS / SSL / TLS on your requests.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Selfsigned certificates allow you to get HTTPS / SSL / TLS on your requests.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
});
</script>
</div>

View file

@ -219,231 +219,42 @@
</div>
<!-- end icon -->
</div>
<div
role="alert"
data-fetch
class="bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border"
>
<button data-fetch-close class="absolute right-7 top-1.5">
<svg
class="cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
>
<path
d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"
></path>
</svg>
</button>
<h5 data-fetch-status class="text-lg mb-0 text-white dark:text-gray-300">
Fetching
</h5>
<p data-fetch-msg class="text-white dark:text-gray-300 mb-0 text-sm">
Please wait...
</p>
</div>
<script>
class SetupPlugin {
constructor() {
// Alert elements
this.alertEl = document.querySelector("[data-fetch]");
this.alertCloseEl = document.querySelector("[data-fetch-close]");
this.alertStatusEl = document.querySelector("[data-fetch-status]");
this.alertMsgEl = document.querySelector("[data-fetch-msg]");
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = {
info: {
el: document.querySelector("[data-info]"),
value: `Allow access based on internal and external
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: `Allow access based on internal and external
IP/network/rDNS/ASN whitelists.`,
type: "text",
},
count_url: {
el: document.querySelector("[data-count-url]"),
value: "unknown",
type: "text",
},
count_ip: {
el: document.querySelector("[data-count-ip]"),
value: "unknown",
type: "text",
},
count_rdns: {
el: document.querySelector("[data-count-rdns]"),
value: "unknown",
type: "text",
},
count_asn: {
el: document.querySelector("[data-count-asn]"),
value: "unknown",
type: "text",
},
count_user_agent: {
el: document.querySelector("[data-count-user-agent]"),
value: "unknown",
type: "text",
},
/* EXAMPLE
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
};
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(
`[data-name="${nameKey}"]`,
).textContent = nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove(
"bg-sky-500",
"bg-green-500",
"bg-red-500",
);
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch")
return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}
new SetupPlugin();
type: "text",
},
count_url: {
el: document.querySelector("[data-count-url]"),
value: "unknown",
type: "text",
},
count_ip: {
el: document.querySelector("[data-count-ip]"),
value: "unknown",
type: "text",
},
count_rdns: {
el: document.querySelector("[data-count-rdns]"),
value: "unknown",
type: "text",
},
count_asn: {
el: document.querySelector("[data-count-asn]"),
value: "unknown",
type: "text",
},
count_user_agent: {
el: document.querySelector("[data-count-user-agent]"),
value: "unknown",
type: "text",
},
});
</script>
</div>
{% endblock %}

View file

@ -274,7 +274,7 @@ class Database:
def get_metadata(self) -> Dict[str, str]:
"""Get the metadata from the database"""
data = {"version": "1.5.4", "integration": "unknown"}
data = {"version": "1.5.6", "integration": "unknown"}
with self.__db_session() as session:
with suppress(ProgrammingError, OperationalError):
metadata = session.query(Metadata).with_entities(Metadata.version, Metadata.integration).filter_by(id=1).first()

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,244 @@
class SetupPlugin {
constructor(data) {
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = data;
/* EXAMPLE
{
info: {
el: document.querySelector("[data-info]"),
value: `Anti-bot technology is designed to detect and mitigate suspicious or
malicious bots, preventing them from reaching an organization's websites
or IT ecosystem.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]"
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
this.createAlertEl();
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(location.href, {
method: "POST",
headers: {
"X-CSRFToken": "{{ csrf_token() }}",
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
}
createAlertEl() {
// Container
this.alertEl = this.createEl(
"div",
[
["data-fetch", ""],
["role", "alert"],
],
"bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:bg-slate-850 dark:shadow-dark-xl bg-clip-border",
"",
""
);
this.alertCloseEl = this.createEl(
"button",
[["data-fetch-close", ""]],
"absolute right-7 top-1.5",
"",
this.alertEl
);
this.alertCloseIconEl = this.createEl(
"svg",
[
["xmlns", "http://www.w3.org/2000/svg"],
["viewBox", "0 0 320 512"],
],
"cursor-pointer fill-white dark:fill-gray-300 dark:opacity-80 absolute h-5 w-5",
"",
this.alertCloseEl
);
// Close icon paths
const paths = [
"M11.7 2.805a.75.75 0 0 1 .6 0A60.65 60.65 0 0 1 22.83 8.72a.75.75 0 0 1-.231 1.337 49.948 49.948 0 0 0-9.902 3.912l-.003.002c-.114.06-.227.119-.34.18a.75.75 0 0 1-.707 0A50.88 50.88 0 0 0 7.5 12.173v-.224c0-.131.067-.248.172-.311a54.615 54.615 0 0 1 4.653-2.52.75.75 0 0 0-.65-1.352 56.123 56.123 0 0 0-4.78 2.589 1.858 1.858 0 0 0-.859 1.228 49.803 49.803 0 0 0-4.634-1.527.75.75 0 0 1-.231-1.337A60.653 60.653 0 0 1 11.7 2.805Z",
,
"M13.06 15.473a48.45 48.45 0 0 1 7.666-3.282c.134 1.414.22 2.843.255 4.284a.75.75 0 0 1-.46.711 47.87 47.87 0 0 0-8.105 4.342.75.75 0 0 1-.832 0 47.87 47.87 0 0 0-8.104-4.342.75.75 0 0 1-.461-.71c.035-1.442.121-2.87.255-4.286.921.304 1.83.634 2.726.99v1.27a1.5 1.5 0 0 0-.14 2.508c-.09.38-.222.753-.397 1.11.452.213.901.434 1.346.66a6.727 6.727 0 0 0 .551-1.607 1.5 1.5 0 0 0 .14-2.67v-.645a48.549 48.549 0 0 1 3.44 1.667 2.25 2.25 0 0 0 2.12 0Z",
,
"M4.462 19.462c.42-.419.753-.89 1-1.395.453.214.902.435 1.347.662a6.742 6.742 0 0 1-1.286 1.794.75.75 0 0 1-1.06-1.06Z",
];
paths.forEach((path) => {
this.createEl("path", [["d", path]], "", "", this.alertCloseIconEl);
});
// Status
this.alertStatusEl = this.createEl(
"h5",
[["data-fetch-status", ""]],
"text-lg mb-0 text-white dark:text-gray-300",
"Fetching",
this.alertEl
);
this.alertMsgEl = this.createEl(
"p",
[["data-fetch-msg", ""]],
"text-white dark:text-gray-300 mb-0 text-sm",
"Please wait...",
this.alertEl
);
document.body.appendChild(this.alertEl);
this.alertCloseEl.addEventListener("click", () => {
this.alertEl.classList.add("hidden");
});
}
createEl(tag, attArr, className, text, parent) {
const el = document.createElement(tag);
attArr.forEach((att) => {
el.setAttribute(att[0], att[1]);
});
if (className) el.className = className;
if (text) el.textContent = text;
if (parent) parent.appendChild(el);
return el;
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (value === "active")
this.setStatus(el, textEl, "fill-green-500", "Active");
if (value === "inactive")
this.setStatus(el, textEl, "fill-red-500", "Inactive");
if (value === "unknown")
this.setStatus(el, textEl, "fill-sky-500", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(`[data-name="${nameKey}"]`).textContent =
nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("fill-green-500", "fill-red-500", "fill-sky-500");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove("bg-sky-500", "bg-green-500", "bg-red-500");
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch") return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}

View file

@ -116,7 +116,7 @@ class ServiceModal {
this.updateModalData(obj);
//show modal
this.resetFilterInp();
this.changeSubmitBtn("EDIT", "edit-btn");
this.changeSubmitBtn("SAVE", "valid-btn");
this.openModal();
}
} catch (err) {}

View file

@ -259,7 +259,13 @@ class FolderDropdown {
class FolderEditor {
constructor() {
this.editor = ace.edit("editor");
this.editor = ace.edit("editor", {
backwards: false,
wrap: false,
caseSensitive: false,
wholeWord: false,
regExp: false,
});
this.darkMode = document.querySelector("[data-dark-toggle]");
this.initEditor();
this.listenDarkToggle();
@ -268,6 +274,8 @@ class FolderEditor {
initEditor() {
//editor options
this.editor.setShowPrintMargin(false);
this.editor.session.setUseSoftTabs(true);
this.editor.setOption("showInvisibles", true);
this.setDarkMode();
}

View file

@ -8,16 +8,15 @@
}
* {
font-family: "Open Sans", sans-serif !important;
font-family: "Open Sans", sans-serif;
}
.ace_editor,
.ace_editor div,
.ace_content {
font-family: "Monaco", "Menlo", "Ubuntu Mono", "Consolas", "source-code-pro",
.ace_editor * {
font-family: "Monaco", "Menlo", "Ubuntu Mono", "Droid Sans Mono", "Consolas",
monospace !important;
font-size: 16px !important;
font-weight: normal !important;
font-weight: 400 !important;
letter-spacing: 0 !important;
}
.sr-only {

View file

@ -18,6 +18,7 @@
<link rel="stylesheet" type="text/css" href="./css/dashboard.css" />
<script type="module" src="./js/global.js"></script>
<script src="./js/plugins/setup.js"></script>
<script async src="./js/utils/purify/purify.min.js"></script>
<script src="./js/editor/ace.js"></script>

View file

@ -439,7 +439,7 @@
<a
class="leading-8 font-bold hover:brightness-75"
target="_blank"
href="https://docs.bunkerweb.io/1.5.4/plugins/?utm_campaign=self&utm_source=ui#writing-a-plugin"
href="https://docs.bunkerweb.io/latest/plugins/?utm_campaign=self&utm_source=ui#writing-a-plugin"
>check doc</a
>
</h6>
@ -449,7 +449,7 @@
<a
target="_blank"
class="{% if current_endpoint == 'logs' %}font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/20 hover:bg-primary/30{% else %}dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} hover:rounded-lg dark:text-white dark:opacity-80 py-1 text-sm ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap px-4 transition"
href="{{request.url_root}}plugins?plugin_id={{plugin['id']}}"
href="{{request.url_root}}plugins/{{plugin['id']}}"
>
<div
class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5"