'use strict' const express = require('express') const nodemailer = require('nodemailer') const { createProxyMiddleware } = require('http-proxy-middleware') const { SMTP_HOST, SMTP_PORT = '587', SMTP_USER, SMTP_PASS, SMTP_FROM, RECIPIENT_EMAIL, APP_URL = '', } = process.env const app = express() // express.json() is intentionally NOT applied globally — it would consume the // request body stream before http-proxy-middleware can forward it to cryptgeon. app.use(express.static('public', { index: false })) app.get('/', (_req, res) => res.sendFile('index.html', { root: 'public' })) app.get('/link', (_req, res) => res.sendFile('link.html', { root: 'public' })) // Health-Endpoint fuer den Docker-Healthcheck. Muss vor dem Catch-all-Proxy // stehen, sonst landet die Anfrage bei cryptgeon. app.get('/healthz', (_req, res) => res.json({ ok: true })) const transporter = nodemailer.createTransport({ host: SMTP_HOST, port: Number(SMTP_PORT), secure: Number(SMTP_PORT) === 465, auth: { user: SMTP_USER, pass: SMTP_PASS }, }) app.post('/api/send', express.json(), async (req, res) => { const { url, name } = req.body if (!url || typeof url !== 'string' || !url.includes('/note/')) { return res.status(400).json({ error: 'Ungültige Anfrage' }) } if (!name || typeof name !== 'string' || !name.trim()) { return res.status(400).json({ error: 'Name fehlt' }) } const senderName = name.trim() // Reject URLs that don't originate from our own domain if (APP_URL && !url.startsWith(APP_URL)) { return res.status(400).json({ error: 'Ungültige URL' }) } try { await transporter.sendMail({ from: SMTP_FROM, to: RECIPIENT_EMAIL, subject: `Neues Passwort von ${senderName}`, text: [ `${senderName} hat Ihnen sicher ein Passwort übermittelt.`, '', 'Klicken Sie auf den folgenden Link um es einzusehen:', url, '', 'Dieser Link ist einmalig abrufbar und löscht sich nach dem ersten Öffnen automatisch.', ].join('\n'), html: `

Neues Passwort erhalten

${senderName}

hat Ihnen sicher ein Passwort übermittelt.

Passwort abrufen →

Dieser Link ist einmalig abrufbar und löscht sich nach dem ersten Öffnen automatisch.

`, }) res.json({ ok: true }) } catch (err) { console.error('Mail error:', err.message) res.status(500).json({ error: 'E-Mail konnte nicht gesendet werden' }) } }) // All other requests → proxy to cryptgeon (API + note decrypt UI) app.use( createProxyMiddleware({ target: 'http://cryptgeon:8000', changeOrigin: true, on: { error: (err, req, res) => { console.error(`Proxy error [${req.method} ${req.url}]:`, err.message) if (!res.headersSent) { res.status(502).json({ error: 'Cryptgeon nicht erreichbar' }) } }, }, }) ) app.listen(3000, () => console.log('SendSecret listening on :3000'))