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 typing
from .config import get_default_value
from .config import CONFIG_DEFAULTS, THEMES, get_default_value
from .connection import RELAY_SOFTWARE, Connection
from .schema import TABLES, VERSIONS, migrate_0

View file

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

View file

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

View file

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

View file

@ -1,5 +1,37 @@
-extends "base.haml"
-set page="Config"
-block content
.section
UvU
%form.section(action="/admin/config" method="POST")
#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"
-set page="Domain Bans"
-block content
.section
UvU
%details.section
%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")
#add-instance
%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
%input(type="url", name="actor", placeholder="Actor URL")
%input(type="url", id="actor" name="actor", placeholder="Actor 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
%input(name="software", placeholder="software")
%input(name="software", id="software" placeholder="software")
%input(type="submit" value="Add Instance")

View file

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

View file

@ -133,9 +133,8 @@ table tbody td {
padding: 5px;
}
#menu > a[active="true"] {
cursor: default;
background-color: var(--background);
#menu > a[active="true"]:not(:hover) {
background-color: var(--primary-hover);
color: var(--primary);
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:
from collections.abc import Callable, Coroutine, Generator
from tinysql import Database
from bsql import Database
from typing import Self
from ..application import Application
from ..cache import Cache
from ..config import Config
@ -28,7 +29,7 @@ def register_route(*paths: str) -> Callable:
for path in paths:
VIEWS.append([path, view])
return View
return view
return wrapper
@ -43,8 +44,14 @@ class View(AbstractView):
return self._run_handler(handler).__await__()
async def _run_handler(self, handler: Coroutine) -> Response:
return await handler(self.request, **self.request.match_info)
@classmethod
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

View file

@ -7,6 +7,8 @@ from argon2.exceptions import VerifyMismatchError
from .base import View, register_route
from ..database import CONFIG_DEFAULTS, THEMES
from ..logger import LogLevel
from ..misc import ACTOR_FORMATS, Message, Response
if typing.TYPE_CHECKING:
@ -18,6 +20,11 @@ UNAUTH_ROUTES = {
'/login'
}
CONFIG_IGNORE = (
'schema-version',
'private-key'
)
@web.middleware
async def handle_frontend_path(request: web.Request, handler: Coroutine) -> Response:
@ -179,18 +186,85 @@ class AdminInstancesDelete(View):
@register_route('/admin/whitelist')
class AdminWhitelist(View):
async def get(self, request: Request) -> Response:
data = self.template.render('page/admin-whitelist.haml', self)
async def get(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')
@register_route('/admin/domain_bans')
class AdminDomainBans(View):
async def get(self, request: Request) -> Response:
data = self.template.render('page/admin-domain_bans.haml', self)
async def get(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')
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')
class AdminSoftwareBans(View):
async def get(self, request: Request) -> Response:
@ -200,11 +274,37 @@ class AdminSoftwareBans(View):
@register_route('/admin/config')
class AdminConfig(View):
async def get(self, request: Request) -> Response:
data = self.template.render('page/admin-config.haml', self)
async def get(self, request: Request, message: str | None = None) -> Response:
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')
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')
class StyleCss(View):
async def get(self, request: Request) -> Response:

View file

@ -2,7 +2,7 @@ aiohttp>=3.9.1
aiohttp-swagger[performance]==1.0.16
aputils@https://git.barkshark.xyz/barkshark/aputils/archive/0.1.7.tar.gz
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
hamlish-jinja@https://git.barkshark.xyz/barkshark/hamlish-jinja/archive/0.3.5.tar.gz
hiredis==2.3.2