298 lines
9.3 KiB
Python
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()
|