feat(PROJ-12): E-Mail Export EML/PDF/ZIP
- GET /api/export/pdf/{id}: PDF-Generierung (stdlib, kein ext. Paket)
- POST /api/export/zip: Streaming-ZIP mit manifest.csv, Anhänge optional
- Max. 500 Mails pro Export, Zugriffscheck per Rolle
- Audit-Log für jeden Export
- Frontend: PDF-Button in Mail-Ansicht
- Frontend: Checkboxen + ZIP-Export-Dialog in Suchergebnissen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -391,3 +391,32 @@ export interface SystemStats {
|
||||
export async function getSystemStats(): Promise<SystemStats> {
|
||||
return request<SystemStats>("/api/admin/system/stats");
|
||||
}
|
||||
|
||||
// ── Export ────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function exportMailPDF(id: string): Promise<{ blob: Blob; filename: string }> {
|
||||
const token = getToken();
|
||||
const res = await fetch(`${API_BASE}/api/export/pdf/${id}`, {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
});
|
||||
if (!res.ok) throw new Error("PDF export failed");
|
||||
const blob = await res.blob();
|
||||
const cd = res.headers.get("Content-Disposition") || "";
|
||||
const filename = cd.match(/filename="([^"]+)"/)?.[1] || `${id.slice(0, 16)}.pdf`;
|
||||
return { blob, filename };
|
||||
}
|
||||
|
||||
export async function exportMailsZIP(ids: string[], attachments: boolean): Promise<{ blob: Blob }> {
|
||||
const token = getToken();
|
||||
const res = await fetch(`${API_BASE}/api/export/zip`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
body: JSON.stringify({ ids, attachments }),
|
||||
});
|
||||
if (!res.ok) throw new Error("ZIP export failed");
|
||||
const blob = await res.blob();
|
||||
return { blob };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user