Browse Source

start adding restore functionality

pull/1/head
Jonathan Cobb 4 years ago
parent
commit
a104db8906
7 changed files with 178 additions and 30 deletions
  1. +11
    -1
      src/_services/network.service.js
  2. +49
    -4
      src/_store/networks.module.js
  3. +102
    -5
      src/account/NetworkPage.vue
  4. +13
    -17
      src/account/NetworksPage.vue
  5. +1
    -1
      src/account/NewNetworkPage.vue
  6. +1
    -1
      src/account/profile/ActionPage.vue
  7. +1
    -1
      src/auth/MultifactorAuthPage.vue

+ 11
- 1
src/_services/network.service.js View File

@@ -9,7 +9,9 @@ export const networkService = {
getStatusesByNetworkId, getStatusesByNetworkId,
getNodesByNetworkId, getNodesByNetworkId,
stopNetwork, stopNetwork,
deleteNetwork
deleteNetwork,
requestNetworkKeys,
retrieveNetworkKeys
}; };


function getAllNetworks(userId, messages, errors) { function getAllNetworks(userId, messages, errors) {
@@ -46,3 +48,11 @@ function stopNetwork(userId, networkId, messages, errors) {
function deleteNetwork(userId, networkId, messages, errors) { function deleteNetwork(userId, networkId, messages, errors) {
return fetch(`${config.apiUrl}/users/${userId}/plans/${networkId}`, util.deleteWithAuth()).then(util.handleCrudResponse(messages, errors)); return fetch(`${config.apiUrl}/users/${userId}/plans/${networkId}`, util.deleteWithAuth()).then(util.handleCrudResponse(messages, errors));
} }

function requestNetworkKeys(userId, networkId, messages, errors) {
return fetch(`${config.apiUrl}/users/${userId}/networks/${networkId}/actions/keys`, util.getWithAuth()).then(util.handleCrudResponse(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));
}

+ 49
- 4
src/_store/networks.module.js View File

@@ -5,7 +5,8 @@ import { util } from '../_helpers';
const state = { const state = {
loading: { loading: {
networks: false, network: false, deleting: false, networks: false, network: false, deleting: false,
nearestRegions: false, startingNetwork: false, networkStatuses: false, networkNodes: false
nearestRegions: false, startingNetwork: false, networkStatuses: false, networkNodes: false,
requestNetworkKeys: false, retrieveNetworkKeys: false
}, },
creating: null, creating: null,
error: null, error: null,
@@ -14,7 +15,9 @@ const state = {
nearestRegions: null, nearestRegions: null,
newNodeNotification: null, newNodeNotification: null,
networkStatuses: {}, networkStatuses: {},
networkNodes: null
networkNodes: null,
networkKeysRequested: null,
networkKeys: null
}; };


const actions = { const actions = {
@@ -70,7 +73,7 @@ const actions = {
}, },


stopNetwork({ commit }, {userId, networkId, messages, errors}) { stopNetwork({ commit }, {userId, networkId, messages, errors}) {
commit('stopNetworkRequest', id);
commit('stopNetworkRequest', networkId);
networkService.stopNetwork(userId, networkId, messages, errors) networkService.stopNetwork(userId, networkId, messages, errors)
.then( .then(
network => commit('stopNetworkSuccess', network), network => commit('stopNetworkSuccess', network),
@@ -79,7 +82,7 @@ const actions = {
}, },


deleteNetwork({ commit }, {userId, networkId, messages, errors}) { deleteNetwork({ commit }, {userId, networkId, messages, errors}) {
commit('deleteNetworkRequest', id);
commit('deleteNetworkRequest', networkId);
networkService.deleteNetwork(userId, networkId, messages, errors) networkService.deleteNetwork(userId, networkId, messages, errors)
.then( .then(
network => commit('deleteNetworkSuccess', network), network => commit('deleteNetworkSuccess', network),
@@ -95,6 +98,24 @@ const actions = {
error => commit('getNearestRegionsFailure', error) error => commit('getNearestRegionsFailure', error)
); );
}, },

requestNetworkKeys({ commit }, {userId, networkId, messages, errors}) {
commit('requestNetworkKeysRequest');
networkService.requestNetworkKeys(userId, networkId, messages, errors)
.then(
ok => commit('requestNetworkKeysSuccess', networkId),
error => commit('requestNetworkKeysFailure', error)
);
},

retrieveNetworkKeys({ commit }, {userId, networkId, code, password, messages, errors}) {
commit('retrieveNetworkKeysRequest');
networkService.retrieveNetworkKeys(userId, networkId, code, password, messages, errors)
.then(
keys => commit('retrieveNetworkKeysSuccess', keys),
error => commit('retrieveNetworkKeysFailure', error)
);
}
}; };


const mutations = { const mutations = {
@@ -200,6 +221,30 @@ const mutations = {
getNearestRegionsFailure(state, error) { getNearestRegionsFailure(state, error) {
state.loading.nearestRegions = false; state.loading.nearestRegions = false;
state.error = { error }; state.error = { error };
},

requestNetworkKeysRequest(state) {
state.loading.requestNetworkKeys = true;
},
requestNetworkKeysSuccess(state, networkId) {
state.loading.requestNetworkKeys = false;
state.networkKeysRequested = networkId;
},
requestNetworkKeysFailure(state, error) {
state.loading.requestNetworkKeys = false;
state.error = { error };
},

retrieveNetworkKeysRequest(state) {
state.loading.retrieveNetworkKeys = true;
},
retrieveNetworkKeysSuccess(state, keys) {
state.loading.retrieveNetworkKeys = false;
state.networkKeys = keys;
},
retrieveNetworkKeysFailure(state, error) {
state.loading.retrieveNetworkKeys = false;
state.error = { error };
} }
}; };




+ 102
- 5
src/account/NetworkPage.vue View File

@@ -2,6 +2,7 @@
<div v-if="network"> <div v-if="network">
<h4>{{network.name}}.{{network.domainName}} - <i>{{messages['msg_network_state_'+network.state]}}</i></h4> <h4>{{network.name}}.{{network.domainName}} - <i>{{messages['msg_network_state_'+network.state]}}</i></h4>
<div v-if="stats"> <div v-if="stats">
<!-- adapted from: https://code-boxx.com/simple-vanilla-javascript-progress-bar/ -->
<div class="progress-wrap"> <div class="progress-wrap">
<div class="progress-bar" :style="'width: '+stats.percent+'%'" :id="'progressBar_'+networkId"></div> <div class="progress-bar" :style="'width: '+stats.percent+'%'" :id="'progressBar_'+networkId"></div>
<div class="progress-text">{{messages.label_percent.parseMessage(this, {percent: stats.percent})}}</div> <div class="progress-text">{{messages.label_percent.parseMessage(this, {percent: stats.percent})}}</div>
@@ -38,6 +39,54 @@
</table> </table>
</div> </div>


<div v-if="network.state === 'running'">
<button @click="requestRestoreKey()">{{messages.link_network_action_request_keys}}</button>
<div v-if="errors.has('networkKeys')" class="invalid-feedback d-block">{{ errors.first('networkKeys') }}</div>
<div v-if="networkKeysRequested && networkKeysRequested === networkId">{{messages.message_network_action_keys_requested}}</div>

<h5>{{messages.message_network_action_retrieve_keys}}</h5>
<form @submit.prevent="retrieveRestoreKey()">
<div class="form-group">
<label for="restoreKeyCode">{{messages.field_network_key_download_code}}</label>
<input type="text" v-model="restoreKeyCode" v-validate="'required'" name="restoreKeyCode" class="form-control" :class="{ 'is-invalid': errors.has('retrieveNetworkKeys') }" />
<div v-if="errors.has('retrieveNetworkKeys')" class="invalid-feedback">{{ errors.first('retrieveNetworkKeys') }}</div>
</div>
<div class="form-group">
<label for="restoreKeyPassword">{{messages.field_network_key_download_password}}</label>
<input type="text" v-model="restoreKeyPassword" v-validate="'required'" name="restoreKeyPassword" class="form-control" :class="{ 'is-invalid': errors.has('password') }" />
<div v-if="errors.has('password')" class="invalid-feedback">{{ errors.first('password') }}</div>
</div>
<button>{{messages.button_label_retrieve_keys}}</button>
<div v-if="networkKeys">
<hr/>
<h4><b>{{messages.message_network_keys}}</b></h4>: {{networkKeys}}
<hr/>
{{messages.message_network_keys_description}}
</div>
</form>
</div>

<div v-if="network.state === 'stopped'">
<!-- todo: add button to restart network in restore mode -->
</div>

<div>
<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="network.state === 'running'" style="border: 2px solid #000;">
<hr/>
<button @click="stopNet()" class="text-danger">{{messages.link_network_action_stop}}</button>
{{messages.link_network_action_stop_description}}
</div>
<div v-else></div>
<hr/>
<div style="border: 2px solid #000;">
<button @click="deleteNet()" class="text-danger">{{messages.link_network_action_delete}}</button>
{{messages.link_network_action_delete_description}}
</div>
</div>

</div> </div>
</template> </template>


@@ -48,17 +97,25 @@
export default { export default {
data() { data() {
return { return {
user: util.currentUser(),
networkId: this.$route.params.id, networkId: this.$route.params.id,
stats: null, stats: null,
refresher: null
refresher: null,
restoreKeyCode: null,
restoreKeyPassword: null
}; };
}, },
computed: { computed: {
...mapState('networks', ['network', 'newNodeNotification', 'networkStatuses', 'networkNodes']),
...mapState('networks', [
'network', 'newNodeNotification', 'networkStatuses', 'networkNodes', 'networkKeysRequested', 'networkKeys'
]),
...mapState('system', ['messages']) ...mapState('system', ['messages'])
}, },
methods: { methods: {
...mapActions('networks', ['getNetworkById', 'deleteNetwork', 'getStatusesByNetworkId', 'getNodesByNetworkId']),
...mapActions('networks', [
'getNetworkById', 'deleteNetwork', 'getStatusesByNetworkId', 'getNodesByNetworkId',
'stopNetwork', 'deleteNetwork', 'requestNetworkKeys'
]),
...mapGetters('networks', ['loading']), ...mapGetters('networks', ['loading']),
refreshStatus (userId) { refreshStatus (userId) {
this.getNetworkById({userId: userId, networkId: this.networkId, messages: this.messages, errors: this.errors}); this.getNetworkById({userId: userId, networkId: this.networkId, messages: this.messages, errors: this.errors});
@@ -78,6 +135,44 @@
}, },
startStatusRefresher (user) { startStatusRefresher (user) {
this.refresher = setInterval(() => this.refreshStatus(user.uuid), 5000); this.refresher = setInterval(() => this.refreshStatus(user.uuid), 5000);
},
stopNet () {
this.errors.clear();
this.stopNetwork({
userId: this.user.uuid,
networkId: this.networkId,
messages: this.messages,
errors: this.errors
});
},
deleteNet () {
this.errors.clear();
this.deleteNetwork({
userId: this.user.uuid,
networkId: this.networkId,
messages: this.messages,
errors: this.errors
});
},
requestRestoreKey () {
this.errors.clear();
this.requestNetworkKeys({
userId: this.user.uuid,
networkId: this.networkId,
messages: this.messages,
errors: this.errors
});
},
retrieveRestoreKey () {
this.errors.clear();
this.retrieveNetworkKeys({
userId: this.user.uuid,
networkId: this.networkId,
code: this.restoreKeyCode,
password: this.restoreKeyPassword,
messages: this.messages,
errors: this.errors
});
} }
}, },
created () { created () {
@@ -93,10 +188,12 @@
// console.log('watch.networkNodes: received: '+JSON.stringify(nodes)); // console.log('watch.networkNodes: received: '+JSON.stringify(nodes));
}, },
networkStatuses (stats) { networkStatuses (stats) {
console.log('received stats: '+JSON.stringify(stats));
if (stats) { if (stats) {
// adapted from: https://code-boxx.com/simple-vanilla-javascript-progress-bar/
if (!stats.hasOwnProperty(this.networkId)) return; if (!stats.hasOwnProperty(this.networkId)) return;
if (stats[this.networkId].length === 0) {
clearInterval(this.refresher);
return;
}
this.stats = stats[this.networkId][0]; // todo: when we have multiple nodes, this will need to be changed this.stats = stats[this.networkId][0]; // todo: when we have multiple nodes, this will need to be changed
if (this.stats.percent === 100) { if (this.stats.percent === 100) {
clearInterval(this.refresher); clearInterval(this.refresher);


+ 13
- 17
src/account/NetworksPage.vue View File

@@ -10,27 +10,14 @@
<th nowrap="nowrap">{{messages.label_field_networks_locale}}</th> <th nowrap="nowrap">{{messages.label_field_networks_locale}}</th>
<th nowrap="nowrap">{{messages.label_field_networks_timezone}}</th> <th nowrap="nowrap">{{messages.label_field_networks_timezone}}</th>
<th nowrap="nowrap">{{messages.label_field_networks_object_state}}</th> <th nowrap="nowrap">{{messages.label_field_networks_object_state}}</th>
<th nowrap="nowrap">{{messages.label_field_networks_action_view}}</th>
<th nowrap="nowrap">{{messages.label_field_networks_action_stop}}</th>
<th nowrap="nowrap">{{messages.label_field_networks_action_delete}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="network in networks" :key="network.uuid"> <tr v-for="network in networks" :key="network.uuid">
<td>{{network.name}}.{{network.domainName}}</td>
<td><router-link :to="{ path: '/bubble/'+ network.name }">{{network.name}}.{{network.domainName}}</router-link></td>
<td nowrap="nowrap">{{messages['locale_'+network.locale] || network.locale}}</td> <td nowrap="nowrap">{{messages['locale_'+network.locale] || network.locale}}</td>
<td nowrap="nowrap">{{messages['tz_name_'+network.timezone] || network.timezone}}</td> <td nowrap="nowrap">{{messages['tz_name_'+network.timezone] || network.timezone}}</td>
<td>{{messages['msg_network_state_'+network.state]}}</td> <td>{{messages['msg_network_state_'+network.state]}}</td>
<td><router-link :to="{ path: '/bubble/'+ network.name }">{{messages.table_row_networks_action_view}}</router-link></td>

<td v-if="network.state === 'running'">
<a @click="stopNetwork(network.uuid)" class="text-danger">{{messages.table_row_networks_action_stop}}</a>
</td>
<td v-else></td>

<td>
<a @click="deleteNetwork(network.uuid)" class="text-danger">{{messages.table_row_networks_action_delete}}</a>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -52,9 +39,7 @@


export default { export default {
computed: { computed: {
...mapState({
networks: state => state.networks.networks
}),
...mapState('networks', ['networks']),
...mapState('system', ['messages']) ...mapState('system', ['messages'])
}, },
created () { created () {
@@ -63,6 +48,17 @@
methods: { methods: {
...mapActions('networks', ['getAllNetworks', 'stopNetwork', 'deleteNetwork']), ...mapActions('networks', ['getAllNetworks', 'stopNetwork', 'deleteNetwork']),
...mapGetters('networks', ['loading']) ...mapGetters('networks', ['loading'])
},
watch: {
networks (nets) {
if (nets && nets.length) {
if (nets.length === 0) {
this.$router.replace({path: '/bubble/new'});
} else if (nets.length === 1 && util.currentUser().admin !== true) {
this.$router.replace({path: '/bubble/' + nets[0].name});
}
}
}
} }
}; };
</script> </script>

+ 1
- 1
src/account/NewNetworkPage.vue View File

@@ -228,7 +228,7 @@
accountPlan: { accountPlan: {
name: '', name: '',
domain: '', domain: '',
locale: '',
locale: util.currentUser().locale,
timezone: '', timezone: '',
plan: 'bubble', plan: 'bubble',
footprint: 'Worldwide', footprint: 'Worldwide',


+ 1
- 1
src/account/profile/ActionPage.vue View File

@@ -42,7 +42,7 @@
errors: this.errors errors: this.errors
}); });
} else { } else {
this.$router.push({path:'/me/profile', params: {'action': 'invalid'}});
this.$router.push({path:'/me/profile', params: {action: 'invalid'}});
} }
}, },
watch: { watch: {


+ 1
- 1
src/auth/MultifactorAuthPage.vue View File

@@ -70,7 +70,7 @@
if (isAuthenticator(type)) { if (isAuthenticator(type)) {
console.log('submitVerification: sending authenticator code: '+code); console.log('submitVerification: sending authenticator code: '+code);
this.sendAuthenticatorCode({ this.sendAuthenticatorCode({
uuid: this.username,
userId: this.username,
code: code, code: code,
verifyOnly: false, verifyOnly: false,
messages: this.messages, messages: this.messages,


Loading…
Cancel
Save