@actual-app/sync-server 26.8.0-nightly.20260802 → 26.8.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/build/app.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as run } from "./chunks/migrations-DOEWw8Fg.js";
2
2
  //#region app.ts
3
3
  run().then(() => {
4
- import("./chunks/app-Du06KVql.js").then((app) => app.run());
4
+ import("./chunks/app-GzHx-kED.js").then((app) => app.run());
5
5
  }).catch((err) => {
6
6
  console.log("Error starting app:", err);
7
7
  process.exit(1);
@@ -41,7 +41,7 @@ if (values.help) {
41
41
  process.exit();
42
42
  }
43
43
  if (values.version) {
44
- console.log("v26.8.0-nightly.20260802");
44
+ console.log("v26.8.0");
45
45
  process.exit();
46
46
  }
47
47
  var setupDataDir = (dataDir) => {
@@ -1 +1 @@
1
- {"version":3,"file":"actual-server.js","names":[],"sources":["../../bin/actual-server.js"],"sourcesContent":["#!/usr/bin/env node\nimport { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parseArgs } from 'node:util';\n\nconst args = process.argv;\n\nconst options = {\n help: {\n type: 'boolean',\n short: 'h',\n },\n version: {\n type: 'boolean',\n short: 'v',\n },\n 'reset-password': {\n type: 'boolean',\n },\n config: {\n type: 'string',\n },\n};\n\nconst { values } = parseArgs({\n args,\n options,\n allowPositionals: true,\n});\n\nif (values.help) {\n console.log(\n [\n 'usage: actual-server [options]',\n '',\n 'options:',\n ' --config Path to config file',\n '',\n ' -h --help Print this list and exit.',\n ' -v --version Print the version and exit.',\n '',\n 'Examples:',\n '',\n 'Runs actual-server with default configuration',\n ' actual-server',\n '',\n 'Runs actual-server with custom configuration',\n ' actual-server --config ./config.json',\n ].join('\\n'),\n );\n\n process.exit();\n}\n\nif (values.version) {\n console.log('v' + __APP_VERSION__);\n process.exit();\n}\n\nconst setupDataDir = dataDir => {\n if (process.env.ACTUAL_DATA_DIR) {\n return; // Env variables must not be overwritten\n }\n\n if (dataDir) {\n process.env.ACTUAL_DATA_DIR = dataDir; // Use the dir specified\n } else {\n // Setup defaults\n if (existsSync('./data')) {\n // The default data directory exists - use it\n console.info('Found existing data directory');\n process.env.ACTUAL_DATA_DIR = resolve('./data');\n } else {\n console.info(\n 'Using default data directory. You can specify a custom config with --config',\n );\n process.env.ACTUAL_DATA_DIR = resolve('./');\n }\n\n console.info(`Data directory: ${process.env.ACTUAL_DATA_DIR}`);\n }\n};\n\nif (values.config) {\n const configExists = existsSync(values.config);\n\n if (!configExists) {\n console.log(\n `Please specify a valid config path. The path ${String(values.config)} does not exist.`,\n );\n\n process.exit();\n } else {\n console.log(`Loading config from ${String(values.config)}`);\n const configJson = JSON.parse(readFileSync(values.config, 'utf-8'));\n process.env.ACTUAL_CONFIG_PATH = values.config;\n setupDataDir(configJson.dataDir);\n }\n} else {\n // If no config is specified, check for a default config in the current directory\n const defaultConfigJsonFile = './config.json';\n const configExists = existsSync(defaultConfigJsonFile);\n\n if (configExists) {\n console.info('Found config.json in the current directory');\n const configJson = JSON.parse(readFileSync(defaultConfigJsonFile, 'utf-8'));\n process.env.ACTUAL_CONFIG_PATH = defaultConfigJsonFile;\n setupDataDir(configJson.dataDir);\n } else {\n setupDataDir(); // No default config exists - setup data dir with defaults\n }\n}\n\nif (values['reset-password']) {\n console.info('Running reset password script...');\n await import('../src/scripts/reset-password.js');\n process.exit();\n}\n\n// start the sync server\nvoid import('../app.js');\n"],"mappings":";;;;;AAKA,IAAM,OAAO,QAAQ;AAmBrB,IAAM,EAAE,WAAW,UAAU;CAC3B;CACA,SAAA;EAlBA,MAAM;GACJ,MAAM;GACN,OAAO;EACT;EACA,SAAS;GACP,MAAM;GACN,OAAO;EACT;EACA,kBAAkB,EAChB,MAAM,UACR;EACA,QAAQ,EACN,MAAM,SACR;CAKM;CACN,kBAAkB;AACpB,CAAC;AAED,IAAI,OAAO,MAAM;CACf,QAAQ,IACN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI,CACb;CAEA,QAAQ,KAAK;AACf;AAEA,IAAI,OAAO,SAAS;CAClB,QAAQ,IAAI,0BAAqB;CACjC,QAAQ,KAAK;AACf;AAEA,IAAM,gBAAe,YAAW;CAC9B,IAAI,QAAQ,IAAI,iBACd;CAGF,IAAI,SACF,QAAQ,IAAI,kBAAkB;MACzB;EAEL,IAAI,WAAW,QAAQ,GAAG;GAExB,QAAQ,KAAK,+BAA+B;GAC5C,QAAQ,IAAI,kBAAkB,QAAQ,QAAQ;EAChD,OAAO;GACL,QAAQ,KACN,6EACF;GACA,QAAQ,IAAI,kBAAkB,QAAQ,IAAI;EAC5C;EAEA,QAAQ,KAAK,mBAAmB,QAAQ,IAAI,iBAAiB;CAC/D;AACF;AAEA,IAAI,OAAO,QAGT,IAAI,CAFiB,WAAW,OAAO,MAEvB,GAAG;CACjB,QAAQ,IACN,gDAAgD,OAAO,OAAO,MAAM,EAAE,iBACxE;CAEA,QAAQ,KAAK;AACf,OAAO;CACL,QAAQ,IAAI,uBAAuB,OAAO,OAAO,MAAM,GAAG;CAC1D,MAAM,aAAa,KAAK,MAAM,aAAa,OAAO,QAAQ,OAAO,CAAC;CAClE,QAAQ,IAAI,qBAAqB,OAAO;CACxC,aAAa,WAAW,OAAO;AACjC;KACK;CAEL,MAAM,wBAAwB;CAG9B,IAFqB,WAAW,qBAEjB,GAAG;EAChB,QAAQ,KAAK,4CAA4C;EACzD,MAAM,aAAa,KAAK,MAAM,aAAa,uBAAuB,OAAO,CAAC;EAC1E,QAAQ,IAAI,qBAAqB;EACjC,aAAa,WAAW,OAAO;CACjC,OACE,aAAa;AAEjB;AAEA,IAAI,OAAO,mBAAmB;CAC5B,QAAQ,KAAK,kCAAkC;CAC/C,MAAM,OAAO;CACb,QAAQ,KAAK;AACf;AAGK,OAAO"}
1
+ {"version":3,"file":"actual-server.js","names":[],"sources":["../../bin/actual-server.js"],"sourcesContent":["#!/usr/bin/env node\nimport { existsSync, readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parseArgs } from 'node:util';\n\nconst args = process.argv;\n\nconst options = {\n help: {\n type: 'boolean',\n short: 'h',\n },\n version: {\n type: 'boolean',\n short: 'v',\n },\n 'reset-password': {\n type: 'boolean',\n },\n config: {\n type: 'string',\n },\n};\n\nconst { values } = parseArgs({\n args,\n options,\n allowPositionals: true,\n});\n\nif (values.help) {\n console.log(\n [\n 'usage: actual-server [options]',\n '',\n 'options:',\n ' --config Path to config file',\n '',\n ' -h --help Print this list and exit.',\n ' -v --version Print the version and exit.',\n '',\n 'Examples:',\n '',\n 'Runs actual-server with default configuration',\n ' actual-server',\n '',\n 'Runs actual-server with custom configuration',\n ' actual-server --config ./config.json',\n ].join('\\n'),\n );\n\n process.exit();\n}\n\nif (values.version) {\n console.log('v' + __APP_VERSION__);\n process.exit();\n}\n\nconst setupDataDir = dataDir => {\n if (process.env.ACTUAL_DATA_DIR) {\n return; // Env variables must not be overwritten\n }\n\n if (dataDir) {\n process.env.ACTUAL_DATA_DIR = dataDir; // Use the dir specified\n } else {\n // Setup defaults\n if (existsSync('./data')) {\n // The default data directory exists - use it\n console.info('Found existing data directory');\n process.env.ACTUAL_DATA_DIR = resolve('./data');\n } else {\n console.info(\n 'Using default data directory. You can specify a custom config with --config',\n );\n process.env.ACTUAL_DATA_DIR = resolve('./');\n }\n\n console.info(`Data directory: ${process.env.ACTUAL_DATA_DIR}`);\n }\n};\n\nif (values.config) {\n const configExists = existsSync(values.config);\n\n if (!configExists) {\n console.log(\n `Please specify a valid config path. The path ${String(values.config)} does not exist.`,\n );\n\n process.exit();\n } else {\n console.log(`Loading config from ${String(values.config)}`);\n const configJson = JSON.parse(readFileSync(values.config, 'utf-8'));\n process.env.ACTUAL_CONFIG_PATH = values.config;\n setupDataDir(configJson.dataDir);\n }\n} else {\n // If no config is specified, check for a default config in the current directory\n const defaultConfigJsonFile = './config.json';\n const configExists = existsSync(defaultConfigJsonFile);\n\n if (configExists) {\n console.info('Found config.json in the current directory');\n const configJson = JSON.parse(readFileSync(defaultConfigJsonFile, 'utf-8'));\n process.env.ACTUAL_CONFIG_PATH = defaultConfigJsonFile;\n setupDataDir(configJson.dataDir);\n } else {\n setupDataDir(); // No default config exists - setup data dir with defaults\n }\n}\n\nif (values['reset-password']) {\n console.info('Running reset password script...');\n await import('../src/scripts/reset-password.js');\n process.exit();\n}\n\n// start the sync server\nvoid import('../app.js');\n"],"mappings":";;;;;AAKA,IAAM,OAAO,QAAQ;AAmBrB,IAAM,EAAE,WAAW,UAAU;CAC3B;CACA,SAAA;EAlBA,MAAM;GACJ,MAAM;GACN,OAAO;EACT;EACA,SAAS;GACP,MAAM;GACN,OAAO;EACT;EACA,kBAAkB,EAChB,MAAM,UACR;EACA,QAAQ,EACN,MAAM,SACR;CAKM;CACN,kBAAkB;AACpB,CAAC;AAED,IAAI,OAAO,MAAM;CACf,QAAQ,IACN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI,CACb;CAEA,QAAQ,KAAK;AACf;AAEA,IAAI,OAAO,SAAS;CAClB,QAAQ,IAAI,SAAqB;CACjC,QAAQ,KAAK;AACf;AAEA,IAAM,gBAAe,YAAW;CAC9B,IAAI,QAAQ,IAAI,iBACd;CAGF,IAAI,SACF,QAAQ,IAAI,kBAAkB;MACzB;EAEL,IAAI,WAAW,QAAQ,GAAG;GAExB,QAAQ,KAAK,+BAA+B;GAC5C,QAAQ,IAAI,kBAAkB,QAAQ,QAAQ;EAChD,OAAO;GACL,QAAQ,KACN,6EACF;GACA,QAAQ,IAAI,kBAAkB,QAAQ,IAAI;EAC5C;EAEA,QAAQ,KAAK,mBAAmB,QAAQ,IAAI,iBAAiB;CAC/D;AACF;AAEA,IAAI,OAAO,QAGT,IAAI,CAFiB,WAAW,OAAO,MAEvB,GAAG;CACjB,QAAQ,IACN,gDAAgD,OAAO,OAAO,MAAM,EAAE,iBACxE;CAEA,QAAQ,KAAK;AACf,OAAO;CACL,QAAQ,IAAI,uBAAuB,OAAO,OAAO,MAAM,GAAG;CAC1D,MAAM,aAAa,KAAK,MAAM,aAAa,OAAO,QAAQ,OAAO,CAAC;CAClE,QAAQ,IAAI,qBAAqB,OAAO;CACxC,aAAa,WAAW,OAAO;AACjC;KACK;CAEL,MAAM,wBAAwB;CAG9B,IAFqB,WAAW,qBAEjB,GAAG;EAChB,QAAQ,KAAK,4CAA4C;EACzD,MAAM,aAAa,KAAK,MAAM,aAAa,uBAAuB,OAAO,CAAC;EAC1E,QAAQ,IAAI,qBAAqB;EACjC,aAAa,WAAW,OAAO;CACjC,OACE,aAAa;AAEjB;AAEA,IAAI,OAAO,mBAAmB;CAC5B,QAAQ,KAAK,kCAAkC;CAC/C,MAAM,OAAO;CACb,QAAQ,KAAK;AACf;AAGK,OAAO"}
@@ -4437,31 +4437,19 @@ app$2.post("/status", handleError(async (req, res) => {
4437
4437
  }));
4438
4438
  app$2.post("/accounts", handleError(async (req, res) => {
4439
4439
  let accessKey = secretsService.get(SecretName.simplefin_accessKey);
4440
- if (isInvalidAccessKey(accessKey)) {
4441
- const token = secretsService.get(SecretName.simplefin_token);
4442
- if (token == null || isForbidden(token)) {
4443
- invalidToken(res);
4444
- return;
4445
- }
4446
- const claimUrl = decodeClaimUrl(token);
4447
- if (claimUrl == null) {
4448
- console.log("SimpleFIN setup token does not decode to a claim URL - re-enter the token");
4449
- invalidToken(res);
4450
- return;
4451
- }
4452
- try {
4453
- accessKey = await claimAccessKey(claimUrl);
4454
- } catch (e) {
4455
- console.log("Failed to claim the SimpleFIN setup token:");
4456
- serverDown(e, res);
4457
- return;
4458
- }
4440
+ try {
4459
4441
  if (isInvalidAccessKey(accessKey)) {
4460
- console.log(`SimpleFIN rejected the setup token claim: ${accessKey.slice(0, 200) || "(empty response)"}`);
4461
- invalidToken(res);
4462
- return;
4442
+ const token = secretsService.get(SecretName.simplefin_token);
4443
+ if (token == null || isForbidden(token)) throw new Error("No token");
4444
+ else {
4445
+ accessKey = await getAccessKey(token);
4446
+ if (isInvalidAccessKey(accessKey)) throw new Error("No access key");
4447
+ secretsService.set(SecretName.simplefin_accessKey, accessKey);
4448
+ }
4463
4449
  }
4464
- secretsService.set(SecretName.simplefin_accessKey, accessKey);
4450
+ } catch {
4451
+ invalidToken(res);
4452
+ return;
4465
4453
  }
4466
4454
  try {
4467
4455
  const accounts = await getAccounts(accessKey, null, null, null, true);
@@ -4632,7 +4620,6 @@ function serverDown(e, res) {
4632
4620
  }
4633
4621
  });
4634
4622
  }
4635
- var ACCESS_KEY_FORMAT = /^.*\/\/.*:.*@.*$/;
4636
4623
  function parseAccessKey(accessKey) {
4637
4624
  let scheme = null;
4638
4625
  let rest = null;
@@ -4640,7 +4627,7 @@ function parseAccessKey(accessKey) {
4640
4627
  let username = null;
4641
4628
  let password = null;
4642
4629
  let baseUrl = null;
4643
- if (!accessKey || !ACCESS_KEY_FORMAT.test(accessKey)) {
4630
+ if (!accessKey || !accessKey.match(/^.*\/\/.*:.*@.*$/)) {
4644
4631
  console.log("Invalid SimpleFIN access key");
4645
4632
  throw new Error(`Invalid access key`);
4646
4633
  }
@@ -4654,31 +4641,19 @@ function parseAccessKey(accessKey) {
4654
4641
  password
4655
4642
  };
4656
4643
  }
4657
- function decodeClaimUrl(base64Token) {
4658
- const decoded = Buffer.from(base64Token, "base64").toString();
4659
- let url;
4660
- try {
4661
- url = new URL(decoded);
4662
- } catch {
4663
- return null;
4664
- }
4665
- if (url.protocol !== "https:" && url.protocol !== "http:") return null;
4666
- return decoded;
4667
- }
4668
- async function claimAccessKey(claimUrl) {
4669
- await assertUrlAllowed(claimUrl, { allowPrivateNetwork: true });
4670
- const response = await fetch(claimUrl, {
4644
+ async function getAccessKey(base64Token) {
4645
+ const token = Buffer.from(base64Token, "base64").toString();
4646
+ await assertUrlAllowed(token, { allowPrivateNetwork: true });
4647
+ return (await (await fetch(token, {
4671
4648
  method: "POST",
4672
4649
  redirect: "manual"
4673
- });
4674
- if (!response.ok && response.status !== 403) throw new Error(`SimpleFIN claim failed with HTTP ${response.status}`);
4675
- return (await response.text()).trim();
4650
+ })).text()).trim();
4676
4651
  }
4677
4652
  function isForbidden(value) {
4678
4653
  return typeof value === "string" && value.startsWith("Forbidden");
4679
4654
  }
4680
4655
  function isInvalidAccessKey(accessKey) {
4681
- return typeof accessKey !== "string" || isForbidden(accessKey) || !ACCESS_KEY_FORMAT.test(accessKey);
4656
+ return typeof accessKey !== "string" || accessKey.trim() === "" || isForbidden(accessKey);
4682
4657
  }
4683
4658
  async function getTransactions(accessKey, accounts, startDate, endDate) {
4684
4659
  const now = /* @__PURE__ */ new Date();
@@ -5710,4 +5685,4 @@ async function run() {
5710
5685
  //#endregion
5711
5686
  export { run };
5712
5687
 
5713
- //# sourceMappingURL=app-Du06KVql.js.map
5688
+ //# sourceMappingURL=app-GzHx-kED.js.map