Ver a proveniência

Merge branch 'master' into kris/restore_from_backup

pull/44/head
Kristijan Mitrovic há 4 anos
ascendente
cometimento
4b5160f601
2 ficheiros alterados com 603 adições e 2 eliminações
  1. +594
    -0
      src/_pages/main/account/Devices.vue
  2. +9
    -2
      src/_router/index.js

+ 594
- 0
src/_pages/main/account/Devices.vue Ver ficheiro

@@ -0,0 +1,594 @@
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ -->
<template>
<div class="bubble-form">
<em v-if="loading && loading.devices">{{ messages.loading_devices }}</em>
<div v-if="devices && devices.length > 0">
<h2>{{ messages.table_title_devices }}</h2>
<table border="1">
<thead>
<tr>
<th nowrap="nowrap">{{ messages.label_field_device_name }}</th>
<!-- <th nowrap="nowrap">{{messages.label_field_device_type}}</th>-->
<th nowrap="nowrap">{{ messages.label_field_device_app }}</th>
<!-- <th nowrap="nowrap">{{messages.label_field_device_enabled}}</th>-->
<th>{{ messages.label_field_device_vpn_config }}</th>
<th nowrap="nowrap">
{{ messages.label_field_device_certificate }}
</th>
<th nowrap="nowrap">
{{ messages.label_field_device_security_level }}
</th>
<th nowrap="nowrap">
{{ messages.label_field_device_connection }}
</th>
<!-- <th>{{messages.label_field_device_ctime}}</th>-->
<th>{{ messages.label_field_device_help }}</th>
<th><!-- delete --></th>
</tr>
</thead>
<tbody>
<tr v-for="device in devices">
<td nowrap="nowrap">{{ device.name }}</td>
<!-- <td nowrap="nowrap">-->
<!-- <i :class="messages['device_type_icon_'+device.deviceType]+' bubble-app-icon'"></i><br/>-->
<!-- {{messages['device_type_'+device.deviceType]}}-->
<!-- </td>-->
<td align="center">
<a
v-if="appLinks"
target="_blank"
rel="noopener noreferrer"
:href="appLinks[device.deviceType]"
>
<i
:class="
messages['device_type_icon_' + device.deviceType] +
' bubble-app-icon'
"
></i
><br />
{{ messages.message_download_app }}
</a>
</td>
<!-- <td>{{messages['message_'+device.enabled]}}</td>-->
<td>
<div
v-if="displayVpnConfig[device.uuid] === true"
class="device-vpn-config-div"
>
<h3>{{ device.name }}</h3>
<hr />

