mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-11 12:43:09 -05:00
:electron: Prep for configure server page (#4847)
* prep work for configure server page * release notes * better log message
This commit is contained in:
@@ -86,6 +86,12 @@ global.Actual = {
|
||||
});
|
||||
},
|
||||
|
||||
startSyncServer: () => {},
|
||||
|
||||
stopSyncServer: () => {},
|
||||
|
||||
isSyncServerRunning: () => false,
|
||||
|
||||
startOAuthServer: () => {
|
||||
return '';
|
||||
},
|
||||
|
||||
@@ -65,7 +65,7 @@ if (isPlaywrightTest) {
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let clientWin: BrowserWindow | null;
|
||||
let serverProcess: UtilityProcess | null;
|
||||
let actualServerProcess: UtilityProcess | null;
|
||||
let syncServerProcess: UtilityProcess | null;
|
||||
|
||||
let oAuthServer: ReturnType<typeof createServer> | null;
|
||||
|
||||
@@ -264,19 +264,19 @@ async function startSyncServer() {
|
||||
let syncServerStarted = false;
|
||||
|
||||
const syncServerPromise = new Promise<void>(resolve => {
|
||||
actualServerProcess = utilityProcess.fork(serverPath, [], forkOptions);
|
||||
syncServerProcess = utilityProcess.fork(serverPath, [], forkOptions);
|
||||
|
||||
actualServerProcess.stdout?.on('data', (chunk: Buffer) => {
|
||||
syncServerProcess.stdout?.on('data', (chunk: Buffer) => {
|
||||
// Send the Server console.log messages to the main browser window
|
||||
logMessage('info', `Sync-Server: ${chunk.toString('utf8')}`);
|
||||
});
|
||||
|
||||
actualServerProcess.stderr?.on('data', (chunk: Buffer) => {
|
||||
syncServerProcess.stderr?.on('data', (chunk: Buffer) => {
|
||||
// Send the Server console.error messages out to the main browser window
|
||||
logMessage('error', `Sync-Server: ${chunk.toString('utf8')}`);
|
||||
});
|
||||
|
||||
actualServerProcess.on('message', msg => {
|
||||
syncServerProcess.on('message', msg => {
|
||||
switch (msg.type) {
|
||||
case 'server-started':
|
||||
logMessage('info', 'Sync-Server: Actual Sync Server has started!');
|
||||
@@ -310,6 +310,12 @@ async function startSyncServer() {
|
||||
}
|
||||
}
|
||||
|
||||
async function stopSyncServer() {
|
||||
syncServerProcess?.kill();
|
||||
syncServerProcess = null;
|
||||
logMessage('info', 'Sync-Server: Stopped');
|
||||
}
|
||||
|
||||
async function createWindow() {
|
||||
const windowState = await getWindowState();
|
||||
|
||||
@@ -548,6 +554,14 @@ ipcMain.on('get-bootstrap-data', event => {
|
||||
event.returnValue = payload;
|
||||
});
|
||||
|
||||
ipcMain.handle('start-sync-server', async () => startSyncServer());
|
||||
|
||||
ipcMain.handle('stop-sync-server', async () => stopSyncServer());
|
||||
|
||||
ipcMain.handle('is-sync-server-running', async () =>
|
||||
syncServerProcess ? true : false,
|
||||
);
|
||||
|
||||
ipcMain.handle('start-oauth-server', async () => {
|
||||
const { url, server: newServer } = await createOAuthServer();
|
||||
oAuthServer = newServer;
|
||||
|
||||
@@ -29,6 +29,12 @@ contextBridge.exposeInMainWorld('Actual', {
|
||||
});
|
||||
},
|
||||
|
||||
startSyncServer: () => ipcRenderer.invoke('start-sync-server'),
|
||||
|
||||
stopSyncServer: () => ipcRenderer.invoke('stop-sync-server'),
|
||||
|
||||
isSyncServerRunning: () => ipcRenderer.invoke('is-sync-server-running'),
|
||||
|
||||
startOAuthServer: () => ipcRenderer.invoke('start-oauth-server'),
|
||||
|
||||
relaunch: () => {
|
||||
|
||||
@@ -20,14 +20,19 @@ export const removeItem: T.RemoveItem = async function (key) {
|
||||
|
||||
export async function multiGet<K extends readonly (keyof GlobalPrefsJson)[]>(
|
||||
keys: K,
|
||||
) {
|
||||
return new Promise(function (resolve) {
|
||||
return resolve(
|
||||
keys.map(function (key) {
|
||||
return [key, store[key]];
|
||||
}) as { [P in keyof K]: [K[P], GlobalPrefsJson[K[P]]] },
|
||||
);
|
||||
});
|
||||
): Promise<{ [P in K[number]]: GlobalPrefsJson[P] }> {
|
||||
const results = keys.map(key => [key, store[key]]) as {
|
||||
[P in keyof K]: [K[P], GlobalPrefsJson[K[P]]];
|
||||
};
|
||||
|
||||
// Convert the array of tuples to an object with properly typed properties
|
||||
return results.reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
},
|
||||
{} as { [P in K[number]]: GlobalPrefsJson[P] },
|
||||
);
|
||||
}
|
||||
|
||||
export const multiSet: T.MultiSet = async function (keyValues) {
|
||||
|
||||
@@ -21,7 +21,8 @@ export type RemoveItem = typeof removeItem;
|
||||
|
||||
export function multiGet<K extends readonly (keyof GlobalPrefsJson)[]>(
|
||||
keys: K,
|
||||
): Promise<{ [P in keyof K]: [K[P], GlobalPrefsJson[K[P]]] }>;
|
||||
): Promise<{ [P in K[number]]: GlobalPrefsJson[P] }>;
|
||||
|
||||
export type MultiGet = typeof multiGet;
|
||||
|
||||
export function multiSet<K extends keyof GlobalPrefsJson>(
|
||||
|
||||
@@ -58,10 +58,19 @@ export const removeItem: T.RemoveItem = function (key) {
|
||||
|
||||
export async function multiGet<K extends readonly (keyof GlobalPrefsJson)[]>(
|
||||
keys: K,
|
||||
) {
|
||||
return keys.map(key => [key, store[key]]) as {
|
||||
): Promise<{ [P in K[number]]: GlobalPrefsJson[P] }> {
|
||||
const results = keys.map(key => [key, store[key]]) as {
|
||||
[P in keyof K]: [K[P], GlobalPrefsJson[K[P]]];
|
||||
};
|
||||
|
||||
// Convert the array of tuples to an object with properly typed properties
|
||||
return results.reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
},
|
||||
{} as { [P in K[number]]: GlobalPrefsJson[P] },
|
||||
);
|
||||
}
|
||||
|
||||
export const multiSet: T.MultiSet = function (keyValues) {
|
||||
|
||||
@@ -50,25 +50,37 @@ export const removeItem: T.RemoveItem = async function (key) {
|
||||
|
||||
export async function multiGet<K extends readonly (keyof GlobalPrefsJson)[]>(
|
||||
keys: K,
|
||||
) {
|
||||
): Promise<{ [P in K[number]]: GlobalPrefsJson[P] }> {
|
||||
const db = await getDatabase();
|
||||
|
||||
const transaction = db.transaction(['asyncStorage'], 'readonly');
|
||||
const objectStore = transaction.objectStore('asyncStorage');
|
||||
|
||||
const promise = Promise.all(
|
||||
const results = await Promise.all(
|
||||
keys.map(key => {
|
||||
return new Promise<[string, string]>((resolve, reject) => {
|
||||
const req = objectStore.get(key);
|
||||
req.onerror = e => reject(e);
|
||||
// @ts-expect-error fix me
|
||||
req.onsuccess = e => resolve([key, e.target.result]);
|
||||
});
|
||||
return new Promise<[K[number], GlobalPrefsJson[K[number]]]>(
|
||||
(resolve, reject) => {
|
||||
const req = objectStore.get(key);
|
||||
req.onerror = e => reject(e);
|
||||
req.onsuccess = e => {
|
||||
const target = e.target as IDBRequest<GlobalPrefsJson[K[number]]>;
|
||||
resolve([key, target.result]);
|
||||
};
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
transaction.commit();
|
||||
return promise;
|
||||
|
||||
// Convert the array of tuples to an object with properly typed properties
|
||||
return results.reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
},
|
||||
{} as { [P in K[number]]: GlobalPrefsJson[P] },
|
||||
);
|
||||
}
|
||||
|
||||
export const multiSet: T.MultiSet = async function (keyValues) {
|
||||
|
||||
@@ -883,10 +883,8 @@ async function accountsBankSync({
|
||||
}: {
|
||||
ids: Array<AccountEntity['id']>;
|
||||
}): Promise<SyncResponseWithErrors> {
|
||||
const [[, userId], [, userKey]] = await asyncStorage.multiGet([
|
||||
'user-id',
|
||||
'user-key',
|
||||
]);
|
||||
const { 'user-id': userId, 'user-key': userKey } =
|
||||
await asyncStorage.multiGet(['user-id', 'user-key']);
|
||||
|
||||
const accounts = await db.runQuery<
|
||||
db.DbAccount & { bankId: db.DbBank['bank_id'] }
|
||||
|
||||
@@ -99,21 +99,25 @@ async function saveGlobalPrefs(prefs: GlobalPrefs) {
|
||||
prefs.serverSelfSignedCert,
|
||||
);
|
||||
}
|
||||
if (prefs.syncServerConfig !== undefined) {
|
||||
await asyncStorage.setItem('syncServerConfig', prefs.syncServerConfig);
|
||||
}
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
async function loadGlobalPrefs(): Promise<GlobalPrefs> {
|
||||
const [
|
||||
[, floatingSidebar],
|
||||
[, categoryExpandedState],
|
||||
[, maxMonths],
|
||||
[, documentDir],
|
||||
[, encryptKey],
|
||||
[, language],
|
||||
[, theme],
|
||||
[, preferredDarkTheme],
|
||||
[, serverSelfSignedCert],
|
||||
] = await asyncStorage.multiGet([
|
||||
const {
|
||||
'floating-sidebar': floatingSidebar,
|
||||
'category-expanded-state': categoryExpandedState,
|
||||
'max-months': maxMonths,
|
||||
'document-dir': documentDir,
|
||||
'encrypt-key': encryptKey,
|
||||
language,
|
||||
theme,
|
||||
'preferred-dark-theme': preferredDarkTheme,
|
||||
'server-self-signed-cert': serverSelfSignedCert,
|
||||
syncServerConfig,
|
||||
} = await asyncStorage.multiGet([
|
||||
'floating-sidebar',
|
||||
'category-expanded-state',
|
||||
'max-months',
|
||||
@@ -123,6 +127,7 @@ async function loadGlobalPrefs(): Promise<GlobalPrefs> {
|
||||
'theme',
|
||||
'preferred-dark-theme',
|
||||
'server-self-signed-cert',
|
||||
'syncServerConfig',
|
||||
] as const);
|
||||
return {
|
||||
floatingSidebar: floatingSidebar === 'true',
|
||||
@@ -144,6 +149,7 @@ async function loadGlobalPrefs(): Promise<GlobalPrefs> {
|
||||
? preferredDarkTheme
|
||||
: 'dark',
|
||||
serverSelfSignedCert: serverSelfSignedCert || undefined,
|
||||
syncServerConfig: syncServerConfig || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
3
packages/loot-core/typings/window.d.ts
vendored
3
packages/loot-core/typings/window.d.ts
vendored
@@ -29,6 +29,9 @@ type Actual = {
|
||||
) => void;
|
||||
isUpdateReadyForDownload: () => boolean;
|
||||
waitForUpdateReadyForDownload: () => Promise<void>;
|
||||
startSyncServer: () => Promise<void>;
|
||||
stopSyncServer: () => Promise<void>;
|
||||
isSyncServerRunning: () => Promise<boolean>;
|
||||
startOAuthServer: () => Promise<string>;
|
||||
};
|
||||
|
||||
|
||||
6
upcoming-release-notes/4847.md
Normal file
6
upcoming-release-notes/4847.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Enhancements
|
||||
authors: [MikesGlitch]
|
||||
---
|
||||
|
||||
Allowing the UI to call internal sync server commands when running in the Desktop app
|
||||
Reference in New Issue
Block a user