From d34f5eccb6dda83d3952653ecc3cd3ba1b22b7d6 Mon Sep 17 00:00:00 2001 From: Robert Dyer <rdyer@unl.edu> Date: Tue, 18 Jun 2024 14:55:09 -0500 Subject: [PATCH] Timeout SimpleFIN sync calls after 60 seconds (#2891) --- .../components/accounts/AccountSyncCheck.jsx | 6 ++ .../components/modals/CreateAccountModal.tsx | 60 +++++++++++-------- .../loot-core/src/server/accounts/sync.ts | 1 + packages/loot-core/src/server/main.ts | 23 ++++--- packages/loot-core/src/server/post.ts | 7 ++- upcoming-release-notes/2891.md | 6 ++ 6 files changed, 69 insertions(+), 34 deletions(-) create mode 100644 upcoming-release-notes/2891.md diff --git a/packages/desktop-client/src/components/accounts/AccountSyncCheck.jsx b/packages/desktop-client/src/components/accounts/AccountSyncCheck.jsx index 677899234..989e585c7 100644 --- a/packages/desktop-client/src/components/accounts/AccountSyncCheck.jsx +++ b/packages/desktop-client/src/components/accounts/AccountSyncCheck.jsx @@ -35,6 +35,12 @@ function getErrorMessage(type, code) { case 'RATE_LIMIT_EXCEEDED': return 'Rate limit exceeded for this item. Please try again later.'; + case 'INVALID_ACCESS_TOKEN': + return 'Your SimpleFIN Access Token is no longer valid. Please reset and generate a new token.'; + + case 'ACCOUNT_NEEDS_ATTENTION': + return 'The account needs your attention at [SimpleFIN](https://beta-bridge.simplefin.org/auth/login).'; + default: } diff --git a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx index 492a03f84..4f042cd1d 100644 --- a/packages/desktop-client/src/components/modals/CreateAccountModal.tsx +++ b/packages/desktop-client/src/components/modals/CreateAccountModal.tsx @@ -67,36 +67,46 @@ export function CreateAccountModal({ setLoadingSimpleFinAccounts(true); - const results = await send('simplefin-accounts'); + try { + const results = await send('simplefin-accounts'); + if (results.error_code) { + throw new Error(results.reason); + } - const newAccounts = []; + const newAccounts = []; - type NormalizedAccount = { - account_id: string; - name: string; - institution: string; - orgDomain: string; - orgId: string; - balance: number; - }; - - for (const oldAccount of results.accounts) { - const newAccount: NormalizedAccount = { - account_id: oldAccount.id, - name: oldAccount.name, - institution: oldAccount.org.name, - orgDomain: oldAccount.org.domain, - orgId: oldAccount.org.id, - balance: oldAccount.balance, + type NormalizedAccount = { + account_id: string; + name: string; + institution: string; + orgDomain: string; + orgId: string; + balance: number; }; - newAccounts.push(newAccount); - } + for (const oldAccount of results.accounts) { + const newAccount: NormalizedAccount = { + account_id: oldAccount.id, + name: oldAccount.name, + institution: oldAccount.org.name, + orgDomain: oldAccount.org.domain, + orgId: oldAccount.org.id, + balance: oldAccount.balance, + }; - actions.pushModal('select-linked-accounts', { - accounts: newAccounts, - syncSource: 'simpleFin', - }); + newAccounts.push(newAccount); + } + + actions.pushModal('select-linked-accounts', { + accounts: newAccounts, + syncSource: 'simpleFin', + }); + } catch (err) { + console.error(err); + actions.pushModal('simplefin-init', { + onSuccess: () => setIsSimpleFinSetupComplete(true), + }); + } setLoadingSimpleFinAccounts(false); }; diff --git a/packages/loot-core/src/server/accounts/sync.ts b/packages/loot-core/src/server/accounts/sync.ts index 2b9609204..034a21231 100644 --- a/packages/loot-core/src/server/accounts/sync.ts +++ b/packages/loot-core/src/server/accounts/sync.ts @@ -142,6 +142,7 @@ async function downloadSimpleFinTransactions(acctId, since) { { 'X-ACTUAL-TOKEN': userToken, }, + 60000, ); if (res.error_code) { diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts index 6af6f82dc..706171673 100644 --- a/packages/loot-core/src/server/main.ts +++ b/packages/loot-core/src/server/main.ts @@ -982,13 +982,18 @@ handlers['simplefin-accounts'] = async function () { return { error: 'unauthorized' }; } - return post( - getServer().SIMPLEFIN_SERVER + '/accounts', - {}, - { - 'X-ACTUAL-TOKEN': userToken, - }, - ); + try { + return await post( + getServer().SIMPLEFIN_SERVER + '/accounts', + {}, + { + 'X-ACTUAL-TOKEN': userToken, + }, + 60000, + ); + } catch (error) { + return { error_code: 'TIMED_OUT' }; + } }; handlers['gocardless-get-banks'] = async function (country) { @@ -1093,7 +1098,9 @@ handlers['accounts-bank-sync'] = async function ({ id }) { } else if (err instanceof PostError && err.reason !== 'internal') { errors.push({ accountId: acct.id, - message: `Account “${acct.name}†is not linked properly. Please link it again`, + message: err.reason + ? err.reason + : `Account “${acct.name}†is not linked properly. Please link it again.`, }); } else { errors.push({ diff --git a/packages/loot-core/src/server/post.ts b/packages/loot-core/src/server/post.ts index cc6bc8f13..8b49c359d 100644 --- a/packages/loot-core/src/server/post.ts +++ b/packages/loot-core/src/server/post.ts @@ -19,19 +19,24 @@ function throwIfNot200(res, text) { } } -export async function post(url, data, headers = {}) { +export async function post(url, data, headers = {}, timeout = null) { let text; let res; try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + const signal = timeout ? controller.signal : null; res = await fetch(url, { method: 'POST', body: JSON.stringify(data), + signal, headers: { ...headers, 'Content-Type': 'application/json', }, }); + clearTimeout(timeoutId); text = await res.text(); } catch (err) { throw new PostError('network-failure'); diff --git a/upcoming-release-notes/2891.md b/upcoming-release-notes/2891.md new file mode 100644 index 000000000..ec18e60e6 --- /dev/null +++ b/upcoming-release-notes/2891.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [psybers] +--- + +Timeout SimpleFIN sync calls after 60 seconds. -- GitLab