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/<token>/download gibt 403 bei upload_only - POST /share/<token>/upload erlaubt upload_only + write - GET /share/<token>/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) <noreply@anthropic.com>
This commit is contained in:
parent
116c33a7dc
commit
e1eb6a83ae
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
<div class="link-info">
|
||||
<code>{{ currentOrigin }}/share/{{ link.token }}</code>
|
||||
<small>
|
||||
{{ link.permission === 'write' ? 'Lesen+Schreiben' : 'Nur Lesen' }}
|
||||
{{ {read: 'Nur Lesen', write: 'Lesen+Schreiben', upload_only: 'Nur Upload'}[link.permission] || link.permission }}
|
||||
| {{ link.download_count }} Downloads
|
||||
<template v-if="link.expires_at"> | Bis {{ formatDate(link.expires_at) }}</template>
|
||||
<template v-if="link.has_password"> | Passwortgeschuetzt</template>
|
||||
|
|
@ -255,7 +255,11 @@ const selectedShareUser = ref(null)
|
|||
const shareUserPermission = ref('read')
|
||||
const userSearchResults = ref([])
|
||||
const userPermOptions = [{ label: 'Lesen', value: 'read' }, { label: 'Schreiben', value: 'write' }, { label: 'Admin', value: 'admin' }]
|
||||
const linkPermOptions = [{ label: 'Nur Lesen (Download)', value: 'read' }, { label: 'Lesen + Hochladen (nur Ordner)', value: 'write' }]
|
||||
const linkPermOptions = [
|
||||
{ label: 'Nur Lesen (Download)', value: 'read' },
|
||||
{ label: 'Lesen + Hochladen (nur Ordner)', value: 'write' },
|
||||
{ label: 'Nur Upload (Ordner, kein Einblick)', value: 'upload_only' },
|
||||
]
|
||||
const shareLinkPermission = ref('read')
|
||||
const currentOrigin = window.location.origin
|
||||
const shareLoading = ref(false)
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@
|
|||
</div>
|
||||
|
||||
<div v-else-if="fileInfo" class="share-info">
|
||||
<h2>{{ fileInfo.name }}</h2>
|
||||
<p class="file-size" v-if="fileInfo.size && !fileInfo.is_folder">{{ formatSize(fileInfo.size) }}</p>
|
||||
<Tag v-if="fileInfo.is_folder" value="Ordner" severity="info" />
|
||||
<h2 v-if="fileInfo.permission !== 'upload_only'">{{ fileInfo.name }}</h2>
|
||||
<h2 v-else>Datei-Upload</h2>
|
||||
<p class="file-size" v-if="fileInfo.size && !fileInfo.is_folder && fileInfo.permission !== 'upload_only'">{{ formatSize(fileInfo.size) }}</p>
|
||||
<Tag v-if="fileInfo.is_folder && fileInfo.permission !== 'upload_only'" value="Ordner" severity="info" />
|
||||
|
||||
<div v-if="fileInfo.has_password && !authenticated" class="password-form">
|
||||
<p>Diese Freigabe ist passwortgeschuetzt.</p>
|
||||
|
|
@ -28,8 +29,8 @@
|
|||
</div>
|
||||
|
||||
<div v-else class="actions-section">
|
||||
<!-- Download (files only) -->
|
||||
<div v-if="!fileInfo.is_folder" class="action-block">
|
||||
<!-- Download (files only, not upload_only) -->
|
||||
<div v-if="!fileInfo.is_folder && fileInfo.download_allowed" class="action-block">
|
||||
<Button
|
||||
label="Herunterladen"
|
||||
icon="pi pi-download"
|
||||
|
|
@ -38,6 +39,11 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<!-- Upload-only hint -->
|
||||
<p v-if="fileInfo.permission === 'upload_only'" class="upload-only-hint">
|
||||
Dieser Link erlaubt nur das Hochladen von Dateien.
|
||||
</p>
|
||||
|
||||
<!-- Upload (folders only) -->
|
||||
<div v-if="fileInfo.upload_allowed" class="action-block">
|
||||
<div class="upload-area"
|
||||
|
|
@ -216,4 +222,5 @@ onMounted(loadInfo)
|
|||
.upload-area p { margin: 0.5rem 0; color: var(--p-text-muted-color); font-size: 0.9rem; }
|
||||
.upload-progress { margin-top: 1rem; }
|
||||
.upload-progress p { font-size: 0.85rem; color: var(--p-text-muted-color); margin-top: 0.5rem; }
|
||||
.upload-only-hint { color: var(--p-text-muted-color); font-size: 0.9rem; margin-bottom: 1rem; }
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue