diff --git a/app.py b/app.py index 0eb80b6..1d18b13 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,13 @@ -from flask import Flask, request, redirect, url_for, render_template +from flask import Flask, request, redirect, url_for, render_template, jsonify from plankapy import Planka, User, InvalidToken from flask_wtf import FlaskForm from wtforms import StringField, SubmitField, PasswordField from wtforms.validators import DataRequired, Email, ValidationError +from werkzeug.middleware.proxy_fix import ProxyFix from configparser import ConfigParser from random import SystemRandom +from typing import List, Tuple import sqlite3 import smtplib @@ -22,6 +24,12 @@ app.config["SECRET_KEY"] = "".join( config = ConfigParser() config.read("settings.ini") +if config.getboolean("App", "debug", fallback=False): + app.debug = True + +if config.getboolean("App", "proxyfix", fallback=False): + app.wsgi_app = ProxyFix(app.wsgi_app) + def initialize_database(): conn = sqlite3.connect("db.sqlite3") @@ -49,7 +57,7 @@ def rate_limit(request): """ SELECT COUNT(*) FROM requests - WHERE ip = ? AND created_at > datetime('now', '-1 hour') + WHERE ip = ? AND created_at > datetime('now', '-1 day') """, (request.remote_addr,), ) @@ -76,6 +84,16 @@ def get_mailserver(): return mailserver +def get_footer_links() -> List[Tuple[str, str]]: + links = [] + + if "Footer" in config.sections(): + for key in config["Footer"]: + links.append((key.capitalize(), config["Footer"][key])) + + return links + + def send_email(email, token): mailserver = get_mailserver() sender = config.get("SMTP", "from", fallback=config["SMTP"]["username"]) @@ -126,6 +144,7 @@ def process_request(request): app=config["App"]["name"], title="Already Requested", subtitle="You have already requested access with this email address.", + footer_links=get_footer_links(), ) token = str(uuid.uuid4()) @@ -154,7 +173,13 @@ class EmailForm(FlaskForm): @app.route("/", methods=["GET", "POST"]) def start_request(): if rate_limit(request): - return render_template("rate_limit.html") + return render_template( + "rate_limit.html", + app=config["App"]["name"], + title="Rate Limited", + subtitle="You have reached the rate limit for requests. Please try again later.", + footer_links=get_footer_links(), + ) form = EmailForm() @@ -167,6 +192,7 @@ def start_request(): title="Request Access", subtitle="Please enter your email address to request access.", form=form, + footer_links=get_footer_links(), ) @@ -177,6 +203,7 @@ def post_request(): app=config["App"]["name"], title="Request Received", subtitle="Your request has been received. Please check your email for further instructions.", + footer_links=get_footer_links(), ) @@ -187,8 +214,6 @@ class SignupForm(FlaskForm): password = PasswordField("Password", validators=[DataRequired()]) submit = SubmitField("Submit") - email.render_kw = {"readonly": True} - def validate_username(self, field): planka = Planka( url=config["Planka"]["url"], @@ -231,6 +256,7 @@ def confirm_request(token): app=config["App"]["name"], title="Invalid Token", subtitle="The token you provided is invalid.", + footer_links=get_footer_links(), ) email = row[0] @@ -252,7 +278,22 @@ def confirm_request(token): email=email, ) - users.create(new_user) + try: + users.create(new_user) + except InvalidToken: + form.password.errors.append( + "Your password did not meet Planka's requirements. Please try again." + ) + + return render_template( + "signup.html", + app=config["App"]["name"], + title="Complete Signup", + subtitle="Please confirm your email address by filling out the form below.", + email=email, + form=form, + footer_links=get_footer_links(), + ) cursor.execute( """ @@ -274,6 +315,7 @@ def confirm_request(token): subtitle="Please confirm your email address by filling out the form below.", email=email, form=form, + footer_links=get_footer_links(), ) @@ -285,7 +327,26 @@ def post_signup(): title="Signup Complete", subtitle="Your account has been created. You may now log in.", planka=config["Planka"]["url"], + footer_links=get_footer_links(), ) +@app.route("/cron") +def cron(): + conn = sqlite3.connect("db.sqlite3") + cursor = conn.cursor() + + cursor.execute( + """ + DELETE FROM requests + WHERE created_at < datetime('now', '-2 day') + """ + ) + + conn.commit() + conn.close() + + return jsonify({"status": "ok"}) + + initialize_database() diff --git a/settings.dist.ini b/settings.dist.ini new file mode 100644 index 0000000..860aa49 --- /dev/null +++ b/settings.dist.ini @@ -0,0 +1,29 @@ +[App] +# Name of the app +Name = Private.coffee Planka + +# Hostname of the app - the app always assumes that HTTPS is used +Host = register.planka.private.coffee + +# Set to 1 if you are using a reverse proxy in front of the app +ProxyFix = 1 + +[SMTP] +# SMTP server settings +Host = mail.local +Port = 587 +Username = planka@mail.local +Password = verysecurepassword +SSL = 1 # Set to 0 if you are not using SSL/TLS +STARTTLS = 1 # Set to 0 if you are not using STARTTLS + +[Planka] +# URL and credentials for the Planka instance +URL = https://planka.local +Username = admin@mail.local +Password = extremelysecurepassword + +[Footer] +Website = https://private.coffee +Legal = https://private.coffee/legal.html +Privacy = https://private.coffee/privacy.html \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 6518f55..ea8f752 100644 --- a/templates/base.html +++ b/templates/base.html @@ -4,7 +4,7 @@ {{ app }} - {{ title }} - +
@@ -22,7 +22,9 @@