Ver código fonte

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 <kmitrovic@itekako.com>
Reviewed-on: https://git.bubblev.org/bubblev/bubble-web/pulls/8
pull/13/head
Kristijan Mitrovic 4 anos atrás
committed by jonathan
pai
commit
142ac2090a
10 arquivos alterados com 397 adições e 59 exclusões
  1. +3
    -1
      src/_helpers/router.js
  2. +20
    -2
      src/_services/network.service.js
  3. +11
    -0
      src/_services/user.service.js
  4. +23
    -0
      src/_store/account.module.js
  5. +70
    -7
      src/_store/networks.module.js
  6. +9
    -0
      src/_store/system.module.js
  7. +149
    -44
      src/account/NetworkPage.vue
  8. +4
    -2
      src/account/NetworksPage.vue
  9. +11
    -3
      src/app/App.vue
  10. +97
    -0
      src/auth/RestorePage.vue

+ 3
- 1
src/_helpers/router.js Ver arquivo

@@ -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'


+ 20
- 2
src/_services/network.service.js Ver arquivo

@@ -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));
}
}

function getNetworkBackups(userId, networkId, messages, errors) {
return fetch(`${config.apiUrl}/users/${userId}/networks/${networkId}/backups`, util.getWithAuth())
.then(util.handleCrudResponse(messages, errors));
}

+ 11
- 0
src/_services/user.service.js Ver arquivo

@@ -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');


+ 23
- 0
src/_store/account.module.js Ver arquivo

@@ -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;


+ 70
- 7
src/_store/networks.module.js Ver arquivo

@@ -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 };
}
};



+ 9
- 0
src/_store/system.module.js Ver arquivo

@@ -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,


+ 149
- 44
src/account/NetworkPage.vue Ver arquivo

@@ -1,11 +1,17 @@
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ -->
<template>
<div v-if="network">
<h4 v-if="network.state === 'running' && configs && configs.networkUuid && network.uuid !== configs.networkUuid">
{{network.nickname}} - <i>{{messages['msg_network_state_'+network.state]}}</i><br/>
<h6><button :title="messages.message_network_connect" :onclick="'window.open(\''+networkAppLoginUrl+'\', \'_blank\')'">{{messages.message_network_connect}}</button></h6>
</h4>
<h4 v-else>{{network.nickname}} - <i>{{messages['msg_network_state_'+network.state]}}</i></h4>
<h4>{{ network.nickname }} - <i>{{ messages['msg_network_state_'+network.state] }}</i></h4>
<h6 v-if="this.isNotSelfNet && (isInReadyToRestoreState || this.network.state === 'running')">
<button v-if="this.network.state === 'running'" :title="messages.message_network_connect"
:onclick="'window.open(\'' + networkAppLoginUrl + '\', \'_blank\')'">
{{messages.message_network_connect}}
</button>
<button v-else :title="messages.message_network_restore"
:onclick="'window.open(\'' + nodeRestoreUrl + '\', \'_blank\')'">
{{ messages.message_network_restore }}
</button>
</h6>

<div v-if="stats && (network.state === 'starting' || network.state === 'restoring')">
<!-- adapted from: https://code-boxx.com/simple-vanilla-javascript-progress-bar/ -->
@@ -40,7 +46,7 @@
<span v-html="messages.message_launch_support.parseMessage(this)"></span>
</div>

<div v-if="network.state === 'running' && configs.networkUuid && network.uuid === configs.networkUuid">
<div v-if="isSelfNetAndRunning">
<button class="btn btn-secondary" @click="requestRestoreKey()"
:disabled="loading && loading.requestNetworkKeys">
{{messages.link_network_action_request_keys}}
@@ -75,25 +81,62 @@
readonly="true" />
{{ messages.message_network_keys_description }}
</div>
</div>

<hr/>
<span v-html="latestBackupInfoHtml"></span>

<span v-if="allowQueueBackup">
<br/>
<button @click="queueBckup()" class="btn btn-secondary" :disabled="loading && loading.queueBackup">
{{ messages.link_backup_network }}
</button>
</span>
</div>

<div v-if="network.state === 'stopped'">
<!-- todo: add button to restart network in restore mode -->
<hr/>
<div v-if="errors.has('networkRestore')" class="invalid-feedback d-block">
{{ errors.first('networkRestore') }}
</div>

<div v-if="networkNodes && networkNodes.length === 0">
<div v-if="restoreKey" class="alert alert-success">
{{ messages.restore_key_label }} {{ restoreKey }}
</div>
<div v-else>
<button @click="restoreNet()" class="btn btn-primary" :disabled="loading && loading.restoring">
{{ messages.button_label_restore }}
</button>
<img v-show="loading && loading.restoring" :src="loadingImgSrc" />
</div>
{{ messages.button_description_restore }}
</div>
<div v-else v-html="messages.restore_not_possible_nodes_exist_html" />