<div
v-if="
qrCodeImageBase64 &&
messages['device_type_vpn_' + device.deviceType] ===
'show_qr_code'
"
>
<img :src="'data:image/png;base64,' + qrCodeImageBase64" />
<div
v-if="errors.has('deviceQRcode')"
class="invalid-feedback d-block"
>
{{ errors.first('deviceQRcode') }}
</div>
</div>
<div
v-else-if="
vpnConfBase64 &&
messages['device_type_vpn_' + device.deviceType] ===
'download_conf'
"
>
<Button
color="default"
v-if="vpnConfBase64"
@click="
util.downloadURI(
'data:text/plain;base64,' + vpnConfBase64,
vpnConfFileName
)
"
>
{{ messages.message_device_vpn_download_conf }}
</Button>
<div
v-if="errors.has('deviceVpnConf')"
class="invalid-feedback d-block"
>
{{ errors.first('deviceVpnConf') }}
</div>
</div>
<div v-else>
<h5>{{ messages.message_device_vpn_unavailable }}</h5>
</div>
<hr />
<Button
color="default"
@click="hideVpnConfig()"
class="btn btn-primary"
>
{{ messages.button_label_close_device_vpn_config }}
</Button>
</div>
<div
v-else-if="
messages[
'!button_label_vpn_config_' +
messages['device_type_vpn_' + device.deviceType]
]
"
>
<Button color="default" @click="showVpnConfig(device.uuid)">
{{
messages[
'button_label_vpn_config_' +
messages['device_type_vpn_' + device.deviceType]
]
}}
</Button>
</div>
<div v-else>
{{ messages.message_device_vpn_unavailable }}
</div>
</td>
<td>
<a :href="'/api/auth/cacert?deviceType=' + device.deviceType">
{{ messages.message_download_ca_cert }}
</a>
</td>
<td>
<form v-if="configs.securityLevels" @submit.prevent="false">
<input type="hidden" name="deviceId" :value="device.uuid" />
<v-select
v-model="device.securityLevel"
@change="setSecurityLevel($event)"
:options="configs.securityLevels"
>
<template v-slot:option="option">
{{ messages['device_security_level_' + option.label] }}
</template>
<template v-slot:selected-option="option">
{{ messages['device_security_level_' + option.label] }}
</template>
</v-select>
</form>
</td>
<td class="device-connection-status">
<div v-if="device.status.ip">
{{ device.status.ip }}
<div v-if="device.status.location">
<hr />
<span
v-if="
device.status.location.city &&
device.status.location.region &&
device.status.location.country
"
>
{{ device.status.location.city }},
{{ device.status.location.region }},
{{ messages['country_' + device.status.location.country] }}
</span>
<span
v-else-if="
device.status.location.region &&
device.status.location.country
"
>
{{ device.status.location.region }},
{{ messages['country_' + device.status.location.country] }}
</span>
<span
v-else-if="
device.status.location.city &&
device.status.location.country
"
>
{{ device.status.location.city }},
{{ messages['country_' + device.status.location.country] }}
</span>
<span v-else-if="device.status.location.country">
{{
messages['country_' + device.status.location.country]
}}</span
>
<span
v-else-if="
device.status.location.city &&
device.status.location.region
"
>
{{ device.status.location.city }},
{{ device.status.location.region }}
</span>
<span v-else-if="device.status.location.region">
{{ device.status.location.region }}</span
>
<span v-else-if="device.status.location.city">
{{ device.status.location.city }}</span
>
</div>
</div>
<div
v-if="device.status.bytesSent || device.status.bytesReceived"
>
<hr />
<div v-if="device.status.bytesSent">
{{ device.status.bytesSent }}{{ device.status.sentUnits }}
{{ messages.label_field_device_transfer_sent }}
</div>
<div v-if="device.status.bytesReceived">
{{ device.status.bytesReceived
}}{{ device.status.receivedUnits }}
{{ messages.label_field_device_transfer_received }}
</div>
</div>
<div v-if="device.status.lastHandshakeTime">
<hr />
{{ messages.label_field_device_connection_handshake }}:
<span
v-if="
device.status.lastHandshakeHours &&
device.status.lastHandshakeMinutes
"
>
{{ device.status.lastHandshakeHours
}}{{ messages.units_hours_short }},
{{ device.status.lastHandshakeMinutes
}}{{ messages.units_minutes_short }}
</span>
<span v-else-if="device.status.lastHandshakeHours">
{{ device.status.lastHandshakeHours }}
{{ messages.units_hours_short }}
</span>
<span
v-else-if="
device.status.lastHandshakeMinutes &&
device.status.lastHandshakeSeconds
"
>
{{ device.status.lastHandshakeMinutes }}
{{ messages.units_minutes_short }},
{{ device.status.lastHandshakeSeconds }}
{{ messages.units_seconds_short }}
</span>
<span v-else-if="device.status.lastHandshakeMinutes">
{{ device.status.lastHandshakeMinutes }}
{{ messages.units_minutes_short }}
</span>
<span v-else-if="device.status.lastHandshakeSeconds">
{{ device.status.lastHandshakeSeconds }}
{{ messages.units_seconds_short }}
</span>
{{ messages.label_field_device_connection_handshake_ago }}
</div>
</td>
<!-- <td nowrap="nowrap">{{messages.label_device_ctime_format.parseDateMessage(device.ctime, messages)}}</td>-->
<td>
<div
v-if="displayDeviceHelp[device.uuid] === true"
class="device-help-div"
>
<div
v-html="
messages['device_type_' + device.deviceType + '_help_html']
"
></div>
<Button
color="default"
@click="hideDeviceHelp()"
class="btn btn-primary"
>
{{ messages.button_label_close_device_help }}
</Button>
</div>
<div v-else>
<Button color="default" @click="showDeviceHelp(device.uuid)">
{{ messages.label_field_device_help }}
</Button>
</div>
</td>
<td>
<i
@click="removeDevice(device.uuid)"
aria-hidden="true"
:class="messages.button_label_remove_device_icon"
:title="messages.button_label_remove_device"
></i>
<span class="sr-only">{{
messages.button_label_remove_device
}}</span>
</td>
</tr>
</tbody>
</table>
</div>

