E-Mail Anhang-Downloads stabilisieren

KI-AGENT: Lädt Anhänge nun authentifiziert als Blob herunter und macht die Backend-Zuordnung von Anhängen zur Originalmail robuster, inklusive Positions-Fallback.
This commit is contained in:
2026-05-23 20:42:56 +02:00
parent 358cd906ae
commit 8697810127
2 changed files with 50 additions and 7 deletions

View File

@@ -55,7 +55,6 @@ type EmailMessage = {
}
const { $api } = useNuxtApp()
const config = useRuntimeConfig()
const toast = useToast()
const accounts = ref<EmailAccount[]>([])
@@ -454,8 +453,38 @@ async function moveSelectedMessage() {
}
}
function downloadAttachment(attachmentId: string) {
window.open(`${config.public.apiBase}/api/email/attachments/${attachmentId}/download`, "_blank")
async function downloadAttachment(attachment: NonNullable<EmailMessage["attachments"]>[number]) {
actionLoading.value = `attachment-${attachment.id}`
try {
const response = await $fetch.raw(`/api/email/attachments/${attachment.id}/download`, {
responseType: "blob",
credentials: "include",
headers: {
...(useCookie("token").value ? { Authorization: `Bearer ${useCookie("token").value}` } : {}),
},
})
const blob = response._data as Blob
const disposition = response.headers.get("content-disposition") || ""
const dispositionFilename = disposition.match(/filename="([^"]+)"/)?.[1]
const filename = dispositionFilename || attachment.filename || "anhang"
const url = URL.createObjectURL(blob)
const link = document.createElement("a")
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
link.remove()
URL.revokeObjectURL(url)
} catch (err: any) {
toast.add({
title: "Download fehlgeschlagen",
description: err?.data?.error || err?.message || "Der Anhang konnte nicht geladen werden.",
color: "error",
})
} finally {
actionLoading.value = ""
}
}
async function setMessageSeen(messageId: string, seen: boolean) {
@@ -814,12 +843,16 @@ onMounted(loadAccounts)
v-for="attachment in selectedMessage.attachments"
:key="attachment.id"
class="flex items-center gap-2 rounded-md border border-(--ui-border) px-3 py-2 text-left text-sm hover:bg-(--ui-bg-muted)"
@click="downloadAttachment(attachment.id)"
@click="downloadAttachment(attachment)"
>
<UIcon name="i-heroicons-paper-clip" class="size-4 text-dimmed" />
<span>{{ attachment.filename || 'Anhang' }}</span>
<span class="text-xs text-dimmed">{{ formatAttachmentSize(attachment.size) }}</span>
<UIcon name="i-heroicons-arrow-down-tray" class="size-4 text-dimmed" />
<UIcon
:name="actionLoading === `attachment-${attachment.id}` ? 'i-heroicons-arrow-path' : 'i-heroicons-arrow-down-tray'"
class="size-4 text-dimmed"
:class="actionLoading === `attachment-${attachment.id}` ? 'animate-spin' : ''"
/>
</button>
</div>
</div>