diff --git a/src/app.py b/src/app.py index d391bc3..05b244e 100644 --- a/src/app.py +++ b/src/app.py @@ -10,7 +10,7 @@ from flask_mail import Mail from utils import Config from utils import health_database_status, init_security_real_good from utils import storage -from views import ItemView, ProfileView, UploadView, IndexView +from views import ItemView, ProfileView, UploadView, IndexView, ContentView from models import db @@ -45,7 +45,7 @@ CORS(app) Mail(app) storage.init_app(app) -for view in [ItemView, ProfileView, UploadView, IndexView]: +for view in [ItemView, ProfileView, UploadView, IndexView, ContentView]: view.register(app, trailing_slash=False) health.add_check(health_database_status) diff --git a/src/views/__init__.py b/src/views/__init__.py index 22493e9..ab1ce67 100644 --- a/src/views/__init__.py +++ b/src/views/__init__.py @@ -2,3 +2,4 @@ from .profileview import ProfileView from .uploadview import UploadView from .itemview import ItemView from .indexview import IndexView +from .contentview import ContentView \ No newline at end of file diff --git a/src/views/contentview.py b/src/views/contentview.py new file mode 100644 index 0000000..602d544 --- /dev/null +++ b/src/views/contentview.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +import string +from flask import abort, Response, current_app +from flask_classful import FlaskView +from flask_security import login_required, current_user + +from utils import storage +from minio.error import NoSuchKey + +from models import db, Item, Purchase + + +class ContentView(FlaskView): + """ + This is just a simple content proxy with access control + """ + + def _stream_from_minio(self, bucket: str, id_: int, filename: str = None): + try: + data = storage.connection.get_object(bucket, str(id_)) + except NoSuchKey: + abort(404) + + headers = {} + if filename: + headers['Content-Disposition'] = f'attachment; filename="{filename}"' + + return Response(data.stream(), mimetype=data.headers['Content-type'], headers=headers) + + def preview(self, id_: int): + i = Item.query.get_or_404(id_) + + return self._stream_from_minio(current_app.config['MINIO_PREVIEW_BUCKET_NAME'], i.id) + + @login_required + def caff(self, id_: int): + p = Purchase.query.filter(db.and_(Purchase.purchaser_id == current_user.id, Purchase.item_id == id_)).first() + + if not p: + abort(403) + + allowed_chars = string.ascii_lowercase + string.ascii_uppercase + string.digits + filename = ''.join(filter(lambda x: x in allowed_chars, p.item.name)).lower() + + if not filename: + filename = str(p.item.id) + + filename += f'_{p.id}.caff' + + return self._stream_from_minio(current_app.config['MINIO_CAFF_BUCKET_NAME'], p.item.id, filename)