started bans page and format

This commit is contained in:
Jordan Blasenhauer 2024-06-26 12:02:26 +02:00
parent 6d14ad67c6
commit 9881fe11c6
7 changed files with 381 additions and 2 deletions

View file

@ -0,0 +1,60 @@
<script setup>
// Containers
import Grid from "@components/Widget/Grid.vue";
import GridLayout from "@components/Widget/GridLayout.vue";
import Title from "@components/Widget/Title.vue";
import Subtitle from "@components/Widget/Subtitle.vue";
import Table from "@components/Widget/Table.vue";
import ListPairs from "@components/List/Pairs.vue";
import MessageUnmatch from "@components/Message/Unmatch.vue";
/**
@name Builder/Bans.vue
@description This component is lightweight builder containing only the necessary components to create the bans page.
@example
[
{
type: "card",
gridLayoutClass: "transparent",
widgets: [{ type: "MessageUnmatch", data: { text: "bans_not_found" } }],
},
];
@param {array} builder - Array of containers and widgets
*/
const props = defineProps({
builder: {
type: Array,
required: true,
},
});
</script>
<template>
<!-- top level grid (layout) -->
<GridLayout
v-for="(container, index) in props.builder"
:key="index"
:gridLayoutClass="container.containerClass"
:type="container.type"
:title="container.title"
:link="container.link"
:columns="container.containerColumns"
:id="container.id"
>
<!-- widget grid -->
<Grid>
<!-- widget element -->
<template v-for="(widget, index) in container.widgets" :key="index">
<MessageUnmatch
v-if="widget.type === 'MessageUnmatch'"
v-bind="widget.data"
/>
<Title v-if="widget.type === 'Title'" v-bind="widget.data" />
<Subtitle v-if="widget.type === 'Subtitle'" v-bind="widget.data" />
<Table v-if="widget.type === 'Table'" v-bind="widget.data" />
<ListPairs v-if="widget.type === 'ListPairs'" v-bind="widget.data" />
</template>
</Grid>
</GridLayout>
</template>

View file

@ -242,5 +242,11 @@
"reports_table_data": "Data",
"reports_total": "Total reports",
"reports_top_status": "Top status code",
"reports_top_reason": "Top reason"
"reports_top_reason": "Top reason",
"bans_search" : "Search bans",
"bans_search_desc" : "Search within ban ip, ban start / end date",
"bans_reason": "Reason",
"bans_reason_desc": "Reason is the method that triggered the ban.",
"bans_terms": "Interval",
"bans_terms_desc": "Order of magnitude before unban."
}

View file

@ -0,0 +1,41 @@
<script setup>
import { reactive, onBeforeMount, onMounted } from "vue";
import DashboardLayout from "@components/Dashboard/Layout.vue";
import BuilderBans from "@components/Builder/Bans.vue";
import { useGlobal } from "@utils/global.js";
import { useForm } from "@utils/form.js";
/**
@name Page/Bans.vue
@description This component is the ban page.
This page displays global information about bans, and allow to delete or upload some bans.
*/
const bans = reactive({
builder: "",
});
onBeforeMount(() => {
// Get builder data
const dataAtt = "data-server-builder";
const dataEl = document.querySelector(`[${dataAtt}]`);
const data =
dataEl && !dataEl.getAttribute(dataAtt).includes(dataAtt)
? JSON.parse(dataEl.getAttribute(dataAtt))
: {};
bans.builder = data;
});
onMounted(() => {
useGlobal();
useForm();
});
const builder = [];
</script>
<template>
<DashboardLayout>
<BuilderBans v-if="builder" :builder="builder" />
</DashboardLayout>
</template>

View file

@ -0,0 +1,11 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
import { getI18n } from "@utils/lang.js";
import Bans from "./Bans.vue";
const pinia = createPinia();
createApp(Bans)
.use(pinia)
.use(getI18n(["dashboard", "action", "inp", "icons", "bans"]))
.mount("#app");

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/images/favicon.ico" />
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | Bans</title>
</head>
<body>
<div class="hidden" data-server-global='{"username" : "admin"}'></div>
<div class="hidden"
data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'>
</div>
<div class="hidden"
data-server-builder='[{"type":"card","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"bunkerweb"},{"key":"instances_type","value":"manual"},{"key":"instances_status","value":"instances_active"}],"status":"success","title":"bunkerweb","buttons":[{"attrs":{"data-form-INSTANCE_ID":"bunkerweb","data-form-operation":"reload","data-submit-form":"true"},"text":"action_reload","color":"warning","size":"normal"},{"attrs":{"data-form-INSTANCE_ID":"bunkerweb","data-form-operation":"stop","data-submit-form":"true"},"text":"action_stop","color":"error","size":"normal"}]}}]}]'>
</div>
<div id="app"></div>
<script type="module" src="bans.js"></script>
</body>
</html>

View file

