Compare commits

..

3 commits

Author SHA1 Message Date
Izalia Mae 2a866eaaaa create admin domain bans page 2024-03-03 03:03:32 -05:00
Izalia Mae 15e7cac1b8 update barkshark-sql to fix an issue 2024-03-03 01:41:54 -05:00
Izalia Mae e5d8c9dcb0 create admin config page 2024-03-03 00:20:38 -05:00
14 changed files with 283 additions and 33 deletions

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import bsql import bsql
import typing import typing
from .config import get_default_value from .config import CONFIG_DEFAULTS, THEMES, get_default_value
from .connection import RELAY_SOFTWARE, Connection from .connection import RELAY_SOFTWARE, Connection
from .schema import TABLES, VERSIONS, migrate_0 from .schema import TABLES, VERSIONS, migrate_0

View file

@ -61,10 +61,10 @@ THEMES = {
CONFIG_DEFAULTS: dict[str, tuple[str, Any]] = { CONFIG_DEFAULTS: dict[str, tuple[str, Any]] = {
'schema-version': ('int', 20240206), 'schema-version': ('int', 20240206),
'private-key': ('str', None),
'log-level': ('loglevel', logging.LogLevel.INFO), 'log-level': ('loglevel', logging.LogLevel.INFO),
'name': ('str', 'ActivityRelay'), 'name': ('str', 'ActivityRelay'),
'note': ('str', 'Make a note about your instance here.'), 'note': ('str', 'Make a note about your instance here.'),
'private-key': ('str', None),
'theme': ('str', 'default'), 'theme': ('str', 'default'),
'whitelist-enabled': ('bool', False) 'whitelist-enabled': ('bool', False)
} }

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,37 @@
-extends "base.haml" -extends "base.haml"
-set page="Config" -set page="Config"
-block content -block content
.section %form.section(action="/admin/config" method="POST")
UvU #config-options
%label(for="name") << Name
%input(id = "name" name="name" placeholder="Relay Name" value="{{config.name or ''}}")
%label(for="description") << Description
%textarea(id="description" name="note" value="{{config.note}}") << {{config.note}}
%label(for="theme") << Color Theme
%select(id="theme" name="theme")
-for theme in themes
-if theme == config.theme
%option(value="{{theme}}" selected) -> =theme.title()
-else
%option(value="{{theme}}") -> =theme.title()
%label(for="log-level") << Log Level
%select(id="log-level" name="log-level")
-for level in LogLevel
-if level == config["log-level"]
%option(value="{{level.name}}" selected) -> =level.name.title()
-else
%option(value="{{level.name}}") -> =level.name.title()
%label(for="whitelist-enabled") << Whitelist
-if config["whitelist-enabled"]
%input(id="whitelist-enabled" name="whitelist-enabled" type="checkbox" checked)
-else
%input(id="whitelist-enabled" name="whitelist-enabled" type="checkbox")
%input(type="submit" value="Save")

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

@ -6,13 +6,13 @@
%form(target="/admin/instances", method="POST") %form(target="/admin/instances", method="POST")
#add-instance #add-instance
%label(for="domain") << Domain %label(for="domain") << Domain
%input(type="domain", name="domain", placeholder="Domain") %input(type="domain", id="domain" name="domain", placeholder="Domain")
%label(for="actor") << Actor URL %label(for="actor") << Actor URL
%input(type="url", name="actor", placeholder="Actor URL") %input(type="url", id="actor" name="actor", placeholder="Actor URL")
%label(for="inbox") << Inbox URL %label(for="inbox") << Inbox URL
%input(type="url", name="inbox", placeholder="Inbox URL") %input(type="url", id="inbox" name="inbox", placeholder="Inbox URL")
%label(for="software") << Software %label(for="software") << Software
%input(name="software", placeholder="software") %input(name="software", id="software" placeholder="software")
%input(type="submit" value="Add Instance") %input(type="submit" value="Add Instance")

View file

@ -2,7 +2,9 @@
-set page = "Home" -set page = "Home"
-block content -block content
.section .section
=config.note -for line in config.note.splitlines()
-if line
%p -> =line
.section .section
%p %p

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,20 @@
#config-options {
display: grid;
grid-template-columns: max-content auto;
grid-gap: var(--spacing);
margin-bottom: var(--spacing);
align-items: center;
}
form input[type="submit"] {
display: block;
margin: 0 auto;
}
form input[type="checkbox"] {
justify-self: left;
}
textarea {
height: 4em;
}

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

@ -7,6 +7,8 @@ from argon2.exceptions import VerifyMismatchError
from .base import View, register_route from .base import View, register_route
from ..database import CONFIG_DEFAULTS, THEMES
from ..logger import LogLevel
from ..misc import ACTOR_FORMATS, Message, Response from ..misc import ACTOR_FORMATS, Message, Response
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
@ -18,6 +20,11 @@ UNAUTH_ROUTES = {
'/login' '/login'
} }
CONFIG_IGNORE = (
'schema-version',
'private-key'
)
@web.middleware @web.middleware
async def handle_frontend_path(request: web.Request, handler: Coroutine) -> Response: async def handle_frontend_path(request: web.Request, handler: Coroutine) -> Response:
@ -179,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:
@ -200,11 +274,37 @@ class AdminSoftwareBans(View):
@register_route('/admin/config') @register_route('/admin/config')
class AdminConfig(View): class AdminConfig(View):
async def get(self, request: Request) -> Response: async def get(self, request: Request, message: str | None = None) -> Response:
data = self.template.render('page/admin-config.haml', self) context = {
'themes': tuple(THEMES.keys()),
'LogLevel': LogLevel,
'message': message
}
data = self.template.render('page/admin-config.haml', self, **context)
return Response.new(data, ctype = 'html') return Response.new(data, ctype = 'html')
async def post(self, request: Request) -> Response:
form = dict(await request.post())
with self.database.session(True) as conn:
for key in CONFIG_DEFAULTS:
value = form.get(key)
if key == 'whitelist-enabled':
value = bool(value)
elif key.lower() in CONFIG_IGNORE:
continue
if value is None:
continue
conn.put_config(key, value)
return await self.get(request, message = 'Updated config')
@register_route('/style.css') @register_route('/style.css')
class StyleCss(View): class StyleCss(View):
async def get(self, request: Request) -> Response: async def get(self, request: Request) -> Response:

View file

@ -2,7 +2,7 @@ aiohttp>=3.9.1
aiohttp-swagger[performance]==1.0.16 aiohttp-swagger[performance]==1.0.16
aputils@https://git.barkshark.xyz/barkshark/aputils/archive/0.1.7.tar.gz aputils@https://git.barkshark.xyz/barkshark/aputils/archive/0.1.7.tar.gz
argon2-cffi==23.1.0 argon2-cffi==23.1.0
barkshark-sql@https://git.barkshark.xyz/barkshark/bsql/archive/0.1.1.tar.gz barkshark-sql@https://git.barkshark.xyz/barkshark/bsql/archive/499649a736fd22eb3752ce38fd7304a9b8432ab9.tar.gz
click>=8.1.2 click>=8.1.2
hamlish-jinja@https://git.barkshark.xyz/barkshark/hamlish-jinja/archive/0.3.5.tar.gz hamlish-jinja@https://git.barkshark.xyz/barkshark/hamlish-jinja/archive/0.3.5.tar.gz
hiredis==2.3.2 hiredis==2.3.2