80 lines
2.7 KiB
Python
80 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from flask import Blueprint, render_template, Response, request, redirect, url_for, abort, send_file
|
|
from ..services.preferences import get_preferences, list_profiles, active_profile, BOOTSTRAP_THEMES, FONT_FAMILIES
|
|
from ..services import auth
|
|
from ..services.frontend_assets import asset_path
|
|
|
|
# for favicon
|
|
from flask import current_app, send_from_directory
|
|
|
|
bp = Blueprint("main", __name__)
|
|
|
|
|
|
def _asset_url(key: str) -> str:
|
|
path = asset_path(key)
|
|
return path if path.startswith("http") else url_for("static", filename=path)
|
|
|
|
|
|
@bp.get("/favicon.ico")
|
|
def favicon_ico():
|
|
response = send_from_directory(
|
|
current_app.static_folder,
|
|
"favicon.svg",
|
|
mimetype="image/svg+xml",
|
|
)
|
|
return response
|
|
|
|
|
|
@bp.route("/login", methods=["GET", "POST"])
|
|
def login():
|
|
# Note: When optional authentication is disabled, /login is intentionally unavailable.
|
|
if not auth.enabled():
|
|
abort(404)
|
|
error = ""
|
|
if request.method == "POST":
|
|
user = auth.login_user(request.form.get("username", ""), request.form.get("password", ""))
|
|
if user:
|
|
return redirect(request.args.get("next") or url_for("main.index"))
|
|
error = "Invalid username or password"
|
|
return render_template("login.html", error=error)
|
|
|
|
|
|
@bp.get("/logout")
|
|
def logout():
|
|
auth.logout_user()
|
|
if not auth.enabled():
|
|
return redirect(url_for("main.index"))
|
|
return redirect(url_for("main.login"))
|
|
|
|
|
|
@bp.get("/")
|
|
def index():
|
|
prefs = get_preferences()
|
|
return render_template(
|
|
"index.html",
|
|
prefs=prefs,
|
|
profiles=list_profiles(),
|
|
active_profile=active_profile(),
|
|
bootstrap_themes=BOOTSTRAP_THEMES,
|
|
font_families=FONT_FAMILIES,
|
|
auth_enabled=auth.enabled(),
|
|
current_user=auth.current_user(),
|
|
)
|
|
|
|
|
|
@bp.get("/docs")
|
|
def docs():
|
|
html = f"""<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>pyTorrent API Docs</title><link rel="stylesheet" href="{_asset_url('swagger_css')}"></head><body><div id="swagger-ui"></div><script src="{_asset_url('swagger_js')}"></script><script>window.onload=()=>SwaggerUIBundle({{url:'/api/openapi.json',dom_id:'#swagger-ui',deepLinking:true,persistAuthorization:true}});</script></body></html>"""
|
|
return Response(html, mimetype="text/html")
|
|
|
|
|
|
@bp.get("/api/openapi.json")
|
|
def openapi():
|
|
spec_path = Path(current_app.root_path) / "openapi" / "openapi.json"
|
|
response = send_file(spec_path, mimetype="application/json", conditional=False, max_age=0)
|
|
response.headers["Cache-Control"] = "no-store, no-cache, private"
|
|
return response
|