@actual-app/sync-server 25.4.0-alpha.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.
Files changed (137) hide show
  1. package/.dockerignore +12 -0
  2. package/README.md +19 -0
  3. package/app.js +11 -0
  4. package/babel.config.json +3 -0
  5. package/bin/@actual-app/sync-server +55 -0
  6. package/docker/alpine.Dockerfile +62 -0
  7. package/docker/ubuntu.Dockerfile +63 -0
  8. package/docker-compose.yml +29 -0
  9. package/jest.config.json +19 -0
  10. package/jest.global-setup.js +101 -0
  11. package/jest.global-teardown.js +6 -0
  12. package/migrations/1694360000000-create-folders.js +25 -0
  13. package/migrations/1694360479680-create-account-db.js +30 -0
  14. package/migrations/1694362247011-create-secret-table.js +16 -0
  15. package/migrations/1702667624000-rename-nordigen-secrets.js +19 -0
  16. package/migrations/1718889148000-openid.js +41 -0
  17. package/migrations/1719409568000-multiuser.js +116 -0
  18. package/package.json +64 -0
  19. package/src/account-db.js +239 -0
  20. package/src/accounts/openid.js +361 -0
  21. package/src/accounts/password.js +149 -0
  22. package/src/app-account.js +155 -0
  23. package/src/app-admin.js +410 -0
  24. package/src/app-admin.test.js +381 -0
  25. package/src/app-gocardless/README.md +198 -0
  26. package/src/app-gocardless/app-gocardless.js +274 -0
  27. package/src/app-gocardless/bank-factory.js +91 -0
  28. package/src/app-gocardless/banks/abanca_caglesmm.js +22 -0
  29. package/src/app-gocardless/banks/abnamro_abnanl2a.js +57 -0
  30. package/src/app-gocardless/banks/american_express_aesudef1.js +40 -0
  31. package/src/app-gocardless/banks/bancsabadell_bsabesbbb.js +31 -0
  32. package/src/app-gocardless/banks/bank.interface.ts +51 -0
  33. package/src/app-gocardless/banks/bank_of_ireland_b365_bofiie2d.js +39 -0
  34. package/src/app-gocardless/banks/bankinter_bkbkesmm.js +24 -0
  35. package/src/app-gocardless/banks/belfius_gkccbebb.js +17 -0
  36. package/src/app-gocardless/banks/berliner_sparkasse_beladebexxx.js +61 -0
  37. package/src/app-gocardless/banks/bnp_be_gebabebb.js +73 -0
  38. package/src/app-gocardless/banks/cbc_cregbebb.js +34 -0
  39. package/src/app-gocardless/banks/commerzbank_cobadeff.js +51 -0
  40. package/src/app-gocardless/banks/danskebank_dabno22.js +39 -0
  41. package/src/app-gocardless/banks/direkt_heladef1822.js +18 -0
  42. package/src/app-gocardless/banks/easybank_bawaatww.js +50 -0
  43. package/src/app-gocardless/banks/entercard_swednokk.js +40 -0
  44. package/src/app-gocardless/banks/fortuneo_ftnofrp1xxx.js +46 -0
  45. package/src/app-gocardless/banks/hype_hyeeit22.js +74 -0
  46. package/src/app-gocardless/banks/ing_ingbrobu.js +70 -0
  47. package/src/app-gocardless/banks/ing_ingddeff.js +47 -0
  48. package/src/app-gocardless/banks/ing_pl_ingbplpw.js +46 -0
  49. package/src/app-gocardless/banks/integration-bank.js +115 -0
  50. package/src/app-gocardless/banks/isybank_itbbitmm.js +18 -0
  51. package/src/app-gocardless/banks/kbc_kredbebb.js +33 -0
  52. package/src/app-gocardless/banks/lhv-lhvbee22.js +36 -0
  53. package/src/app-gocardless/banks/mbank_retail_brexplpw.js +56 -0
  54. package/src/app-gocardless/banks/nationwide_naiagb21.js +46 -0
  55. package/src/app-gocardless/banks/nbg_ethngraaxxx.js +51 -0
  56. package/src/app-gocardless/banks/norwegian_xx_norwnok1.js +74 -0
  57. package/src/app-gocardless/banks/revolut_revolt21.js +37 -0
  58. package/src/app-gocardless/banks/sandboxfinance_sfin0000.js +28 -0
  59. package/src/app-gocardless/banks/seb_kort_bank_ab.js +58 -0
  60. package/src/app-gocardless/banks/seb_privat.js +29 -0
  61. package/src/app-gocardless/banks/sparnord_spnodk22.js +24 -0
  62. package/src/app-gocardless/banks/spk_karlsruhe_karsde66.js +61 -0
  63. package/src/app-gocardless/banks/spk_marburg_biedenkopf_heladef1mar.js +30 -0
  64. package/src/app-gocardless/banks/spk_worms_alzey_ried_malade51wor.js +19 -0
  65. package/src/app-gocardless/banks/ssk_dusseldorf_dussdeddxxx.js +50 -0
  66. package/src/app-gocardless/banks/swedbank_habalv22.js +47 -0
  67. package/src/app-gocardless/banks/tests/abanca_caglesmm.spec.js +21 -0
  68. package/src/app-gocardless/banks/tests/abnamro_abnanl2a.spec.js +61 -0
  69. package/src/app-gocardless/banks/tests/bancsabadell_bsabesbbb.spec.js +53 -0
  70. package/src/app-gocardless/banks/tests/belfius_gkccbebb.spec.js +22 -0
  71. package/src/app-gocardless/banks/tests/cbc_cregbebb.spec.js +34 -0
  72. package/src/app-gocardless/banks/tests/commerzbank_cobadeff.spec.js +110 -0
  73. package/src/app-gocardless/banks/tests/easybank_bawaatww.spec.js +54 -0
  74. package/src/app-gocardless/banks/tests/fortuneo_ftnofrp1xxx.spec.js +206 -0
  75. package/src/app-gocardless/banks/tests/ing_ingddeff.spec.js +302 -0
  76. package/src/app-gocardless/banks/tests/ing_pl_ingbplpw.spec.js +202 -0
  77. package/src/app-gocardless/banks/tests/integration_bank.spec.js +158 -0
  78. package/src/app-gocardless/banks/tests/kbc_kredbebb.spec.js +38 -0
  79. package/src/app-gocardless/banks/tests/lhv-lhvbee22.spec.js +68 -0
  80. package/src/app-gocardless/banks/tests/mbank_retail_brexplpw.spec.js +171 -0
  81. package/src/app-gocardless/banks/tests/nationwide_naiagb21.spec.js +105 -0
  82. package/src/app-gocardless/banks/tests/nbg_ethngraaxxx.spec.js +48 -0
  83. package/src/app-gocardless/banks/tests/revolut_revolt21.spec.js +42 -0
  84. package/src/app-gocardless/banks/tests/sandboxfinance_sfin0000.spec.js +133 -0
  85. package/src/app-gocardless/banks/tests/spk_marburg_biedenkopf_heladef1mar.spec.js +256 -0
  86. package/src/app-gocardless/banks/tests/ssk_dusseldorf_dussdeddxxx.spec.js +102 -0
  87. package/src/app-gocardless/banks/tests/swedbank_habalv22.spec.js +57 -0
  88. package/src/app-gocardless/banks/tests/virgin_nrnbgb22.spec.js +54 -0
  89. package/src/app-gocardless/banks/util/extract-payeeName-from-remittanceInfo.js +36 -0
  90. package/src/app-gocardless/banks/virgin_nrnbgb22.js +39 -0
  91. package/src/app-gocardless/errors.js +84 -0
  92. package/src/app-gocardless/gocardless-node.types.ts +497 -0
  93. package/src/app-gocardless/gocardless.types.ts +93 -0
  94. package/src/app-gocardless/link.html +18 -0
  95. package/src/app-gocardless/services/gocardless-service.js +620 -0
  96. package/src/app-gocardless/services/tests/fixtures.js +181 -0
  97. package/src/app-gocardless/services/tests/gocardless-service.spec.js +537 -0
  98. package/src/app-gocardless/tests/bank-factory.spec.js +20 -0
  99. package/src/app-gocardless/tests/utils.spec.js +162 -0
  100. package/src/app-gocardless/util/handle-error.js +16 -0
  101. package/src/app-gocardless/utils.js +45 -0
  102. package/src/app-openid.js +108 -0
  103. package/src/app-pluggyai/app-pluggyai.js +215 -0
  104. package/src/app-pluggyai/pluggyai-service.js +120 -0
  105. package/src/app-secrets.js +61 -0
  106. package/src/app-simplefin/app-simplefin.js +418 -0
  107. package/src/app-sync/errors.js +13 -0
  108. package/src/app-sync/services/files-service.js +243 -0
  109. package/src/app-sync/tests/services/files-service.test.js +250 -0
  110. package/src/app-sync/validation.js +77 -0
  111. package/src/app-sync.js +391 -0
  112. package/src/app-sync.test.js +877 -0
  113. package/src/app.js +145 -0
  114. package/src/config-types.ts +44 -0
  115. package/src/db.js +58 -0
  116. package/src/load-config.js +307 -0
  117. package/src/migrations.js +36 -0
  118. package/src/run-migrations.js +8 -0
  119. package/src/scripts/disable-openid.js +44 -0
  120. package/src/scripts/enable-openid.js +53 -0
  121. package/src/scripts/health-check.js +23 -0
  122. package/src/scripts/reset-password.js +51 -0
  123. package/src/secrets.test.js +83 -0
  124. package/src/services/secrets-service.js +94 -0
  125. package/src/services/user-service.js +272 -0
  126. package/src/sql/messages.sql +9 -0
  127. package/src/sync-simple.js +95 -0
  128. package/src/util/hash.js +5 -0
  129. package/src/util/middlewares.js +62 -0
  130. package/src/util/paths.js +13 -0
  131. package/src/util/payee-name.js +45 -0
  132. package/src/util/prompt.js +88 -0
  133. package/src/util/title/index.js +59 -0
  134. package/src/util/title/lower-case.js +93 -0
  135. package/src/util/title/specials.js +21 -0
  136. package/src/util/validate-user.js +68 -0
  137. package/tsconfig.json +21 -0
