2021-12-23 21:39:21 +00:00
|
|
|
import requests
|
|
|
|
import os
|
|
|
|
import functools
|
|
|
|
import dotenv
|
|
|
|
import giteapy
|
|
|
|
import time
|
|
|
|
import base64
|
|
|
|
import yaml
|
2015-01-19 07:10:03 +00:00
|
|
|
|
2021-12-23 21:39:21 +00:00
|
|
|
from slugify import slugify
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
from flask import Flask, request, url_for, Response
|
|
|
|
from requests import api
|
|
|
|
from flask_micropub import MicropubClient
|
|
|
|
|
|
|
|
dotenv.load_dotenv()
|
|
|
|
|
|
|
|
PERMITTED_DOMAIN = os.environ.get(
|
|
|
|
'PERMITTED_DOMAINS', 'https://brainsteam.co.uk/').split(';')
|
2015-01-19 07:10:03 +00:00
|
|
|
|
|
|
|
app = Flask(__name__)
|
2015-01-19 17:01:56 +00:00
|
|
|
app.config['SECRET_KEY'] = 'my super secret key'
|
2021-12-23 21:39:21 +00:00
|
|
|
micropub = MicropubClient(app, client_id='https://brainsteam.co.uk')
|
|
|
|
|
|
|
|
|
|
|
|
def authed_endpoint(f):
|
|
|
|
@functools.wraps(f)
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
|
|
|
|
authtok = request.headers.get('Authorization')
|
|
|
|
|
|
|
|
if authtok is None:
|
2021-12-24 10:49:31 +00:00
|
|
|
return {
|
|
|
|
"error": "unauthorized",
|
|
|
|
"error_description": "An auth token was not provided"
|
|
|
|
}, 401
|
2021-12-23 21:39:21 +00:00
|
|
|
|
|
|
|
auth = requests.get("https://tokens.indieauth.com/token", headers={
|
|
|
|
"Authorization": authtok, "Accept": "application/json"}).json()
|
|
|
|
|
|
|
|
if auth['me'] not in PERMITTED_DOMAIN:
|
2021-12-24 10:49:31 +00:00
|
|
|
return {"error": "forbidden", "error_description": f"User {auth['me']} not permitted to post here"}, 403
|
2021-12-23 21:39:21 +00:00
|
|
|
|
|
|
|
return f(*args, *kwargs)
|
|
|
|
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
|
|
|
|
_api_client = None
|
|
|
|
|
|
|
|
|
|
|
|
def get_api_client():
|
|
|
|
global _api_client
|
|
|
|
|
|
|
|
if _api_client is None:
|
|
|
|
config = giteapy.Configuration()
|
|
|
|
config.host = os.environ.get('GITEA_URL')
|
|
|
|
config.api_key['access_token'] = os.environ.get('GITEA_API_KEY')
|
|
|
|
_api_client = giteapy.RepositoryApi(giteapy.ApiClient(config))
|
2015-01-19 07:10:03 +00:00
|
|
|
|
2021-12-23 21:39:21 +00:00
|
|
|
return _api_client
|
2015-01-19 07:10:03 +00:00
|
|
|
|
2021-12-23 21:39:21 +00:00
|
|
|
|
|
|
|
@app.route('/', methods=['POST'])
|
|
|
|
@authed_endpoint
|
|
|
|
def req():
|
|
|
|
|
2021-12-23 21:53:21 +00:00
|
|
|
doc = request.form.to_dict(flat=True)
|
2021-12-23 21:39:21 +00:00
|
|
|
|
|
|
|
if 'name' in doc:
|
2021-12-24 10:49:31 +00:00
|
|
|
entry_type = "post"
|
2021-12-23 21:39:21 +00:00
|
|
|
else:
|
2021-12-24 10:49:31 +00:00
|
|
|
entry_type = "note"
|
2021-12-23 21:39:21 +00:00
|
|
|
|
|
|
|
now = datetime.now()
|
|
|
|
now_ts = int(time.mktime(now.timetuple()))
|
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
url = os.path.join("/", entry_type + "s",
|
|
|
|
now.strftime("%Y/%m/%d"), str(now_ts))
|
2021-12-23 21:39:21 +00:00
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
if 'name' in doc:
|
|
|
|
slug = slugify(doc['name']) + str(now_ts)
|
|
|
|
else:
|
|
|
|
slug = str(now_ts)
|
2021-12-23 21:39:21 +00:00
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
file_path = os.path.join(os.environ.get(
|
|
|
|
'CONTENT_PREFIX'), entry_type + "s", now.strftime("%Y/%m/%d"), slug + ".md")
|
2021-12-23 21:53:21 +00:00
|
|
|
|
|
|
|
frontmatter = {
|
2021-12-24 10:49:31 +00:00
|
|
|
"url": url,
|
|
|
|
"type": entry_type,
|
|
|
|
"date": now.isoformat(sep='T'),
|
2021-12-23 21:53:21 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
if 'category' in doc:
|
|
|
|
categories = [doc['category']]
|
|
|
|
else:
|
|
|
|
categories = request.form.getlist('category[]')
|
|
|
|
|
|
|
|
if 'name' in doc:
|
|
|
|
frontmatter['title'] = doc['name']
|
2021-12-23 21:53:21 +00:00
|
|
|
|
|
|
|
if len(categories) > 0:
|
2021-12-24 10:49:31 +00:00
|
|
|
frontmatter['tags'] = categories
|
|
|
|
|
|
|
|
frontmatter_str = yaml.dump(frontmatter)
|
2021-12-23 21:53:21 +00:00
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
if 'photo' in doc:
|
2021-12-23 21:53:21 +00:00
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
if os.environ.get('MICROPUB_IMAGE_STRATEGY') == 'copy':
|
|
|
|
# download the photo
|
|
|
|
r = requests.get(doc['photo'])
|
2021-12-23 21:53:21 +00:00
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
# generate local filename
|
|
|
|
filename = os.path.join(os.environ.get('MICROPUB_MEDIA_PATH'), now.strftime("%Y/%m/%d"), str(now_ts) + ".jpg")
|
|
|
|
photo_url = os.path.join(os.environ.get('MICROPUB_MEDIA_URL_PREFIX'), now.strftime("%Y/%m/%d"), str(now_ts) + ".jpg")
|
2021-12-23 21:53:21 +00:00
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
# make directory if needed
|
2021-12-24 10:50:40 +00:00
|
|
|
if not os.path.exists(os.path.dirname(filename)):
|
|
|
|
os.makedirs(os.path.dirname(filename))
|
2021-12-24 10:49:31 +00:00
|
|
|
|
|
|
|
with open(filename, 'wb') as f:
|
|
|
|
f.write(r.content)
|
|
|
|
|
|
|
|
# elif os.environ.get('MICROPUB_IMAGE_STRATEGY') == 'gitea':
|
|
|
|
|
|
|
|
else:
|
|
|
|
photo_url = doc['photo']
|
|
|
|
|
|
|
|
|
|
|
|
docstr = f"![image]({photo_url}) \n\n {doc['content']}"
|
|
|
|
else:
|
|
|
|
docstr = doc['content']
|
|
|
|
|
|
|
|
content = base64.encodestring(
|
|
|
|
f"---\n{frontmatter_str}\n---\n\n{docstr}".encode("utf8")).decode("utf8")
|
2021-12-23 21:39:21 +00:00
|
|
|
|
|
|
|
api = get_api_client()
|
|
|
|
|
|
|
|
body = giteapy.CreateFileOptions(content=content)
|
|
|
|
|
|
|
|
try:
|
2021-12-24 10:49:31 +00:00
|
|
|
r = api.repo_create_file(os.environ.get(
|
|
|
|
'GITEA_REPO_OWNER'), os.environ.get('GITEA_REPO_NAME'), file_path, body)
|
2021-12-23 21:39:21 +00:00
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
return Response(status=202, headers={"Location": url})
|
2021-12-23 21:39:21 +00:00
|
|
|
|
|
|
|
except Exception as e:
|
2021-12-24 10:49:31 +00:00
|
|
|
return {"error": str(e)}, 500
|
2021-12-23 21:39:21 +00:00
|
|
|
|
2021-12-23 21:53:21 +00:00
|
|
|
# print(r)
|
2021-12-23 21:39:21 +00:00
|
|
|
|
2021-12-23 21:53:21 +00:00
|
|
|
# return {"hello": "world"}
|
2021-12-23 21:39:21 +00:00
|
|
|
|
|
|
|
|
2021-12-24 10:49:31 +00:00
|
|
|
@app.route("/", methods=['GET'])
|
|
|
|
@authed_endpoint
|
2015-02-07 17:40:31 +00:00
|
|
|
def index():
|
2021-12-24 10:49:31 +00:00
|
|
|
|
|
|
|
if request.args.get('q') == 'config':
|
|
|
|
return {
|
|
|
|
"media-endpoint": "/micropub/media",
|
|
|
|
"syndicate-to": [
|
|
|
|
{
|
|
|
|
"uid": "mastodon",
|
|
|
|
"name": "Mastodon"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"post-types": [
|
|
|
|
{
|
|
|
|
"type": "note",
|
|
|
|
"name": "Note"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "article",
|
|
|
|
"name": "Blog Post"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "photo",
|
|
|
|
"name": "Photo"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "reply",
|
|
|
|
"name": "Reply"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "bookmark",
|
|
|
|
"name": "Bookmark"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/form', methods=['GET'])
|
|
|
|
def authform():
|
2015-02-07 17:40:31 +00:00
|
|
|
return """
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<form action="/authenticate" method="GET">
|
|
|
|
<input type="text" name="me" placeholder="your domain.com"/>
|
|
|
|
<button type="submit">Authenticate</button>
|
|
|
|
</form>
|
|
|
|
<form action="/authorize" method="GET">
|
|
|
|
<input type="text" name="me" placeholder="your domain.com"/>
|
|
|
|
<select name="scope">
|
|
|
|
<option>read</option>
|
|
|
|
<option>post</option>
|
|
|
|
<option>comment</option>
|
|
|
|
</select>
|
|
|
|
<button type="submit">Authorize</button>
|
|
|
|
</form>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/authenticate')
|
|
|
|
def authenticate():
|
|
|
|
return micropub.authenticate(
|
|
|
|
request.args.get('me'), next_url=url_for('index'))
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/authorize')
|
|
|
|
def authorize():
|
|
|
|
return micropub.authorize(
|
|
|
|
request.args.get('me'), next_url=url_for('index'),
|
|
|
|
scope=request.args.get('scope'))
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/indieauth-callback')
|
|
|
|
@micropub.authenticated_handler
|
|
|
|
def indieauth_callback(resp):
|
2015-01-19 07:10:03 +00:00
|
|
|
return """
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
2015-01-19 17:01:56 +00:00
|
|
|
<body>
|
2015-02-07 17:40:31 +00:00
|
|
|
Authenticated:
|
2015-01-19 17:01:56 +00:00
|
|
|
<ul>
|
|
|
|
<li>me: {}</li>
|
|
|
|
<li>next: {}</li>
|
|
|
|
<li>error: {}</li>
|
|
|
|
</ul>
|
|
|
|
</body>
|
2015-01-19 07:10:03 +00:00
|
|
|
</html>
|
2015-02-07 17:40:31 +00:00
|
|
|
""".format(resp.me, resp.next_url, resp.error)
|
2015-01-19 07:10:03 +00:00
|
|
|
|
|
|
|
|
2015-02-07 17:40:31 +00:00
|
|
|
@app.route('/micropub-callback')
|
|
|
|
@micropub.authorized_handler
|
|
|
|
def micropub_callback(resp):
|
2021-12-23 21:39:21 +00:00
|
|
|
|
2015-01-19 07:10:03 +00:00
|
|
|
return """
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
2015-01-19 17:01:56 +00:00
|
|
|
<body>
|
2015-02-07 17:40:31 +00:00
|
|
|
Authorized:
|
|
|
|
<ul>
|
|
|
|
<li>me: {}</li>
|
|
|
|
<li>endpoint: {}</li>
|
|
|
|
<li>token: {}</li>
|
|
|
|
<li>next: {}</li>
|
|
|
|
<li>error: {}</li>
|
|
|
|
</ul>
|
2015-01-19 17:01:56 +00:00
|
|
|
</body>
|
2015-01-19 07:10:03 +00:00
|
|
|
</html>
|
2015-02-07 17:40:31 +00:00
|
|
|
""".format(resp.me, resp.micropub_endpoint, resp.access_token,
|
|
|
|
resp.next_url, resp.error)
|
2015-01-19 07:10:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
app.run(debug=True)
|