<hr />

<form @submit.prevent="addDevice()">
<h4>{{ messages.form_title_add_device }}</h4>

<div class="form-group">
<Input
v-model="deviceName"
name="name"
class="form-control"
:placeholder="messages.label_field_device_name"
/>
<div
v-if="submitted && errors.has('name')"
class="invalid-feedback d-block"
>
{{ errors.first('name') }}
</div>
<div
v-if="submitted && errors.has('deviceName')"
class="invalid-feedback d-block"
>
{{ errors.first('deviceName') }}
</div>
</div>

<div class="form-group" v-if="addableDeviceTypes">
<v-select
v-model="deviceType"
name="deviceType"
:placeholder="messages.label_field_device_type"
:options="addableDeviceTypes"
>
<template v-slot:option="option">
{{ messages['device_type_' + option.label] }}
</template>
<template v-slot:selected-option="option">
{{ messages.label_field_device_type }}:
{{ messages['device_type_' + option.label] }}
</template>
</v-select>
</div>

<hr />
<div class="form-group">
<Button
color="default"
type="submit"
class="btn btn-primary"
:disabled="loading || !addDeviceReady"
>
{{ messages.button_label_add_device }}
</Button>
<img v-show="loading" :src="loadingImgSrc" />
</div>
</form>

<hr />

<div v-if="certDeviceTypes">
<table border="0" width="100%">
<tr>
<td colspan="5">
<h4>{{ messages.message_download_ca_cert }}</h4>
</td>
</tr>
<tr>
<td
v-for="certDevice in certDeviceTypes"
align="center"
:width="certDeviceWidth + '%'"
>
<a :href="'/api/auth/cacert?deviceType=' + certDevice">
<i
:class="
messages['device_type_icon_' + certDevice] +
' bubble-app-icon'
"
></i
><br />
{{ messages['device_type_' + certDevice] }}
</a>
</td>
</tr>
</table>

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

<style lang="scss" scoped>
@import '../../../_scss/components/form';

.bubble-form {
width: 800px;
}
</style>

<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { util } from '~/_helpers';
import config from 'config';
import { loadingImgSrc } from '~/_store';

import { Input, Button } from '~/_components/shared';

