Logo-Upload: Magic-Bytes-Pruefung (PNG/JPEG/WebP), dynamischer Dateiname
This commit is contained in:
37
app.py
37
app.py
@@ -168,25 +168,44 @@ def save_config_route():
|
|||||||
|
|
||||||
@app.route('/upload_logo', methods=['POST'])
|
@app.route('/upload_logo', methods=['POST'])
|
||||||
def upload_logo():
|
def upload_logo():
|
||||||
"""Logo-Datei hochladen und in static/logo.png speichern."""
|
"""Logo-Datei hochladen, auf gueltiges Bild pruefen und speichern."""
|
||||||
if 'logo' not in request.files:
|
if 'logo' not in request.files:
|
||||||
return jsonify({'error': 'Keine Datei'}), 400
|
return jsonify({'error': 'Keine Datei'}), 400
|
||||||
file = request.files['logo']
|
file = request.files['logo']
|
||||||
if file.filename == '':
|
if file.filename == '':
|
||||||
return jsonify({'error': 'Keine Datei ausgewaehlt'}), 400
|
return jsonify({'error': 'Keine Datei ausgewaehlt'}), 400
|
||||||
file.save(LOGO_FILE)
|
|
||||||
|
raw = file.read()
|
||||||
|
# Magic-Bytes-Pruefung
|
||||||
|
if raw[:8] == b'\x89PNG\r\n\x1a\n':
|
||||||
|
ext = 'png'
|
||||||
|
elif raw[:2] in (b'\xff\xd8',):
|
||||||
|
ext = 'jpg'
|
||||||
|
elif raw[:4] == b'RIFF' and raw[8:12] == b'WEBP':
|
||||||
|
ext = 'webp'
|
||||||
|
else:
|
||||||
|
return jsonify({'error': 'Nur PNG, JPEG oder WebP erlaubt'}), 400
|
||||||
|
|
||||||
|
filename = f'logo.{ext}'
|
||||||
|
filepath = os.path.join('static', filename)
|
||||||
|
with open(filepath, 'wb') as f:
|
||||||
|
f.write(raw)
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
config['logo'] = 'logo.png'
|
config['logo'] = filename
|
||||||
save_config(config)
|
save_config(config)
|
||||||
return jsonify({'status': 'ok', 'filename': 'logo.png'})
|
return jsonify({'status': 'ok', 'filename': filename})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/delete_logo', methods=['POST'])
|
@app.route('/delete_logo', methods=['POST'])
|
||||||
def delete_logo():
|
def delete_logo():
|
||||||
"""Logo loeschen."""
|
"""Logo loeschen."""
|
||||||
if os.path.exists(LOGO_FILE):
|
|
||||||
os.remove(LOGO_FILE)
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
|
logo_name = config.get('logo', '')
|
||||||
|
if logo_name:
|
||||||
|
logo_path = os.path.join('static', logo_name)
|
||||||
|
if os.path.exists(logo_path):
|
||||||
|
os.remove(logo_path)
|
||||||
config['logo'] = ''
|
config['logo'] = ''
|
||||||
save_config(config)
|
save_config(config)
|
||||||
return jsonify({'status': 'ok'})
|
return jsonify({'status': 'ok'})
|
||||||
@@ -251,8 +270,10 @@ def invoice_pdf():
|
|||||||
pdf.cell(col_w, 4, " | ".join(contact_parts), ln=True)
|
pdf.cell(col_w, 4, " | ".join(contact_parts), ln=True)
|
||||||
|
|
||||||
# Logo rechts (Bild oder Text-Fallback)
|
# Logo rechts (Bild oder Text-Fallback)
|
||||||
if os.path.exists(LOGO_FILE):
|
logo_name = config.get('logo', '')
|
||||||
pdf.image(LOGO_FILE, x=col_right, y=15, w=60)
|
logo_path = os.path.join('static', logo_name) if logo_name else ''
|
||||||
|
if logo_name and os.path.exists(logo_path):
|
||||||
|
pdf.image(logo_path, x=col_right, y=15, w=60)
|
||||||
else:
|
else:
|
||||||
pdf.set_font("Arial", "B", 22)
|
pdf.set_font("Arial", "B", 22)
|
||||||
pdf.set_xy(col_right, 15)
|
pdf.set_xy(col_right, 15)
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{% if config.logo %}
|
{% if config.logo %}
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<img src="{{ url_for('static', filename='logo.png') }}" style="max-height:80px;max-width:240px" class="border rounded p-1">
|
<img src="{{ url_for('static', filename=config.logo) }}" style="max-height:80px;max-width:240px" class="border rounded p-1">
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-outline-danger btn-sm mb-2" onclick="deleteLogo()">
|
<button type="button" class="btn btn-outline-danger btn-sm mb-2" onclick="deleteLogo()">
|
||||||
<i class="ti ti-trash me-1"></i>Logo entfernen
|
<i class="ti ti-trash me-1"></i>Logo entfernen
|
||||||
|
|||||||
Reference in New Issue
Block a user