mirror of
https://git.pleroma.social/pleroma/relay.git
synced 2024-11-14 03:27:59 +00:00
split views.py
This commit is contained in:
parent
b8e0641733
commit
226f940cdc
|
@ -5,12 +5,8 @@ import os
|
|||
import socket
|
||||
import typing
|
||||
|
||||
from aiohttp.abc import AbstractView
|
||||
from aiohttp.hdrs import METH_ALL as METHODS
|
||||
from aiohttp.web import Response as AiohttpResponse
|
||||
from aiohttp.web_exceptions import HTTPMethodNotAllowed
|
||||
from aputils.message import Message as ApMessage
|
||||
from functools import cached_property
|
||||
from uuid import uuid4
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
|
@ -232,67 +228,3 @@ class Response(AiohttpResponse):
|
|||
@location.setter
|
||||
def location(self, value: str) -> None:
|
||||
self.headers['Location'] = value
|
||||
|
||||
|
||||
class View(AbstractView):
|
||||
def __await__(self) -> Generator[Response]:
|
||||
if self.request.method not in METHODS:
|
||||
raise HTTPMethodNotAllowed(self.request.method, self.allowed_methods)
|
||||
|
||||
if not (handler := self.handlers.get(self.request.method)):
|
||||
raise HTTPMethodNotAllowed(self.request.method, self.allowed_methods) from None
|
||||
|
||||
return self._run_handler(handler).__await__()
|
||||
|
||||
|
||||
async def _run_handler(self, handler: Awaitable) -> Response:
|
||||
with self.database.config.connection_class(self.database) as conn:
|
||||
# todo: remove on next tinysql release
|
||||
conn.open()
|
||||
|
||||
return await handler(self.request, conn, **self.request.match_info)
|
||||
|
||||
|
||||
@cached_property
|
||||
def allowed_methods(self) -> tuple[str]:
|
||||
return tuple(self.handlers.keys())
|
||||
|
||||
|
||||
@cached_property
|
||||
def handlers(self) -> dict[str, Coroutine]:
|
||||
data = {}
|
||||
|
||||
for method in METHODS:
|
||||
try:
|
||||
data[method] = getattr(self, method.lower())
|
||||
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# app components
|
||||
@property
|
||||
def app(self) -> Application:
|
||||
return self.request.app
|
||||
|
||||
|
||||
@property
|
||||
def cache(self) -> Cache:
|
||||
return self.app.cache
|
||||
|
||||
|
||||
@property
|
||||
def client(self) -> HttpClient:
|
||||
return self.app.client
|
||||
|
||||
|
||||
@property
|
||||
def config(self) -> Config:
|
||||
return self.app.config
|
||||
|
||||
|
||||
@property
|
||||
def database(self) -> Database:
|
||||
return self.app.database
|
||||
|
|
4
relay/views/__init__.py
Normal file
4
relay/views/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from . import activitypub, frontend, misc
|
||||
from .base import VIEWS
|
|
@ -1,95 +1,27 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import traceback
|
||||
import typing
|
||||
|
||||
from aputils.errors import SignatureFailureError
|
||||
from aputils.misc import Digest, HttpDate, Signature
|
||||
from aputils.objects import Nodeinfo, Webfinger, WellKnownNodeinfo
|
||||
from pathlib import Path
|
||||
from aputils.objects import Webfinger
|
||||
|
||||
from . import __version__
|
||||
from . import logger as logging
|
||||
from .database.connection import Connection
|
||||
from .misc import Message, Response, View
|
||||
from .processors import run_processor
|
||||
from .base import View, register_route
|
||||
|
||||
from .. import logger as logging
|
||||
from ..misc import Message, Response
|
||||
from ..processors import run_processor
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from aiohttp.web import Request
|
||||
from aputils.signer import Signer
|
||||
from collections.abc import Callable
|
||||
from tinysql import Row
|
||||
|
||||
|
||||
VIEWS = []
|
||||
VERSION = __version__
|
||||
HOME_TEMPLATE = """
|
||||
<html><head>
|
||||
<title>ActivityPub Relay at {host}</title>
|
||||
<style>
|
||||
p {{ color: #FFFFFF; font-family: monospace, arial; font-size: 100%; }}
|
||||
body {{ background-color: #000000; }}
|
||||
a {{ color: #26F; }}
|
||||
a:visited {{ color: #46C; }}
|
||||
a:hover {{ color: #8AF; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>This is an Activity Relay for fediverse instances.</p>
|
||||
<p>{note}</p>
|
||||
<p>
|
||||
You may subscribe to this relay with the address:
|
||||
<a href="https://{host}/actor">https://{host}/actor</a>
|
||||
</p>
|
||||
<p>
|
||||
To host your own relay, you may download the code at this address:
|
||||
<a href="https://git.pleroma.social/pleroma/relay">
|
||||
https://git.pleroma.social/pleroma/relay
|
||||
</a>
|
||||
</p>
|
||||
<br><p>List of {count} registered instances:<br>{targets}</p>
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
|
||||
if Path(__file__).parent.parent.joinpath('.git').exists():
|
||||
try:
|
||||
commit_label = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode('ascii')
|
||||
VERSION = f'{__version__} {commit_label}'
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def register_route(*paths: str) -> Callable:
|
||||
def wrapper(view: View) -> View:
|
||||
for path in paths:
|
||||
VIEWS.append([path, view])
|
||||
|
||||
return View
|
||||
return wrapper
|
||||
from ..database.connection import Connection
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
@register_route('/')
|
||||
class HomeView(View):
|
||||
async def get(self, request: Request, conn: Connection) -> Response:
|
||||
config = conn.get_config_all()
|
||||
inboxes = conn.execute('SELECT * FROM inboxes').all()
|
||||
|
||||
text = HOME_TEMPLATE.format(
|
||||
host = self.config.domain,
|
||||
note = config['note'],
|
||||
count = len(inboxes),
|
||||
targets = '<br>'.join(inbox['domain'] for inbox in inboxes)
|
||||
)
|
||||
|
||||
return Response.new(text, ctype='html')
|
||||
|
||||
|
||||
|
||||
@register_route('/actor', '/inbox')
|
||||
class ActorView(View):
|
||||
def __init__(self, request: Request):
|
||||
|
@ -247,31 +179,3 @@ class WebfingerView(View):
|
|||
)
|
||||
|
||||
return Response.new(data, ctype = 'json')
|
||||
|
||||
|
||||
@register_route('/nodeinfo/{niversion:\\d.\\d}.json', '/nodeinfo/{niversion:\\d.\\d}')
|
||||
class NodeinfoView(View):
|
||||
# pylint: disable=no-self-use
|
||||
async def get(self, request: Request, conn: Connection, niversion: str) -> Response:
|
||||
inboxes = conn.execute('SELECT * FROM inboxes').all()
|
||||
|
||||
data = {
|
||||
'name': 'activityrelay',
|
||||
'version': VERSION,
|
||||
'protocols': ['activitypub'],
|
||||
'open_regs': not conn.get_config('whitelist-enabled'),
|
||||
'users': 1,
|
||||
'metadata': {'peers': [inbox['domain'] for inbox in inboxes]}
|
||||
}
|
||||
|
||||
if niversion == '2.1':
|
||||
data['repo'] = 'https://git.pleroma.social/pleroma/relay'
|
||||
|
||||
return Response.new(Nodeinfo.new(**data), ctype = 'json')
|
||||
|
||||
|
||||
@register_route('/.well-known/nodeinfo')
|
||||
class WellknownNodeinfoView(View):
|
||||
async def get(self, request: Request, conn: Connection) -> Response:
|
||||
data = WellKnownNodeinfo.new_template(self.config.domain)
|
||||
return Response.new(data, ctype = 'json')
|
93
relay/views/base.py
Normal file
93
relay/views/base.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
|
||||
from aiohttp.abc import AbstractView
|
||||
from aiohttp.hdrs import METH_ALL as METHODS
|
||||
from aiohttp.web import HTTPMethodNotAllowed
|
||||
from functools import cached_property
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from collections.abc import Callable, Coroutine, Generator
|
||||
from tinysql import Database
|
||||
from ..application import Application
|
||||
from ..cache import Cache
|
||||
from ..config import Config
|
||||
from ..http_client import HttpClient
|
||||
from ..misc import Response
|
||||
|
||||
|
||||
VIEWS = []
|
||||
|
||||
|
||||
def register_route(*paths: str) -> Callable:
|
||||
def wrapper(view: View) -> View:
|
||||
for path in paths:
|
||||
VIEWS.append([path, view])
|
||||
|
||||
return View
|
||||
return wrapper
|
||||
|
||||
|
||||
class View(AbstractView):
|
||||
def __await__(self) -> Generator[Response]:
|
||||
if self.request.method not in METHODS:
|
||||
raise HTTPMethodNotAllowed(self.request.method, self.allowed_methods)
|
||||
|
||||
if not (handler := self.handlers.get(self.request.method)):
|
||||
raise HTTPMethodNotAllowed(self.request.method, self.allowed_methods)
|
||||
|
||||
return self._run_handler(handler).__await__()
|
||||
|
||||
|
||||
async def _run_handler(self, handler: Coroutine) -> Response:
|
||||
with self.database.config.connection_class(self.database) as conn:
|
||||
# todo: remove on next tinysql release
|
||||
conn.open()
|
||||
|
||||
return await handler(self.request, conn, **self.request.match_info)
|
||||
|
||||
|
||||
@cached_property
|
||||
def allowed_methods(self) -> tuple[str]:
|
||||
return tuple(self.handlers.keys())
|
||||
|
||||
|
||||
@cached_property
|
||||
def handlers(self) -> dict[str, Coroutine]:
|
||||
data = {}
|
||||
|
||||
for method in METHODS:
|
||||
try:
|
||||
data[method] = getattr(self, method.lower())
|
||||
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# app components
|
||||
@property
|
||||
def app(self) -> Application:
|
||||
return self.request.app
|
||||
|
||||
|
||||
@property
|
||||
def cache(self) -> Cache:
|
||||
return self.app.cache
|
||||
|
||||
|
||||
@property
|
||||
def client(self) -> HttpClient:
|
||||
return self.app.client
|
||||
|
||||
|
||||
@property
|
||||
def config(self) -> Config:
|
||||
return self.app.config
|
||||
|
||||
|
||||
@property
|
||||
def database(self) -> Database:
|
||||
return self.app.database
|
62
relay/views/frontend.py
Normal file
62
relay/views/frontend.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
|
||||
from .base import View, register_route
|
||||
|
||||
from .. import __version__
|
||||
from ..misc import Response
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from aiohttp.web import Request
|
||||
from aputils.signer import Signer
|
||||
from collections.abc import Callable
|
||||
from tinysql import Row
|
||||
from ..database.connection import Connection
|
||||
|
||||
|
||||
HOME_TEMPLATE = """
|
||||
<html><head>
|
||||
<title>ActivityPub Relay at {host}</title>
|
||||
<style>
|
||||
p {{ color: #FFFFFF; font-family: monospace, arial; font-size: 100%; }}
|
||||
body {{ background-color: #000000; }}
|
||||
a {{ color: #26F; }}
|
||||
a:visited {{ color: #46C; }}
|
||||
a:hover {{ color: #8AF; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>This is an Activity Relay for fediverse instances.</p>
|
||||
<p>{note}</p>
|
||||
<p>
|
||||
You may subscribe to this relay with the address:
|
||||
<a href="https://{host}/actor">https://{host}/actor</a>
|
||||
</p>
|
||||
<p>
|
||||
To host your own relay, you may download the code at this address:
|
||||
<a href="https://git.pleroma.social/pleroma/relay">
|
||||
https://git.pleroma.social/pleroma/relay
|
||||
</a>
|
||||
</p>
|
||||
<br><p>List of {count} registered instances:<br>{targets}</p>
|
||||
</body></html>
|
||||
"""
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
@register_route('/')
|
||||
class HomeView(View):
|
||||
async def get(self, request: Request, conn: Connection) -> Response:
|
||||
config = conn.get_config_all()
|
||||
inboxes = conn.execute('SELECT * FROM inboxes').all()
|
||||
|
||||
text = HOME_TEMPLATE.format(
|
||||
host = self.config.domain,
|
||||
note = config['note'],
|
||||
count = len(inboxes),
|
||||
targets = '<br>'.join(inbox['domain'] for inbox in inboxes)
|
||||
)
|
||||
|
||||
return Response.new(text, ctype='html')
|
58
relay/views/misc.py
Normal file
58
relay/views/misc.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import typing
|
||||
|
||||
from aputils.objects import Nodeinfo, WellKnownNodeinfo
|
||||
from pathlib import Path
|
||||
|
||||
from .base import View, register_route
|
||||
|
||||
from .. import __version__
|
||||
from ..misc import Response
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from aiohttp.web import Request
|
||||
from ..database.connection import Connection
|
||||
|
||||
|
||||
VERSION = __version__
|
||||
|
||||
|
||||
if Path(__file__).parent.parent.joinpath('.git').exists():
|
||||
try:
|
||||
commit_label = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode('ascii')
|
||||
VERSION = f'{__version__} {commit_label}'
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
@register_route('/nodeinfo/{niversion:\\d.\\d}.json', '/nodeinfo/{niversion:\\d.\\d}')
|
||||
class NodeinfoView(View):
|
||||
# pylint: disable=no-self-use
|
||||
async def get(self, request: Request, conn: Connection, niversion: str) -> Response:
|
||||
inboxes = conn.execute('SELECT * FROM inboxes').all()
|
||||
|
||||
data = {
|
||||
'name': 'activityrelay',
|
||||
'version': VERSION,
|
||||
'protocols': ['activitypub'],
|
||||
'open_regs': not conn.get_config('whitelist-enabled'),
|
||||
'users': 1,
|
||||
'metadata': {'peers': [inbox['domain'] for inbox in inboxes]}
|
||||
}
|
||||
|
||||
if niversion == '2.1':
|
||||
data['repo'] = 'https://git.pleroma.social/pleroma/relay'
|
||||
|
||||
return Response.new(Nodeinfo.new(**data), ctype = 'json')
|
||||
|
||||
|
||||
@register_route('/.well-known/nodeinfo')
|
||||
class WellknownNodeinfoView(View):
|
||||
async def get(self, request: Request, conn: Connection) -> Response:
|
||||
data = WellKnownNodeinfo.new_template(self.config.domain)
|
||||
return Response.new(data, ctype = 'json')
|
Loading…
Reference in a new issue