</div>

<div v-if="network.state === 'running' || network.state === 'starting' || network.state === 'stopped' || network.state === 'error_stopping'">
<div v-if="configs.sageLauncher && (network.state === 'running' || network.state === 'starting' || network.state === 'restoring' || network.state === 'stopped' || network.state === 'error_stopping')">
<hr/>
<div class="text-danger"><h4>{{messages.title_network_danger_zone}}</h4></div>
<div v-if="errors.has('node')" class="invalid-feedback d-block">{{ errors.first('node') }}</div>
<div v-if="errors.has('accountPlan')" class="invalid-feedback d-block">{{ errors.first('accountPlan') }}</div>
<div v-if="network.state === 'running' || network.state === 'starting'" style="border: 2px solid #000;">
<button @click="stopNet()" class="btn btn-danger">{{messages.link_network_action_stop}}</button>

<div v-if="network.state === 'running' || network.state === 'starting' || network.state === 'restoring'"
style="border: 2px solid #000;">
<button @click="stopNet()" class="btn btn-danger" :disabled="loading && loading.stopping">
{{messages.link_network_action_stop}}
</button>
<img v-show="loading && loading.stopping" :src="loadingImgSrc" />
{{messages.link_network_action_stop_description}}

<!-- the next condition is to prevent this info shown twice on this page -->
<span v-if="!isSelfNetAndRunning" v-html="latestBackupInfoHtml"></span>
</div>
<hr/>
<div style="border: 2px solid #000;">
<button @click="deleteNet()" class="btn btn-danger">{{messages.link_network_action_delete}}</button>
<div v-else-if="network.state === 'stopped' || network.state === 'error_stopping'"
style="border: 2px solid #000;">
<button @click="deleteNet()" class="btn btn-danger" :disabled="loading && loading.deleting">
{{messages.link_network_action_delete}}
</button>
<img v-show="loading && loading.deleting" :src="loadingImgSrc" />
{{messages.link_network_action_delete_description}}
</div>
</div>
@@ -123,11 +166,13 @@
computed: {
...mapState('networks', [
'network', 'newNodeNotification', 'networkStatuses', 'networkNodes', 'networkKeysRequested',
'deletedNetworkUuid', 'networkKeys', 'loading'
'deletedNetworkUuid', 'networkKeys', 'loading', 'restoreKey', 'backups'
]),
...mapState('system', ['messages', 'configs', 'appLinks']),
showSetupHelp () {
return (this.network !== null && (this.network.state === 'running' || this.network.state === 'starting' || this.network.state === 'restoring'));
return (this.network !== null && this.network.uuid !== this.configs.networkUuid
&& (this.network.state === 'running' || this.network.state === 'starting'
|| this.network.state === 'restoring'));
},
addableDeviceTypes: function () {
if (this.messages && this.messages['!addable_device_types']) {
@@ -140,12 +185,48 @@
},
networkAppLoginUrl: function () {
return 'https://'+this.network.name+'.'+this.network.domainName+'/appLogin?session='+util.currentUser().token+'&uri=/devices';
},
nodeRestoreUrl: function () {
return 'https://' + this.networkNodes[0].fqdn + ':' + this.networkNodes[0].sslPort + '/restore';
},
allowQueueBackup: function () {
if (this.backups === null) return false;
if (this.backups.length === 0) return true;

let lastBackupStatus = this.backups[0].status;
return lastBackupStatus !== 'queued' && lastBackupStatus !== 'backup_in_progress';
},
latestBackupInfoHtml: function() {
if (this.backups === null) {
return '<hr/>' + this.messages.label_latest_backup + '<img src="' + loadingImgSrc + '" />';
} else if (this.backups.length === 0) {
return '<hr/>' + this.messages.label_no_latest_backup;
} else {
let lastBackup = this.backups[0];
return '<hr/>' + this.messages.label_latest_backup
+ " " + lastBackup.label + " <i>" + lastBackup.status + "</i> "
+ this.messages.date_format_app_data_epoch_time.parseDateMessage(lastBackup.creationTime,
this.messages);
}
},
isSelfNetAndRunning: function() {
return this.network && this.network.state === 'running'
&& this.configs && this.configs.networkUuid && this.network.uuid === this.configs.networkUuid;
},
isNotSelfNet: function() {
return this.configs && this.configs.networkUuid
&& this.network && this.network.uuid !== this.configs.networkUuid
},
isInReadyToRestoreState: function() {
return this.network && this.network.state === 'restoring' && (!this.stats || this.stats.percent === 100)
&& this.networkNodes && this.networkNodes.length === 1
}
},
methods: {
...mapActions('networks', [
'getNetworkById', 'deleteNetwork', 'getStatusesByNetworkId', 'getNodesByNetworkId',
'stopNetwork', 'deleteNetwork', 'requestNetworkKeys', 'retrieveNetworkKeys'
'stopNetwork', 'queueBackup', 'restoreNetwork', 'deleteNetwork', 'requestNetworkKeys',
'retrieveNetworkKeys', 'getBackups'
]),
...mapActions('system', ['getAppLinks']),
refreshStatus (userId) {
@@ -163,12 +244,23 @@
messages: this.messages,
errors: this.errors
});
if (this.backups === null || this.refresher === null) {
// note about the second part of the condition above: if refreshes is turned on, then fetch backups
// from BE only once
this.getBackups({ userId: userId, networkId: this.networkId,
messages: this.messages, errors: this.errors });
}
},
startStatusRefresher (user) {
// todo: separate refresher for network -- after "stop" we should refresh the status to show it is stopped
this.refresher = setInterval(() => this.refreshStatus(user.uuid), 5000);
},

