From 04f34ed57a3a6da6d2a8996eaeb1641836e032d0 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Thu, 20 Feb 2025 10:12:43 -0800 Subject: [PATCH 1/5] Extract preferences related server handlers from main.ts to server/preferences/app.ts --- packages/loot-core/src/server/main.ts | 100 +------------ .../loot-core/src/server/preferences/app.ts | 140 ++++++++++++++++-- .../server/preferences/types/handlers.d.ts | 10 -- .../loot-core/src/types/server-handlers.d.ts | 9 -- 4 files changed, 132 insertions(+), 127 deletions(-) delete mode 100644 packages/loot-core/src/server/preferences/types/handlers.d.ts diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts index 3e25b3723c0..e13cd0d35f3 100644 --- a/packages/loot-core/src/server/main.ts +++ b/packages/loot-core/src/server/main.ts @@ -14,7 +14,6 @@ import { logger } from '../platform/server/log'; import * as sqlite from '../platform/server/sqlite'; import * as monthUtils from '../shared/months'; import { q } from '../shared/query'; -import { stringToInteger } from '../shared/util'; import { type Budget } from '../types/budget'; import { Handlers } from '../types/handlers'; import { OpenIdConfig } from '../types/models/openid'; @@ -501,103 +500,6 @@ handlers['query'] = async function (query) { return aqlQuery(query); }; -handlers['save-global-prefs'] = async function (prefs) { - if ('maxMonths' in prefs) { - await asyncStorage.setItem('max-months', '' + prefs.maxMonths); - } - if ('documentDir' in prefs) { - if (await fs.exists(prefs.documentDir)) { - await asyncStorage.setItem('document-dir', prefs.documentDir); - } - } - if ('floatingSidebar' in prefs) { - await asyncStorage.setItem('floating-sidebar', '' + prefs.floatingSidebar); - } - if ('language' in prefs) { - await asyncStorage.setItem('language', prefs.language); - } - if ('theme' in prefs) { - await asyncStorage.setItem('theme', prefs.theme); - } - if ('preferredDarkTheme' in prefs) { - await asyncStorage.setItem( - 'preferred-dark-theme', - prefs.preferredDarkTheme, - ); - } - if ('serverSelfSignedCert' in prefs) { - await asyncStorage.setItem( - 'server-self-signed-cert', - prefs.serverSelfSignedCert, - ); - } - return 'ok'; -}; - -handlers['load-global-prefs'] = async function () { - const [ - [, floatingSidebar], - [, maxMonths], - [, documentDir], - [, encryptKey], - [, language], - [, theme], - [, preferredDarkTheme], - [, serverSelfSignedCert], - ] = await asyncStorage.multiGet([ - 'floating-sidebar', - 'max-months', - 'document-dir', - 'encrypt-key', - 'language', - 'theme', - 'preferred-dark-theme', - 'server-self-signed-cert', - ] as const); - return { - floatingSidebar: floatingSidebar === 'true' ? true : false, - maxMonths: stringToInteger(maxMonths || ''), - documentDir: documentDir || getDefaultDocumentDir(), - keyId: encryptKey && JSON.parse(encryptKey).id, - language, - theme: - theme === 'light' || - theme === 'dark' || - theme === 'auto' || - theme === 'development' || - theme === 'midnight' - ? theme - : 'auto', - preferredDarkTheme: - preferredDarkTheme === 'dark' || preferredDarkTheme === 'midnight' - ? preferredDarkTheme - : 'dark', - serverSelfSignedCert: serverSelfSignedCert || undefined, - }; -}; - -handlers['save-prefs'] = async function (prefsToSet) { - const { cloudFileId } = prefs.getPrefs(); - - // Need to sync the budget name on the server as well - if (prefsToSet.budgetName && cloudFileId) { - const userToken = await asyncStorage.getItem('user-token'); - - await post(getServer().SYNC_SERVER + '/update-user-filename', { - token: userToken, - fileId: cloudFileId, - name: prefsToSet.budgetName, - }); - } - - await prefs.savePrefs(prefsToSet); - return 'ok'; -}; - -handlers['load-prefs'] = async function () { - return prefs.getPrefs(); -}; - handlers['sync-reset'] = async function () { return await resetSync(); }; @@ -1606,7 +1508,7 @@ app.combine( accountsApp, ); -function getDefaultDocumentDir() { +export function getDefaultDocumentDir() { if (Platform.isMobile) { // On mobile, unfortunately we need to be backwards compatible // with the old folder structure which does not store files inside diff --git a/packages/loot-core/src/server/preferences/app.ts b/packages/loot-core/src/server/preferences/app.ts index d1c73344e70..f959448c8c1 100644 --- a/packages/loot-core/src/server/preferences/app.ts +++ b/packages/loot-core/src/server/preferences/app.ts @@ -1,24 +1,52 @@ -import { type SyncedPrefs } from '../../types/prefs'; +import * as asyncStorage from '../../platform/server/asyncStorage'; +import * as fs from '../../platform/server/fs'; +import { stringToInteger } from '../../shared/util'; +import { + GlobalPrefs, + MetadataPrefs, + type SyncedPrefs, +} from '../../types/prefs'; import { createApp } from '../app'; import * as db from '../db'; +import { getDefaultDocumentDir } from '../main'; import { mutator } from '../mutators'; +import { post } from '../post'; +import { + getPrefs as _getMetadataPrefs, + savePrefs as _saveMetadataPrefs, +} from '../prefs'; +import { getServer } from '../server-config'; import { undoable } from '../undo'; -import { PreferencesHandlers } from './types/handlers'; +export interface PreferencesHandlers { + 'preferences/save': typeof saveSyncedPrefs; + 'preferences/get': typeof getSyncedPrefs; + 'save-global-prefs': typeof saveGlobalPrefs; + 'load-global-prefs': typeof loadGlobalPrefs; + 'save-prefs': typeof saveMetadataPrefs; + 'load-prefs': typeof loadMetadataPrefs; +} export const app = createApp(); -const savePreferences = async ({ +app.method('preferences/save', mutator(undoable(saveSyncedPrefs))); +app.method('preferences/get', getSyncedPrefs); +app.method('save-global-prefs', saveGlobalPrefs); +app.method('load-global-prefs', loadGlobalPrefs); +app.method('save-prefs', saveMetadataPrefs); +app.method('load-prefs', loadMetadataPrefs); + +async function saveSyncedPrefs({ id, value, }: { id: keyof SyncedPrefs; value: string | undefined; -}) => { +}) { await db.update('preferences', { id, value }); -}; +} -const getPreferences = async (): Promise => { +async function getSyncedPrefs(): Promise { const prefs = (await db.all('SELECT id, value FROM preferences')) as Array<{ id: string; value: string; @@ -28,7 +56,101 @@ const getPreferences = async (): Promise => { carry[id as keyof SyncedPrefs] = value; return carry; }, {}); -}; +} + +async function saveGlobalPrefs(prefs: GlobalPrefs) { + if ('maxMonths' in prefs) { + await asyncStorage.setItem('max-months', '' + prefs.maxMonths); + } + if ('documentDir' in prefs) { + if (await fs.exists(prefs.documentDir)) { + await asyncStorage.setItem('document-dir', prefs.documentDir); + } + } + if ('floatingSidebar' in prefs) { + await asyncStorage.setItem('floating-sidebar', '' + prefs.floatingSidebar); + } + if ('language' in prefs) { + await asyncStorage.setItem('language', prefs.language); + } + if ('theme' in prefs) { + await asyncStorage.setItem('theme', prefs.theme); + } + if ('preferredDarkTheme' in prefs) { + await asyncStorage.setItem( + 'preferred-dark-theme', + prefs.preferredDarkTheme, + ); + } + if ('serverSelfSignedCert' in prefs) { + await asyncStorage.setItem( + 'server-self-signed-cert', + prefs.serverSelfSignedCert, + ); + } + return 'ok'; +} + +async function loadGlobalPrefs() { + const [ + [, floatingSidebar], + [, maxMonths], + [, documentDir], + [, encryptKey], + [, language], + [, theme], + [, preferredDarkTheme], + [, serverSelfSignedCert], + ] = await asyncStorage.multiGet([ + 'floating-sidebar', + 'max-months', + 'document-dir', + 'encrypt-key', + 'language', + 'theme', + 'preferred-dark-theme', + 'server-self-signed-cert', + ] as const); + return { + floatingSidebar: floatingSidebar === 'true' ? true : false, + maxMonths: stringToInteger(maxMonths || ''), + documentDir: documentDir || getDefaultDocumentDir(), + keyId: encryptKey && JSON.parse(encryptKey).id, + language, + theme: + theme === 'light' || + theme === 'dark' || + theme === 'auto' || + theme === 'development' || + theme === 'midnight' + ? theme + : 'auto', + preferredDarkTheme: + preferredDarkTheme === 'dark' || preferredDarkTheme === 'midnight' + ? preferredDarkTheme + : 'dark', + serverSelfSignedCert: serverSelfSignedCert || undefined, + }; +} + +async function saveMetadataPrefs(prefsToSet: MetadataPrefs) { + const { cloudFileId } = _getMetadataPrefs(); + + // Need to sync the budget name on the server as well + if (prefsToSet.budgetName && cloudFileId) { + const userToken = await asyncStorage.getItem('user-token'); + + await post(getServer().SYNC_SERVER + '/update-user-filename', { + token: userToken, + fileId: cloudFileId, + name: prefsToSet.budgetName, + }); + } + + await _saveMetadataPrefs(prefsToSet); + return 'ok'; +} -app.method('preferences/save', mutator(undoable(savePreferences))); -app.method('preferences/get', getPreferences); +function loadMetadataPrefs(): MetadataPrefs { + return _getMetadataPrefs(); +} diff --git a/packages/loot-core/src/server/preferences/types/handlers.d.ts b/packages/loot-core/src/server/preferences/types/handlers.d.ts deleted file mode 100644 index 8af5516bd1f..00000000000 --- a/packages/loot-core/src/server/preferences/types/handlers.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { type SyncedPrefs } from '../../../types/prefs'; - -export interface PreferencesHandlers { - 'preferences/save': (arg: { - id: keyof SyncedPrefs; - value: string | undefined; - }) => Promise; - - 'preferences/get': () => Promise; -} diff --git a/packages/loot-core/src/types/server-handlers.d.ts b/packages/loot-core/src/types/server-handlers.d.ts index 80f1001502e..26ff36f1906 100644 --- a/packages/loot-core/src/types/server-handlers.d.ts +++ b/packages/loot-core/src/types/server-handlers.d.ts @@ -11,7 +11,6 @@ import { PayeeEntity, } from './models'; import { OpenIdConfig } from './models/openid'; -import { GlobalPrefs, MetadataPrefs } from './prefs'; // eslint-disable-next-line import/no-unresolved import { Query } from './query'; import { EmptyObject } from './util'; @@ -118,14 +117,6 @@ export interface ServerHandlers { // eslint-disable-next-line @typescript-eslint/no-explicit-any query: (query: Query) => Promise<{ data: any; dependencies: string[] }>; - 'save-global-prefs': (prefs) => Promise<'ok'>; - - 'load-global-prefs': () => Promise; - - 'save-prefs': (prefsToSet) => Promise<'ok'>; - - 'load-prefs': () => Promise; - 'sync-reset': () => Promise<{ error?: { reason: string; meta?: unknown } }>; 'sync-repair': () => Promise; From ab8a25150b91e77521350c9a768018215c096939 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Thu, 20 Feb 2025 10:54:14 -0800 Subject: [PATCH 2/5] Release notes --- upcoming-release-notes/4420.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 upcoming-release-notes/4420.md diff --git a/upcoming-release-notes/4420.md b/upcoming-release-notes/4420.md new file mode 100644 index 00000000000..ab175def739 --- /dev/null +++ b/upcoming-release-notes/4420.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [joel-jeremy] +--- + +Extract preferences related server handlers from main.ts to server/preferences/app.ts \ No newline at end of file From d5a7ca14d5170d2ac49e2b1aa70ef731c072ae61 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Thu, 20 Feb 2025 10:55:13 -0800 Subject: [PATCH 3/5] Fix types --- packages/loot-core/src/types/handlers.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/loot-core/src/types/handlers.d.ts b/packages/loot-core/src/types/handlers.d.ts index 1b5057bbcf2..24f666d08d5 100644 --- a/packages/loot-core/src/types/handlers.d.ts +++ b/packages/loot-core/src/types/handlers.d.ts @@ -4,7 +4,7 @@ import type { BudgetHandlers } from '../server/budget/types/handlers'; import type { DashboardHandlers } from '../server/dashboard/types/handlers'; import type { FiltersHandlers } from '../server/filters/types/handlers'; import type { NotesHandlers } from '../server/notes/types/handlers'; -import type { PreferencesHandlers } from '../server/preferences/types/handlers'; +import type { PreferencesHandlers } from '../server/preferences/app'; import type { ReportsHandlers } from '../server/reports/types/handlers'; import type { RulesHandlers } from '../server/rules/types/handlers'; import type { SchedulesHandlers } from '../server/schedules/types/handlers'; From 66924d5e5f1ac3e6ff825b789df217fccdbe46d2 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Thu, 20 Feb 2025 11:04:41 -0800 Subject: [PATCH 4/5] FIx typecheck errors --- packages/loot-core/src/server/preferences/app.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/loot-core/src/server/preferences/app.ts b/packages/loot-core/src/server/preferences/app.ts index f959448c8c1..c7a39e7f35b 100644 --- a/packages/loot-core/src/server/preferences/app.ts +++ b/packages/loot-core/src/server/preferences/app.ts @@ -63,7 +63,7 @@ async function saveGlobalPrefs(prefs: GlobalPrefs) { await asyncStorage.setItem('max-months', '' + prefs.maxMonths); } if ('documentDir' in prefs) { - if (await fs.exists(prefs.documentDir)) { + if (prefs.documentDir && (await fs.exists(prefs.documentDir))) { await asyncStorage.setItem('document-dir', prefs.documentDir); } } @@ -140,7 +140,12 @@ async function saveMetadataPrefs(prefsToSet: MetadataPrefs) { if (prefsToSet.budgetName && cloudFileId) { const userToken = await asyncStorage.getItem('user-token'); - await post(getServer().SYNC_SERVER + '/update-user-filename', { + const syncServer = getServer()?.SYNC_SERVER; + if (!syncServer) { + throw new Error('No sync server set'); + } + + await post(syncServer + '/update-user-filename', { token: userToken, fileId: cloudFileId, name: prefsToSet.budgetName, @@ -151,6 +156,6 @@ async function saveMetadataPrefs(prefsToSet: MetadataPrefs) { return 'ok'; } -function loadMetadataPrefs(): MetadataPrefs { +async function loadMetadataPrefs(): Promise { return _getMetadataPrefs(); } From 62f63b6939aa057e82091f4ede69c09f30a889e3 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Fri, 21 Feb 2025 08:35:26 -0800 Subject: [PATCH 5/5] Code review --- packages/loot-core/src/server/preferences/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/loot-core/src/server/preferences/app.ts b/packages/loot-core/src/server/preferences/app.ts index c7a39e7f35b..a174da7e797 100644 --- a/packages/loot-core/src/server/preferences/app.ts +++ b/packages/loot-core/src/server/preferences/app.ts @@ -112,7 +112,7 @@ async function loadGlobalPrefs() { 'server-self-signed-cert', ] as const); return { - floatingSidebar: floatingSidebar === 'true' ? true : false, + floatingSidebar: floatingSidebar === 'true', maxMonths: stringToInteger(maxMonths || ''), documentDir: documentDir || getDefaultDocumentDir(), keyId: encryptKey && JSON.parse(encryptKey).id,