diff --git a/poetry.lock b/poetry.lock index 41db64b..0617181 100644 --- a/poetry.lock +++ b/poetry.lock @@ -232,14 +232,14 @@ tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"] [[package]] name = "flask" -version = "2.3.3" +version = "3.0.0" description = "A simple framework for building complex web applications." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "flask-2.3.3-py3-none-any.whl", hash = "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"}, - {file = "flask-2.3.3.tar.gz", hash = "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc"}, + {file = "flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638"}, + {file = "flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58"}, ] [package.dependencies] @@ -247,7 +247,7 @@ blinker = ">=1.6.2" click = ">=8.1.3" itsdangerous = ">=2.1.2" Jinja2 = ">=3.1.2" -Werkzeug = ">=2.3.7" +Werkzeug = ">=3.0.0" [package.extras] async = ["asgiref (>=3.2)"] @@ -515,14 +515,14 @@ files = [ [[package]] name = "werkzeug" -version = "2.3.7" +version = "3.0.0" description = "The comprehensive WSGI web application library." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "werkzeug-2.3.7-py3-none-any.whl", hash = "sha256:effc12dba7f3bd72e605ce49807bbe692bd729c3bb122a3b91747a6ae77df528"}, - {file = "werkzeug-2.3.7.tar.gz", hash = "sha256:2b8c0e447b4b9dbcc85dd97b6eeb4dcbaf6c8b6c3be0bd654e25553e0a2157d8"}, + {file = "werkzeug-3.0.0-py3-none-any.whl", hash = "sha256:cbb2600f7eabe51dbc0502f58be0b3e1b96b893b05695ea2b35b43d4de2d9962"}, + {file = "werkzeug-3.0.0.tar.gz", hash = "sha256:3ffff4dcc32db52ef3cc94dff3000a3c2846890f3a5a51800a27b909c5e770f0"}, ] [package.dependencies] @@ -534,4 +534,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "a8a740d71fe53f39a3e5d8ff069560abfb1e99db162b77892eb16cd0b34bda9d" +content-hash = "f0bb5dc5c2086b3834b9e7c46313f161aa0dab256d158209eb616101c4010d69" diff --git a/pyles/blueprints/api/files.py b/pyles/blueprints/api/files.py index 3beb20b..a82058e 100644 --- a/pyles/blueprints/api/files.py +++ b/pyles/blueprints/api/files.py @@ -1,9 +1,11 @@ # SPDX-License-Identifier: Apache-2.0 +from datetime import datetime + from flask import Blueprint, g, request, jsonify from werkzeug.utils import secure_filename -from pyles.db import File +from pyles.db import db, File, Tag, FileTag from pyles.files import upload_file from pyles.user import token_required from pyles.settings import BASE_URL @@ -11,25 +13,49 @@ from pyles.settings import BASE_URL bp = Blueprint("files_api", __name__) -@bp.post("/upload") +@bp.post("/api/upload") @token_required def upload(): file = request.files.get("file") if not file: return jsonify({"error": "Missing file"}), 400 + expires = None + tags = [] + try: + exp = request.args.get("expires", type=int) + expires = datetime.fromtimestamp(exp) if exp else None + except: + pass + + if raw_tags := request.args.get("tags"): + tags = [int(raw) for raw in raw_tags.split(",")] + tags: list[Tag] = Tag.select().where(Tag.id.in_(tags), Tag.user_id == g.user.id) hash, content_type = upload_file(file) - db_file: File = File.create( - user=g.user, - filename=secure_filename(file.filename), - hash=hash, - content_type=content_type, - ) + + with db.atomic(): + db_file: File = File.create( + user=g.user, + filename=secure_filename(file.filename), + hash=hash, + content_type=content_type, + expires=expires, + ) + + if len(tags) > 0: + FileTag.bulk_create(FileTag(file=db_file, tag=tag) for tag in tags) return jsonify( { "id": db_file.id, "hash": db_file.hash, "url": f"{BASE_URL}/{db_file.path}", + "tags": [{"id": tag.id, "name": tag.name} for tag in tags], } ) + + +@bp.patch("/api/files/") +@token_required +def update_file(): + ... diff --git a/pyles/user.py b/pyles/user.py index 3a3ffff..46e78b7 100644 --- a/pyles/user.py +++ b/pyles/user.py @@ -3,7 +3,7 @@ from functools import wraps from itsdangerous.url_safe import URLSafeSerializer -from flask import g, request, redirect, url_for, jsonify +from flask import g, request, redirect, url_for, jsonify, session from pyles.settings import SECRET_KEY from pyles.db import User @@ -27,3 +27,24 @@ def token_required(f): return f(*args, **kwargs) return inner + +def login_required(f): + @wraps(f) + def inner(*args, **kwargs): + token = session.get("token", None) + if not token: + return redirect(url_for("index")) + + _, id = URLSafeSerializer(SECRET_KEY).loads_unsafe(token) + u: User = User.get_or_none(id=id) + if u is None: + session.pop("token", None) + return redirect(url_for("index")) + + if not u.verify_token(token): + session.pop("token", None) + return redirect(url_for("index")) + g.user = u + return f(*args, **kwargs) + + return inner diff --git a/pyproject.toml b/pyproject.toml index 137e895..2b6c8c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.11" -flask = "^2.3.3" +flask = "^3.0.0" itsdangerous = "^2.1.2" environs = "^9.5.0" gunicorn = "^21.2.0"