diff --git a/server/index.ts b/server/index.ts index 7fbd32e..fa0f5ed 100644 --- a/server/index.ts +++ b/server/index.ts @@ -60,7 +60,7 @@ const cspConfig = { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], - imgSrc: ["'self'", 'data:', 'https://api.qrserver.com'], + imgSrc: ["'self'", 'data:'], formAction: ["'self'"], frameAncestors: ["'none'"], objectSrc: ["'none'"], diff --git a/server/modules/proton-mail.ts b/server/modules/proton-mail.ts index d011171..e8225fc 100644 --- a/server/modules/proton-mail.ts +++ b/server/modules/proton-mail.ts @@ -12,9 +12,18 @@ import { logError, logInfo, logSuccess, logVerbose, logWarn } from '@/utils/log' let imapConnected = false; let monitorStartTime = 0; +let reconnectAttempts = 0; +const MAX_RECONNECT_DELAY = 300000; // 5 minutes export const isImapConnected = () => imapConnected; +const getReconnectDelay = () => { + const baseDelay = 10000; // 10 seconds + const delay = Math.min(baseDelay * 2 ** reconnectAttempts, MAX_RECONNECT_DELAY); + reconnectAttempts++; + return delay; +}; + export async function startProtonMonitor() { if (!PROTON_IMAP_USERNAME || !PROTON_IMAP_PASSWORD) { logError('Missing required env vars: PROTON_IMAP_USERNAME and PROTON_IMAP_PASSWORD'); @@ -114,6 +123,7 @@ export async function startProtonMonitor() { const nameMatch = rawFrom.match(/^"?([^"<]+)"?\s* { imapConnected = true; + reconnectAttempts = 0; logSuccess('IMAP is ready'); openInbox(); }); @@ -132,14 +143,26 @@ export async function startProtonMonitor() { logError('IMAP error:', err.message); }); - imap.on('close', (hadError: boolean) => { + const handleReconnect = (reason: string) => { imapConnected = false; - logError(`IMAP connection closed (hadError: ${hadError})`); + logError(reason); + + const delay = getReconnectDelay(); + logInfo(`Attempting to reconnect in ${delay / 1000}s (attempt ${reconnectAttempts})...`); + setTimeout(() => { + if (!imapConnected) { + logInfo('Reconnecting to Proton Bridge...'); + imap.connect(); + } + }, delay); + }; + + imap.on('close', (hadError: boolean) => { + handleReconnect(`IMAP connection closed (hadError: ${hadError})`); }); imap.on('end', () => { - imapConnected = false; - logError('IMAP connection ended by server'); + handleReconnect('IMAP connection ended by server'); }); imap.connect(); diff --git a/server/routes/admin.ts b/server/routes/admin.ts index 3a810f9..d13744c 100644 --- a/server/routes/admin.ts +++ b/server/routes/admin.ts @@ -161,27 +161,36 @@ const handleQRSection = async () => { if ((!cachedQR || now - qrCacheTime > QR_CACHE_TTL) && !generatingPromise) { generatingPromise = (async () => { - const qr = await generateLinkQR(); - cachedQR = qr; - qrCacheTime = Date.now(); + try { + const qr = await generateLinkQR(); + cachedQR = qr; + qrCacheTime = Date.now(); - finishLink() - .then(async () => { - await initSignal(); - }) - .catch(() => {}) - .finally(() => { - generatingPromise = null; - cachedQR = null; - qrCacheTime = 0; - }); + finishLink() + .then(async () => { + await initSignal(); + }) + .catch(() => {}) + .finally(() => { + generatingPromise = null; + cachedQR = null; + qrCacheTime = 0; + }); - return qr; + return qr; + } catch (error) { + generatingPromise = null; + throw error; + } })(); } if (generatingPromise && !cachedQR) { - await generatingPromise; + try { + await generatingPromise; + } catch { + return '

Signal daemon is starting up, please refresh in a few seconds...

'; + } } return `