Create some quick and dirty admin endpoints
This commit is contained in:
parent
35a37aab8a
commit
e5b9b133fc
7 changed files with 90 additions and 13 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -78,6 +78,7 @@ class RelayConfig(DotDict):
|
|||
"json": 1024,
|
||||
"objects": 1024,
|
||||
"digests": 1024,
|
||||
"admin_token": None,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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), {
|
||||
|
|
Loading…
Reference in a new issue