@ -1,7 +1,7 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
import { getI18n } from "@utils/lang.js";
import Reports from "./reports.vue";
import Reports from "./Reports.vue";
const pinia = createPinia();

239
vuejs/tests/bans.py Normal file
View file

@ -0,0 +1,239 @@
import json
no_bans = []
bans = [
{
"reason": "ui",
"date": 1719393920,
"ip": "127.0.0.1",
"remain": "23 hours and 49 minutes",
"term": "hour(s)",
"ban_start": "26/06/2024 09:25:20",
"ban_end": "27/06/2024 09:15:15",
},
{
"reason": "ui",
"date": 1719393920,
"ip": "127.0.0.1",
"remain": "23 hours and 49 minutes",
"term": "hour(s)",
"ban_start": "26/06/2024 09:25:20",
"ban_end": "27/06/2024 09:15:15",
},
]
# Reoder bans dict
for ban in bans:
ban["ip"] = ban.pop("ip")
ban["reason"] = ban.pop("reason")
ban["ban_start"] = ban.pop("ban_start")
ban["ban_end"] = ban.pop("ban_end")
ban["remain"] = ban.pop("remain")
ban["term"] = ban.pop("term")
def get_bans_filter(bans):
if len(bans) <= 5:
return []
total_reasons = ["all"]
total_terms = ["all"]
for ban in bans:
if ban.get("reason") and ban.get("reason") not in total_reasons:
total_reasons.append(ban.get("reason"))
if ban.get("term") and ban.get("term") not in total_terms:
total_terms.append(ban.get("term"))
filters = []
filters.append(
{
"filter": "table",
"filterName": "keyword",
"type": "keyword",
"value": "",
"keys": ["ip", "ban_start", "ban_end"],
"field": {
"id": "bans-keyword",
"value": "",
"type": "text",
"name": "bans-keyword",
"label": "bans_search",
"placeholder": "inp_keyword",
"isClipboard": False,
"popovers": [
{
"text": "bans_search_desc",
"iconName": "info",
},
],
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
},
},
)
if len(total_reasons) > 1:
filters.append(
{
"filter": "table",
"filterName": "reason",
"type": "select",
"value": "all",
"keys": ["reason"],
"field": {
"id": "bans-reason",
"value": "all",
"values": total_reasons,
"name": "bans-reason",
"onlyDown": True,
"label": "bans_reason",
"popovers": [
{
"text": "bans_reason_desc",
"iconName": "info",
},
],
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
},
},
)
if len(total_terms) > 1:
filters.append(
{
"filter": "table",
"filterName": "term",
"type": "select",
"value": "all",
"keys": ["term"],
"field": {
"id": "bans-terms",
"value": "all",
"values": total_terms,
"name": "bans-terms",
"onlyDown": True,
"label": "bans_terms",
"popovers": [
{
"text": "bans_terms_desc",
"iconName": "info",
},
],
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
},
},
)
return filters
def get_reports_list(reports):
data = []
# loop on each dict
for report in reports:
item = []
for k, v in report.items():
item.append(
{
k: json.dumps(v) if isinstance(v, dict) else str(v),
"type": "Text",
"data": {
"text": json.dumps(v) if isinstance(v, dict) else str(v),
},
}
)
data.append(item)
return data
def get_reports_details(details):
return {
"type": "card",
"containerColumns": {"pc": 4, "tablet": 6, "mobile": 12},
"widgets": [
{
"type": "Title",
"data": {"title": "dashboard_details"},
},
{
"type": "ListPairs",
"data": {
"pairs": [
{"key": "reports_total", "value": details.get("total_reports")},
{"key": "reports_top_status", "value": details.get("top_code")},
{
"key": "reports_top_reason",
"value": details.get("top_reason"),
},
],
},
},
],
}
def reports_builder(reports, details=None):
if not reports:
return [
{
"type": "void",
"widgets": [
{"type": "MessageUnmatch", "data": {"text": "reports_not_found"}}
],
},
]
details = get_reports_details(details)
filters = get_bans_filter(reports)
reports_list = get_reports_list(reports)
reports_table = {
"type": "card",
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
"widgets": [
{
"type": "Title",
"data": {"title": "reports_title"},
},
{
"type": "Table",
"data": {
"title": "reports_table_title",
"minWidth": "xl",
"header": [
"reports_table_date",
"reports_table_ip",
"reports_table_country",
"reports_table_method",
"reports_table_url",
"reports_table_status_code",
"reports_table_cache_user_agent",
"reports_table_reason",
"reports_table_data",
],
"positions": [1, 1, 1, 1, 2, 1, 2, 1, 2],
"items": reports_list,
"filters": filters,
},
},
],
}
builder = [details, reports_table]
return builder
# output = reports_builder(reports)
output = reports_builder(no_bans)
# store on a file
with open("reports.json", "w") as f:
json.dump(output, f, indent=4)