@@ -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, '_'); | |||
@@ -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)); | |||
} | |||
@@ -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; | |||
}, | |||
@@ -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: { | |||
@@ -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"> | |||