improvements in processing the library item uploading
Release Inventarsystem / release-docker (push) Successful in 2m16s

This commit is contained in:
2026-08-16 22:50:17 +02:00
parent 8783f97a09
commit c62b2b553d
2 changed files with 110 additions and 122 deletions
+39 -38
View File
@@ -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():