rebreak-monorepo/backend/server/utils/imap-providers.ts
chahinebrini 518510c088 feat(mail): IONOS-Detection + MX-Lookup-Fallback + humanisierte Error-Messages
- imap-providers: IONOS/1&1/1blu, msn.com, magenta.de, yahoo.co.uk, ymail.com, tutanota hinzugefügt
- detectImapProviderAsync: MX-Lookup-Fallback für Custom-Domains (IONOS kundenserver.de/ionos.de Pattern)
- connect.post.ts: nutzt jetzt detectImapProviderAsync statt sync-Variante
- ConnectMailSheet: rohe Server-Errors werden via humanizeMailError() + t() übersetzt
- useMailConnect: IONOS/t-online/freenet Domains in Client-Side-Detection ergänzt
- Locale de/en: provider_other, app_password_guide_other, host_unreachable, unknown Text präzisiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 05:15:29 +02:00

139 lines
5.4 KiB
TypeScript

import dns from "node:dns/promises";
/**
* IMAP-Provider Konfigurationen
* Automatisch erkennen anhand der Email-Domain
*/
export interface ImapConfig {
host: string;
port: number;
name: string;
}
const PROVIDER_MAP: Record<string, ImapConfig> = {
// Google
"gmail.com": { host: "imap.gmail.com", port: 993, name: "Gmail" },
"googlemail.com": { host: "imap.gmail.com", port: 993, name: "Gmail" },
// Apple
"icloud.com": { host: "imap.mail.me.com", port: 993, name: "iCloud" },
"me.com": { host: "imap.mail.me.com", port: 993, name: "iCloud" },
"mac.com": { host: "imap.mail.me.com", port: 993, name: "iCloud" },
// Microsoft
"outlook.com": { host: "outlook.office365.com", port: 993, name: "Outlook" },
"hotmail.com": { host: "outlook.office365.com", port: 993, name: "Hotmail" },
"hotmail.de": { host: "outlook.office365.com", port: 993, name: "Hotmail" },
"live.com": { host: "outlook.office365.com", port: 993, name: "Live" },
"live.de": { host: "outlook.office365.com", port: 993, name: "Live" },
"msn.com": { host: "outlook.office365.com", port: 993, name: "Outlook" },
// Yahoo
"yahoo.com": { host: "imap.mail.yahoo.com", port: 993, name: "Yahoo" },
"yahoo.de": { host: "imap.mail.yahoo.com", port: 993, name: "Yahoo" },
"yahoo.co.uk": { host: "imap.mail.yahoo.com", port: 993, name: "Yahoo" },
"ymail.com": { host: "imap.mail.yahoo.com", port: 993, name: "Yahoo" },
// GMX / Web.de (United Internet)
"gmx.de": { host: "imap.gmx.net", port: 993, name: "GMX" },
"gmx.net": { host: "imap.gmx.net", port: 993, name: "GMX" },
"gmx.at": { host: "imap.gmx.net", port: 993, name: "GMX" },
"gmx.ch": { host: "imap.gmx.net", port: 993, name: "GMX" },
"web.de": { host: "imap.web.de", port: 993, name: "Web.de" },
// Telekom
"t-online.de": { host: "secureimap.t-online.de", port: 993, name: "T-Online" },
"magenta.de": { host: "secureimap.t-online.de", port: 993, name: "T-Online" },
// Freenet
"freenet.de": { host: "mx.freenet.de", port: 993, name: "Freenet" },
// Posteo
"posteo.de": { host: "posteo.de", port: 993, name: "Posteo" },
// Tutanota / Tuta
"tutanota.com": { host: "mail.tutanota.com", port: 993, name: "Tutanota" },
"tuta.io": { host: "mail.tutanota.com", port: 993, name: "Tutanota" },
// IONOS / 1&1 (direkte Consumer-Domains)
"ionos.de": { host: "imap.ionos.de", port: 993, name: "IONOS" },
"1und1.de": { host: "imap.ionos.de", port: 993, name: "IONOS" },
"1and1.com": { host: "imap.ionos.de", port: 993, name: "IONOS" },
"1and1.de": { host: "imap.ionos.de", port: 993, name: "IONOS" },
"1blu.de": { host: "imap.1blu.de", port: 993, name: "1blu" },
};
/**
* MX-Record-Patterns die IONOS-Hosting-Infrastruktur identifizieren.
* IONOS-Kunden mit eigener Domain nutzen mx00.ionos.de oder kundenserver.de.
* Quelle: IONOS Hilfe-Seite + eigene DNS-Lookups (verifiziert 2026-05).
*/
const MX_IONOS_PATTERNS = [
"ionos.de",
"kundenserver.de",
"perfora.net",
"ui-dns.de",
"ui-dns.org",
"ui-dns.biz",
"ui-dns.com",
];
/**
* Versucht via MX-Record-Lookup den Provider einer Custom-Domain zu erkennen.
* Wird nur aufgerufen wenn PROVIDER_MAP keinen direkten Treffer hat.
* Timeout: 3s (schnell genug für Connect-Flow, kein User-Impact).
*/
async function detectImapProviderByMx(domain: string): Promise<ImapConfig | null> {
try {
const mxRecords = await Promise.race([
dns.resolveMx(domain),
new Promise<never>((_, reject) => setTimeout(() => reject(new Error("timeout")), 3000)),
]);
for (const mx of mxRecords) {
const exchange = mx.exchange.toLowerCase();
if (MX_IONOS_PATTERNS.some((pattern) => exchange.endsWith(pattern))) {
return { host: "imap.ionos.de", port: 993, name: "IONOS" };
}
}
} catch {
// DNS-Lookup fehlgeschlagen oder Timeout — kein Problem, Fallback greift
}
return null;
}
export function detectImapProvider(email: string): ImapConfig {
const domain = email.split("@")[1]?.toLowerCase() ?? "";
return (
PROVIDER_MAP[domain] ?? {
host: `imap.${domain}`,
port: 993,
name: domain,
}
);
}
/**
* Async-Variante mit MX-Lookup-Fallback für Custom-Domains.
* Nutzen wenn detectImapProvider keinen bekannten Provider zurückgibt
* (erkennbar daran dass name === domain, also kein Friendly-Name).
*/
export async function detectImapProviderAsync(email: string): Promise<ImapConfig> {
const domain = email.split("@")[1]?.toLowerCase() ?? "";
const direct = PROVIDER_MAP[domain];
if (direct) return direct;
const byMx = await detectImapProviderByMx(domain);
if (byMx) return byMx;
// Letzter Fallback: imap.<domain> — Standard-Konvention
return { host: `imap.${domain}`, port: 993, name: domain };
}
const SMTP_MAP: Record<string, { host: string; port: number }> = {
"imap.gmail.com": { host: "smtp.gmail.com", port: 587 },
"imap.mail.me.com": { host: "smtp.mail.me.com", port: 587 },
"imap.gmx.net": { host: "mail.gmx.net", port: 587 },
"imap.web.de": { host: "smtp.web.de", port: 587 },
"outlook.office365.com": { host: "smtp-mail.outlook.com", port: 587 },
"imap.mail.yahoo.com": { host: "smtp.mail.yahoo.com", port: 587 },
"secureimap.t-online.de": { host: "securesmtp.t-online.de", port: 587 },
"mx.freenet.de": { host: "mx.freenet.de", port: 587 },
"posteo.de": { host: "posteo.de", port: 587 },
};
export function detectSmtpProvider(imapHost: string): { host: string; port: number } {
return SMTP_MAP[imapHost] ?? { host: imapHost.replace("imap.", "smtp."), port: 587 };
}