improvements in processing the library item uploading
Release Inventarsystem / release-docker (push) Successful in 2m16s
Release Inventarsystem / release-docker (push) Successful in 2m16s
This commit is contained in:
+39
-38
@@ -9961,25 +9961,28 @@ def fetch_book_info(isbn):
|
||||
app.logger.error(f"Error fetching book data: {e}")
|
||||
return jsonify({"error": f"Failed to fetch book information"}), 500
|
||||
|
||||
|
||||
@app.route('/download_book_cover', methods=['POST'])
|
||||
def download_book_cover():
|
||||
"""
|
||||
API endpoint to download and save a book cover image from URL
|
||||
API endpoint to download a book cover image from URL
|
||||
and save it directly to MongoDB GridFS.
|
||||
"""
|
||||
if 'username' not in session:
|
||||
return jsonify({"error": "Not authorized"}), 403
|
||||
|
||||
current_permissions = us.get_effective_permissions(session['username'])
|
||||
|
||||
|
||||
if not current_permissions['actions'].get('can_insert', False):
|
||||
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
|
||||
return redirect(url_for('library_view'))
|
||||
return jsonify({"error": "Ihnen fehlen die nötigen Berechtigungen."}), 403
|
||||
|
||||
if not cfg.MODULES.is_enabled('library'):
|
||||
return jsonify({"error": "Bibliotheks-Modul ist deaktiviert."}), 403
|
||||
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
image_url = data.get('url')
|
||||
|
||||
|
||||
if not image_url:
|
||||
return jsonify({"error": "No image URL provided"}), 400
|
||||
|
||||
@@ -9987,71 +9990,69 @@ def download_book_cover():
|
||||
if parsed_url.scheme != 'https' or not parsed_url.netloc:
|
||||
return jsonify({"error": "Only public HTTPS URLs are allowed"}), 400
|
||||
|
||||
# 2. SSRF Protection: Strict Allowlist Check
|
||||
# if parsed_url.netloc not in ALLOWED_COVER_DOMAINS:
|
||||
# return jsonify({"error": "Target host is not an allowed book cover provider"}), 403
|
||||
|
||||
# Download the image (allow_redirects=False prevents redirecting to internal IPs)
|
||||
response = requests.get(image_url, stream=True, timeout=10, allow_redirects=False)
|
||||
|
||||
|
||||
if response.status_code != 200:
|
||||
return jsonify({"error": f"Failed to download image: Status {response.status_code}"}), 400
|
||||
|
||||
# Check content type
|
||||
|
||||
content_type = response.headers.get('content-type', '')
|
||||
allowed_types = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']
|
||||
|
||||
|
||||
if not any(allowed_type in content_type.lower() for allowed_type in allowed_types):
|
||||
return jsonify({
|
||||
"error": f"Nicht unterstütztes Bildformat: {content_type}. Erlaubte Formate: JPG, JPEG, PNG, GIF"
|
||||
}), 400
|
||||
|
||||
# Check content length header
|
||||
content_length = response.headers.get('Content-Length')
|
||||
if content_length:
|
||||
try:
|
||||
if int(content_length) > 5 * 1024 * 1024:
|
||||
return jsonify({"error": "Image is too large"}), 413
|
||||
return jsonify({"error": "Image is too large (max 5MB)"}), 413
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Generate a fully unique filename
|
||||
|
||||
unique_id = str(uuid.uuid4())
|
||||
timestamp = time.strftime("%Y%m%d%H%M%S")
|
||||
|
||||
|
||||
extension = '.jpg' # default
|
||||
if 'image/png' in content_type.lower():
|
||||
extension = '.png'
|
||||
elif 'image/gif' in content_type.lower():
|
||||
extension = '.gif'
|
||||
|
||||
|
||||
filename = f"book_cover_{unique_id}_{timestamp}{extension}"
|
||||
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||
|
||||
# Save image in chunks (prevents memory exhaustion and enforces size limits)
|
||||
with open(filepath, 'wb') as f:
|
||||
written = 0
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
written += len(chunk)
|
||||
if written > 5 * 1024 * 1024:
|
||||
# Clean up the partial file before aborting
|
||||
os.remove(filepath)
|
||||
return jsonify({"error": "Image is too large"}), 413
|
||||
f.write(chunk)
|
||||
|
||||
|
||||
image_data = io.BytesIO()
|
||||
written = 0
|
||||
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
written += len(chunk)
|
||||
if written > 5 * 1024 * 1024:
|
||||
return jsonify({"error": "Image is too large (max 5MB)"}), 413
|
||||
image_data.write(chunk)
|
||||
|
||||
image_data.seek(0)
|
||||
|
||||
fs = get_gridfs()
|
||||
fs.put(
|
||||
image_data,
|
||||
filename=filename,
|
||||
content_type=content_type
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"filename": filename,
|
||||
"message": "Image downloaded successfully"
|
||||
"message": "Image downloaded and stored directly in database"
|
||||
})
|
||||
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
app.logger.error(f"Network error downloading book cover: {e}")
|
||||
return jsonify({"error": "Netzwerkfehler beim Herunterladen des Bildes."}), 500
|
||||
except Exception as e:
|
||||
app.logger.error(f"Error downloading book cover: {e}")
|
||||
# Fixed syntax here: Removed the injected HTML that was appended to this line
|
||||
return jsonify({"error": f"Failed to download image"}), 500
|
||||
return jsonify({"error": "Failed to download image"}), 500
|
||||
|
||||
"""
|
||||
@app.route('/proxy_image')
|
||||
def proxy_image():
|
||||
|
||||
Reference in New Issue
Block a user