Browse Source

add support for downloading VPN configs

pull/1/head
Jonathan Cobb 5 years ago
parent
commit
10a000cac0
5 changed files with 155 additions and 6 deletions
  1. +23
    -0
      src/_helpers/util.js
  2. +10
    -0
      src/_services/device.service.js
  3. +50
    -3
      src/_store/devices.module.js
  4. +60
    -3
      src/account/DevicesPage.vue
  5. +12
    -0
      src/index.html

+ 23
- 0
src/_helpers/util.js View File

@@ -132,6 +132,29 @@ export const util = {
}
},

handlePlaintextResponse: function(messages, errors) {
return function (response) {
return response.text().then(text => {
if (!response.ok) {
if (response.status === 404) {
// todo: show nicer error message
const errData = JSON.parse(''+text);
console.log('handleCrudResponse: received 404: ' + text);

} else if (response.status === 422) {
const errData = JSON.parse(''+text);
console.log('handleCrudResponse: received 422, error: ' + text);
util.setValidationErrors(errData, messages, errors);
}

const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return text;
});
}
},

setValidationErrors: function(data, messages, errors) {
for (let i=0; i<data.length; i++) {
const messageTemplate = data[i].messageTemplate.replace(/\./g, '_');


+ 10
- 0
src/_services/device.service.js View File

@@ -3,6 +3,8 @@ import { util } from '../_helpers';

export const deviceService = {
getDevicesByUserId,
getDeviceQRcodeById,
getDeviceVPNconfById,
addDeviceByUserId,
removeDeviceByUserId
};
@@ -11,6 +13,14 @@ function getDevicesByUserId(userId, messages, errors) {
return fetch(`${config.apiUrl}/users/${userId}/devices`, util.getWithAuth()).then(util.handleCrudResponse(messages, errors));
}

function getDeviceQRcodeById(userId, deviceId, messages, errors) {
return fetch(`${config.apiUrl}/users/${userId}/devices/${deviceId}/vpn/QR.png.base64`, util.getWithAuth()).then(util.handlePlaintextResponse(messages, errors));
}

function getDeviceVPNconfById(userId, deviceId, messages, errors) {
return fetch(`${config.apiUrl}/users/${userId}/devices/${deviceId}/vpn/vpn.conf.base64`, util.getWithAuth()).then(util.handlePlaintextResponse(messages, errors));
}

function addDeviceByUserId(userId, device, messages, errors) {
return fetch(`${config.apiUrl}/users/${userId}/devices`, util.putWithAuth(device)).then(util.handleCrudResponse(messages, errors));
}


+ 50
- 3
src/_store/devices.module.js View File

@@ -3,11 +3,14 @@ import { util } from '../_helpers';

const state = {
loading: {
devices: false, addDevice: false, removeDevice: false
devices: false, addDevice: false, removeDevice: false,
qrCode: false, vpnConf: false
},
error: null,
devices: [],
device: null
device: null,
qrCodeImageBase64: null,
vpnConfBase64: null
};

const actions = {
@@ -20,6 +23,24 @@ const actions = {
);
},

getDeviceQRcodeById({ commit }, {userId, deviceId, messages, errors}) {
commit('getDeviceQRcodeByIdRequest');
deviceService.getDeviceQRcodeById(userId, deviceId, messages, errors)
.then(
qrData => commit('getDeviceQRcodeByIdSuccess', qrData),
error => commit('getDeviceQRcodeByIdFailure', error)
);
},

getDeviceVPNconfById({ commit }, {userId, deviceId, messages, errors}) {
commit('getDeviceVPNconfByIdRequest');
deviceService.getDeviceVPNconfById(userId, deviceId, messages, errors)
.then(
conf => commit('getDeviceVPNconfByIdSuccess', conf),
error => commit('getDeviceVPNconfByIdFailure', error)
);
},

addDeviceByUserId({ commit }, {userId, device, messages, errors}) {
commit('addDeviceByUserIdRequest');
deviceService.addDeviceByUserId(userId, device, messages, errors)
@@ -48,7 +69,33 @@ const mutations = {
state.devices = devices;
},
getDevicesByUserIdFailure(state, error) {
state.loading.users = false;
state.loading.devices = false;
state.error = error;
},

getDeviceQRcodeByIdRequest(state) {
state.loading.qrCode = true;
state.qrCodeImageBase64 = null;
},
getDeviceQRcodeByIdSuccess(state, qrCodeImageBase64) {
state.loading.qrCode = false;
state.qrCodeImageBase64 = qrCodeImageBase64;
},
getDeviceQRcodeByIdFailure(state, error) {
state.loading.qrCode = false;
state.error = error;
},

getDeviceVPNconfByIdRequest(state) {
state.loading.vpnConf = true;
state.vpnConfBase64 = null;
},
getDeviceVPNconfByIdSuccess(state, confData) {
state.loading.vpnConf = false;
state.vpnConfBase64 = confData;
},
getDeviceVPNconfByIdFailure(state, error) {
state.loading.vpnConf = false;
state.error = error;
},



+ 60
- 3
src/account/DevicesPage.vue View File

@@ -8,6 +8,7 @@
<tr>
<th nowrap="nowrap">{{messages.label_field_device_name}}</th>
<!-- <th nowrap="nowrap">{{messages.label_field_device_enabled}}</th>-->
<th>{{messages.label_field_device_vpn_config}}</th>
<th>{{messages.label_field_device_ctime}}</th>
<th><!-- delete --></th>
</tr>
@@ -17,6 +18,28 @@
<td nowrap="nowrap">{{device.name}}</td>
<!-- <td>{{messages['message_'+device.enabled]}}</td>-->
<td nowrap="nowrap">{{messages.label_device_ctime_format.parseDateMessage(device.ctime, messages)}}</td>
<td>
<div v-if="displayVpnConfig[device.uuid] === true" class="device-vpn-config-div">
<h3>{{device.name}}</h3>
<hr/>

<div v-if="qrCodeImageBase64">
<img :src="'data:image/png;base64,'+qrCodeImageBase64"/>
</div>
<div v-if="errors.has('deviceQRcode')" class="invalid-feedback d-block">{{ errors.first('deviceQRcode') }}</div>

<hr/>

<button v-if="vpnConfBase64" @click="downloadURI('data:text/plain;base64,'+vpnConfBase64, 'vpn.conf')">{{messages.message_device_vpn_download_conf}}</button>
<div v-if="errors.has('deviceVpnConf')" class="invalid-feedback d-block">{{ errors.first('deviceVpnConf') }}</div>

<hr/>
<button @click="hideVpnConfig()" class="btn btn-primary">{{messages.button_label_close_device_vpn_config}}</button>
</div>
<div v-else>
<button @click="showVpnConfig(device.uuid)">{{messages.message_device_vpn_show_config}}</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>
@@ -57,17 +80,20 @@
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { util } from '../_helpers';
import config from 'config';

export default {
data () {
return {
userId: util.currentUser().uuid,
submitted: false,
deviceName: null
deviceName: null,
displayVpnConfig: {},
config: config
};
},
computed: {
...mapState('devices', ['devices', 'device']),
...mapState('devices', ['devices', 'device', 'qrCodeImageBase64', 'vpnConfBase64']),
...mapState('system', ['messages'])
},
created () {
@@ -78,7 +104,10 @@
});
},
methods: {
...mapActions('devices', ['getDevicesByUserId', 'addDeviceByUserId', 'removeDeviceByUserId']),
...mapActions('devices', [
'getDevicesByUserId', 'addDeviceByUserId', 'removeDeviceByUserId',
'getDeviceQRcodeById', 'getDeviceVPNconfById'
]),
...mapGetters('devices', ['loading']),
addDevice () {
this.errors.clear();
@@ -101,6 +130,34 @@
messages: this.messages,
errors: this.errors
});
},
showVpnConfig (id) {
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 = {}; },

downloadURI(uri, name) { // adapted from https://stackoverflow.com/a/15832662/1251543
let link = document.createElement("a");
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
},
watch: {


+ 12
- 0
src/index.html View File

@@ -44,6 +44,18 @@
left:0;
z-index:1000;
}

.device-vpn-config-div {
width: 400px;
height: 600px;
background-color: white;
position: absolute;
top:0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
</style>

<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">


Loading…
Cancel
Save