Browse Source

returning to this prototype, trying to run it again

master
manetta 3 years ago
parent
commit
f42cb3a85b
  1. 1
      .gitignore
  2. 48
      README.md
  3. 14
      requirements.txt
  4. 24
      run.py

1
.gitignore

@ -1,2 +1,3 @@
venv/
*.pem
__pycache__

48
README.md

@ -1,4 +1,50 @@
# AP test in Flask
This is a testing area! :)
A basic ActivityPub server written in Flask, that listens to follow requests, based on this tutorial: <https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/>
This is a testing area :), to see if it is possible to use Python and Flask to write a small server that speaks ActivityPub.
This repository was sparked from a curiosity in federation and light-weight (static) website making.
Operating on the level of the ActivityPub protocol will hopefully give some insights in the way that networks on the Fediverse federate with each other.
* What are the minimal requirements to run an Activity Pub server?
* How could a static site federate its content with the Fediverse?
* How are ActivityPub feeds similar and different from RSS feeds?
* How can such server be used? What kind of publishing tools can be imagined?
* What ActivityPub "objects" (is that the right term?) can we try out and use, next to the commonly used "Note" object?
# Install this prototype
When you run this prototype on a server, you could connect to the Fediverse.
## Prepare your server
For this you need to following:
* a subdomain for the server, for example: `ap.example.com`
* a SSL certificate for this domain, for which you could use: `certbot` from <https://letsencrypt.org/>
* a reverse proxy configuration, relaying the subdomain to the Flask application running on port `5010` (by default)
Once this is set up, you can install this prototype.
## Install the prototype
Make a virtual environment:
`$ python3 -m venv FOLDERNAME`
Activate the environment:
`$ source FOLDERNAME/bin/activate`
Install the dependencies:
`$ pip install -r requirements.txt`
Run the Flask application:
`$ python3 run.py`

14
requirements.txt

@ -0,0 +1,14 @@
certifi==2020.11.8
chardet==3.0.4
click==7.1.2
Flask==1.1.2
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
pkg-resources==0.0.0
pycryptodome==3.9.9
requests==2.25.0
urllib3==1.26.2
Werkzeug==1.0.1

24
tada.py → run.py

@ -17,6 +17,7 @@ A...P...
# Config
DOMAIN = 'https://ap.virtualprivateserver.space'
# domain = 'http://localhost:5000' # for local testing only, ActivityPub doesn't allow the usage of http:// (it only accepts https://)
def publicKey():
if not os.path.exists('./public.pem'):
os.system('openssl genrsa -out private.pem 2048')
@ -26,6 +27,7 @@ def publicKey():
PUBLICKEY = publicKey.replace('\n', '\\n') # JSON-LD doesn't want to work with linebreaks,
# but needs the \n character to know where to break the line ;)
return PUBLICKEY
INBOX = []
# Create the application.
@ -34,8 +36,8 @@ APP = flask.Flask(__name__)
@APP.route('/', methods=['GET'])
def index():
""" Displays the index page accessible at '/' """
content = 'test - index ({})'.format(DOMAIN)
content = f'This ActivityPub Server is running !!! (exciting): ({ DOMAIN })'
return content
@APP.route('/.well-known/webfinger', methods=['GET'])
@ -55,7 +57,6 @@ def webfinger():
json = flask.render_template('webfinger.json', query=query, actor=actor, domain=DOMAIN)
resp = flask.Response(json, status=200, mimetype='application/json')
return resp
else:
return 'no query'
@ -68,13 +69,12 @@ def return_actor(actor):
preferredUsername = actor # but could become a custom username, set by the user, stored in the database
# this preferredUsername doesn't show up yet in Mastodon ...
json = flask.render_template('actor.json', actor=actor, preferredUsername=preferredUsername, publicKey=publicKey(), domain=DOMAIN) # actor = alice
resp = flask.Response(json, status=200, mimetype='application/json')
resp = flask.Response(json, status=200, mimetype='application/json')
return resp
@APP.route('/inspect')
def inspect():
def inspect():
return flask.Response(b'<br><br>'.join(INBOX), status=200)
@APP.route('/users/<actor>/inbox', methods=['GET', 'POST'])
@ -86,14 +86,14 @@ def inbox(actor):
$ curl -d '{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","movedTo":{"@id":"as:movedTo","@type":"@id"},"Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation","toot":"http://joinmastodon.org/ns#","Emoji":"toot:Emoji","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"featured":{"@id":"toot:featured","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value"}],"id":"https://post.lurk.org/02d04ed5-dda6-48f3-a551-2e9c554de745","type":"Follow","actor":"https://post.lurk.org/users/manetta","object":"https://ap.virtualprivateserver.space/users/test","signature":{"type":"RsaSignature2017","creator":"https://post.lurk.org/users/manetta#main-key","created":"2018-11-28T16:15:35Z","signatureValue":"XUdBg+Zj9pkdOXlAYHhOtZlmU1Jdt63zwh2cXoJ8E8C1C+KvgGilkyfPTud9VNymVwdUQRl+YEW9KAZiiGaHb9H+tdVUr9BEkuR5E/tGehbMZr1sakC+qPehe4s3bRKEpJjTTJnTiSHaW7V6Qvr1u6+MVts6oj32az/ixuB/CfodSr3K/K+jZmmOl6SIUqX7Xg7xGwOxIsYaR7g9wbcJ4qyzKcTPZonPMsONq9/RSm3SeQBo7WO1FKlQiFxVP/y5eFaFP8GYDLZyK7Nj5kDL5TannfEpuF8f3oyTBErQhcFQYKcBZNbuaqX/WiIaGjtHIL2ctJe0Psb5Nfshx4MXmQ=="}}' -H "Content-Type: application/json" -X POST http://localhost:5001/users/test/inbox
"""
if flask.request.method == 'GET':
return '''This has been a <em>{}</em> request. <br>
It came with the following header: <br><br><em>{}</em><br><br>
You have searched for the actor <em>{}</em>. <br>
This is <em>{}</em>'s shared inbox: <br><br><em>{}</em>'''.format(flask.request.method, flask.request.headers, actor, DOMAIN, str(INBOX))
if flask.request.method == 'GET':
return f'''This has been a <em>{ flask.request.method }</em> request. <br>
It came with the following header: <br><br><em>{ flask.request.headers }</em><br><br>
You have searched for the actor <em>{ actor }</em>. <br>
This is <em>{ DOMAIN }</em>'s shared inbox: <br><br><em>{ str(INBOX) }</em>'''
if flask.request.method == 'POST':
INBOX.append(flask.request.data)
INBOX.append(flask.request.data)
return flask.Response(status=200)
if __name__ == '__main__':
Loading…
Cancel
Save