use api for instances admin page

This commit is contained in:
Izalia Mae 2024-03-14 23:48:22 -04:00
parent aeb84d7a72
commit f775335e80
5 changed files with 151 additions and 31 deletions

View file

@ -1,29 +1,32 @@
-extends "base.haml" -extends "base.haml"
-set page="Instances" -set page="Instances"
-block head
%script(type="application/javascript" src="/static/instance.js" nonce="{{view.request['hash']}}")
-block content -block content
%details.section %details.section
%summary << Add Instance %summary << Add Instance
%form(action="/admin/instances" method="POST")
#add-item #add-item
%label(for="domain") << Domain %label(for="new-actor") << Actor
%input(type="domain" id="domain" name="domain" placeholder="Domain") %input(type="url" id="new-actor" placeholder="Actor URL")
%label(for="actor") << Actor URL %label(for="new-inbox") << Inbox
%input(type="url" id="actor" name="actor" placeholder="Actor URL") %input(type="url" id="new-inbox" placeholder="Inbox URL")
%label(for="inbox") << Inbox URL %label(for="new-followid") << Follow ID
%input(type="url" id="inbox" name="inbox" placeholder="Inbox URL") %input(type="url" id="new-followid" placeholder="Follow ID URL")
%label(for="software") << Software %label(for="new-software") << Software
%input(name="software" id="software" placeholder="software") %input(id="new-software" placeholder="software")
%input(type="submit" value="Add Instance") %input(type="button" value="Add Instance", onclick="add_instance()")
-if requests -if requests
%fieldset.section %fieldset.section.requests
%legend << Follow Requests %legend << Follow Requests
.data-table .data-table
%table %table#requests
%thead %thead
%tr %tr
%td.instance << Instance %td.instance << Instance
@ -34,7 +37,7 @@
%tbody %tbody
-for request in requests -for request in requests
%tr %tr(id="{{request.domain}}")
%td.instance %td.instance
%a(href="https://{{request.domain}}" target="_new") -> =request.domain %a(href="https://{{request.domain}}" target="_new") -> =request.domain
@ -45,16 +48,16 @@
=request.created.strftime("%Y-%m-%d") =request.created.strftime("%Y-%m-%d")
%td.approve %td.approve
%a(href="/admin/instances/approve/{{request.domain}}" title="Approve Request") << &check; %a(href="#" onclick="req_response('{{request.domain}}', true)" title="Approve Request") << &check;
%td.deny %td.deny
%a(href="/admin/instances/deny/{{request.domain}}" title="Deny Request") << &#10006; %a(href="#" onclick="req_response('{{request.domain}}', false)" title="Deny Request") << &#10006;
%fieldset.section %fieldset.section.instances
%legend << Instances %legend << Instances
.data-table .data-table
%table %table#instances
%thead %thead
%tr %tr
%td.instance << Instance %td.instance << Instance
@ -64,7 +67,7 @@
%tbody %tbody
-for instance in instances -for instance in instances
%tr %tr(id="{{instance.domain}}")
%td.instance %td.instance
%a(href="https://{{instance.domain}}/" target="_new") -> =instance.domain %a(href="https://{{instance.domain}}/" target="_new") -> =instance.domain
@ -75,4 +78,4 @@
=instance.created.strftime("%Y-%m-%d") =instance.created.strftime("%Y-%m-%d")
%td.remove %td.remove
%a(href="/admin/instances/delete/{{instance.domain}}" title="Remove Instance") << &#10006; %a(href="#" onclick="del_instance('{{instance.domain}}')" title="Remove Instance") << &#10006;

View file

@ -62,9 +62,18 @@ class Client {
throw new Error(message.error); throw new Error(message.error);
} }
if (Array.isArray(message)) {
message.forEach((msg) => {
if (Object.hasOwn(msg, "created")) {
msg.created = new Date(msg.created);
}
});
} else {
if (Object.hasOwn(message, "created")) { if (Object.hasOwn(message, "created")) {
message.created = new Date(message.created); message.created = new Date(message.created);
} }
}
return message; return message;
} }

View file

@ -15,8 +15,6 @@ function create_ban_object(domain, reason, note) {
async function ban() { async function ban() {
var table = document.querySelector("table"); var table = document.querySelector("table");
var row = table.insertRow(-1);
var elems = { var elems = {
domain: document.getElementById("new-domain"), domain: document.getElementById("new-domain"),
reason: document.getElementById("new-reason"), reason: document.getElementById("new-reason"),
@ -42,7 +40,9 @@ async function ban() {
return return
} }
var row = table.insertRow(-1);
row.id = ban.domain; row.id = ban.domain;
var new_domain = row.insertCell(0); var new_domain = row.insertCell(0);
var new_date = row.insertCell(1); var new_date = row.insertCell(1);
var new_remove = row.insertCell(2); var new_remove = row.insertCell(2);

View file

@ -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 = `<a href="https://${instance.domain}/" target="_new">${instance.domain}</a>`;
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 = `<a href="#" onclick="del_instance('${instance.domain}')" title="Remove Instance">&#10006;</a>`;
}
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);
}
});
}

View file

@ -10,7 +10,7 @@ from .base import View, register_route
from .. import __version__ from .. import __version__
from ..database import ConfigData from ..database import ConfigData
from ..misc import Message, Response, get_app from ..misc import Message, Response, boolean, get_app
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from aiohttp.web import Request from aiohttp.web import Request
@ -161,7 +161,7 @@ class Config(View):
class Inbox(View): class Inbox(View):
async def get(self, request: Request) -> Response: async def get(self, request: Request) -> Response:
with self.database.session() as conn: with self.database.session() as conn:
data = tuple(conn.execute('SELECT * FROM inboxes').all()) data = conn.get_inboxes()
return Response.new(data, ctype = 'json') return Response.new(data, ctype = 'json')
@ -186,6 +186,12 @@ class Inbox(View):
data['inbox'] = actor_data.shared_inbox 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) row = conn.put_inbox(**data)
return Response.new(row, ctype = 'json') return Response.new(row, ctype = 'json')
@ -206,7 +212,7 @@ class Inbox(View):
return Response.new(instance, ctype = 'json') 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: with self.database.session() as conn:
data = await self.get_api_data(['domain'], []) data = await self.get_api_data(['domain'], [])
@ -232,10 +238,7 @@ class RequestView(View):
async def post(self, request: Request) -> Response: async def post(self, request: Request) -> Response:
data = await self.get_api_data(['domain', 'accept'], []) data = await self.get_api_data(['domain', 'accept'], [])
data['accept'] = boolean(data['accept'])
if not isinstance(data['accept'], bool):
atype = type(data['accept']).__name__
return Response.new_error(400, f'Invalid type for "accept": {atype}', 'json')
try: try:
with self.database.session(True) as conn: with self.database.session(True) as conn: