create admin domain bans page

This commit is contained in:
Izalia Mae 2024-03-03 03:03:32 -05:00
parent 15e7cac1b8
commit 2a866eaaaa
7 changed files with 184 additions and 21 deletions

View file

@ -266,10 +266,10 @@ class Connection(SqlConnection):
params = {} params = {}
if reason: if reason is not None:
params['reason'] = reason params['reason'] = reason
if note: if note is not None:
params['note'] = note params['note'] = note
statement = Update('domain_bans', params) statement = Update('domain_bans', params)
@ -321,10 +321,10 @@ class Connection(SqlConnection):
params = {} params = {}
if reason: if reason is not None:
params['reason'] = reason params['reason'] = reason
if note: if note is not None:
params['note'] = note params['note'] = note
statement = Update('software_bans', params) statement = Update('software_bans', params)

View file

@ -1,6 +1,6 @@
-macro menu_item(name, path) -macro menu_item(name, path)
-if view.request.path == path -if view.request.path == path or (path != "/" and view.request.path.startswith(path))
%a.button(active="true") -> =name %a.button(href="{{path}}" active="true") -> =name
-else -else
%a.button(href="{{path}}") -> =name %a.button(href="{{path}}") -> =name
@ -69,8 +69,15 @@
const menu_open = document.getElementById("menu-open"); const menu_open = document.getElementById("menu-open");
const menu_close = document.getElementById("menu-close"); const menu_close = document.getElementById("menu-close");
menu_open.addEventListener("click", (event) => {menu.attributes.visible.nodeValue = "true"}); menu_open.addEventListener("click", (event) => {
menu_close.addEventListener("click", (event) => {menu.attributes.visible.nodeValue = "false"}); var new_value = menu.attributes.visible.nodeValue === "true" ? "false" : "true";
menu.attributes.visible.nodeValue = new_value;
});
menu_close.addEventListener("click", (event) => {
menu.attributes.visible.nodeValue = "false"
});
body.addEventListener("click", (event) => { body.addEventListener("click", (event) => {
if (event.target === menu_open) { if (event.target === menu_open) {
return; return;

View file

@ -1,5 +1,48 @@
-extends "base.haml" -extends "base.haml"
-set page="Domain Bans" -set page="Domain Bans"
-block content -block content
.section %details.section
UvU %summary << Ban Domain
%form(action="/admin/domain_bans", method="POST")
#add-domain
%label(for="domain") << Domain
%input(type="domain" id="domain" name="domain" placeholder="Domain")
%label(for="reason") << Ban Reason
%textarea(id="reason" name="reason") << {{""}}
%label(for="note") << Admin Note
%textarea(id="note" name="note") << {{""}}
%input(type="submit" value="Ban Domain")
#domains.section
%table
%thead
%tr
%td.domain << Instance
%td.date << Joined
%td.remove
%tbody
-for ban in bans
%tr
%td.domain
%details
%summary -> =ban.domain
%form(action="/admin/domain_bans" method="POST")
.items
.reason << Reason
%textarea.reason(id="reason" name="reason") << {{ban.reason or ""}}
.note << Note
%textarea.note(id="note" name="note") << {{ban.note or ""}}
%input(type="hidden" name="domain", value="{{ban.domain}}")
%input(type="submit" value="Update")
%td.date
=ban.created.strftime("%Y-%m-%d")
%td.remove
%a(href="/admin/domain_bans/delete/{{ban.domain}}" title="Unban domain") << &#10006;

View file

@ -133,9 +133,8 @@ table tbody td {
padding: 5px; padding: 5px;
} }
#menu > a[active="true"] { #menu > a[active="true"]:not(:hover) {
cursor: default; background-color: var(--primary-hover);
background-color: var(--background);
color: var(--primary); color: var(--primary);
border-color: transparent; border-color: transparent;
} }

View file

@ -0,0 +1,40 @@
form input[type="submit"] {
display: block;
margin: 0 auto;
}
textarea {
height: calc(5em);
}
table .items {
display: grid;
grid-template-columns: max-content auto;
grid-gap: var(--spacing);
margin-top: var(--spacing);
}
#domains table {
width: 100%;
}
#domains .domain {
width: 100%;
}
#domains .date {
width: max-content;
text-align: right;
}
#domains thead td {
text-align: center !important;
}
#add-domain {
display: grid;
grid-template-columns: max-content auto;
grid-gap: var(--spacing);
margin-top: var(--spacing);
margin-bottom: var(--spacing);
}

