feat(communication): federate tenant collaboration with signed delivery

This commit is contained in:
2026-09-30 14:48:05 +02:00
parent ea641bb4d0
commit 7b5696eda4
16 changed files with 914 additions and 14 deletions

View File

@@ -8,6 +8,7 @@ const api = async (path, options = {}) => $fetch(path, {
baseURL: config.public.apiBase, credentials: 'omit', ...options,
headers: { ...options.headers, ...(token.value ? { Authorization: `Bearer ${token.value}` } : {}) }
})
const loginUrl = computed(() => `${import.meta.client ? window.location.origin : ''}/guest?workspace=${workspaceId.value}`)
const message = e => e?.data?.message || e?.data?.error || 'Die Aktion konnte nicht ausgeführt werden.'
function expire() { token.value = ''; rooms.value = []; selected.value = ''; invitation.value = null; sessionStorage.removeItem('fedeo-guest-session') }
async function run(fn) { busy.value = true; error.value = ''; try { await fn() } catch (e) { error.value = message(e) } finally { busy.value = false } }
@@ -38,6 +39,7 @@ async function accept() {
await run(async () => {
const result = await api(`/guest/api/invitations/${invitation.value.id}/accept`, { method: 'POST', body: { token: invitationToken.value } })
invitation.value = null; invitationToken.value = ''; sessionStorage.removeItem('fedeo-guest-invite')
history.replaceState(null, '', `/guest?workspace=${workspaceId.value}`)
await refresh(); selected.value = result.roomId
})
}
@@ -47,8 +49,8 @@ onMounted(async () => {
const fragment = new URLSearchParams(window.location.hash.slice(1))
invitationToken.value = fragment.get('invite') || sessionStorage.getItem('fedeo-guest-invite') || ''
if (invitationToken.value) sessionStorage.setItem('fedeo-guest-invite', invitationToken.value)
history.replaceState(null, '', window.location.pathname)
workspaceId.value = localStorage.getItem('fedeo-guest-workspace') || ''
workspaceId.value = new URLSearchParams(window.location.search).get('workspace') || localStorage.getItem('fedeo-guest-workspace') || ''
history.replaceState(null, '', workspaceId.value ? `/guest?workspace=${encodeURIComponent(workspaceId.value)}` : '/guest')
token.value = sessionStorage.getItem('fedeo-guest-session') || ''
if (token.value) {
await run(async () => {
@@ -84,6 +86,7 @@ onBeforeUnmount(() => clearInterval(timer))
<p class="text-sm">Dein Zugriff gilt für diesen Unterraum. Du siehst die dort eingeladenen Teilnehmer und Nachrichten ab deinem Beitritt.</p>
<UButton :loading="busy" @click="accept">Einladung annehmen</UButton>
</section>
<p v-if="token && workspaceId" class="text-sm text-gray-500">Für spätere Anmeldungen kannst du <a :href="loginUrl" class="text-green-700 underline">diese Gastanmeldung als Lesezeichen speichern</a>.</p>
<template v-if="token"><nav v-if="rooms.length" aria-label="Freigegebene Unterräume" class="flex flex-wrap gap-2"><UButton v-for="room in rooms" :key="room.id" :variant="selected === room.id ? 'solid' : 'outline'" @click="selected = room.id">{{ room.name }}{{ room.unread ? ` (${room.unread})` : '' }}</UButton></nav><p v-else-if="!invitation" class="rounded-xl border border-gray-200 p-6 text-gray-500">Derzeit sind keine Unterräume für dich freigegeben.</p><CollaborationRoom v-if="selected" :key="selected" :room-id="selected" :api="api" base="/guest/api" @changed="refresh" @unauthorized="expire" /></template>
</main>
</template>