diff --git a/relay/frontend/page/admin-instances.haml b/relay/frontend/page/admin-instances.haml index aaab1c9..fcb0630 100644 --- a/relay/frontend/page/admin-instances.haml +++ b/relay/frontend/page/admin-instances.haml @@ -1,29 +1,32 @@ -extends "base.haml" -set page="Instances" + +-block head + %script(type="application/javascript" src="/static/instance.js" nonce="{{view.request['hash']}}") + -block content %details.section %summary << Add Instance - %form(action="/admin/instances" method="POST") - #add-item - %label(for="domain") << Domain - %input(type="domain" id="domain" name="domain" placeholder="Domain") + #add-item + %label(for="new-actor") << Actor + %input(type="url" id="new-actor" placeholder="Actor URL") - %label(for="actor") << Actor URL - %input(type="url" id="actor" name="actor" placeholder="Actor URL") + %label(for="new-inbox") << Inbox + %input(type="url" id="new-inbox" placeholder="Inbox URL") - %label(for="inbox") << Inbox URL - %input(type="url" id="inbox" name="inbox" placeholder="Inbox URL") + %label(for="new-followid") << Follow ID + %input(type="url" id="new-followid" placeholder="Follow ID URL") - %label(for="software") << Software - %input(name="software" id="software" placeholder="software") + %label(for="new-software") << Software + %input(id="new-software" placeholder="software") - %input(type="submit" value="Add Instance") + %input(type="button" value="Add Instance", onclick="add_instance()") -if requests - %fieldset.section + %fieldset.section.requests %legend << Follow Requests .data-table - %table + %table#requests %thead %tr %td.instance << Instance @@ -34,7 +37,7 @@ %tbody -for request in requests - %tr + %tr(id="{{request.domain}}") %td.instance %a(href="https://{{request.domain}}" target="_new") -> =request.domain @@ -45,16 +48,16 @@ =request.created.strftime("%Y-%m-%d") %td.approve - %a(href="/admin/instances/approve/{{request.domain}}" title="Approve Request") << ✓ + %a(href="#" onclick="req_response('{{request.domain}}', true)" title="Approve Request") << ✓ %td.deny - %a(href="/admin/instances/deny/{{request.domain}}" title="Deny Request") << ✖ + %a(href="#" onclick="req_response('{{request.domain}}', false)" title="Deny Request") << ✖ - %fieldset.section + %fieldset.section.instances %legend << Instances .data-table - %table + %table#instances %thead %tr %td.instance << Instance @@ -64,7 +67,7 @@ %tbody -for instance in instances - %tr + %tr(id="{{instance.domain}}") %td.instance %a(href="https://{{instance.domain}}/" target="_new") -> =instance.domain @@ -75,4 +78,4 @@ =instance.created.strftime("%Y-%m-%d") %td.remove - %a(href="/admin/instances/delete/{{instance.domain}}" title="Remove Instance") << ✖ + %a(href="#" onclick="del_instance('{{instance.domain}}')" title="Remove Instance") << ✖ diff --git a/relay/frontend/static/api.js b/relay/frontend/static/api.js index 43dd433..cbdb1c9 100644 --- a/relay/frontend/static/api.js +++ b/relay/frontend/static/api.js @@ -62,8 +62,17 @@ class Client { throw new Error(message.error); } - if (Object.hasOwn(message, "created")) { - message.created = new Date(message.created); + if (Array.isArray(message)) { + message.forEach((msg) => { + if (Object.hasOwn(msg, "created")) { + msg.created = new Date(msg.created); + } + }); + + } else { + if (Object.hasOwn(message, "created")) { + message.created = new Date(message.created); + } } return message; diff --git a/relay/frontend/static/domain_ban.js b/relay/frontend/static/domain_ban.js index f01f675..ae92420 100644 --- a/relay/frontend/static/domain_ban.js +++ b/relay/frontend/static/domain_ban.js @@ -15,8 +15,6 @@ function create_ban_object(domain, reason, note) { async function ban() { var table = document.querySelector("table"); - var row = table.insertRow(-1); - var elems = { domain: document.getElementById("new-domain"), reason: document.getElementById("new-reason"), @@ -42,7 +40,9 @@ async function ban() { return } + var row = table.insertRow(-1); row.id = ban.domain; + var new_domain = row.insertCell(0); var new_date = row.insertCell(1); var new_remove = row.insertCell(2); diff --git a/relay/frontend/static/instance.js b/relay/frontend/static/instance.js new file mode 100644 index 0000000..e5f422c --- /dev/null +++ b/relay/frontend/static/instance.js @@ -0,0 +1,105 @@ +function append_table_row(table, instance) { + var row = table.insertRow(-1); + row.id = instance.domain; + + var domain = row.insertCell(0); + domain.className = "domain"; + domain.innerHTML = `${instance.domain}`; + + var software = row.insertCell(1); + software.className = "software"; + software.innerHTML = instance.software + + var date = row.insertCell(2); + date.className = "date"; + date.innerHTML = get_date_string(instance.created); + + var remove = row.insertCell(3); + remove.className = "remove"; + remove.innerHTML = ``; +} + + +async function add_instance() { + var elems = { + actor: document.getElementById("new-actor"), + inbox: document.getElementById("new-inbox"), + followid: document.getElementById("new-followid"), + software: document.getElementById("new-software") + } + + var values = { + actor: elems.actor.value.trim(), + inbox: elems.inbox.value.trim(), + followid: elems.followid.value.trim(), + software: elems.software.value.trim() + } + + if (values.actor === "") { + alert("Domain, actor, and inbox are required"); + return; + } + + try { + var instance = await client.request("POST", "v1/instance", values); + + } catch (err) { + alert(err); + return + } + + append_table_row(document.getElementById("instances"), instance); + + elems.actor.value = null; + elems.inbox.value = null; + elems.followid.value = null; + elems.software.value = null; + + document.querySelector("details.section").open = false; +} + + +async function del_instance(domain) { + try { + await client.request("DELETE", "v1/instance", {"domain": domain}); + + } catch (error) { + alert(error); + return; + } + + document.getElementById(domain).remove(); +} + + +async function req_response(domain, accept) { + params = { + "domain": domain, + "accept": accept + } + + try { + await client.request("POST", "v1/request", params); + + } catch (error) { + alert(error); + return; + } + + document.getElementById(domain).remove(); + + if (document.getElementById("requests").rows.length < 2) { + document.querySelector("fieldset.requests").remove() + } + + if (!accept) { + return; + } + + instances = await client.request("GET", `v1/instance`, null); + instances.forEach((instance) => { + if (instance.domain === domain) { + append_table_row(document.getElementById("instances"), instance); + } + }); +} diff --git a/relay/views/api.py b/relay/views/api.py index db7ac7a..ca0470c 100644 --- a/relay/views/api.py +++ b/relay/views/api.py @@ -10,7 +10,7 @@ from .base import View, register_route from .. import __version__ from ..database import ConfigData -from ..misc import Message, Response, get_app +from ..misc import Message, Response, boolean, get_app if typing.TYPE_CHECKING: from aiohttp.web import Request @@ -161,7 +161,7 @@ class Config(View): class Inbox(View): async def get(self, request: Request) -> Response: with self.database.session() as conn: - data = tuple(conn.execute('SELECT * FROM inboxes').all()) + data = conn.get_inboxes() return Response.new(data, ctype = 'json') @@ -186,6 +186,12 @@ class Inbox(View): data['inbox'] = actor_data.shared_inbox + if not data.get('software'): + nodeinfo = await self.client.fetch_nodeinfo(data['domain']) + + if nodeinfo is not None: + data['software'] = nodeinfo.sw_name + row = conn.put_inbox(**data) return Response.new(row, ctype = 'json') @@ -206,7 +212,7 @@ class Inbox(View): return Response.new(instance, ctype = 'json') - async def delete(self, request: Request, domain: str) -> Response: + async def delete(self, request: Request) -> Response: with self.database.session() as conn: data = await self.get_api_data(['domain'], []) @@ -232,10 +238,7 @@ class RequestView(View): async def post(self, request: Request) -> Response: data = await self.get_api_data(['domain', 'accept'], []) - - if not isinstance(data['accept'], bool): - atype = type(data['accept']).__name__ - return Response.new_error(400, f'Invalid type for "accept": {atype}', 'json') + data['accept'] = boolean(data['accept']) try: with self.database.session(True) as conn: