mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-21 15:36:50 -05:00
Extract encryption related server handlers from main.ts to server/encryption/app.ts (#4662)
* Extract encryption related server handlers from main.ts to server/encryption/app.ts * Release notes
This commit is contained in:
committed by
GitHub
parent
6fb7fe1343
commit
c77168fa18
@@ -49,7 +49,7 @@ export function FixEncryptionKeyModal({
|
||||
|
||||
const { error } = await send('key-test', {
|
||||
password,
|
||||
fileId: cloudFileId,
|
||||
cloudFileId,
|
||||
});
|
||||
if (error) {
|
||||
setError(getTestKeyError(error));
|
||||
|
||||
@@ -203,7 +203,7 @@ handlers['api/download-budget'] = async function ({ syncId, password }) {
|
||||
}
|
||||
|
||||
const result = await handlers['key-test']({
|
||||
fileId: remoteBudget ? remoteBudget.fileId : localBudget.cloudFileId,
|
||||
cloudFileId: remoteBudget ? remoteBudget.fileId : localBudget.cloudFileId,
|
||||
password,
|
||||
});
|
||||
if (result.error) {
|
||||
|
||||
371
packages/loot-core/src/server/auth/app.ts
Normal file
371
packages/loot-core/src/server/auth/app.ts
Normal file
@@ -0,0 +1,371 @@
|
||||
import * as asyncStorage from '../../platform/server/asyncStorage';
|
||||
import { OpenIdConfig } from '../../types/models';
|
||||
import { createApp } from '../app';
|
||||
import * as encryption from '../encryption';
|
||||
import { PostError } from '../errors';
|
||||
import { get, post } from '../post';
|
||||
import { getServer, isValidBaseURL } from '../server-config';
|
||||
|
||||
export type AuthHandlers = {
|
||||
'get-did-bootstrap': typeof didBootstrap;
|
||||
'subscribe-needs-bootstrap': typeof needsBootstrap;
|
||||
'subscribe-bootstrap': typeof bootstrap;
|
||||
'subscribe-get-login-methods': typeof getLoginMethods;
|
||||
'subscribe-get-user': typeof getUser;
|
||||
'subscribe-change-password': typeof changePassword;
|
||||
'subscribe-sign-in': typeof signIn;
|
||||
'subscribe-sign-out': typeof signOut;
|
||||
'subscribe-set-token': typeof setToken;
|
||||
'enable-openid': typeof enableOpenId;
|
||||
'get-openid-config': typeof getOpenIdConfig;
|
||||
'enable-password': typeof enablePassword;
|
||||
};
|
||||
|
||||
export const app = createApp<AuthHandlers>();
|
||||
app.method('get-did-bootstrap', didBootstrap);
|
||||
app.method('subscribe-needs-bootstrap', needsBootstrap);
|
||||
app.method('subscribe-bootstrap', bootstrap);
|
||||
app.method('subscribe-get-login-methods', getLoginMethods);
|
||||
app.method('subscribe-get-user', getUser);
|
||||
app.method('subscribe-change-password', changePassword);
|
||||
app.method('subscribe-sign-in', signIn);
|
||||
app.method('subscribe-sign-out', signOut);
|
||||
app.method('subscribe-set-token', setToken);
|
||||
app.method('enable-openid', enableOpenId);
|
||||
app.method('get-openid-config', getOpenIdConfig);
|
||||
app.method('enable-password', enablePassword);
|
||||
|
||||
async function didBootstrap() {
|
||||
return Boolean(await asyncStorage.getItem('did-bootstrap'));
|
||||
}
|
||||
|
||||
async function needsBootstrap({ url }: { url?: string } = {}) {
|
||||
if (url && !isValidBaseURL(url)) {
|
||||
return { error: 'get-server-failure' };
|
||||
}
|
||||
|
||||
let serverConfig: ReturnType<typeof getServer>;
|
||||
|
||||
try {
|
||||
serverConfig = getServer(url);
|
||||
if (!serverConfig) {
|
||||
return { bootstrapped: true, hasServer: false };
|
||||
}
|
||||
} catch (err) {
|
||||
return { error: 'get-server-failure' };
|
||||
}
|
||||
|
||||
let resText: string;
|
||||
try {
|
||||
resText = await get(serverConfig.SIGNUP_SERVER + '/needs-bootstrap');
|
||||
} catch (err) {
|
||||
return { error: 'network-failure' };
|
||||
}
|
||||
|
||||
let res: {
|
||||
status: 'ok';
|
||||
data: {
|
||||
bootstrapped: boolean;
|
||||
loginMethod: 'password' | 'openid' | string;
|
||||
availableLoginMethods: Array<{
|
||||
method: string;
|
||||
displayName: string;
|
||||
active: boolean;
|
||||
}>;
|
||||
multiuser: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
res = JSON.parse(resText);
|
||||
} catch (err) {
|
||||
return { error: 'parse-failure' };
|
||||
}
|
||||
|
||||
return {
|
||||
bootstrapped: res.data.bootstrapped,
|
||||
availableLoginMethods: res.data.availableLoginMethods || [
|
||||
{ method: 'password', active: true, displayName: 'Password' },
|
||||
],
|
||||
multiuser: res.data.multiuser || false,
|
||||
hasServer: true,
|
||||
};
|
||||
}
|
||||
|
||||
async function bootstrap(loginConfig: {
|
||||
password?: string;
|
||||
openId?: OpenIdConfig;
|
||||
}) {
|
||||
try {
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
throw new Error('No sync server configured.');
|
||||
}
|
||||
await post(serverConfig.SIGNUP_SERVER + '/bootstrap', loginConfig);
|
||||
} catch (err) {
|
||||
if (err instanceof PostError) {
|
||||
return {
|
||||
error: err.reason || 'network-failure',
|
||||
};
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async function getLoginMethods() {
|
||||
let res: {
|
||||
methods?: Array<{ method: string; displayName: string; active: boolean }>;
|
||||
};
|
||||
try {
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
throw new Error('No sync server configured.');
|
||||
}
|
||||
res = await fetch(serverConfig.SIGNUP_SERVER + '/login-methods').then(res =>
|
||||
res.json(),
|
||||
);
|
||||
} catch (err) {
|
||||
if (err instanceof PostError) {
|
||||
return {
|
||||
error: err.reason || 'network-failure',
|
||||
};
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (res.methods) {
|
||||
return { methods: res.methods };
|
||||
}
|
||||
return { error: 'internal' };
|
||||
}
|
||||
|
||||
async function getUser() {
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
if (!(await asyncStorage.getItem('did-bootstrap'))) {
|
||||
return null;
|
||||
}
|
||||
return { offline: false };
|
||||
}
|
||||
|
||||
const userToken = await asyncStorage.getItem('user-token');
|
||||
|
||||
if (!userToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await get(serverConfig.SIGNUP_SERVER + '/validate', {
|
||||
headers: {
|
||||
'X-ACTUAL-TOKEN': userToken,
|
||||
},
|
||||
});
|
||||
let tokenExpired = false;
|
||||
const {
|
||||
status,
|
||||
reason,
|
||||
data: {
|
||||
userName = null,
|
||||
permission = '',
|
||||
userId = null,
|
||||
displayName = null,
|
||||
loginMethod = null,
|
||||
} = {},
|
||||
} = JSON.parse(res) || {};
|
||||
|
||||
if (status === 'error') {
|
||||
if (reason === 'unauthorized') {
|
||||
return null;
|
||||
} else if (reason === 'token-expired') {
|
||||
tokenExpired = true;
|
||||
} else {
|
||||
return { offline: true };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
offline: false,
|
||||
userName,
|
||||
permission,
|
||||
userId,
|
||||
displayName,
|
||||
loginMethod,
|
||||
tokenExpired,
|
||||
};
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return { offline: true };
|
||||
}
|
||||
}
|
||||
|
||||
async function changePassword({ password }: { password: string }) {
|
||||
const userToken = await asyncStorage.getItem('user-token');
|
||||
if (!userToken) {
|
||||
return { error: 'not-logged-in' };
|
||||
}
|
||||
|
||||
try {
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
throw new Error('No sync server configured.');
|
||||
}
|
||||
await post(serverConfig.SIGNUP_SERVER + '/change-password', {
|
||||
token: userToken,
|
||||
password,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof PostError) {
|
||||
return {
|
||||
error: err.reason || 'network-failure',
|
||||
};
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
async function signIn(
|
||||
loginInfo:
|
||||
| {
|
||||
password: string;
|
||||
loginMethod?: string;
|
||||
}
|
||||
| {
|
||||
returnUrl: string;
|
||||
loginMethod?: 'openid';
|
||||
},
|
||||
) {
|
||||
if (
|
||||
typeof loginInfo.loginMethod !== 'string' ||
|
||||
loginInfo.loginMethod == null
|
||||
) {
|
||||
loginInfo.loginMethod = 'password';
|
||||
}
|
||||
let res: {
|
||||
token?: string;
|
||||
redirect_url?: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
throw new Error('No sync server configured.');
|
||||
}
|
||||
res = await post(serverConfig.SIGNUP_SERVER + '/login', loginInfo);
|
||||
} catch (err) {
|
||||
if (err instanceof PostError) {
|
||||
return {
|
||||
error: err.reason || 'network-failure',
|
||||
};
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (res.redirect_url) {
|
||||
return { redirectUrl: res.redirect_url };
|
||||
}
|
||||
|
||||
if (!res.token) {
|
||||
throw new Error('login: User token not set');
|
||||
}
|
||||
|
||||
await asyncStorage.setItem('user-token', res.token);
|
||||
return {};
|
||||
}
|
||||
|
||||
async function signOut() {
|
||||
encryption.unloadAllKeys();
|
||||
await asyncStorage.multiRemove([
|
||||
'user-token',
|
||||
'encrypt-keys',
|
||||
'lastBudget',
|
||||
'readOnly',
|
||||
]);
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
async function setToken({ token }: { token: string }) {
|
||||
await asyncStorage.setItem('user-token', token);
|
||||
}
|
||||
|
||||
async function enableOpenId(openIdConfig: { openId: OpenIdConfig }) {
|
||||
try {
|
||||
const userToken = await asyncStorage.getItem('user-token');
|
||||
|
||||
if (!userToken) {
|
||||
return { error: 'unauthorized' };
|
||||
}
|
||||
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
throw new Error('No sync server configured.');
|
||||
}
|
||||
|
||||
await post(serverConfig.BASE_SERVER + '/openid/enable', openIdConfig, {
|
||||
'X-ACTUAL-TOKEN': userToken,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof PostError) {
|
||||
return {
|
||||
error: err.reason || 'network-failure',
|
||||
};
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async function getOpenIdConfig() {
|
||||
try {
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
throw new Error('No sync server configured.');
|
||||
}
|
||||
|
||||
const res = await get(serverConfig.BASE_SERVER + '/openid/config');
|
||||
|
||||
if (res) {
|
||||
const config = JSON.parse(res) as OpenIdConfig;
|
||||
return { openId: config };
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (err) {
|
||||
return { error: 'config-fetch-failed' };
|
||||
}
|
||||
}
|
||||
|
||||
async function enablePassword(passwordConfig: { password: string }) {
|
||||
try {
|
||||
const userToken = await asyncStorage.getItem('user-token');
|
||||
|
||||
if (!userToken) {
|
||||
return { error: 'unauthorized' };
|
||||
}
|
||||
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
throw new Error('No sync server configured.');
|
||||
}
|
||||
|
||||
await post(serverConfig.BASE_SERVER + '/openid/disable', passwordConfig, {
|
||||
'X-ACTUAL-TOKEN': userToken,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof PostError) {
|
||||
return {
|
||||
error: err.reason || 'network-failure',
|
||||
};
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
132
packages/loot-core/src/server/encryption/app.ts
Normal file
132
packages/loot-core/src/server/encryption/app.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import * as asyncStorage from '../../platform/server/asyncStorage';
|
||||
import { Budget } from '../../types/budget';
|
||||
import { createApp } from '../app';
|
||||
import { post } from '../post';
|
||||
import * as prefs from '../prefs';
|
||||
import { getServer } from '../server-config';
|
||||
import { makeTestMessage, resetSync } from '../sync';
|
||||
|
||||
import * as encryption from '.';
|
||||
|
||||
export type EncryptionHandlers = {
|
||||
'key-make': typeof keyMake;
|
||||
'key-test': typeof keyTest;
|
||||
};
|
||||
|
||||
export const app = createApp<EncryptionHandlers>();
|
||||
app.method('key-make', keyMake);
|
||||
app.method('key-test', keyTest);
|
||||
|
||||
// A user can only enable/change their key with the file loaded. This
|
||||
// will change in the future: during onboarding the user should be
|
||||
// able to enable encryption. (Imagine if they are importing data from
|
||||
// another source, they should be able to encrypt first)
|
||||
async function keyMake({ password }: { password: string }) {
|
||||
if (!prefs.getPrefs()) {
|
||||
throw new Error('key-make must be called with file loaded');
|
||||
}
|
||||
|
||||
const salt = encryption.randomBytes(32).toString('base64');
|
||||
const id = uuidv4();
|
||||
const key = await encryption.createKey({ id, password, salt });
|
||||
|
||||
// Load the key
|
||||
await encryption.loadKey(key);
|
||||
|
||||
// Make some test data to use if the key is valid or not
|
||||
const testContent = await makeTestMessage(key.getId());
|
||||
|
||||
// Changing your key necessitates a sync reset as well. This will
|
||||
// clear all existing encrypted data from the server so you won't
|
||||
// have a mix of data encrypted with different keys.
|
||||
return await resetSync({
|
||||
key,
|
||||
salt,
|
||||
testContent: JSON.stringify({
|
||||
...testContent,
|
||||
value: testContent.value.toString('base64'),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// This can be called both while a file is already loaded or not. This
|
||||
// will see if a key is valid and if so save it off.
|
||||
async function keyTest({
|
||||
cloudFileId,
|
||||
password,
|
||||
}: {
|
||||
cloudFileId?: Budget['cloudFileId'];
|
||||
password: string;
|
||||
}) {
|
||||
const userToken = await asyncStorage.getItem('user-token');
|
||||
|
||||
if (cloudFileId == null) {
|
||||
cloudFileId = prefs.getPrefs().cloudFileId;
|
||||
}
|
||||
|
||||
let validCloudFileId: NonNullable<Budget['cloudFileId']>;
|
||||
let res: {
|
||||
id: string;
|
||||
salt: string;
|
||||
test: string | null;
|
||||
};
|
||||
try {
|
||||
const serverConfig = getServer();
|
||||
if (!serverConfig) {
|
||||
throw new Error('No sync server configured.');
|
||||
}
|
||||
res = await post(serverConfig.SYNC_SERVER + '/user-get-key', {
|
||||
token: userToken,
|
||||
fileId: cloudFileId,
|
||||
});
|
||||
validCloudFileId = cloudFileId!;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return { error: { reason: 'network' } };
|
||||
}
|
||||
|
||||
const { id, salt, test: originalTest } = res;
|
||||
|
||||
if (!originalTest) {
|
||||
return { error: { reason: 'old-key-style' } };
|
||||
}
|
||||
|
||||
const test: {
|
||||
value: string;
|
||||
meta: {
|
||||
keyId: string;
|
||||
algorithm: string;
|
||||
iv: string;
|
||||
authTag: string;
|
||||
};
|
||||
} = JSON.parse(originalTest);
|
||||
|
||||
const key = await encryption.createKey({ id, password, salt });
|
||||
encryption.loadKey(key);
|
||||
|
||||
try {
|
||||
await encryption.decrypt(Buffer.from(test.value, 'base64'), test.meta);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
// Unload the key, it's invalid
|
||||
encryption.unloadKey(key);
|
||||
return { error: { reason: 'decrypt-failure' } };
|
||||
}
|
||||
|
||||
// Persist key in async storage
|
||||
const keys = JSON.parse((await asyncStorage.getItem(`encrypt-keys`)) || '{}');
|
||||
keys[validCloudFileId] = key.serialize();
|
||||
await asyncStorage.setItem('encrypt-keys', JSON.stringify(keys));
|
||||
|
||||
// Save the key id in prefs if the are loaded. If they aren't, we
|
||||
// are testing a key to download a file and when the file is
|
||||
// actually downloaded it will update the prefs with the latest key id
|
||||
if (prefs.getPrefs()) {
|
||||
await prefs.savePrefs({ encryptKeyId: key.getId() });
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as encryption from './encryption';
|
||||
import * as encryption from '.';
|
||||
|
||||
afterEach(() => encryption.unloadAllKeys());
|
||||
|
||||
19
packages/loot-core/src/server/encryption/index.test.ts
Normal file
19
packages/loot-core/src/server/encryption/index.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as encryption from '.';
|
||||
|
||||
afterEach(() => encryption.unloadAllKeys());
|
||||
|
||||
describe('Encryption', () => {
|
||||
test('should encrypt and decrypt', async () => {
|
||||
const key = await encryption.createKey({
|
||||
id: 'foo',
|
||||
password: 'mypassword',
|
||||
salt: 'salt',
|
||||
});
|
||||
await encryption.loadKey(key);
|
||||
|
||||
const data = await encryption.encrypt('hello', 'foo');
|
||||
|
||||
const output = await encryption.decrypt(data.value, data.meta);
|
||||
expect(output.toString()).toBe('hello');
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,6 @@
|
||||
import './polyfills';
|
||||
|
||||
import * as injectAPI from '@actual-app/api/injected';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import * as asyncStorage from '../platform/server/asyncStorage';
|
||||
import * as connection from '../platform/server/connection';
|
||||
@@ -21,6 +20,7 @@ import { app as budgetFilesApp } from './budgetfiles/app';
|
||||
import { app as dashboardApp } from './dashboard/app';
|
||||
import * as db from './db';
|
||||
import * as encryption from './encryption';
|
||||
import { app as encryptionApp } from './encryption/app';
|
||||
import { app as filtersApp } from './filters/app';
|
||||
import { app } from './main-app';
|
||||
import { mutator, runHandler } from './mutators';
|
||||
@@ -35,7 +35,7 @@ import { app as rulesApp } from './rules/app';
|
||||
import { app as schedulesApp } from './schedules/app';
|
||||
import { getServer, isValidBaseURL, setServer } from './server-config';
|
||||
import { app as spreadsheetApp } from './spreadsheet/app';
|
||||
import { fullSync, setSyncingMode, makeTestMessage, resetSync } from './sync';
|
||||
import { fullSync, setSyncingMode } from './sync';
|
||||
import { app as syncApp } from './sync/app';
|
||||
import { app as toolsApp } from './tools/app';
|
||||
import { app as transactionsApp } from './transactions/app';
|
||||
@@ -71,95 +71,6 @@ handlers['query'] = async function (query) {
|
||||
return aqlQuery(query);
|
||||
};
|
||||
|
||||
// A user can only enable/change their key with the file loaded. This
|
||||
// will change in the future: during onboarding the user should be
|
||||
// able to enable encryption. (Imagine if they are importing data from
|
||||
// another source, they should be able to encrypt first)
|
||||
handlers['key-make'] = async function ({ password }) {
|
||||
if (!prefs.getPrefs()) {
|
||||
throw new Error('user-set-key must be called with file loaded');
|
||||
}
|
||||
|
||||
const salt = encryption.randomBytes(32).toString('base64');
|
||||
const id = uuidv4();
|
||||
const key = await encryption.createKey({ id, password, salt });
|
||||
|
||||
// Load the key
|
||||
await encryption.loadKey(key);
|
||||
|
||||
// Make some test data to use if the key is valid or not
|
||||
const testContent = await makeTestMessage(key.getId());
|
||||
|
||||
// Changing your key necessitates a sync reset as well. This will
|
||||
// clear all existing encrypted data from the server so you won't
|
||||
// have a mix of data encrypted with different keys.
|
||||
return await resetSync({
|
||||
key,
|
||||
salt,
|
||||
testContent: JSON.stringify({
|
||||
...testContent,
|
||||
value: testContent.value.toString('base64'),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
// This can be called both while a file is already loaded or not. This
|
||||
// will see if a key is valid and if so save it off.
|
||||
handlers['key-test'] = async function ({ fileId, password }) {
|
||||
const userToken = await asyncStorage.getItem('user-token');
|
||||
|
||||
if (fileId == null) {
|
||||
fileId = prefs.getPrefs().cloudFileId;
|
||||
}
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await post(getServer().SYNC_SERVER + '/user-get-key', {
|
||||
token: userToken,
|
||||
fileId,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return { error: { reason: 'network' } };
|
||||
}
|
||||
|
||||
const { id, salt, test: originalTest } = res;
|
||||
|
||||
let test = originalTest;
|
||||
if (test == null) {
|
||||
return { error: { reason: 'old-key-style' } };
|
||||
}
|
||||
|
||||
test = JSON.parse(test);
|
||||
|
||||
const key = await encryption.createKey({ id, password, salt });
|
||||
encryption.loadKey(key);
|
||||
|
||||
try {
|
||||
await encryption.decrypt(Buffer.from(test.value, 'base64'), test.meta);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
// Unload the key, it's invalid
|
||||
encryption.unloadKey(key);
|
||||
return { error: { reason: 'decrypt-failure' } };
|
||||
}
|
||||
|
||||
// Persist key in async storage
|
||||
const keys = JSON.parse((await asyncStorage.getItem(`encrypt-keys`)) || '{}');
|
||||
keys[fileId] = key.serialize();
|
||||
await asyncStorage.setItem('encrypt-keys', JSON.stringify(keys));
|
||||
|
||||
// Save the key id in prefs if the are loaded. If they aren't, we
|
||||
// are testing a key to download a file and when the file is
|
||||
// actually downloaded it will update the prefs with the latest key id
|
||||
if (prefs.getPrefs()) {
|
||||
await prefs.savePrefs({ encryptKeyId: key.getId() });
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
handlers['get-did-bootstrap'] = async function () {
|
||||
return Boolean(await asyncStorage.getItem('did-bootstrap'));
|
||||
};
|
||||
@@ -529,6 +440,7 @@ app.combine(
|
||||
spreadsheetApp,
|
||||
syncApp,
|
||||
budgetFilesApp,
|
||||
encryptionApp,
|
||||
);
|
||||
|
||||
export function getDefaultDocumentDir() {
|
||||
|
||||
4
packages/loot-core/src/types/handlers.d.ts
vendored
4
packages/loot-core/src/types/handlers.d.ts
vendored
@@ -3,6 +3,7 @@ import type { AdminHandlers } from '../server/admin/app';
|
||||
import type { BudgetHandlers } from '../server/budget/app';
|
||||
import type { BudgetFileHandlers } from '../server/budgetfiles/app';
|
||||
import type { DashboardHandlers } from '../server/dashboard/app';
|
||||
import type { EncryptionHandlers } from '../server/encryption/app';
|
||||
import type { FiltersHandlers } from '../server/filters/app';
|
||||
import type { NotesHandlers } from '../server/notes/app';
|
||||
import type { PayeesHandlers } from '../server/payees/app';
|
||||
@@ -36,6 +37,7 @@ export interface Handlers
|
||||
PayeesHandlers,
|
||||
SpreadsheetHandlers,
|
||||
SyncHandlers,
|
||||
BudgetFileHandlers {}
|
||||
BudgetFileHandlers,
|
||||
EncryptionHandlers {}
|
||||
|
||||
export type HandlerFunctions = Handlers[keyof Handlers];
|
||||
|
||||
@@ -15,15 +15,6 @@ export interface ServerHandlers {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
query: (query: QueryState) => Promise<{ data: any; dependencies: string[] }>;
|
||||
|
||||
'key-make': (arg: {
|
||||
password;
|
||||
}) => Promise<{ error?: { reason: string; meta?: unknown } }>;
|
||||
|
||||
'key-test': (arg: {
|
||||
fileId;
|
||||
password;
|
||||
}) => Promise<{ error?: { reason: string } }>;
|
||||
|
||||
'get-did-bootstrap': () => Promise<boolean>;
|
||||
|
||||
'subscribe-needs-bootstrap': (args: { url }) => Promise<
|
||||
|
||||
6
upcoming-release-notes/4662.md
Normal file
6
upcoming-release-notes/4662.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [joel-jeremy]
|
||||
---
|
||||
|
||||
Extract encryption related server handlers from main.ts to server/encryption/app.ts
|
||||
Reference in New Issue
Block a user