From 8cf7712054a24804c7eec7b7afdf31dc5e98d67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Sun, 1 Nov 2020 19:39:01 +0100 Subject: [PATCH 01/15] Added SETTINGS_FILE environment variable / constant --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e3e4053..ef6fd95 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Set the following environment variables directly or by placing them in `.env` fi | `PORT` | Port where manager should listen for requests | `3006` | | `DEVICE_HOSTS` | Comma separated list of IPs or domain names to whitelist for CORS | `http://umbrel.local` | | `USER_FILE` | Path to the user's data file (automatically created on user registration) | `/db/user.json` | +| `SETTINGS_FILE` | Path to the user's settings file (automatically created on user registration) | `/db/settings.json` | | `SHUTDOWN_SIGNAL_FILE` | Path to write a file to signal a system shutdown | `/signals/shutdown` | | `REBOOT_SIGNAL_FILE` | Path to write a file to signal a system reboot | `/signals/reboot` | | `MIDDLEWARE_API_URL` | IP or domain where [`umbrel-middleware`](https://github.com/getumbrel/umbrel-middleware) is listening | `http://localhost` | From 4a947fc061613509c3dfe19690a6ab117d331c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Sun, 1 Nov 2020 19:41:36 +0100 Subject: [PATCH 02/15] Added settings API --- logic/auth.js | 31 ++++++++++++++++++++++++++++++- routes/v1/account.js | 18 ++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/logic/auth.js b/logic/auth.js index 53a4018..c95cf60 100644 --- a/logic/auth.js +++ b/logic/auth.js @@ -199,6 +199,32 @@ async function getInfo() { } }; +async function getSettings() { + try { + const settings = await diskLogic.readSettingsFile(); + + return settings; + } catch (error) { + throw new NodeError('Unable to get account settings'); + } +}; + +async function updateSetting(setting, value) { + try { + const settings = await diskLogic.readSettingsFile(); + + if(setting && value) { + settings[setting] = value; + } + + await diskLogic.writeSettingsFile(settings); + + return settings; + } catch (error) { + throw new NodeError(`Unable to update ${setting || 'setting'}`); + } +}; + async function seed(user) { //Decrypt mnemonic seed @@ -228,9 +254,10 @@ async function register(user, seed) { throw new NodeError('Unable to encrypt mnemonic seed'); } - //save user + //save user and init settings try { await diskLogic.writeUserFile({ name: user.name, password: user.password, seed: encryptedSeed }); + await diskLogic.writeSettingsFile({}); } catch (error) { throw new NodeError('Unable to register user'); } @@ -289,6 +316,8 @@ module.exports = { hashCredentials, isRegistered, getInfo, + getSettings, + updateSetting, seed, login, register, diff --git a/routes/v1/account.js b/routes/v1/account.js index 00f2795..080ba54 100644 --- a/routes/v1/account.js +++ b/routes/v1/account.js @@ -105,6 +105,24 @@ router.get('/info', auth.jwt, safeHandler(async (req, res) => { return res.status(constants.STATUS_CODES.OK).json(info); })); +router.get('/settings', auth.jwt, safeHandler(async (req, res) => { + const settings = await authLogic.getSettings(); + + return res.status(constants.STATUS_CODES.OK).json(settings); +})); + +router.post('/settings/update', auth.convertReqBodyToBasicAuth, auth.basic, incorrectPasswordAuthHandler, safeHandler(async (req, res) => { + const { setting, value } = req.body; + + try { + const settings = await authLogic.updateSetting(setting, value); + + return res.status(constants.STATUS_CODES.OK).json(settings); + } catch (error) { + return next(error); + } +})); + router.post('/seed', auth.convertReqBodyToBasicAuth, auth.basic, incorrectPasswordAuthHandler, safeHandler(async (req, res) => { const seed = await authLogic.seed(req.user); From 7485aa066d735df9864d169df0d09bd67ef70628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Sun, 1 Nov 2020 19:55:56 +0100 Subject: [PATCH 03/15] Changed auth --- routes/v1/account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/v1/account.js b/routes/v1/account.js index 080ba54..ea2debe 100644 --- a/routes/v1/account.js +++ b/routes/v1/account.js @@ -111,7 +111,7 @@ router.get('/settings', auth.jwt, safeHandler(async (req, res) => { return res.status(constants.STATUS_CODES.OK).json(settings); })); -router.post('/settings/update', auth.convertReqBodyToBasicAuth, auth.basic, incorrectPasswordAuthHandler, safeHandler(async (req, res) => { +router.post('/settings/update', auth.jwt, safeHandler(async (req, res) => { const { setting, value } = req.body; try { From 2c0dbe2cfe4fb0195879f112b97dcfe111474298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Sun, 1 Nov 2020 20:23:16 +0100 Subject: [PATCH 04/15] Create settings file if it does not exist --- logic/auth.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/logic/auth.js b/logic/auth.js index c95cf60..2b31687 100644 --- a/logic/auth.js +++ b/logic/auth.js @@ -205,6 +205,10 @@ async function getSettings() { return settings; } catch (error) { + if(error.code === 'ENOENT') { + await diskLogic.writeSettingsFile({}); + return {}; + } throw new NodeError('Unable to get account settings'); } }; From 03ad78ce1e4d92a39e7deee1ffe024348fca48d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Mon, 2 Nov 2020 20:46:54 +0100 Subject: [PATCH 05/15] Added default settings file path in constants --- README.md | 1 + utils/const.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index ef6fd95..7d36b71 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Set the following environment variables directly or by placing them in `.env` fi | `DEVICE_HOSTS` | Comma separated list of IPs or domain names to whitelist for CORS | `http://umbrel.local` | | `USER_FILE` | Path to the user's data file (automatically created on user registration) | `/db/user.json` | | `SETTINGS_FILE` | Path to the user's settings file (automatically created on user registration) | `/db/settings.json` | +| `DEFAULT_SETTINGS_FILE` | Path to the default settings file | `/templates/db-settings-sample.json` | | `SHUTDOWN_SIGNAL_FILE` | Path to write a file to signal a system shutdown | `/signals/shutdown` | | `REBOOT_SIGNAL_FILE` | Path to write a file to signal a system reboot | `/signals/reboot` | | `MIDDLEWARE_API_URL` | IP or domain where [`umbrel-middleware`](https://github.com/getumbrel/umbrel-middleware) is listening | `http://localhost` | diff --git a/utils/const.js b/utils/const.js index 0fdfe21..a679aa8 100644 --- a/utils/const.js +++ b/utils/const.js @@ -8,6 +8,8 @@ module.exports = { STATUS_DIR: process.env.STATUS_DIR || '/statuses', APPS_DIR: process.env.APPS_DIR || '/apps', TOR_HIDDEN_SERVICE_DIR: process.env.TOR_HIDDEN_SERVICE_DIR || '/var/lib/tor', + SETTINGS_FILE: process.env.SETTINGS_FILE || '/db/settings.json', + DEFAULT_SETTINGS_FILE: process.env.DEFAULT_SETTINGS_FILE || '/templates/db-settings-sample.json', SHUTDOWN_SIGNAL_FILE: process.env.SHUTDOWN_SIGNAL_FILE || '/signals/shutdown', REBOOT_SIGNAL_FILE: process.env.REBOOT_SIGNAL_FILE || '/signals/reboot', JWT_PUBLIC_KEY_FILE: process.env.JWT_PUBLIC_KEY_FILE || '/db/jwt-public-key/jwt.pem', From b1caedaebf63c08839a64366fc99319d0452881f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Mon, 2 Nov 2020 20:49:40 +0100 Subject: [PATCH 06/15] Added default settings reading + overwriting if custom exist --- logic/auth.js | 10 ++++++++-- logic/disk.js | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/logic/auth.js b/logic/auth.js index 2b31687..74b49fc 100644 --- a/logic/auth.js +++ b/logic/auth.js @@ -201,14 +201,20 @@ async function getInfo() { async function getSettings() { try { + const defaultSettings = await diskLogic.readDefaultSettingsFile(); const settings = await diskLogic.readSettingsFile(); - return settings; + return { ...defaultSettings, ...settings }; } catch (error) { + try { if(error.code === 'ENOENT') { + const defaultSettings = await diskLogic.readDefaultSettingsFile(); await diskLogic.writeSettingsFile({}); - return {}; + + return defaultSettings; } + } catch { }; + throw new NodeError('Unable to get account settings'); } }; diff --git a/logic/disk.js b/logic/disk.js index 2d17fda..3456000 100644 --- a/logic/disk.js +++ b/logic/disk.js @@ -61,6 +61,10 @@ async function readUserFile() { return {...defaultProperties, ...userFile}; } +function readDefaultSettingsFile() { + return diskService.readJsonFile(constants.DEFAULT_SETTINGS_FILE); +} + function readSettingsFile() { return diskService.readJsonFile(constants.SETTINGS_FILE); } @@ -256,6 +260,7 @@ module.exports = { fileExists, getBuildDetails, listVersionsForApp, + readDefaultSettingsFile, readSettingsFile, readUserFile, writeAppVersionFile, From ae1609a611511b13b49b339dc6d6291b3c978a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Mon, 2 Nov 2020 20:50:28 +0100 Subject: [PATCH 07/15] Delete custom setting when updating with empty value --- logic/auth.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/logic/auth.js b/logic/auth.js index 74b49fc..7f2a109 100644 --- a/logic/auth.js +++ b/logic/auth.js @@ -207,12 +207,12 @@ async function getSettings() { return { ...defaultSettings, ...settings }; } catch (error) { try { - if(error.code === 'ENOENT') { + if(error.code === 'ENOENT') { const defaultSettings = await diskLogic.readDefaultSettingsFile(); - await diskLogic.writeSettingsFile({}); + await diskLogic.writeSettingsFile({}); return defaultSettings; - } + } } catch { }; throw new NodeError('Unable to get account settings'); @@ -224,7 +224,8 @@ async function updateSetting(setting, value) { const settings = await diskLogic.readSettingsFile(); if(setting && value) { - settings[setting] = value; + if(value == '') delete settings[setting]; + else settings[setting] = value; } await diskLogic.writeSettingsFile(settings); From 653e65e713a81e5eba1611110e522b30b11212f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Fri, 6 Nov 2020 08:36:00 +0100 Subject: [PATCH 08/15] Update POST .../settings/update route to .../settings --- routes/v1/account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/v1/account.js b/routes/v1/account.js index ea2debe..fe9949d 100644 --- a/routes/v1/account.js +++ b/routes/v1/account.js @@ -111,7 +111,7 @@ router.get('/settings', auth.jwt, safeHandler(async (req, res) => { return res.status(constants.STATUS_CODES.OK).json(settings); })); -router.post('/settings/update', auth.jwt, safeHandler(async (req, res) => { +router.post('/settings', auth.jwt, safeHandler(async (req, res) => { const { setting, value } = req.body; try { From 6120e6dcea36f83fed8395bbd65fa0da272f210e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Fri, 6 Nov 2020 08:37:40 +0100 Subject: [PATCH 09/15] Use nested object for settings instead of a file --- logic/auth.js | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/logic/auth.js b/logic/auth.js index 7f2a109..5a08cc1 100644 --- a/logic/auth.js +++ b/logic/auth.js @@ -202,35 +202,26 @@ async function getInfo() { async function getSettings() { try { const defaultSettings = await diskLogic.readDefaultSettingsFile(); - const settings = await diskLogic.readSettingsFile(); + const { settings } = await diskLogic.readUserFile(); return { ...defaultSettings, ...settings }; } catch (error) { - try { - if(error.code === 'ENOENT') { - const defaultSettings = await diskLogic.readDefaultSettingsFile(); - await diskLogic.writeSettingsFile({}); - - return defaultSettings; - } - } catch { }; - throw new NodeError('Unable to get account settings'); } }; async function updateSetting(setting, value) { try { - const settings = await diskLogic.readSettingsFile(); + const user = await diskLogic.readUserFile(); if(setting && value) { - if(value == '') delete settings[setting]; - else settings[setting] = value; + if(value == '') delete user.settings[setting]; + else user.settings[setting] = value; } - await diskLogic.writeSettingsFile(settings); + await diskLogic.writeUserFile(user); - return settings; + return user.settings; } catch (error) { throw new NodeError(`Unable to update ${setting || 'setting'}`); } @@ -267,8 +258,7 @@ async function register(user, seed) { //save user and init settings try { - await diskLogic.writeUserFile({ name: user.name, password: user.password, seed: encryptedSeed }); - await diskLogic.writeSettingsFile({}); + await diskLogic.writeUserFile({ name: user.name, password: user.password, seed: encryptedSeed, settings: {} }); } catch (error) { throw new NodeError('Unable to register user'); } From 0212cd6fab17cef8ca3d42df49367d0c4ece03b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Fri, 6 Nov 2020 08:52:48 +0100 Subject: [PATCH 10/15] Add settings object to user if it doesn't exist --- logic/auth.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/logic/auth.js b/logic/auth.js index 5a08cc1..2be6aa2 100644 --- a/logic/auth.js +++ b/logic/auth.js @@ -213,12 +213,14 @@ async function getSettings() { async function updateSetting(setting, value) { try { const user = await diskLogic.readUserFile(); + const { settings = {} } = user; - if(setting && value) { - if(value == '') delete user.settings[setting]; - else user.settings[setting] = value; + if(setting) { + if(value) settings[setting] = value; + else delete settings[setting]; } + user.settings = settings; await diskLogic.writeUserFile(user); return user.settings; From 39810ad692ab332b00bbe519b2437a1f3bec4a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Fri, 6 Nov 2020 10:29:07 +0100 Subject: [PATCH 11/15] Added default settings file + require it once --- logic/auth.js | 3 ++- resources/db-default-settings.json | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 resources/db-default-settings.json diff --git a/logic/auth.js b/logic/auth.js index 2be6aa2..56929a2 100644 --- a/logic/auth.js +++ b/logic/auth.js @@ -12,6 +12,8 @@ const JWTHelper = require('utils/jwt.js'); const constants = require('utils/const.js'); const UUID = require('utils/UUID.js'); +const defaultSettings = require('resources/db-default-settings.json'); + const saltRounds = 10; const SYSTEM_USER = UUID.fetchBootUUID() || 'admin'; @@ -201,7 +203,6 @@ async function getInfo() { async function getSettings() { try { - const defaultSettings = await diskLogic.readDefaultSettingsFile(); const { settings } = await diskLogic.readUserFile(); return { ...defaultSettings, ...settings }; diff --git a/resources/db-default-settings.json b/resources/db-default-settings.json new file mode 100644 index 0000000..9297c11 --- /dev/null +++ b/resources/db-default-settings.json @@ -0,0 +1,3 @@ +{ + "currency": "USD" +} \ No newline at end of file From e6fd9b6bb773494c635958280edc3111bafc2a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Fri, 6 Nov 2020 10:30:56 +0100 Subject: [PATCH 12/15] Removed no longer used environment variables in README --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7d36b71..e3e4053 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,6 @@ Set the following environment variables directly or by placing them in `.env` fi | `PORT` | Port where manager should listen for requests | `3006` | | `DEVICE_HOSTS` | Comma separated list of IPs or domain names to whitelist for CORS | `http://umbrel.local` | | `USER_FILE` | Path to the user's data file (automatically created on user registration) | `/db/user.json` | -| `SETTINGS_FILE` | Path to the user's settings file (automatically created on user registration) | `/db/settings.json` | -| `DEFAULT_SETTINGS_FILE` | Path to the default settings file | `/templates/db-settings-sample.json` | | `SHUTDOWN_SIGNAL_FILE` | Path to write a file to signal a system shutdown | `/signals/shutdown` | | `REBOOT_SIGNAL_FILE` | Path to write a file to signal a system reboot | `/signals/reboot` | | `MIDDLEWARE_API_URL` | IP or domain where [`umbrel-middleware`](https://github.com/getumbrel/umbrel-middleware) is listening | `http://localhost` | From e6823a8dc255e909ffce2384223004cea6aa492e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Fri, 6 Nov 2020 13:05:42 +0100 Subject: [PATCH 13/15] Deleted no longer required constants and diskLogic functions --- logic/disk.js | 12 ------------ utils/const.js | 2 -- 2 files changed, 14 deletions(-) diff --git a/logic/disk.js b/logic/disk.js index 3456000..4b2f82b 100644 --- a/logic/disk.js +++ b/logic/disk.js @@ -61,18 +61,6 @@ async function readUserFile() { return {...defaultProperties, ...userFile}; } -function readDefaultSettingsFile() { - return diskService.readJsonFile(constants.DEFAULT_SETTINGS_FILE); -} - -function readSettingsFile() { - return diskService.readJsonFile(constants.SETTINGS_FILE); -} - -function writeSettingsFile(json) { - return diskService.writeJsonFile(constants.SETTINGS_FILE, json); -} - async function writeUserFile(json) { return diskService.writeJsonFile(constants.USER_FILE, json); } diff --git a/utils/const.js b/utils/const.js index a679aa8..0fdfe21 100644 --- a/utils/const.js +++ b/utils/const.js @@ -8,8 +8,6 @@ module.exports = { STATUS_DIR: process.env.STATUS_DIR || '/statuses', APPS_DIR: process.env.APPS_DIR || '/apps', TOR_HIDDEN_SERVICE_DIR: process.env.TOR_HIDDEN_SERVICE_DIR || '/var/lib/tor', - SETTINGS_FILE: process.env.SETTINGS_FILE || '/db/settings.json', - DEFAULT_SETTINGS_FILE: process.env.DEFAULT_SETTINGS_FILE || '/templates/db-settings-sample.json', SHUTDOWN_SIGNAL_FILE: process.env.SHUTDOWN_SIGNAL_FILE || '/signals/shutdown', REBOOT_SIGNAL_FILE: process.env.REBOOT_SIGNAL_FILE || '/signals/reboot', JWT_PUBLIC_KEY_FILE: process.env.JWT_PUBLIC_KEY_FILE || '/db/jwt-public-key/jwt.pem', From e9648fcfaa1a50e216387c204468633d1bc1153f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Fri, 6 Nov 2020 13:09:33 +0100 Subject: [PATCH 14/15] Forgot to delete functions exports --- logic/disk.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/logic/disk.js b/logic/disk.js index 4b2f82b..42305ac 100644 --- a/logic/disk.js +++ b/logic/disk.js @@ -248,11 +248,8 @@ module.exports = { fileExists, getBuildDetails, listVersionsForApp, - readDefaultSettingsFile, - readSettingsFile, readUserFile, writeAppVersionFile, - writeSettingsFile, writeUserFile, writeUmbrelSeedFile, umbrelSeedFileExists, From d78d765dd1bc2d4f5382f5bf64669f2259c155b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?= Date: Sat, 7 Nov 2020 11:56:33 +0100 Subject: [PATCH 15/15] Apply review suggestions --- logic/auth.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/logic/auth.js b/logic/auth.js index 56929a2..4faac16 100644 --- a/logic/auth.js +++ b/logic/auth.js @@ -213,20 +213,23 @@ async function getSettings() { async function updateSetting(setting, value) { try { - const user = await diskLogic.readUserFile(); - const { settings = {} } = user; - - if(setting) { - if(value) settings[setting] = value; - else delete settings[setting]; + const settings = await getSettings(); + + if(!setting) return settings; + + if(typeof value !== undefined) { + settings[setting] = value; + } else { + delete settings[setting]; } - + + const user = await diskLogic.readUserFile(); user.settings = settings; await diskLogic.writeUserFile(user); - return user.settings; + return settings; } catch (error) { - throw new NodeError(`Unable to update ${setting || 'setting'}`); + throw new NodeError(`Unable to update setting`); } };