mirror of
https://github.com/MrUnknownDE/medien-dl.git
synced 2026-07-14 20:30:47 +02:00
add rate-limit and ssrf-fix
This commit is contained in:
@@ -14,6 +14,8 @@ import random
|
|||||||
import string
|
import string
|
||||||
import re
|
import re
|
||||||
from flask import Flask, render_template, request, jsonify, Response, copy_current_request_context, make_response
|
from flask import Flask, render_template, request, jsonify, Response, copy_current_request_context, make_response
|
||||||
|
from flask_limiter import Limiter
|
||||||
|
from flask_limiter.util import get_remote_address
|
||||||
import time
|
import time
|
||||||
# import queue # ALT
|
# import queue # ALT
|
||||||
import multiprocessing # NEU: Für prozessübergreifende Queue
|
import multiprocessing # NEU: Für prozessübergreifende Queue
|
||||||
@@ -94,6 +96,13 @@ logging.info(f"Maximale Worker-Threads (für Hintergrundverarbeitung): {MAX_WORK
|
|||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = os.urandom(24)
|
app.secret_key = os.urandom(24)
|
||||||
|
|
||||||
|
limiter = Limiter(
|
||||||
|
get_remote_address,
|
||||||
|
app=app,
|
||||||
|
default_limits=[],
|
||||||
|
storage_uri="memory://",
|
||||||
|
)
|
||||||
|
|
||||||
# --- Globaler Status -> Job-Status Speicher ---
|
# --- Globaler Status -> Job-Status Speicher ---
|
||||||
manager = multiprocessing.Manager()
|
manager = multiprocessing.Manager()
|
||||||
job_statuses = manager.dict()
|
job_statuses = manager.dict()
|
||||||
@@ -116,6 +125,9 @@ def generate_s3_object_name(extension):
|
|||||||
def strip_ansi_codes(text):
|
def strip_ansi_codes(text):
|
||||||
return ANSI_ESCAPE_REGEX.sub('', text)
|
return ANSI_ESCAPE_REGEX.sub('', text)
|
||||||
|
|
||||||
|
def _domain_matches(domain, expected):
|
||||||
|
return domain == expected or domain.endswith("." + expected)
|
||||||
|
|
||||||
def format_size(size_bytes):
|
def format_size(size_bytes):
|
||||||
if size_bytes == 0: return "0 B"
|
if size_bytes == 0: return "0 B"
|
||||||
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
|
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
|
||||||
@@ -760,8 +772,8 @@ def sitemap_xml():
|
|||||||
return resp
|
return resp
|
||||||
|
|
||||||
@app.route('/start_download', methods=['POST'])
|
@app.route('/start_download', methods=['POST'])
|
||||||
|
@limiter.limit("5 per minute")
|
||||||
def start_download():
|
def start_download():
|
||||||
# (Logik unverändert)
|
|
||||||
url = request.form.get('url'); platform = request.form.get('platform', DEFAULT_PLATFORM)
|
url = request.form.get('url'); platform = request.form.get('platform', DEFAULT_PLATFORM)
|
||||||
yt_format = request.form.get('yt_format', DEFAULT_YT_FORMAT); mp3_bitrate = request.form.get('mp3_bitrate', DEFAULT_MP3_BITRATE)
|
yt_format = request.form.get('yt_format', DEFAULT_YT_FORMAT); mp3_bitrate = request.form.get('mp3_bitrate', DEFAULT_MP3_BITRATE)
|
||||||
mp4_quality = request.form.get('mp4_quality', DEFAULT_MP4_QUALITY)
|
mp4_quality = request.form.get('mp4_quality', DEFAULT_MP4_QUALITY)
|
||||||
@@ -770,13 +782,13 @@ def start_download():
|
|||||||
is_valid_url = False
|
is_valid_url = False
|
||||||
if url and url.startswith(("http://", "https://")):
|
if url and url.startswith(("http://", "https://")):
|
||||||
parsed_url = urllib.parse.urlparse(url)
|
parsed_url = urllib.parse.urlparse(url)
|
||||||
domain = parsed_url.netloc.lower()
|
domain = parsed_url.netloc.lower().split(":")[0] # Port abschneiden
|
||||||
path = parsed_url.path.lower()
|
path = parsed_url.path.lower()
|
||||||
if platform == "SoundCloud" and "soundcloud.com" in domain: is_valid_url = True
|
if platform == "SoundCloud" and _domain_matches(domain, "soundcloud.com"): is_valid_url = True
|
||||||
elif platform == "YouTube" and ("youtube.com" in domain or "youtu.be" in domain): is_valid_url = True
|
elif platform == "YouTube" and (_domain_matches(domain, "youtube.com") or _domain_matches(domain, "youtu.be")): is_valid_url = True
|
||||||
elif platform == "TikTok" and "tiktok.com" in domain: is_valid_url = True
|
elif platform == "TikTok" and _domain_matches(domain, "tiktok.com"): is_valid_url = True
|
||||||
elif platform == "Instagram" and "instagram.com" in domain and ("/reel/" in path or "/p/" in path): is_valid_url = True
|
elif platform == "Instagram" and _domain_matches(domain, "instagram.com") and ("/reel/" in path or "/p/" in path): is_valid_url = True
|
||||||
elif platform == "Twitter" and ("twitter.com" in domain or "x.com" in domain) and "/status/" in path: is_valid_url = True
|
elif platform == "Twitter" and (_domain_matches(domain, "twitter.com") or _domain_matches(domain, "x.com")) and "/status/" in path: is_valid_url = True
|
||||||
elif platform not in SUPPORTED_PLATFORMS:
|
elif platform not in SUPPORTED_PLATFORMS:
|
||||||
logging.warning(f"Unbekannte Plattform '{platform}' angegeben, versuche trotzdem mit URL '{url}'")
|
logging.warning(f"Unbekannte Plattform '{platform}' angegeben, versuche trotzdem mit URL '{url}'")
|
||||||
is_valid_url = True
|
is_valid_url = True
|
||||||
@@ -913,6 +925,10 @@ def start_background_threads():
|
|||||||
_threads_started_globally = True
|
_threads_started_globally = True
|
||||||
logging.info(f"Hintergrund-Threads global gestartet ({len(_background_threads)} Threads).")
|
logging.info(f"Hintergrund-Threads global gestartet ({len(_background_threads)} Threads).")
|
||||||
|
|
||||||
|
@app.errorhandler(429)
|
||||||
|
def ratelimit_handler(e):
|
||||||
|
return jsonify({"error": "Zu viele Anfragen. Bitte warte eine Minute."}), 429
|
||||||
|
|
||||||
start_background_threads()
|
start_background_threads()
|
||||||
|
|
||||||
# --- Hauptprogramm (nur für lokale Entwicklung mit `python app.py`) ---
|
# --- Hauptprogramm (nur für lokale Entwicklung mit `python app.py`) ---
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ Flask>=3.1.0
|
|||||||
python-dotenv>=1.0.0
|
python-dotenv>=1.0.0
|
||||||
yt-dlp
|
yt-dlp
|
||||||
boto3>=1.35.0
|
boto3>=1.35.0
|
||||||
|
flask-limiter>=3.5.0
|
||||||
# Optional, aber nützlich für Produktion:
|
# Optional, aber nützlich für Produktion:
|
||||||
gunicorn
|
gunicorn
|
||||||
Reference in New Issue
Block a user