From cdedfcfba7df13e5e52b649910023ea436c76cb5 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 9 Mar 2023 13:17:03 +0200 Subject: [PATCH 01/24] Add user flags isSyncOnStartupRequired to model --- workers/loc.api/sync/schema/models.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workers/loc.api/sync/schema/models.js b/workers/loc.api/sync/schema/models.js index 70e04b16b..7eb4d10aa 100644 --- a/workers/loc.api/sync/schema/models.js +++ b/workers/loc.api/sync/schema/models.js @@ -63,6 +63,8 @@ const _models = new Map([ isNotProtected: 'INT', isSubAccount: 'INT', isSubUser: 'INT', + shouldNotSyncOnStartupAfterUpdate: 'INT', + isSyncOnStartupRequired: 'INT', createdAt: 'BIGINT', updatedAt: 'BIGINT', From ab79efc78e92cd1d3d917c48525c7a7545129e7f Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 9 Mar 2023 13:17:36 +0200 Subject: [PATCH 02/24] Bump db version up to 33 --- workers/loc.api/sync/schema/models.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/loc.api/sync/schema/models.js b/workers/loc.api/sync/schema/models.js index 7eb4d10aa..a582409b6 100644 --- a/workers/loc.api/sync/schema/models.js +++ b/workers/loc.api/sync/schema/models.js @@ -8,7 +8,7 @@ * e.g. `migration.v1.js`, where `v1` is `SUPPORTED_DB_VERSION` */ -const SUPPORTED_DB_VERSION = 32 +const SUPPORTED_DB_VERSION = 33 const TABLES_NAMES = require('./tables-names') const { From 72442459a48e522b6e8bd15f4118f4705cab7b97 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 9 Mar 2023 13:18:45 +0200 Subject: [PATCH 03/24] Add ability to pick session isSyncOnStartupRequired prop --- .../loc.api/sync/authenticator/helpers/pick-session-props.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/workers/loc.api/sync/authenticator/helpers/pick-session-props.js b/workers/loc.api/sync/authenticator/helpers/pick-session-props.js index 30cc960aa..5f48230d6 100644 --- a/workers/loc.api/sync/authenticator/helpers/pick-session-props.js +++ b/workers/loc.api/sync/authenticator/helpers/pick-session-props.js @@ -22,6 +22,8 @@ module.exports = (session, isReturnedPassword) => { 'isSubUser', 'subUsers', 'token', + 'shouldNotSyncOnStartupAfterUpdate', + 'isSyncOnStartupRequired', ...passwordProp ] const { subUsers: reqSubUsers } = { ...session } From bf8f0720db499ce097fb6debd31b6a44533af78f Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 9 Mar 2023 13:21:19 +0200 Subject: [PATCH 04/24] Add ability to get user session for data inserter --- .../loc.api/sync/data.inserter/helpers/utils.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/workers/loc.api/sync/data.inserter/helpers/utils.js b/workers/loc.api/sync/data.inserter/helpers/utils.js index 1dc0d6c04..44b54645d 100644 --- a/workers/loc.api/sync/data.inserter/helpers/utils.js +++ b/workers/loc.api/sync/data.inserter/helpers/utils.js @@ -42,7 +42,8 @@ const getAuthFromDb = async (authenticator, opts = {}) => { ownerUserId, isOwnerScheduler } = opts ?? {} - const auth = new Map() + const sessionAuth = new Map() + const syncAuth = new Map() const sessions = shouldGetEveryone ? await authenticator.getUsers( { isSubAccount: true, isSubUser: false }, @@ -51,7 +52,7 @@ const getAuthFromDb = async (authenticator, opts = {}) => { : authenticator.getUserSessions() if (sessions.size === 0) { - return auth + return { sessionAuth, syncAuth } } for (const session of sessions) { @@ -94,10 +95,11 @@ const getAuthFromDb = async (authenticator, opts = {}) => { } if (!isSubAccount) { - auth.set( + syncAuth.set( `${email}-${username}`, authPayload ) + sessionAuth.set(_id, user) continue } @@ -111,14 +113,16 @@ const getAuthFromDb = async (authenticator, opts = {}) => { subUsers.forEach((subUser) => { const { email, username } = { ...subUser } - auth.set( + syncAuth.set( `${email}-${username}`, { ...authPayload, subUser } ) }) + + sessionAuth.set(_id, user) } - return auth + return { sessionAuth, syncAuth } } const getAllowedCollsNames = (allowedColls) => { From 5051426adb79c165b9eb713ab9a741dbdaab59dc Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 9 Mar 2023 13:24:50 +0200 Subject: [PATCH 05/24] Match ledgers balances hook with getting auth --- .../hooks/recalc.sub.account.ledgers.balances.hook.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/workers/loc.api/sync/data.inserter/hooks/recalc.sub.account.ledgers.balances.hook.js b/workers/loc.api/sync/data.inserter/hooks/recalc.sub.account.ledgers.balances.hook.js index 288caf71c..2cfab8c1a 100644 --- a/workers/loc.api/sync/data.inserter/hooks/recalc.sub.account.ledgers.balances.hook.js +++ b/workers/loc.api/sync/data.inserter/hooks/recalc.sub.account.ledgers.balances.hook.js @@ -290,10 +290,10 @@ class RecalcSubAccountLedgersBalancesHook extends DataInserterHook { const dataInserter = this.getDataInserter() const auth = dataInserter ? dataInserter.getAuth() - : await getAuthFromDb( - this.authenticator, - { shouldGetEveryone: true } - ) + : (await getAuthFromDb( + this.authenticator, + { shouldGetEveryone: true } + )).syncAuth if ( !auth || From 57e69dc1796153bb95c7114f0e76b1d7ab2c8794 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Thu, 9 Mar 2023 13:26:08 +0200 Subject: [PATCH 06/24] Set user sync state after successful sync --- workers/loc.api/sync/data.inserter/index.js | 43 ++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/workers/loc.api/sync/data.inserter/index.js b/workers/loc.api/sync/data.inserter/index.js index 2260b0c69..3d81331e4 100644 --- a/workers/loc.api/sync/data.inserter/index.js +++ b/workers/loc.api/sync/data.inserter/index.js @@ -49,6 +49,7 @@ const depsTypes = (TYPES) => [ TYPES.ApiMiddleware, TYPES.SyncSchema, TYPES.ALLOWED_COLLS, + TYPES.TABLES_NAMES, TYPES.Authenticator, TYPES.ConvertCurrencyHook, TYPES.RecalcSubAccountLedgersBalancesHook, @@ -66,6 +67,7 @@ class DataInserter extends EventEmitter { apiMiddleware, syncSchema, ALLOWED_COLLS, + TABLES_NAMES, authenticator, convertCurrencyHook, recalcSubAccountLedgersBalancesHook, @@ -83,6 +85,7 @@ class DataInserter extends EventEmitter { this.apiMiddleware = apiMiddleware this.syncSchema = syncSchema this.ALLOWED_COLLS = ALLOWED_COLLS + this.TABLES_NAMES = TABLES_NAMES this.authenticator = authenticator this.convertCurrencyHook = convertCurrencyHook this.recalcSubAccountLedgersBalancesHook = recalcSubAccountLedgersBalancesHook @@ -96,6 +99,7 @@ class DataInserter extends EventEmitter { this._asyncProgressHandlers = [] this._auth = null + this._sessionAuth = null this._allowedCollsNames = getAllowedCollsNames( this.ALLOWED_COLLS ) @@ -166,13 +170,15 @@ class DataInserter extends EventEmitter { return } - this._auth = await getAuthFromDb( + const { sessionAuth, syncAuth } = await getAuthFromDb( this.authenticator, { ownerUserId: this.ownerUserId, isOwnerScheduler: this.isOwnerScheduler } ) + this._auth = syncAuth + this._sessionAuth = sessionAuth if ( !this._auth || @@ -923,9 +929,44 @@ class DataInserter extends EventEmitter { isNotInTrans: true, doNotQueueQuery: true }) + + await this._setUserSyncState() }) } + async _setUserSyncState () { + const didNotAllCollsSync = this.syncColls + .some((collName) => collName === this.ALLOWED_COLLS.ALL) + + if ( + didNotAllCollsSync || + !(this._sessionAuth instanceof Map) || + this._sessionAuth.size === 0 + ) { + return + } + + const userIds = [...this._sessionAuth].reduce((accum, curr) => { + const { _id, isSyncOnStartupRequired } = curr ?? {} + + if (isSyncOnStartupRequired) { + accum.push(_id) + } + + return accum + }, []) + + if (userIds.length === 0) { + return + } + + await this.dao.updateCollBy( + this.TABLES_NAMES.USERS, + { $in: { _id: userIds } }, + { isSyncOnStartupRequired: false } + ) + } + _addAfterAllInsertsHooks (hook, opts) { const hookArr = Array.isArray(hook) ? hook From aa507a1c8f658badb36f2a2d306e692865a2eb7c Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Fri, 10 Mar 2023 11:44:18 +0200 Subject: [PATCH 07/24] Return isSyncOnStartupRequired flags on sign-in --- workers/loc.api/sync/authenticator/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index 5db496102..87b8b5a0b 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -264,7 +264,9 @@ class Authenticator { authToken, apiKey, apiSecret, - password + password, + shouldNotSyncOnStartupAfterUpdate, + isSyncOnStartupRequired } = user ?? {} let newAuthToken = null @@ -391,7 +393,9 @@ class Authenticator { ...returnedUser, email: emailFromApi, isSubAccount: isSubAccountFromDb, - token: createdToken + token: createdToken, + shouldNotSyncOnStartupAfterUpdate, + isSyncOnStartupRequired } } From 14c9f2285c23cd1752076d9266fb4040cb79f799 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Fri, 10 Mar 2023 11:45:10 +0200 Subject: [PATCH 08/24] Launch sync on sign-in if required --- workers/loc.api/service.report.framework.js | 32 +++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/workers/loc.api/service.report.framework.js b/workers/loc.api/service.report.framework.js index 2117386db..31a409725 100644 --- a/workers/loc.api/service.report.framework.js +++ b/workers/loc.api/service.report.framework.js @@ -90,8 +90,36 @@ class FrameworkReportService extends ReportService { } signIn (space, args, cb) { - return this._responder(() => { - return this._authenticator.signIn(args) + return this._responder(async () => { + const { + _id, + email, + isSubAccount, + token, + shouldNotSyncOnStartupAfterUpdate, + isSyncOnStartupRequired + } = await this._authenticator.signIn( + args, + { isReturnedUser: true } + ) + + if ( + shouldNotSyncOnStartupAfterUpdate && + isSyncOnStartupRequired + ) { + await this._sync.start({ + syncColls: this._ALLOWED_COLLS.ALL, + isSolveAfterRedirToApi: true, + ownerUserId: _id + }) + } + + return { + email, + isSubAccount, + token, + shouldNotSyncOnStartupAfterUpdate + } }, 'signIn', args, cb) } From 6f276f59865aebe7fe6dab0207aa8d8bdab37032 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Fri, 10 Mar 2023 13:41:35 +0200 Subject: [PATCH 09/24] Add ability to update user --- workers/loc.api/sync/authenticator/index.js | 73 ++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index 87b8b5a0b..0be9bbacc 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -1,6 +1,7 @@ 'use strict' const { v4: uuidv4 } = require('uuid') +const { pick } = require('lodash') const { AuthError } = require('bfx-report/workers/loc.api/errors') @@ -187,7 +188,9 @@ class Authenticator { isSubAccount: serializeVal(isSubAccount), isSubUser: serializeVal(isSubUser), passwordHash, - isNotProtected: serializeVal(isNotProtected) + isNotProtected: serializeVal(isNotProtected), + shouldNotSyncOnStartupAfterUpdate: 0, + isSyncOnStartupRequired: 0 }, { isNotInTrans, withWorkerThreads, doNotQueueQuery } ) @@ -941,6 +944,74 @@ class Authenticator { return true } + async updateUser (args, opts) { + const { + email, + password: userPwd, + isSubAccount, + token + } = args?.auth ?? {} + const freshUserData = pick( + args?.params, + [ + 'shouldNotSyncOnStartupAfterUpdate', + 'isSyncOnStartupRequired' + ] + ) + const { + isNotInTrans = false, + doNotQueueQuery = false, + withWorkerThreads = false + } = opts ?? {} + + const { + _id, + email: emailFromDb + } = await this.verifyUser( + { + auth: { + email, + password: userPwd, + isSubAccount, + token + } + }, + { + isNotInTrans, + withWorkerThreads, + doNotQueueQuery + } + ) + + const res = await this.dao.updateCollBy( + this.TABLES_NAMES.USERS, + { _id, email: emailFromDb }, + freshUserData, + { withWorkerThreads, doNotQueueQuery } + ) + + if (res && res.changes < 1) { + throw new AuthError() + } + + const existedToken = ( + token && + typeof token === 'string' + ) + ? token + : this.getUserSessionByEmail({ email, isSubAccount })?.token + + if ( + existedToken && + typeof existedToken === 'string' + ) { + return + } + + const session = this.userSessions.get(existedToken) + Object.assign(session, freshUserData) + } + setUserSession (user) { const { token, From 32868dcf9677821a0590573642327a31de0b2ac2 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 13 Mar 2023 14:41:53 +0200 Subject: [PATCH 10/24] Add UserUpdatingError class --- workers/loc.api/errors/index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/workers/loc.api/errors/index.js b/workers/loc.api/errors/index.js index 57d7f3d1a..f68dfc410 100644 --- a/workers/loc.api/errors/index.js +++ b/workers/loc.api/errors/index.js @@ -113,6 +113,14 @@ class SubAccountUpdatingError extends AuthError { } } +class UserUpdatingError extends AuthError { + constructor (message = 'ERR_USER_UPDATE_HAS_BEEN_FAILED') { + super(message) + + this.statusMessage = 'User update has been failed' + } +} + class UserRemovingError extends AuthError { constructor (message = 'ERR_USER_REMOVE_HAS_BEEN_FAILED') { super(message) @@ -235,6 +243,7 @@ module.exports = { MigrationLaunchingError, SubAccountCreatingError, SubAccountUpdatingError, + UserUpdatingError, UserRemovingError, UserWasPreviouslyStoredInDbError, SubAccountLedgersBalancesRecalcError, From d58c871f95b705b230bcf7b8d4d9db5dd1520cb8 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 13 Mar 2023 14:42:19 +0200 Subject: [PATCH 11/24] Improve updateUser method --- workers/loc.api/sync/authenticator/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index 0be9bbacc..6d367cd07 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -14,6 +14,7 @@ const { isSubAccountApiKeys } = require('../../helpers') const { + UserUpdatingError, UserRemovingError, UserWasPreviouslyStoredInDbError, AuthTokenGenerationError @@ -991,7 +992,7 @@ class Authenticator { ) if (res && res.changes < 1) { - throw new AuthError() + throw new UserUpdatingError() } const existedToken = ( @@ -1005,11 +1006,13 @@ class Authenticator { existedToken && typeof existedToken === 'string' ) { - return + return true } const session = this.userSessions.get(existedToken) Object.assign(session, freshUserData) + + return true } setUserSession (user) { From 79194b9d1f9a96eec3f475dc4c0f1130706cee88 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 13 Mar 2023 14:43:25 +0200 Subject: [PATCH 12/24] Add updateUser endpoint --- workers/loc.api/service.report.framework.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/workers/loc.api/service.report.framework.js b/workers/loc.api/service.report.framework.js index 31a409725..186a98b5f 100644 --- a/workers/loc.api/service.report.framework.js +++ b/workers/loc.api/service.report.framework.js @@ -189,6 +189,12 @@ class FrameworkReportService extends ReportService { }, 'removeUser', args, cb) } + updateUser (space, args, cb) { + return this._responder(() => { + return this._authenticator.updateUser(args) + }, 'updateUser', args, cb) + } + createSubAccount (space, args, cb) { return this._responder(() => { checkParams(args, 'paramsSchemaForCreateSubAccount') From cfaeae82d0d38a71a97f658b328549ad29ead136 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 14 Mar 2023 13:18:04 +0200 Subject: [PATCH 13/24] Fix setting user sync state after syncing --- workers/loc.api/sync/data.inserter/index.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/workers/loc.api/sync/data.inserter/index.js b/workers/loc.api/sync/data.inserter/index.js index 3d81331e4..44e974700 100644 --- a/workers/loc.api/sync/data.inserter/index.js +++ b/workers/loc.api/sync/data.inserter/index.js @@ -930,13 +930,13 @@ class DataInserter extends EventEmitter { doNotQueueQuery: true }) - await this._setUserSyncState() + await this._setUserSyncState({ doNotQueueQuery: true }) }) } - async _setUserSyncState () { + async _setUserSyncState (opts) { const didNotAllCollsSync = this.syncColls - .some((collName) => collName === this.ALLOWED_COLLS.ALL) + .every((collName) => collName !== this.ALLOWED_COLLS.ALL) if ( didNotAllCollsSync || @@ -946,8 +946,8 @@ class DataInserter extends EventEmitter { return } - const userIds = [...this._sessionAuth].reduce((accum, curr) => { - const { _id, isSyncOnStartupRequired } = curr ?? {} + const userIds = [...this._sessionAuth].reduce((accum, [key, val]) => { + const { _id, isSyncOnStartupRequired } = val ?? {} if (isSyncOnStartupRequired) { accum.push(_id) @@ -963,7 +963,8 @@ class DataInserter extends EventEmitter { await this.dao.updateCollBy( this.TABLES_NAMES.USERS, { $in: { _id: userIds } }, - { isSyncOnStartupRequired: false } + { isSyncOnStartupRequired: 0 }, + opts ) } From a7e5505361d7e40848b54e1d40b5c60ba5aa1704 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 14 Mar 2023 13:19:52 +0200 Subject: [PATCH 14/24] Add ability to update users sync on startup required state --- workers/loc.api/sync/dao/dao.better.sqlite.js | 24 +++++++++++++++++++ workers/loc.api/sync/dao/dao.js | 5 ++++ 2 files changed, 29 insertions(+) diff --git a/workers/loc.api/sync/dao/dao.better.sqlite.js b/workers/loc.api/sync/dao/dao.better.sqlite.js index 7cc4e3649..621761eec 100644 --- a/workers/loc.api/sync/dao/dao.better.sqlite.js +++ b/workers/loc.api/sync/dao/dao.better.sqlite.js @@ -953,6 +953,30 @@ class BetterSqliteDAO extends DAO { }, { withWorkerThreads: true }) } + /** + * @override + */ + async updateUsersSyncOnStartupRequiredState (opts) { + const { + doNotQueueQuery = false, + withWorkerThreads = false + } = opts ?? {} + + await this.updateCollBy( + this.TABLES_NAMES.USERS, + { + $or: { + $isNull: 'shouldNotSyncOnStartupAfterUpdate', + $eq: { shouldNotSyncOnStartupAfterUpdate: 0 } + } + }, + { isSyncOnStartupRequired: 1 }, + { withWorkerThreads, doNotQueueQuery } + ) + + return true + } + /** * @override */ diff --git a/workers/loc.api/sync/dao/dao.js b/workers/loc.api/sync/dao/dao.js index b360e5060..78f7edc4d 100644 --- a/workers/loc.api/sync/dao/dao.js +++ b/workers/loc.api/sync/dao/dao.js @@ -135,6 +135,11 @@ class DAO { */ async getUsers () { throw new ImplementationError() } + /** + * @abstract + */ + async updateUsersSyncOnStartupRequiredState () { throw new ImplementationError() } + /** * @abstract */ From df8901e378e8e06752a1b999b0016ad4db900fbf Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 14 Mar 2023 13:22:35 +0200 Subject: [PATCH 15/24] Fix launching sync after sign-in if required --- workers/loc.api/service.report.framework.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/loc.api/service.report.framework.js b/workers/loc.api/service.report.framework.js index 186a98b5f..71d5b66b8 100644 --- a/workers/loc.api/service.report.framework.js +++ b/workers/loc.api/service.report.framework.js @@ -104,7 +104,7 @@ class FrameworkReportService extends ReportService { ) if ( - shouldNotSyncOnStartupAfterUpdate && + !shouldNotSyncOnStartupAfterUpdate && isSyncOnStartupRequired ) { await this._sync.start({ From 7cc09e97689a254a0fec5b7e5cd83991f1a3ada6 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 14 Mar 2023 13:24:38 +0200 Subject: [PATCH 16/24] Request sync after db migration --- workers/loc.api/sync/dao/db-migrations/db.migrator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/workers/loc.api/sync/dao/db-migrations/db.migrator.js b/workers/loc.api/sync/dao/db-migrations/db.migrator.js index 265bf0462..c59ff0d75 100644 --- a/workers/loc.api/sync/dao/db-migrations/db.migrator.js +++ b/workers/loc.api/sync/dao/db-migrations/db.migrator.js @@ -135,6 +135,7 @@ class DbMigrator { await this.dbBackupManager.backupDb({ currVer, supportedVer }) await this.migrate(versions, isDown) + await this.dao.updateUsersSyncOnStartupRequiredState() } } From a11d070fec1ffc0ef363191747cb9137c4c9f428 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 14 Mar 2023 13:26:44 +0200 Subject: [PATCH 17/24] Add ability request sync via ipc for electron app auto-update --- .../loc.api/process.message.manager/index.js | 22 +++++++++++++++++++ .../process.messages.js | 4 +++- .../process.message.manager/process.states.js | 4 +++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/workers/loc.api/process.message.manager/index.js b/workers/loc.api/process.message.manager/index.js index d34778d32..446867558 100644 --- a/workers/loc.api/process.message.manager/index.js +++ b/workers/loc.api/process.message.manager/index.js @@ -278,6 +278,28 @@ class ProcessMessageManager { { backupFilesMetadata } ) } + + async [PROCESS_STATES.REQUEST_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE] (err, state, data) { + if (err) { + this.logger.debug('[Users sync on startup required state has not been updated]') + this.logger.error(err) + + this.sendState( + PROCESS_MESSAGES.RESPONSE_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE, + { err } + ) + + return + } + + const isDone = await this.dao + .updateUsersSyncOnStartupRequiredState() + + this.sendState( + PROCESS_MESSAGES.RESPONSE_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE, + { isDone } + ) + } } decorateInjectable(ProcessMessageManager, depsTypes) diff --git a/workers/loc.api/process.message.manager/process.messages.js b/workers/loc.api/process.message.manager/process.messages.js index 8968616f6..4ac7fc5de 100644 --- a/workers/loc.api/process.message.manager/process.messages.js +++ b/workers/loc.api/process.message.manager/process.messages.js @@ -26,5 +26,7 @@ module.exports = { REQUEST_MIGRATION_HAS_FAILED_WHAT_SHOULD_BE_DONE: 'request:migration-has-failed:what-should-be-done', REQUEST_SHOULD_ALL_TABLES_BE_REMOVED: 'request:should-all-tables-be-removed', - RESPONSE_GET_BACKUP_FILES_METADATA: 'response:get-backup-files-metadata' + RESPONSE_GET_BACKUP_FILES_METADATA: 'response:get-backup-files-metadata', + + RESPONSE_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE: 'response:update-users-sync-on-startup-required-state' } diff --git a/workers/loc.api/process.message.manager/process.states.js b/workers/loc.api/process.message.manager/process.states.js index 7b64bf820..2b548f74a 100644 --- a/workers/loc.api/process.message.manager/process.states.js +++ b/workers/loc.api/process.message.manager/process.states.js @@ -9,5 +9,7 @@ module.exports = { RESPONSE_MIGRATION_HAS_FAILED_WHAT_SHOULD_BE_DONE: 'response:migration-has-failed:what-should-be-done', - REQUEST_GET_BACKUP_FILES_METADATA: 'request:get-backup-files-metadata' + REQUEST_GET_BACKUP_FILES_METADATA: 'request:get-backup-files-metadata', + + REQUEST_UPDATE_USERS_SYNC_ON_STARTUP_REQUIRED_STATE: 'request:update-users-sync-on-startup-required-state' } From 15114f6798e8d094d8cdad67e591e7a49500651a Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 14 Mar 2023 13:27:45 +0200 Subject: [PATCH 18/24] Add v33 db migration --- .../sqlite-migrations/migration.v33.js | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 workers/loc.api/sync/dao/db-migrations/sqlite-migrations/migration.v33.js diff --git a/workers/loc.api/sync/dao/db-migrations/sqlite-migrations/migration.v33.js b/workers/loc.api/sync/dao/db-migrations/sqlite-migrations/migration.v33.js new file mode 100644 index 000000000..39a027363 --- /dev/null +++ b/workers/loc.api/sync/dao/db-migrations/sqlite-migrations/migration.v33.js @@ -0,0 +1,68 @@ +'use strict' + +const AbstractMigration = require('./abstract.migration') +const { getSqlArrToModifyColumns } = require('./helpers') + +const { + ID_PRIMARY_KEY +} = require('./helpers/const') + +class MigrationV33 extends AbstractMigration { + /** + * @override + */ + up () { + const sqlArr = [ + 'ALTER TABLE users ADD COLUMN shouldNotSyncOnStartupAfterUpdate INT', + 'ALTER TABLE users ADD COLUMN isSyncOnStartupRequired INT', + + 'UPDATE users SET shouldNotSyncOnStartupAfterUpdate = 0', + 'UPDATE users SET isSyncOnStartupRequired = 0' + ] + + this.addSql(sqlArr) + } + + /** + * @override + */ + beforeDown () { return this.dao.disableForeignKeys() } + + /** + * @override + */ + down () { + const sqlArr = [ + ...getSqlArrToModifyColumns( + 'users', + { + _id: ID_PRIMARY_KEY, + id: 'BIGINT', + email: 'VARCHAR(255)', + apiKey: 'VARCHAR(255)', + apiSecret: 'VARCHAR(255)', + authToken: 'VARCHAR(255)', + active: 'INT', + isDataFromDb: 'INT', + timezone: 'VARCHAR(255)', + username: 'VARCHAR(255)', + passwordHash: 'VARCHAR(255)', + isNotProtected: 'INT', + isSubAccount: 'INT', + isSubUser: 'INT', + createdAt: 'BIGINT', + updatedAt: 'BIGINT' + } + ) + ] + + this.addSql(sqlArr) + } + + /** + * @override + */ + afterDown () { return this.dao.enableForeignKeys() } +} + +module.exports = MigrationV33 From d49668c0c6940f58d3a539302c74451d820ab980 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 15 Mar 2023 13:54:36 +0200 Subject: [PATCH 19/24] Add test case for updateUser endpoint --- .../api-sync-mode-sqlite-test-cases.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/test-cases/api-sync-mode-sqlite-test-cases.js b/test/test-cases/api-sync-mode-sqlite-test-cases.js index e90ce6f35..99ed97812 100644 --- a/test/test-cases/api-sync-mode-sqlite-test-cases.js +++ b/test/test-cases/api-sync-mode-sqlite-test-cases.js @@ -107,10 +107,35 @@ module.exports = ( assert.strictEqual(res.body.result.email, email) assert.strictEqual(res.body.result.isSubAccount, isSubAccount) assert.isString(res.body.result.token) + assert.isBoolean(res.body.result.shouldNotSyncOnStartupAfterUpdate) + assert.isNotOk(res.body.result.shouldNotSyncOnStartupAfterUpdate) auth.token = res.body.result.token }) + it('it should be successfully performed by the updateUser method', async function () { + this.timeout(5000) + + const res = await agent + .post(`${basePath}/json-rpc`) + .type('json') + .send({ + auth, + method: 'updateUser', + params: { + shouldNotSyncOnStartupAfterUpdate: true + }, + id: 5 + }) + .expect('Content-Type', /json/) + .expect(200) + + assert.isObject(res.body) + assert.propertyVal(res.body, 'id', 5) + assert.isBoolean(res.body.result) + assert.isOk(res.body.result) + }) + it('it should be successfully performed by the signIn method by token', async function () { this.timeout(5000) @@ -131,6 +156,8 @@ module.exports = ( assert.strictEqual(res.body.result.email, email) assert.strictEqual(res.body.result.isSubAccount, isSubAccount) assert.strictEqual(res.body.result.token, auth.token) + assert.isBoolean(res.body.result.shouldNotSyncOnStartupAfterUpdate) + assert.isOk(res.body.result.shouldNotSyncOnStartupAfterUpdate) }) it('it should not be successfully performed by the signIn method', async function () { From 6bcd2da6e5c6a11a45875203e7532d4b80af1676 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 15 Mar 2023 13:55:02 +0200 Subject: [PATCH 20/24] Improve test case for getUsers endpoint --- test/test-cases/api-sync-mode-sqlite-test-cases.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test-cases/api-sync-mode-sqlite-test-cases.js b/test/test-cases/api-sync-mode-sqlite-test-cases.js index 99ed97812..02efa0b3f 100644 --- a/test/test-cases/api-sync-mode-sqlite-test-cases.js +++ b/test/test-cases/api-sync-mode-sqlite-test-cases.js @@ -355,6 +355,7 @@ module.exports = ( assert.isString(user.email) assert.isBoolean(user.isSubAccount) assert.isBoolean(user.isNotProtected) + assert.isBoolean(user.isRestrictedToBeAddedToSubAccount) assert.isArray(user.subUsers) user.subUsers.forEach((subUser) => { From c9cf7662a229eff7f091f18612a2e7c41ede47a7 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 15 Mar 2023 13:57:24 +0200 Subject: [PATCH 21/24] Fix user data normalization --- .../sync/dao/helpers/users/normalize-user-data.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/workers/loc.api/sync/dao/helpers/users/normalize-user-data.js b/workers/loc.api/sync/dao/helpers/users/normalize-user-data.js index 6fe5b7fbc..1e44b0afe 100644 --- a/workers/loc.api/sync/dao/helpers/users/normalize-user-data.js +++ b/workers/loc.api/sync/dao/helpers/users/normalize-user-data.js @@ -7,8 +7,10 @@ const _normalize = (userData) => { isSubAccount, isSubUser, haveSubUsers, - isNotProtected - } = { ...userData } + isNotProtected, + shouldNotSyncOnStartupAfterUpdate, + isSyncOnStartupRequired + } = userData ?? {} return { ...userData, @@ -17,7 +19,9 @@ const _normalize = (userData) => { isSubAccount: !!isSubAccount, isSubUser: !!isSubUser, haveSubUsers: !!haveSubUsers, - isNotProtected: !!isNotProtected + isNotProtected: !!isNotProtected, + shouldNotSyncOnStartupAfterUpdate: !!shouldNotSyncOnStartupAfterUpdate, + isSyncOnStartupRequired: !!isSyncOnStartupRequired } } From 05525630e7e7ae92bd9805c803f3fb71eb4a0d6d Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Wed, 15 Mar 2023 13:58:31 +0200 Subject: [PATCH 22/24] Fix updating user data --- workers/loc.api/sync/authenticator/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index 6d367cd07..fa240fada 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -1003,8 +1003,8 @@ class Authenticator { : this.getUserSessionByEmail({ email, isSubAccount })?.token if ( - existedToken && - typeof existedToken === 'string' + !existedToken || + typeof existedToken !== 'string' ) { return true } From 395037c44ea19fce477be1f58761508d105789dd Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 21 Mar 2023 15:24:14 +0200 Subject: [PATCH 23/24] Add ability to set options into AuthTokenGenerationError --- workers/loc.api/errors/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/workers/loc.api/errors/index.js b/workers/loc.api/errors/index.js index f68dfc410..c28a293d1 100644 --- a/workers/loc.api/errors/index.js +++ b/workers/loc.api/errors/index.js @@ -6,6 +6,9 @@ const { ConflictError, ArgsParamsError } = require('bfx-report/workers/loc.api/errors') +const { + getErrorArgs +} = require('bfx-report/workers/loc.api/errors/helpers') class CollSyncPermissionError extends BaseError { constructor (message = 'ERR_PERMISSION_DENIED_TO_SYNC_SELECTED_COLL') { @@ -220,8 +223,10 @@ class SyncInfoUpdatingError extends BaseError { } class AuthTokenGenerationError extends AuthError { - constructor (message = 'ERR_AUTH_TOKEN_HAS_NOT_BEEN_GENERATED') { - super(message) + constructor (args) { + const _args = getErrorArgs(args, 'ERR_AUTH_TOKEN_HAS_NOT_BEEN_GENERATED') + + super(_args) } } From 5f480dd2971149f9379e151540af8ec8965449c2 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 21 Mar 2023 15:30:02 +0200 Subject: [PATCH 24/24] Add auth token invalid flag into AuthError --- workers/loc.api/sync/authenticator/index.js | 47 +++++++++++++-------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index fa240fada..d2f54065e 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -1150,27 +1150,36 @@ class Authenticator { } async generateAuthToken (args) { - const opts = { - ttl: this.authTokenTTLSec, - writePermission: false - } + try { + const opts = { + ttl: this.authTokenTTLSec, + writePermission: false + } - const res = await this.getDataFromApi({ - getData: (s, args) => this.rService._generateToken(args, opts), - args, - callerName: 'AUTHENTICATOR', - eNetErrorAttemptsTimeframeMin: 10 / 60, - eNetErrorAttemptsTimeoutMs: 1000, - shouldNotInterrupt: true - }) + const res = await this.getDataFromApi({ + getData: (s, args) => this.rService._generateToken(args, opts), + args, + callerName: 'AUTHENTICATOR', + eNetErrorAttemptsTimeframeMin: 10 / 60, + eNetErrorAttemptsTimeoutMs: 1000, + shouldNotInterrupt: true + }) - const [authToken] = Array.isArray(res) ? res : [null] + const [authToken] = Array.isArray(res) ? res : [null] - if (!authToken) { - throw new AuthTokenGenerationError() - } + if (!authToken) { + throw new AuthTokenGenerationError() + } - return authToken + return authToken + } catch (err) { + throw new AuthTokenGenerationError({ + data: { + isAuthTokenGenerationError: true, + rootMessage: err.toString() + } + }) + } } async invalidateAuthToken (args) { @@ -1253,7 +1262,9 @@ class Authenticator { ) if (res?.changes < 1) { - throw new AuthTokenGenerationError() + throw new AuthTokenGenerationError({ + data: { isAuthTokenGenerationError: true } + }) } session.authToken = newAuthToken