diff --git a/Website/main.py b/Website/main.py index 0c89d95..179d1e8 100644 --- a/Website/main.py +++ b/Website/main.py @@ -43,6 +43,7 @@ def set_security_headers(response): BASE_DIR = os.path.dirname(os.path.abspath(__file__)) INVOICE_UPLOAD_DIR = os.path.join(BASE_DIR, "static", "uploads", "invoices") +TUTORIAL_UPLOAD_DIR = os.path.join(BASE_DIR, "static", "uploads", "tutorials") MONGO_URI = os.environ.get("MONGO_URI", "mongodb://localhost:27017") MONGO_DB_NAME = os.environ.get("MONGO_DB_NAME", "Invario_Website") INSTANCE_REPO_URL = os.environ.get("INSTANCE_REPO_URL", "https://github.com/AIIrondev/legendary-octo-garbanzo") @@ -106,6 +107,13 @@ def _is_allowed_image_filename(filename: str) -> bool: return lowered.endswith(".jpg") or lowered.endswith(".jpeg") or lowered.endswith(".png") or lowered.endswith(".webp") +def _is_allowed_tutorial_video_filename(filename: str) -> bool: + if not filename: + return False + lowered = filename.lower() + return lowered.endswith(".mp4") or lowered.endswith(".webm") or lowered.endswith(".ogv") or lowered.endswith(".ogg") or lowered.endswith(".m4v") + + def _save_invoice_pdf(file_obj, invoice_number: str) -> str | None: if not file_obj or not file_obj.filename: return None @@ -119,6 +127,20 @@ def _save_invoice_pdf(file_obj, invoice_number: str) -> str | None: file_obj.save(target) return f"uploads/invoices/{unique_name}" + +def _save_tutorial_video_file(file_obj, title: str) -> str | None: + if not file_obj or not file_obj.filename: + return None + original_name = secure_filename(file_obj.filename) + if not _is_allowed_tutorial_video_filename(original_name): + return None + os.makedirs(TUTORIAL_UPLOAD_DIR, exist_ok=True) + safe_title = secure_filename(title or "tutorial") + unique_name = f"{datetime.utcnow().strftime('%Y%m%d%H%M%S')}_{safe_title}_{original_name}" + target = os.path.join(TUTORIAL_UPLOAD_DIR, unique_name) + file_obj.save(target) + return f"/static/uploads/tutorials/{unique_name}" + def _get_mongo_client() -> MongoClient: global _MONGO_CLIENT if _MONGO_CLIENT is not None: @@ -2564,6 +2586,65 @@ def append_video(): return redirect(url_for("admin_blog")) +@app.route('/admin/tutorials/upload', methods=['GET', 'POST']) +@admin_required +def admin_tutorial_upload(): + if request.method == 'POST': + title = _sanitize_text(request.form.get("title") or "", 200) + description = _sanitize_text(request.form.get("description") or "", 500) + duration = _sanitize_text(request.form.get("duration") or "", 20) + category = _sanitize_text(request.form.get("category") or "", 50) + thumbnail_url = _sanitize_text(request.form.get("thumbnail_url") or "", 300) + uploaded_file = request.files.get("video_file") + + if not title: + flash("Bitte einen Titel fuer das Tutorial eingeben.", "error") + return redirect(url_for("admin_tutorial_upload")) + + saved_video_path = _save_tutorial_video_file(uploaded_file, title) + if not saved_video_path: + flash("Bitte eine gueltige Videodatei hochladen (.mp4, .webm, .ogv, .ogg, .m4v).", "error") + return redirect(url_for("admin_tutorial_upload")) + + client = None + try: + client, col = _get_collection("tutorials") + col.insert_one( + { + "title": title, + "description": description, + "video_url": saved_video_path, + "thumbnail_url": thumbnail_url, + "duration": duration, + "category": category, + "published": True, + "created_at": datetime.utcnow().isoformat(timespec="seconds") + "Z", + "source": "upload", + } + ) + flash("Tutorial-Video wurde hochgeladen.", "success") + except PyMongoError: + flash("Tutorial-Video konnte nicht gespeichert werden.", "error") + finally: + if client: + client.close() + + return redirect(url_for("admin_tutorial_upload")) + + tutorials = [] + client = None + try: + client, col = _get_collection("tutorials") + tutorials = list(col.find({"source": "upload"}, {"_id": 1, "title": 1, "description": 1, "video_url": 1, "thumbnail_url": 1, "duration": 1, "category": 1, "created_at": 1}).sort("created_at", -1)) + except PyMongoError: + flash("Upload-Uebersicht konnte nicht geladen werden.", "error") + finally: + if client: + client.close() + + return render_template("admin_tutorial_upload.html", tutorials=tutorials) + + @app.route('/chat', methods=['GET', 'POST']) @login_required def user_chat(): diff --git a/Website/templates/admin_blog.html b/Website/templates/admin_blog.html index 74d259c..ac4dc43 100644 --- a/Website/templates/admin_blog.html +++ b/Website/templates/admin_blog.html @@ -216,7 +216,10 @@ {% endblock %} {% block content %} -