mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Merge branch 'dev' of github.com:bunkerity/bunkerweb into dev
This commit is contained in:
commit
bf5e3141b2
29 changed files with 623 additions and 4136 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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
244
src/ui/static/js/plugins/setup.js
Normal file
244
src/ui/static/js/plugins/setup.js
Normal 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"];
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
1
src/ui/templates/head.html
vendored
1
src/ui/templates/head.html
vendored
|
|
@ -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>
|
||||
|
|
|
|||
4
src/ui/templates/menu.html
vendored
4
src/ui/templates/menu.html
vendored
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue