me-site/src/index.py
2022-08-31 03:18:44 +02:00

298 lines
9.3 KiB
Python

from flask import request, session, Blueprint, Response, redirect, escape
from htmlpy import *
from config import CONFIG
from os.path import exists
import os
import datetime
import requests
import gnupg
main_pages = Blueprint("main", __name__)
def download_file(url, file):
os.makedirs("/app/static/", exist_ok=True)
r = requests.get(url, allow_redirects=True)
open(file, "wb").write(r.content)
# Downloads all bootstrap files to flasks static directory
def cache_bootstrap():
download_file(
"https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css",
"/app/static/bootstrap.min.css",
)
download_file(
"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css",
"/app/static/bootstrap-icons.css",
)
download_file(
"https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js",
"/app/static/bootstrap.bundle.min.js",
)
# Bootstrap CSS
BOOTSTRAP = [
Reference(
"/static/bootstrap.min.css",
"stylesheet",
),
Reference(
"/static/bootstrap-icons.css",
"stylesheet",
),
Script(src="/static/bootstrap.bundle.min.js"),
]
# Encrypt msg with GPG
def encrypt(msg):
pgp = gnupg.GPG()
pgp.import_keys(open("/config/pub.key").read())
return str(pgp.encrypt(msg, pgp.list_keys()[0]["fingerprint"]))
# Save msg in `/data/messages`
def save_message(msg, name=""):
os.makedirs("/data/messages", exist_ok=True)
dt = datetime.datetime.now().strftime("%Y-%m-%d.%H-%M")
f = open(f"/data/messages/{name}-{dt}.asc", "w")
f.write(msg)
# Send Notification to all handlers
def notify(msg, title=None):
try:
gotify_notification(msg, title)
except:
pass
# Gotify Notification Handler
def gotify_notification(msg, title=None):
token = CONFIG["notify"]["gotify"]["token"]
url = CONFIG["notify"]["gotify"]["host"]
requests.post(
f"https://{url}/message?token={token}",
{"title": title, "message": msg, "priority": "5"},
)
# Message Sending Page
@main_pages.route("/message", methods=["GET", "POST"])
def send_message():
if request.method == "POST":
# If POST handle message
msg = request.form["message"]
name = request.form["msg_name"]
if msg is not None:
cipher = encrypt(msg)
save_message(cipher, name)
notify(f"New Message from {name}")
return redirect(request.url_root)
else:
# Return Message Form
return buildSite(
[
Div(
[
Heading(1, "Message"),
LineBreak(),
Form(
destination=request.url,
inner=[
Input(
"",
placeholder="Name",
name="msg_name",
global_attr=GlobalAttributes(
css_class="form-control bg-dark text-white",
style="margin-bottom: 15px",
),
),
TextArea(
inner="",
placeholder="Message",
name="message",
global_attr=GlobalAttributes(
css_class="form-control bg-dark text-white",
style="margin-bottom: 15px;",
),
),
Input(
type=InputType.Submit,
value="Send Message",
name="submit",
global_attr=GlobalAttributes(
css_class="btn btn-danger text-white text-decoration-none"
),
),
],
),
],
global_attr=GlobalAttributes(
css_class="container", style="margin-top: 25px"
),
)
],
"Send a message",
).to_code()
# Wrapper for Base HTML
def buildSite(content, title=None):
return Document(
head=Head(
[
Title(title),
Meta(
name="viewport",
content="user-scalable=no, width=device-width, initial-scale=1.0",
),
BOOTSTRAP,
]
),
body=Body(
content,
global_attr=GlobalAttributes(
css_class="bg-dark text-white justify-content-center text-center"
),
),
)
# Public Key Page
@main_pages.route("/public_key", methods=["GET"])
def public_key():
try:
ret = open("/config/pub.key").read()
pgp = gnupg.GPG()
pgp.import_keys(open("/config/pub.key").read())
key_id = (
str(pgp.list_keys()[0]["uids"][0]).replace("<", "[ ").replace(">", " ]")
)
ua = request.user_agent.string.lower()
if "chrome" in ua or "safari" in ua or "firefox" in ua:
# If request is from common browser return stylized html
return buildSite(
[
Div(
Div(
[
Bold("To Import: "),
Span(
f'curl -sL "{request.base_url}"|gpg --import',
global_attr=GlobalAttributes(
style="display: block;font-family: monospace,monospace;margin-top: 10px; font-size: 20px;overflow-wrap: break-word;"
),
),
],
global_attr=GlobalAttributes(css_class="alert alert-info"),
),
global_attr=GlobalAttributes(
css_class="container", style="margin-top: 25px"
),
),
Heading(
4,
key_id,
global_attr=GlobalAttributes(
css_class="container card",
style="padding-top: 10px; padding-bottom: 10px; background: black; margin-bottom: 15px;",
),
),
Div(
Paragraph(ret.replace("\n", "<br>")),
global_attr=GlobalAttributes(
css_class="container card bg-primary"
),
),
],
"Public Key",
).to_code()
# Return raw key
resp = Response(response=ret, status=200, mimetype="application/pgp-keys")
return resp
except:
# If key does not exist return error
return Response(response="", status=502)
# Contact
def build_contact_block():
return Div(
[
Heading(1, "Contact"),
ThematicBreak(),
[
Link("/public_key", "My PGP Key"),
LineBreak(),
Link("/message", "Write a message"),
LineBreak(),
LineBreak(),
]
if exists("/config/pub.key")
else None,
Link(f"mailto:{CONFIG['email']}", CONFIG["email"]),
LineBreak(),
],
global_attr=GlobalAttributes(css_class="container border-dark"),
)
# Donations
def build_donation_block():
return Div(
[
Heading(1, "Donation"),
ThematicBreak(),
Paragraph(
[
Bold("Monero: "),
Span(
CONFIG["xmr_address"],
global_attr=GlobalAttributes(
style="color: orange;overflow-wrap: break-word;"
),
),
]
)
if "xmr_address" in CONFIG
else None,
],
global_attr=GlobalAttributes(css_class="container", style="margin-top: 20px"),
)
# Basic Information
def build_information_block():
return Div(
[
Image(
"/assets/me",
200,
200,
"Me",
global_attr=GlobalAttributes(css_class="rounded"),
),
LineBreak(),
LineBreak(),
Heading(1, CONFIG["name"]),
ThematicBreak(),
],
global_attr=GlobalAttributes(
css_class="container border-dark", style="margin-top: 20px"
),
)
# Main
@main_pages.route("/", methods=["GET"])
def index():
return buildSite(
[build_information_block(), build_contact_block(), build_donation_block()],
"About Me",
).to_code()