feat: Web-GUI Live-Refresh via SSE

FilesView abonniert beim Mount die SSE-Events des Backends. Lock/
Unlock, Create, Update oder Delete durch andere Clients loest einen
debounced Reload der aktuellen Ordner-Ansicht aus. EventSource
reconnected automatisch; wird beim Unmount sauber geschlossen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker 2026-04-12 10:21:00 +02:00
parent b33e66cad9
commit 28fb1c47c2
1 changed files with 32 additions and 1 deletions

View File

@ -241,7 +241,7 @@
</template> </template>
<script setup> <script setup>
import { ref, watch, onMounted } from 'vue' import { ref, watch, onMounted, onUnmounted } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useAuthStore } from '../stores/auth' import { useAuthStore } from '../stores/auth'
import { useFilesStore } from '../stores/files' import { useFilesStore } from '../stores/files'
@ -718,8 +718,39 @@ watch(() => route.params.folderId, () => {
filesStore.loadFiles(currentParentId()) filesStore.loadFiles(currentParentId())
}) })
// Live updates: subscribe to server-sent events so that lock changes /
// uploads / deletions by other users or clients refresh the current
// folder automatically.
let eventSource = null
let reloadDebounce = null
function scheduleReload() {
if (reloadDebounce) return
reloadDebounce = setTimeout(() => {
reloadDebounce = null
filesStore.loadFiles(currentParentId())
}, 300)
}
onMounted(() => { onMounted(() => {
filesStore.loadFiles(currentParentId()) filesStore.loadFiles(currentParentId())
if (auth.accessToken) {
const url = `/api/sync/events?token=${encodeURIComponent(auth.accessToken)}`
try {
eventSource = new EventSource(url)
// Lock/unlock/create/update/delete all warrant a refresh of the list
const handler = () => scheduleReload()
eventSource.addEventListener('file', handler)
eventSource.addEventListener('message', handler)
eventSource.onerror = () => { /* browser auto-reconnects */ }
} catch { /* SSE not available - fall back to manual refresh */ }
}
})
onUnmounted(() => {
if (reloadDebounce) { clearTimeout(reloadDebounce); reloadDebounce = null }
if (eventSource) { eventSource.close(); eventSource = null }
}) })
</script> </script>