From e1eb6a83ae0a4e9253467a59ddd98d9678850636 Mon Sep 17 00:00:00 2001 From: Stefan Hacker Date: Sat, 11 Apr 2026 18:44:02 +0200 Subject: [PATCH] feat: Share-Link Typ 'Nur Upload' (upload_only) Dritter Link-Typ neben read und write: - upload_only: Nur Dateien hochladen, kein Download, kein Ordnerinhalt sichtbar, Ordnername wird nicht angezeigt Backend-Absicherung: - GET /share//download gibt 403 bei upload_only - POST /share//upload erlaubt upload_only + write - GET /share//info gibt download_allowed zurueck Frontend Share-Dialog: - Drei Optionen: Nur Lesen / Lesen+Hochladen / Nur Upload - Bestehende Links zeigen Typ an Frontend ShareView: - upload_only: Zeigt nur Upload-Zone, kein Dateiname, kein Download - Hinweistext 'Dieser Link erlaubt nur das Hochladen von Dateien' Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/app/api/files.py | 16 ++++++++++------ frontend/src/views/FilesView.vue | 8 ++++++-- frontend/src/views/ShareView.vue | 17 ++++++++++++----- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/backend/app/api/files.py b/backend/app/api/files.py index b3ac8ab..6e15ecc 100644 --- a/backend/app/api/files.py +++ b/backend/app/api/files.py @@ -421,8 +421,8 @@ def create_share_link(file_id): max_downloads = data.get('max_downloads') permission = data.get('permission', 'read') - if permission not in ('read', 'write'): - return jsonify({'error': 'Berechtigung muss "read" oder "write" sein'}), 400 + if permission not in ('read', 'write', 'upload_only'): + return jsonify({'error': 'Berechtigung muss "read", "write" oder "upload_only" sein'}), 400 token = secrets.token_urlsafe(32) password_hash = None @@ -498,7 +498,8 @@ def share_info(token): 'mime_type': f.mime_type, 'has_password': bool(link.password_hash), 'permission': link.permission, - 'upload_allowed': f.is_folder and link.permission == 'write', + 'upload_allowed': f.is_folder and link.permission in ('write', 'upload_only'), + 'download_allowed': link.permission in ('read', 'write'), }), 200 @@ -531,6 +532,9 @@ def share_download(token): if not link: return jsonify({'error': 'Link nicht gefunden'}), 404 + if link.permission == 'upload_only': + return jsonify({'error': 'Dieser Link erlaubt nur Upload, keinen Download'}), 403 + if link.is_expired(): return jsonify({'error': 'Link abgelaufen'}), 410 @@ -576,9 +580,9 @@ def share_upload(token): if link.is_expired(): return jsonify({'error': 'Link abgelaufen'}), 410 - # Check write permission - if link.permission != 'write': - return jsonify({'error': 'Dieser Link erlaubt nur Lesen'}), 403 + # Check write/upload permission + if link.permission not in ('write', 'upload_only'): + return jsonify({'error': 'Dieser Link erlaubt keinen Upload'}), 403 # Check password if set if link.password_hash: diff --git a/frontend/src/views/FilesView.vue b/frontend/src/views/FilesView.vue index 4b44523..24fb924 100644 --- a/frontend/src/views/FilesView.vue +++ b/frontend/src/views/FilesView.vue @@ -184,7 +184,7 @@