Files
pwss/docker-pwss/code/serve.py
2022-08-18 15:42:27 +03:00

129 lines
3.0 KiB
Python

from flask import (
Flask,
request,
session,
g,
redirect,
url_for,
render_template,
send_file,
)
from flask_limiter import Limiter
from revprox import ReverseProxied
from utils import (
authenticate,
check_auth,
get_ip,
get_valid_sessions,
read_config,
)
import os
import sys
DEBUG = False
SECRET_KEY = os.getenv("SECRET_KEY", "2f6aa45dfcfc37a50537f0b05af6452c")
DATABASE = os.getenv("DATABASE", "serve.db")
SESSION_EXPIRY = int(os.getenv("SESSION_EXPIRY", 1800))
FOLDERS = os.getenv("STATIC_FOLDER")
app = Flask(__name__)
app.config.from_object(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)
limiter = Limiter(
key_func=get_ip,
default_limits=["5 per 5 seconds"],
storage_uri="memcached://localhost:11211",
)
limiter.init_app(app)
@app.route("/s/<path:path>", methods=["POST", "GET"])
@limiter.limit(os.getenv("LIMITER_SHARE"))
def serve(path=None):
realpath = os.path.join(FOLDERS, path)
is_auth = check_auth(path)
if not is_auth:
session["return_to"] = path
return redirect(
url_for("login", folder=path.split(os.sep)[0]), code=302
)
if os.path.isdir(realpath):
if not path.endswith("/"):
return redirect(url_for("serve", path=f"{path}/"), code=302)
realpath = os.path.join(realpath, "index.html")
if not os.path.exists(realpath):
return "", 404
return send_file(
realpath,
)
@app.route("/l", methods=["POST", "GET"])
@app.route("/l/<folder>", methods=["POST", "GET"])
def login(folder=None):
if request.method == "POST":
folder = "".join(
letter for letter in request.form["folder"] if letter.isalnum()
)
config = read_config(folder)
success = authenticate(config, request.form["password"])
print(
f"{'Successful' if success else 'Failed'} login {folder}: {get_ip()}",
file=sys.stderr,
)
ret = session.get("return_to", None)
if "return_to" in session:
session.pop("return_to")
if ret:
return redirect(url_for("serve", path=ret))
else:
# GET
config = read_config(folder)
sessions = get_valid_sessions()
if not folder:
folder = ""
return render_template("login.html", folder=folder, sessions=sessions)
@app.route("/logout", methods=["GET"])
def logout():
to_delete = [key for key in session if key.startswith("auth/")]
for key in to_delete:
del session[key]
return redirect(url_for("index"))
@app.route("/", methods=["GET"])
def index():
return render_template("index.html")
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, "_database", None)
if db is not None:
db.close()
@app.errorhandler(429)
def ratelimit_handler(e):
print(
f"Ratelimit exceeded: {e.description} key: {e.limit.key_func()}",
file=sys.stderr,
)
return render_template("ratelimit.html", description=e.description), 429
if __name__ == "__main__":
app.run()