From 142ac2090a04f3e1091d81cf9cf492e92ad7c9b0 Mon Sep 17 00:00:00 2001 From: Kristijan Mitrovic Date: Tue, 7 Jul 2020 19:11:44 +0000 Subject: [PATCH] Add UI needed for restore bubble process (#8) Remove not used property from parameter object Show bubble menu item only to admins on nodes Merge branch 'master' into kris/add_restore_ui # Conflicts: # src/account/NetworkPage.vue Reference method properly within this Merge branch 'master' into kris/add_restore_ui # Conflicts: # src/account/NetworkPage.vue Show link properly for network in restoring state Load backups only when needed for netwrok in restoring state Link for restoring node Use latest stats for network Merge branch 'master' into kris/add_restore_ui Fix loading image for backups label Call backups API only once while starting network Show backup info in proper places only Show latest backup and allow queuing new one Use proper name for waiting restoring flag Merge branch 'master' into kris/add_restore_ui Add support for stopping metworks in restoring state Merge branch 'master' into kris/add_restore_ui # Conflicts: # src/_store/system.module.js # src/account/NetworkPage.vue Use dinamyc labels-messages Report restore is started on the page Redirect to restore page in restoring mode Fix indent Split page for restore from login page Merge branch 'master' into kris/add_restore_ui Merge branch 'master' into kris/add_restore_ui Merge branch 'master' into kris/add_restore_ui Merge branch 'master' into kris/add_restore_ui Merge branch 'master' into kris/add_restore_ui # Conflicts: # src/_store/system.module.js # src/account/NetworksPage.vue # src/auth/LoginPage.vue Merge branch 'master' into kris/add_restore_ui Merge branch 'master' into kris/add_restore_ui Merge branch 'master' into kris/add_restore_ui # Conflicts: # src/auth/LoginPage.vue Merge branch 'master' into kris/add_restore_ui # Conflicts: # src/_services/user.service.js Reference configs properly through this object Add support for restoring bubble on running instance Show restore short key to user Enhance Danger Zone on UI Add restore network UI elements Redirect to single bubble page on non-sage networks Remove setup help message on the node itself Add bubble info page on end bubble server's WebUI Co-authored-by: Kristijan Mitrovic Reviewed-on: https://git.bubblev.org/bubblev/bubble-web/pulls/8 --- src/_helpers/router.js | 4 +- src/_services/network.service.js | 22 +++- src/_services/user.service.js | 11 ++ src/_store/account.module.js | 23 ++++ src/_store/networks.module.js | 77 ++++++++++-- src/_store/system.module.js | 9 ++ src/account/NetworkPage.vue | 193 ++++++++++++++++++++++++------- src/account/NetworksPage.vue | 6 +- src/app/App.vue | 14 ++- src/auth/RestorePage.vue | 97 ++++++++++++++++ 10 files changed, 397 insertions(+), 59 deletions(-) create mode 100644 src/auth/RestorePage.vue diff --git a/src/_helpers/router.js b/src/_helpers/router.js index 35617d0..3335028 100644 --- a/src/_helpers/router.js +++ b/src/_helpers/router.js @@ -15,6 +15,7 @@ import LogoutPage from '../auth/LogoutPage' import ForgotPasswordPage from '../auth/ForgotPasswordPage' import MultifactorAuthPage from '../auth/MultifactorAuthPage' import AppLoginPage from '../auth/AppLoginPage' +import RestorePage from "../auth/RestorePage" import DashboardPage from '../account/DashboardPage' import ProfilePage from '../account/profile/ProfilePage' import ActionPage from '../account/profile/ActionPage' @@ -111,6 +112,7 @@ export const router = new Router({ { path: '/logout', component: LogoutPage }, { path: '/forgotPassword', component: ForgotPasswordPage }, { path: '/appLogin', component: AppLoginPage }, + { path: '/restore', component: RestorePage }, { path: '/admin/accounts', component: AccountsPage }, { path: '/admin/new_account', component: ProfilePage }, @@ -128,7 +130,7 @@ export const router = new Router({ }); const publicPages = [ - '/login', '/logout', '/register', '/appLogin', + '/login', '/logout', '/register', '/appLogin', '/restore', '/forgotPassword', '/resetPassword', '/action', '/auth', '/activate', '/legal' diff --git a/src/_services/network.service.js b/src/_services/network.service.js index 07b76a3..579c6df 100644 --- a/src/_services/network.service.js +++ b/src/_services/network.service.js @@ -10,13 +10,16 @@ export const networkService = { getNetworkById, getNearestRegions, startNetwork, + queueBackup, forkNetwork, getStatusesByNetworkId, getNodesByNetworkId, stopNetwork, + restoreNetwork, deleteNetwork, requestNetworkKeys, - retrieveNetworkKeys + retrieveNetworkKeys, + getNetworkBackups }; function getAllNetworks(userId, messages, errors) { @@ -58,6 +61,16 @@ function stopNetwork(userId, networkId, messages, errors) { return fetch(`${config.apiUrl}/users/${userId}/networks/${networkId}/actions/stop`, util.postWithAuth()).then(util.handleCrudResponse(messages, errors)); } +function queueBackup(userId, networkId, messages, errors) { + return fetch(`${config.apiUrl}/users/${userId}/networks/${networkId}/backups/user_requested`, util.putWithAuth()) + .then(util.handleCrudResponse(messages, errors)); +} + +function restoreNetwork(userId, networkId, messages, errors) { + return fetch(`${config.apiUrl}/users/${userId}/networks/${networkId}/actions/restore`, + util.postWithAuth()).then(util.handleCrudResponse(messages, errors)); +} + // deleting network is not allowed via API, instead we delete the AccountPlan, which in turn deletes the network function deleteNetwork(userId, networkId, messages, errors) { return fetch(`${config.apiUrl}/users/${userId}/plans/${networkId}`, util.deleteWithAuth()).then(util.handleCrudResponse(messages, errors)); @@ -69,4 +82,9 @@ function requestNetworkKeys(userId, networkId, messages, errors) { function retrieveNetworkKeys(userId, networkId, code, password, messages, errors) { return fetch(`${config.apiUrl}/users/${userId}/networks/${networkId}/actions/keys/${code}`, util.postWithAuth({name: 'password', value: password})).then(util.handleCrudResponse(messages, errors)); -} \ No newline at end of file +} + +function getNetworkBackups(userId, networkId, messages, errors) { + return fetch(`${config.apiUrl}/users/${userId}/networks/${networkId}/backups`, util.getWithAuth()) + .then(util.handleCrudResponse(messages, errors)); +} diff --git a/src/_services/user.service.js b/src/_services/user.service.js index b241fa1..5ff4307 100644 --- a/src/_services/user.service.js +++ b/src/_services/user.service.js @@ -9,6 +9,7 @@ export const userService = { login, appLogin, logout, + restore, forgotPassword, register, searchAccounts, @@ -66,6 +67,16 @@ function appLogin(session, messages, errors) { .then(setSessionUser); } +function restore(shortKey, longKey, password, messages, errors) { + const requestOptions = { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ 'data': longKey, 'password': password }) + }; + return fetch(`${config.apiUrl}/auth/restore/${shortKey}`, requestOptions) + .then(handleAuthResponse(messages, errors)); +} + function logout(messages, errors) { if (util.currentUser() === null) { console.log('userService.logout: already logged out'); diff --git a/src/_store/account.module.js b/src/_store/account.module.js index 919cc6d..6cb997a 100644 --- a/src/_store/account.module.js +++ b/src/_store/account.module.js @@ -10,6 +10,7 @@ const user = util.currentUser(); const defaultStatus = { loggingIn: false, loggedIn: false, + restoring: false, registering: false, updating: false, settingLocale: false, @@ -111,6 +112,17 @@ const actions = { error => commit('logoutFailure', error) ); }, + restore({ dispatch, commit }, { shortKey, longKey, password, systemConfigs, messages, errors }) { + commit('restoreRequest'); + userService.restore(shortKey, longKey, password, messages, errors) + .then( + ok => { + commit('restoreSuccess'); + systemConfigs.isWaitingRestoring = false; + }, + error => commit('restoreFailure', error) + ); + }, forgotPassword({ commit }, {username, messages, errors}) { commit('forgotPasswordRequest'); userService.forgotPassword(username, messages, errors) @@ -293,6 +305,17 @@ const mutations = { console.log('logout failed: '+JSON.stringify(error)); }, + restoreRequest(state) { + state.status = Object.assign({}, state.status, {restoring: true}); + }, + restoreSuccess(state) { + state.status = Object.assign({}, state.status, {restoring: false}); + }, + restoreFailure(state, error) { + state.status = Object.assign({}, state.status, {restoring: false}); + console.log('restore failed: ' + JSON.stringify(error)); + }, + forgotPasswordRequest(state) { state.status = Object.assign({}, {sendingResetPasswordMessage: true}); state.resetPasswordMessageSent = false; diff --git a/src/_store/networks.module.js b/src/_store/networks.module.js index c8c76e1..a6df403 100644 --- a/src/_store/networks.module.js +++ b/src/_store/networks.module.js @@ -8,9 +8,9 @@ import { util } from '../_helpers'; const state = { loading: { - networks: false, network: false, deleting: false, + networks: false, network: false, stopping: false, restoring: false, deleting: false, nearestRegions: false, startingNetwork: false, networkStatuses: false, networkNodes: false, - requestNetworkKeys: false, retrieveNetworkKeys: false + requestNetworkKeys: false, retrieveNetworkKeys: false, queueBackup: false }, creating: null, error: null, @@ -22,7 +22,9 @@ const state = { networkNodes: null, deletedNetworkUuid: null, networkKeysRequested: null, - networkKeys: null + networkKeys: null, + restoreKey: null, + backups: null, }; const actions = { @@ -35,13 +37,18 @@ const actions = { ); }, + getBackups({ commit }, { userId, networkId, messages, errors }) { + commit('getNetworkBackupsRequest'); + networkService.getNetworkBackups(userId, networkId, messages, errors) + .then(backups => commit('getNetworkBackupsSuccess', backups), + error => commit('getNetworkBackupsFailure', error)); + }, + getNetworkById({ commit }, {userId, networkId, messages, errors}) { commit('getNetworkByIdRequest'); networkService.getNetworkById(userId, networkId, messages, errors) - .then( - network => commit('getNetworkByIdSuccess', network), - error => commit('getNetworkByIdFailure', error) - ); + .then(network => commit('getNetworkByIdSuccess', network), + error => commit('getNetworkByIdFailure', error)); }, addPlanAndStartNetwork({ commit }, {userId, accountPlan, cloud, region, messages, errors}) { @@ -95,6 +102,25 @@ const actions = { ); }, + queueBackup({ commit, dispatch }, { userId, networkId, messages, errors }) { + commit('queueBackupRequest', networkId); + networkService.queueBackup(userId, networkId, messages, errors) + .then(backup => commit('queueBackupSuccess', backup), + error => commit('queueBackupFailure', { networkId, error: error.toString() })) + .then(r => dispatch('getBackups', + { userId: userId, networkId: networkId, + messages: messages, errors: errors })); +}, + + restoreNetwork({ commit }, { userId, networkId, messages, errors }) { + commit('restoreNetworkRequest', networkId); + networkService.restoreNetwork(userId, networkId, messages, errors) + .then( + network => commit('restoreNetworkSuccess', network), + error => commit('restoreNetworkFailure', { networkId, error: error.toString() }) + ); + }, + deleteNetwork({ commit }, {userId, networkId, messages, errors}) { commit('deleteNetworkRequest', networkId); networkService.deleteNetwork(userId, networkId, messages, errors) @@ -215,6 +241,31 @@ const mutations = { state.error = error; }, + queueBackupRequest(state, id) { + state.loading.queueBackup = true; + }, + queueBackupSuccess(state, id) { + // noop - state.loading.queueBackup will be set to false only after backup info is loaded to prevent allowing + // another backup in queue before this one i really processed. + }, + queueBackupFailure(state, { id, error }) { + state.loading.queueBackup = false; + state.error = error; + }, + + restoreNetworkRequest(state, networkId) { + state.loading.restoring = true; + state.restoreKey = null; + }, + restoreNetworkSuccess(state, restoreNodeNotification) { + state.restoreKey = restoreNodeNotification.restoreKey; + state.loading.restoring = false; + }, + restoreNetworkFailure(state, { restoreNodeNotification, error }) { + state.loading.restoring = false; + state.error = error; + }, + deleteNetworkRequest(state, id) { state.loading.deleting = true; }, @@ -271,6 +322,18 @@ const mutations = { retrieveNetworkKeysFailure(state, error) { state.loading.retrieveNetworkKeys = false; state.error = { error }; + }, + getNetworkBackupsRequest(state, backups) { + state.backups = null; + }, + getNetworkBackupsSuccess(state, backups) { + state.backups = backups; + state.loading.queueBackup = false; + }, + getNetworkBackupsFailure(state, error) { + state.backups = null; + state.loading.queueBackup = false; + state.error = { error }; } }; diff --git a/src/_store/system.module.js b/src/_store/system.module.js index 7a20261..dad227f 100644 --- a/src/_store/system.module.js +++ b/src/_store/system.module.js @@ -21,6 +21,7 @@ const state = { launchLock: null, promoCodePolicy: null, requireSendMetrics: null, + isWaitingRestoring: false, support: {}, securityLevels: null }, @@ -177,6 +178,14 @@ const getters = { index: 1 }); } else { + if (isAdmin) { + dashApps.push({ + href: '/bubble/' + configs.networkUuid, + title: messages.label_menu_network, + icon: messages.label_menu_networks_icon, + index: 1 + }); + } dashApps.push({ href: '/devices', title: messages.label_menu_devices, diff --git a/src/account/NetworkPage.vue b/src/account/NetworkPage.vue index 66e9613..d777487 100644 --- a/src/account/NetworkPage.vue +++ b/src/account/NetworkPage.vue @@ -1,11 +1,17 @@