diff --git a/packages/desktop-client/src/components/ServerContext.js b/packages/desktop-client/src/components/ServerContext.js index 27050331855fd6ddaf2a89e4c0560fb731a94655..ae0fb1b4355779c4ce265f2c1a4dade38e57ad57 100644 --- a/packages/desktop-client/src/components/ServerContext.js +++ b/packages/desktop-client/src/components/ServerContext.js @@ -14,14 +14,6 @@ export const useServerURL = () => useContext(ServerContext).url; export const useServerVersion = () => useContext(ServerContext).version; export const useSetServerURL = () => useContext(ServerContext).setURL; -async function getServerUrl() { - let url = (await send('get-server-url')) || ''; - if (url === 'https://not-configured/') { - url = ''; - } - return url; -} - async function getServerVersion() { let { error, version } = await send('get-server-version'); if (error) { @@ -36,7 +28,7 @@ export function ServerProvider({ children }) { useEffect(() => { async function run() { - setServerURL(await getServerUrl()); + setServerURL(await send('get-server-url')); setVersion(await getServerVersion()); } run(); @@ -46,7 +38,7 @@ export function ServerProvider({ children }) { async (url, opts = {}) => { let { error } = await send('set-server-url', { ...opts, url }); if (!error) { - setServerURL(await getServerUrl()); + setServerURL(await send('get-server-url')); setVersion(await getServerVersion()); } return { error }; diff --git a/packages/desktop-client/src/components/manager/subscribe/common.tsx b/packages/desktop-client/src/components/manager/subscribe/common.tsx index ed96e65706752a7e13eb638faa145a07caba2346..9e0082fb7ae99124e36e618783f804d48f7cf010 100644 --- a/packages/desktop-client/src/components/manager/subscribe/common.tsx +++ b/packages/desktop-client/src/components/manager/subscribe/common.tsx @@ -41,7 +41,8 @@ export function useBootstrapped() { }; let url = await send('get-server-url'); - if (url == null) { + let bootstrapped = await send('get-did-bootstrap'); + if (url == null && !bootstrapped) { // A server hasn't been specified yet let serverURL = window.location.origin; let result = await send('subscribe-needs-bootstrap', { diff --git a/packages/desktop-client/src/hooks/useFeatureFlag.ts b/packages/desktop-client/src/hooks/useFeatureFlag.ts index 0d55d746aaba59a09df748e53ff56f86dc98de2c..7aa96bfb45655c2f213e35691771e6bc0df3e81d 100644 --- a/packages/desktop-client/src/hooks/useFeatureFlag.ts +++ b/packages/desktop-client/src/hooks/useFeatureFlag.ts @@ -1,6 +1,7 @@ import { useSelector } from 'react-redux'; const DEFAULT_FEATURE_FLAG_STATE: Record<string, boolean> = { + reportBudget: false, syncAccount: false, goalTemplatesEnabled: false, }; diff --git a/packages/loot-core/src/server/accounts/sync.ts b/packages/loot-core/src/server/accounts/sync.ts index 18b6aabc033acebffb804eff38ee1d95f84db2a0..7cf272d1f7c165e82c6c5f9c288755e91422acdc 100644 --- a/packages/loot-core/src/server/accounts/sync.ts +++ b/packages/loot-core/src/server/accounts/sync.ts @@ -77,27 +77,27 @@ export async function getAccounts(userId, userKey, id) { export async function getNordigenAccounts(userId, userKey, id) { const userToken = await asyncStorage.getItem('user-token'); - if (userToken) { - let res = await post( - getServer().NORDIGEN_SERVER + '/accounts', - { - userId, - key: userKey, - item_id: id, - }, - { - 'X-ACTUAL-TOKEN': userToken, - }, - ); + if (!userToken) return; - let { accounts } = res; + let res = await post( + getServer().NORDIGEN_SERVER + '/accounts', + { + userId, + key: userKey, + item_id: id, + }, + { + 'X-ACTUAL-TOKEN': userToken, + }, + ); - accounts.forEach(acct => { - acct.balances.current = getAccountBalance(acct); - }); + let { accounts } = res; - return accounts; - } + accounts.forEach(acct => { + acct.balances.current = getAccountBalance(acct); + }); + + return accounts; } export function fromPlaid(trans) { @@ -182,41 +182,40 @@ async function downloadNordigenTransactions( since, ) { let userToken = await asyncStorage.getItem('user-token'); - if (userToken) { - const endDate = new Date().toISOString().split('T')[0]; - - const res = await post( - getServer().NORDIGEN_SERVER + '/transactions', - { - userId: userId, - key: userKey, - requisitionId: bankId, - accountId: acctId, - startDate: since, - endDate, - }, - { - 'X-ACTUAL-TOKEN': userToken, - }, - ); + if (!userToken) return; - if (res.error_code) { - throw BankSyncError(res.error_type, res.error_code); - } + const endDate = new Date().toISOString().split('T')[0]; - const { - transactions: { all }, - balances, - startingBalance, - } = res; + const res = await post( + getServer().NORDIGEN_SERVER + '/transactions', + { + userId: userId, + key: userKey, + requisitionId: bankId, + accountId: acctId, + startDate: since, + endDate, + }, + { + 'X-ACTUAL-TOKEN': userToken, + }, + ); - return { - transactions: all, - accountBalance: balances, - startingBalance, - }; + if (res.error_code) { + throw BankSyncError(res.error_type, res.error_code); } - return; + + const { + transactions: { all }, + balances, + startingBalance, + } = res; + + return { + transactions: all, + accountBalance: balances, + startingBalance, + }; } async function resolvePayee(trans, payeeName, payeesToCreate) { diff --git a/packages/loot-core/src/server/cloud-storage.ts b/packages/loot-core/src/server/cloud-storage.ts index 12dafc4bddfcd57451fc12d05067435693a1df17..6c7cc80b5fca7f835cbf20152cf8d62e3a3275be 100644 --- a/packages/loot-core/src/server/cloud-storage.ts +++ b/packages/loot-core/src/server/cloud-storage.ts @@ -326,7 +326,7 @@ export async function possiblyUpload() { } export async function removeFile(fileId) { - const [[, userToken]] = await asyncStorage.multiGet(['user-token']); + let userToken = await asyncStorage.getItem('user-token'); await post(getServer().SYNC_SERVER + '/delete-user-file', { token: userToken, diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts index 95c10bd46313cccb012e272be65418cf34524472..f6a1e998c2f79409aa09017e0705b1d9df5bd882 100644 --- a/packages/loot-core/src/server/main.ts +++ b/packages/loot-core/src/server/main.ts @@ -81,7 +81,6 @@ import { uniqueFileName, idFromFileName } from './util/budget-name'; let DEMO_BUDGET_ID = '_demo-budget'; let TEST_BUDGET_ID = '_test-budget'; -let UNCONFIGURED_SERVER = 'https://not-configured/'; // util @@ -1132,40 +1131,42 @@ handlers['accounts-sync'] = async function ({ id }) { handlers['secret-set'] = async function ({ name, value }) { let userToken = await asyncStorage.getItem('user-token'); - if (userToken) { - try { - return await post( - getServer().BASE_SERVER + '/secret', - { - name, - value, - }, - { - 'X-ACTUAL-TOKEN': userToken, - }, - ); - } catch (error) { - console.error(error); - return { error: 'failed' }; - } + if (!userToken) { + return { error: 'unauthorized' }; + } + + try { + return await post( + getServer().BASE_SERVER + '/secret', + { + name, + value, + }, + { + 'X-ACTUAL-TOKEN': userToken, + }, + ); + } catch (error) { + console.error(error); + return { error: 'failed' }; } - return { error: 'unauthorized' }; }; handlers['secret-check'] = async function (name) { let userToken = await asyncStorage.getItem('user-token'); - if (userToken) { - try { - return await get(getServer().BASE_SERVER + '/secret/' + name, { - 'X-ACTUAL-TOKEN': userToken, - }); - } catch (error) { - console.error(error); - return { error: 'failed' }; - } + if (!userToken) { + return { error: 'unauthorized' }; + } + + try { + return await get(getServer().BASE_SERVER + '/secret/' + name, { + 'X-ACTUAL-TOKEN': userToken, + }); + } catch (error) { + console.error(error); + return { error: 'failed' }; } - return { error: 'unauthorized' }; }; handlers['nordigen-poll-web-token'] = async function ({ @@ -1173,62 +1174,59 @@ handlers['nordigen-poll-web-token'] = async function ({ requisitionId, }) { let userToken = await asyncStorage.getItem('user-token'); + if (!userToken) return null; - if (userToken) { - let startTime = Date.now(); - stopPolling = false; + let startTime = Date.now(); + stopPolling = false; - async function getData(cb) { - if (stopPolling) { - return; - } + async function getData(cb) { + if (stopPolling) { + return; + } - if (Date.now() - startTime >= 1000 * 60 * 10) { - cb('timeout'); - return; - } + if (Date.now() - startTime >= 1000 * 60 * 10) { + cb('timeout'); + return; + } - let data = await post( - getServer().NORDIGEN_SERVER + '/get-accounts', - { - upgradingAccountId, - requisitionId, - }, - { - 'X-ACTUAL-TOKEN': userToken, - }, - ); + let data = await post( + getServer().NORDIGEN_SERVER + '/get-accounts', + { + upgradingAccountId, + requisitionId, + }, + { + 'X-ACTUAL-TOKEN': userToken, + }, + ); - if (data) { - if (data.error) { - cb('unknown'); - } else { - cb(null, data); - } + if (data) { + if (data.error) { + cb('unknown'); } else { - setTimeout(() => getData(cb), 3000); + cb(null, data); } + } else { + setTimeout(() => getData(cb), 3000); } - - return new Promise(resolve => { - getData((error, data) => { - if (error) { - resolve({ error }); - } else { - resolve({ data }); - } - }); - }); } - return null; + return new Promise(resolve => { + getData((error, data) => { + if (error) { + resolve({ error }); + } else { + resolve({ data }); + } + }); + }); }; handlers['nordigen-status'] = async function () { const userToken = await asyncStorage.getItem('user-token'); if (!userToken) { - return Promise.reject({ error: 'unauthorized' }); + return { error: 'unauthorized' }; } return post( @@ -1244,7 +1242,7 @@ handlers['nordigen-get-banks'] = async function (country) { const userToken = await asyncStorage.getItem('user-token'); if (!userToken) { - return Promise.reject({ error: 'unauthorized' }); + return { error: 'unauthorized' }; } return post( @@ -1268,25 +1266,26 @@ handlers['nordigen-create-web-token'] = async function ({ }) { let userToken = await asyncStorage.getItem('user-token'); - if (userToken) { - try { - return await post( - getServer().NORDIGEN_SERVER + '/create-web-token', - { - upgradingAccountId, - institutionId, - accessValidForDays, - }, - { - 'X-ACTUAL-TOKEN': userToken, - }, - ); - } catch (error) { - console.error(error); - return { error: 'failed' }; - } + if (!userToken) { + return { error: 'unauthorized' }; + } + + try { + return await post( + getServer().NORDIGEN_SERVER + '/create-web-token', + { + upgradingAccountId, + institutionId, + accessValidForDays, + }, + { + 'X-ACTUAL-TOKEN': userToken, + }, + ); + } catch (error) { + console.error(error); + return { error: 'failed' }; } - return { error: 'unauthorized' }; }; handlers['nordigen-accounts-sync'] = async function ({ id }) { @@ -1418,8 +1417,11 @@ handlers['account-unlink'] = mutator(async function ({ id }) { // No more accounts are associated with this bank. We can remove // it from Nordigen. let userToken = await asyncStorage.getItem('user-token'); + if (!userToken) { + return 'ok'; + } - if (userToken && count === 0) { + if (count === 0) { let { bank_id: requisitionId } = await db.first( 'SELECT bank_id FROM banks WHERE id = ?', [bankId], @@ -1624,10 +1626,14 @@ handlers['key-test'] = async function ({ fileId, password }) { return {}; }; +handlers['get-did-bootstrap'] = async function () { + return Boolean(await asyncStorage.getItem('did-bootstrap')); +}; + handlers['subscribe-needs-bootstrap'] = async function ({ url, }: { url? } = {}) { - if (getServer(url).BASE_SERVER === UNCONFIGURED_SERVER) { + if (!getServer(url)) { return { bootstrapped: true, hasServer: false }; } @@ -1666,45 +1672,48 @@ handlers['subscribe-bootstrap'] = async function ({ password }) { return { error: 'internal' }; }; -handlers['subscribe-set-user'] = async function ({ token }) { - await asyncStorage.setItem('user-token', token); -}; - handlers['subscribe-get-user'] = async function () { - if (getServer() && getServer().BASE_SERVER === UNCONFIGURED_SERVER) { + if (!getServer()) { + if (!(await asyncStorage.getItem('did-bootstrap'))) { + return null; + } return { offline: false }; } let userToken = await asyncStorage.getItem('user-token'); - if (userToken) { - try { - const res = await get(getServer().SIGNUP_SERVER + '/validate', { - headers: { - 'X-ACTUAL-TOKEN': userToken, - }, - }); - const { status, reason } = JSON.parse(res); + if (!userToken) { + return null; + } - if (status === 'error') { - if (reason === 'unauthorized') { - return null; - } - return { offline: true }; - } + try { + const res = await get(getServer().SIGNUP_SERVER + '/validate', { + headers: { + 'X-ACTUAL-TOKEN': userToken, + }, + }); + const { status, reason } = JSON.parse(res); - return { offline: false }; - } catch (e) { - console.log(e); + if (status === 'error') { + if (reason === 'unauthorized') { + return null; + } return { offline: true }; } - } - return null; + return { offline: false }; + } catch (e) { + console.log(e); + return { offline: true }; + } }; handlers['subscribe-change-password'] = async function ({ password }) { let userToken = await asyncStorage.getItem('user-token'); + if (!userToken) { + return { error: 'not-logged-in' }; + } + try { await post(getServer().SIGNUP_SERVER + '/change-password', { token: userToken, @@ -1742,7 +1751,7 @@ handlers['subscribe-sign-out'] = async function () { }; handlers['get-server-version'] = async function () { - if (!getServer() || getServer().BASE_SERVER === UNCONFIGURED_SERVER) { + if (!getServer()) { return { error: 'no-server' }; } @@ -1764,7 +1773,9 @@ handlers['get-server-url'] = async function () { }; handlers['set-server-url'] = async function ({ url, validate = true }) { - if (url != null) { + if (url == null) { + await asyncStorage.removeItem('user-token'); + } else { if (validate) { // Validate the server is running let { error } = await runHandler(handlers['subscribe-needs-bootstrap'], { @@ -1774,12 +1785,10 @@ handlers['set-server-url'] = async function ({ url, validate = true }) { return { error }; } } - } else { - // When the server isn't configured, we just use a placeholder - url = UNCONFIGURED_SERVER; } - asyncStorage.setItem('server-url', url); + await asyncStorage.setItem('server-url', url); + await asyncStorage.setItem('did-bootstrap', true); setServer(url); return {}; }; @@ -2170,7 +2179,7 @@ async function loadBudget(id) { // Older versions didn't tag the file with the current user, so do // so now if (!prefs.getPrefs().userId) { - let [[, userId]] = await asyncStorage.multiGet(['user-token']); + let userId = await asyncStorage.getItem('user-token'); prefs.savePrefs({ userId }); } @@ -2244,10 +2253,12 @@ async function loadBudget(id) { if (process.env.NODE_ENV !== 'test') { if (process.env.IS_BETA || id === DEMO_BUDGET_ID) { setSyncingMode('disabled'); - } else if (id === TEST_BUDGET_ID) { - await asyncStorage.setItem('lastBudget', id); } else { - setSyncingMode('enabled'); + if (getServer()) { + setSyncingMode('enabled'); + } else { + setSyncingMode('disabled'); + } await asyncStorage.setItem('lastBudget', id); @@ -2405,10 +2416,19 @@ export async function initApp(isDev, socketName) { // } // } - const url = await asyncStorage.getItem('server-url'); - if (url) { - setServer(url); + let url = await asyncStorage.getItem('server-url'); + + // TODO: remove this if statement after a few releases + if (url === 'https://not-configured/') { + url = null; + await asyncStorage.setItem('server-url', null); + await asyncStorage.setItem('did-bootstrap', true); + } + + if (!url) { + await asyncStorage.removeItem('user-token'); } + setServer(url); connection.init(socketName, app.handlers); diff --git a/packages/loot-core/src/types/main-handlers.d.ts b/packages/loot-core/src/types/main-handlers.d.ts index f7158fb2a177181d3c30ea34c20f25fa8e1b512d..669d1acbc96ccf776bfd8d78b9bc89bc6389d526 100644 --- a/packages/loot-core/src/types/main-handlers.d.ts +++ b/packages/loot-core/src/types/main-handlers.d.ts @@ -248,6 +248,8 @@ export interface MainHandlers { 'key-test': (arg: { fileId; password }) => Promise<unknown>; + 'get-did-bootstrap': () => Promise<boolean>; + 'subscribe-needs-bootstrap': ( args: { url } = {}, ) => Promise< @@ -256,8 +258,6 @@ export interface MainHandlers { 'subscribe-bootstrap': (arg: { password }) => Promise<{ error: string }>; - 'subscribe-set-user': (arg: { token }) => Promise<unknown>; - 'subscribe-get-user': () => Promise<{ offline: boolean } | null>; 'subscribe-change-password': (arg: { diff --git a/upcoming-release-notes/984.md b/upcoming-release-notes/984.md new file mode 100644 index 0000000000000000000000000000000000000000..525352b989f483f8ac4698d50a9922fe1bd2c2a3 --- /dev/null +++ b/upcoming-release-notes/984.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [j-f1] +--- + +Stop frontend from attempting to connect to an invalid server when no server is configured