fix(quick-261005-d5d): Kalender-Test bricht bei unerreichbarem Exchange nach 15 s ab
EWS-Aufrufe ueber httpntlm bekommen eine eigene 15-s-Zeitgrenze; httpreqs timeout greift waehrend des Verbindungsaufbaus nicht (gemessen 134 s). Netzfehler liefern im Test den Schluessel 'unreachable', das Formular meldet 'nicht erreichbar' statt 'Zugangsdaten pruefen'. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,43 @@ export interface CalendarEvent {
|
||||
color?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* quick-261005: Ein Provider wirft diesen Fehler aus `testConnection`, wenn
|
||||
* der Kalender-Server gar nicht erreichbar ist (Zeitueberschreitung,
|
||||
* abgewiesene Verbindung, Name unbekannt) — im Unterschied zu „erreichbar,
|
||||
* aber Anmeldung abgelehnt“ (`false`). Traegt bewusst keine Details (T-05-13).
|
||||
*/
|
||||
export class CalendarSourceUnreachableError extends Error {
|
||||
constructor() {
|
||||
super('Calendar server not reachable');
|
||||
this.name = 'CalendarSourceUnreachableError';
|
||||
}
|
||||
}
|
||||
|
||||
/** Fehlerkennungen von Node/httpreq, die „Server nicht erreichbar“ bedeuten. */
|
||||
const NETWORK_UNREACHABLE_CODES = new Set([
|
||||
'TIMEOUT', // httpreq bei Ablauf von `timeout`
|
||||
'ETIMEDOUT',
|
||||
'ECONNREFUSED',
|
||||
'ECONNRESET',
|
||||
'EHOSTUNREACH',
|
||||
'ENETUNREACH',
|
||||
'ENOTFOUND',
|
||||
'EAI_AGAIN',
|
||||
]);
|
||||
|
||||
export function isNetworkUnreachableError(error: unknown): boolean {
|
||||
const code = (error as { code?: unknown; cause?: { code?: unknown } } | null)?.code;
|
||||
const causeCode = (error as { cause?: { code?: unknown } } | null)?.cause?.code;
|
||||
return (
|
||||
(typeof code === 'string' && NETWORK_UNREACHABLE_CODES.has(code)) ||
|
||||
(typeof causeCode === 'string' && NETWORK_UNREACHABLE_CODES.has(causeCode))
|
||||
);
|
||||
}
|
||||
|
||||
/** Stabiler Fehlerschluessel fuer die Oberflaeche (dort uebersetzt). */
|
||||
export const CALENDAR_TEST_UNREACHABLE = 'unreachable';
|
||||
|
||||
/**
|
||||
* Provider interface for calendar source integrations.
|
||||
* Each provider (ICS, CalDAV, Exchange) implements this contract.
|
||||
@@ -319,13 +356,15 @@ export class CalendarService {
|
||||
});
|
||||
|
||||
return { success };
|
||||
} catch {
|
||||
const errorMsg = 'Connection failed'; // T-05-13: generic error, no credentials
|
||||
} catch (error) {
|
||||
const unreachable = error instanceof CalendarSourceUnreachableError;
|
||||
// T-05-13: generic error, no credentials
|
||||
const errorMsg = unreachable ? 'Server not reachable' : 'Connection failed';
|
||||
await tenantPrisma.calendarSource.update({
|
||||
where: { id },
|
||||
data: { lastSyncError: errorMsg },
|
||||
});
|
||||
return { success: false, error: errorMsg };
|
||||
return { success: false, error: unreachable ? CALENDAR_TEST_UNREACHABLE : errorMsg };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +403,10 @@ export class CalendarService {
|
||||
try {
|
||||
const success = await provider.testConnection(tempSource);
|
||||
return { success };
|
||||
} catch {
|
||||
} catch (error) {
|
||||
if (error instanceof CalendarSourceUnreachableError) {
|
||||
return { success: false, error: CALENDAR_TEST_UNREACHABLE };
|
||||
}
|
||||
return { success: false, error: 'Connection failed' };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user