View file

@ -12,7 +12,8 @@ from ..misc import Response
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from collections.abc import Callable, Coroutine, Generator from collections.abc import Callable, Coroutine, Generator
from tinysql import Database from bsql import Database
from typing import Self
from ..application import Application from ..application import Application
from ..cache import Cache from ..cache import Cache
from ..config import Config from ..config import Config
@ -28,7 +29,7 @@ def register_route(*paths: str) -> Callable:
for path in paths: for path in paths:
VIEWS.append([path, view]) VIEWS.append([path, view])
return View return view
return wrapper return wrapper
@ -43,8 +44,14 @@ class View(AbstractView):
return self._run_handler(handler).__await__() return self._run_handler(handler).__await__()
async def _run_handler(self, handler: Coroutine) -> Response: @classmethod
return await handler(self.request, **self.request.match_info) async def run(cls: type[Self], method: str, request: Request, **kwargs: Any) -> Self:
view = cls(request)
return await view.handlers[method](request, **kwargs)
async def _run_handler(self, handler: Coroutine, **kwargs: Any) -> Response:
return await handler(self.request, **self.request.match_info, **kwargs)
@cached_property @cached_property

View file

@ -186,18 +186,85 @@ class AdminInstancesDelete(View):
@register_route('/admin/whitelist') @register_route('/admin/whitelist')
class AdminWhitelist(View): class AdminWhitelist(View):
async def get(self, request: Request) -> Response: async def get(self,
data = self.template.render('page/admin-whitelist.haml', self) request: Request,
error: str | None = None,
message: str | None = None) -> Response:
with self.database.session() as conn:
context = {
'domains': tuple(conn.execute('SELECT * FROM whitelist').all())
}
if error:
context['error'] = error
if message:
context['message'] = message
data = self.template.render('page/admin-whitelist.haml', self, **context)
return Response.new(data, ctype = 'html') return Response.new(data, ctype = 'html')
@register_route('/admin/domain_bans') @register_route('/admin/domain_bans')
class AdminDomainBans(View): class AdminDomainBans(View):
async def get(self, request: Request) -> Response: async def get(self,
data = self.template.render('page/admin-domain_bans.haml', self) request: Request,
error: str | None = None,
message: str | None = None) -> Response:
with self.database.session() as conn:
context = {
'bans': tuple(conn.execute('SELECT * FROM domain_bans ORDER BY domain ASC').all())
}
if error:
context['error'] = error
if message:
context['message'] = message
data = self.template.render('page/admin-domain_bans.haml', self, **context)
return Response.new(data, ctype = 'html') return Response.new(data, ctype = 'html')
async def post(self, request: Request) -> Response:
data = await request.post()
print(data)
if not data['domain']:
return await self.get(request, error = 'Missing domain')
with self.database.session(True) as conn:
if (ban := conn.get_domain_ban(data['domain'])):
conn.update_domain_ban(
data['domain'],
data.get('reason'),
data.get('note')
)
else:
conn.put_domain_ban(
data['domain'],
data.get('reason'),
data.get('note')
)
return await self.get(request, message = "Added/updated domain ban")
@register_route('/admin/domain_bans/delete/{domain}')
class AdminDomainBansDelete(View):
async def get(self, request: Request, domain: str) -> Response:
with self.database.session() as conn:
if not (conn.get_domain_ban(domain)):
return await AdminDomainBans.run("GET", request, message = 'Domain ban not found')
conn.del_domain_ban(domain)
return await AdminDomainBans.run("GET", request, message = 'Unbanned domain')
@register_route('/admin/software_bans') @register_route('/admin/software_bans')
class AdminSoftwareBans(View): class AdminSoftwareBans(View):
async def get(self, request: Request) -> Response: async def get(self, request: Request) -> Response: