mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
update core template + actions
*create script to get and setup core plugins data using ajax after page load instead of using jinja *data will be get from actions.py, dummy values were moved from jinja to actions.py until real logic with db or instance *update every core plugins with new logic
This commit is contained in:
parent
7377909976
commit
503868b5d6
40 changed files with 4469 additions and 506 deletions
|
|
@ -110,9 +110,26 @@
|
|||
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.showOnSuccess = [];
|
||||
this.showOnSuccessEls = document.querySelectorAll(
|
||||
"[data-fetch-success-show]",
|
||||
);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
|
@ -131,24 +148,15 @@
|
|||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
console.log(JSON.stringify(res));
|
||||
console.log(res.data);
|
||||
console.log(res.data.data);
|
||||
// Update data and DOM
|
||||
this.getFetchDataByKey(res.data);
|
||||
this.getFetchDataByKey(res.data.data);
|
||||
this.updateDataDOM();
|
||||
// Show hidden elements
|
||||
if (this.showOnSuccess.length > 0) {
|
||||
this.showOnSuccess.forEach((el) => {
|
||||
el.classList.remove("hidden");
|
||||
});
|
||||
}
|
||||
this.showSuccessEls();
|
||||
// Feedback
|
||||
this.updateAlert("success");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
this.updateAlert("error");
|
||||
});
|
||||
});
|
||||
|
|
@ -158,59 +166,103 @@
|
|||
});
|
||||
}
|
||||
|
||||
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)) {
|
||||
this.data[key]["value"] =
|
||||
fetchDataObj[key] || this.data[key]["value"] || "";
|
||||
value["value"] = fetchDataObj[key] || value["value"] || "";
|
||||
}
|
||||
}
|
||||
|
||||
updateDataDOM() {
|
||||
for (const [key, value] of Object.entries(this.data)) {
|
||||
for (const [key, val] of Object.entries(this.data)) {
|
||||
const el = val["el"];
|
||||
const type = val["type"];
|
||||
const value = val["value"];
|
||||
|
||||
// Case text
|
||||
if (this.data[key]["type"] == "text") {
|
||||
this.data[key]["el"].textContent = this.data[key]["value"] || "";
|
||||
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 (this.data[key]["type"] == "text") {
|
||||
this.data[key]["el"].textContent = this.data[key]["value"] || "";
|
||||
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;
|
||||
|
||||
if (type == "fetch") {
|
||||
this.alertStatusEl.textContent = "Fetching";
|
||||
this.alertMsgEl.textContent = "Please wait...";
|
||||
this.alertEl.classList.remove("bg-green-500");
|
||||
this.alertEl.classList.remove("bg-red-500");
|
||||
this.alertEl.classList.add("bg-sky-500");
|
||||
}
|
||||
if (type == "success") {
|
||||
this.alertStatusEl.textContent = "Success";
|
||||
this.alertMsgEl.textContent = "Data fetched successfully";
|
||||
this.alertEl.classList.remove("bg-sky-500");
|
||||
this.alertEl.classList.remove("bg-red-500");
|
||||
this.alertEl.classList.add("bg-green-500");
|
||||
}
|
||||
if (type == "error") {
|
||||
this.alertStatusEl.textContent = "Error";
|
||||
this.alertMsgEl.textContent = "Something went wrong";
|
||||
this.alertEl.classList.remove("bg-sky-500");
|
||||
this.alertEl.classList.remove("bg-green-500");
|
||||
this.alertEl.classList.add("bg-red-500");
|
||||
}
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def authbasic():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"count": 3,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ authbasic_info or "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." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
|
@ -29,9 +26,7 @@
|
|||
>
|
||||
AUTH BASIC
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ authbasic_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-green-500 mx-0.5"
|
||||
|
|
@ -60,6 +55,213 @@
|
|||
</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: `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();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,9 @@
|
|||
# Spoofing an action file
|
||||
def badbehavior():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"count": 3,
|
||||
"items": [{"code": 400, "count": 24}, {"code": 403, "count": 845}, {"code": 402, "count": 12}],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
{% extends "base.html" %} {% block content %} {% set items = [{"code" : 400,
|
||||
"count" : 24}, {"code" : 403, "count" : 845}, {"code" : 402, "count" : 12}]%}
|
||||
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
|
|
@ -11,11 +9,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ bad_behavior_info or "Ban IP generating too much 'bad' HTTP status
|
||||
code in a period of time." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -31,9 +27,7 @@
|
|||
>
|
||||
BAD BEHAVIOR
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ bad_behavior_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
|
|
@ -64,9 +58,9 @@
|
|||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
{% if items|length != 0 %}
|
||||
<div
|
||||
class="2xl:col-span-4 3xl:col-span-3 w-full md:max-w-[350px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-fetch-success-show
|
||||
class="hidden 2xl:col-span-4 3xl:col-span-3 w-full md:max-w-[350px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">
|
||||
|
|
@ -91,29 +85,237 @@
|
|||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
{% for item in items %}
|
||||
<li
|
||||
data-item
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="code"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-6 m-0 my-1"
|
||||
>
|
||||
{{item['code']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="count"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-6 m-0 my-1"
|
||||
>
|
||||
{{item['count']}}
|
||||
</p>
|
||||
></p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
<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
|
||||
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();
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1 +1,11 @@
|
|||
# Spoofing an action file
|
||||
def blacklist():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"count_url": 4,
|
||||
"count_ip": 2,
|
||||
"count_rdns": 1,
|
||||
"count_asn": 10,
|
||||
"count_user_agent": 10,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ blacklist_info or "Deny access based on internal and external
|
||||
IP/network/rDNS/ASN blacklists." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
|
@ -30,9 +28,7 @@
|
|||
>
|
||||
URL
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ blacklist_url_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-url class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -72,9 +68,7 @@
|
|||
>
|
||||
IP
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ blacklist_ip_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-ip class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -112,9 +106,7 @@
|
|||
>
|
||||
RDNS
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ blacklist_rdns_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-rdns class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -160,9 +152,7 @@
|
|||
>
|
||||
ASN
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ blacklist_asn_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-asn class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -200,9 +190,7 @@
|
|||
>
|
||||
User Agent
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ blacklist_user_agent_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-user-agent class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -231,6 +219,231 @@
|
|||
</div>
|
||||
<!-- end icon -->
|
||||
</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 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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def bunkernet():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"status": "active",
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,57 +3,25 @@
|
|||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- status -->
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if bunkernet_status %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-6 2xl:col-span-3 3xl:col-span-2 w-fit h-fit transition hover:scale-102 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="mx-1 flex justify-start items-center">
|
||||
<h5 class="mb-0 font-bold dark:text-white/90 mr-4">STATUS</h5>
|
||||
<svg
|
||||
data-status-svg
|
||||
class="w-8 h-8"
|
||||
viewBox="0 0 100 100"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-8 h-8 fill-green-500"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<circle cx="50" cy="50" r="50" />
|
||||
</svg>
|
||||
</div>
|
||||
<p
|
||||
data-status-text
|
||||
class="mx-1 transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
Active
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-6 2xl:col-span-3 3xl:col-span-2 w-fit h-fit transition hover:scale-102 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="mx-1 flex justify-start items-center">
|
||||
<h5 class="mb-0 font-bold dark:text-white/90 mr-4">STATUS</h5>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-8 h-8 fill-red-500"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-1.72 6.97a.75.75 0 1 0-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06L12 13.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L13.06 12l1.72-1.72a.75.75 0 1 0-1.06-1.06L12 10.94l-1.72-1.72Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p
|
||||
class="mx-1 transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
Inactive
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- end status -->
|
||||
</div>
|
||||
|
|
@ -66,11 +34,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ bunkernet_info or "BunkerNet is a crowdsourced database of malicious
|
||||
requests shared between all BunkerWeb instances over the world. " }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
|
@ -180,6 +146,206 @@
|
|||
</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: `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();
|
||||
</script>
|
||||
|
||||
<!-- end test -->
|
||||
<script async>
|
||||
function ping() {
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def cors():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"count": 3,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ cors_info or "Cross-Origin Resource Sharing lets you manage how your
|
||||
service can be contacted from different origins." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
|
@ -28,9 +26,7 @@
|
|||
>
|
||||
CORS
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ cors_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
|
|
@ -61,6 +57,211 @@
|
|||
</div>
|
||||
<!-- end icon -->
|
||||
</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: `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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,9 @@
|
|||
# Spoofing an action file
|
||||
def country():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"blacklist_count": 3,
|
||||
"whitelist_count": 23,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ country_info or "The country security feature allows you to apply
|
||||
policy based on the country of the IP address of clients (blacklist /
|
||||
whitelist)." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -31,9 +28,7 @@
|
|||
>
|
||||
Country
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ country_blacklist_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-blacklist class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
|
|
@ -75,9 +70,7 @@
|
|||
>
|
||||
Country
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ country_blacklist_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-whitelist class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-green-500 mx-0.5"
|
||||
|
|
@ -106,6 +99,216 @@
|
|||
</div>
|
||||
<!-- end icon -->
|
||||
</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: `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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def customcert():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"items": [{"server_name": "www.example.com", "cn": "Let's encrypt", "expire": "15/11/2024"}, {"server_name": "app1.com", "cn": "Self signed", "expire": "11/01/2028"}, {"server_name": "test.2.fr", "cn": "Default", "expire": "31/08/2035"}],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
{% extends "base.html" %} {% block content %} {% set items = [ {"server_name" :
|
||||
"www.example.com", "cn" : "Let's encrypt", "expire" : "15/11/2024"},
|
||||
{"server_name" : "app1.com", "cn" : "Self signed", "expire" : "11/01/2028"},
|
||||
{"server_name" : "test.2.fr", "cn" : "Default", "expire" : "31/08/2035"} ]%}
|
||||
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
|
|
@ -12,19 +8,16 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ custom_certificate_info or "Custom certificates allow you to get
|
||||
HTTPS / SSL / TLS on your requests." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
{% if items|length != 0 %}
|
||||
|
||||
<div
|
||||
class="col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">
|
||||
|
|
@ -54,34 +47,235 @@
|
|||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
{% for item in items %}
|
||||
<li
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
data-item
|
||||
class="hidden items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="server_name"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['server_name']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="cn"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['cn']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="expire"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['expire']}}
|
||||
</p>
|
||||
></p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<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();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,10 @@
|
|||
# Spoofing an action file
|
||||
def db():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"driver": "SQLite",
|
||||
"version": "13.2",
|
||||
"size": "14.8",
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ db_info or "BunkerWeb securely stores its current configuration in
|
||||
a backend database, which contains essential data for smooth
|
||||
operation." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -33,9 +30,9 @@
|
|||
>
|
||||
DRIVER
|
||||
<span
|
||||
data-driver
|
||||
class="ml-1 font-semibold transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ db_driver or "unknown" }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -45,9 +42,9 @@
|
|||
>
|
||||
VERSION
|
||||
<span
|
||||
data-version
|
||||
class="ml-1 font-semibold transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ db_version or "unknown" }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -64,9 +61,7 @@
|
|||
>
|
||||
SIZE
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ db_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-size class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-sky-500 mx-0.5">
|
||||
|
|
@ -102,6 +97,227 @@
|
|||
</div>
|
||||
<!-- end icon -->
|
||||
</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: `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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def dnsbl():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"items": [{"server_name": "www.example.com", "status": "ok"}, {"server_name": "app1.com", "status": "ok"}, {"server_name": "test.2.fr", "status": "ko"}],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
{% extends "base.html" %} {% block content %} {% set items = [ {"server_name" :
|
||||
"www.example.com", "status" : "ok"}, {"server_name" : "app1.com", "status" :
|
||||
"ok"}, {"server_name" : "test.2.fr", "status" : "ko"} ]%}
|
||||
{% extends "base.html" %} {% block content %}
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
|
|
@ -11,17 +9,16 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ dnsbl_info or "Deny access based on external DNSBL servers." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
{% if items|length != 0 %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">DNSBL LIST</h5>
|
||||
|
|
@ -43,55 +40,230 @@
|
|||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
{% for item in items %}
|
||||
<li
|
||||
data-item
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="server_name"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-8 m-0 my-1"
|
||||
>
|
||||
{{item['server_name']}}
|
||||
</p>
|
||||
{% if item['status'] == "ko"%}
|
||||
<div class="col-span-4 ml-2 m-0 my-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-6 h-6 fill-red-500"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-1.72 6.97a.75.75 0 1 0-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06L12 13.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L13.06 12l1.72-1.72a.75.75 0 1 0-1.06-1.06L12 10.94l-1.72-1.72Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-span-4 ml-2 m-0 my-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-6 h-6 fill-green-500"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{% endif %}
|
||||
></p>
|
||||
<p
|
||||
data-name="status"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 ml-2 m-0 my-1"
|
||||
></p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<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();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def errors():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"items": [{"count": 74, "code": "403"}, {"count": 82, "code": "404"}, {"count": "32", "code": "400"}],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
{% extends "base.html" %} {% block content %} {% set items = [ {"count" : 74,
|
||||
"code" : "403"}, {"count" : 82, "code" : "404"}, {"count" : "32", "code" :
|
||||
"400"} ]%}
|
||||
{% extends "base.html" %} {% block content %}
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
|
|
@ -11,17 +9,16 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ dnsbl_info or "Deny access based on external DNSBL servers." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
{% if items|length != 0 %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-8 3xl:col-span-9 w-full md:max-w-[400px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full md:max-w-[400px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">ERRORS LIST</h5>
|
||||
|
|
@ -43,29 +40,230 @@
|
|||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
{% for item in items %}
|
||||
<li
|
||||
data-item
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="code"
|
||||
class="ml-1 dark:text-gray-400 dark:opacity-80 text-sm col-span-8 m-0 my-1"
|
||||
>
|
||||
{{item['code']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="count"
|
||||
class="ml-1 dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['count']}}
|
||||
</p>
|
||||
></p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<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();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,11 @@
|
|||
# Spoofing an action file
|
||||
def greylist():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"count_url": 4,
|
||||
"count_ip": 2,
|
||||
"count_rdns": 1,
|
||||
"count_asn": 10,
|
||||
"count_user_agent": 10,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ greylist_info or "Allow access while keeping security features
|
||||
based on internal and external IP/network/rDNS/ASN greylists. " }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
|
@ -30,9 +28,7 @@
|
|||
>
|
||||
URL
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ greylist_url_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-url class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -72,9 +68,7 @@
|
|||
>
|
||||
IP
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ greylist_ip_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-ip class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -112,9 +106,7 @@
|
|||
>
|
||||
RDNS
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ greylist_rdns_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-rdns class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -160,9 +152,7 @@
|
|||
>
|
||||
ASN
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ greylist_asn_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-asn class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -200,9 +190,7 @@
|
|||
>
|
||||
User Agent
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ greylist_user_agent_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-user-agent class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
|
|
@ -231,6 +219,231 @@
|
|||
</div>
|
||||
<!-- end icon -->
|
||||
</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 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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def letsencrypt():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"items": [{"server_name": "www.example.com", "cn": "Let's encrypt", "expire": "15/11/2024"}, {"server_name": "app1.com", "cn": "Self signed", "expire": "11/01/2028"}, {"server_name": "test.2.fr", "cn": "Default", "expire": "31/08/2035"}],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
{% extends "base.html" %} {% block content %} {% set items = [ {"server_name" :
|
||||
"www.example.com", "cn" : "Let's encrypt", "expire" : "15/11/2024"},
|
||||
{"server_name" : "app1.com", "cn" : "Self signed", "expire" : "11/01/2028"},
|
||||
{"server_name" : "test.2.fr", "cn" : "Default", "expire" : "31/08/2035"} ]%}
|
||||
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
|
|
@ -12,18 +8,16 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ lets_encrypt_info or "Let's Encrypt certificates for secure HTTP
|
||||
requests." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
{% if items|length != 0 %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">
|
||||
|
|
@ -53,34 +47,235 @@
|
|||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
{% for item in items %}
|
||||
<li
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
data-item
|
||||
class="hidden items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="server_name"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['server_name']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="cn"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['cn']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="expire"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['expire']}}
|
||||
</p>
|
||||
></p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<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();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def limit():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"items": [{"url": "http://www.example.com", "count": 24}, {"url": "http://www.example.com", "count": 24}],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
{% extends "base.html" %} {% block content %} {% set items = [ {"url" :
|
||||
"http://www.example.com", "count" : 24},{"url" : "http://www.example.com",
|
||||
"count" : 24} ]%}
|
||||
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
|
|
@ -11,17 +8,16 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ limit_info or "Limit maximum number of requests and connections." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
{% if items|length != 0 %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">
|
||||
|
|
@ -46,29 +42,234 @@
|
|||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
{% for item in items %}
|
||||
<li
|
||||
data-item
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="url"
|
||||
class="ml-1 dark:text-gray-400 dark:opacity-80 text-sm col-span-8 m-0 my-1"
|
||||
>
|
||||
{{item['url']}}
|
||||
</p>
|
||||
<p
|
||||
data-name="count"
|
||||
class="ml-1 dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['count']}}
|
||||
</p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,9 @@
|
|||
# Spoofing an action file
|
||||
def misc():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"count_disabled_servers": 0,
|
||||
"count_disallowed_methods": 0,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ misc_info or "Miscellaneous settings (methods, servers...)." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
|
@ -29,9 +28,10 @@
|
|||
>
|
||||
DEFAULT SERVER DISABLED
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ default_server_count or "unknown" }}
|
||||
</h5>
|
||||
<h5
|
||||
data-count-server-disabled
|
||||
class="mb-1 font-bold dark:text-white/90"
|
||||
></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-sky-500 mx-0.5">
|
||||
|
|
@ -74,9 +74,10 @@
|
|||
>
|
||||
DISALLOWED METHODS
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ disallowed_methods_count or "unknown" }}
|
||||
</h5>
|
||||
<h5
|
||||
data-count-disallowed-methods
|
||||
class="mb-1 font-bold dark:text-white/90"
|
||||
></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-sky-500 mx-0.5">
|
||||
|
|
@ -103,6 +104,215 @@
|
|||
</div>
|
||||
<!-- end icon -->
|
||||
</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: `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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def modsecurity():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"count": 3,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@
|
|||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
<p data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ modsec_info or "ModSecurity is an open source, cross platform web application firewall (WAF) engine for Apache, IIS and Nginx that is developed by Trustwave's SpiderLabs." }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -27,7 +26,7 @@
|
|||
>
|
||||
MODSECURITY
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">{{ modsec_count or "unknown" }}</h5>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
|
|
@ -59,6 +58,216 @@
|
|||
</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();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def redis():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"status": "active",
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,58 +3,25 @@
|
|||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- status -->
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if redis_status %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-6 2xl:col-span-3 3xl:col-span-2 w-fit h-fit transition hover:scale-102 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="mx-1 flex justify-start items-center">
|
||||
<h5 class="mb-0 font-bold dark:text-white/90 mr-4">STATUS</h5>
|
||||
<svg
|
||||
data-status-svg
|
||||
class="w-8 h-8"
|
||||
viewBox="0 0 100 100"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-8 h-8 fill-green-500"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<circle cx="50" cy="50" r="50" />
|
||||
</svg>
|
||||
</div>
|
||||
<p
|
||||
data-status-text
|
||||
class="mx-1 transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
Active
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-6 2xl:col-span-3 3xl:col-span-2 w-fit h-fit transition hover:scale-102 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="mx-1 flex justify-start items-center">
|
||||
<h5 class="mb-0 font-bold dark:text-white/90 mr-4">STATUS</h5>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-8 h-8 fill-red-500"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-1.72 6.97a.75.75 0 1 0-1.06 1.06L10.94 12l-1.72 1.72a.75.75 0 1 0 1.06 1.06L12 13.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L13.06 12l1.72-1.72a.75.75 0 1 0-1.06-1.06L12 10.94l-1.72-1.72Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p
|
||||
class="mx-1 transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
Inactive
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- end status -->
|
||||
</div>
|
||||
|
||||
|
|
@ -66,11 +33,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ redis_info or "Redis server configuration when using BunkerWeb in
|
||||
cluster mode. " }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
|
@ -179,7 +144,205 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end test -->
|
||||
<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
|
||||
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();
|
||||
</script>
|
||||
|
||||
<script async>
|
||||
function ping() {
|
||||
let data = new FormData();
|
||||
|
|
|
|||
|
|
@ -1 +1,12 @@
|
|||
# Spoofing an action file
|
||||
def reversescan():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"items": [
|
||||
{"port": 4000, "count": 400},
|
||||
{"port": 4400, "count": 780},
|
||||
{"port": 5000, "count": 40},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
{% extends "base.html" %} {% block content %} {% set items = [ {"port" : 4000,
|
||||
"count" : 400}, {"port" : 4400, "count" : 780}, {"port" : 5000, "count" : 40},
|
||||
]%}
|
||||
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
|
|
@ -11,21 +8,16 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ reversescan_info or "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." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
{% if items|length != 0 %}
|
||||
|
||||
<div
|
||||
class="col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[500px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[500px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">
|
||||
|
|
@ -50,29 +42,232 @@
|
|||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
{% for item in items %}
|
||||
<li
|
||||
data-item
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="port"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-5 m-0 my-1"
|
||||
>
|
||||
{{item['port']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="count"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-7 m-0 my-1"
|
||||
>
|
||||
{{item['count']}}
|
||||
</p>
|
||||
></p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</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
|
||||
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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
# Spoofing an action file
|
||||
def selfsigned():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"items": [{"server_name": "www.example.com", "cn": "Let's encrypt", "expire": "15/11/2024"}, {"server_name": "app1.com", "cn": "Self signed", "expire": "11/01/2028"}, {"server_name": "test.2.fr", "cn": "Default", "expire": "31/08/2035"}],
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
{% extends "base.html" %} {% block content %} {% set items = [ {"server_name" :
|
||||
"www.example.com", "cn" : "Let's encrypt", "expire" : "15/11/2024"},
|
||||
{"server_name" : "app1.com", "cn" : "Self signed", "expire" : "11/01/2028"},
|
||||
{"server_name" : "test.2.fr", "cn" : "Default", "expire" : "31/08/2035"} ]%}
|
||||
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
|
|
@ -12,18 +8,16 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ lets_encrypt_info or "Selfsigned certificates for secure HTTP
|
||||
requests." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
{% if items|length != 0 %}
|
||||
<div
|
||||
class="col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">
|
||||
|
|
@ -53,34 +47,235 @@
|
|||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
{% for item in items %}
|
||||
<li
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
data-item
|
||||
class="hidden items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="server_name"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['server_name']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="cn"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['cn']}}
|
||||
</p>
|
||||
></p>
|
||||
<p
|
||||
data-name="expire"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
>
|
||||
{{item['expire']}}
|
||||
</p>
|
||||
></p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<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();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1 +1,11 @@
|
|||
# Spoofing an action file
|
||||
def whitelist():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"count_url": 4,
|
||||
"count_ip": 2,
|
||||
"count_rdns": 1,
|
||||
"count_asn": 10,
|
||||
"count_user_agent": 10,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,9 @@
|
|||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
{{ whitelist_info or "Allow access based on internal and external
|
||||
IP/network/rDNS/ASN whitelists." }}
|
||||
</p>
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
|
@ -30,13 +28,11 @@
|
|||
>
|
||||
URL
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ whitelist_url_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-url class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-green-500 mx-0.5">
|
||||
passed
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -72,13 +68,11 @@
|
|||
>
|
||||
IP
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ whitelist_ip_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-ip class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-green-500 mx-0.5">
|
||||
passed
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -112,13 +106,11 @@
|
|||
>
|
||||
RDNS
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ whitelist_rdns_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-rdns class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-green-500 mx-0.5">
|
||||
passed
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -160,13 +152,11 @@
|
|||
>
|
||||
ASN
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ whitelist_asn_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-asn class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-green-500 mx-0.5">
|
||||
passed
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -200,13 +190,11 @@
|
|||
>
|
||||
User Agent
|
||||
</p>
|
||||
<h5 class="mb-1 font-bold dark:text-white/90">
|
||||
{{ whitelist_user_agent_count or "unknown" }}
|
||||
</h5>
|
||||
<h5 data-count-user-agent class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-green-500 mx-0.5">
|
||||
passed
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -231,6 +219,231 @@
|
|||
</div>
|
||||
<!-- end icon -->
|
||||
</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: `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();
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ class ServiceModal {
|
|||
//modal forms
|
||||
this.formNewEdit = this.modal.querySelector("[data-services-modal-form]");
|
||||
this.formDelete = this.modal.querySelector(
|
||||
"[data-services-modal-form-delete]"
|
||||
"[data-services-modal-form-delete]",
|
||||
);
|
||||
this.submitBtn = document.querySelector(
|
||||
"button[data-services-modal-submit]"
|
||||
"button[data-services-modal-submit]",
|
||||
);
|
||||
//container
|
||||
this.container = document.querySelector("main");
|
||||
|
|
@ -92,7 +92,7 @@ class ServiceModal {
|
|||
) {
|
||||
//set form info and right form
|
||||
const [action, serviceName, isDraft, method] = this.getActionData(
|
||||
e.target
|
||||
e.target,
|
||||
);
|
||||
const oldServName = e.target
|
||||
.closest("[data-services-service]")
|
||||
|
|
@ -104,7 +104,7 @@ class ServiceModal {
|
|||
oldServName,
|
||||
this.formNewEdit,
|
||||
isDraft,
|
||||
method
|
||||
method,
|
||||
);
|
||||
//get service data and parse it
|
||||
//multiple type logic is launch at same time on relate class
|
||||
|
|
@ -128,7 +128,7 @@ class ServiceModal {
|
|||
) {
|
||||
//set form info and right form
|
||||
const [action, serviceName, isDraft, method] = this.getActionData(
|
||||
e.target
|
||||
e.target,
|
||||
);
|
||||
this.setForm(
|
||||
action,
|
||||
|
|
@ -136,7 +136,7 @@ class ServiceModal {
|
|||
serviceName,
|
||||
this.formNewEdit,
|
||||
isDraft,
|
||||
method
|
||||
method,
|
||||
);
|
||||
//set default value with method default
|
||||
//get service data and parse it
|
||||
|
|
@ -168,7 +168,7 @@ class ServiceModal {
|
|||
) {
|
||||
//set form info and right form
|
||||
const [action, serviceName, isDraft, method] = this.getActionData(
|
||||
e.target
|
||||
e.target,
|
||||
);
|
||||
this.setForm(
|
||||
action,
|
||||
|
|
@ -176,7 +176,7 @@ class ServiceModal {
|
|||
serviceName,
|
||||
this.formNewEdit,
|
||||
isDraft,
|
||||
method
|
||||
method,
|
||||
);
|
||||
//set default value with method default
|
||||
this.setSettingsDefault();
|
||||
|
|
@ -200,7 +200,7 @@ class ServiceModal {
|
|||
) {
|
||||
//set form info and right form
|
||||
const [action, serviceName, isDraft, method] = this.getActionData(
|
||||
e.target
|
||||
e.target,
|
||||
);
|
||||
this.setForm(
|
||||
action,
|
||||
|
|
@ -208,7 +208,7 @@ class ServiceModal {
|
|||
serviceName,
|
||||
this.formDelete,
|
||||
isDraft,
|
||||
method
|
||||
method,
|
||||
);
|
||||
//show modal
|
||||
this.openModal();
|
||||
|
|
@ -229,7 +229,7 @@ class ServiceModal {
|
|||
"delete-btn",
|
||||
"valid-btn",
|
||||
"edit-btn",
|
||||
"info-btn"
|
||||
"info-btn",
|
||||
);
|
||||
this.submitBtn.classList.add(btnType);
|
||||
}
|
||||
|
|
@ -290,15 +290,15 @@ class ServiceModal {
|
|||
//click the custom select dropdown to update select value
|
||||
select.parentElement
|
||||
.querySelector(
|
||||
`button[data-setting-select-dropdown-btn][value='${defaultVal}']`
|
||||
`button[data-setting-select-dropdown-btn][value='${defaultVal}']`,
|
||||
)
|
||||
.click();
|
||||
|
||||
//set state to custom visible el
|
||||
const btnCustom = document.querySelector(
|
||||
`[data-setting-select=${select.getAttribute(
|
||||
"data-setting-select-default"
|
||||
)}]`
|
||||
"data-setting-select-default",
|
||||
)}]`,
|
||||
);
|
||||
|
||||
this.setDisabledDefault(btnCustom, defaultMethod);
|
||||
|
|
@ -374,9 +374,8 @@ class ServiceModal {
|
|||
|
||||
if (action === "delete") {
|
||||
this.showDeleteForm();
|
||||
formEl.querySelector(
|
||||
`[data-services-modal-text]`
|
||||
).textContent = `Are you sure you want to delete ${serviceName} ?`;
|
||||
formEl.querySelector(`[data-services-modal-text]`).textContent =
|
||||
`Are you sure you want to delete ${serviceName} ?`;
|
||||
const nameInp = formEl.querySelector(`input[name="SERVER_NAME"]`);
|
||||
nameInp.setAttribute("value", serviceName);
|
||||
nameInp.value = serviceName;
|
||||
|
|
@ -487,7 +486,7 @@ class ServiceModal {
|
|||
if (inp.tagName === "SELECT") {
|
||||
inp.parentElement
|
||||
.querySelector(
|
||||
`button[data-setting-select-dropdown-btn][value='${value}']`
|
||||
`button[data-setting-select-dropdown-btn][value='${value}']`,
|
||||
)
|
||||
.click();
|
||||
inp.setAttribute("data-method", method);
|
||||
|
|
@ -599,7 +598,7 @@ class Multiple {
|
|||
const attName = btn.getAttribute(`data-${this.prefix}-multiple-add`);
|
||||
//get all multiple groups
|
||||
const multipleEls = document.querySelectorAll(
|
||||
`[data-${this.prefix}-settings-multiple*="${attName}"]`
|
||||
`[data-${this.prefix}-settings-multiple*="${attName}"]`,
|
||||
);
|
||||
//case no schema
|
||||
if (multipleEls.length <= 0) return;
|
||||
|
|
@ -611,7 +610,7 @@ class Multiple {
|
|||
//and keep the highest num
|
||||
multipleEls.forEach((container) => {
|
||||
const ctnrName = container.getAttribute(
|
||||
"data-services-settings-multiple"
|
||||
"data-services-settings-multiple",
|
||||
);
|
||||
const num = this.getSuffixNumOrFalse(ctnrName);
|
||||
if (!isNaN(num) && num > topNum) topNum = num;
|
||||
|
|
@ -622,7 +621,7 @@ class Multiple {
|
|||
const setNum = +currNum === 0 ? `` : `_${currNum}`;
|
||||
//the default (schema) group is the last group
|
||||
const schema = document.querySelector(
|
||||
`[data-${this.prefix}-settings-multiple="${attName}_SCHEMA"]`
|
||||
`[data-${this.prefix}-settings-multiple="${attName}_SCHEMA"]`,
|
||||
);
|
||||
//clone schema to create a group with new num
|
||||
const schemaClone = schema.cloneNode(true);
|
||||
|
|
@ -660,7 +659,7 @@ class Multiple {
|
|||
.hasAttribute(`data-${this.prefix}-multiple-delete`)
|
||||
) {
|
||||
const multContainer = e.target.closest(
|
||||
"[data-services-settings-multiple]"
|
||||
"[data-services-settings-multiple]",
|
||||
);
|
||||
multContainer.remove();
|
||||
}
|
||||
|
|
@ -682,13 +681,13 @@ class Multiple {
|
|||
? name.replace(`_${splitName[splitName.length - 1]}`, "").trim()
|
||||
: name.trim();
|
||||
const relateSetting = document.querySelector(
|
||||
`[data-setting-container=${nameSuffixLess}_SCHEMA]`
|
||||
`[data-setting-container=${nameSuffixLess}_SCHEMA]`,
|
||||
);
|
||||
const relateCtnr = relateSetting.closest(
|
||||
"[data-services-settings-multiple]"
|
||||
"[data-services-settings-multiple]",
|
||||
);
|
||||
const relateCtnrName = relateCtnr.getAttribute(
|
||||
"data-services-settings-multiple"
|
||||
"data-services-settings-multiple",
|
||||
);
|
||||
//then we sort the setting on the right container name by suffixe number
|
||||
if (!(relateCtnrName in sortMultiples)) {
|
||||
|
|
@ -706,7 +705,7 @@ class Multiple {
|
|||
addOneMultGroup() {
|
||||
const settings = document.querySelector("[data-services-modal-form]");
|
||||
const multAddBtns = settings.querySelectorAll(
|
||||
"[data-services-multiple-add]"
|
||||
"[data-services-multiple-add]",
|
||||
);
|
||||
multAddBtns.forEach((btn) => {
|
||||
//check if already one (SCHEMA exclude so length >= 2)
|
||||
|
|
@ -721,7 +720,7 @@ class Multiple {
|
|||
|
||||
showMultByAtt(att) {
|
||||
const multContainers = document.querySelectorAll(
|
||||
`[data-services-settings-multiple^=${att}]`
|
||||
`[data-services-settings-multiple^=${att}]`,
|
||||
);
|
||||
multContainers.forEach((container) => {
|
||||
if (
|
||||
|
|
@ -735,7 +734,7 @@ class Multiple {
|
|||
|
||||
toggleMultByAtt(att) {
|
||||
const multContainers = document.querySelectorAll(
|
||||
`[data-services-settings-multiple^=${att}]`
|
||||
`[data-services-settings-multiple^=${att}]`,
|
||||
);
|
||||
multContainers.forEach((container) => {
|
||||
if (
|
||||
|
|
@ -751,7 +750,7 @@ class Multiple {
|
|||
//get schema settings
|
||||
const multiples = {};
|
||||
const schemaSettings = document.querySelectorAll(
|
||||
`[data-setting-container$="SCHEMA"]`
|
||||
`[data-setting-container$="SCHEMA"]`,
|
||||
);
|
||||
// loop on every schema settings
|
||||
schemaSettings.forEach((schema) => {
|
||||
|
|
@ -777,11 +776,11 @@ class Multiple {
|
|||
setMultipleToDOM(sortMultObj, setMethodUI = false) {
|
||||
//we loop on each multiple that contains values to render to DOM
|
||||
for (const [schemaCtnrName, multGroupBySuffix] of Object.entries(
|
||||
sortMultObj
|
||||
sortMultObj,
|
||||
)) {
|
||||
//we need to access the DOM schema container
|
||||
const schemaCtnr = document.querySelector(
|
||||
`[data-services-settings-multiple="${schemaCtnrName}"]`
|
||||
`[data-services-settings-multiple="${schemaCtnrName}"]`,
|
||||
);
|
||||
//now we have to loop on each multiple settings group
|
||||
for (const [suffix, settings] of Object.entries(multGroupBySuffix)) {
|
||||
|
|
@ -797,14 +796,14 @@ class Multiple {
|
|||
for (const [name, data] of Object.entries(settings)) {
|
||||
//get setting container of clone container
|
||||
const settingContainer = schemaCtnrClone.querySelector(
|
||||
`[data-setting-container="${name}"]`
|
||||
`[data-setting-container="${name}"]`,
|
||||
);
|
||||
//replace input info and disabled state
|
||||
this.setSetting(
|
||||
data["value"],
|
||||
setMethodUI ? "ui" : data["method"],
|
||||
data["global"],
|
||||
settingContainer
|
||||
settingContainer,
|
||||
);
|
||||
}
|
||||
//send schema clone to DOM and show it
|
||||
|
|
@ -819,7 +818,7 @@ class Multiple {
|
|||
"data-services-settings-multiple",
|
||||
schemaCtnrClone
|
||||
.getAttribute("data-services-settings-multiple")
|
||||
.replace("_SCHEMA", suffix)
|
||||
.replace("_SCHEMA", suffix),
|
||||
);
|
||||
|
||||
//rename title
|
||||
|
|
@ -833,18 +832,18 @@ class Multiple {
|
|||
|
||||
//rename setting container
|
||||
const settingCtnrs = schemaCtnrClone.querySelectorAll(
|
||||
"[data-setting-container]"
|
||||
"[data-setting-container]",
|
||||
);
|
||||
settingCtnrs.forEach((settingCtnr) => {
|
||||
settingCtnr.setAttribute(
|
||||
"data-setting-container",
|
||||
settingCtnr
|
||||
.getAttribute("data-setting-container")
|
||||
.replace("_SCHEMA", suffix)
|
||||
.replace("_SCHEMA", suffix),
|
||||
);
|
||||
settingCtnr.setAttribute(
|
||||
"id",
|
||||
settingCtnr.getAttribute("id").replace("_SCHEMA", suffix)
|
||||
settingCtnr.getAttribute("id").replace("_SCHEMA", suffix),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -922,15 +921,15 @@ class Multiple {
|
|||
//click the custom select dropdown btn value to update select value
|
||||
select.parentElement
|
||||
.querySelector(
|
||||
`button[data-setting-select-dropdown-btn][value='${defaultVal}']`
|
||||
`button[data-setting-select-dropdown-btn][value='${defaultVal}']`,
|
||||
)
|
||||
.click();
|
||||
|
||||
//set state to custom visible el
|
||||
const btnCustom = document.querySelector(
|
||||
`[data-setting-select=${select.getAttribute(
|
||||
"data-setting-select-default"
|
||||
)}]`
|
||||
"data-setting-select-default",
|
||||
)}]`,
|
||||
);
|
||||
|
||||
this.setDisabledMultServ(btnCustom, method, global);
|
||||
|
|
@ -966,10 +965,10 @@ class Multiple {
|
|||
selects.forEach((select) => {
|
||||
const method = select.getAttribute("data-default-method");
|
||||
const name = select.getAttribute(
|
||||
"data-services-setting-select-default"
|
||||
"data-services-setting-select-default",
|
||||
);
|
||||
const selDOM = document.querySelector(
|
||||
`button[data-services-setting-select='${name}']`
|
||||
`button[data-services-setting-select='${name}']`,
|
||||
);
|
||||
if (method === "ui" || method === "default") {
|
||||
selDOM.removeAttribute("disabled", "");
|
||||
|
|
@ -1004,7 +1003,7 @@ class Multiple {
|
|||
hiddenIfNoMultiples() {
|
||||
//hide multiple btn if no multiple exist on a plugin
|
||||
const multiples = document.querySelectorAll(
|
||||
`[data-${this.prefix}-settings-multiple]`
|
||||
`[data-${this.prefix}-settings-multiple]`,
|
||||
);
|
||||
multiples.forEach((container) => {
|
||||
if (container.querySelectorAll(`[data-setting-container]`).length <= 0)
|
||||
|
|
@ -1016,7 +1015,7 @@ class Multiple {
|
|||
|
||||
removePrevMultiples() {
|
||||
const multiPlugins = document.querySelectorAll(
|
||||
`[data-${this.prefix}-settings-multiple]`
|
||||
`[data-${this.prefix}-settings-multiple]`,
|
||||
);
|
||||
multiPlugins.forEach((multiGrp) => {
|
||||
if (
|
||||
|
|
@ -1054,7 +1053,7 @@ const setModal = new ServiceModal();
|
|||
const format = new FormatValue();
|
||||
const setFilterGlobal = new FilterSettings(
|
||||
"settings-filter",
|
||||
"[data-service-content='settings']"
|
||||
"[data-service-content='settings']",
|
||||
);
|
||||
|
||||
const setMultiple = new Multiple("services");
|
||||
|
|
|
|||
Loading…
Reference in a new issue