clearRefresherInterval (refresherId) {
if (refresherId !== null) {
clearInterval(refresherId);
refresherId = null;
}
},
stopRefreshStatus (userId) {
this.getNetworkById({userId: userId, networkId: this.networkId, messages: this.messages, errors: this.errors});
},
@@ -184,12 +276,29 @@
messages: this.messages,
errors: this.errors
});
clearInterval(this.refresher);
this.refresher = null;
clearRefresherInterval(this.refresher);
this.stopRefresher = setInterval(() => this.stopRefreshStatus(this.user.uuid), 5000);
}
}
},
queueBckup () {
this.errors.clear();
this.queueBackup({
userId: this.user.uuid,
networkId: this.networkId,
messages: this.messages,
errors: this.errors
});
},
restoreNet () {
this.errors.clear();
this.restoreNetwork({
userId: this.user.uuid,
networkId: this.networkId,
messages: this.messages,
errors: this.errors
});
},
deleteNet () {
if (confirm(this.messages.confirmation_network_action_delete)) {
this.errors.clear();
@@ -230,24 +339,19 @@
this.getAppLinks(user.locale);
},
beforeDestroy () {
if (this.refresher !== null) clearInterval(this.refresher);
if (this.stopRefresher !== null) clearInterval(this.stopRefresher);
this.clearRefresherInterval(this.refresher);
this.clearRefresherInterval(this.stopRefresher);
},
watch: {
network (net) {
if (net) {
if (net.state !== 'starting' && net.state !== 'restoring' && this.refresher !== null) {
clearInterval(this.refresher);
this.refresher = null;
}
if (net.state === 'stopped' && this.stopRefresher !== null) {
clearInterval(this.stopRefresher);
this.stopRefresher = null;
}
if (net.uuid === 'Not Found') {
this.$router.replace({path: '/bubbles'});
}
if (net.uuid === 'Not Found') this.$router.replace({path: '/bubbles'});
this.networkUuid = net.uuid;

if (net.state !== 'starting' && net.state !== 'restoring') {
this.clearRefresherInterval(this.refresher);
}
if (net.state !== 'stopping') this.clearRefresherInterval(this.stopRefresher);
}
},
networkNodes (nodes) {
@@ -255,19 +359,20 @@
},
networkStatuses (stats) {
if (this.network && stats && stats.length && stats.length > 0) {
let latestStats = null;
for (let i=0; i<stats.length; i++) {
if (stats[i].network === this.network.uuid) {
this.stats = stats[i];
if (this.stats.percent === 100) {
clearInterval(this.refresher);
this.refresher = null;
}
return;
if (stats[i].network === this.network.uuid
&& (latestStats === null || stats[i].ctime > latestStats.ctime)) {
latestStats = stats[i];
}
}
// status not found for our network
clearInterval(this.refresher);
this.refresher = null;
if (latestStats !== null) {
this.stats = latestStats;
if (this.stats.percent === 100) this.clearRefresherInterval(this.refresher);
} else {
// status not found for our network
this.clearRefresherInterval(this.refresher);
}
}
},
deletedNetworkUuid (uuid) {


+ 4
- 2
src/account/NetworksPage.vue Ver arquivo

@@ -46,10 +46,12 @@
},
computed: {
...mapState('networks', ['networks']),
...mapState('system', ['messages']),
...mapState('system', ['messages', 'configs']),
...mapState('users', ['policy'])
},
created () {
if (!this.configs.sageLauncher) { this.$router.replace({ path: '/bubble/' + this.configs.networkUuid }); }

const user = util.currentUser();
const selectedLocale = (user !== null && typeof user.locale !== 'undefined' && user.locale !== null ? user.locale : 'detect');
this.getAllNetworks({userId: user.uuid, messages: this.messages, errors: this.errors});
@@ -88,4 +90,4 @@
}
}
};
</script>
</script>

+ 11
- 3
src/app/App.vue Ver arquivo

@@ -1,6 +1,8 @@
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ -->
<template>
<div class="jumbotron">
<div v-if="!configs"><img :src="loadingImgSrc" /></div>

<div v-else class="jumbotron">
<totp-modal/>

<table v-if="this.user !== null && status.loggedIn && activated && path && path !== '' && path !== '/'" class="dash-icon-panel">
@@ -49,13 +51,15 @@
<script>
import {mapState, mapActions, mapGetters} from 'vuex'
import { util } from '../_helpers'
import { loadingImgSrc } from '../_store';

export default {
name: 'app',
data() {
return {
showLocaleSelector: false,
selectedLocale: 'detect'
selectedLocale: 'detect',
loadingImgSrc: loadingImgSrc
}
},
computed: {
@@ -87,7 +91,8 @@ export default {
return appView;
},
locales () { return this.configs.locales; },
path () { return this.$route.path; }
path () { return this.$route.path; },
isInRestoringMode () { return this.configs ? this.configs.isWaitingRestoring : undefined; }
},
methods: {
...mapActions({ clearAlert: 'alert/clear' }),
@@ -136,6 +141,9 @@ export default {
locale (loc) {
this.selectedLocale = loc;
this.reloadMessages()
},
isInRestoringMode (restoringMode) {
if (restoringMode === true && this.path !== '/restore') this.$router.replace('/restore');
}
},
created() {


+ 97
- 0
src/auth/RestorePage.vue Ver arquivo

@@ -0,0 +1,97 @@
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ -->
<template>
<div>
<h2>{{ messages.form_title_restore }}</h2>

<div v-if="!configs"><img :src="loadingImgSrc" /></div>

<div v-else-if="!configs.isWaitingRestoring" class="alert alert-info">
{{ messages.message_restore_not_applicable }}<hr/>
<a href="/">{{ messages.message_back_to_root }}</a>
</div>

<div v-else>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label htmlFor="restoreShortKey">{{messages.field_label_restore_short_key}}</label>
<input type="text" v-model="restoreShortKey" name="restoreShortKey" class="form-control"
:class="{ 'is-invalid': submitted && !restoreShortKey }" />
<div v-show="submitted && !restoreShortKey" class="invalid-feedback">
{{ messages.err_restoreShortKey_required }}
</div>
<div v-if="submitted && errors.has('restoreShortKey')" class="invalid-feedback d-block">
{{ errors.first('restoreShortKey') }}
</div>
</div>

<div class="form-group">
<label htmlFor="restoreLongNetworkKey">{{messages.field_label_restore_long_key}}</label>
<textarea v-model="restoreLongNetworkKey" name="restoreLongNetworkKey" class="form-control"
:class="{ 'is-invalid': submitted && !restoreLongNetworkKey }" />
<div v-show="submitted && !restoreLongNetworkKey" class="invalid-feedback">
{{ messages.err_restoreLongNetworkKey_required }}
</div>
<div v-if="submitted && errors.has('restoreLongNetworkKey')" class="invalid-feedback d-block">
{{ errors.first('restoreLongNetworkKey') }}
</div>
</div>

<div class="form-group">
<label htmlFor="password">{{messages.field_label_password}}</label>
<input type="password" v-model="password" name="password" class="form-control"
:class="{ 'is-invalid': submitted && !password }" />
<div v-show="submitted && !password" class="invalid-feedback">
{{ messages.err_password_required }}
</div>
<div v-if="submitted && errors.has('password')" class="invalid-feedback d-block">
{{ errors.first('password') }}
</div>
</div>

<div class="form-group">
<button class="btn btn-primary" :disabled="status.restoring">
{{ messages.button_label_restore }}
</button>
<img v-show="status.restoring" :src="loadingImgSrc" />
</div>
</form>
</div>
</div>
</template>

<script>
import { mapState, mapActions } from 'vuex';
import { loadingImgSrc } from '../_store';

export default {
data () {
return {
restoreShortKey: (this.$route.query && this.$route.query.k) ? this.$route.query.k : null,
restoreLongNetworkKey: null,
password: '',
submitted: false,
loadingImgSrc: loadingImgSrc
}
},
created () {
this.loadSystemConfigs();
},
computed: {
...mapState('account', [ 'status' ]),
...mapState('system', [ 'configs', 'messages' ])
},
methods: {
...mapActions('account', [ 'restore' ]),
...mapActions('system', [ 'loadSystemConfigs' ]),
handleSubmit (e) {
this.errors.clear();
this.submitted = true;
const { restoreShortKey, restoreLongNetworkKey, password } = this;
this.restore({
shortKey: restoreShortKey, longKey: restoreLongNetworkKey, password: password,
systemConfigs: this.configs, messages: this.messages, errors: this.errors
});
}
}
};
</script>

Carregando…
Cancelar
Salvar