@actual-app/sync-server 26.8.0-nightly.20260721 → 26.8.0-nightly.20260723

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-tbcwdal9.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);
package/build/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","names":[],"sources":["../app.ts"],"sourcesContent":["import { run as runMigrations } from './src/migrations';\n\nrunMigrations()\n .then(() => {\n //import the app here becasue initial migrations need to be run first - they are dependencies of the app.js\n void import('./src/app.js').then(app => app.run()); // run the app\n })\n .catch(err => {\n console.log('Error starting app:', err);\n process.exit(1);\n });\n"],"mappings":";;AAEA,IAAc,EACX,WAAW;CAEV,OAAY,4BAAgB,MAAK,QAAO,IAAI,IAAI,CAAC;AACnD,CAAC,EACA,OAAM,QAAO;CACZ,QAAQ,IAAI,uBAAuB,GAAG;CACtC,QAAQ,KAAK,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"app.js","names":[],"sources":["../app.ts"],"sourcesContent":["import { run as runMigrations } from './src/migrations';\n\nrunMigrations()\n .then(() => {\n //import the app here becasue initial migrations need to be run first - they are dependencies of the app.js\n void import('./src/app.js').then(app => app.run()); // run the app\n })\n .catch(err => {\n console.log('Error starting app:', err);\n process.exit(1);\n });\n"],"mappings":";;AAEA,IAAc,CAAC,CACZ,WAAW;CAEV,OAAY,2BAAe,CAAC,MAAK,QAAO,IAAI,IAAI,CAAC;AACnD,CAAC,CAAC,CACD,OAAM,QAAO;CACZ,QAAQ,IAAI,uBAAuB,GAAG;CACtC,QAAQ,KAAK,CAAC;AAChB,CAAC"}
@@ -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.20260721");
44
+ console.log("v26.8.0-nightly.20260723");
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,EAAE,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,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 +1 @@
1
- {"version":3,"file":"1694360479680-create-account-db-PwKHxKju.js","names":[],"sources":["../../migrations/1694360479680-create-account-db.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n await getAccountDb().exec(`\n CREATE TABLE IF NOT EXISTS auth\n (password TEXT PRIMARY KEY);\n\n CREATE TABLE IF NOT EXISTS sessions\n (token TEXT PRIMARY KEY);\n\n CREATE TABLE IF NOT EXISTS files\n (id TEXT PRIMARY KEY,\n group_id TEXT,\n sync_version SMALLINT,\n encrypt_meta TEXT,\n encrypt_keyid TEXT,\n encrypt_salt TEXT,\n encrypt_test TEXT,\n deleted BOOLEAN DEFAULT FALSE,\n name TEXT);\n `);\n};\n\nexport const down = async function () {\n await getAccountDb().exec(`\n DROP TABLE auth;\n DROP TABLE sessions;\n DROP TABLE files;\n `);\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,aAAa,EAAE,KAAK;;;;;;;;;;;;;;;;;GAiBzB;AACH;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,EAAE,KAAK;;;;GAIzB;AACH"}
1
+ {"version":3,"file":"1694360479680-create-account-db-PwKHxKju.js","names":[],"sources":["../../migrations/1694360479680-create-account-db.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n await getAccountDb().exec(`\n CREATE TABLE IF NOT EXISTS auth\n (password TEXT PRIMARY KEY);\n\n CREATE TABLE IF NOT EXISTS sessions\n (token TEXT PRIMARY KEY);\n\n CREATE TABLE IF NOT EXISTS files\n (id TEXT PRIMARY KEY,\n group_id TEXT,\n sync_version SMALLINT,\n encrypt_meta TEXT,\n encrypt_keyid TEXT,\n encrypt_salt TEXT,\n encrypt_test TEXT,\n deleted BOOLEAN DEFAULT FALSE,\n name TEXT);\n `);\n};\n\nexport const down = async function () {\n await getAccountDb().exec(`\n DROP TABLE auth;\n DROP TABLE sessions;\n DROP TABLE files;\n `);\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,aAAa,CAAC,CAAC,KAAK;;;;;;;;;;;;;;;;;GAiBzB;AACH;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,CAAC,CAAC,KAAK;;;;GAIzB;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"1694362247011-create-secret-table-CTg8s4SC.js","names":[],"sources":["../../migrations/1694362247011-create-secret-table.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n await getAccountDb().exec(`\n CREATE TABLE IF NOT EXISTS secrets (\n name TEXT PRIMARY KEY,\n value BLOB\n );\n `);\n};\n\nexport const down = async function () {\n await getAccountDb().exec(`\n DROP TABLE secrets;\n `);\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,aAAa,EAAE,KAAK;;;;;GAKzB;AACH;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,EAAE,KAAK;;GAEzB;AACH"}
1
+ {"version":3,"file":"1694362247011-create-secret-table-CTg8s4SC.js","names":[],"sources":["../../migrations/1694362247011-create-secret-table.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n await getAccountDb().exec(`\n CREATE TABLE IF NOT EXISTS secrets (\n name TEXT PRIMARY KEY,\n value BLOB\n );\n `);\n};\n\nexport const down = async function () {\n await getAccountDb().exec(`\n DROP TABLE secrets;\n `);\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,aAAa,CAAC,CAAC,KAAK;;;;;GAKzB;AACH;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,CAAC,CAAC,KAAK;;GAEzB;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"1702667624000-rename-nordigen-secrets-BFEve46p.js","names":[],"sources":["../../migrations/1702667624000-rename-nordigen-secrets.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n await getAccountDb().exec(\n `UPDATE secrets SET name = 'gocardless_secretId' WHERE name = 'nordigen_secretId'`,\n );\n await getAccountDb().exec(\n `UPDATE secrets SET name = 'gocardless_secretKey' WHERE name = 'nordigen_secretKey'`,\n );\n};\n\nexport const down = async function () {\n await getAccountDb().exec(\n `UPDATE secrets SET name = 'nordigen_secretId' WHERE name = 'gocardless_secretId'`,\n );\n await getAccountDb().exec(\n `UPDATE secrets SET name = 'nordigen_secretKey' WHERE name = 'gocardless_secretKey'`,\n );\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,aAAa,EAAE,KACnB,kFACF;CACA,MAAM,aAAa,EAAE,KACnB,oFACF;AACF;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,EAAE,KACnB,kFACF;CACA,MAAM,aAAa,EAAE,KACnB,oFACF;AACF"}
1
+ {"version":3,"file":"1702667624000-rename-nordigen-secrets-BFEve46p.js","names":[],"sources":["../../migrations/1702667624000-rename-nordigen-secrets.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n await getAccountDb().exec(\n `UPDATE secrets SET name = 'gocardless_secretId' WHERE name = 'nordigen_secretId'`,\n );\n await getAccountDb().exec(\n `UPDATE secrets SET name = 'gocardless_secretKey' WHERE name = 'nordigen_secretKey'`,\n );\n};\n\nexport const down = async function () {\n await getAccountDb().exec(\n `UPDATE secrets SET name = 'nordigen_secretId' WHERE name = 'gocardless_secretId'`,\n );\n await getAccountDb().exec(\n `UPDATE secrets SET name = 'nordigen_secretKey' WHERE name = 'gocardless_secretKey'`,\n );\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,aAAa,CAAC,CAAC,KACnB,kFACF;CACA,MAAM,aAAa,CAAC,CAAC,KACnB,oFACF;AACF;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,CAAC,CAAC,KACnB,kFACF;CACA,MAAM,aAAa,CAAC,CAAC,KACnB,oFACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"1718889148000-openid-9qjigJ2O.js","names":[],"sources":["../../migrations/1718889148000-openid.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n await getAccountDb().exec(\n `\n BEGIN TRANSACTION;\n CREATE TABLE auth_new\n (method TEXT PRIMARY KEY,\n display_name TEXT,\n extra_data TEXT, active INTEGER);\n\n INSERT INTO auth_new (method, display_name, extra_data, active)\n SELECT 'password', 'Password', password, 1 FROM auth;\n DROP TABLE auth;\n ALTER TABLE auth_new RENAME TO auth;\n\n CREATE TABLE pending_openid_requests\n (state TEXT PRIMARY KEY,\n code_verifier TEXT,\n return_url TEXT,\n expiry_time INTEGER);\n COMMIT;`,\n );\n};\n\nexport const down = async function () {\n await getAccountDb().exec(\n `\n BEGIN TRANSACTION;\n ALTER TABLE auth RENAME TO auth_temp;\n CREATE TABLE auth\n (password TEXT);\n INSERT INTO auth (password)\n SELECT extra_data FROM auth_temp WHERE method = 'password';\n DROP TABLE auth_temp;\n\n DROP TABLE pending_openid_requests;\n COMMIT;\n `,\n );\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,aAAa,EAAE,KACnB;;;;;;;;;;;;;;;;;YAkBF;AACF;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,EAAE,KACnB;;;;;;;;;;;OAYF;AACF"}
1
+ {"version":3,"file":"1718889148000-openid-9qjigJ2O.js","names":[],"sources":["../../migrations/1718889148000-openid.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n await getAccountDb().exec(\n `\n BEGIN TRANSACTION;\n CREATE TABLE auth_new\n (method TEXT PRIMARY KEY,\n display_name TEXT,\n extra_data TEXT, active INTEGER);\n\n INSERT INTO auth_new (method, display_name, extra_data, active)\n SELECT 'password', 'Password', password, 1 FROM auth;\n DROP TABLE auth;\n ALTER TABLE auth_new RENAME TO auth;\n\n CREATE TABLE pending_openid_requests\n (state TEXT PRIMARY KEY,\n code_verifier TEXT,\n return_url TEXT,\n expiry_time INTEGER);\n COMMIT;`,\n );\n};\n\nexport const down = async function () {\n await getAccountDb().exec(\n `\n BEGIN TRANSACTION;\n ALTER TABLE auth RENAME TO auth_temp;\n CREATE TABLE auth\n (password TEXT);\n INSERT INTO auth (password)\n SELECT extra_data FROM auth_temp WHERE method = 'password';\n DROP TABLE auth_temp;\n\n DROP TABLE pending_openid_requests;\n COMMIT;\n `,\n );\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,aAAa,CAAC,CAAC,KACnB;;;;;;;;;;;;;;;;;YAkBF;AACF;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,CAAC,CAAC,KACnB;;;;;;;;;;;OAYF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"1719409568000-multiuser-SFJ94reU.js","names":["uuidv4"],"sources":["../../migrations/1719409568000-multiuser.js"],"sourcesContent":["import { v4 as uuidv4 } from 'uuid';\n\nimport { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n const accountDb = getAccountDb();\n\n accountDb.transaction(() => {\n accountDb.exec(\n `\n CREATE TABLE users\n (id TEXT PRIMARY KEY,\n user_name TEXT,\n display_name TEXT,\n role TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n owner INTEGER NOT NULL DEFAULT 0);\n\n CREATE TABLE user_access\n (user_id TEXT,\n file_id TEXT,\n PRIMARY KEY (user_id, file_id),\n FOREIGN KEY (user_id) REFERENCES users(id),\n FOREIGN KEY (file_id) REFERENCES files(id)\n );\n\n ALTER TABLE files\n ADD COLUMN owner TEXT;\n\n ALTER TABLE sessions\n ADD COLUMN expires_at INTEGER;\n\n ALTER TABLE sessions\n ADD COLUMN user_id TEXT;\n\n ALTER TABLE sessions\n ADD COLUMN auth_method TEXT;\n `,\n );\n\n const userId = uuidv4();\n accountDb.mutate(\n 'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, 1, 1, ?)',\n [userId, '', '', 'ADMIN'],\n );\n\n accountDb.mutate(\n 'UPDATE sessions SET user_id = ?, expires_at = ?, auth_method = ? WHERE auth_method IS NULL',\n [userId, -1, 'password'],\n );\n });\n};\n\nexport const down = async function () {\n await getAccountDb().exec(\n `\n BEGIN TRANSACTION;\n\n DROP TABLE IF EXISTS user_access;\n\n CREATE TABLE sessions_backup (\n token TEXT PRIMARY KEY\n );\n\n INSERT INTO sessions_backup (token)\n SELECT token FROM sessions;\n\n DROP TABLE sessions;\n\n ALTER TABLE sessions_backup RENAME TO sessions;\n\n CREATE TABLE files_backup (\n id TEXT PRIMARY KEY,\n group_id TEXT,\n sync_version SMALLINT,\n encrypt_meta TEXT,\n encrypt_keyid TEXT,\n encrypt_salt TEXT,\n encrypt_test TEXT,\n deleted BOOLEAN DEFAULT FALSE,\n name TEXT\n );\n\n INSERT INTO files_backup (\n id,\n group_id,\n sync_version,\n encrypt_meta,\n encrypt_keyid,\n encrypt_salt,\n encrypt_test,\n deleted,\n name\n )\n SELECT\n id,\n group_id,\n sync_version,\n encrypt_meta,\n encrypt_keyid,\n encrypt_salt,\n encrypt_test,\n deleted,\n name\n FROM files;\n\n DROP TABLE files;\n\n ALTER TABLE files_backup RENAME TO files;\n\n DROP TABLE IF EXISTS users;\n\n COMMIT;\n `,\n );\n};\n"],"mappings":";;;AAIA,IAAa,KAAK,iBAAkB;CAClC,MAAM,YAAY,aAAa;CAE/B,UAAU,kBAAkB;EAC1B,UAAU,KACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA6BF;EAEA,MAAM,SAASA,GAAO;EACtB,UAAU,OACR,mGACA;GAAC;GAAQ;GAAI;GAAI;EAAO,CAC1B;EAEA,UAAU,OACR,8FACA;GAAC;GAAQ;GAAI;EAAU,CACzB;CACF,CAAC;AACH;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,EAAE,KACnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2DF;AACF"}
1
+ {"version":3,"file":"1719409568000-multiuser-SFJ94reU.js","names":["uuidv4"],"sources":["../../migrations/1719409568000-multiuser.js"],"sourcesContent":["import { v4 as uuidv4 } from 'uuid';\n\nimport { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n const accountDb = getAccountDb();\n\n accountDb.transaction(() => {\n accountDb.exec(\n `\n CREATE TABLE users\n (id TEXT PRIMARY KEY,\n user_name TEXT,\n display_name TEXT,\n role TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n owner INTEGER NOT NULL DEFAULT 0);\n\n CREATE TABLE user_access\n (user_id TEXT,\n file_id TEXT,\n PRIMARY KEY (user_id, file_id),\n FOREIGN KEY (user_id) REFERENCES users(id),\n FOREIGN KEY (file_id) REFERENCES files(id)\n );\n\n ALTER TABLE files\n ADD COLUMN owner TEXT;\n\n ALTER TABLE sessions\n ADD COLUMN expires_at INTEGER;\n\n ALTER TABLE sessions\n ADD COLUMN user_id TEXT;\n\n ALTER TABLE sessions\n ADD COLUMN auth_method TEXT;\n `,\n );\n\n const userId = uuidv4();\n accountDb.mutate(\n 'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, 1, 1, ?)',\n [userId, '', '', 'ADMIN'],\n );\n\n accountDb.mutate(\n 'UPDATE sessions SET user_id = ?, expires_at = ?, auth_method = ? WHERE auth_method IS NULL',\n [userId, -1, 'password'],\n );\n });\n};\n\nexport const down = async function () {\n await getAccountDb().exec(\n `\n BEGIN TRANSACTION;\n\n DROP TABLE IF EXISTS user_access;\n\n CREATE TABLE sessions_backup (\n token TEXT PRIMARY KEY\n );\n\n INSERT INTO sessions_backup (token)\n SELECT token FROM sessions;\n\n DROP TABLE sessions;\n\n ALTER TABLE sessions_backup RENAME TO sessions;\n\n CREATE TABLE files_backup (\n id TEXT PRIMARY KEY,\n group_id TEXT,\n sync_version SMALLINT,\n encrypt_meta TEXT,\n encrypt_keyid TEXT,\n encrypt_salt TEXT,\n encrypt_test TEXT,\n deleted BOOLEAN DEFAULT FALSE,\n name TEXT\n );\n\n INSERT INTO files_backup (\n id,\n group_id,\n sync_version,\n encrypt_meta,\n encrypt_keyid,\n encrypt_salt,\n encrypt_test,\n deleted,\n name\n )\n SELECT\n id,\n group_id,\n sync_version,\n encrypt_meta,\n encrypt_keyid,\n encrypt_salt,\n encrypt_test,\n deleted,\n name\n FROM files;\n\n DROP TABLE files;\n\n ALTER TABLE files_backup RENAME TO files;\n\n DROP TABLE IF EXISTS users;\n\n COMMIT;\n `,\n );\n};\n"],"mappings":";;;AAIA,IAAa,KAAK,iBAAkB;CAClC,MAAM,YAAY,aAAa;CAE/B,UAAU,kBAAkB;EAC1B,UAAU,KACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA6BF;EAEA,MAAM,SAASA,GAAO;EACtB,UAAU,OACR,mGACA;GAAC;GAAQ;GAAI;GAAI;EAAO,CAC1B;EAEA,UAAU,OACR,8FACA;GAAC;GAAQ;GAAI;EAAU,CACzB;CACF,CAAC;AACH;AAEA,IAAa,OAAO,iBAAkB;CACpC,MAAM,aAAa,CAAC,CAAC,KACnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2DF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"1763873568237-server-global-prefs-CZx8n-l2.js","names":[],"sources":["../../migrations/1763873568237-server-global-prefs.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n const accountDb = getAccountDb();\n\n accountDb.exec(`\n CREATE TABLE IF NOT EXISTS server_prefs\n (key TEXT NOT NULL PRIMARY KEY,\n value TEXT);\n `);\n};\n\nexport const down = async function () {\n const accountDb = getAccountDb();\n\n accountDb.exec(`\n DROP TABLE IF EXISTS server_prefs;\n `);\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAGlC,aAAQ,EAAE,KAAK;;;;GAId;AACH;AAEA,IAAa,OAAO,iBAAkB;CAGpC,aAAQ,EAAE,KAAK;;GAEd;AACH"}
1
+ {"version":3,"file":"1763873568237-server-global-prefs-CZx8n-l2.js","names":[],"sources":["../../migrations/1763873568237-server-global-prefs.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n const accountDb = getAccountDb();\n\n accountDb.exec(`\n CREATE TABLE IF NOT EXISTS server_prefs\n (key TEXT NOT NULL PRIMARY KEY,\n value TEXT);\n `);\n};\n\nexport const down = async function () {\n const accountDb = getAccountDb();\n\n accountDb.exec(`\n DROP TABLE IF EXISTS server_prefs;\n `);\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAGlC,aAAQ,CAAC,CAAC,KAAK;;;;GAId;AACH;AAEA,IAAa,OAAO,iBAAkB;CAGpC,aAAQ,CAAC,CAAC,KAAK;;GAEd;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"account-db-B0u6vCZk.js","names":["uuidv4","config","config"],"sources":["../../src/services/user-service.ts","../../src/util/validate-user.ts","../../src/accounts/password.js","../../src/accounts/openid.ts","../../src/db.js","../../src/account-db.js"],"sourcesContent":["// @ts-strict-ignore\nimport { getAccountDb } from '#account-db';\n\nexport function getUserByUsername(userName) {\n if (!userName || typeof userName !== 'string') {\n return null;\n }\n const { id } =\n getAccountDb().first('SELECT id FROM users WHERE user_name = ?', [\n userName,\n ]) || {};\n return id || null;\n}\n\nexport function getUserById(userId) {\n if (!userId) {\n return null;\n }\n const { id } =\n getAccountDb().first('SELECT * FROM users WHERE id = ?', [userId]) || {};\n return id || null;\n}\n\nexport function getFileById(fileId) {\n if (!fileId) {\n return null;\n }\n const { id } =\n getAccountDb().first('SELECT * FROM files WHERE files.id = ?', [fileId]) ||\n {};\n return id || null;\n}\n\nexport function validateRole(roleId) {\n const possibleRoles = ['BASIC', 'ADMIN'];\n return possibleRoles.some(a => a === roleId);\n}\n\nexport function getOwnerCount(): number {\n const { ownerCount } = getAccountDb().first(\n `SELECT count(*) as ownerCount FROM users WHERE users.user_name <> '' and users.owner = 1`,\n ) || { ownerCount: 0 };\n return ownerCount;\n}\n\nexport function getOwnerId() {\n const { id } =\n getAccountDb().first(\n `SELECT users.id FROM users WHERE users.user_name <> '' and users.owner = 1`,\n ) || {};\n return id;\n}\n\nexport function getAllUsers() {\n return getAccountDb().all(\n `SELECT users.id, user_name as userName, display_name as displayName, enabled, ifnull(owner,0) as owner, role\n FROM users\n WHERE users.user_name <> ''`,\n );\n}\n\nexport function insertUser(userId, userName, displayName, enabled, role) {\n getAccountDb().mutate(\n 'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, ?, 0, ?)',\n [userId, userName, displayName, enabled, role],\n );\n}\n\nexport function updateUserWithRole(\n userId,\n userName,\n displayName,\n enabled,\n roleId,\n) {\n getAccountDb().transaction(() => {\n getAccountDb().mutate(\n 'UPDATE users SET user_name = ?, display_name = ?, enabled = ?, role = ? WHERE id = ?',\n [userName, displayName, enabled, roleId, userId],\n );\n if (!enabled) {\n getAccountDb().mutate('DELETE FROM sessions WHERE user_id = ?', [userId]);\n }\n });\n}\n\nexport function deleteUser(userId) {\n return getAccountDb().transaction(() => {\n const { changes } = getAccountDb().mutate(\n 'DELETE FROM users WHERE id = ? and owner = 0',\n [userId],\n );\n if (changes > 0) {\n getAccountDb().mutate('DELETE FROM sessions WHERE user_id = ?', [userId]);\n }\n return changes;\n });\n}\nexport function deleteUserAccess(userId) {\n try {\n return getAccountDb().mutate('DELETE FROM user_access WHERE user_id = ?', [\n userId,\n ]).changes;\n } catch (error) {\n throw new Error(`Failed to delete user access: ${error.message}`);\n }\n}\n\nexport function transferAllFilesFromUser(ownerId, oldUserId) {\n if (!ownerId || !oldUserId) {\n throw new Error('Invalid user IDs');\n }\n try {\n getAccountDb().transaction(() => {\n const ownerExists = getUserById(ownerId);\n if (!ownerExists) {\n throw new Error('New owner not found');\n }\n getAccountDb().mutate('UPDATE files set owner = ? WHERE owner = ?', [\n ownerId,\n oldUserId,\n ]);\n });\n } catch (error) {\n throw new Error(`Failed to transfer files: ${error.message}`);\n }\n}\n\nexport function updateFileOwner(ownerId, fileId) {\n if (!ownerId || !fileId) {\n throw new Error('Invalid parameters');\n }\n try {\n const result = getAccountDb().mutate(\n 'UPDATE files set owner = ? WHERE id = ?',\n [ownerId, fileId],\n );\n if (result.changes === 0) {\n throw new Error('File not found');\n }\n } catch (error) {\n throw new Error(`Failed to update file owner: ${error.message}`);\n }\n}\n\nexport function getUserAccess(fileId, userId, isAdmin) {\n return getAccountDb().all(\n `SELECT users.id as userId, user_name as userName, files.owner, display_name as displayName\n FROM users\n JOIN user_access ON user_access.user_id = users.id\n JOIN files ON files.id = user_access.file_id\n WHERE files.id = ? and (files.owner = ? OR 1 = ?)`,\n [fileId, userId, isAdmin ? 1 : 0],\n );\n}\n\nexport function countUserAccess(fileId, userId) {\n const { accessCount } =\n getAccountDb().first(\n `SELECT COUNT(*) as accessCount\n FROM files\n WHERE files.id = ? AND (files.owner = ? OR EXISTS (\n SELECT 1 FROM user_access\n WHERE user_access.user_id = ? AND user_access.file_id = ?)\n )`,\n [fileId, userId, userId, fileId],\n ) || {};\n\n return accessCount || 0;\n}\n\nexport function checkFilePermission(fileId, userId) {\n return (\n getAccountDb().first(\n `SELECT 1 as granted\n FROM files\n WHERE files.id = ? and (files.owner = ?)`,\n [fileId, userId],\n ) || { granted: 0 }\n );\n}\n\nexport function addUserAccess(userId, fileId) {\n if (!userId || !fileId) {\n throw new Error('Invalid parameters');\n }\n try {\n const userExists = getUserById(userId);\n const fileExists = getFileById(fileId);\n if (!userExists || !fileExists) {\n throw new Error('User or file not found');\n }\n getAccountDb().mutate(\n 'INSERT INTO user_access (user_id, file_id) VALUES (?, ?)',\n [userId, fileId],\n );\n } catch (error) {\n if (error.message.includes('UNIQUE constraint')) {\n throw new Error('Access already exists');\n }\n throw new Error(`Failed to add user access: ${error.message}`);\n }\n}\n\nexport function deleteUserAccessByFileId(userIds, fileId) {\n if (!Array.isArray(userIds) || userIds.length === 0) {\n throw new Error('The provided userIds must be a non-empty array.');\n }\n\n const CHUNK_SIZE = 999;\n let totalChanges = 0;\n\n try {\n getAccountDb().transaction(() => {\n for (let i = 0; i < userIds.length; i += CHUNK_SIZE) {\n const chunk = userIds.slice(i, i + CHUNK_SIZE);\n const placeholders = chunk.map(() => '?').join(',');\n\n const sql = `DELETE FROM user_access WHERE user_id IN (${placeholders}) AND file_id = ?`;\n\n const result = getAccountDb().mutate(sql, [...chunk, fileId]);\n totalChanges += result.changes;\n }\n });\n } catch (error) {\n throw new Error(`Failed to delete user access: ${error.message}`);\n }\n\n return totalChanges;\n}\n\nexport function getAllUserAccess(fileId) {\n //This can't be used here until we can create user invite links:\n //const isLoginMode = config.get('userCreationMode') === 'login';\n const isLoginMode = false;\n const joinType = isLoginMode ? 'JOIN' : 'LEFT JOIN';\n\n return getAccountDb().all(\n `\n SELECT\n users.id as userId,\n user_name as userName,\n display_name as displayName,\n CASE WHEN user_access.file_id IS NULL THEN 0 ELSE 1 END as haveAccess,\n CASE WHEN files.id IS NULL THEN 0 ELSE 1 END as owner\n FROM users\n ${joinType} user_access ON user_access.file_id = ? AND user_access.user_id = users.id\n ${joinType} files ON files.id = ? AND files.owner = users.id\n WHERE users.enabled = 1\n AND users.user_name <> ''\n `,\n [fileId, fileId],\n );\n}\n\nexport function getOpenIDConfig() {\n return (\n getAccountDb().first(`SELECT * FROM auth WHERE method = ?`, ['openid']) ||\n null\n );\n}\n","import type { Request, Response } from 'express';\nimport ipaddr from 'ipaddr.js';\n\nimport { getSession } from '#account-db';\nimport { config } from '#load-config';\n\nexport const TOKEN_EXPIRATION_NEVER = -1;\nconst MS_PER_SECOND = 1000;\n\nexport function validateSession(req: Request, res: Response) {\n let { token } = req.body || {};\n\n if (!token) {\n token = req.headers['x-actual-token'];\n }\n\n const session = getSession(token);\n\n if (!session) {\n res.status(401);\n res.send({\n status: 'error',\n reason: 'unauthorized',\n details: 'token-not-found',\n });\n return null;\n }\n\n if (\n session.expires_at !== TOKEN_EXPIRATION_NEVER &&\n session.expires_at * MS_PER_SECOND <= Date.now()\n ) {\n res.status(401);\n res.send({\n status: 'error',\n reason: 'token-expired',\n });\n return null;\n }\n\n return session;\n}\n\nexport function validateAuthHeader(req: Request) {\n // fallback to trustedProxies when trustedAuthProxies not set\n const trustedAuthProxies: string[] =\n config.get('trustedAuthProxies') ?? config.get('trustedProxies');\n // ensure the first hop from our server is trusted\n const peer = req.socket.remoteAddress;\n if (peer === undefined) {\n console.error(`Header Auth Login attempted but there was no defined peer.`);\n return false;\n }\n const peerIp = ipaddr.process(peer);\n const rangeList = {\n allowed_ips: trustedAuthProxies.map(q => ipaddr.parseCIDR(q)),\n };\n\n const matched = ipaddr.subnetMatch(peerIp, rangeList, 'fail');\n\n if (matched === 'allowed_ips') {\n console.info(`Header Auth Login permitted from ${peer}`);\n return true;\n } else {\n console.warn(`Header Auth Login attempted from ${peer}`);\n return false;\n }\n}\n","import * as argon2 from 'argon2';\nimport * as bcrypt from 'bcrypt';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { clearExpiredSessions, getAccountDb } from '#account-db';\nimport { config } from '#load-config';\nimport { TOKEN_EXPIRATION_NEVER } from '#util/validate-user';\n\n// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id\nconst ARGON2_OPTIONS = {\n type: argon2.argon2id,\n memoryCost: 47104,\n timeCost: 1,\n parallelism: 1,\n};\n\nexport function isValidPassword(password) {\n return password != null && password !== '';\n}\n\nexport function hashPassword(password) {\n return argon2.hash(password, ARGON2_OPTIONS);\n}\n\nexport async function verifyPassword(password, hash) {\n if (typeof hash !== 'string') return false;\n\n if (hash.startsWith('$argon2')) {\n try {\n return await argon2.verify(hash, password);\n } catch {\n return false;\n }\n }\n\n try {\n return await bcrypt.compare(password, hash);\n } catch {\n return false;\n }\n}\n\nfunction isLegacyHash(hash) {\n return typeof hash === 'string' && !hash.startsWith('$argon2');\n}\n\nexport function setPasswordHash(hashed) {\n const accountDb = getAccountDb();\n accountDb.transaction(() => {\n accountDb.mutate('DELETE FROM auth WHERE method = ?', ['password']);\n accountDb.mutate('UPDATE auth SET active = 0');\n accountDb.mutate(\n \"INSERT INTO auth (method, display_name, extra_data, active) VALUES ('password', 'Password', ?, 1)\",\n [hashed],\n );\n });\n}\n\nexport async function bootstrapPassword(password) {\n if (!isValidPassword(password)) {\n return { error: 'invalid-password' };\n }\n\n const hashed = await hashPassword(password);\n setPasswordHash(hashed);\n\n return {};\n}\n\nexport async function loginWithPassword(password) {\n if (!isValidPassword(password)) {\n return { error: 'invalid-password' };\n }\n\n const accountDb = getAccountDb();\n const { extra_data: passwordHash } =\n accountDb.first('SELECT extra_data FROM auth WHERE method = ?', [\n 'password',\n ]) || {};\n\n if (!passwordHash) {\n return { error: 'invalid-password' };\n }\n\n const confirmed = await verifyPassword(password, passwordHash);\n\n if (!confirmed) {\n return { error: 'invalid-password' };\n }\n\n if (isLegacyHash(passwordHash)) {\n const rehashed = await hashPassword(password);\n accountDb.mutate(\n \"UPDATE auth SET extra_data = ? WHERE method = 'password' AND extra_data = ?\",\n [rehashed, passwordHash],\n );\n }\n\n const sessionRow = accountDb.first(\n 'SELECT * FROM sessions WHERE auth_method = ?',\n ['password'],\n );\n\n const token = sessionRow ? sessionRow.token : uuidv4();\n\n const { totalOfUsers } = accountDb.first(\n 'SELECT count(*) as totalOfUsers FROM users',\n );\n let userId = null;\n if (totalOfUsers === 0) {\n userId = uuidv4();\n accountDb.mutate(\n 'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, 1, 1, ?)',\n [userId, '', '', 'ADMIN'],\n );\n } else {\n const { id: userIdFromDb } = accountDb.first(\n 'SELECT id FROM users WHERE user_name = ?',\n [''],\n );\n\n userId = userIdFromDb;\n\n if (!userId) {\n return { error: 'user-not-found' };\n }\n }\n\n let expiration = TOKEN_EXPIRATION_NEVER;\n if (\n config.get('token_expiration') !== 'never' &&\n config.get('token_expiration') !== 'openid-provider' &&\n typeof config.get('token_expiration') === 'number'\n ) {\n expiration =\n Math.floor(Date.now() / 1000) + config.get('token_expiration') * 60;\n }\n\n if (!sessionRow) {\n accountDb.mutate(\n 'INSERT INTO sessions (token, expires_at, user_id, auth_method) VALUES (?, ?, ?, ?)',\n [token, expiration, userId, 'password'],\n );\n } else {\n accountDb.mutate(\n 'UPDATE sessions SET user_id = ?, expires_at = ? WHERE token = ?',\n [userId, expiration, token],\n );\n }\n\n clearExpiredSessions();\n\n return { token };\n}\n\nexport async function changePassword(newPassword) {\n const accountDb = getAccountDb();\n\n if (!isValidPassword(newPassword)) {\n return { error: 'invalid-password' };\n }\n\n const hashed = await hashPassword(newPassword);\n accountDb.mutate(\"UPDATE auth SET extra_data = ? WHERE method = 'password'\", [\n hashed,\n ]);\n return {};\n}\n\nexport async function checkPassword(password) {\n if (!isValidPassword(password)) {\n return false;\n }\n\n const accountDb = getAccountDb();\n const { extra_data: passwordHash } =\n accountDb.first('SELECT extra_data FROM auth WHERE method = ?', [\n 'password',\n ]) || {};\n\n if (!passwordHash) {\n return false;\n }\n\n return await verifyPassword(password, passwordHash);\n}\n","// @ts-strict-ignore\nimport { custom, generators, Issuer } from 'openid-client';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport {\n clearExpiredSessions,\n getAccountDb,\n listLoginMethods,\n} from '#account-db';\nimport { config } from '#load-config';\nimport {\n getUserByUsername,\n transferAllFilesFromUser,\n} from '#services/user-service';\nimport { TOKEN_EXPIRATION_NEVER } from '#util/validate-user';\n\nimport { checkPassword } from './password';\n\nexport type ConfigParameter = {\n issuer?: string;\n discoveryURL?: string;\n client_id?: string;\n client_secret?: string;\n server_hostname?: string;\n};\n\nexport async function bootstrapOpenId(configParameter: ConfigParameter) {\n if (!('issuer' in configParameter) && !('discoveryURL' in configParameter)) {\n return { error: 'missing-issuer-or-discoveryURL' };\n }\n if (!('client_id' in configParameter)) {\n return { error: 'missing-client-id' };\n }\n if (!('client_secret' in configParameter)) {\n return { error: 'missing-client-secret' };\n }\n if (!('server_hostname' in configParameter)) {\n return { error: 'missing-server-hostname' };\n }\n\n custom.setHttpOptionsDefaults({\n timeout: 20 * 1000, // 20 seconds\n });\n\n try {\n //FOR BACKWARD COMPATIBLITY:\n //If we don't put discoverURL into the issuer, it will break already enabled openid instances\n if (configParameter.discoveryURL) {\n configParameter.issuer = configParameter.discoveryURL;\n delete configParameter.discoveryURL;\n }\n\n await setupOpenIdClient(configParameter);\n } catch (err) {\n console.error('Error setting up OpenID client:', err);\n return { error: 'configuration-error' };\n }\n\n const accountDb = getAccountDb();\n try {\n accountDb.transaction(() => {\n accountDb.mutate('DELETE FROM auth WHERE method = ?', ['openid']);\n accountDb.mutate('UPDATE auth SET active = 0');\n accountDb.mutate(\n \"INSERT INTO auth (method, display_name, extra_data, active) VALUES ('openid', 'OpenID', ?, 1)\",\n [JSON.stringify(configParameter)],\n );\n });\n } catch (err) {\n console.error('Error updating auth table:', err);\n return { error: 'database-error' };\n }\n\n return {};\n}\n\nasync function setupOpenIdClient(configParameter) {\n const issuer =\n typeof configParameter.issuer === 'string'\n ? await Issuer.discover(configParameter.issuer)\n : new Issuer({\n issuer: configParameter.issuer.name,\n authorization_endpoint: configParameter.issuer.authorization_endpoint,\n token_endpoint: configParameter.issuer.token_endpoint,\n userinfo_endpoint: configParameter.issuer.userinfo_endpoint,\n });\n\n const client = new issuer.Client({\n client_id: configParameter.client_id,\n client_secret: configParameter.client_secret,\n redirect_uri: new URL(\n '/openid/callback',\n configParameter.server_hostname,\n ).toString(),\n validate_id_token: true,\n });\n\n return client;\n}\n\nexport async function loginWithOpenIdSetup(\n returnUrl,\n firstTimeLoginPassword = '',\n) {\n if (!returnUrl) {\n return { error: 'return-url-missing' };\n }\n if (!isValidRedirectUrl(returnUrl)) {\n return { error: 'invalid-return-url' };\n }\n\n const accountDb = getAccountDb();\n\n const { countUsersWithUserName } = accountDb.first(\n 'SELECT count(*) as countUsersWithUserName FROM users WHERE user_name <> ?',\n [''],\n );\n if (countUsersWithUserName === 0) {\n const methods = listLoginMethods();\n if (methods.some(authMethod => authMethod.method === 'password')) {\n const valid = await checkPassword(firstTimeLoginPassword);\n\n if (!valid) {\n return { error: 'invalid-password' };\n }\n }\n }\n\n let config = accountDb.first('SELECT extra_data FROM auth WHERE method = ?', [\n 'openid',\n ]);\n if (!config) {\n return { error: 'openid-not-configured' };\n }\n\n try {\n config = JSON.parse(config['extra_data']);\n } catch (err) {\n console.error('Error parsing OpenID configuration:', err);\n return { error: 'openid-setup-failed' };\n }\n\n let client;\n try {\n client = await setupOpenIdClient(config);\n } catch (err) {\n console.error('Error setting up OpenID client:', err);\n return { error: 'openid-setup-failed' };\n }\n\n const state = generators.state();\n const code_verifier = generators.codeVerifier();\n const code_challenge = generators.codeChallenge(code_verifier);\n\n const now_time = Date.now();\n const expiry_time = now_time + 300 * 1000;\n\n accountDb.mutate(\n 'DELETE FROM pending_openid_requests WHERE expiry_time < ?',\n [now_time],\n );\n accountDb.mutate(\n 'INSERT INTO pending_openid_requests (state, code_verifier, return_url, expiry_time) VALUES (?, ?, ?, ?)',\n [state, code_verifier, returnUrl, expiry_time],\n );\n\n const url = client.authorizationUrl({\n response_type: 'code',\n scope: 'openid email profile',\n state,\n code_challenge,\n code_challenge_method: 'S256',\n });\n\n return { url };\n}\n\nexport async function loginWithOpenIdFinalize(body) {\n if (!body.code) {\n return { error: 'missing-authorization-code' };\n }\n if (!body.state) {\n return { error: 'missing-state' };\n }\n\n const accountDb = getAccountDb();\n let configFromDb = accountDb.first(\n \"SELECT extra_data FROM auth WHERE method = 'openid' AND active = 1\",\n );\n if (!configFromDb) {\n return { error: 'openid-not-configured' };\n }\n try {\n configFromDb = JSON.parse(configFromDb['extra_data']);\n } catch (err) {\n console.error('Error parsing OpenID configuration:', err);\n return { error: 'openid-setup-failed' };\n }\n let client;\n try {\n client = await setupOpenIdClient(configFromDb);\n } catch (err) {\n console.error('Error setting up OpenID client:', err);\n return { error: 'openid-setup-failed' };\n }\n\n const pendingRequest = accountDb.first(\n 'SELECT code_verifier, return_url FROM pending_openid_requests WHERE state = ? AND expiry_time > ?',\n [body.state, Date.now()],\n );\n\n if (!pendingRequest) {\n return { error: 'invalid-or-expired-state' };\n }\n\n const { code_verifier, return_url } = pendingRequest;\n\n try {\n let tokenSet = null;\n\n if (!configFromDb.authMethod || configFromDb.authMethod === 'openid') {\n const params = { code: body.code, state: body.state, iss: body.iss };\n tokenSet = await client.callback(client.redirect_uris[0], params, {\n code_verifier,\n state: body.state,\n });\n } else {\n tokenSet = await client.grant({\n grant_type: 'authorization_code',\n code: body.code,\n redirect_uri: client.redirect_uris[0],\n code_verifier,\n });\n }\n const userInfo = await client.userinfo(tokenSet.access_token);\n const identity =\n userInfo.preferred_username ??\n userInfo.login ??\n userInfo.email ??\n userInfo.id ??\n userInfo.sub;\n\n if (identity == null) {\n return { error: 'openid-grant-failed: no identification was found' };\n }\n\n let userId = null;\n try {\n accountDb.transaction(() => {\n const { countUsersWithUserName } = accountDb.first(\n 'SELECT count(*) as countUsersWithUserName FROM users WHERE user_name <> ?',\n [''],\n );\n\n // Check if user was created by another transaction\n const existingUser = accountDb.first(\n 'SELECT id FROM users WHERE user_name = ?',\n [identity],\n );\n\n if (\n !existingUser &&\n (countUsersWithUserName === 0 ||\n config.get('userCreationMode') === 'login')\n ) {\n userId = uuidv4();\n accountDb.mutate(\n 'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, 1, ?, ?)',\n [\n userId,\n identity,\n userInfo.name ?? userInfo.email ?? identity,\n countUsersWithUserName === 0 ? '1' : '0',\n countUsersWithUserName === 0 ? 'ADMIN' : 'BASIC',\n ],\n );\n\n if (countUsersWithUserName === 0) {\n const userFromPasswordMethod = getUserByUsername('');\n if (userFromPasswordMethod) {\n transferAllFilesFromUser(userId, userFromPasswordMethod.user_id);\n }\n }\n } else {\n const { id: userIdFromDb, display_name: displayName } =\n accountDb.first(\n 'SELECT id, display_name FROM users WHERE user_name = ? and enabled = 1',\n [identity],\n ) || {};\n\n if (userIdFromDb == null) {\n throw new Error('openid-grant-failed');\n }\n\n if (!displayName && userInfo.name) {\n accountDb.mutate('UPDATE users set display_name = ? WHERE id = ?', [\n userInfo.name,\n userIdFromDb,\n ]);\n }\n\n userId = userIdFromDb;\n }\n });\n } catch (error) {\n if (error.message === 'user-already-exists') {\n return { error: 'user-already-exists' };\n } else if (error.message === 'openid-grant-failed') {\n return { error: 'openid-grant-failed' };\n } else {\n throw error; // Re-throw other unexpected errors\n }\n }\n\n const token = uuidv4();\n\n let expiration;\n if (config.get('token_expiration') === 'openid-provider') {\n expiration = tokenSet.expires_at ?? TOKEN_EXPIRATION_NEVER;\n } else if (config.get('token_expiration') === 'never') {\n expiration = TOKEN_EXPIRATION_NEVER;\n } else if (typeof config.get('token_expiration') === 'number') {\n expiration =\n Math.floor(Date.now() / 1000) + config.get('token_expiration');\n } else {\n expiration = Math.floor(Date.now() / 1000) + 10 * 60; // Default to 10 minutes\n }\n\n accountDb.mutate(\n 'INSERT INTO sessions (token, expires_at, user_id, auth_method) VALUES (?, ?, ?, ?)',\n [token, expiration, userId, 'openid'],\n );\n\n clearExpiredSessions();\n\n return { url: `${return_url}/openid-cb?token=${token}` };\n } catch (err) {\n console.error('OpenID grant failed:', err);\n return { error: 'openid-grant-failed' };\n }\n}\n\nexport function getServerHostname() {\n const auth = getAccountDb().first(\n 'select * from auth WHERE method = ? and active = 1',\n ['openid'],\n );\n if (auth && auth.extra_data) {\n try {\n const openIdConfig = JSON.parse(auth.extra_data);\n return openIdConfig.server_hostname;\n } catch (error) {\n console.error('Error parsing OpenID configuration:', error);\n }\n }\n return null;\n}\n\nexport function isValidRedirectUrl(url: string | undefined): url is string {\n const serverHostname = getServerHostname();\n\n if (!serverHostname) {\n return false;\n }\n\n try {\n const redirectUrl = new URL(url);\n const serverUrl = new URL(serverHostname);\n\n if (\n redirectUrl.hostname === serverUrl.hostname ||\n redirectUrl.hostname === 'localhost'\n ) {\n return true;\n } else {\n return false;\n }\n } catch {\n return false;\n }\n}\n","import Database from 'better-sqlite3';\n\nexport class WrappedDatabase {\n constructor(db) {\n this.db = db;\n }\n\n /**\n * @param {string} sql\n * @param {(string | number)[]} params\n */\n all(sql, params = []) {\n const stmt = this.db.prepare(sql);\n return stmt.all(...params);\n }\n\n /**\n * @param {string} sql\n * @param {string[]} params\n */\n first(sql, params = []) {\n const rows = this.all(sql, params);\n return rows.length === 0 ? null : rows[0];\n }\n\n /**\n * @param {string} sql\n */\n exec(sql) {\n return this.db.exec(sql);\n }\n\n /**\n * @param {string} sql\n * @param {(string | number | null | undefined)[]} params\n */\n mutate(sql, params = []) {\n const stmt = this.db.prepare(sql);\n const info = stmt.run(...params);\n return { changes: info.changes, insertId: info.lastInsertRowid };\n }\n\n /**\n * @param {() => void} fn\n */\n transaction(fn) {\n return this.db.transaction(fn)();\n }\n\n close() {\n this.db.close();\n }\n}\n\n/** @param {string} filename */\nexport function openDatabase(filename) {\n return new WrappedDatabase(new Database(filename));\n}\n","import { join, resolve } from 'node:path';\n\nimport { bootstrapOpenId } from './accounts/openid';\nimport {\n bootstrapPassword,\n hashPassword,\n isValidPassword,\n loginWithPassword,\n setPasswordHash,\n verifyPassword,\n} from './accounts/password';\nimport { openDatabase } from './db';\nimport { config } from './load-config';\n\nlet _accountDb;\n\nexport function getAccountDb() {\n if (_accountDb === undefined) {\n const dbPath = join(resolve(config.get('serverFiles')), 'account.sqlite');\n _accountDb = openDatabase(dbPath);\n }\n\n return _accountDb;\n}\n\nexport function needsBootstrap() {\n const accountDb = getAccountDb();\n const rows = accountDb.all('SELECT * FROM auth');\n return rows.length === 0;\n}\n\nexport function listLoginMethods() {\n const accountDb = getAccountDb();\n const rows = accountDb.all('SELECT method, display_name, active FROM auth');\n return rows\n .filter(f =>\n rows.length > 1 && config.get('enforceOpenId')\n ? f.method === 'openid'\n : true,\n )\n .map(r => ({\n method: r.method,\n active: r.active,\n displayName: r.display_name,\n }));\n}\n\nexport function getActiveLoginMethod() {\n const accountDb = getAccountDb();\n const { method } =\n accountDb.first('SELECT method FROM auth WHERE active = 1') || {};\n return method;\n}\n\n/*\n * Get the Login Method in the following order\n * req (the frontend can say which method in the case it wants to resort to forcing password auth)\n * config options\n * fall back to using password\n */\nexport function getLoginMethod(req) {\n if (\n typeof req !== 'undefined' &&\n (req.body || { loginMethod: null }).loginMethod &&\n config.get('allowedLoginMethods').includes(req.body.loginMethod)\n ) {\n const accountDb = getAccountDb();\n const row = accountDb.first('SELECT method FROM auth WHERE method = ?', [\n req.body.loginMethod,\n ]);\n if (row) return req.body.loginMethod;\n }\n\n //BY-PASS ANY OTHER CONFIGURATION TO ENSURE HEADER AUTH\n if (\n config.get('loginMethod') === 'header' &&\n config.get('allowedLoginMethods').includes('header')\n ) {\n return config.get('loginMethod');\n }\n\n const activeMethod = getActiveLoginMethod();\n return activeMethod || config.get('loginMethod');\n}\n\nexport async function bootstrap(loginSettings, forced = false) {\n if (!loginSettings) {\n return { error: 'invalid-login-settings' };\n }\n const passEnabled = 'password' in loginSettings;\n const openIdEnabled = 'openId' in loginSettings;\n\n const accountDb = getAccountDb();\n\n let passwordHash;\n if (passEnabled) {\n if (!isValidPassword(loginSettings.password)) {\n return { error: 'invalid-password' };\n }\n passwordHash = await hashPassword(loginSettings.password);\n }\n\n accountDb.mutate('BEGIN TRANSACTION');\n try {\n const { countOfOwner } =\n accountDb.first(\n `SELECT count(*) as countOfOwner\n FROM users\n WHERE users.user_name <> '' and users.owner = 1`,\n ) || {};\n\n if (!forced && (!openIdEnabled || countOfOwner > 0)) {\n if (!needsBootstrap()) {\n accountDb.mutate('ROLLBACK');\n return { error: 'already-bootstrapped' };\n }\n }\n\n if (!passEnabled && !openIdEnabled) {\n accountDb.mutate('ROLLBACK');\n return { error: 'no-auth-method-selected' };\n }\n\n if (passEnabled && openIdEnabled && !forced) {\n accountDb.mutate('ROLLBACK');\n return { error: 'max-one-method-allowed' };\n }\n\n if (passEnabled) {\n setPasswordHash(passwordHash);\n }\n\n if (openIdEnabled && forced) {\n const { error } = await bootstrapOpenId(loginSettings.openId);\n if (error) {\n accountDb.mutate('ROLLBACK');\n return { error };\n }\n }\n\n accountDb.mutate('COMMIT');\n return passEnabled ? await loginWithPassword(loginSettings.password) : {};\n } catch (error) {\n accountDb.mutate('ROLLBACK');\n throw error;\n }\n}\n\nexport function isAdmin(userId) {\n return hasPermission(userId, 'ADMIN');\n}\n\nexport function hasPermission(userId, permission) {\n return getUserPermission(userId) === permission;\n}\n\nexport async function enableOpenID(loginSettings) {\n if (!loginSettings || !loginSettings.openId) {\n return { error: 'invalid-login-settings' };\n }\n\n const { error } = (await bootstrapOpenId(loginSettings.openId)) || {};\n if (error) {\n return { error };\n }\n\n getAccountDb().mutate('DELETE FROM sessions');\n}\n\nexport async function disableOpenID(loginSettings) {\n if (!loginSettings || !loginSettings.password) {\n return { error: 'invalid-login-settings' };\n }\n\n const accountDb = getAccountDb();\n const { extra_data: passwordHash } =\n accountDb.first('SELECT extra_data FROM auth WHERE method = ?', [\n 'password',\n ]) || {};\n\n if (!passwordHash) {\n return { error: 'invalid-password' };\n }\n\n if (!loginSettings?.password) {\n return { error: 'invalid-password' };\n }\n\n const confirmed = await verifyPassword(loginSettings.password, passwordHash);\n\n if (!confirmed) {\n return { error: 'invalid-password' };\n }\n\n const { error } = await bootstrapPassword(loginSettings.password);\n if (error) {\n return { error };\n }\n\n try {\n accountDb.transaction(() => {\n accountDb.mutate('DELETE FROM sessions');\n accountDb.mutate(\n `DELETE FROM user_access\n WHERE user_access.user_id IN (\n SELECT users.id\n FROM users\n WHERE users.user_name <> ?\n );`,\n [''],\n );\n accountDb.mutate('DELETE FROM users WHERE user_name <> ?', ['']);\n accountDb.mutate('DELETE FROM auth WHERE method = ?', ['openid']);\n });\n } catch (err) {\n console.error('Error cleaning up openid information:', err);\n return { error: 'database-error' };\n }\n}\n\nexport function getSession(token) {\n const accountDb = getAccountDb();\n return accountDb.first(\n `SELECT sessions.*\n FROM sessions\n JOIN users ON users.id = sessions.user_id\n WHERE sessions.token = ? AND users.enabled = 1`,\n [token],\n );\n}\n\nexport function getUserInfo(userId) {\n const accountDb = getAccountDb();\n return accountDb.first('SELECT * FROM users WHERE id = ?', [userId]);\n}\n\nexport function getUserPermission(userId) {\n const accountDb = getAccountDb();\n const { role } = accountDb.first(\n `SELECT role FROM users\n WHERE users.id = ?`,\n [userId],\n ) || { role: '' };\n\n return role;\n}\n\nexport function getServerPrefs() {\n const accountDb = getAccountDb();\n const rows = accountDb.all('SELECT key, value FROM server_prefs') || [];\n\n return rows.reduce((prefs, row) => {\n prefs[row.key] = row.value;\n return prefs;\n }, {});\n}\n\nexport function setServerPrefs(prefs) {\n const accountDb = getAccountDb();\n\n if (!prefs) {\n return;\n }\n\n accountDb.transaction(() => {\n Object.entries(prefs).forEach(([key, value]) => {\n accountDb.mutate(\n 'INSERT INTO server_prefs (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value',\n [key, value],\n );\n });\n });\n}\n\nexport function clearExpiredSessions() {\n const clearThreshold = Math.floor(Date.now() / 1000) - 3600;\n\n const deletedSessions = getAccountDb().mutate(\n 'DELETE FROM sessions WHERE expires_at <> -1 and expires_at < ?',\n [clearThreshold],\n ).changes;\n\n console.log(`Deleted ${deletedSessions} old sessions`);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"account-db-B0u6vCZk.js","names":["uuidv4","config","config"],"sources":["../../src/services/user-service.ts","../../src/util/validate-user.ts","../../src/accounts/password.js","../../src/accounts/openid.ts","../../src/db.js","../../src/account-db.js"],"sourcesContent":["// @ts-strict-ignore\nimport { getAccountDb } from '#account-db';\n\nexport function getUserByUsername(userName) {\n if (!userName || typeof userName !== 'string') {\n return null;\n }\n const { id } =\n getAccountDb().first('SELECT id FROM users WHERE user_name = ?', [\n userName,\n ]) || {};\n return id || null;\n}\n\nexport function getUserById(userId) {\n if (!userId) {\n return null;\n }\n const { id } =\n getAccountDb().first('SELECT * FROM users WHERE id = ?', [userId]) || {};\n return id || null;\n}\n\nexport function getFileById(fileId) {\n if (!fileId) {\n return null;\n }\n const { id } =\n getAccountDb().first('SELECT * FROM files WHERE files.id = ?', [fileId]) ||\n {};\n return id || null;\n}\n\nexport function validateRole(roleId) {\n const possibleRoles = ['BASIC', 'ADMIN'];\n return possibleRoles.some(a => a === roleId);\n}\n\nexport function getOwnerCount(): number {\n const { ownerCount } = getAccountDb().first(\n `SELECT count(*) as ownerCount FROM users WHERE users.user_name <> '' and users.owner = 1`,\n ) || { ownerCount: 0 };\n return ownerCount;\n}\n\nexport function getOwnerId() {\n const { id } =\n getAccountDb().first(\n `SELECT users.id FROM users WHERE users.user_name <> '' and users.owner = 1`,\n ) || {};\n return id;\n}\n\nexport function getAllUsers() {\n return getAccountDb().all(\n `SELECT users.id, user_name as userName, display_name as displayName, enabled, ifnull(owner,0) as owner, role\n FROM users\n WHERE users.user_name <> ''`,\n );\n}\n\nexport function insertUser(userId, userName, displayName, enabled, role) {\n getAccountDb().mutate(\n 'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, ?, 0, ?)',\n [userId, userName, displayName, enabled, role],\n );\n}\n\nexport function updateUserWithRole(\n userId,\n userName,\n displayName,\n enabled,\n roleId,\n) {\n getAccountDb().transaction(() => {\n getAccountDb().mutate(\n 'UPDATE users SET user_name = ?, display_name = ?, enabled = ?, role = ? WHERE id = ?',\n [userName, displayName, enabled, roleId, userId],\n );\n if (!enabled) {\n getAccountDb().mutate('DELETE FROM sessions WHERE user_id = ?', [userId]);\n }\n });\n}\n\nexport function deleteUser(userId) {\n return getAccountDb().transaction(() => {\n const { changes } = getAccountDb().mutate(\n 'DELETE FROM users WHERE id = ? and owner = 0',\n [userId],\n );\n if (changes > 0) {\n getAccountDb().mutate('DELETE FROM sessions WHERE user_id = ?', [userId]);\n }\n return changes;\n });\n}\nexport function deleteUserAccess(userId) {\n try {\n return getAccountDb().mutate('DELETE FROM user_access WHERE user_id = ?', [\n userId,\n ]).changes;\n } catch (error) {\n throw new Error(`Failed to delete user access: ${error.message}`);\n }\n}\n\nexport function transferAllFilesFromUser(ownerId, oldUserId) {\n if (!ownerId || !oldUserId) {\n throw new Error('Invalid user IDs');\n }\n try {\n getAccountDb().transaction(() => {\n const ownerExists = getUserById(ownerId);\n if (!ownerExists) {\n throw new Error('New owner not found');\n }\n getAccountDb().mutate('UPDATE files set owner = ? WHERE owner = ?', [\n ownerId,\n oldUserId,\n ]);\n });\n } catch (error) {\n throw new Error(`Failed to transfer files: ${error.message}`);\n }\n}\n\nexport function updateFileOwner(ownerId, fileId) {\n if (!ownerId || !fileId) {\n throw new Error('Invalid parameters');\n }\n try {\n const result = getAccountDb().mutate(\n 'UPDATE files set owner = ? WHERE id = ?',\n [ownerId, fileId],\n );\n if (result.changes === 0) {\n throw new Error('File not found');\n }\n } catch (error) {\n throw new Error(`Failed to update file owner: ${error.message}`);\n }\n}\n\nexport function getUserAccess(fileId, userId, isAdmin) {\n return getAccountDb().all(\n `SELECT users.id as userId, user_name as userName, files.owner, display_name as displayName\n FROM users\n JOIN user_access ON user_access.user_id = users.id\n JOIN files ON files.id = user_access.file_id\n WHERE files.id = ? and (files.owner = ? OR 1 = ?)`,\n [fileId, userId, isAdmin ? 1 : 0],\n );\n}\n\nexport function countUserAccess(fileId, userId) {\n const { accessCount } =\n getAccountDb().first(\n `SELECT COUNT(*) as accessCount\n FROM files\n WHERE files.id = ? AND (files.owner = ? OR EXISTS (\n SELECT 1 FROM user_access\n WHERE user_access.user_id = ? AND user_access.file_id = ?)\n )`,\n [fileId, userId, userId, fileId],\n ) || {};\n\n return accessCount || 0;\n}\n\nexport function checkFilePermission(fileId, userId) {\n return (\n getAccountDb().first(\n `SELECT 1 as granted\n FROM files\n WHERE files.id = ? and (files.owner = ?)`,\n [fileId, userId],\n ) || { granted: 0 }\n );\n}\n\nexport function addUserAccess(userId, fileId) {\n if (!userId || !fileId) {\n throw new Error('Invalid parameters');\n }\n try {\n const userExists = getUserById(userId);\n const fileExists = getFileById(fileId);\n if (!userExists || !fileExists) {\n throw new Error('User or file not found');\n }\n getAccountDb().mutate(\n 'INSERT INTO user_access (user_id, file_id) VALUES (?, ?)',\n [userId, fileId],\n );\n } catch (error) {\n if (error.message.includes('UNIQUE constraint')) {\n throw new Error('Access already exists');\n }\n throw new Error(`Failed to add user access: ${error.message}`);\n }\n}\n\nexport function deleteUserAccessByFileId(userIds, fileId) {\n if (!Array.isArray(userIds) || userIds.length === 0) {\n throw new Error('The provided userIds must be a non-empty array.');\n }\n\n const CHUNK_SIZE = 999;\n let totalChanges = 0;\n\n try {\n getAccountDb().transaction(() => {\n for (let i = 0; i < userIds.length; i += CHUNK_SIZE) {\n const chunk = userIds.slice(i, i + CHUNK_SIZE);\n const placeholders = chunk.map(() => '?').join(',');\n\n const sql = `DELETE FROM user_access WHERE user_id IN (${placeholders}) AND file_id = ?`;\n\n const result = getAccountDb().mutate(sql, [...chunk, fileId]);\n totalChanges += result.changes;\n }\n });\n } catch (error) {\n throw new Error(`Failed to delete user access: ${error.message}`);\n }\n\n return totalChanges;\n}\n\nexport function getAllUserAccess(fileId) {\n //This can't be used here until we can create user invite links:\n //const isLoginMode = config.get('userCreationMode') === 'login';\n const isLoginMode = false;\n const joinType = isLoginMode ? 'JOIN' : 'LEFT JOIN';\n\n return getAccountDb().all(\n `\n SELECT\n users.id as userId,\n user_name as userName,\n display_name as displayName,\n CASE WHEN user_access.file_id IS NULL THEN 0 ELSE 1 END as haveAccess,\n CASE WHEN files.id IS NULL THEN 0 ELSE 1 END as owner\n FROM users\n ${joinType} user_access ON user_access.file_id = ? AND user_access.user_id = users.id\n ${joinType} files ON files.id = ? AND files.owner = users.id\n WHERE users.enabled = 1\n AND users.user_name <> ''\n `,\n [fileId, fileId],\n );\n}\n\nexport function getOpenIDConfig() {\n return (\n getAccountDb().first(`SELECT * FROM auth WHERE method = ?`, ['openid']) ||\n null\n );\n}\n","import type { Request, Response } from 'express';\nimport ipaddr from 'ipaddr.js';\n\nimport { getSession } from '#account-db';\nimport { config } from '#load-config';\n\nexport const TOKEN_EXPIRATION_NEVER = -1;\nconst MS_PER_SECOND = 1000;\n\nexport function validateSession(req: Request, res: Response) {\n let { token } = req.body || {};\n\n if (!token) {\n token = req.headers['x-actual-token'];\n }\n\n const session = getSession(token);\n\n if (!session) {\n res.status(401);\n res.send({\n status: 'error',\n reason: 'unauthorized',\n details: 'token-not-found',\n });\n return null;\n }\n\n if (\n session.expires_at !== TOKEN_EXPIRATION_NEVER &&\n session.expires_at * MS_PER_SECOND <= Date.now()\n ) {\n res.status(401);\n res.send({\n status: 'error',\n reason: 'token-expired',\n });\n return null;\n }\n\n return session;\n}\n\nexport function validateAuthHeader(req: Request) {\n // fallback to trustedProxies when trustedAuthProxies not set\n const trustedAuthProxies: string[] =\n config.get('trustedAuthProxies') ?? config.get('trustedProxies');\n // ensure the first hop from our server is trusted\n const peer = req.socket.remoteAddress;\n if (peer === undefined) {\n console.error(`Header Auth Login attempted but there was no defined peer.`);\n return false;\n }\n const peerIp = ipaddr.process(peer);\n const rangeList = {\n allowed_ips: trustedAuthProxies.map(q => ipaddr.parseCIDR(q)),\n };\n\n const matched = ipaddr.subnetMatch(peerIp, rangeList, 'fail');\n\n if (matched === 'allowed_ips') {\n console.info(`Header Auth Login permitted from ${peer}`);\n return true;\n } else {\n console.warn(`Header Auth Login attempted from ${peer}`);\n return false;\n }\n}\n","import * as argon2 from 'argon2';\nimport * as bcrypt from 'bcrypt';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { clearExpiredSessions, getAccountDb } from '#account-db';\nimport { config } from '#load-config';\nimport { TOKEN_EXPIRATION_NEVER } from '#util/validate-user';\n\n// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id\nconst ARGON2_OPTIONS = {\n type: argon2.argon2id,\n memoryCost: 47104,\n timeCost: 1,\n parallelism: 1,\n};\n\nexport function isValidPassword(password) {\n return password != null && password !== '';\n}\n\nexport function hashPassword(password) {\n return argon2.hash(password, ARGON2_OPTIONS);\n}\n\nexport async function verifyPassword(password, hash) {\n if (typeof hash !== 'string') return false;\n\n if (hash.startsWith('$argon2')) {\n try {\n return await argon2.verify(hash, password);\n } catch {\n return false;\n }\n }\n\n try {\n return await bcrypt.compare(password, hash);\n } catch {\n return false;\n }\n}\n\nfunction isLegacyHash(hash) {\n return typeof hash === 'string' && !hash.startsWith('$argon2');\n}\n\nexport function setPasswordHash(hashed) {\n const accountDb = getAccountDb();\n accountDb.transaction(() => {\n accountDb.mutate('DELETE FROM auth WHERE method = ?', ['password']);\n accountDb.mutate('UPDATE auth SET active = 0');\n accountDb.mutate(\n \"INSERT INTO auth (method, display_name, extra_data, active) VALUES ('password', 'Password', ?, 1)\",\n [hashed],\n );\n });\n}\n\nexport async function bootstrapPassword(password) {\n if (!isValidPassword(password)) {\n return { error: 'invalid-password' };\n }\n\n const hashed = await hashPassword(password);\n setPasswordHash(hashed);\n\n return {};\n}\n\nexport async function loginWithPassword(password) {\n if (!isValidPassword(password)) {\n return { error: 'invalid-password' };\n }\n\n const accountDb = getAccountDb();\n const { extra_data: passwordHash } =\n accountDb.first('SELECT extra_data FROM auth WHERE method = ?', [\n 'password',\n ]) || {};\n\n if (!passwordHash) {\n return { error: 'invalid-password' };\n }\n\n const confirmed = await verifyPassword(password, passwordHash);\n\n if (!confirmed) {\n return { error: 'invalid-password' };\n }\n\n if (isLegacyHash(passwordHash)) {\n const rehashed = await hashPassword(password);\n accountDb.mutate(\n \"UPDATE auth SET extra_data = ? WHERE method = 'password' AND extra_data = ?\",\n [rehashed, passwordHash],\n );\n }\n\n const sessionRow = accountDb.first(\n 'SELECT * FROM sessions WHERE auth_method = ?',\n ['password'],\n );\n\n const token = sessionRow ? sessionRow.token : uuidv4();\n\n const { totalOfUsers } = accountDb.first(\n 'SELECT count(*) as totalOfUsers FROM users',\n );\n let userId = null;\n if (totalOfUsers === 0) {\n userId = uuidv4();\n accountDb.mutate(\n 'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, 1, 1, ?)',\n [userId, '', '', 'ADMIN'],\n );\n } else {\n const { id: userIdFromDb } = accountDb.first(\n 'SELECT id FROM users WHERE user_name = ?',\n [''],\n );\n\n userId = userIdFromDb;\n\n if (!userId) {\n return { error: 'user-not-found' };\n }\n }\n\n let expiration = TOKEN_EXPIRATION_NEVER;\n if (\n config.get('token_expiration') !== 'never' &&\n config.get('token_expiration') !== 'openid-provider' &&\n typeof config.get('token_expiration') === 'number'\n ) {\n expiration =\n Math.floor(Date.now() / 1000) + config.get('token_expiration') * 60;\n }\n\n if (!sessionRow) {\n accountDb.mutate(\n 'INSERT INTO sessions (token, expires_at, user_id, auth_method) VALUES (?, ?, ?, ?)',\n [token, expiration, userId, 'password'],\n );\n } else {\n accountDb.mutate(\n 'UPDATE sessions SET user_id = ?, expires_at = ? WHERE token = ?',\n [userId, expiration, token],\n );\n }\n\n clearExpiredSessions();\n\n return { token };\n}\n\nexport async function changePassword(newPassword) {\n const accountDb = getAccountDb();\n\n if (!isValidPassword(newPassword)) {\n return { error: 'invalid-password' };\n }\n\n const hashed = await hashPassword(newPassword);\n accountDb.mutate(\"UPDATE auth SET extra_data = ? WHERE method = 'password'\", [\n hashed,\n ]);\n return {};\n}\n\nexport async function checkPassword(password) {\n if (!isValidPassword(password)) {\n return false;\n }\n\n const accountDb = getAccountDb();\n const { extra_data: passwordHash } =\n accountDb.first('SELECT extra_data FROM auth WHERE method = ?', [\n 'password',\n ]) || {};\n\n if (!passwordHash) {\n return false;\n }\n\n return await verifyPassword(password, passwordHash);\n}\n","// @ts-strict-ignore\nimport { custom, generators, Issuer } from 'openid-client';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport {\n clearExpiredSessions,\n getAccountDb,\n listLoginMethods,\n} from '#account-db';\nimport { config } from '#load-config';\nimport {\n getUserByUsername,\n transferAllFilesFromUser,\n} from '#services/user-service';\nimport { TOKEN_EXPIRATION_NEVER } from '#util/validate-user';\n\nimport { checkPassword } from './password';\n\nexport type ConfigParameter = {\n issuer?: string;\n discoveryURL?: string;\n client_id?: string;\n client_secret?: string;\n server_hostname?: string;\n};\n\nexport async function bootstrapOpenId(configParameter: ConfigParameter) {\n if (!('issuer' in configParameter) && !('discoveryURL' in configParameter)) {\n return { error: 'missing-issuer-or-discoveryURL' };\n }\n if (!('client_id' in configParameter)) {\n return { error: 'missing-client-id' };\n }\n if (!('client_secret' in configParameter)) {\n return { error: 'missing-client-secret' };\n }\n if (!('server_hostname' in configParameter)) {\n return { error: 'missing-server-hostname' };\n }\n\n custom.setHttpOptionsDefaults({\n timeout: 20 * 1000, // 20 seconds\n });\n\n try {\n //FOR BACKWARD COMPATIBLITY:\n //If we don't put discoverURL into the issuer, it will break already enabled openid instances\n if (configParameter.discoveryURL) {\n configParameter.issuer = configParameter.discoveryURL;\n delete configParameter.discoveryURL;\n }\n\n await setupOpenIdClient(configParameter);\n } catch (err) {\n console.error('Error setting up OpenID client:', err);\n return { error: 'configuration-error' };\n }\n\n const accountDb = getAccountDb();\n try {\n accountDb.transaction(() => {\n accountDb.mutate('DELETE FROM auth WHERE method = ?', ['openid']);\n accountDb.mutate('UPDATE auth SET active = 0');\n accountDb.mutate(\n \"INSERT INTO auth (method, display_name, extra_data, active) VALUES ('openid', 'OpenID', ?, 1)\",\n [JSON.stringify(configParameter)],\n );\n });\n } catch (err) {\n console.error('Error updating auth table:', err);\n return { error: 'database-error' };\n }\n\n return {};\n}\n\nasync function setupOpenIdClient(configParameter) {\n const issuer =\n typeof configParameter.issuer === 'string'\n ? await Issuer.discover(configParameter.issuer)\n : new Issuer({\n issuer: configParameter.issuer.name,\n authorization_endpoint: configParameter.issuer.authorization_endpoint,\n token_endpoint: configParameter.issuer.token_endpoint,\n userinfo_endpoint: configParameter.issuer.userinfo_endpoint,\n });\n\n const client = new issuer.Client({\n client_id: configParameter.client_id,\n client_secret: configParameter.client_secret,\n redirect_uri: new URL(\n '/openid/callback',\n configParameter.server_hostname,\n ).toString(),\n validate_id_token: true,\n });\n\n return client;\n}\n\nexport async function loginWithOpenIdSetup(\n returnUrl,\n firstTimeLoginPassword = '',\n) {\n if (!returnUrl) {\n return { error: 'return-url-missing' };\n }\n if (!isValidRedirectUrl(returnUrl)) {\n return { error: 'invalid-return-url' };\n }\n\n const accountDb = getAccountDb();\n\n const { countUsersWithUserName } = accountDb.first(\n 'SELECT count(*) as countUsersWithUserName FROM users WHERE user_name <> ?',\n [''],\n );\n if (countUsersWithUserName === 0) {\n const methods = listLoginMethods();\n if (methods.some(authMethod => authMethod.method === 'password')) {\n const valid = await checkPassword(firstTimeLoginPassword);\n\n if (!valid) {\n return { error: 'invalid-password' };\n }\n }\n }\n\n let config = accountDb.first('SELECT extra_data FROM auth WHERE method = ?', [\n 'openid',\n ]);\n if (!config) {\n return { error: 'openid-not-configured' };\n }\n\n try {\n config = JSON.parse(config['extra_data']);\n } catch (err) {\n console.error('Error parsing OpenID configuration:', err);\n return { error: 'openid-setup-failed' };\n }\n\n let client;\n try {\n client = await setupOpenIdClient(config);\n } catch (err) {\n console.error('Error setting up OpenID client:', err);\n return { error: 'openid-setup-failed' };\n }\n\n const state = generators.state();\n const code_verifier = generators.codeVerifier();\n const code_challenge = generators.codeChallenge(code_verifier);\n\n const now_time = Date.now();\n const expiry_time = now_time + 300 * 1000;\n\n accountDb.mutate(\n 'DELETE FROM pending_openid_requests WHERE expiry_time < ?',\n [now_time],\n );\n accountDb.mutate(\n 'INSERT INTO pending_openid_requests (state, code_verifier, return_url, expiry_time) VALUES (?, ?, ?, ?)',\n [state, code_verifier, returnUrl, expiry_time],\n );\n\n const url = client.authorizationUrl({\n response_type: 'code',\n scope: 'openid email profile',\n state,\n code_challenge,\n code_challenge_method: 'S256',\n });\n\n return { url };\n}\n\nexport async function loginWithOpenIdFinalize(body) {\n if (!body.code) {\n return { error: 'missing-authorization-code' };\n }\n if (!body.state) {\n return { error: 'missing-state' };\n }\n\n const accountDb = getAccountDb();\n let configFromDb = accountDb.first(\n \"SELECT extra_data FROM auth WHERE method = 'openid' AND active = 1\",\n );\n if (!configFromDb) {\n return { error: 'openid-not-configured' };\n }\n try {\n configFromDb = JSON.parse(configFromDb['extra_data']);\n } catch (err) {\n console.error('Error parsing OpenID configuration:', err);\n return { error: 'openid-setup-failed' };\n }\n let client;\n try {\n client = await setupOpenIdClient(configFromDb);\n } catch (err) {\n console.error('Error setting up OpenID client:', err);\n return { error: 'openid-setup-failed' };\n }\n\n const pendingRequest = accountDb.first(\n 'SELECT code_verifier, return_url FROM pending_openid_requests WHERE state = ? AND expiry_time > ?',\n [body.state, Date.now()],\n );\n\n if (!pendingRequest) {\n return { error: 'invalid-or-expired-state' };\n }\n\n const { code_verifier, return_url } = pendingRequest;\n\n try {\n let tokenSet = null;\n\n if (!configFromDb.authMethod || configFromDb.authMethod === 'openid') {\n const params = { code: body.code, state: body.state, iss: body.iss };\n tokenSet = await client.callback(client.redirect_uris[0], params, {\n code_verifier,\n state: body.state,\n });\n } else {\n tokenSet = await client.grant({\n grant_type: 'authorization_code',\n code: body.code,\n redirect_uri: client.redirect_uris[0],\n code_verifier,\n });\n }\n const userInfo = await client.userinfo(tokenSet.access_token);\n const identity =\n userInfo.preferred_username ??\n userInfo.login ??\n userInfo.email ??\n userInfo.id ??\n userInfo.sub;\n\n if (identity == null) {\n return { error: 'openid-grant-failed: no identification was found' };\n }\n\n let userId = null;\n try {\n accountDb.transaction(() => {\n const { countUsersWithUserName } = accountDb.first(\n 'SELECT count(*) as countUsersWithUserName FROM users WHERE user_name <> ?',\n [''],\n );\n\n // Check if user was created by another transaction\n const existingUser = accountDb.first(\n 'SELECT id FROM users WHERE user_name = ?',\n [identity],\n );\n\n if (\n !existingUser &&\n (countUsersWithUserName === 0 ||\n config.get('userCreationMode') === 'login')\n ) {\n userId = uuidv4();\n accountDb.mutate(\n 'INSERT INTO users (id, user_name, display_name, enabled, owner, role) VALUES (?, ?, ?, 1, ?, ?)',\n [\n userId,\n identity,\n userInfo.name ?? userInfo.email ?? identity,\n countUsersWithUserName === 0 ? '1' : '0',\n countUsersWithUserName === 0 ? 'ADMIN' : 'BASIC',\n ],\n );\n\n if (countUsersWithUserName === 0) {\n const userFromPasswordMethod = getUserByUsername('');\n if (userFromPasswordMethod) {\n transferAllFilesFromUser(userId, userFromPasswordMethod.user_id);\n }\n }\n } else {\n const { id: userIdFromDb, display_name: displayName } =\n accountDb.first(\n 'SELECT id, display_name FROM users WHERE user_name = ? and enabled = 1',\n [identity],\n ) || {};\n\n if (userIdFromDb == null) {\n throw new Error('openid-grant-failed');\n }\n\n if (!displayName && userInfo.name) {\n accountDb.mutate('UPDATE users set display_name = ? WHERE id = ?', [\n userInfo.name,\n userIdFromDb,\n ]);\n }\n\n userId = userIdFromDb;\n }\n });\n } catch (error) {\n if (error.message === 'user-already-exists') {\n return { error: 'user-already-exists' };\n } else if (error.message === 'openid-grant-failed') {\n return { error: 'openid-grant-failed' };\n } else {\n throw error; // Re-throw other unexpected errors\n }\n }\n\n const token = uuidv4();\n\n let expiration;\n if (config.get('token_expiration') === 'openid-provider') {\n expiration = tokenSet.expires_at ?? TOKEN_EXPIRATION_NEVER;\n } else if (config.get('token_expiration') === 'never') {\n expiration = TOKEN_EXPIRATION_NEVER;\n } else if (typeof config.get('token_expiration') === 'number') {\n expiration =\n Math.floor(Date.now() / 1000) + config.get('token_expiration');\n } else {\n expiration = Math.floor(Date.now() / 1000) + 10 * 60; // Default to 10 minutes\n }\n\n accountDb.mutate(\n 'INSERT INTO sessions (token, expires_at, user_id, auth_method) VALUES (?, ?, ?, ?)',\n [token, expiration, userId, 'openid'],\n );\n\n clearExpiredSessions();\n\n return { url: `${return_url}/openid-cb?token=${token}` };\n } catch (err) {\n console.error('OpenID grant failed:', err);\n return { error: 'openid-grant-failed' };\n }\n}\n\nexport function getServerHostname() {\n const auth = getAccountDb().first(\n 'select * from auth WHERE method = ? and active = 1',\n ['openid'],\n );\n if (auth && auth.extra_data) {\n try {\n const openIdConfig = JSON.parse(auth.extra_data);\n return openIdConfig.server_hostname;\n } catch (error) {\n console.error('Error parsing OpenID configuration:', error);\n }\n }\n return null;\n}\n\nexport function isValidRedirectUrl(url: string | undefined): url is string {\n const serverHostname = getServerHostname();\n\n if (!serverHostname) {\n return false;\n }\n\n try {\n const redirectUrl = new URL(url);\n const serverUrl = new URL(serverHostname);\n\n if (\n redirectUrl.hostname === serverUrl.hostname ||\n redirectUrl.hostname === 'localhost'\n ) {\n return true;\n } else {\n return false;\n }\n } catch {\n return false;\n }\n}\n","import Database from 'better-sqlite3';\n\nexport class WrappedDatabase {\n constructor(db) {\n this.db = db;\n }\n\n /**\n * @param {string} sql\n * @param {(string | number)[]} params\n */\n all(sql, params = []) {\n const stmt = this.db.prepare(sql);\n return stmt.all(...params);\n }\n\n /**\n * @param {string} sql\n * @param {string[]} params\n */\n first(sql, params = []) {\n const rows = this.all(sql, params);\n return rows.length === 0 ? null : rows[0];\n }\n\n /**\n * @param {string} sql\n */\n exec(sql) {\n return this.db.exec(sql);\n }\n\n /**\n * @param {string} sql\n * @param {(string | number | null | undefined)[]} params\n */\n mutate(sql, params = []) {\n const stmt = this.db.prepare(sql);\n const info = stmt.run(...params);\n return { changes: info.changes, insertId: info.lastInsertRowid };\n }\n\n /**\n * @param {() => void} fn\n */\n transaction(fn) {\n return this.db.transaction(fn)();\n }\n\n close() {\n this.db.close();\n }\n}\n\n/** @param {string} filename */\nexport function openDatabase(filename) {\n return new WrappedDatabase(new Database(filename));\n}\n","import { join, resolve } from 'node:path';\n\nimport { bootstrapOpenId } from './accounts/openid';\nimport {\n bootstrapPassword,\n hashPassword,\n isValidPassword,\n loginWithPassword,\n setPasswordHash,\n verifyPassword,\n} from './accounts/password';\nimport { openDatabase } from './db';\nimport { config } from './load-config';\n\nlet _accountDb;\n\nexport function getAccountDb() {\n if (_accountDb === undefined) {\n const dbPath = join(resolve(config.get('serverFiles')), 'account.sqlite');\n _accountDb = openDatabase(dbPath);\n }\n\n return _accountDb;\n}\n\nexport function needsBootstrap() {\n const accountDb = getAccountDb();\n const rows = accountDb.all('SELECT * FROM auth');\n return rows.length === 0;\n}\n\nexport function listLoginMethods() {\n const accountDb = getAccountDb();\n const rows = accountDb.all('SELECT method, display_name, active FROM auth');\n return rows\n .filter(f =>\n rows.length > 1 && config.get('enforceOpenId')\n ? f.method === 'openid'\n : true,\n )\n .map(r => ({\n method: r.method,\n active: r.active,\n displayName: r.display_name,\n }));\n}\n\nexport function getActiveLoginMethod() {\n const accountDb = getAccountDb();\n const { method } =\n accountDb.first('SELECT method FROM auth WHERE active = 1') || {};\n return method;\n}\n\n/*\n * Get the Login Method in the following order\n * req (the frontend can say which method in the case it wants to resort to forcing password auth)\n * config options\n * fall back to using password\n */\nexport function getLoginMethod(req) {\n if (\n typeof req !== 'undefined' &&\n (req.body || { loginMethod: null }).loginMethod &&\n config.get('allowedLoginMethods').includes(req.body.loginMethod)\n ) {\n const accountDb = getAccountDb();\n const row = accountDb.first('SELECT method FROM auth WHERE method = ?', [\n req.body.loginMethod,\n ]);\n if (row) return req.body.loginMethod;\n }\n\n //BY-PASS ANY OTHER CONFIGURATION TO ENSURE HEADER AUTH\n if (\n config.get('loginMethod') === 'header' &&\n config.get('allowedLoginMethods').includes('header')\n ) {\n return config.get('loginMethod');\n }\n\n const activeMethod = getActiveLoginMethod();\n return activeMethod || config.get('loginMethod');\n}\n\nexport async function bootstrap(loginSettings, forced = false) {\n if (!loginSettings) {\n return { error: 'invalid-login-settings' };\n }\n const passEnabled = 'password' in loginSettings;\n const openIdEnabled = 'openId' in loginSettings;\n\n const accountDb = getAccountDb();\n\n let passwordHash;\n if (passEnabled) {\n if (!isValidPassword(loginSettings.password)) {\n return { error: 'invalid-password' };\n }\n passwordHash = await hashPassword(loginSettings.password);\n }\n\n accountDb.mutate('BEGIN TRANSACTION');\n try {\n const { countOfOwner } =\n accountDb.first(\n `SELECT count(*) as countOfOwner\n FROM users\n WHERE users.user_name <> '' and users.owner = 1`,\n ) || {};\n\n if (!forced && (!openIdEnabled || countOfOwner > 0)) {\n if (!needsBootstrap()) {\n accountDb.mutate('ROLLBACK');\n return { error: 'already-bootstrapped' };\n }\n }\n\n if (!passEnabled && !openIdEnabled) {\n accountDb.mutate('ROLLBACK');\n return { error: 'no-auth-method-selected' };\n }\n\n if (passEnabled && openIdEnabled && !forced) {\n accountDb.mutate('ROLLBACK');\n return { error: 'max-one-method-allowed' };\n }\n\n if (passEnabled) {\n setPasswordHash(passwordHash);\n }\n\n if (openIdEnabled && forced) {\n const { error } = await bootstrapOpenId(loginSettings.openId);\n if (error) {\n accountDb.mutate('ROLLBACK');\n return { error };\n }\n }\n\n accountDb.mutate('COMMIT');\n return passEnabled ? await loginWithPassword(loginSettings.password) : {};\n } catch (error) {\n accountDb.mutate('ROLLBACK');\n throw error;\n }\n}\n\nexport function isAdmin(userId) {\n return hasPermission(userId, 'ADMIN');\n}\n\nexport function hasPermission(userId, permission) {\n return getUserPermission(userId) === permission;\n}\n\nexport async function enableOpenID(loginSettings) {\n if (!loginSettings || !loginSettings.openId) {\n return { error: 'invalid-login-settings' };\n }\n\n const { error } = (await bootstrapOpenId(loginSettings.openId)) || {};\n if (error) {\n return { error };\n }\n\n getAccountDb().mutate('DELETE FROM sessions');\n}\n\nexport async function disableOpenID(loginSettings) {\n if (!loginSettings || !loginSettings.password) {\n return { error: 'invalid-login-settings' };\n }\n\n const accountDb = getAccountDb();\n const { extra_data: passwordHash } =\n accountDb.first('SELECT extra_data FROM auth WHERE method = ?', [\n 'password',\n ]) || {};\n\n if (!passwordHash) {\n return { error: 'invalid-password' };\n }\n\n if (!loginSettings?.password) {\n return { error: 'invalid-password' };\n }\n\n const confirmed = await verifyPassword(loginSettings.password, passwordHash);\n\n if (!confirmed) {\n return { error: 'invalid-password' };\n }\n\n const { error } = await bootstrapPassword(loginSettings.password);\n if (error) {\n return { error };\n }\n\n try {\n accountDb.transaction(() => {\n accountDb.mutate('DELETE FROM sessions');\n accountDb.mutate(\n `DELETE FROM user_access\n WHERE user_access.user_id IN (\n SELECT users.id\n FROM users\n WHERE users.user_name <> ?\n );`,\n [''],\n );\n accountDb.mutate('DELETE FROM users WHERE user_name <> ?', ['']);\n accountDb.mutate('DELETE FROM auth WHERE method = ?', ['openid']);\n });\n } catch (err) {\n console.error('Error cleaning up openid information:', err);\n return { error: 'database-error' };\n }\n}\n\nexport function getSession(token) {\n const accountDb = getAccountDb();\n return accountDb.first(\n `SELECT sessions.*\n FROM sessions\n JOIN users ON users.id = sessions.user_id\n WHERE sessions.token = ? AND users.enabled = 1`,\n [token],\n );\n}\n\nexport function getUserInfo(userId) {\n const accountDb = getAccountDb();\n return accountDb.first('SELECT * FROM users WHERE id = ?', [userId]);\n}\n\nexport function getUserPermission(userId) {\n const accountDb = getAccountDb();\n const { role } = accountDb.first(\n `SELECT role FROM users\n WHERE users.id = ?`,\n [userId],\n ) || { role: '' };\n\n return role;\n}\n\nexport function getServerPrefs() {\n const accountDb = getAccountDb();\n const rows = accountDb.all('SELECT key, value FROM server_prefs') || [];\n\n return rows.reduce((prefs, row) => {\n prefs[row.key] = row.value;\n return prefs;\n }, {});\n}\n\nexport function setServerPrefs(prefs) {\n const accountDb = getAccountDb();\n\n if (!prefs) {\n return;\n }\n\n accountDb.transaction(() => {\n Object.entries(prefs).forEach(([key, value]) => {\n accountDb.mutate(\n 'INSERT INTO server_prefs (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value',\n [key, value],\n );\n });\n });\n}\n\nexport function clearExpiredSessions() {\n const clearThreshold = Math.floor(Date.now() / 1000) - 3600;\n\n const deletedSessions = getAccountDb().mutate(\n 'DELETE FROM sessions WHERE expires_at <> -1 and expires_at < ?',\n [clearThreshold],\n ).changes;\n\n console.log(`Deleted ${deletedSessions} old sessions`);\n}\n"],"mappings":"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"}
@@ -1333,9 +1333,13 @@ app$7.use(express.json());
1333
1333
  function extractPsuHeaders(req) {
1334
1334
  const ip = req.ip;
1335
1335
  const ua = typeof req.headers["user-agent"] === "string" ? req.headers["user-agent"] : void 0;
1336
- const headers = {};
1337
- if (ip) headers["Psu-Ip-Address"] = ip;
1336
+ if (!ip || isBlockedIp(ip)) {
1337
+ debug("Skipping PSU headers because PSU IP is local/private");
1338
+ return {};
1339
+ }
1340
+ const headers = { "Psu-Ip-Address": ip };
1338
1341
  if (ua) headers["Psu-User-Agent"] = ua;
1342
+ debug("Using PSU headers for public PSU IP");
1339
1343
  return headers;
1340
1344
  }
1341
1345
  async function buildSessionResult(session, psuHeaders) {
@@ -1884,7 +1888,7 @@ var prepositions = [
1884
1888
  "within",
1885
1889
  "without"
1886
1890
  ];
1887
- var lowerCaseSet = new Set([
1891
+ var lowerCaseSet = /* @__PURE__ */ new Set([
1888
1892
  ...conjunctions,
1889
1893
  ...articles,
1890
1894
  ...prepositions
@@ -5681,4 +5685,4 @@ async function run() {
5681
5685
  //#endregion
5682
5686
  export { run };
5683
5687
 
5684
- //# sourceMappingURL=app-tbcwdal9.js.map
5688
+ //# sourceMappingURL=app-GzHx-kED.js.map