From 5b0a509e7020a2a56469521f384fa7f9f917afe3 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 17 May 2026 15:01:04 -0500 Subject: [PATCH] =?UTF-8?q?fix(zoho):=20P0/P1=20criticals=20=E2=80=94=20cr?= =?UTF-8?q?edential=20check,=20response=20validation,=20timeout,=20null=20?= =?UTF-8?q?normalization=20(Neo=20N1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/index.js | 53 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/server/index.js b/server/index.js index 8d54d10..30d2ae9 100644 --- a/server/index.js +++ b/server/index.js @@ -208,8 +208,8 @@ const supportSchema = z.object({ const ZOHO_ENABLED = process.env.ZOHO_ENABLED === 'true' const ZOHO_API_DOMAIN = process.env.ZOHO_API_DOMAIN || 'https://www.zohoapis.com' const ZOHO_CLIENT_ID = process.env.ZOHO_CLIENT_ID || '' -const ZOHO_CLIENT_SECRET = process.env.ZOHO_CLIENT_SECRET || '' -const ZOHO_REFRESH_TOKEN = process.env.ZOHO_REFRESH_TOKEN || '' +const ZOHO_CLIENT_SECRET = process.env.ZOHO_CLIENT_SECRET || null +const ZOHO_REFRESH_TOKEN = process.env.ZOHO_REFRESH_TOKEN || null const ZOHO_REDIRECT_URI = process.env.ZOHO_REDIRECT_URI || '' // In-memory access token cache @@ -253,6 +253,12 @@ async function getZohoAccessToken() { async function forwardToZoho(leadData) { if (!ZOHO_ENABLED) return + // Issue #2: Short-circuit if Zoho credentials are missing + if (!ZOHO_CLIENT_SECRET || !ZOHO_REFRESH_TOKEN) { + log.warn('[Zoho] Skipping forwarding - ZOHO_CLIENT_SECRET or ZOHO_REFRESH_TOKEN not configured') + return + } + try { const accessToken = await getZohoAccessToken() if (!accessToken) { @@ -270,26 +276,43 @@ async function forwardToZoho(leadData) { Phone: leadData.phone || '', Zip_Code: leadData.zip || '', Description: leadData.message || '', - Service_Interest: leadData.service_interest || '', + Service_Interest: leadData.service_interest || null, }, ], } - const response = await fetch(url, { - method: 'POST', - headers: { - 'Authorization': `Zoho-oauthtoken ${accessToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload), - }) + // Issue #5: Add timeout using AbortController + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), 10000) // 10 second timeout + + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Authorization': `Zoho-oauthtoken ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + signal: controller.signal, + }) + + // Issue #3: Check response.ok before processing + if (!response.ok) { + const text = await response.text() + log.error(`[Zoho] Lead forwarding failed (${response.status}):`, text) + return + } - if (response.ok) { const result = await response.json() log.info('[Zoho] Lead forwarded successfully:', result.data?.[0]?.details?.id || 'no id returned') - } else { - const text = await response.text() - log.error(`[Zoho] Lead forwarding failed (${response.status}):`, text) + } catch (fetchErr) { + if (fetchErr.name === 'AbortError') { + log.warn('[Zoho] Lead forwarding timed out after 10 seconds') + } else { + log.error('[Zoho] Forwarding error:', fetchErr.message) + } + } finally { + clearTimeout(timeoutId) } } catch (err) { log.error('[Zoho] Forwarding error:', err.message)