Ensure trailing slashes are handled correctly
This commit is contained in:
parent
97512598f5
commit
5e0cb48906
8
main.py
8
main.py
|
@ -1,6 +1,4 @@
|
|||
"""
|
||||
Main Flask-based app for Slinky
|
||||
"""
|
||||
"""Main Flask-based app for Slinky."""
|
||||
|
||||
from flask import Flask, Response, render_template
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
@ -15,10 +13,10 @@ app.register_blueprint(slinky_webapp)
|
|||
@app.route("/")
|
||||
@protect
|
||||
def index() -> Response:
|
||||
"""
|
||||
Index/Landing page
|
||||
"""Index/Landing page.
|
||||
|
||||
Returns:
|
||||
str: string of page content
|
||||
|
||||
"""
|
||||
return Response(render_template("index.html"), 200)
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
"""
|
||||
Web component
|
||||
"""
|
||||
"""Web component."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
import pathlib
|
||||
from datetime import UTC, datetime
|
||||
from functools import wraps
|
||||
from typing import Any, Callable
|
||||
from typing import Callable
|
||||
|
||||
import yaml
|
||||
from flask import Blueprint, Response, render_template, request
|
||||
|
@ -18,22 +17,18 @@ from slinky import Slinky, random_string
|
|||
|
||||
slinky_webapp = Blueprint("webapp", __name__, template_folder="templates")
|
||||
|
||||
with open("config.yaml", encoding="utf-8-sig") as conffile:
|
||||
with pathlib.Path("config.yaml").open(encoding="utf-8-sig") as conffile:
|
||||
cfg = yaml.safe_load(conffile)
|
||||
|
||||
|
||||
class DelForm(FlaskForm):
|
||||
"""
|
||||
Delete form definition
|
||||
"""
|
||||
"""Delete form definition."""
|
||||
|
||||
delete = HiddenField("delete")
|
||||
|
||||
|
||||
class AddForm(FlaskForm):
|
||||
"""
|
||||
Add form definition
|
||||
"""
|
||||
"""Add form definition."""
|
||||
|
||||
shortcode = StringField(
|
||||
"Shortcode",
|
||||
|
@ -79,18 +74,18 @@ class AddForm(FlaskForm):
|
|||
|
||||
|
||||
def protect(func: Callable[..., Response]) -> Callable[..., Response]:
|
||||
"""
|
||||
Decorator that will protect the admin interface
|
||||
"""Protect the admin interface.
|
||||
|
||||
Args:
|
||||
func (Callable): Wrapped function
|
||||
|
||||
Returns:
|
||||
Callable: Function wrapper
|
||||
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def check_ip(*args: Any, **kwargs: Any) -> Response:
|
||||
def check_ip(*args: ..., **kwargs: ...) -> Response:
|
||||
remote_addr = request.remote_addr
|
||||
|
||||
if "x-forwarded-for" in request.headers:
|
||||
|
@ -99,10 +94,7 @@ def protect(func: Callable[..., Response]) -> Callable[..., Response]:
|
|||
if "x-real-ip" in request.headers:
|
||||
remote_addr = request.headers["x-real-ip"]
|
||||
|
||||
if (
|
||||
os.environ.get("FLASK_ENV", "") != "development"
|
||||
and remote_addr not in cfg["allowed_ips"]
|
||||
):
|
||||
if os.environ.get("FLASK_ENV", "") != "development" and remote_addr not in cfg["allowed_ips"]:
|
||||
logging.warning("Protected URL access attempt from %s", remote_addr)
|
||||
return Response("Not found", 404)
|
||||
return func(*args, **kwargs)
|
||||
|
@ -112,28 +104,32 @@ def protect(func: Callable[..., Response]) -> Callable[..., Response]:
|
|||
|
||||
@slinky_webapp.route("/<path:path>", strict_slashes=False)
|
||||
def try_path_as_shortcode(path: str) -> Response:
|
||||
"""
|
||||
Try the initial path as a shortcode, redirect if found
|
||||
"""Try the initial path as a shortcode, redirect if found.
|
||||
|
||||
Returns:
|
||||
Response: redirect if found, otherwise 404
|
||||
|
||||
"""
|
||||
path = path.strip("/")
|
||||
should_redirect = True
|
||||
slinky = Slinky(cfg["db"])
|
||||
shortcode = slinky.get_by_shortcode(path)
|
||||
|
||||
if shortcode.url:
|
||||
if shortcode.fixed_views == 0:
|
||||
logging.warning("Shortcode out of views")
|
||||
should_redirect = False
|
||||
elif shortcode.fixed_views > 0:
|
||||
slinky.remove_view(shortcode.id)
|
||||
if datetime.fromisoformat(shortcode.expiry) < datetime.now():
|
||||
if datetime.fromisoformat(shortcode.expiry).astimezone(UTC) < datetime.now(UTC):
|
||||
logging.warning("Shortcode expired")
|
||||
should_redirect = False
|
||||
|
||||
if should_redirect:
|
||||
return Response(
|
||||
"Redirecting...", status=302, headers={"location": shortcode.url}
|
||||
"Redirecting...",
|
||||
status=302,
|
||||
headers={"location": shortcode.url},
|
||||
)
|
||||
|
||||
return Response("Not found", 404)
|
||||
|
@ -142,11 +138,11 @@ def try_path_as_shortcode(path: str) -> Response:
|
|||
@slinky_webapp.route("/_/add", methods=["GET", "POST"])
|
||||
@protect
|
||||
def add() -> Response:
|
||||
"""
|
||||
Create and add a new shorturl
|
||||
"""Create and add a new shorturl.
|
||||
|
||||
Returns:
|
||||
Response: HTTP response
|
||||
|
||||
"""
|
||||
slinky = Slinky(cfg["db"])
|
||||
|
||||
|
@ -207,11 +203,11 @@ def add() -> Response:
|
|||
@slinky_webapp.route("/_/list", methods=["GET", "POST"])
|
||||
@protect
|
||||
def lister() -> Response:
|
||||
"""
|
||||
List the shortcodes, URLs, etc.
|
||||
"""List the shortcodes, URLs, etc.
|
||||
|
||||
Returns:
|
||||
Response: HTTP response
|
||||
|
||||
"""
|
||||
form = DelForm(meta={"csrf": False})
|
||||
slinky = Slinky(cfg["db"])
|
||||
|
@ -227,17 +223,17 @@ def lister() -> Response:
|
|||
|
||||
@slinky_webapp.route("/_/edit/<int:id>", methods=["GET", "POST"])
|
||||
@protect
|
||||
def edit(id: int) -> Response: # pylint: disable=invalid-name,redefined-builtin
|
||||
"""
|
||||
Edit the shortcode.
|
||||
def edit(shortcut_id: int) -> Response: # pylint: disable=invalid-name,redefined-builtin
|
||||
"""Edit the shortcode.
|
||||
|
||||
Returns:
|
||||
Response: HTTP response
|
||||
|
||||
"""
|
||||
form = DelForm(meta={"csrf": False})
|
||||
slinky = Slinky(cfg["db"])
|
||||
|
||||
logging.debug("Editing: %d", id)
|
||||
logging.debug("Editing: %d", shortcut_id)
|
||||
|
||||
if form.is_submitted():
|
||||
slinky.delete_by_shortcode(form.delete.data.strip())
|
||||
|
|
Loading…
Reference in a new issue