E-Mail Anhänge ohne Fetch herunterladen

KI-AGENT: Der E-Mail Anhang-Download nutzt jetzt einen nativen Browser-Link statt Cross-Origin-Fetch und erlaubt dafür den bestehenden JWT gezielt als Download-Token.
This commit is contained in:
2026-05-23 21:04:53 +02:00
parent a34bf43756
commit 154d7060f8
2 changed files with 25 additions and 15 deletions

View File

@@ -68,6 +68,15 @@ export default fp(async (server: FastifyInstance) => {
return
}
const urlPath = req.url.split("?")[0]
const queryToken = (req.query as any)?.downloadToken
const downloadToken =
typeof queryToken === "string"
&& urlPath.startsWith("/api/email/attachments/")
&& urlPath.endsWith("/download")
? queryToken
: null
// 1⃣ Token aus Header oder Cookie lesen
const cookieToken = req.cookies?.token
const authHeader = req.headers.authorization
@@ -78,7 +87,7 @@ export default fp(async (server: FastifyInstance) => {
const token =
headerToken && headerToken.length > 10
? headerToken
: cookieToken || null
: cookieToken || downloadToken || null
if (!token) {
return reply.code(401).send({ error: "Authentication required" })

View File

@@ -55,6 +55,7 @@ type EmailMessage = {
}
const { $api } = useNuxtApp()
const runtimeConfig = useRuntimeConfig()
const toast = useToast()
const accounts = ref<EmailAccount[]>([])
@@ -456,24 +457,22 @@ async function moveSelectedMessage() {
async function downloadAttachment(attachment: NonNullable<EmailMessage["attachments"]>[number]) {
actionLoading.value = `attachment-${attachment.id}`
try {
const response = await $api.raw(`/api/email/attachments/${attachment.id}/download`, {
responseType: "arrayBuffer",
timeout: 60_000,
})
const contentType = response.headers.get("content-type") || attachment.contentType || "application/octet-stream"
const blob = new Blob([response._data as ArrayBuffer], { type: contentType })
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 apiBase = String(runtimeConfig.public.apiBase || "").replace(/\/$/, "")
const path = `/api/email/attachments/${attachment.id}/download`
const downloadUrl = new URL(apiBase ? `${apiBase}${path}` : path, window.location.origin)
const token = useCookie("token").value
if (token) {
downloadUrl.searchParams.set("downloadToken", token)
}
const link = document.createElement("a")
link.href = url
link.download = filename
link.href = downloadUrl.toString()
link.download = attachment.filename || "anhang"
document.body.appendChild(link)
link.click()
link.remove()
URL.revokeObjectURL(url)
} catch (err: any) {
toast.add({
title: "Download fehlgeschlagen",
@@ -481,7 +480,9 @@ async function downloadAttachment(attachment: NonNullable<EmailMessage["attachme
color: "error",
})
} finally {
actionLoading.value = ""
window.setTimeout(() => {
actionLoading.value = ""
}, 750)
}
}