mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
feat: add country tracking to ban management; update templates and scripts for country display
This commit is contained in:
parent
56432f62c4
commit
756daea931
9 changed files with 388 additions and 19 deletions
|
|
@ -62,7 +62,7 @@ repos:
|
|||
- id: codespell
|
||||
name: Codespell Spell Checker
|
||||
exclude: (^src/(ui/templates|common/core/.+/files|bw/loading)/.+.html|modsecurity-rules.conf.*|src/ui/app/static/(fonts|libs)/.+)$
|
||||
entry: codespell --ignore-regex="(tabEl|Widgits)" --skip CHANGELOG.md,CODE_OF_CONDUCT.md,src/ui/client/build.py,src/ui/app/static/json/countries.geojson,src/ui/app/static/js/pages/reports.js,src/ui/app/static/json/periscop.min.json,src/ui/app/static/json/blockhaus.min.json,src/ui/app/routes/reports.py
|
||||
entry: codespell --ignore-regex="(tabEl|Widgits)" --skip CHANGELOG.md,CODE_OF_CONDUCT.md,src/ui/client/build.py,src/ui/app/static/json/countries.geojson,src/ui/app/static/js/pages/bans.js,src/ui/app/static/json/periscop.min.json,src/ui/app/static/json/blockhaus.min.json,src/ui/app/routes/reports.py
|
||||
language: python
|
||||
types: [text]
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ local api = class("api")
|
|||
local datastore = cdatastore:new()
|
||||
local logger = clogger:new("API")
|
||||
|
||||
local get_country = utils.get_country
|
||||
local get_variable = utils.get_variable
|
||||
local is_ip_in_networks = utils.is_ip_in_networks
|
||||
-- local run = shell.run
|
||||
|
|
@ -248,6 +249,7 @@ api.global.POST["^/ban$"] = function(self)
|
|||
exp = 86400,
|
||||
reason = "manual",
|
||||
service = "unknown",
|
||||
country = "local",
|
||||
}
|
||||
ban.ip = ip["ip"]
|
||||
if ip["exp"] then
|
||||
|
|
@ -259,12 +261,19 @@ api.global.POST["^/ban$"] = function(self)
|
|||
if ip["service"] then
|
||||
ban.service = ip["service"]
|
||||
end
|
||||
local country, err = get_country(ban["ip"])
|
||||
if not country then
|
||||
country = "unknown"
|
||||
logger:log(ERR, "can't get country code " .. err)
|
||||
end
|
||||
ban.country = country
|
||||
datastore:set(
|
||||
"bans_ip_" .. ban["ip"],
|
||||
encode({
|
||||
reason = ban["reason"],
|
||||
service = ban["service"],
|
||||
date = os.time(),
|
||||
country = ban["country"],
|
||||
}),
|
||||
ban["exp"]
|
||||
)
|
||||
|
|
@ -301,6 +310,7 @@ api.global.GET["^/bans$"] = function(self)
|
|||
reason = ban_data["reason"],
|
||||
service = ban_data["service"],
|
||||
date = ban_data["date"],
|
||||
country = ban_data["country"],
|
||||
exp = math.floor(ttl),
|
||||
})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -735,12 +735,13 @@ utils.is_banned = function(ip)
|
|||
return false, "not banned"
|
||||
end
|
||||
|
||||
utils.add_ban = function(ip, reason, ttl, service)
|
||||
utils.add_ban = function(ip, reason, ttl, service, country)
|
||||
-- Set on local datastore
|
||||
local ban_data = encode({
|
||||
reason = reason,
|
||||
service = service or "unknown",
|
||||
date = os.time(),
|
||||
country = country or "local",
|
||||
})
|
||||
local ok, err = datastore:set("bans_ip_" .. ip, ban_data, ttl)
|
||||
if not ok then
|
||||
|
|
|
|||
|
|
@ -257,13 +257,16 @@ class CLI(ApiCaller):
|
|||
cli_str += "No ban found\n"
|
||||
|
||||
for ban in bans:
|
||||
banned_country = ban.get("country", "unknown")
|
||||
banned_date = ""
|
||||
remaining = "for eternity"
|
||||
if ban["date"] != -1:
|
||||
banned_date = f"the {datetime.fromtimestamp(ban['date']).strftime('%Y-%m-%d at %H:%M:%S %Z')} "
|
||||
if ban["exp"] != -1:
|
||||
remaining = f"for {format_remaining_time(ban['exp'])} remaining"
|
||||
cli_str += f"- {ban['ip']} ; banned {banned_date}{remaining} with reason \"{ban.get('reason', 'no reason given')}\""
|
||||
cli_str += (
|
||||
f"- {ban['ip']} from country \"{banned_country}\" ; banned {banned_date}{remaining} with reason \"{ban.get('reason', 'no reason given')}\""
|
||||
)
|
||||
|
||||
if ban.get("service", "unknown") != "unknown":
|
||||
cli_str += f" by {ban['service'] if ban['service'] != '_' else 'default server'}"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ local timer_at = ngx.timer.at
|
|||
local add_ban = utils.add_ban
|
||||
local is_whitelisted = utils.is_whitelisted
|
||||
local is_banned = utils.is_banned
|
||||
local get_country = utils.get_country
|
||||
local get_security_mode = utils.get_security_mode
|
||||
local tostring = tostring
|
||||
|
||||
|
|
@ -39,6 +40,16 @@ function badbehavior:log()
|
|||
end
|
||||
-- Get security mode
|
||||
local security_mode = get_security_mode(self.ctx)
|
||||
-- Get country
|
||||
local country = "local"
|
||||
local err
|
||||
if self.ctx.bw.ip_is_global then
|
||||
country, err = get_country(self.ctx.bw.remote_addr)
|
||||
if not country then
|
||||
country = "unknown"
|
||||
self.logger:log(ERR, "can't get country code " .. err)
|
||||
end
|
||||
end
|
||||
-- Call increase function later and with cosocket enabled
|
||||
local ok, err = timer_at(
|
||||
0,
|
||||
|
|
@ -49,7 +60,8 @@ function badbehavior:log()
|
|||
tonumber(self.variables["BAD_BEHAVIOR_THRESHOLD"]),
|
||||
self.use_redis,
|
||||
self.ctx.bw.server_name,
|
||||
security_mode
|
||||
security_mode,
|
||||
country
|
||||
)
|
||||
if not ok then
|
||||
return self:ret(false, "can't create increase timer : " .. err)
|
||||
|
|
@ -67,7 +79,17 @@ function badbehavior:log_stream()
|
|||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function badbehavior.increase(premature, ip, count_time, ban_time, threshold, use_redis, server_name, security_mode)
|
||||
function badbehavior.increase(
|
||||
premature,
|
||||
ip,
|
||||
count_time,
|
||||
ban_time,
|
||||
threshold,
|
||||
use_redis,
|
||||
server_name,
|
||||
security_mode,
|
||||
country
|
||||
)
|
||||
-- Instantiate objects
|
||||
local logger = require "bunkerweb.logger":new("badbehavior")
|
||||
local datastore = require "bunkerweb.datastore":new()
|
||||
|
|
@ -108,7 +130,7 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
-- Store local ban
|
||||
if counter > threshold then
|
||||
if security_mode == "block" then
|
||||
ok, err = add_ban(ip, "bad behavior", ban_time, server_name)
|
||||
ok, err = add_ban(ip, "bad behavior", ban_time, server_name, country)
|
||||
if not ok then
|
||||
logger:log(ERR, "(increase) can't save ban : " .. err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -2,8 +2,301 @@ $(document).ready(function () {
|
|||
var actionLock = false;
|
||||
let addBanNumber = 1;
|
||||
const banNumber = parseInt($("#bans_number").val());
|
||||
const dataCountries = ($("#countries").val() || "")
|
||||
.split(",")
|
||||
.filter((code) => code && code !== "local");
|
||||
const baseFlagsUrl = $("#base_flags_url").val().trim();
|
||||
const isReadOnly = $("#is-read-only").val().trim() === "True";
|
||||
|
||||
const countriesDataNames = {
|
||||
AD: "Andorra",
|
||||
AE: "United Arab Emirates",
|
||||
AF: "Afghanistan",
|
||||
AG: "Antigua and Barbuda",
|
||||
AI: "Anguilla",
|
||||
AL: "Albania",
|
||||
AM: "Armenia",
|
||||
AO: "Angola",
|
||||
AQ: "Antarctica",
|
||||
AR: "Argentina",
|
||||
AS: "American Samoa",
|
||||
AT: "Austria",
|
||||
AU: "Australia",
|
||||
AW: "Aruba",
|
||||
AX: "Åland Islands",
|
||||
AZ: "Azerbaijan",
|
||||
BA: "Bosnia and Herzegovina",
|
||||
BB: "Barbados",
|
||||
BD: "Bangladesh",
|
||||
BE: "Belgium",
|
||||
BF: "Burkina Faso",
|
||||
BG: "Bulgaria",
|
||||
BH: "Bahrain",
|
||||
BI: "Burundi",
|
||||
BJ: "Benin",
|
||||
BL: "Saint Barthélemy",
|
||||
BM: "Bermuda",
|
||||
BN: "Brunei Darussalam",
|
||||
BO: "Bolivia, Plurinational State of",
|
||||
BQ: "Caribbean Netherlands",
|
||||
BR: "Brazil",
|
||||
BS: "Bahamas",
|
||||
BT: "Bhutan",
|
||||
BV: "Bouvet Island",
|
||||
BW: "Botswana",
|
||||
BY: "Belarus",
|
||||
BZ: "Belize",
|
||||
CA: "Canada",
|
||||
CC: "Cocos (Keeling) Islands",
|
||||
CD: "Congo, the Democratic Republic of the",
|
||||
CF: "Central African Republic",
|
||||
CG: "Republic of the Congo",
|
||||
CH: "Switzerland",
|
||||
CI: "Côte d'Ivoire",
|
||||
CK: "Cook Islands",
|
||||
CL: "Chile",
|
||||
CM: "Cameroon",
|
||||
CN: "China (People's Republic of China)",
|
||||
CO: "Colombia",
|
||||
CR: "Costa Rica",
|
||||
CU: "Cuba",
|
||||
CV: "Cape Verde",
|
||||
CW: "Curaçao",
|
||||
CX: "Christmas Island",
|
||||
CY: "Cyprus",
|
||||
CZ: "Czech Republic",
|
||||
DE: "Germany",
|
||||
DJ: "Djibouti",
|
||||
DK: "Denmark",
|
||||
DM: "Dominica",
|
||||
DO: "Dominican Republic",
|
||||
DZ: "Algeria",
|
||||
EC: "Ecuador",
|
||||
EE: "Estonia",
|
||||
EG: "Egypt",
|
||||
EH: "Western Sahara",
|
||||
ER: "Eritrea",
|
||||
ES: "Spain",
|
||||
ET: "Ethiopia",
|
||||
EU: "Europe",
|
||||
FI: "Finland",
|
||||
FJ: "Fiji",
|
||||
FK: "Falkland Islands (Malvinas)",
|
||||
FM: "Micronesia, Federated States of",
|
||||
FO: "Faroe Islands",
|
||||
FR: "France",
|
||||
GA: "Gabon",
|
||||
GB: "United Kingdom",
|
||||
GD: "Grenada",
|
||||
GE: "Georgia",
|
||||
GF: "French Guiana",
|
||||
GG: "Guernsey",
|
||||
GH: "Ghana",
|
||||
GI: "Gibraltar",
|
||||
GL: "Greenland",
|
||||
GM: "Gambia",
|
||||
GN: "Guinea",
|
||||
GP: "Guadeloupe",
|
||||
GQ: "Equatorial Guinea",
|
||||
GR: "Greece",
|
||||
GS: "South Georgia and the South Sandwich Islands",
|
||||
GT: "Guatemala",
|
||||
GU: "Guam",
|
||||
GW: "Guinea-Bissau",
|
||||
GY: "Guyana",
|
||||
HK: "Hong Kong",
|
||||
HM: "Heard Island and McDonald Islands",
|
||||
HN: "Honduras",
|
||||
HR: "Croatia",
|
||||
HT: "Haiti",
|
||||
HU: "Hungary",
|
||||
ID: "Indonesia",
|
||||
IE: "Ireland",
|
||||
IL: "Israel",
|
||||
IM: "Isle of Man",
|
||||
IN: "India",
|
||||
IO: "British Indian Ocean Territory",
|
||||
IQ: "Iraq",
|
||||
IR: "Iran, Islamic Republic of",
|
||||
IS: "Iceland",
|
||||
IT: "Italy",
|
||||
JE: "Jersey",
|
||||
JM: "Jamaica",
|
||||
JO: "Jordan",
|
||||
JP: "Japan",
|
||||
KE: "Kenya",
|
||||
KG: "Kyrgyzstan",
|
||||
KH: "Cambodia",
|
||||
KI: "Kiribati",
|
||||
KM: "Comoros",
|
||||
KN: "Saint Kitts and Nevis",
|
||||
KP: "Korea, Democratic People's Republic of",
|
||||
KR: "Korea, Republic of",
|
||||
KW: "Kuwait",
|
||||
KY: "Cayman Islands",
|
||||
KZ: "Kazakhstan",
|
||||
LA: "Laos (Lao People's Democratic Republic)",
|
||||
LB: "Lebanon",
|
||||
LC: "Saint Lucia",
|
||||
LI: "Liechtenstein",
|
||||
LK: "Sri Lanka",
|
||||
LR: "Liberia",
|
||||
LS: "Lesotho",
|
||||
LT: "Lithuania",
|
||||
LU: "Luxembourg",
|
||||
LV: "Latvia",
|
||||
LY: "Libya",
|
||||
MA: "Morocco",
|
||||
MC: "Monaco",
|
||||
MD: "Moldova, Republic of",
|
||||
ME: "Montenegro",
|
||||
MF: "Saint Martin",
|
||||
MG: "Madagascar",
|
||||
MH: "Marshall Islands",
|
||||
MK: "North Macedonia",
|
||||
ML: "Mali",
|
||||
MM: "Myanmar",
|
||||
MN: "Mongolia",
|
||||
MO: "Macao",
|
||||
MP: "Northern Mariana Islands",
|
||||
MQ: "Martinique",
|
||||
MR: "Mauritania",
|
||||
MS: "Montserrat",
|
||||
MT: "Malta",
|
||||
MU: "Mauritius",
|
||||
MV: "Maldives",
|
||||
MW: "Malawi",
|
||||
MX: "Mexico",
|
||||
MY: "Malaysia",
|
||||
MZ: "Mozambique",
|
||||
NA: "Namibia",
|
||||
NC: "New Caledonia",
|
||||
NE: "Niger",
|
||||
NF: "Norfolk Island",
|
||||
NG: "Nigeria",
|
||||
NI: "Nicaragua",
|
||||
NL: "Netherlands",
|
||||
NO: "Norway",
|
||||
NP: "Nepal",
|
||||
NR: "Nauru",
|
||||
NU: "Niue",
|
||||
NZ: "New Zealand",
|
||||
OM: "Oman",
|
||||
PA: "Panama",
|
||||
PE: "Peru",
|
||||
PF: "French Polynesia",
|
||||
PG: "Papua New Guinea",
|
||||
PH: "Philippines",
|
||||
PK: "Pakistan",
|
||||
PL: "Poland",
|
||||
PM: "Saint Pierre and Miquelon",
|
||||
PN: "Pitcairn",
|
||||
PR: "Puerto Rico",
|
||||
PS: "Palestine",
|
||||
PT: "Portugal",
|
||||
PW: "Palau",
|
||||
PY: "Paraguay",
|
||||
QA: "Qatar",
|
||||
RE: "Réunion",
|
||||
RO: "Romania",
|
||||
RS: "Serbia",
|
||||
RU: "Russian Federation",
|
||||
RW: "Rwanda",
|
||||
SA: "Saudi Arabia",
|
||||
SB: "Solomon Islands",
|
||||
SC: "Seychelles",
|
||||
SD: "Sudan",
|
||||
SE: "Sweden",
|
||||
SG: "Singapore",
|
||||
SH: "Saint Helena, Ascension and Tristan da Cunha",
|
||||
SI: "Slovenia",
|
||||
SJ: "Svalbard and Jan Mayen Islands",
|
||||
SK: "Slovakia",
|
||||
SL: "Sierra Leone",
|
||||
SM: "San Marino",
|
||||
SN: "Senegal",
|
||||
SO: "Somalia",
|
||||
SR: "Suriname",
|
||||
SS: "South Sudan",
|
||||
ST: "Sao Tome and Principe",
|
||||
SV: "El Salvador",
|
||||
SX: "Sint Maarten (Dutch part)",
|
||||
SY: "Syrian Arab Republic",
|
||||
SZ: "Swaziland",
|
||||
TC: "Turks and Caicos Islands",
|
||||
TD: "Chad",
|
||||
TF: "French Southern Territories",
|
||||
TG: "Togo",
|
||||
TH: "Thailand",
|
||||
TJ: "Tajikistan",
|
||||
TK: "Tokelau",
|
||||
TL: "Timor-Leste",
|
||||
TM: "Turkmenistan",
|
||||
TN: "Tunisia",
|
||||
TO: "Tonga",
|
||||
TR: "Turkey",
|
||||
TT: "Trinidad and Tobago",
|
||||
TV: "Tuvalu",
|
||||
TW: "Taiwan (Republic of China)",
|
||||
TZ: "Tanzania, United Republic of",
|
||||
UA: "Ukraine",
|
||||
UG: "Uganda",
|
||||
UM: "US Minor Outlying Islands",
|
||||
US: "United States",
|
||||
UY: "Uruguay",
|
||||
UZ: "Uzbekistan",
|
||||
VA: "Holy See (Vatican City State)",
|
||||
VC: "Saint Vincent and the Grenadines",
|
||||
VE: "Venezuela, Bolivarian Republic of",
|
||||
VG: "Virgin Islands, British",
|
||||
VI: "Virgin Islands, U.S.",
|
||||
VN: "Vietnam",
|
||||
VU: "Vanuatu",
|
||||
WF: "Wallis and Futuna Islands",
|
||||
WS: "Samoa",
|
||||
XK: "Kosovo",
|
||||
YE: "Yemen",
|
||||
YT: "Mayotte",
|
||||
ZA: "South Africa",
|
||||
ZM: "Zambia",
|
||||
ZW: "Zimbabwe",
|
||||
};
|
||||
|
||||
// Filter countriesDataNames to include only necessary countries
|
||||
const filteredCountriesDataNames = dataCountries.reduce((obj, code) => {
|
||||
if (countriesDataNames[code]) {
|
||||
obj[code] = countriesDataNames[code];
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
// Assuming baseFlagsUrl, dataCountries, and countriesDataNames are defined
|
||||
const countriesSearchPanesOptions = [
|
||||
{
|
||||
label: `<img src="${baseFlagsUrl}/zz.svg" class="border border-1 p-0 me-1" height="17" /> - N/A`,
|
||||
value: (rowData) => rowData[4].includes("N/A"),
|
||||
},
|
||||
...Object.entries(filteredCountriesDataNames).map(([code, name]) => ({
|
||||
label: `<img src="${baseFlagsUrl}/${code.toLowerCase()}.svg" class="border border-1 p-0 me-1" height="17" /> - ${name}`,
|
||||
value: (rowData) =>
|
||||
rowData[4].includes(`data-bs-original-title="${code}"`),
|
||||
})),
|
||||
];
|
||||
|
||||
// Batch update tooltips
|
||||
const updateCountryTooltips = () => {
|
||||
$("[data-bs-original-title]").each(function () {
|
||||
const $elem = $(this);
|
||||
const countryCode = $elem.attr("data-bs-original-title");
|
||||
const countryName = countriesDataNames[countryCode];
|
||||
if (countryName) {
|
||||
$elem.attr("data-bs-original-title", countryName);
|
||||
}
|
||||
});
|
||||
// Initialize tooltips once
|
||||
$('[data-bs-toggle="tooltip"]').tooltip();
|
||||
};
|
||||
|
||||
// Utility functions
|
||||
function addDays(date, days) {
|
||||
const result = new Date(date);
|
||||
|
|
@ -142,7 +435,7 @@ $(document).ready(function () {
|
|||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [2, 5, 6],
|
||||
columns: [2, 4, 6, 7],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
|
|
@ -306,7 +599,7 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
initializeDataTable({
|
||||
const bans_table = initializeDataTable({
|
||||
tableSelector: "#bans",
|
||||
tableName: "bans",
|
||||
columnVisibilityCondition: (column) => column > 2 && column < 8,
|
||||
|
|
@ -328,7 +621,7 @@ $(document).ready(function () {
|
|||
targets: -1,
|
||||
},
|
||||
{
|
||||
targets: [2, 6],
|
||||
targets: [2, 7],
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
|
|
@ -381,6 +674,14 @@ $(document).ready(function () {
|
|||
},
|
||||
targets: 2,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: countriesSearchPanesOptions,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
|
|
@ -388,7 +689,7 @@ $(document).ready(function () {
|
|||
{
|
||||
label: "Next 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return date - now < 24 * 60 * 60 * 1000;
|
||||
},
|
||||
|
|
@ -396,7 +697,7 @@ $(document).ready(function () {
|
|||
{
|
||||
label: "Next 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return date - now < 7 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
|
|
@ -404,7 +705,7 @@ $(document).ready(function () {
|
|||
{
|
||||
label: "Next 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return date - now < 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
|
|
@ -412,7 +713,7 @@ $(document).ready(function () {
|
|||
{
|
||||
label: "More than 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return date - now >= 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
|
|
@ -421,11 +722,11 @@ $(document).ready(function () {
|
|||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 6,
|
||||
targets: 7,
|
||||
},
|
||||
{
|
||||
searchPanes: { show: true },
|
||||
targets: 5,
|
||||
targets: 6,
|
||||
},
|
||||
],
|
||||
order: [[6, "asc"]],
|
||||
|
|
@ -461,10 +762,14 @@ $(document).ready(function () {
|
|||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
updateCountryTooltips();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Update tooltips after table draw
|
||||
bans_table.on("draw.dt", updateCountryTooltips);
|
||||
|
||||
$(document).on("click", ".unban-ip", function () {
|
||||
if (isReadOnly) {
|
||||
alert("This action is not allowed in read-only mode.");
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
{% block content %}
|
||||
<!-- Content -->
|
||||
<div class="card table-responsive text-nowrap p-4 pb-8 min-vh-70">
|
||||
{% set base_flags_url = url_for('static', filename='img/flags') %}
|
||||
<input type="hidden" id="bans_number" value="{{ bans|length }}" />
|
||||
<input type="hidden" id="base_flags_url" value="{{ base_flags_url }}" />
|
||||
<textarea type="hidden"
|
||||
id="columns_preferences_defaults"
|
||||
class="visually-hidden">{{ columns_preferences_defaults['bans']|tojson }}</textarea>
|
||||
|
|
@ -29,6 +31,9 @@
|
|||
<th data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="The banned IP address">IP address</th>
|
||||
<th data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="The banned IP country">Country</th>
|
||||
<th data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="The reason why the Report was created">Reason</th>
|
||||
|
|
@ -47,12 +52,32 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% set ns = namespace(countries=[]) %}
|
||||
{% for ban in bans %}
|
||||
{% if ban.get("country", "local") and ban.get('country', 'local') not in ns.countries %}
|
||||
{% set ns.countries = ns.countries + [ban.get('country', 'local')] %}
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="ban-start-date">{{ ban["start_date"] }}</td>
|
||||
<td>{{ ban["ip"] }}</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="{% if ban.get('country', 'local') in ("local", "unknown") %}N/A{% else %}{{ ban.get('country', 'local') }}{% endif %}">
|
||||
<img src="{{ base_flags_url }}/{% if ban.get('country', 'local') in ("local", "unknown") %}zz{% else %}{{ ban.get('country', 'local') |lower }}{% endif %}.svg"
|
||||
class="border border-1 p-0 me-1"
|
||||
height="17" />
|
||||
-
|
||||
{% if ban.get('country', 'local') in ("local", "unknown") %}
|
||||
N/A
|
||||
{% else %}
|
||||
{{ ban["country"] }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ ban["reason"] }}</td>
|
||||
<td>{{ ban["service"] if ban["service"] != "_" else "default server" }}</td>
|
||||
<td class="ban-end-date">{{ ban["end_date"] }}</td>
|
||||
|
|
@ -74,6 +99,7 @@
|
|||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<input type="hidden" id="countries" value="{{ ns.countries|join(',') }}" />
|
||||
<span class="position-absolute bottom-0 start-50 translate-middle badge rounded-pill bg-secondary">
|
||||
TZ: <script nonce="{{ script_nonce }}">document.write(Intl.DateTimeFormat().resolvedOptions().timeZone);</script>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -124,9 +124,7 @@
|
|||
nonce="{{ script_nonce }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
{% if not starting %}
|
||||
<input type="hidden" id="home-path" value="{{ url_for('home') }}" />
|
||||
{% endif %}
|
||||
{% if not starting %}<input type="hidden" id="home-path" value="{{ url_for('home') }}" />{% endif %}
|
||||
<input type="hidden" id="is-read-only" value="{{ is_readonly }}" />
|
||||
<input type="hidden" id="theme" value="{{ theme }}" />
|
||||
<input type="hidden"
|
||||
|
|
@ -268,6 +266,9 @@
|
|||
<script src="{{ url_for('static', filename='js/pages/plugin_page.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
{% endif %}
|
||||
<script async defer src="{{ url_for('static', filename='js/buttons.js') }}" nonce="{{ script_nonce }}"></script>
|
||||
<script async
|
||||
defer
|
||||
src="{{ url_for('static', filename='js/buttons.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ COLUMNS_PREFERENCES_DEFAULTS = {
|
|||
"5": True,
|
||||
"6": True,
|
||||
"7": True,
|
||||
"8": True,
|
||||
},
|
||||
"configs": {
|
||||
"3": True,
|
||||
|
|
|
|||
Loading…
Reference in a new issue