Create some quick and dirty admin endpoints

This commit is contained in:
Reid 'arrdem' McKenzie 2022-11-26 19:26:39 -07:00
parent 35a37aab8a
commit e5b9b133fc
7 changed files with 90 additions and 13 deletions

View file

@ -4,4 +4,4 @@ cd "$(realpath $(dirname $0))"
bazel build ...
exec ../../bazel-bin/projects/activitypub_relay/activitypub_relay -c $(realpath ./relay.yaml) run
exec ../../bazel-bin/projects/activitypub_relay/activitypub_relay -c $(realpath ./relay_prod.yaml) run

View file

@ -357,6 +357,7 @@ def relay_setup(obj: Application):
obj.config.listen = os.getenv("LISTEN_ADDRESS", obj.config.listen)
obj.config.port = int(os.getenv("LISTEN_PORT", obj.config.port))
obj.config.host = os.getenv("RELAY_HOSTNAME")
obj.config.admin_token = os.getenv("RELAY_ADMIN_TOKEN")
if not obj.config.host:
click.echo("Error: No relay host configured! Set $RELAY_HOSTNAME")
exit(1)

View file

@ -78,6 +78,7 @@ class RelayConfig(DotDict):
"json": 1024,
"objects": 1024,
"digests": 1024,
"admin_token": None,
}
)

View file

@ -6,6 +6,7 @@ import logging
import socket
from urllib.parse import urlparse
import uuid
from typing import Union
from Crypto.Hash import SHA, SHA256, SHA512
from Crypto.PublicKey import RSA
@ -453,7 +454,7 @@ class Message(DotDict):
class Response(AiohttpResponse):
@classmethod
def new(cls, body="", status=200, headers=None, ctype="text"):
def new(cls, body: Union[dict, str, bytes] = "", status=200, headers=None, ctype="text"):
kwargs = {
"status": status,
"headers": headers,

View file

@ -1,6 +1,6 @@
import logging
from aiohttp.web import HTTPUnauthorized
from aiohttp.web import HTTPUnauthorized, Request
from relay import __version__, misc
from relay.http_debug import STATS
from relay.misc import (
@ -148,7 +148,8 @@ async def inbox(request):
@register_route("GET", "/.well-known/webfinger")
async def webfinger(request):
subject = request.query["resource"]
if not (subject := request.query.get("resource")):
return Response.new_error(404, "no resource specified", "json")
if subject != f"acct:relay@{request.app.config.host}":
return Response.new_error(404, "user not found", "json")
@ -203,5 +204,73 @@ async def nodeinfo_wellknown(request):
@register_route("GET", "/stats")
async def stats(*args, **kwargs):
return Response.new(STATS, ctype="json")
async def stats(request):
stats = STATS.copy()
stats["pending_requests"] = len(request.app.database.get("follow-requests", {}))
return Response.new(stats, ctype="json")
@register_route("POST", "/admin/config")
async def set_config(request: Request):
if not (auth := request.headers.get("Authorization")):
return Response.new_error(403, "access denied", "json")
if not auth == f"Bearer {request.app.config.admin_token}":
return Response.new_error(403, "access denied", "json")
# FIXME: config doesn't have a way to go from JSON or update, using dict stuff
new_config = await request.json()
request.app.config.update(new_config)
# If there are pending follows which are NOW whitelisted, allow them
if request.app.config.whitelist_enabled:
for domain in request.app.config.whitelist:
if (pending_follow := request.app.database.get_request(domain, False)):
await misc.request(
actor.shared_inbox,
misc.Message.new_response(
host=request.app.config.host,
actor=pending_follow["actor"],
followid=pending_follow["followid"],
accept=True
),
)
await misc.request(
actor.shared_inbox,
misc.Message.new_follow(
host=request.app.config.host,
actor=pending_follow["actor"]
),
)
request.app.database.del_request(domain)
request.app.database.save()
request.app.config.save()
return Response.new(status=202)
@register_route("GET", "/admin/config")
def set_config(request: Request):
if not (auth := request.headers.get("Authorization")):
return Response.new_error(403, "access denied", "json")
if not auth == f"Bearer {request.app.config.admin_token}":
return Response.new_error(403, "access denied", "json")
return Response.new(request.app.config, status=200, ctype="json")
@register_route("GET", "/admin/pending")
def get_pending(request):
if not (auth := request.headers.get("Authorization")):
return Response.new_error(403, "access denied", "json")
if not auth == f"Bearer {request.app.config.admin_token}":
return Response.new_error(403, "access denied", "json")
return Response.new(request.app.database["follow-requests"], status=200, ctype="json")

View file

@ -4,12 +4,8 @@
ichor entrypoint
"""
from ichor.bootstrap import (
BOOTSTRAP,
NOT1,
TRUE,
)
from ichor import isa
from ichor.bootstrap import BOOTSTRAP, NOT1, TRUE
from ichor.interpreter import Interpreter

View file

@ -5,6 +5,7 @@
from abc import ABC
from dataclasses import dataclass
import typing as t
from uuid import UUID
from shoggoth.types import Keyword, List, Symbol
@ -85,12 +86,20 @@ class Multiverse:
"""
soup: t.Dict[]
@dataclass
class Namespace:
pass
@dataclass
class Entry:
ns: "Namespace"
expr: t.Any
value: t.Any
soup: t.Dict[UUID, Entry] = {}
BOOTSTRAP = "lang.shoggoth.v0.bootstrap"
SPECIALS = Multiverse.Namespace(Symbol(BOOTSTRAP), {