export default {
components: {
Input,
Button,
},
data() {
return {
user: util.currentUser(),
userId: util.currentUser().uuid,
submitted: false,
deviceName: null,
deviceType: null,
displayVpnConfig: {},
displayDeviceHelp: {},
config: config,
loadingImgSrc: loadingImgSrc,
util: util,
};
},
computed: {
...mapState('devices', [
'deviceTypes',
'devices',
'device',
'qrCodeImageBase64',
'vpnConfBase64',
]),
...mapState('system', ['messages', 'appLinks', 'configs']),
...mapGetters('devices', ['loading']),
vpnConfFileName: function() {
if (this.configs && this.configs.networkUuid) {
return 'bubble-' + this.configs.networkUuid.split('-')[0] + '-vpn.conf';
} else {
return 'vpn.conf';
}
},
addDeviceReady: function() {
return (
this.deviceName !== null &&
this.deviceName !== '' &&
this.deviceType !== null &&
this.deviceType !== ''
);
},
addableDeviceTypes: function() {
if (this.messages && this.messages['!addable_device_types']) {
return this.messages['addable_device_types'].split('|');
}
return [];
},
addableDeviceWidth: function() {
return 100.0 / this.addableDeviceTypes.length;
},
certDeviceTypes: function() {
if (this.messages && this.messages['!cert_device_types']) {
return this.messages['cert_device_types'].split('|');
}
return [];
},
certDeviceWidth: function() {
return 100.0 / this.certDeviceTypes.length;
},
},
created() {
this.getDevicesByUserId({
userId: this.userId,
messages: this.messages,
errors: this.errors,
});
this.getAllDeviceTypesByUserId({
userId: this.userId,
messages: this.messages,
errors: this.errors,
});
const user = util.currentUser();
if (user) this.getAppLinks(user.locale);
this.loadSystemConfigs();
},
mounted() {
window.addEventListener('keyup', (ev) => {
if (ev.key === 'Escape') {
this.hideVpnConfig();
this.hideDeviceHelp();
}
});
},
methods: {
...mapActions('devices', [
'getAllDeviceTypesByUserId',
'getDevicesByUserId',
'addDeviceByUserId',
'removeDeviceByUserId',
'getDeviceQRcodeById',
'getDeviceVPNconfById',
'setDeviceSecurityLevel',
]),
...mapActions('system', ['getAppLinks', 'loadSystemConfigs']),
addDevice() {
console.log('add device');
this.errors.clear();
this.submitted = true;
this.addDeviceByUserId({
userId: this.userId,
device: {
name: this.deviceName,
deviceType: this.deviceType,
},
messages: this.messages,
errors: this.errors,
});
},
removeDevice(id) {
this.errors.clear();
this.submitted = true;
this.removeDeviceByUserId({
userId: this.userId,
deviceId: id,
messages: this.messages,
errors: this.errors,
});
},
setSecurityLevel(event) {
this.setDeviceSecurityLevel({
userId: this.userId,
deviceId: event.target.form.deviceId.value,
securityLevel: event.target.value,
messages: this.messages,
errors: this.errors,
});
},
showVpnConfig(id) {
this.hideDeviceHelp();
const show = {};
show[id] = true;
this.errors.clear();
this.getDeviceQRcodeById({
userId: this.userId,
deviceId: id,
messages: this.messages,
errors: this.errors,
});
this.getDeviceVPNconfById({
userId: this.userId,
deviceId: id,
messages: this.messages,
errors: this.errors,
});
this.displayVpnConfig = Object.assign({}, show);
},
hideVpnConfig() {
this.displayVpnConfig = {};
},

showDeviceHelp(id) {
this.hideVpnConfig();
this.displayDeviceHelp = {};
this.displayDeviceHelp[id] = true;
},
hideDeviceHelp() {
this.displayDeviceHelp = {};
},
},
watch: {
device(dev) {
if (dev) {
// after device added, clear device fields
this.deviceName = null;
this.deviceType = null;
if (dev.uuid) this.showVpnConfig(dev.uuid);
this.getDevicesByUserId({
userId: this.userId,
messages: this.messages,
errors: this.errors,
});
}
},
},
};
</script>

+ 9
- 2
src/_router/index.js Ver ficheiro

@@ -94,7 +94,8 @@ export const router = new Router({
},
{
path: 'me/change-password',
component: () => import('~/_pages/main/account/ChangePassword'),
component: () =>
import('~/_pages/main/account/ChangePassword'),
},
{
path: 'me/set-password/:code',
@@ -106,7 +107,8 @@ export const router = new Router({
},
{
path: 'me/payment',
component: () => import('~/_pages/main/account/PaymentMethods'),
component: () =>
import('~/_pages/main/account/PaymentMethods'),
},
{
path: 'me/bills',
@@ -116,6 +118,11 @@ export const router = new Router({
path: 'me/policy',
component: () => import('~/_pages/main/account/Policy'),
},

{
path: 'devices',
component: () => import('~/_pages/main/account/Devices'),
},
],
},
{


Carregando…
Cancelar
Guardar