From 4003279453bc304dbdc37f4e82810e181e9e5d6f Mon Sep 17 00:00:00 2001 From: Thomas Hackner Date: Mon, 6 Jul 2026 15:42:12 +0200 Subject: [PATCH] =?UTF-8?q?Anh=C3=A4nge=20(Unterlagen=20+=20Mailanh=C3=A4n?= =?UTF-8?q?ge):=20PDF=20im=20neuen=20Tab=20=C3=B6ffnen=20statt=20Download?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- server.js | 19 +++++++++++++++++++ views/bewerbung.ejs | 9 +++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/server.js b/server.js index 0f6c15a..f45e3e0 100644 --- a/server.js +++ b/server.js @@ -103,6 +103,21 @@ if (!fs.existsSync(emailAnhaengeDir)) { fs.mkdirSync(emailAnhaengeDir, { recursive: true }); } +// Serve a stored file with `Content-Disposition: inline` so the browser opens it +// (PDF/image) in a tab instead of downloading. Falls back to the file extension +// when no MIME type is stored. +function serveInline(res, filePath, filename, mime) { + const type = mime || { + '.pdf': 'application/pdf', '.png': 'image/png', '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp', + }[path.extname(filename || filePath).toLowerCase()]; + if (type) res.type(type); + const safe = String(filename || 'datei').replace(/["\r\n]/g, ''); + res.setHeader('Content-Disposition', + `inline; filename="${safe}"; filename*=UTF-8''${encodeURIComponent(safe)}`); + res.sendFile(filePath); +} + // --- E-mail display + reply helpers -------------------------------------- // A stored message is rendered as HTML when we have an HTML body (or the plain // body is actually HTML markup — some senders put HTML in the text/plain part). @@ -1475,6 +1490,8 @@ initializeDatabase().then(() => { if (!a) return res.status(404).send('Anhang nicht gefunden'); const p = path.join(emailAnhaengeDir, a.pfad); if (!fs.existsSync(p)) return res.status(404).send('Datei nicht gefunden'); + // inline=1 opens viewable files (PDF/image) in the browser tab instead of forcing a download. + if (req.query.inline === '1') return serveInline(res, p, a.name || a.pfad, a.mime); res.download(p, a.name || a.pfad); } catch (error) { console.error('Error downloading e-mail attachment:', error); @@ -1943,6 +1960,8 @@ initializeDatabase().then(() => { if (!anhang) return res.status(404).send('Anhang nicht gefunden'); const filePath = path.join(anhaengeDir, anhang.pfad); if (!fs.existsSync(filePath)) return res.status(404).send('Datei nicht gefunden'); + // inline=1 opens viewable files (PDF/image) in the browser tab instead of forcing a download. + if (req.query.inline === '1') return serveInline(res, filePath, anhang.dateiname, anhang.mime); res.download(filePath, anhang.dateiname); } catch (error) { console.error('Error downloading attachment:', error); diff --git a/views/bewerbung.ejs b/views/bewerbung.ejs index 47e6d37..273ac1d 100644 --- a/views/bewerbung.ejs +++ b/views/bewerbung.ejs @@ -154,15 +154,16 @@