diff --git a/backend/app/api/files.py b/backend/app/api/files.py index 979b2ba..9866a0d 100644 --- a/backend/app/api/files.py +++ b/backend/app/api/files.py @@ -665,6 +665,11 @@ def set_permission(file_id): db.session.commit() + # SSE: notify target user (they just got/updated access) + owner + other + # share recipients so everyone's file list refreshes. + notify_file_change(f.owner_id, f.id, 'permission', + shared_with=[target.id, *_share_recipients(f)]) + # Notify user via email if is_new: try: @@ -692,8 +697,13 @@ def remove_permission(file_id, perm_id): if not is_owner and perm.granted_by != user.id: return jsonify({'error': 'Du kannst nur selbst erstellte Freigaben entfernen'}), 403 + target_user_id = perm.user_id db.session.delete(perm) db.session.commit() + + notify_file_change(f.owner_id, f.id, 'permission', + shared_with=[target_user_id, *_share_recipients(f)]) + return jsonify({'message': 'Berechtigung entfernt'}), 200 diff --git a/frontend/src/stores/files.js b/frontend/src/stores/files.js index 89e8dee..bf8d24b 100644 --- a/frontend/src/stores/files.js +++ b/frontend/src/stores/files.js @@ -17,6 +17,13 @@ export const useFilesStore = defineStore('files', () => { const response = await apiClient.get('/files', { params }) files.value = response.data.files breadcrumb.value = response.data.breadcrumb + } catch (err) { + // Let the caller handle access/deletion errors - just clear the list + if (err.response && (err.response.status === 403 || err.response.status === 404)) { + files.value = [] + breadcrumb.value = [] + } + throw err } finally { loading.value = false } diff --git a/frontend/src/views/FilesView.vue b/frontend/src/views/FilesView.vue index e1769c4..ef22f1e 100644 --- a/frontend/src/views/FilesView.vue +++ b/frontend/src/views/FilesView.vue @@ -764,8 +764,26 @@ async function doDelete() { } } +async function safeLoadCurrentFolder() { + try { + await filesStore.loadFiles(currentParentId()) + } catch (err) { + const status = err.response?.status + if (status === 403 || status === 404) { + toast.add({ + severity: 'warn', + summary: 'Kein Zugriff', + detail: 'Dieser Ordner wurde geloescht oder die Freigabe wurde entfernt.', + life: 5000, + }) + // Redirect to root after short delay so user sees the toast + setTimeout(() => router.push('/files'), 600) + } + } +} + watch(() => route.params.folderId, () => { - filesStore.loadFiles(currentParentId()) + safeLoadCurrentFolder() }) // Live updates: subscribe to server-sent events so that lock changes / @@ -778,12 +796,12 @@ function scheduleReload() { if (reloadDebounce) return reloadDebounce = setTimeout(() => { reloadDebounce = null - filesStore.loadFiles(currentParentId()) + safeLoadCurrentFolder() }, 300) } onMounted(() => { - filesStore.loadFiles(currentParentId()) + safeLoadCurrentFolder() if (auth.accessToken) { const url = `/api/sync/events?token=${encodeURIComponent(auth.accessToken)}`