mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
update bans
*change "period" by "term" *start form post logic *add unban logic *update term and remain logic to keep only needed values
This commit is contained in:
parent
d8b1cecf49
commit
13e546023e
4 changed files with 170 additions and 85 deletions
|
|
@ -48,7 +48,7 @@ from src.Config import Config
|
|||
from src.ReverseProxied import ReverseProxied
|
||||
from src.User import AnonymousUser, User
|
||||
|
||||
from utils import check_settings, get_b64encoded_qr_image, path_to_dict, get_remain, get_period_from_remain
|
||||
from utils import check_settings, get_b64encoded_qr_image, path_to_dict, get_remain, get_term_from_remain
|
||||
from Database import Database # type: ignore
|
||||
from logging import getLogger
|
||||
|
||||
|
|
@ -1599,7 +1599,7 @@ def logs_container(container_id):
|
|||
@login_required
|
||||
def block_requests():
|
||||
# TODO : Get block requests from database to send it
|
||||
requests = [
|
||||
block_requests = [
|
||||
{"ip": "124.0.0.1", "url": "/test", "date": "12/51/9851", "reason": "antibot", "method": "GET", "status": 403, "data": "{fesfmk fesfsf sfesfes}"},
|
||||
{"ip": "124.0.0.2", "url": "/test", "date": "12/51/9851", "reason": "test", "method": "GET", "status": 403, "data": "{fesfmk fesfsf sfesfes}"},
|
||||
{"ip": "124.0.0.3", "url": "/test", "date": "12/51/9851", "reason": "antibot", "method": "GET", "status": 403, "data": "{fesfmk fesfsf sfesfes}"},
|
||||
|
|
@ -1608,7 +1608,7 @@ def block_requests():
|
|||
# Prepare data
|
||||
reasons = {}
|
||||
codes = {}
|
||||
for request in requests:
|
||||
for request in block_requests:
|
||||
# Get top reasons
|
||||
if not request["reason"] in reasons:
|
||||
reasons[request["reason"]] = 0
|
||||
|
|
@ -1623,11 +1623,7 @@ def block_requests():
|
|||
|
||||
return render_template(
|
||||
"block_requests.html",
|
||||
block_requests=[
|
||||
{"ip": "124.0.0.1", "url": "/test", "date": "12/51/9851", "reason": "antibot", "method": "GET", "status": "403", "data": "{fesfmk fesfsf sfesfes}"},
|
||||
{"ip": "124.0.0.2", "url": "/test", "date": "12/51/9851", "reason": "test", "method": "GET", "status": "403", "data": "{fesfmk fesfsf sfesfes}"},
|
||||
{"ip": "124.0.0.3", "url": "/test", "date": "12/51/9851", "reason": "antibot", "method": "GET", "status": "403", "data": "{fesfmk fesfsf sfesfes}"},
|
||||
],
|
||||
block_requests=block_requests,
|
||||
top_code=top_code,
|
||||
top_reason=top_reason,
|
||||
username=current_user.get_id(),
|
||||
|
|
@ -1635,24 +1631,64 @@ def block_requests():
|
|||
)
|
||||
|
||||
|
||||
@app.route("/bans", methods=["GET"])
|
||||
@app.route("/bans", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def bans():
|
||||
if request.method == "POST":
|
||||
# Check variables
|
||||
if not request.form:
|
||||
flash("Missing form data.", "error")
|
||||
return redirect(url_for("bans"))
|
||||
|
||||
if "operation" not in request.form:
|
||||
flash("Operation unknown", "error")
|
||||
return redirect(url_for("bans"))
|
||||
|
||||
if "data" not in request.form:
|
||||
flash("No data to proceed", "error")
|
||||
return redirect(url_for("bans"))
|
||||
|
||||
# Multiple operations : add ban or unban
|
||||
operation = request.form["operation"]
|
||||
# data = request.form["data"]
|
||||
|
||||
# TODO : unban logic
|
||||
# data format for unban is the same as bans send on client
|
||||
if operation == "unban":
|
||||
pass
|
||||
|
||||
# TODO : add ban logic
|
||||
# data format : [{"ip": string, "ban_start": timestamp, "ban_end": timestamp, "reason": string}]
|
||||
# "ban_start" is optional : default is time.time()
|
||||
# "ban_end" is optional : default is one month
|
||||
if operation == "ban":
|
||||
pass
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
"loading",
|
||||
next=url_for("bans"),
|
||||
message="Update bans",
|
||||
)
|
||||
)
|
||||
|
||||
# TODO : Get bans list from database and send it
|
||||
# Need to limit the number of bans around 100 last ones
|
||||
bans = [
|
||||
{"ip": "124.0.0.1", "ban_start": 1705663430, "ban_end": 1705675421, "reason": "antibot"},
|
||||
{"ip": "124.0.0.2", "ban_start": 1705663430, "ban_end": 1705685421, "reason": "test"},
|
||||
{"ip": "124.0.0.3", "ban_start": 1705663430, "ban_end": 1705664748, "reason": "unknown"},
|
||||
{"ip": "124.0.0.1", "ban_start": 1705663430, "ban_end": 1705758948, "reason": "antibot"},
|
||||
{"ip": "124.0.0.2", "ban_start": 1705663430, "ban_end": 1708437348, "reason": "test"},
|
||||
{"ip": "124.0.0.3", "ban_start": 1705663430, "ban_end": 1740059748, "reason": "unknown"},
|
||||
]
|
||||
|
||||
# Prepare data
|
||||
reasons = {}
|
||||
now_stamp = int(time())
|
||||
now_stamp = int(time()) # in seconds
|
||||
|
||||
for ban in bans:
|
||||
# Add remain
|
||||
ban["remain"] = "unknown" if ban["ban_end"] - now_stamp < 0 else get_remain(ban["ban_end"] - now_stamp)
|
||||
ban["period"] = get_period_from_remain(ban["remain"])
|
||||
remain = "unknown" if ban["ban_end"] - now_stamp < 0 else get_remain(ban["ban_end"] - now_stamp)
|
||||
ban["remain"] = remain
|
||||
ban["term"] = get_term_from_remain(remain)
|
||||
# Convert stamp to date
|
||||
ban["ban_start"] = datetime.fromtimestamp(ban["ban_start"])
|
||||
ban["ban_end"] = datetime.fromtimestamp(ban["ban_end"])
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
import { Checkbox } from "./utils/form";
|
||||
|
||||
class Filter {
|
||||
constructor(prefix = "bans") {
|
||||
this.prefix = prefix;
|
||||
this.container = document.querySelector(`[data-${this.prefix}-filter]`);
|
||||
this.keyInp = document.querySelector("input#keyword");
|
||||
this.periodValue = "all";
|
||||
this.termValue = "all";
|
||||
this.reasonValue = "all";
|
||||
this.initHandler();
|
||||
}
|
||||
|
||||
initHandler() {
|
||||
// REASON HANDLER
|
||||
+this.container.addEventListener("click", (e) => {
|
||||
this.container.addEventListener("click", (e) => {
|
||||
try {
|
||||
if (
|
||||
e.target
|
||||
|
|
@ -34,23 +32,21 @@ class Filter {
|
|||
}
|
||||
} catch (err) {}
|
||||
});
|
||||
// PERIOD HANDLER
|
||||
+this.container.addEventListener("click", (e) => {
|
||||
// TERM HANDLER
|
||||
this.container.addEventListener("click", (e) => {
|
||||
try {
|
||||
if (
|
||||
e.target
|
||||
.closest("button")
|
||||
.getAttribute(`data-${this.prefix}-setting-select-dropdown-btn`) ===
|
||||
"period"
|
||||
"term"
|
||||
) {
|
||||
setTimeout(() => {
|
||||
const value = document
|
||||
.querySelector(
|
||||
`[data-${this.prefix}-setting-select-text="period"]`,
|
||||
)
|
||||
.querySelector(`[data-${this.prefix}-setting-select-text="term"]`)
|
||||
.textContent.trim();
|
||||
|
||||
this.periodValue = value;
|
||||
this.termValue = value;
|
||||
//run filter
|
||||
this.filter();
|
||||
}, 10);
|
||||
|
|
@ -74,7 +70,7 @@ class Filter {
|
|||
//filter type
|
||||
this.setFilterKeyword(bans);
|
||||
this.setFilterReason(bans);
|
||||
this.setFilterPeriod(bans);
|
||||
this.setFilterTerm(bans);
|
||||
}
|
||||
|
||||
setFilterKeyword(bans) {
|
||||
|
|
@ -98,12 +94,12 @@ class Filter {
|
|||
}
|
||||
}
|
||||
|
||||
setFilterPeriod(bans) {
|
||||
if (this.periodValue === "all") return;
|
||||
setFilterTerm(bans) {
|
||||
if (this.termValue === "all") return;
|
||||
for (let i = 0; i < bans.length; i++) {
|
||||
const el = bans[i];
|
||||
const type = this.getElAttribut(el, "period");
|
||||
if (type !== this.periodValue) el.classList.add("hidden");
|
||||
const type = this.getElAttribut(el, "term");
|
||||
if (type !== this.termValue) el.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -300,6 +296,66 @@ class Dropdown {
|
|||
}
|
||||
}
|
||||
|
||||
class Unban {
|
||||
constructor(prefix = "bans") {
|
||||
this.prefix = prefix;
|
||||
this.container = document.querySelector("main");
|
||||
this.listEl = document.querySelector(`[data-${this.prefix}-list]`);
|
||||
this.unbanForm = document.querySelector("#unban-items");
|
||||
this.unbanBtn = document.querySelector(`button[data-unban-btn]`);
|
||||
this.unbanInp = document.querySelector(`input[data-unban-inp]`);
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Look if an item is select to enable unban button
|
||||
this.container.addEventListener("click", (e) => {
|
||||
try {
|
||||
if (
|
||||
e.target.closest("div").hasAttribute(`data-${this.prefix}-ban-select`)
|
||||
) {
|
||||
// timeout to wait for select value to change
|
||||
setTimeout(() => {
|
||||
// Check if at least one item is selected
|
||||
const selected = this.listEl.querySelectorAll(
|
||||
`input[data-checked="true"]`,
|
||||
);
|
||||
|
||||
// Case true, enable unban button
|
||||
if (selected.length > 0) {
|
||||
this.unbanBtn.removeAttribute("disabled");
|
||||
}
|
||||
|
||||
// Case false, disable unban button
|
||||
if (selected.length === 0) {
|
||||
this.unbanBtn.setAttribute("disabled", "");
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
} catch (err) {}
|
||||
});
|
||||
// unban button
|
||||
this.unbanForm.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
if (this.unbanBtn.hasAttribute("disabled")) return;
|
||||
// Get all selected items
|
||||
const selected = this.listEl.querySelectorAll(
|
||||
`input[data-checked="true"]`,
|
||||
);
|
||||
const getDatas = [];
|
||||
selected.forEach((el) => {
|
||||
const data = el
|
||||
.closest(`li[data-${this.prefix}-list-item]`)
|
||||
.getAttribute(`data-${this.prefix}-list-item`);
|
||||
getDatas.push(data);
|
||||
});
|
||||
this.unbanInp.value = JSON.stringify(getDatas);
|
||||
this.unbanInp.setAttribute("value", JSON.stringify(getDatas));
|
||||
this.unbanForm.submit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const setDropdown = new Dropdown();
|
||||
const setFilter = new Filter();
|
||||
new Checkbox();
|
||||
const setUnban = new Unban();
|
||||
|
|
|
|||
51
src/ui/templates/bans.html
vendored
51
src/ui/templates/bans.html
vendored
|
|
@ -2,14 +2,14 @@
|
|||
url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
||||
|
||||
{% set reasons = [] %}
|
||||
{% set periods = [] %}
|
||||
{% set terms = [] %}
|
||||
|
||||
{% for ban in bans %}
|
||||
{% if ban["reason"] not in reasons %}
|
||||
{% set reasons = reasons.append(ban["reason"]) %}
|
||||
{% endif %}
|
||||
{% if ban["period"] not in periods %}
|
||||
{% set periods = periods.append(ban["period"]) %}
|
||||
{% if ban["term"] not in terms %}
|
||||
{% set terms = terms.append(ban["term"]) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
|
@ -134,28 +134,28 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
</div>
|
||||
<!-- end select reason -->
|
||||
|
||||
<!-- select period -->
|
||||
<!-- select term -->
|
||||
<div class="flex flex-col relative col-span-12 md:col-span-6">
|
||||
<h5
|
||||
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
|
||||
>
|
||||
Period
|
||||
Term
|
||||
</h5>
|
||||
<button
|
||||
aria-controls="filter-state"
|
||||
data-{{current_endpoint}}-setting-select="period"
|
||||
data-{{current_endpoint}}-setting-select="term"
|
||||
class="disabled:opacity-75 dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400 dark:disabled:bg-gray-800 dark:disabled:border-gray-800 duration-300 ease-in-out dark:opacity-90 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 focus:border-green-500 flex justify-between align-middle items-center text-left text-sm leading-5.6 ease w-full rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-1.5 py-1 md:px-3 font-normal text-gray-700 transition-all placeholder:text-gray-500"
|
||||
>
|
||||
<span
|
||||
aria-description="current filter state value"
|
||||
id="{{current_endpoint}}-period"
|
||||
data-name="{{current_endpoint}}-period"
|
||||
data-{{current_endpoint}}-setting-select-text="period"
|
||||
id="{{current_endpoint}}-term"
|
||||
data-name="{{current_endpoint}}-term"
|
||||
data-{{current_endpoint}}-setting-select-text="term"
|
||||
>all</span
|
||||
>
|
||||
<!-- chevron -->
|
||||
<svg
|
||||
data-{{current_endpoint}}-setting-select="period"
|
||||
data-{{current_endpoint}}-setting-select="term"
|
||||
class="transition-transform h-4 w-4 fill-gray-500"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
|
|
@ -170,31 +170,31 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
<div
|
||||
id="filter-state"
|
||||
role="listbox"
|
||||
data-{{current_endpoint}}-setting-select-dropdown="period"
|
||||
data-{{current_endpoint}}-setting-select-dropdown="term"
|
||||
class="hidden z-100 absolute h-full flex-col w-full translate-y-16"
|
||||
>
|
||||
<button
|
||||
role="option"
|
||||
data-{{current_endpoint}}-setting-select-dropdown-btn="period"
|
||||
data-{{current_endpoint}}-setting-select-dropdown-btn="term"
|
||||
value="all"
|
||||
class="border-t rounded-t border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300 dark:bg-primary bg-primary text-gray-300"
|
||||
>
|
||||
all
|
||||
</button>
|
||||
{% for period in periods %}
|
||||
{% for term in terms %}
|
||||
<button
|
||||
role="option"
|
||||
data-{{current_endpoint}}-setting-select-dropdown-btn="period"
|
||||
value="{{period}}"
|
||||
data-{{current_endpoint}}-setting-select-dropdown-btn="term"
|
||||
value="{{term}}"
|
||||
class="{% if loop.last %}rounded-b{%endif%} border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 bg-white text-gray-700 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300"
|
||||
>
|
||||
{{period}}
|
||||
{{term}}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- end dropdown-->
|
||||
</div>
|
||||
<!-- end select period -->
|
||||
<!-- end select term -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- end filter -->
|
||||
|
|
@ -245,7 +245,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
<p
|
||||
class="dark:text-gray-300 h-8 text-sm font-bold col-span-1 m-0 pb-2 border-b border-gray-400"
|
||||
>
|
||||
Period
|
||||
Term
|
||||
</p>
|
||||
<!-- end header-->
|
||||
<!-- list -->
|
||||
|
|
@ -257,7 +257,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
>
|
||||
|
||||
<div
|
||||
data-{{current_endpoint}}-select
|
||||
data-{{current_endpoint}}-ban-select
|
||||
data-checkbox-handler="ban-item-{{loop.index}}" class="relative mb-7 md:mb-0 z-10 ml-2">
|
||||
<label class="sr-only" for="ban-item-{{loop.index}}">Ban ip {{ loop.index}}</label>
|
||||
<input id="ban-item-{{loop.index}}" name="ban-item-{{loop.index}}"
|
||||
|
|
@ -317,9 +317,9 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
</p>
|
||||
<p
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-1 m-0 my-1"
|
||||
data-{{current_endpoint}}-period="{{ban["period"]}}"
|
||||
data-{{current_endpoint}}-term="{{ban["term"]}}"
|
||||
>
|
||||
{{ban["period"]}}
|
||||
{{ban["term"]}}
|
||||
</p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
@ -330,10 +330,13 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full col-span-12 justify-center flex mt-6 mb-3">
|
||||
<button disabled type="submit" class="valid-btn mr-3 text-base">
|
||||
<form id="unban-items" action="/bans" method="post" class="w-full col-span-12 justify-center flex mt-6 mb-3">
|
||||
<input type="hidden" name="csrf_token" value="{{csrf_token()}}">
|
||||
<input type="hidden" name="operation" value="unban">
|
||||
<input data-unban-inp type="hidden" name="data" value="">
|
||||
<button data-unban-btn disabled type="submit" class="valid-btn mr-3 text-base">
|
||||
UNBAN SELECTED
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -10,19 +10,9 @@ from qrcode.main import QRCode
|
|||
import math
|
||||
|
||||
|
||||
def get_remain(stamp):
|
||||
# Convert to milliseconds if not
|
||||
time = str(stamp)
|
||||
length = len(time)
|
||||
|
||||
if length < 13:
|
||||
missing = 13 - length
|
||||
print(missing)
|
||||
for i in range(missing):
|
||||
time = time + "0"
|
||||
|
||||
# Get remain
|
||||
ms = int(time)
|
||||
def get_remain(remain_time):
|
||||
# Convert s to ms
|
||||
ms = int(str(remain_time) + "000")
|
||||
|
||||
seconds = math.floor(ms / 1000)
|
||||
minutes = math.floor(seconds / 60)
|
||||
|
|
@ -35,25 +25,25 @@ def get_remain(stamp):
|
|||
hours %= 24
|
||||
days %= 30
|
||||
months %= 12
|
||||
return f"{years}y {months}m {days}d {hours}h {minutes}min {seconds}s"
|
||||
return f"{f'{years}y' if years else ''} {f'{months}m' if months else ''} {f'{days}d' if days else ''} {f'{hours}h' if hours else ''} {f'{minutes}min' if minutes else ''} {f'{seconds}s' if seconds else ''}"
|
||||
|
||||
|
||||
def get_period_from_remain(remain):
|
||||
def get_term_from_remain(remain):
|
||||
# Data, need format <n>y <n>m <n>d <n>h <n>min <n>s
|
||||
periods = remain.split(" ")
|
||||
period = "unknown"
|
||||
terms = remain.split(" ")
|
||||
term = ""
|
||||
formats = ["years", "months", "days", "hours", "minutes", "seconds"]
|
||||
chars = ["y", "min", "m", "d", "h", "s"]
|
||||
|
||||
# Case not right format
|
||||
if len(periods) != 6:
|
||||
return period
|
||||
# Not handle
|
||||
if remain == "unknown":
|
||||
return remain
|
||||
|
||||
# start from seconds to years, stop when first 0 occurence
|
||||
# The remain period is first 0 occurence - 1
|
||||
for i in range(len(periods)):
|
||||
# The remain term is first 0 occurence - 1
|
||||
for i in range(len(terms)):
|
||||
# remove letter
|
||||
num = periods[len(periods) - 1 - i]
|
||||
num = terms[len(terms) - 1 - i]
|
||||
for char in chars:
|
||||
num = num.replace(char, "")
|
||||
num = "0" if not num else num
|
||||
|
|
@ -62,20 +52,20 @@ def get_period_from_remain(remain):
|
|||
|
||||
# Case seconds or less
|
||||
if not num and i == 0:
|
||||
period = formats[len(formats) - 1]
|
||||
term = formats[len(formats) - 1]
|
||||
break
|
||||
|
||||
# Case years period
|
||||
if num and i == (len(periods) - 1):
|
||||
period = formats[0]
|
||||
# Case last element
|
||||
if num and i == (len(terms) - 1):
|
||||
term = formats[len(formats) - 1 - i]
|
||||
break
|
||||
|
||||
# Case between seconds and years
|
||||
if not num:
|
||||
period = formats[len(formats) - i]
|
||||
term = formats[len(formats) - i]
|
||||
break
|
||||
|
||||
return period
|
||||
return term
|
||||
|
||||
|
||||
def path_to_dict(
|
||||
|
|
|
|||
Loading…
Reference in a new issue