@@ -0,0 +1,13 @@
1
+ import { join } from 'node:path';
2
+
3
+ import { config } from '../load-config.js';
4
+
5
+ /** @param {string} fileId */
6
+ export function getPathForUserFile(fileId) {
7
+ return join(config.get('userFiles'), `file-${fileId}.blob`);
8
+ }
9
+
10
+ /** @param {string} groupId */
11
+ export function getPathForGroupFile(groupId) {
12
+ return join(config.get('userFiles'), `group-${groupId}.sqlite`);
13
+ }
@@ -0,0 +1,45 @@
1
+ import { title } from './title/index.js';
2
+
3
+ function formatPayeeIban(iban) {
4
+ return '(' + iban.slice(0, 4) + ' XXX ' + iban.slice(-4) + ')';
5
+ }
6
+
7
+ export const formatPayeeName = trans => {
8
+ const amount = trans.transactionAmount.amount;
9
+ const nameParts = [];
10
+
11
+ // get the correct name and account fields for the transaction amount
12
+ let name;
13
+ let account;
14
+ if (amount > 0 || Object.is(Number(amount), 0)) {
15
+ name = trans.debtorName;
16
+ account = trans.debtorAccount;
17
+ } else {
18
+ name = trans.creditorName;
19
+ account = trans.creditorAccount;
20
+ }
21
+
22
+ // use the correct name field if it was found
23
+ // if not, use whatever we can find
24
+
25
+ // if the primary name option is set, prevent the account from falling back
26
+ account = name ? account : trans.debtorAccount || trans.creditorAccount;
27
+
28
+ name =
29
+ name ||
30
+ trans.debtorName ||
31
+ trans.creditorName ||
32
+ trans.remittanceInformationUnstructured ||
33
+ (trans.remittanceInformationUnstructuredArray || []).join(', ') ||
34
+ trans.additionalInformation;
35
+
36
+ if (name) {
37
+ nameParts.push(title(name));
38
+ }
39
+
40
+ if (account && account.iban) {
41
+ nameParts.push(formatPayeeIban(account.iban));
42
+ }
43
+
44
+ return nameParts.join(' ');
45
+ };
@@ -0,0 +1,88 @@
1
+ import { createInterface, cursorTo } from 'node:readline';
2
+
3
+ export async function prompt(message) {
4
+ const rl = createInterface({
5
+ input: process.stdin,
6
+ output: process.stdout,
7
+ });
8
+
9
+ const promise = new Promise(resolve => {
10
+ rl.question(message, answer => {
11
+ resolve(answer);
12
+ rl.close();
13
+ });
14
+ });
15
+
16
+ const answer = await promise;
17
+
18
+ return answer;
19
+ }
20
+
21
+ export async function promptPassword() {
22
+ const password = await askForPassword('Enter a password, then press enter: ');
23
+
24
+ if (password === '') {
25
+ console.log('Password cannot be empty.');
26
+ return promptPassword();
27
+ }
28
+
29
+ const password2 = await askForPassword(
30
+ 'Enter the password again, then press enter: ',
31
+ );
32
+
33
+ if (password !== password2) {
34
+ console.log('Passwords do not match.');
35
+ return promptPassword();
36
+ }
37
+
38
+ return password;
39
+ }
40
+
41
+ async function askForPassword(prompt) {
42
+ let dataListener, endListener;
43
+
44
+ const promise = new Promise(resolve => {
45
+ let result = '';
46
+ process.stdout.write(prompt);
47
+ process.stdin.setRawMode(true);
48
+ process.stdin.resume();
49
+ dataListener = key => {
50
+ switch (key[0]) {
51
+ case 0x03: // ^C
52
+ process.exit();
53
+ break;
54
+ case 0x0d: // Enter
55
+ process.stdin.setRawMode(false);
56
+ process.stdin.pause();
57
+ resolve(result);
58
+ break;
59
+ case 0x7f: // Backspace
60
+ case 0x08: // Delete
61
+ if (result) {
62
+ result = result.slice(0, -1);
63
+ cursorTo(process.stdout, prompt.length + result.length);
64
+ process.stdout.write(' ');
65
+ cursorTo(process.stdout, prompt.length + result.length);
66
+ }
67
+ break;
68
+ default:
69
+ result += key;
70
+ process.stdout.write('*');
71
+ break;
72
+ }
73
+ };
74
+ process.stdin.on('data', dataListener);
75
+
76
+ endListener = () => resolve(result);
77
+ process.stdin.on('end', endListener);
78
+ });
79
+
80
+ const answer = await promise;
81
+
82
+ process.stdin.off('data', dataListener);
83
+ process.stdin.off('end', endListener);
84
+
85
+ process.stdout.write('\n');
86
+
87
+ return answer;
88
+ }
@@ -0,0 +1,59 @@
1
+ // Utilities
2
+ import { lowerCaseSet } from './lower-case.js';
3
+ import { specials } from './specials.js';
4
+
5
+ const character =
6
+ '[0-9\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376-\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0523\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4-\u07F5\u07FA\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0972\u097B-\u097F\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58-\u0C59\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3D\u0D60-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32-\u0E33\u0E40-\u0E46\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8B\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065-\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19C1-\u19C7\u1A00-\u1A16\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE-\u1BAF\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2C6F\u2C71-\u2C7D\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400\u4DB5\u4E00\u9FC3\uA000-\uA48C\uA500-\uA60C\uA610-\uA61F\uA62A-\uA62B\uA640-\uA65F\uA662-\uA66E\uA67F-\uA697\uA717-\uA71F\uA722-\uA788\uA78B-\uA78C\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA90A-\uA925\uA930-\uA946\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAC00\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]';
7
+ const regex = new RegExp(
8
+ `(?:(?:(\\s?(?:^|[.\\(\\)!?;:"-])\\s*)(${character}))|(${character}))(${character}*[’']*${character}*)`,
9
+ 'g',
10
+ );
11
+
12
+ const convertToRegExp = specials =>
13
+ specials.map(s => [new RegExp(`\\b${s}\\b`, 'gi'), s]);
14
+
15
+ function parseMatch(match) {
16
+ const firstCharacter = match[0];
17
+
18
+ // test first character
19
+ if (/\s/.test(firstCharacter)) {
20
+ // if whitespace - trim and return
21
+ return match.substr(1);
22
+ }
23
+ if (/[()]/.test(firstCharacter)) {
24
+ // if parens - this shouldn't be replaced
25
+ return null;
26
+ }
27
+
28
+ return match;
29
+ }
30
+
31
+ export function title(str, options = { special: undefined }) {
32
+ str = str
33
+ .toLowerCase()
34
+ .replace(regex, (m, lead = '', forced, lower, rest) => {
35
+ const parsedMatch = parseMatch(m);
36
+ if (!parsedMatch) {
37
+ return m;
38
+ }
39
+ if (!forced) {
40
+ const fullLower = lower + rest;
41
+
42
+ if (lowerCaseSet.has(fullLower)) {
43
+ return parsedMatch;
44
+ }
45
+ }
46
+
47
+ return lead + (lower || forced).toUpperCase() + rest;
48
+ });
49
+
50
+ const customSpecials = options.special || [];
51
+ const replace = [...specials, ...customSpecials];
52
+ const replaceRegExp = convertToRegExp(replace);
53
+
54
+ replaceRegExp.forEach(([pattern, s]) => {
55
+ str = str.replace(pattern, s);
56
+ });
57
+
58
+ return str;
59
+ }
@@ -0,0 +1,93 @@
1
+ const conjunctions = [
2
+ 'for', //
3
+ 'and',
4
+ 'nor',
5
+ 'but',
6
+ 'or',
7
+ 'yet',
8
+ 'so',
9
+ ];
10
+
11
+ const articles = [
12
+ 'a', //
13
+ 'an',
14
+ 'the',
15
+ ];
16
+
17
+ const prepositions = [
18
+ 'aboard',
19
+ 'about',
20
+ 'above',
21
+ 'across',
22
+ 'after',
23
+ 'against',
24
+ 'along',
25
+ 'amid',
26
+ 'among',
27
+ 'anti',
28
+ 'around',
29
+ 'as',
30
+ 'at',
31
+ 'before',
32
+ 'behind',
33
+ 'below',
34
+ 'beneath',
35
+ 'beside',
36
+ 'besides',
37
+ 'between',
38
+ 'beyond',
39
+ 'but',
40
+ 'by',
41
+ 'concerning',
42
+ 'considering',
43
+ 'despite',
44
+ 'down',
45
+ 'during',
46
+ 'except',
47
+ 'excepting',
48
+ 'excluding',
49
+ 'following',
50
+ 'for',
51
+ 'from',
52
+ 'in',
53
+ 'inside',
54
+ 'into',
55
+ 'like',
56
+ 'minus',
57
+ 'near',
58
+ 'of',
59
+ 'off',
60
+ 'on',
61
+ 'onto',
62
+ 'opposite',
63
+ 'over',
64
+ 'past',
65
+ 'per',
66
+ 'plus',
67
+ 'regarding',
68
+ 'round',
69
+ 'save',
70
+ 'since',
71
+ 'than',
72
+ 'through',
73
+ 'to',
74
+ 'toward',
75
+ 'towards',
76
+ 'under',
77
+ 'underneath',
78
+ 'unlike',
79
+ 'until',
80
+ 'up',
81
+ 'upon',
82
+ 'versus',
83
+ 'via',
84
+ 'with',
85
+ 'within',
86
+ 'without',
87
+ ];
88
+
89
+ export const lowerCaseSet = new Set([
90
+ ...conjunctions,
91
+ ...articles,
92
+ ...prepositions,
93
+ ]);
@@ -0,0 +1,21 @@
1
+ export const specials = [
2
+ 'CLI',
3
+ 'API',
4
+ 'HTTP',
5
+ 'HTTPS',
6
+ 'JSX',
7
+ 'DNS',
8
+ 'URL',
9
+ 'CI',
10
+ 'CDN',
11
+ 'GitHub',
12
+ 'CSS',
13
+ 'JS',
14
+ 'JavaScript',
15
+ 'TypeScript',
16
+ 'HTML',
17
+ 'WordPress',
18
+ 'JavaScript',
19
+ 'Next.js',
20
+ 'Node.js',
21
+ ];
@@ -0,0 +1,68 @@
1
+ import ipaddr from 'ipaddr.js';
2
+
3
+ import { getSession } from '../account-db.js';
4
+ import { config } from '../load-config.js';
5
+
6
+ export const TOKEN_EXPIRATION_NEVER = -1;
7
+ const MS_PER_SECOND = 1000;
8
+
9
+ /**
10
+ * @param {import('express').Request} req
11
+ * @param {import('express').Response} res
12
+ */
13
+ export function validateSession(req, res) {
14
+ let { token } = req.body || {};
15
+
16
+ if (!token) {
17
+ token = req.headers['x-actual-token'];
18
+ }
19
+
20
+ const session = getSession(token);
21
+
22
+ if (!session) {
23
+ res.status(401);
24
+ res.send({
25
+ status: 'error',
26
+ reason: 'unauthorized',
27
+ details: 'token-not-found',
28
+ });
29
+ return null;
30
+ }
31
+
32
+ if (
33
+ session.expires_at !== TOKEN_EXPIRATION_NEVER &&
34
+ session.expires_at * MS_PER_SECOND <= Date.now()
35
+ ) {
36
+ res.status(401);
37
+ res.send({
38
+ status: 'error',
39
+ reason: 'token-expired',
40
+ });
41
+ return null;
42
+ }
43
+
44
+ return session;
45
+ }
46
+
47
+ export function validateAuthHeader(req) {
48
+ // fallback to trustedProxies when trustedAuthProxies not set
49
+ const trustedAuthProxies =
50
+ config.get('trustedAuthProxies') ?? config.get('trustedProxies');
51
+ // ensure the first hop from our server is trusted
52
+ const peer = req.socket.remoteAddress;
53
+ const peerIp = ipaddr.process(peer);
54
+ const rangeList = {
55
+ allowed_ips: trustedAuthProxies.map(q => ipaddr.parseCIDR(q)),
56
+ };
57
+
58
+ // @ts-ignore : there is an error in the ts definition for the function, but this is valid
59
+ const matched = ipaddr.subnetMatch(peerIp, rangeList, 'fail');
60
+
61
+ if (matched === 'allowed_ips') {
62
+ console.info(`Header Auth Login permitted from ${peer}`);
63
+ return true;
64
+ } else {
65
+ console.warn(`Header Auth Login attempted from ${peer}`);
66
+ return false;
67
+ }
68
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ // DOM for URL global in Node 16+
5
+ "lib": ["ES2021"],
6
+ "allowSyntheticDefaultImports": true,
7
+ "esModuleInterop": true,
8
+ "experimentalDecorators": true,
9
+ "resolveJsonModule": true,
10
+ "downlevelIteration": true,
11
+ "skipLibCheck": true,
12
+ "jsx": "preserve",
13
+ // Check JS files too
14
+ "allowJs": true,
15
+ "moduleResolution": "node16",
16
+ "module": "node16",
17
+ "outDir": "build"
18
+ },
19
+ "include": ["src/**/*.js", "types/global.d.ts"],
20
+ "exclude": ["node_modules", "build", "./app-plaid.js", "coverage"]
21
+ }