Schreiben als E-Mail uebernehmen
Im Schreiben-Editor gibt es jetzt den Umschalter Brief/E-Mail. Die E-Mail-Fassung wird aus dem - womoeglich bearbeiteten - Brieftext abgeleitet: ohne Absenderblock, Anschrift und Datumszeile, aber mit dem Bestellungsabsatz. Betreff und Text einzeln kopierbar. Die offene Entscheidung aus dem Issue (mailto oder .eml) ist nach Rueckfrage mit BEIDES beantwortet: - mailto fuer kurze Texte. Der Knopf verschwindet, sobald der kodierte Link 1800 Zeichen uebersteigt - mailto uebergibt den Text in der Adresszeile, deren Laenge nirgends zugesichert ist, und ein halbierter Brief faellt erst beim Empfaenger auf. Die Grenze wird NACH der URL-Kodierung gerechnet; 900 Umlaute sind roh harmlos und kodiert weit drueber. - .eml immer: RFC 5322 mit X-Unsent: 1, quoted-printable fuer den Rumpf, RFC-2047-Kodierung fuer Umlaute im Betreff. Bewusst ohne From-Kopf - ein erfundener Absender wuerde je nach Programm uebernommen. Beide Knoepfe erscheinen nur mit E-Mail-Adresse (aus dem Kontakt uebernommen oder von Hand). Ohne Adresse bleibt das Kopieren. Die Ablage kennt den Unterschied: Eine E-Mail heisst in der Akte "E-Mail: ...", und abgelegt wird die E-Mail-Fassung samt Empfaengeradresse im Kopf der PDF - in der Akte soll stehen, was tatsaechlich hinausging. Sicherung in emailFassung(): Findet sich keine Anrede (frei umgebauter Text), wird NICHTS entfernt. Lieber ein Briefkopf zu viel in der Mail als stillschweigend verlorener Inhalt. Geprueft: 20 Einheitstests, vier End-to-End-Tests auf beiden Geraeteprofilen. 590 Unit-Tests, 81 e2e, null Typfehler. Refs #5
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { erfordereZugang, ZugriffVerweigert } from '@/lib/auth/sitzung'
|
||||
import { emailFassung, siehtAusWieEmail } from '@/lib/schreiben/email'
|
||||
import { emlErzeugen } from '@/lib/schreiben/eml'
|
||||
import { dateinameFuerSchreiben, istSchreibenArt } from '@/lib/schreiben/arten'
|
||||
import { heuteAlsTag } from '@/lib/fristen/datum'
|
||||
|
||||
/**
|
||||
* Das Schreiben als .eml-Entwurf herunterladen.
|
||||
*
|
||||
* Gleicher Aufbau wie der Word-Download (../docx/route.ts): POST, weil der
|
||||
* Text mitkommt und nicht in die Adresszeile gehoert; Zugriff wird hier
|
||||
* geprueft, weil sich eine Route unmittelbar aufrufen laesst.
|
||||
*/
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function POST(anfrage: Request): Promise<Response> {
|
||||
let formular: FormData
|
||||
try {
|
||||
formular = await anfrage.formData()
|
||||
} catch {
|
||||
return NextResponse.json({ fehler: 'Ungültige Anfrage.' }, { status: 400 })
|
||||
}
|
||||
|
||||
const betreuteId = String(formular.get('betreuteId') ?? '')
|
||||
const rohArt = String(formular.get('art') ?? '')
|
||||
const text = String(formular.get('text') ?? '')
|
||||
const betreff = String(formular.get('betreff') ?? '').trim()
|
||||
const adresse = String(formular.get('empfaengerEmail') ?? '').trim()
|
||||
const empfaenger = String(formular.get('empfaengerName') ?? '').trim()
|
||||
|
||||
if (!betreuteId || !istSchreibenArt(rohArt) || text.trim().length < 30) {
|
||||
return NextResponse.json({ fehler: 'Unvollständige Angaben.' }, { status: 400 })
|
||||
}
|
||||
if (!siehtAusWieEmail(adresse)) {
|
||||
return NextResponse.json(
|
||||
{ fehler: 'Für diesen Empfänger ist keine E-Mail-Adresse hinterlegt.' },
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
await erfordereZugang(betreuteId, 'DOKUMENTE', 'lesen')
|
||||
} catch (ausnahme) {
|
||||
if (ausnahme instanceof ZugriffVerweigert) {
|
||||
return NextResponse.json({ fehler: ausnahme.message }, { status: 403 })
|
||||
}
|
||||
throw ausnahme
|
||||
}
|
||||
|
||||
const fassung = emailFassung(text, betreff)
|
||||
const datei = emlErzeugen({ an: adresse, betreff: fassung.betreff, text: fassung.text })
|
||||
const dateiname = dateinameFuerSchreiben(rohArt, empfaenger || null, heuteAlsTag(), 'eml')
|
||||
|
||||
return new NextResponse(new Uint8Array(datei), {
|
||||
headers: {
|
||||
'Content-Type': 'message/rfc822',
|
||||
'Content-Disposition': `attachment; filename="${dateiname}"`,
|
||||
'Content-Length': String(datei.length),
|
||||
'Cache-Control': 'no-store, must-revalidate',
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -79,7 +79,15 @@ export default async function SchreibenSeite({
|
||||
|
||||
const kontakte = await db.kontakt.findMany({
|
||||
where: { betreuteId: aktive.id, ...nichtGeloescht },
|
||||
select: { id: true, name: true, organisation: true, strasse: true, plz: true, ort: true },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
organisation: true,
|
||||
strasse: true,
|
||||
plz: true,
|
||||
ort: true,
|
||||
email: true,
|
||||
},
|
||||
orderBy: [{ organisation: 'asc' }, { name: 'asc' }],
|
||||
take: 200,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user