@actual-app/sync-server 26.7.0 → 26.8.0-nightly.20260704
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 +2 -2
- package/build/bin/actual-server.js +1 -1
- package/build/bin/actual-server.js.map +1 -1
- package/build/chunks/{1694360479680-create-account-db-CoSya_e5.js → 1694360479680-create-account-db-PwKHxKju.js} +2 -2
- package/build/chunks/{1694360479680-create-account-db-CoSya_e5.js.map → 1694360479680-create-account-db-PwKHxKju.js.map} +1 -1
- package/build/chunks/{1694362247011-create-secret-table-CQhb5jt5.js → 1694362247011-create-secret-table-CTg8s4SC.js} +2 -2
- package/build/chunks/{1694362247011-create-secret-table-CQhb5jt5.js.map → 1694362247011-create-secret-table-CTg8s4SC.js.map} +1 -1
- package/build/chunks/{1702667624000-rename-nordigen-secrets-IRFLks8C.js → 1702667624000-rename-nordigen-secrets-BFEve46p.js} +2 -2
- package/build/chunks/{1702667624000-rename-nordigen-secrets-IRFLks8C.js.map → 1702667624000-rename-nordigen-secrets-BFEve46p.js.map} +1 -1
- package/build/chunks/{1718889148000-openid-Gp-HJB96.js → 1718889148000-openid-9qjigJ2O.js} +2 -2
- package/build/chunks/{1718889148000-openid-Gp-HJB96.js.map → 1718889148000-openid-9qjigJ2O.js.map} +1 -1
- package/build/chunks/{1719409568000-multiuser-CU5kHE-Z.js → 1719409568000-multiuser-SFJ94reU.js} +2 -2
- package/build/chunks/{1719409568000-multiuser-CU5kHE-Z.js.map → 1719409568000-multiuser-SFJ94reU.js.map} +1 -1
- package/build/chunks/{1763873568237-server-global-prefs-Bb7sFLeD.js → 1763873568237-server-global-prefs-CZx8n-l2.js} +2 -2
- package/build/chunks/{1763873568237-server-global-prefs-Bb7sFLeD.js.map → 1763873568237-server-global-prefs-CZx8n-l2.js.map} +1 -1
- package/build/chunks/{1763873600000-backfill-files-owner-DuVG9XcJ.js → 1763873600000-backfill-files-owner-B8HCP881.js} +2 -2
- package/build/chunks/{1763873600000-backfill-files-owner-DuVG9XcJ.js.map → 1763873600000-backfill-files-owner-B8HCP881.js.map} +1 -1
- package/build/chunks/{account-db-HkRNud0e.js → account-db-B0u6vCZk.js} +51 -24
- package/build/chunks/account-db-B0u6vCZk.js.map +1 -0
- package/build/chunks/{app-CLRlkXbY.js → app-DMyQvS9q.js} +170 -51
- package/build/chunks/app-DMyQvS9q.js.map +1 -0
- package/build/chunks/{migrations-Cneard4X.js → migrations-DOEWw8Fg.js} +8 -8
- package/build/chunks/{migrations-Cneard4X.js.map → migrations-DOEWw8Fg.js.map} +1 -1
- package/build/scripts/disable-openid.js +1 -1
- package/build/scripts/enable-openid.js +1 -1
- package/build/scripts/reset-password.js +2 -2
- package/build/scripts/reset-password.js.map +1 -1
- package/build/scripts/run-migrations.js +1 -1
- package/package.json +3 -2
- package/build/chunks/account-db-HkRNud0e.js.map +0 -1
- package/build/chunks/app-CLRlkXbY.js.map +0 -1
package/build/app.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as run } from "./chunks/migrations-
|
|
1
|
+
import { t as run } from "./chunks/migrations-DOEWw8Fg.js";
|
|
2
2
|
//#region app.ts
|
|
3
3
|
run().then(() => {
|
|
4
|
-
import("./chunks/app-
|
|
4
|
+
import("./chunks/app-DMyQvS9q.js").then((app) => app.run());
|
|
5
5
|
}).catch((err) => {
|
|
6
6
|
console.log("Error starting app:", err);
|
|
7
7
|
process.exit(1);
|
|
@@ -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,
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { i as getAccountDb } from "./account-db-
|
|
1
|
+
import { i as getAccountDb } from "./account-db-B0u6vCZk.js";
|
|
2
2
|
//#region migrations/1694360479680-create-account-db.js
|
|
3
3
|
var up = async function() {
|
|
4
4
|
await getAccountDb().exec(`
|
|
@@ -30,4 +30,4 @@ var down = async function() {
|
|
|
30
30
|
//#endregion
|
|
31
31
|
export { down, up };
|
|
32
32
|
|
|
33
|
-
//# sourceMappingURL=1694360479680-create-account-db-
|
|
33
|
+
//# sourceMappingURL=1694360479680-create-account-db-PwKHxKju.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1694360479680-create-account-db-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { i as getAccountDb } from "./account-db-
|
|
1
|
+
import { i as getAccountDb } from "./account-db-B0u6vCZk.js";
|
|
2
2
|
//#region migrations/1694362247011-create-secret-table.js
|
|
3
3
|
var up = async function() {
|
|
4
4
|
await getAccountDb().exec(`
|
|
@@ -16,4 +16,4 @@ var down = async function() {
|
|
|
16
16
|
//#endregion
|
|
17
17
|
export { down, up };
|
|
18
18
|
|
|
19
|
-
//# sourceMappingURL=1694362247011-create-secret-table-
|
|
19
|
+
//# sourceMappingURL=1694362247011-create-secret-table-CTg8s4SC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1694362247011-create-secret-table-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { i as getAccountDb } from "./account-db-
|
|
1
|
+
import { i as getAccountDb } from "./account-db-B0u6vCZk.js";
|
|
2
2
|
//#region migrations/1702667624000-rename-nordigen-secrets.js
|
|
3
3
|
var up = async function() {
|
|
4
4
|
await getAccountDb().exec(`UPDATE secrets SET name = 'gocardless_secretId' WHERE name = 'nordigen_secretId'`);
|
|
@@ -11,4 +11,4 @@ var down = async function() {
|
|
|
11
11
|
//#endregion
|
|
12
12
|
export { down, up };
|
|
13
13
|
|
|
14
|
-
//# sourceMappingURL=1702667624000-rename-nordigen-secrets-
|
|
14
|
+
//# sourceMappingURL=1702667624000-rename-nordigen-secrets-BFEve46p.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1702667624000-rename-nordigen-secrets-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { i as getAccountDb } from "./account-db-
|
|
1
|
+
import { i as getAccountDb } from "./account-db-B0u6vCZk.js";
|
|
2
2
|
//#region migrations/1718889148000-openid.js
|
|
3
3
|
var up = async function() {
|
|
4
4
|
await getAccountDb().exec(`
|
|
@@ -37,4 +37,4 @@ var down = async function() {
|
|
|
37
37
|
//#endregion
|
|
38
38
|
export { down, up };
|
|
39
39
|
|
|
40
|
-
//# sourceMappingURL=1718889148000-openid-
|
|
40
|
+
//# sourceMappingURL=1718889148000-openid-9qjigJ2O.js.map
|
package/build/chunks/{1718889148000-openid-Gp-HJB96.js.map → 1718889148000-openid-9qjigJ2O.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1718889148000-openid-
|
|
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"}
|
package/build/chunks/{1719409568000-multiuser-CU5kHE-Z.js → 1719409568000-multiuser-SFJ94reU.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as getAccountDb } from "./account-db-
|
|
1
|
+
import { i as getAccountDb } from "./account-db-B0u6vCZk.js";
|
|
2
2
|
import { v4 } from "uuid";
|
|
3
3
|
//#region migrations/1719409568000-multiuser.js
|
|
4
4
|
var up = async function() {
|
|
@@ -111,4 +111,4 @@ var down = async function() {
|
|
|
111
111
|
//#endregion
|
|
112
112
|
export { down, up };
|
|
113
113
|
|
|
114
|
-
//# sourceMappingURL=1719409568000-multiuser-
|
|
114
|
+
//# sourceMappingURL=1719409568000-multiuser-SFJ94reU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1719409568000-multiuser-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { i as getAccountDb } from "./account-db-
|
|
1
|
+
import { i as getAccountDb } from "./account-db-B0u6vCZk.js";
|
|
2
2
|
//#region migrations/1763873568237-server-global-prefs.js
|
|
3
3
|
var up = async function() {
|
|
4
4
|
getAccountDb().exec(`
|
|
@@ -15,4 +15,4 @@ var down = async function() {
|
|
|
15
15
|
//#endregion
|
|
16
16
|
export { down, up };
|
|
17
17
|
|
|
18
|
-
//# sourceMappingURL=1763873568237-server-global-prefs-
|
|
18
|
+
//# sourceMappingURL=1763873568237-server-global-prefs-CZx8n-l2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1763873568237-server-global-prefs-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import { i as getAccountDb } from "./account-db-
|
|
1
|
+
import { i as getAccountDb } from "./account-db-B0u6vCZk.js";
|
|
2
2
|
//#region migrations/1763873600000-backfill-files-owner.js
|
|
3
3
|
var up = async function() {
|
|
4
4
|
const accountDb = getAccountDb();
|
|
@@ -9,4 +9,4 @@ var down = async function() {};
|
|
|
9
9
|
//#endregion
|
|
10
10
|
export { down, up };
|
|
11
11
|
|
|
12
|
-
//# sourceMappingURL=1763873600000-backfill-files-owner-
|
|
12
|
+
//# sourceMappingURL=1763873600000-backfill-files-owner-B8HCP881.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"1763873600000-backfill-files-owner-
|
|
1
|
+
{"version":3,"file":"1763873600000-backfill-files-owner-B8HCP881.js","names":[],"sources":["../../migrations/1763873600000-backfill-files-owner.js"],"sourcesContent":["import { getAccountDb } from '../src/account-db';\n\nexport const up = async function () {\n const accountDb = getAccountDb();\n\n const admin = accountDb.first(\n 'SELECT id FROM users WHERE role = ? ORDER BY id LIMIT 1',\n ['ADMIN'],\n );\n if (admin) {\n accountDb.mutate('UPDATE files SET owner = ? WHERE owner IS NULL', [\n admin.id,\n ]);\n }\n};\n\nexport const down = async function () {\n // Cannot reliably restore NULL owner for backfilled rows; no-op.\n};\n"],"mappings":";;AAEA,IAAa,KAAK,iBAAkB;CAClC,MAAM,YAAY,aAAa;CAE/B,MAAM,QAAQ,UAAU,MACtB,2DACA,CAAC,OAAO,CACV;CACA,IAAI,OACF,UAAU,OAAO,kDAAkD,CACjE,MAAM,EACR,CAAC;AAEL;AAEA,IAAa,OAAO,iBAAkB,CAEtC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { t as configSchema } from "./load-config-8NvK3qSh.js";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import * as bcrypt from "bcrypt";
|
|
4
3
|
import { Issuer, custom, generators } from "openid-client";
|
|
5
4
|
import { v4 } from "uuid";
|
|
6
5
|
import ipaddr from "ipaddr.js";
|
|
6
|
+
import * as argon2 from "argon2";
|
|
7
|
+
import * as bcrypt from "bcrypt";
|
|
7
8
|
import Database from "better-sqlite3";
|
|
8
9
|
//#region src/services/user-service.ts
|
|
9
10
|
function getUserByUsername(userName) {
|
|
@@ -213,29 +214,57 @@ function validateAuthHeader(req) {
|
|
|
213
214
|
}
|
|
214
215
|
//#endregion
|
|
215
216
|
//#region src/accounts/password.js
|
|
217
|
+
var ARGON2_OPTIONS = {
|
|
218
|
+
type: argon2.argon2id,
|
|
219
|
+
memoryCost: 47104,
|
|
220
|
+
timeCost: 1,
|
|
221
|
+
parallelism: 1
|
|
222
|
+
};
|
|
216
223
|
function isValidPassword(password) {
|
|
217
224
|
return password != null && password !== "";
|
|
218
225
|
}
|
|
219
226
|
function hashPassword(password) {
|
|
220
|
-
return
|
|
227
|
+
return argon2.hash(password, ARGON2_OPTIONS);
|
|
221
228
|
}
|
|
222
|
-
function
|
|
223
|
-
if (
|
|
224
|
-
|
|
229
|
+
async function verifyPassword(password, hash) {
|
|
230
|
+
if (typeof hash !== "string") return false;
|
|
231
|
+
if (hash.startsWith("$argon2")) try {
|
|
232
|
+
return await argon2.verify(hash, password);
|
|
233
|
+
} catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
return await bcrypt.compare(password, hash);
|
|
238
|
+
} catch {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function isLegacyHash(hash) {
|
|
243
|
+
return typeof hash === "string" && !hash.startsWith("$argon2");
|
|
244
|
+
}
|
|
245
|
+
function setPasswordHash(hashed) {
|
|
225
246
|
const accountDb = getAccountDb();
|
|
226
247
|
accountDb.transaction(() => {
|
|
227
248
|
accountDb.mutate("DELETE FROM auth WHERE method = ?", ["password"]);
|
|
228
249
|
accountDb.mutate("UPDATE auth SET active = 0");
|
|
229
250
|
accountDb.mutate("INSERT INTO auth (method, display_name, extra_data, active) VALUES ('password', 'Password', ?, 1)", [hashed]);
|
|
230
251
|
});
|
|
252
|
+
}
|
|
253
|
+
async function bootstrapPassword(password) {
|
|
254
|
+
if (!isValidPassword(password)) return { error: "invalid-password" };
|
|
255
|
+
setPasswordHash(await hashPassword(password));
|
|
231
256
|
return {};
|
|
232
257
|
}
|
|
233
|
-
function loginWithPassword(password) {
|
|
258
|
+
async function loginWithPassword(password) {
|
|
234
259
|
if (!isValidPassword(password)) return { error: "invalid-password" };
|
|
235
260
|
const accountDb = getAccountDb();
|
|
236
261
|
const { extra_data: passwordHash } = accountDb.first("SELECT extra_data FROM auth WHERE method = ?", ["password"]) || {};
|
|
237
262
|
if (!passwordHash) return { error: "invalid-password" };
|
|
238
|
-
if (!
|
|
263
|
+
if (!await verifyPassword(password, passwordHash)) return { error: "invalid-password" };
|
|
264
|
+
if (isLegacyHash(passwordHash)) {
|
|
265
|
+
const rehashed = await hashPassword(password);
|
|
266
|
+
accountDb.mutate("UPDATE auth SET extra_data = ? WHERE method = 'password' AND extra_data = ?", [rehashed, passwordHash]);
|
|
267
|
+
}
|
|
239
268
|
const sessionRow = accountDb.first("SELECT * FROM sessions WHERE auth_method = ?", ["password"]);
|
|
240
269
|
const token = sessionRow ? sessionRow.token : v4();
|
|
241
270
|
const { totalOfUsers } = accountDb.first("SELECT count(*) as totalOfUsers FROM users");
|
|
@@ -269,19 +298,18 @@ function loginWithPassword(password) {
|
|
|
269
298
|
clearExpiredSessions();
|
|
270
299
|
return { token };
|
|
271
300
|
}
|
|
272
|
-
function changePassword(newPassword) {
|
|
301
|
+
async function changePassword(newPassword) {
|
|
273
302
|
const accountDb = getAccountDb();
|
|
274
303
|
if (!isValidPassword(newPassword)) return { error: "invalid-password" };
|
|
275
|
-
const hashed = hashPassword(newPassword);
|
|
304
|
+
const hashed = await hashPassword(newPassword);
|
|
276
305
|
accountDb.mutate("UPDATE auth SET extra_data = ? WHERE method = 'password'", [hashed]);
|
|
277
306
|
return {};
|
|
278
307
|
}
|
|
279
|
-
function checkPassword(password) {
|
|
308
|
+
async function checkPassword(password) {
|
|
280
309
|
if (!isValidPassword(password)) return false;
|
|
281
310
|
const { extra_data: passwordHash } = getAccountDb().first("SELECT extra_data FROM auth WHERE method = ?", ["password"]) || {};
|
|
282
311
|
if (!passwordHash) return false;
|
|
283
|
-
|
|
284
|
-
return true;
|
|
312
|
+
return await verifyPassword(password, passwordHash);
|
|
285
313
|
}
|
|
286
314
|
//#endregion
|
|
287
315
|
//#region src/accounts/openid.ts
|
|
@@ -334,7 +362,7 @@ async function loginWithOpenIdSetup(returnUrl, firstTimeLoginPassword = "") {
|
|
|
334
362
|
const { countUsersWithUserName } = accountDb.first("SELECT count(*) as countUsersWithUserName FROM users WHERE user_name <> ?", [""]);
|
|
335
363
|
if (countUsersWithUserName === 0) {
|
|
336
364
|
if (listLoginMethods().some((authMethod) => authMethod.method === "password")) {
|
|
337
|
-
if (!checkPassword(firstTimeLoginPassword)) return { error: "invalid-password" };
|
|
365
|
+
if (!await checkPassword(firstTimeLoginPassword)) return { error: "invalid-password" };
|
|
338
366
|
}
|
|
339
367
|
}
|
|
340
368
|
let config = accountDb.first("SELECT extra_data FROM auth WHERE method = ?", ["openid"]);
|
|
@@ -570,6 +598,11 @@ async function bootstrap(loginSettings, forced = false) {
|
|
|
570
598
|
const passEnabled = "password" in loginSettings;
|
|
571
599
|
const openIdEnabled = "openId" in loginSettings;
|
|
572
600
|
const accountDb = getAccountDb();
|
|
601
|
+
let passwordHash;
|
|
602
|
+
if (passEnabled) {
|
|
603
|
+
if (!isValidPassword(loginSettings.password)) return { error: "invalid-password" };
|
|
604
|
+
passwordHash = await hashPassword(loginSettings.password);
|
|
605
|
+
}
|
|
573
606
|
accountDb.mutate("BEGIN TRANSACTION");
|
|
574
607
|
try {
|
|
575
608
|
const { countOfOwner } = accountDb.first(`SELECT count(*) as countOfOwner
|
|
@@ -589,13 +622,7 @@ async function bootstrap(loginSettings, forced = false) {
|
|
|
589
622
|
accountDb.mutate("ROLLBACK");
|
|
590
623
|
return { error: "max-one-method-allowed" };
|
|
591
624
|
}
|
|
592
|
-
if (passEnabled)
|
|
593
|
-
const { error } = bootstrapPassword(loginSettings.password);
|
|
594
|
-
if (error) {
|
|
595
|
-
accountDb.mutate("ROLLBACK");
|
|
596
|
-
return { error };
|
|
597
|
-
}
|
|
598
|
-
}
|
|
625
|
+
if (passEnabled) setPasswordHash(passwordHash);
|
|
599
626
|
if (openIdEnabled && forced) {
|
|
600
627
|
const { error } = await bootstrapOpenId(loginSettings.openId);
|
|
601
628
|
if (error) {
|
|
@@ -604,7 +631,7 @@ async function bootstrap(loginSettings, forced = false) {
|
|
|
604
631
|
}
|
|
605
632
|
}
|
|
606
633
|
accountDb.mutate("COMMIT");
|
|
607
|
-
return passEnabled ? loginWithPassword(loginSettings.password) : {};
|
|
634
|
+
return passEnabled ? await loginWithPassword(loginSettings.password) : {};
|
|
608
635
|
} catch (error) {
|
|
609
636
|
accountDb.mutate("ROLLBACK");
|
|
610
637
|
throw error;
|
|
@@ -628,8 +655,8 @@ async function disableOpenID(loginSettings) {
|
|
|
628
655
|
const { extra_data: passwordHash } = accountDb.first("SELECT extra_data FROM auth WHERE method = ?", ["password"]) || {};
|
|
629
656
|
if (!passwordHash) return { error: "invalid-password" };
|
|
630
657
|
if (!loginSettings?.password) return { error: "invalid-password" };
|
|
631
|
-
if (!
|
|
632
|
-
const { error } = bootstrapPassword(loginSettings.password);
|
|
658
|
+
if (!await verifyPassword(loginSettings.password, passwordHash)) return { error: "invalid-password" };
|
|
659
|
+
const { error } = await bootstrapPassword(loginSettings.password);
|
|
633
660
|
if (error) return { error };
|
|
634
661
|
try {
|
|
635
662
|
accountDb.transaction(() => {
|
|
@@ -685,4 +712,4 @@ function clearExpiredSessions() {
|
|
|
685
712
|
//#endregion
|
|
686
713
|
export { getFileById as A, updateUserWithRole as B, checkFilePermission as C, deleteUserAccessByFileId as D, deleteUserAccess as E, getUserById as F, getUserByUsername as I, insertUser as L, getOwnerCount as M, getOwnerId as N, getAllUserAccess as O, getUserAccess as P, transferAllFilesFromUser as R, addUserAccess as S, deleteUser as T, validateRole as V, changePassword as _, getActiveLoginMethod as a, validateAuthHeader as b, getUserInfo as c, needsBootstrap as d, setServerPrefs as f, loginWithOpenIdSetup as g, loginWithOpenIdFinalize as h, getAccountDb as i, getOpenIDConfig as j, getAllUsers as k, isAdmin as l, isValidRedirectUrl as m, disableOpenID as n, getLoginMethod as o, openDatabase as p, enableOpenID as r, getServerPrefs as s, bootstrap as t, listLoginMethods as u, checkPassword as v, countUserAccess as w, validateSession as x, loginWithPassword as y, updateFileOwner as z };
|
|
687
714
|
|
|
688
|
-
//# sourceMappingURL=account-db-
|
|
715
|
+
//# sourceMappingURL=account-db-B0u6vCZk.js.map
|
|
@@ -0,0 +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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"}
|