From b7d3654b7200b622fb9822e79929b39b00b103a0 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Thu, 23 Apr 2026 15:51:16 +0200 Subject: [PATCH] Fix: Provider-Domain greift sofort + Domain-Validierung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Nach dem Ändern der Provider-Domain blieb die alte Domain (stressfrei-wechseln.de) im Adress-Hinzufügen-Dialog bestehen, weil der Frontend-Hook useProviderSettings() einen 5-Minuten staleTime hat und nicht invalidiert wurde. Fix: - In allen Provider-Mutations (create/update/delete) wird jetzt auch 'email-provider-public-settings' invalidiert → Domain & Label greifen sofort in allen Komponenten Zusätzlich Domain-Validierung eingebaut: - Frontend: pattern am Input + Live-Fehlermeldung Format: name.tld (mit Subdomains erlaubt, z.B. mail.meine-firma.de) Input auto-lowercase + trim - Backend: validateDomain() in createProviderConfig/updateProviderConfig Wirft Error mit sprechender Meldung bei ungültigem Format - Schützt vor Versehen im UI + direkten API-Aufrufen Co-Authored-By: Claude Opus 4.6 (1M context) --- .../emailProvider/emailProviderService.ts | 21 +++++++++++-- .../src/pages/settings/EmailProviders.tsx | 31 ++++++++++++++----- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/backend/src/services/emailProvider/emailProviderService.ts b/backend/src/services/emailProvider/emailProviderService.ts index 3381801a..0ed2c4be 100644 --- a/backend/src/services/emailProvider/emailProviderService.ts +++ b/backend/src/services/emailProvider/emailProviderService.ts @@ -80,7 +80,24 @@ export interface CreateProviderConfigData { isDefault?: boolean; } +// Validiert Domain-Format (z.B. stressfrei-wechseln.de, mail.beispiel.com) +const DOMAIN_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/; + +function validateDomain(domain: string | undefined): string { + if (!domain || !domain.trim()) { + throw new Error('Domain ist erforderlich'); + } + const normalized = domain.trim().toLowerCase(); + if (!DOMAIN_REGEX.test(normalized)) { + throw new Error(`Ungültige Domain: "${domain}". Format: name.tld (z.B. meine-firma.de)`); + } + return normalized; +} + export async function createProviderConfig(data: CreateProviderConfigData) { + // Domain validieren + const validatedDomain = validateDomain(data.domain); + // Falls isDefault=true, alle anderen auf false setzen if (data.isDefault) { await prisma.emailProviderConfig.updateMany({ @@ -102,7 +119,7 @@ export async function createProviderConfig(data: CreateProviderConfigData) { apiKey: data.apiKey || null, username: data.username || null, passwordEncrypted, - domain: data.domain, + domain: validatedDomain, defaultForwardEmail: data.defaultForwardEmail || null, imapEncryption: data.imapEncryption ?? 'SSL', smtpEncryption: data.smtpEncryption ?? 'SSL', @@ -135,7 +152,7 @@ export async function updateProviderConfig( if (data.apiUrl !== undefined) updateData.apiUrl = data.apiUrl; if (data.apiKey !== undefined) updateData.apiKey = data.apiKey || null; if (data.username !== undefined) updateData.username = data.username || null; - if (data.domain !== undefined) updateData.domain = data.domain; + if (data.domain !== undefined) updateData.domain = validateDomain(data.domain); if (data.defaultForwardEmail !== undefined) updateData.defaultForwardEmail = data.defaultForwardEmail || null; if (data.imapEncryption !== undefined) updateData.imapEncryption = data.imapEncryption; diff --git a/frontend/src/pages/settings/EmailProviders.tsx b/frontend/src/pages/settings/EmailProviders.tsx index bec12819..04518e26 100644 --- a/frontend/src/pages/settings/EmailProviders.tsx +++ b/frontend/src/pages/settings/EmailProviders.tsx @@ -101,6 +101,7 @@ export default function EmailProviders() { emailProviderApi.createConfig(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['email-provider-configs'] }); + queryClient.invalidateQueries({ queryKey: ['email-provider-public-settings'] }); closeModal(); }, }); @@ -110,6 +111,7 @@ export default function EmailProviders() { emailProviderApi.updateConfig(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['email-provider-configs'] }); + queryClient.invalidateQueries({ queryKey: ['email-provider-public-settings'] }); closeModal(); }, }); @@ -118,6 +120,7 @@ export default function EmailProviders() { mutationFn: (id: number) => emailProviderApi.deleteConfig(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['email-provider-configs'] }); + queryClient.invalidateQueries({ queryKey: ['email-provider-public-settings'] }); }, }); @@ -547,13 +550,27 @@ export default function EmailProviders() { - setFormData({ ...formData, domain: e.target.value })} - placeholder="stressfrei-wechseln.de" - required - /> +
+ + setFormData({ ...formData, domain: e.target.value.toLowerCase().trim() }) + } + placeholder="stressfrei-wechseln.de" + required + pattern="^[a-z0-9]([a-z0-9\-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9\-]*[a-z0-9])?)+$" + title="Gültige Domain erforderlich, z.B. meine-firma.de oder mail.beispiel.com" + /> + {formData.domain && + !/^[a-z0-9]([a-z0-9\-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9\-]*[a-z0-9])?)+$/.test( + formData.domain, + ) && ( +

+ Keine gültige Domain – Format: name.tld (z.B. meine-firma.de) +

+ )} +