@actual-app/core 26.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.swcrc +11 -0
- package/bin/build-browser +40 -0
- package/bin/copy-migrations +9 -0
- package/db.sqlite +0 -0
- package/default-db.sqlite +0 -0
- package/migrations/.force-copy-windows +0 -0
- package/migrations/1548957970627_remove-db-version.sql +5 -0
- package/migrations/1550601598648_payees.sql +23 -0
- package/migrations/1555786194328_remove_category_group_unique.sql +25 -0
- package/migrations/1561751833510_indexes.sql +7 -0
- package/migrations/1567699552727_budget.sql +38 -0
- package/migrations/1582384163573_cleared.sql +6 -0
- package/migrations/1597756566448_rules.sql +10 -0
- package/migrations/1608652596043_parent_field.sql +13 -0
- package/migrations/1608652596044_trans_views.sql +56 -0
- package/migrations/1612625548236_optimize.sql +7 -0
- package/migrations/1614782639336_trans_views2.sql +33 -0
- package/migrations/1615745967948_meta.sql +10 -0
- package/migrations/1616167010796_accounts_order.sql +5 -0
- package/migrations/1618975177358_schedules.sql +28 -0
- package/migrations/1632571489012_remove_cache.js +136 -0
- package/migrations/1679728867040_rules_conditions.sql +5 -0
- package/migrations/1681115033845_add_schedule_name.sql +5 -0
- package/migrations/1682974838138_remove_payee_rules.sql +5 -0
- package/migrations/1685007876842_add_category_hidden.sql +6 -0
- package/migrations/1686139660866_remove_account_type.sql +5 -0
- package/migrations/1688749527273_transaction_filters.sql +10 -0
- package/migrations/1688841238000_add_account_type.sql +5 -0
- package/migrations/1691233396000_add_schedule_next_date_tombstone.sql +5 -0
- package/migrations/1694438752000_add_goal_targets.sql +7 -0
- package/migrations/1697046240000_add_reconciled.sql +5 -0
- package/migrations/1704572023730_add_account_sync_source.sql +5 -0
- package/migrations/1704572023731_add_missing_goCardless_sync_source.sql +9 -0
- package/migrations/1707267033000_reports.sql +28 -0
- package/migrations/1712784523000_unhide_input_group.sql +8 -0
- package/migrations/1716359441000_include_current.sql +5 -0
- package/migrations/1720310586000_link_transfer_schedules.sql +19 -0
- package/migrations/1720664867241_add_payee_favorite.sql +5 -0
- package/migrations/1720665000000_goal_context.sql +6 -0
- package/migrations/1722717601000_reports_move_selected_categories.js +55 -0
- package/migrations/1722804019000_create_dashboard_table.js +69 -0
- package/migrations/1723665565000_prefs.js +59 -0
- package/migrations/1730744182000_fix_dashboard_table.sql +7 -0
- package/migrations/1736640000000_custom_report_sorting.sql +7 -0
- package/migrations/1737158400000_add_learn_categories_to_payees.sql +5 -0
- package/migrations/1738491452000_sorting_rename.sql +13 -0
- package/migrations/1739139550000_bank_sync_page.sql +7 -0
- package/migrations/1740506588539_add_last_reconciled_at.sql +5 -0
- package/migrations/1745425408000_update_budgetType_pref.sql +7 -0
- package/migrations/1749799110000_add_tags.sql +10 -0
- package/migrations/1749799110001_tags_tombstone.sql +5 -0
- package/migrations/1754611200000_add_category_template_settings.sql +5 -0
- package/migrations/1759260219000_add_trim_interval_report_setting.sql +6 -0
- package/migrations/1759842823172_add_isGlobal_to_preferences.sql +1 -0
- package/migrations/1762178745667_rename_csv_skip_lines_pref.sql +8 -0
- package/migrations/1765518577215_multiple_dashboards.js +30 -0
- package/migrations/1768872504000_add_payee_locations.sql +21 -0
- package/package.json +128 -0
- package/src/mocks/arbitrary-schema.ts +162 -0
- package/src/mocks/budget.ts +901 -0
- package/src/mocks/files/8859-1.qfx +63 -0
- package/src/mocks/files/best.data-ever$.QFX +124 -0
- package/src/mocks/files/big.data.QiF +91 -0
- package/src/mocks/files/budgets/.commit-to-git +0 -0
- package/src/mocks/files/camt/camt.053.payee-memo.xml +127 -0
- package/src/mocks/files/camt/camt.053.xml +463 -0
- package/src/mocks/files/credit-card.ofx +11 -0
- package/src/mocks/files/data-multi-decimal.ofx +64 -0
- package/src/mocks/files/data-payee-memo.ofx +75 -0
- package/src/mocks/files/data-payee-memo.qif +17 -0
- package/src/mocks/files/data.ofx +124 -0
- package/src/mocks/files/data.qfx +124 -0
- package/src/mocks/files/data.qif +91 -0
- package/src/mocks/files/default-budget-template/db.sqlite +0 -0
- package/src/mocks/files/default-budget-template/metadata.json +6 -0
- package/src/mocks/files/html-vals.qfx +17 -0
- package/src/mocks/index.ts +221 -0
- package/src/mocks/migrations/1508717984291_up_add-poop.sql +13 -0
- package/src/mocks/migrations/1508718036311_up_modify-poop.sql +2 -0
- package/src/mocks/migrations/1508727787513_remove-is_income.sql +15 -0
- package/src/mocks/random.ts +16 -0
- package/src/mocks/setup.ts +180 -0
- package/src/mocks/spreadsheet.ts +101 -0
- package/src/mocks/util.ts +82 -0
- package/src/platform/client/connection/README.md +3 -0
- package/src/platform/client/connection/__mocks__/index.ts +67 -0
- package/src/platform/client/connection/index-types.ts +95 -0
- package/src/platform/client/connection/index.browser.ts +213 -0
- package/src/platform/client/connection/index.ts +155 -0
- package/src/platform/client/undo/index.ts +59 -0
- package/src/platform/exceptions/__mocks__/index.ts +7 -0
- package/src/platform/exceptions/index.ts +9 -0
- package/src/platform/server/asyncStorage/__mocks__/index.ts +50 -0
- package/src/platform/server/asyncStorage/index-types.ts +35 -0
- package/src/platform/server/asyncStorage/index.api.ts +2 -0
- package/src/platform/server/asyncStorage/index.electron.ts +88 -0
- package/src/platform/server/asyncStorage/index.ts +126 -0
- package/src/platform/server/connection/README.md +3 -0
- package/src/platform/server/connection/__mocks__/index.ts +15 -0
- package/src/platform/server/connection/index-types.ts +20 -0
- package/src/platform/server/connection/index.api.ts +13 -0
- package/src/platform/server/connection/index.electron.ts +102 -0
- package/src/platform/server/connection/index.ts +154 -0
- package/src/platform/server/fetch/__mocks__/index.ts +3 -0
- package/src/platform/server/fetch/index.api.ts +1 -0
- package/src/platform/server/fetch/index.electron.ts +18 -0
- package/src/platform/server/fetch/index.ts +20 -0
- package/src/platform/server/fs/index.api.ts +198 -0
- package/src/platform/server/fs/index.electron.ts +208 -0
- package/src/platform/server/fs/index.test.ts +117 -0
- package/src/platform/server/fs/index.ts +416 -0
- package/src/platform/server/fs/path-join.api.ts +1 -0
- package/src/platform/server/fs/path-join.electron.ts +1 -0
- package/src/platform/server/fs/path-join.ts +97 -0
- package/src/platform/server/fs/shared.ts +33 -0
- package/src/platform/server/indexeddb/index.ts +115 -0
- package/src/platform/server/log/index.ts +43 -0
- package/src/platform/server/sqlite/index.api.ts +2 -0
- package/src/platform/server/sqlite/index.electron.ts +134 -0
- package/src/platform/server/sqlite/index.test.ts +108 -0
- package/src/platform/server/sqlite/index.ts +241 -0
- package/src/platform/server/sqlite/normalise.ts +9 -0
- package/src/platform/server/sqlite/unicodeLike.test.ts +58 -0
- package/src/platform/server/sqlite/unicodeLike.ts +31 -0
- package/src/server/__mocks__/post.ts +9 -0
- package/src/server/__snapshots__/main.test.ts.snap +199 -0
- package/src/server/__snapshots__/sheet.test.ts.snap +9 -0
- package/src/server/accounts/__snapshots__/sync.test.ts.snap +136 -0
- package/src/server/accounts/app-bank-sync.test.ts +136 -0
- package/src/server/accounts/app.ts +1294 -0
- package/src/server/accounts/link.ts +25 -0
- package/src/server/accounts/payees.ts +36 -0
- package/src/server/accounts/sync.test.ts +679 -0
- package/src/server/accounts/sync.ts +1168 -0
- package/src/server/accounts/title/index.ts +60 -0
- package/src/server/accounts/title/lower-case.ts +93 -0
- package/src/server/accounts/title/specials.ts +21 -0
- package/src/server/admin/app.ts +241 -0
- package/src/server/api-models.ts +244 -0
- package/src/server/api.test.ts +36 -0
- package/src/server/api.ts +1030 -0
- package/src/server/app.ts +91 -0
- package/src/server/aql/compiler.test.ts +966 -0
- package/src/server/aql/compiler.ts +1222 -0
- package/src/server/aql/exec.test.ts +289 -0
- package/src/server/aql/exec.ts +128 -0
- package/src/server/aql/index.ts +41 -0
- package/src/server/aql/schema/executors.test.ts +420 -0
- package/src/server/aql/schema/executors.ts +345 -0
- package/src/server/aql/schema/index.test.ts +67 -0
- package/src/server/aql/schema/index.ts +409 -0
- package/src/server/aql/schema-helpers.test.ts +242 -0
- package/src/server/aql/schema-helpers.ts +208 -0
- package/src/server/aql/views.test.ts +62 -0
- package/src/server/aql/views.ts +57 -0
- package/src/server/auth/app.ts +387 -0
- package/src/server/bench.ts +29 -0
- package/src/server/budget/actions.ts +686 -0
- package/src/server/budget/app.ts +469 -0
- package/src/server/budget/base.test.ts +340 -0
- package/src/server/budget/base.ts +339 -0
- package/src/server/budget/category-template-context.test.ts +1658 -0
- package/src/server/budget/category-template-context.ts +862 -0
- package/src/server/budget/cleanup-template.pegjs +27 -0
- package/src/server/budget/cleanup-template.ts +408 -0
- package/src/server/budget/envelope.ts +403 -0
- package/src/server/budget/goal-template.pegjs +110 -0
- package/src/server/budget/goal-template.ts +309 -0
- package/src/server/budget/report.ts +308 -0
- package/src/server/budget/schedule-template.test.ts +184 -0
- package/src/server/budget/schedule-template.ts +351 -0
- package/src/server/budget/statements.ts +60 -0
- package/src/server/budget/template-notes.test.ts +393 -0
- package/src/server/budget/template-notes.ts +323 -0
- package/src/server/budget/util.ts +25 -0
- package/src/server/budgetfiles/__snapshots__/backups.test.ts.snap +101 -0
- package/src/server/budgetfiles/app.ts +672 -0
- package/src/server/budgetfiles/backups.test.ts +79 -0
- package/src/server/budgetfiles/backups.ts +251 -0
- package/src/server/cloud-storage.ts +467 -0
- package/src/server/dashboard/app.ts +373 -0
- package/src/server/db/__snapshots__/index.test.ts.snap +271 -0
- package/src/server/db/index.test.ts +300 -0
- package/src/server/db/index.ts +855 -0
- package/src/server/db/mappings.ts +59 -0
- package/src/server/db/sort.ts +58 -0
- package/src/server/db/types/index.ts +342 -0
- package/src/server/db/util.ts +36 -0
- package/src/server/encryption/app.ts +133 -0
- package/src/server/encryption/encryption-internals.api.ts +2 -0
- package/src/server/encryption/encryption-internals.electron.ts +89 -0
- package/src/server/encryption/encryption-internals.ts +109 -0
- package/src/server/encryption/encryption.test.ts +19 -0
- package/src/server/encryption/index.test.ts +19 -0
- package/src/server/encryption/index.ts +89 -0
- package/src/server/errors.ts +110 -0
- package/src/server/filters/app.ts +191 -0
- package/src/server/importers/actual.ts +49 -0
- package/src/server/importers/index.ts +58 -0
- package/src/server/importers/ynab4-types.ts +163 -0
- package/src/server/importers/ynab4.ts +470 -0
- package/src/server/importers/ynab5-types.ts +290 -0
- package/src/server/importers/ynab5.ts +1193 -0
- package/src/server/main-app.ts +25 -0
- package/src/server/main.test.ts +392 -0
- package/src/server/main.ts +336 -0
- package/src/server/migrate/__snapshots__/migrations.test.ts.snap +17 -0
- package/src/server/migrate/cli.ts +100 -0
- package/src/server/migrate/migrations.test.ts +81 -0
- package/src/server/migrate/migrations.ts +192 -0
- package/src/server/models.ts +184 -0
- package/src/server/mutators.ts +139 -0
- package/src/server/notes/app.ts +18 -0
- package/src/server/payees/app.ts +351 -0
- package/src/server/polyfills.ts +26 -0
- package/src/server/post.ts +219 -0
- package/src/server/preferences/app.ts +249 -0
- package/src/server/prefs.ts +91 -0
- package/src/server/reports/app.ts +187 -0
- package/src/server/rules/action.ts +344 -0
- package/src/server/rules/app.ts +193 -0
- package/src/server/rules/condition.ts +436 -0
- package/src/server/rules/customFunctions.ts +61 -0
- package/src/server/rules/formula-action.test.ts +175 -0
- package/src/server/rules/handlebars-helpers.ts +131 -0
- package/src/server/rules/index.test.ts +1095 -0
- package/src/server/rules/index.ts +22 -0
- package/src/server/rules/rule-indexer.ts +89 -0
- package/src/server/rules/rule-utils.ts +274 -0
- package/src/server/rules/rule.ts +193 -0
- package/src/server/schedules/app.test.ts +502 -0
- package/src/server/schedules/app.ts +644 -0
- package/src/server/schedules/find-schedules.ts +391 -0
- package/src/server/server-config.ts +59 -0
- package/src/server/sheet.test.ts +101 -0
- package/src/server/sheet.ts +280 -0
- package/src/server/spreadsheet/__snapshots__/spreadsheet.test.ts.snap +5 -0
- package/src/server/spreadsheet/app.ts +54 -0
- package/src/server/spreadsheet/globals.ts +13 -0
- package/src/server/spreadsheet/graph-data-structure.ts +165 -0
- package/src/server/spreadsheet/scratch +60 -0
- package/src/server/spreadsheet/spreadsheet.test.ts +191 -0
- package/src/server/spreadsheet/spreadsheet.ts +523 -0
- package/src/server/spreadsheet/util.ts +15 -0
- package/src/server/sql/init.sql +88 -0
- package/src/server/sync/__snapshots__/sync.test.ts.snap +31 -0
- package/src/server/sync/app.ts +29 -0
- package/src/server/sync/encoder.ts +129 -0
- package/src/server/sync/index.ts +820 -0
- package/src/server/sync/make-test-message.ts +19 -0
- package/src/server/sync/migrate.test.ts +169 -0
- package/src/server/sync/migrate.ts +48 -0
- package/src/server/sync/repair.ts +39 -0
- package/src/server/sync/reset.ts +91 -0
- package/src/server/sync/sync.property.test.ts +385 -0
- package/src/server/sync/sync.test.ts +349 -0
- package/src/server/sync/utils.ts +3 -0
- package/src/server/tags/app.ts +101 -0
- package/src/server/tests/mockData.json +9352 -0
- package/src/server/tests/mockSyncServer.ts +119 -0
- package/src/server/tools/app.ts +152 -0
- package/src/server/transactions/__snapshots__/transaction-rules.test.ts.snap +173 -0
- package/src/server/transactions/__snapshots__/transfer.test.ts.snap +655 -0
- package/src/server/transactions/app.ts +136 -0
- package/src/server/transactions/export/export-to-csv.ts +132 -0
- package/src/server/transactions/import/__snapshots__/parse-file.test.ts.snap +1582 -0
- package/src/server/transactions/import/ofx2json.test.ts +33 -0
- package/src/server/transactions/import/ofx2json.ts +157 -0
- package/src/server/transactions/import/parse-file.test.ts +224 -0
- package/src/server/transactions/import/parse-file.ts +286 -0
- package/src/server/transactions/import/qif2json.ts +110 -0
- package/src/server/transactions/import/xmlcamt2json.ts +168 -0
- package/src/server/transactions/index.ts +196 -0
- package/src/server/transactions/merge.test.ts +370 -0
- package/src/server/transactions/merge.ts +139 -0
- package/src/server/transactions/transaction-rules.test.ts +994 -0
- package/src/server/transactions/transaction-rules.ts +1038 -0
- package/src/server/transactions/transfer.test.ts +221 -0
- package/src/server/transactions/transfer.ts +173 -0
- package/src/server/undo.ts +271 -0
- package/src/server/update.ts +37 -0
- package/src/server/util/budget-name.ts +61 -0
- package/src/server/util/custom-sync-mapping.ts +48 -0
- package/src/server/util/rschedule.ts +9 -0
- package/src/shared/__mocks__/platform.ts +7 -0
- package/src/shared/__snapshots__/months.test.ts.snap +21 -0
- package/src/shared/arithmetic.test.ts +112 -0
- package/src/shared/arithmetic.ts +170 -0
- package/src/shared/async.test.ts +135 -0
- package/src/shared/async.ts +76 -0
- package/src/shared/constants.ts +5 -0
- package/src/shared/currencies.ts +70 -0
- package/src/shared/dashboard.ts +260 -0
- package/src/shared/environment.ts +18 -0
- package/src/shared/errors.ts +195 -0
- package/src/shared/locale.ts +27 -0
- package/src/shared/location-utils.test.ts +69 -0
- package/src/shared/location-utils.ts +49 -0
- package/src/shared/months.test.ts +5 -0
- package/src/shared/months.ts +485 -0
- package/src/shared/normalisation.ts +6 -0
- package/src/shared/platform.electron.ts +21 -0
- package/src/shared/platform.ts +20 -0
- package/src/shared/query.ts +176 -0
- package/src/shared/rules.test.ts +56 -0
- package/src/shared/rules.ts +371 -0
- package/src/shared/schedules.test.ts +570 -0
- package/src/shared/schedules.ts +560 -0
- package/src/shared/test-helpers.ts +156 -0
- package/src/shared/transactions.test.ts +275 -0
- package/src/shared/transactions.ts +433 -0
- package/src/shared/transfer.test.ts +75 -0
- package/src/shared/transfer.ts +16 -0
- package/src/shared/user.ts +4 -0
- package/src/shared/util.test.ts +240 -0
- package/src/shared/util.ts +633 -0
- package/src/types/api-handlers.ts +287 -0
- package/src/types/budget.ts +8 -0
- package/src/types/file.ts +47 -0
- package/src/types/handlers.ts +46 -0
- package/src/types/models/account.ts +24 -0
- package/src/types/models/bank-sync.ts +23 -0
- package/src/types/models/bank.ts +6 -0
- package/src/types/models/category-group.ts +11 -0
- package/src/types/models/category.ts +13 -0
- package/src/types/models/dashboard.ts +199 -0
- package/src/types/models/gocardless.ts +84 -0
- package/src/types/models/import-transaction.ts +56 -0
- package/src/types/models/index.ts +23 -0
- package/src/types/models/nearby-payee.ts +7 -0
- package/src/types/models/note.ts +4 -0
- package/src/types/models/openid.ts +8 -0
- package/src/types/models/payee-location.ts +8 -0
- package/src/types/models/payee.ts +10 -0
- package/src/types/models/pluggyai.ts +19 -0
- package/src/types/models/reports.ts +144 -0
- package/src/types/models/rule.ts +174 -0
- package/src/types/models/schedule.ts +49 -0
- package/src/types/models/simplefin.ts +28 -0
- package/src/types/models/tags.ts +6 -0
- package/src/types/models/templates.ts +135 -0
- package/src/types/models/transaction-filter.ts +9 -0
- package/src/types/models/transaction.ts +39 -0
- package/src/types/models/user-access.ts +10 -0
- package/src/types/models/user.ts +25 -0
- package/src/types/prefs.ts +167 -0
- package/src/types/server-events.ts +86 -0
- package/src/types/server-handlers.ts +27 -0
- package/src/types/util.ts +26 -0
- package/tsconfig.json +34 -0
- package/typings/pegjs.ts +1 -0
- package/typings/process-worker.ts +12 -0
- package/typings/vite-plugin-peggy-loader.ts +1 -0
- package/typings/window.ts +62 -0
- package/vite.config.ts +109 -0
- package/vite.desktop.config.ts +59 -0
- package/vitest.config.ts +43 -0
- package/vitest.web.config.ts +38 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import { dayFromDate } from '../../shared/months';
|
|
3
|
+
import { fromDateRepr, toDateRepr } from '../models';
|
|
4
|
+
|
|
5
|
+
function isRequired(name, fieldDesc) {
|
|
6
|
+
return fieldDesc.required || name === 'id';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// TODO: All of the data type needs to check the input value. This
|
|
10
|
+
// doesn't just convert, it casts. See integer handling.
|
|
11
|
+
export function convertInputType(value, type) {
|
|
12
|
+
if (value === undefined) {
|
|
13
|
+
throw new Error('Query value cannot be undefined');
|
|
14
|
+
} else if (value === null) {
|
|
15
|
+
if (type === 'boolean') {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
switch (type) {
|
|
23
|
+
case 'date':
|
|
24
|
+
if (value instanceof Date) {
|
|
25
|
+
return toDateRepr(dayFromDate(value));
|
|
26
|
+
} else if (
|
|
27
|
+
value.match(/^\d{4}-\d{2}-\d{2}$/) == null ||
|
|
28
|
+
value < '1995-01-01'
|
|
29
|
+
) {
|
|
30
|
+
throw new Error('Invalid date: ' + value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return toDateRepr(value);
|
|
34
|
+
case 'date-month':
|
|
35
|
+
return toDateRepr(value.slice(0, 7));
|
|
36
|
+
case 'date-year':
|
|
37
|
+
return toDateRepr(value.slice(0, 4));
|
|
38
|
+
case 'boolean':
|
|
39
|
+
return value ? 1 : 0;
|
|
40
|
+
case 'id':
|
|
41
|
+
if (typeof value !== 'string' && value !== null) {
|
|
42
|
+
throw new Error('Invalid id, must be string: ' + value);
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
case 'integer':
|
|
46
|
+
if (typeof value === 'number' && Number.isInteger(value)) {
|
|
47
|
+
return value;
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error("Can't convert to integer: " + JSON.stringify(value));
|
|
50
|
+
}
|
|
51
|
+
case 'json':
|
|
52
|
+
return JSON.stringify(value);
|
|
53
|
+
default:
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function convertOutputType(value, type) {
|
|
59
|
+
if (value === null) {
|
|
60
|
+
if (type === 'boolean') {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
switch (type) {
|
|
67
|
+
case 'date':
|
|
68
|
+
return fromDateRepr(value);
|
|
69
|
+
case 'date-month':
|
|
70
|
+
return fromDateRepr(value).slice(0, 7);
|
|
71
|
+
case 'date-year':
|
|
72
|
+
return fromDateRepr(value).slice(0, 4);
|
|
73
|
+
case 'boolean':
|
|
74
|
+
return value === 1;
|
|
75
|
+
case 'json':
|
|
76
|
+
case 'json/fallback':
|
|
77
|
+
try {
|
|
78
|
+
return JSON.parse(value);
|
|
79
|
+
} catch {
|
|
80
|
+
return type === 'json/fallback' ? value : null;
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function conform(
|
|
89
|
+
schema,
|
|
90
|
+
schemaConfig,
|
|
91
|
+
table,
|
|
92
|
+
obj,
|
|
93
|
+
{ skipNull = false } = {},
|
|
94
|
+
) {
|
|
95
|
+
const tableSchema = schema[table];
|
|
96
|
+
if (tableSchema == null) {
|
|
97
|
+
throw new Error(`Table "${table}" does not exist`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const views = schemaConfig.views || {};
|
|
101
|
+
|
|
102
|
+
// Rename fields if necessary
|
|
103
|
+
const fieldRef = field => {
|
|
104
|
+
if (views[table] && views[table].fields) {
|
|
105
|
+
return views[table].fields[field] || field;
|
|
106
|
+
}
|
|
107
|
+
return field;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return Object.fromEntries(
|
|
111
|
+
Object.keys(obj)
|
|
112
|
+
.map(field => {
|
|
113
|
+
// Fields that start with an underscore are ignored
|
|
114
|
+
if (field[0] === '_') {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const fieldDesc = tableSchema[field];
|
|
119
|
+
if (fieldDesc == null) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Field "${field}" does not exist on table ${table}: ${JSON.stringify(
|
|
122
|
+
obj,
|
|
123
|
+
)}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (isRequired(field, fieldDesc) && obj[field] == null) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`"${field}" is required for table "${table}": ${JSON.stringify(
|
|
130
|
+
obj,
|
|
131
|
+
)}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// This option removes null values (see `convertForInsert`)
|
|
136
|
+
if (skipNull && obj[field] == null) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return [fieldRef(field), convertInputType(obj[field], fieldDesc.type)];
|
|
141
|
+
})
|
|
142
|
+
.filter(Boolean),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function convertForInsert(schema, schemaConfig, table, rawObj) {
|
|
147
|
+
const obj = { ...rawObj };
|
|
148
|
+
|
|
149
|
+
const tableSchema = schema[table];
|
|
150
|
+
if (tableSchema == null) {
|
|
151
|
+
throw new Error(`Error inserting: table "${table}" does not exist`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Inserting checks all the fields in the table and adds any default
|
|
155
|
+
// values necessary
|
|
156
|
+
Object.keys(tableSchema).forEach(field => {
|
|
157
|
+
const fieldDesc = tableSchema[field];
|
|
158
|
+
|
|
159
|
+
if (obj[field] == null) {
|
|
160
|
+
if (fieldDesc.default !== undefined) {
|
|
161
|
+
obj[field] =
|
|
162
|
+
typeof fieldDesc.default === 'function'
|
|
163
|
+
? fieldDesc.default()
|
|
164
|
+
: fieldDesc.default;
|
|
165
|
+
} else if (isRequired(field, fieldDesc)) {
|
|
166
|
+
// Although this check is also done in `conform`, it only
|
|
167
|
+
// checks the fields in `obj`. For insert, we need to do it
|
|
168
|
+
// here to check that all required fields in the table exist
|
|
169
|
+
throw new Error(
|
|
170
|
+
`"${field}" is required for table "${table}": ${JSON.stringify(obj)}`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// We use `skipNull` to remove any null values. There's no need to
|
|
177
|
+
// set those when inserting, that will be the default and it reduces
|
|
178
|
+
// the amount of messages generated to sync
|
|
179
|
+
return conform(schema, schemaConfig, table, obj, { skipNull: true });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function convertForUpdate(schema, schemaConfig, table, rawObj) {
|
|
183
|
+
const obj = { ...rawObj };
|
|
184
|
+
|
|
185
|
+
const tableSchema = schema[table];
|
|
186
|
+
if (tableSchema == null) {
|
|
187
|
+
throw new Error(`Error updating: table "${table}" does not exist`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return conform(schema, schemaConfig, table, obj);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function convertFromSelect(schema, schemaConfig, table, obj) {
|
|
194
|
+
const tableSchema = schema[table];
|
|
195
|
+
if (tableSchema == null) {
|
|
196
|
+
throw new Error(`Table "${table}" does not exist`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const fields = Object.keys(tableSchema);
|
|
200
|
+
const result = {};
|
|
201
|
+
for (let i = 0; i < fields.length; i++) {
|
|
202
|
+
const fieldName = fields[i];
|
|
203
|
+
const fieldDesc = tableSchema[fieldName];
|
|
204
|
+
|
|
205
|
+
result[fieldName] = convertOutputType(obj[fieldName], fieldDesc.type);
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import * as db from '../db';
|
|
3
|
+
|
|
4
|
+
import { makeViews } from './views';
|
|
5
|
+
|
|
6
|
+
beforeEach(global.emptyDatabase());
|
|
7
|
+
|
|
8
|
+
const schema = {
|
|
9
|
+
transactions: {
|
|
10
|
+
id: { type: 'id' },
|
|
11
|
+
amount: { type: 'integer' },
|
|
12
|
+
transfer_id: { type: 'integer' },
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const schemaConfig = {
|
|
17
|
+
views: {
|
|
18
|
+
transactions: {
|
|
19
|
+
fields: {
|
|
20
|
+
amount: 'a_mo_unt',
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
v_transactions1: internalFields => {
|
|
24
|
+
const fields = internalFields({
|
|
25
|
+
transfer_id: 'CASE WHEN amount < 4 THEN null ELSE transfer_id END',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return `SELECT ${fields} FROM transactions`;
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
v_transactions2: (_, publicFields) => {
|
|
32
|
+
const fields = publicFields({
|
|
33
|
+
transfer_id: 'COERCE(transfer_id, "foo")',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return `SELECT ${fields} FROM v_transactions1`;
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
describe('schema views', () => {
|
|
43
|
+
test('generates views with all the right fields', () => {
|
|
44
|
+
const str = makeViews(schema, schemaConfig);
|
|
45
|
+
expect(str).toMatch('DROP VIEW IF EXISTS v_transactions1;');
|
|
46
|
+
expect(str).toMatch(
|
|
47
|
+
'CREATE VIEW v_transactions1 AS SELECT _.id, _.a_mo_unt AS amount, CASE WHEN amount < 4 THEN null ELSE transfer_id END AS transfer_id FROM transactions;',
|
|
48
|
+
);
|
|
49
|
+
expect(str).toMatch('DROP VIEW IF EXISTS v_transactions2;');
|
|
50
|
+
expect(str).toMatch(
|
|
51
|
+
'CREATE VIEW v_transactions2 AS SELECT _.id, _.amount, COERCE(transfer_id, "foo") AS transfer_id FROM v_transactions1;',
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
db.execQuery('DROP TABLE transactions');
|
|
55
|
+
db.execQuery(
|
|
56
|
+
'CREATE TABLE transactions (id TEXT PRIMARY KEY, a_mo_unt INTEGER, transfer_id TEXT)',
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Make sure the string is valid SQL
|
|
60
|
+
db.execQuery(str);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import { quoteAlias } from './compiler';
|
|
3
|
+
|
|
4
|
+
function selectFields(fields) {
|
|
5
|
+
return Object.keys(fields)
|
|
6
|
+
.map(as => {
|
|
7
|
+
let field = fields[as];
|
|
8
|
+
const needsAs = field !== as;
|
|
9
|
+
// If it's just an identifier, we automatically prefix it with
|
|
10
|
+
// `_.` which makes sure it references the root table
|
|
11
|
+
if (!field.match(/[ .]/)) {
|
|
12
|
+
field = `_.${field}`;
|
|
13
|
+
}
|
|
14
|
+
return needsAs ? `${field} AS ${quoteAlias(as)}` : `${field}`;
|
|
15
|
+
})
|
|
16
|
+
.join(', ');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function makeViews(schema, schemaConfig) {
|
|
20
|
+
const views = schemaConfig.views;
|
|
21
|
+
const viewStrs = [];
|
|
22
|
+
|
|
23
|
+
Object.keys(views).forEach(table => {
|
|
24
|
+
const { fields: fieldMappings = {}, ...tableViews } = views[table];
|
|
25
|
+
|
|
26
|
+
const publicFields = Object.fromEntries(
|
|
27
|
+
Object.keys(schema[table]).map(name => [name, name]),
|
|
28
|
+
);
|
|
29
|
+
const internalFields = { ...publicFields, ...fieldMappings };
|
|
30
|
+
|
|
31
|
+
Object.keys(tableViews).forEach(viewName => {
|
|
32
|
+
const publicMaker = overrides => {
|
|
33
|
+
const fields = { ...publicFields, ...overrides };
|
|
34
|
+
return selectFields(fields);
|
|
35
|
+
};
|
|
36
|
+
const internalMaker = overrides => {
|
|
37
|
+
const fields = { ...internalFields, ...overrides };
|
|
38
|
+
return selectFields(fields);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
let sql;
|
|
42
|
+
if (typeof tableViews[viewName] === 'function') {
|
|
43
|
+
sql = tableViews[viewName](internalMaker, publicMaker);
|
|
44
|
+
} else {
|
|
45
|
+
sql = tableViews[viewName];
|
|
46
|
+
}
|
|
47
|
+
sql = sql.trim().replace(/;$/, '');
|
|
48
|
+
|
|
49
|
+
viewStrs.push(`
|
|
50
|
+
DROP VIEW IF EXISTS ${viewName};
|
|
51
|
+
CREATE VIEW ${viewName} AS ${sql};
|
|
52
|
+
`);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return viewStrs.join('\n');
|
|
57
|
+
}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import * as asyncStorage from '../../platform/server/asyncStorage';
|
|
2
|
+
import { logger } from '../../platform/server/log';
|
|
3
|
+
import type { OpenIdConfig } from '../../types/models';
|
|
4
|
+
import { createApp } from '../app';
|
|
5
|
+
import * as encryption from '../encryption';
|
|
6
|
+
import { PostError } from '../errors';
|
|
7
|
+
import { get, post } from '../post';
|
|
8
|
+
import { getServer, isValidBaseURL } from '../server-config';
|
|
9
|
+
|
|
10
|
+
export type AuthHandlers = {
|
|
11
|
+
'get-did-bootstrap': typeof didBootstrap;
|
|
12
|
+
'subscribe-needs-bootstrap': typeof needsBootstrap;
|
|
13
|
+
'subscribe-bootstrap': typeof bootstrap;
|
|
14
|
+
'subscribe-get-login-methods': typeof getLoginMethods;
|
|
15
|
+
'subscribe-get-user': typeof getUser;
|
|
16
|
+
'subscribe-change-password': typeof changePassword;
|
|
17
|
+
'subscribe-sign-in': typeof signIn;
|
|
18
|
+
'subscribe-sign-out': typeof signOut;
|
|
19
|
+
'subscribe-set-token': typeof setToken;
|
|
20
|
+
'enable-openid': typeof enableOpenId;
|
|
21
|
+
'get-openid-config': typeof getOpenIdConfig;
|
|
22
|
+
'enable-password': typeof enablePassword;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const app = createApp<AuthHandlers>();
|
|
26
|
+
app.method('get-did-bootstrap', didBootstrap);
|
|
27
|
+
app.method('subscribe-needs-bootstrap', needsBootstrap);
|
|
28
|
+
app.method('subscribe-bootstrap', bootstrap);
|
|
29
|
+
app.method('subscribe-get-login-methods', getLoginMethods);
|
|
30
|
+
app.method('subscribe-get-user', getUser);
|
|
31
|
+
app.method('subscribe-change-password', changePassword);
|
|
32
|
+
app.method('subscribe-sign-in', signIn);
|
|
33
|
+
app.method('subscribe-sign-out', signOut);
|
|
34
|
+
app.method('subscribe-set-token', setToken);
|
|
35
|
+
app.method('enable-openid', enableOpenId);
|
|
36
|
+
app.method('get-openid-config', getOpenIdConfig);
|
|
37
|
+
app.method('enable-password', enablePassword);
|
|
38
|
+
|
|
39
|
+
async function didBootstrap() {
|
|
40
|
+
return Boolean(await asyncStorage.getItem('did-bootstrap'));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function needsBootstrap({ url }: { url?: string } = {}) {
|
|
44
|
+
if (url && !isValidBaseURL(url)) {
|
|
45
|
+
return { error: 'get-server-failure' };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let serverConfig: ReturnType<typeof getServer>;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
serverConfig = getServer(url);
|
|
52
|
+
if (!serverConfig) {
|
|
53
|
+
return { bootstrapped: true, hasServer: false };
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
return { error: 'get-server-failure' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let resText: string;
|
|
60
|
+
try {
|
|
61
|
+
resText = await get(serverConfig.SIGNUP_SERVER + '/needs-bootstrap');
|
|
62
|
+
} catch {
|
|
63
|
+
return { error: 'network-failure' };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let res: {
|
|
67
|
+
status: 'ok';
|
|
68
|
+
data: {
|
|
69
|
+
bootstrapped: boolean;
|
|
70
|
+
loginMethod: 'password' | 'openid' | string;
|
|
71
|
+
availableLoginMethods: Array<{
|
|
72
|
+
method: string;
|
|
73
|
+
displayName: string;
|
|
74
|
+
active: boolean;
|
|
75
|
+
}>;
|
|
76
|
+
multiuser: boolean;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
res = JSON.parse(resText);
|
|
82
|
+
} catch {
|
|
83
|
+
return { error: 'parse-failure' };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
bootstrapped: res.data.bootstrapped,
|
|
88
|
+
availableLoginMethods: res.data.availableLoginMethods || [
|
|
89
|
+
{ method: 'password', active: true, displayName: 'Password' },
|
|
90
|
+
],
|
|
91
|
+
multiuser: res.data.multiuser || false,
|
|
92
|
+
hasServer: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function bootstrap(loginConfig: {
|
|
97
|
+
password?: string;
|
|
98
|
+
openId?: OpenIdConfig;
|
|
99
|
+
}) {
|
|
100
|
+
try {
|
|
101
|
+
const serverConfig = getServer();
|
|
102
|
+
if (!serverConfig) {
|
|
103
|
+
throw new Error('No sync server configured.');
|
|
104
|
+
}
|
|
105
|
+
await post(serverConfig.SIGNUP_SERVER + '/bootstrap', loginConfig);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (err instanceof PostError) {
|
|
108
|
+
return {
|
|
109
|
+
error: err.reason || 'network-failure',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
throw err;
|
|
114
|
+
}
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function getLoginMethods() {
|
|
119
|
+
let res: {
|
|
120
|
+
methods?: Array<{ method: string; displayName: string; active: boolean }>;
|
|
121
|
+
};
|
|
122
|
+
try {
|
|
123
|
+
const serverConfig = getServer();
|
|
124
|
+
if (!serverConfig) {
|
|
125
|
+
throw new Error('No sync server configured.');
|
|
126
|
+
}
|
|
127
|
+
res = await fetch(serverConfig.SIGNUP_SERVER + '/login-methods').then(res =>
|
|
128
|
+
res.json(),
|
|
129
|
+
);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
if (err instanceof PostError) {
|
|
132
|
+
return {
|
|
133
|
+
error: err.reason || 'network-failure',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (res.methods) {
|
|
141
|
+
return { methods: res.methods };
|
|
142
|
+
}
|
|
143
|
+
return { error: 'internal' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function getUser() {
|
|
147
|
+
const serverConfig = getServer();
|
|
148
|
+
if (!serverConfig) {
|
|
149
|
+
if (!(await asyncStorage.getItem('did-bootstrap'))) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
return { offline: false };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const userToken = await asyncStorage.getItem('user-token');
|
|
156
|
+
|
|
157
|
+
if (!userToken) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const res = await get(serverConfig.SIGNUP_SERVER + '/validate', {
|
|
163
|
+
headers: {
|
|
164
|
+
'X-ACTUAL-TOKEN': userToken,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
let tokenExpired = false;
|
|
168
|
+
const {
|
|
169
|
+
status,
|
|
170
|
+
reason,
|
|
171
|
+
data: {
|
|
172
|
+
userName = null,
|
|
173
|
+
permission = '',
|
|
174
|
+
userId = null,
|
|
175
|
+
displayName = null,
|
|
176
|
+
loginMethod = null,
|
|
177
|
+
prefs: serverPrefs,
|
|
178
|
+
} = {},
|
|
179
|
+
} = JSON.parse(res) || {};
|
|
180
|
+
|
|
181
|
+
if (status === 'error') {
|
|
182
|
+
if (reason === 'unauthorized') {
|
|
183
|
+
return null;
|
|
184
|
+
} else if (reason === 'token-expired') {
|
|
185
|
+
tokenExpired = true;
|
|
186
|
+
} else {
|
|
187
|
+
return { offline: true };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
offline: false,
|
|
193
|
+
userName,
|
|
194
|
+
permission,
|
|
195
|
+
userId,
|
|
196
|
+
displayName,
|
|
197
|
+
loginMethod,
|
|
198
|
+
tokenExpired,
|
|
199
|
+
serverPrefs,
|
|
200
|
+
};
|
|
201
|
+
} catch (e) {
|
|
202
|
+
logger.log(e);
|
|
203
|
+
return { offline: true };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function changePassword({ password }: { password: string }) {
|
|
208
|
+
const userToken = await asyncStorage.getItem('user-token');
|
|
209
|
+
if (!userToken) {
|
|
210
|
+
return { error: 'not-logged-in' };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const serverConfig = getServer();
|
|
215
|
+
if (!serverConfig) {
|
|
216
|
+
throw new Error('No sync server configured.');
|
|
217
|
+
}
|
|
218
|
+
await post(serverConfig.SIGNUP_SERVER + '/change-password', {
|
|
219
|
+
token: userToken,
|
|
220
|
+
password,
|
|
221
|
+
});
|
|
222
|
+
} catch (err) {
|
|
223
|
+
if (err instanceof PostError) {
|
|
224
|
+
return {
|
|
225
|
+
error: err.reason || 'network-failure',
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
throw err;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function signIn(
|
|
236
|
+
loginInfo:
|
|
237
|
+
| {
|
|
238
|
+
password: string;
|
|
239
|
+
loginMethod?: string;
|
|
240
|
+
}
|
|
241
|
+
| {
|
|
242
|
+
returnUrl: string;
|
|
243
|
+
loginMethod?: 'openid';
|
|
244
|
+
},
|
|
245
|
+
) {
|
|
246
|
+
if (
|
|
247
|
+
typeof loginInfo.loginMethod !== 'string' ||
|
|
248
|
+
loginInfo.loginMethod == null
|
|
249
|
+
) {
|
|
250
|
+
loginInfo.loginMethod = 'password';
|
|
251
|
+
}
|
|
252
|
+
let res: {
|
|
253
|
+
token?: string;
|
|
254
|
+
returnUrl?: string;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const serverConfig = getServer();
|
|
259
|
+
if (!serverConfig) {
|
|
260
|
+
throw new Error('No sync server configured.');
|
|
261
|
+
}
|
|
262
|
+
res = await post(serverConfig.SIGNUP_SERVER + '/login', loginInfo);
|
|
263
|
+
} catch (err) {
|
|
264
|
+
if (err instanceof PostError) {
|
|
265
|
+
return {
|
|
266
|
+
error: err.reason || 'network-failure',
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
throw err;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (res.returnUrl) {
|
|
274
|
+
return { redirectUrl: res.returnUrl };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!res.token) {
|
|
278
|
+
throw new Error('login: User token not set');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
await asyncStorage.setItem('user-token', res.token);
|
|
282
|
+
return {};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function signOut() {
|
|
286
|
+
encryption.unloadAllKeys();
|
|
287
|
+
await asyncStorage.multiRemove([
|
|
288
|
+
'user-token',
|
|
289
|
+
'encrypt-keys',
|
|
290
|
+
'lastBudget',
|
|
291
|
+
'readOnly',
|
|
292
|
+
]);
|
|
293
|
+
return 'ok';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function setToken({ token }: { token: string }) {
|
|
297
|
+
await asyncStorage.setItem('user-token', token);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function enableOpenId(openIdConfig: { openId: OpenIdConfig }) {
|
|
301
|
+
try {
|
|
302
|
+
const userToken = await asyncStorage.getItem('user-token');
|
|
303
|
+
|
|
304
|
+
if (!userToken) {
|
|
305
|
+
return { error: 'unauthorized' };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const serverConfig = getServer();
|
|
309
|
+
if (!serverConfig) {
|
|
310
|
+
throw new Error('No sync server configured.');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
await post(serverConfig.BASE_SERVER + '/openid/enable', openIdConfig, {
|
|
314
|
+
'X-ACTUAL-TOKEN': userToken,
|
|
315
|
+
});
|
|
316
|
+
} catch (err) {
|
|
317
|
+
if (err instanceof PostError) {
|
|
318
|
+
return {
|
|
319
|
+
error: err.reason || 'network-failure',
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
throw err;
|
|
324
|
+
}
|
|
325
|
+
return {};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function getOpenIdConfig({ password }: { password: string }) {
|
|
329
|
+
try {
|
|
330
|
+
const userToken = await asyncStorage.getItem('user-token');
|
|
331
|
+
|
|
332
|
+
const serverConfig = getServer();
|
|
333
|
+
if (!serverConfig) {
|
|
334
|
+
throw new Error('No sync server configured.');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const res = await post(
|
|
338
|
+
serverConfig.BASE_SERVER + '/openid/config',
|
|
339
|
+
{ password },
|
|
340
|
+
{
|
|
341
|
+
'X-ACTUAL-TOKEN': userToken,
|
|
342
|
+
},
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
if (res) {
|
|
346
|
+
return res as { openId: OpenIdConfig };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return null;
|
|
350
|
+
} catch (err) {
|
|
351
|
+
if (err instanceof PostError) {
|
|
352
|
+
return {
|
|
353
|
+
error: err.reason || 'network-failure',
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
throw err;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function enablePassword(passwordConfig: { password: string }) {
|
|
362
|
+
try {
|
|
363
|
+
const userToken = await asyncStorage.getItem('user-token');
|
|
364
|
+
|
|
365
|
+
if (!userToken) {
|
|
366
|
+
return { error: 'unauthorized' };
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const serverConfig = getServer();
|
|
370
|
+
if (!serverConfig) {
|
|
371
|
+
throw new Error('No sync server configured.');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
await post(serverConfig.BASE_SERVER + '/openid/disable', passwordConfig, {
|
|
375
|
+
'X-ACTUAL-TOKEN': userToken,
|
|
376
|
+
});
|
|
377
|
+
} catch (err) {
|
|
378
|
+
if (err instanceof PostError) {
|
|
379
|
+
return {
|
|
380
|
+
error: err.reason || 'network-failure',
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
throw err;
|
|
385
|
+
}
|
|
386
|
+
return {};
|
|
387
|
+
}
|