mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
add ReconnectingDisplay and reconnection mecanism
This commit is contained in:
parent
246c001f84
commit
c259a97a74
5 changed files with 133 additions and 2 deletions
|
@ -189,5 +189,34 @@ export default {
|
|||
delete (uri, data = {}, humanKey = null, options = {}) {
|
||||
if (typeof uri === 'string') return this.fetch('DELETE', uri, data, humanKey, options)
|
||||
return store.dispatch('DELETE', { ...uri, data, humanKey, options })
|
||||
},
|
||||
|
||||
/**
|
||||
* Api reconnection helper. Resolve when server is reachable or fail after n attemps
|
||||
*
|
||||
* @param {Number} attemps - number of attemps before rejecting
|
||||
* @param {Number} delay - delay between calls to the API in ms.
|
||||
* @param {Number} initialDelay - delay before calling the API for the first time in ms.
|
||||
* @return {Promise<undefined|Error>}
|
||||
*/
|
||||
tryToReconnect ({ attemps = 1, delay = 2000, initialDelay = 0 } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const api = this
|
||||
|
||||
function reconnect (n) {
|
||||
api.get('logout', {}, { key: 'reconnecting' }).then(resolve).catch(err => {
|
||||
if (err.name === 'APIUnauthorizedError') {
|
||||
resolve()
|
||||
} else if (n < 1) {
|
||||
reject(err)
|
||||
} else {
|
||||
setTimeout(() => reconnect(n - 1), delay)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (initialDelay > 0) setTimeout(() => reconnect(attemps), initialDelay)
|
||||
else reconnect(attemps)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,15 @@
|
|||
"pending": "In progress",
|
||||
"success": "Successfully completed",
|
||||
"warning": "Successfully completed with errors or alerts"
|
||||
},
|
||||
"reconnecting": {
|
||||
"title": "Trying to communicate with the server...",
|
||||
"failed": "Looks like the server is not responding. You can try to reconnect again or try to run `systemctl restart yunohost-api` thru ssh.",
|
||||
"reason": {
|
||||
"unknown": "Connection with the server has been closed for unknown reasons.",
|
||||
"upgrade_system": "Connection with the server has been closed due to yunohost upgrade. Waiting for the server to be reachable again…"
|
||||
},
|
||||
"success": "The server is now reachable! You can try to login"
|
||||
}
|
||||
},
|
||||
"api_error": {
|
||||
|
@ -363,6 +372,7 @@
|
|||
"rerun_diagnosis": "Rerun diagnosis",
|
||||
"restore": "Restore",
|
||||
"restart": "Restart",
|
||||
"retry": "Retry",
|
||||
"human_routes": {
|
||||
"adminpw": "Change admin password",
|
||||
"apps": {
|
||||
|
@ -422,6 +432,7 @@
|
|||
},
|
||||
"postinstall": "Run the post-install",
|
||||
"reboot": "Reboot the server",
|
||||
"reconnecting": "Reconnecting",
|
||||
"services": {
|
||||
"restart": "Restart the service '{name}'",
|
||||
"start": "Start the service '{name}'",
|
||||
|
|
|
@ -10,6 +10,7 @@ export default {
|
|||
connected: localStorage.getItem('connected') === 'true', // Boolean
|
||||
yunohost: null, // Object { version, repo }
|
||||
waiting: false, // Boolean
|
||||
reconnecting: false, // Boolean
|
||||
history: [], // Array of `request`
|
||||
requests: [], // Array of `request`
|
||||
error: null, // null || request
|
||||
|
@ -31,6 +32,10 @@ export default {
|
|||
state.waiting = boolean
|
||||
},
|
||||
|
||||
'SET_RECONNECTING' (state, boolean) {
|
||||
state.reconnecting = boolean
|
||||
},
|
||||
|
||||
'ADD_REQUEST' (state, request) {
|
||||
if (state.requests.length > 10) {
|
||||
// We do not remove requests right after it resolves since an error might bring
|
||||
|
@ -133,6 +138,11 @@ export default {
|
|||
return api.get('logout')
|
||||
},
|
||||
|
||||
'TRY_TO_RECONNECT' ({ commit, dispatch }) {
|
||||
commit('SET_RECONNECTING', true)
|
||||
dispatch('RESET_CONNECTED')
|
||||
},
|
||||
|
||||
'GET_YUNOHOST_INFOS' ({ commit }) {
|
||||
return api.get('versions').then(versions => {
|
||||
commit('SET_YUNOHOST_INFOS', versions.yunohost)
|
||||
|
@ -144,7 +154,7 @@ export default {
|
|||
const { key, ...args } = isObjectLiteral(humanKey) ? humanKey : { key: humanKey }
|
||||
const humanRoute = key ? i18n.t('human_routes.' + key, args) : `[${method}] /${uri}`
|
||||
|
||||
let request = { method, uri, humanRoute, initial, status: 'pending' }
|
||||
let request = { method, uri, humanRouteKey: key, humanRoute, initial, status: 'pending' }
|
||||
if (websocket) {
|
||||
request = { ...request, messages: [], date: Date.now(), warnings: 0, errors: 0 }
|
||||
commit('ADD_HISTORY_ACTION', request)
|
||||
|
@ -252,7 +262,7 @@ export default {
|
|||
|
||||
'DISMISS_WARNING' ({ commit, state }, request) {
|
||||
commit('SET_WAITING', false)
|
||||
delete request.showWarningMessage
|
||||
Vue.delete(request, 'showWarningMessage')
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -262,6 +272,7 @@ export default {
|
|||
yunohost: state => state.yunohost,
|
||||
error: state => state.error,
|
||||
waiting: state => state.waiting,
|
||||
reconnecting: state => state.reconnecting,
|
||||
history: state => state.history,
|
||||
lastAction: state => state.history[state.history.length - 1],
|
||||
currentRequest: state => {
|
||||
|
|
79
app/src/views/_partials/ReconnectingDisplay.vue
Normal file
79
app/src/views/_partials/ReconnectingDisplay.vue
Normal file
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<!-- This card receives style from `ViewLockOverlay` if used inside it -->
|
||||
<b-card-body>
|
||||
<b-card-title class="text-center my-4" v-t="'api.reconnecting.title'" />
|
||||
|
||||
<template v-if="status === 'reconnecting'">
|
||||
<spinner class="mb-4" />
|
||||
|
||||
<b-alert
|
||||
v-if="origin"
|
||||
v-t="'api.reconnecting.reason.' + origin"
|
||||
:variant="origin === 'unknow' ? 'warning' : 'info'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="status === 'failed'">
|
||||
<b-alert variant="danger">
|
||||
<markdown-item :label="$t('api.reconnecting.failed')" />
|
||||
</b-alert>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<b-button
|
||||
variant="success" v-t="'retry'" class="ml-auto"
|
||||
@click="tryToReconnect()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="status === 'success'">
|
||||
<b-alert variant="success" v-t="'api.reconnecting.success'" />
|
||||
|
||||
<login-view skip-install-check force-reload />
|
||||
</template>
|
||||
</b-card-body>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
import api from '@/api'
|
||||
import LoginView from '@/views/Login'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'ReconnectingDisplay',
|
||||
|
||||
components: {
|
||||
LoginView
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
status: 'reconnecting',
|
||||
origin: undefined
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['currentRequest'])
|
||||
},
|
||||
|
||||
methods: {
|
||||
tryToReconnect ({ initialDelay = 0 } = {}) {
|
||||
this.status = 'reconnecting'
|
||||
api.tryToReconnect({ initialDelay }).then(() => {
|
||||
this.status = 'success'
|
||||
}).catch(() => {
|
||||
this.status = 'failed'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
const origin = this.currentRequest.humanRouteKey
|
||||
this.origin = ['upgrade.system'].includes(origin) ? origin.replace('.', '_') : 'unknown'
|
||||
this.tryToReconnect({ initialDelay: 2000 })
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,6 +1,7 @@
|
|||
export { default as ErrorDisplay } from './ErrorDisplay'
|
||||
export { default as WarningDisplay } from './WarningDisplay'
|
||||
export { default as WaitingDisplay } from './WaitingDisplay'
|
||||
export { default as ReconnectingDisplay } from './ReconnectingDisplay'
|
||||
|
||||
export { default as HistoryConsole } from './HistoryConsole'
|
||||
export { default as ViewLockOverlay } from './ViewLockOverlay'
|
||||
|
|
Loading…
Reference in a new issue