168 lines
4.8 KiB
Python
168 lines
4.8 KiB
Python
import logging
|
|
import subprocess
|
|
|
|
from aiohttp.web import HTTPForbidden, HTTPUnauthorized, Response, json_response
|
|
|
|
from . import __version__
|
|
from .application import app
|
|
from .http_debug import STATS
|
|
from .misc import request
|
|
from .processors import run_processor
|
|
|
|
|
|
try:
|
|
commit_label = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode('ascii')
|
|
|
|
except:
|
|
commit_label = '???'
|
|
|
|
|
|
async def home(request):
|
|
targets = '<br>'.join(app['database'].hostnames)
|
|
text = """
|
|
<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>""".format(host=request.host, note=app['config'].note, targets=targets, count=len(app['database'].inboxes))
|
|
|
|
return Response(
|
|
status = 200,
|
|
content_type = 'text/html',
|
|
charset = 'utf-8',
|
|
text = text
|
|
)
|
|
|
|
|
|
async def actor(request):
|
|
database = app['database']
|
|
|
|
data = {
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
"endpoints": {
|
|
"sharedInbox": f"https://{request.host}/inbox"
|
|
},
|
|
"followers": f"https://{request.host}/followers",
|
|
"following": f"https://{request.host}/following",
|
|
"inbox": f"https://{request.host}/inbox",
|
|
"name": "ActivityRelay",
|
|
"type": "Application",
|
|
"id": f"https://{request.host}/actor",
|
|
"publicKey": {
|
|
"id": f"https://{request.host}/actor#main-key",
|
|
"owner": f"https://{request.host}/actor",
|
|
"publicKeyPem": database.pubkey
|
|
},
|
|
"summary": "ActivityRelay bot",
|
|
"preferredUsername": "relay",
|
|
"url": f"https://{request.host}/actor"
|
|
}
|
|
|
|
return json_response(data, content_type='application/activity+json')
|
|
|
|
|
|
async def inbox(request):
|
|
config = app['config']
|
|
database = app['database']
|
|
|
|
if len(config.blocked_software):
|
|
software = await fetch_nodeinfo(request['actor_domain'])
|
|
|
|
if software and software.lower() in config.blocked_software:
|
|
logging.verbose(f'Rejected actor for using specific software: {software}')
|
|
raise HTTPForbidden(body='access denied', content_type='text/plain')
|
|
|
|
## reject if no post data or signature failed validation
|
|
if not request['data'] or not request['validated']:
|
|
logging.verbose('Rejected actor for missing post data')
|
|
raise HTTPUnauthorized(body='access denied', content_type='text/plain')
|
|
|
|
## reject if activity type isn't 'Follow' and the actor isn't following
|
|
if request['data']['type'] != 'Follow' and not database.get_inbox(request['actor_domain']):
|
|
logging.verbose(f'Rejected actor for trying to post while not following: {request["actor"]["id"]}')
|
|
raise HTTPUnauthorized(body='access denied', content_type='text/plain')
|
|
|
|
## reject if the actor isn't whitelisted while the whiltelist is enabled
|
|
elif config.whitelist_enabled and not request['data'].is_whitelisted(request['actor']['id']):
|
|
logging.verbose(f'Rejected actor for not being in the whitelist: {request["actor"]["id"]}')
|
|
raise HTTPForbidden(body='access denied', content_type='text/plain')
|
|
|
|
logging.debug(f">> payload {request['data']}")
|
|
|
|
await run_processor(request, request['data'], request['actor'])
|
|
return Response(body=b'{}', content_type='application/activity+json')
|
|
|
|
|
|
async def webfinger(request):
|
|
config = app['config']
|
|
subject = request.query['resource']
|
|
|
|
if subject != f'acct:relay@{request.host}':
|
|
return json_response({'error': 'user not found'}, status=404)
|
|
|
|
data = {
|
|
'subject': subject,
|
|
'aliases': [config.actor],
|
|
'links': [
|
|
{'href': config.actor, 'rel': 'self', 'type': 'application/activity+json'},
|
|
{'href': config.actor, 'rel': 'self', 'type': 'application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"'}
|
|
]
|
|
}
|
|
|
|
return json_response(data)
|
|
|
|
|
|
async def nodeinfo_2_0(request):
|
|
data = {
|
|
# XXX - is this valid for a relay?
|
|
'openRegistrations': True,
|
|
'protocols': ['activitypub'],
|
|
'services': {
|
|
'inbound': [],
|
|
'outbound': []
|
|
},
|
|
'software': {
|
|
'name': 'activityrelay',
|
|
'version': f'{__version__} {commit_label}'
|
|
},
|
|
'usage': {
|
|
'localPosts': 0,
|
|
'users': {
|
|
'total': 1
|
|
}
|
|
},
|
|
'metadata': {
|
|
'peers': app['database'].hostnames
|
|
},
|
|
'version': '2.0'
|
|
}
|
|
|
|
return json_response(data)
|
|
|
|
|
|
async def nodeinfo_wellknown(request):
|
|
data = {
|
|
'links': [
|
|
{
|
|
'rel': 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
|
'href': f'https://{request.host}/nodeinfo/2.0.json'
|
|
}
|
|
]
|
|
}
|
|
return json_response(data)
|
|
|
|
|
|
async def stats(request):
|
|
return json_response(STATS)
|