fix: don't allow mention spam

add: ip banning, ip ban management page
This commit is contained in:
trisua 2025-04-03 11:22:56 -04:00
parent 131a38abb9
commit e52733b00c
14 changed files with 260 additions and 15 deletions

View file

@ -87,6 +87,11 @@ show_lhs=true) -%}
{{ icon "flag" }}
<span>{{ text "general:link.reports" }}</span>
</a>
<a href="/mod_panel/ip_bans">
{{ icon "ban" }}
<span>{{ text "general:link.ip_bans" }}</span>
</a>
{% endif %}
<b class="title">{{ config.name }}</b>

View file

@ -0,0 +1,70 @@
{% import "macros.html" as macros %} {% extends "root.html" %} {% block head %}
<title>IP Bans - {{ config.name }}</title>
{% endblock %} {% block body %} {{ macros::nav(selected="notifications") }}
<main class="flex flex-col gap-2">
<div class="card-nest w-full">
<div class="card small flex items-center gap-2">
{{ icon "ban" }}
<span>{{ text "general:link.ip_bans" }}</span>
</div>
<div class="card flex flex-col gap-2">
<!-- prettier-ignore -->
{% for item in items %}
<div class="card-nest">
<a
class="card small flex items-center gap-2 flush"
href="/api/v1/auth/profile/find/{{ item.moderator }}"
>
<!-- prettier-ignore -->
{{ components::avatar(username=item.moderator, selector_type="id") }}
<span>{{ item.moderator }}</span>
<span class="fade date">{{ item.created }}</span>
</a>
<div class="card secondary flex flex-col gap-2">
<code>{{ item.ip }}</code>
<span>{{ item.reason|markdown|safe }}</span>
<div class="card w-full flex flex-wrap gap-2">
<button
onclick="remove_report('{{ item.ip }}')"
class="red quaternary"
>
{{ icon "trash" }}
<span>{{ text "general:action.delete" }}</span>
</button>
</div>
</div>
</div>
{% endfor %}
<!-- prettier-ignore -->
{{ components::pagination(page=page, items=items|length) }}
</div>
</div>
</main>
<script>
async function remove_ban(ip) {
if (
!(await trigger("atto::confirm", [
"Are you sure you would like to do this?",
]))
) {
return;
}
fetch(`/api/v1/bans/${id}`, {
method: "DELETE",
})
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
}
</script>
{% endblock %}

View file

@ -241,7 +241,19 @@
"
>{{ token[1] }}</b
>
<span class="fade">{{ token[0] }}</span>
{% if is_helper %}
<span class="flex gap-2 items-center">
<span class="fade"
><a
href="/api/v1/auth/profile/find_by_ip/{{ token[0] }}"
><code>{{ token[0] }}</code></a
></span
>
</span>
{% else %}
<span class="fade"><code>{{ token[0] }}</code></span>
{% endif %}
<span class="fade date">{{ token[2] }}</span>
</div>

View file

@ -104,7 +104,7 @@
atto["hooks::long_text.init"]();
atto["hooks::alt"]();
atto["hooks::online_indicator"]();
// atto["hooks::ips"]();
atto["hooks::ips"]();
atto["hooks::check_reactions"]();
atto["hooks::tabs"]();
atto["hooks::partial_embeds"]();

View file

@ -408,6 +408,62 @@ media_theme_pref();
}
});
self.define("hooks::ips", ({ $ }) => {
for (const anchor of Array.from(document.querySelectorAll("a"))) {
try {
const href = new URL(anchor.href);
if (
href.pathname.startsWith("/api/v1/auth/profile/find_by_ip/")
) {
const ban_button = document.createElement("button");
ban_button.innerText = "Ban IP";
ban_button.className = "quaternary red small";
anchor.parentElement.parentElement.appendChild(ban_button);
ban_button.addEventListener("click", async (e) => {
e.preventDefault();
$.ban_ip(
href.pathname.replace(
"/api/v1/auth/profile/find_by_ip/",
"",
),
);
});
}
} catch {}
}
});
self.define("ban_ip", async ({ $ }, ip) => {
const reason = await $.prompt(
"Please explain your reason for banning this IP below:",
);
if (!reason) {
return;
}
fetch(`/api/v1/bans/${ip}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
ip,
reason,
}),
})
.then((res) => res.json())
.then((res) => {
trigger("app::toast", [
res.success ? "success" : "error",
res.message,
]);
});
});
self.define(
"hooks::attach_to_partial",
({ $ }, partial, full, attach, wrapper, page, run_on_load) => {