From 827cca62733940934a0a38b6cfd1dc04c8d63724 Mon Sep 17 00:00:00 2001 From: axolotle Date: Wed, 10 Nov 2021 18:40:42 +0100 Subject: [PATCH 1/7] delay locales setup to avoid import error --- app/src/i18n/helpers.js | 2 +- app/src/i18n/index.js | 4 ---- app/src/main.js | 11 +++++++---- app/src/store/info.js | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/i18n/helpers.js b/app/src/i18n/helpers.js index 3a4c70ad..773a10e9 100644 --- a/app/src/i18n/helpers.js +++ b/app/src/i18n/helpers.js @@ -1,5 +1,5 @@ -import i18n from '@/i18n' import store from '@/store' +import i18n from '@/i18n' import supportedLocales from './supportedLocales' let dateFnsLocale diff --git a/app/src/i18n/index.js b/app/src/i18n/index.js index 31c039a2..f575be80 100644 --- a/app/src/i18n/index.js +++ b/app/src/i18n/index.js @@ -5,12 +5,8 @@ import Vue from 'vue' import VueI18n from 'vue-i18n' -import { initDefaultLocales } from './helpers' // Plugin Initialization Vue.use(VueI18n) export default new VueI18n({}) - -// Load default locales translations files and setup store data -initDefaultLocales() diff --git a/app/src/main.js b/app/src/main.js index 1fb69763..149c37c3 100644 --- a/app/src/main.js +++ b/app/src/main.js @@ -3,11 +3,12 @@ import App from './App.vue' import BootstrapVue from 'bootstrap-vue' import VueShowdown from 'vue-showdown' -import i18n from './i18n' -import router from './router' import store from './store' +import router from './router' +import i18n from './i18n' import { registerGlobalErrorHandlers } from './api' +import { initDefaultLocales } from './i18n/helpers' Vue.config.productionTip = false @@ -53,11 +54,13 @@ requireComponent.keys().forEach((fileName) => { registerGlobalErrorHandlers() +// Load default locales translations files and setup store data +initDefaultLocales() const app = new Vue({ - i18n, - router, store, + router, + i18n, render: h => h(App) }) diff --git a/app/src/store/info.js b/app/src/store/info.js index 04b335da..9ca3fad7 100644 --- a/app/src/store/info.js +++ b/app/src/store/info.js @@ -1,7 +1,7 @@ import Vue from 'vue' -import api from '@/api' import router from '@/router' import i18n from '@/i18n' +import api from '@/api' import { timeout, isObjectLiteral } from '@/helpers/commons' export default { From 490092afee6f000d6abfeca63322f3692a72de02 Mon Sep 17 00:00:00 2001 From: axolotle Date: Wed, 10 Nov 2021 19:06:58 +0100 Subject: [PATCH 2/7] add component Spinner to separate it from WaitingDisplay --- app/src/components/globals/Spinner.vue | 79 ++++++++++++++++++++++ app/src/views/_partials/WaitingDisplay.vue | 69 +------------------ 2 files changed, 80 insertions(+), 68 deletions(-) create mode 100644 app/src/components/globals/Spinner.vue diff --git a/app/src/components/globals/Spinner.vue b/app/src/components/globals/Spinner.vue new file mode 100644 index 00000000..adfd699e --- /dev/null +++ b/app/src/components/globals/Spinner.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/app/src/views/_partials/WaitingDisplay.vue b/app/src/views/_partials/WaitingDisplay.vue index f9bbb065..78be9fd9 100644 --- a/app/src/views/_partials/WaitingDisplay.vue +++ b/app/src/views/_partials/WaitingDisplay.vue @@ -13,7 +13,7 @@ -
+ - - From 246c001f8415b290c14150edb69c0924b96b5cf6 Mon Sep 17 00:00:00 2001 From: axolotle Date: Wed, 10 Nov 2021 19:10:59 +0100 Subject: [PATCH 3/7] update Login to be able to force-reload the page --- app/src/store/info.js | 1 - app/src/views/Login.vue | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/store/info.js b/app/src/store/info.js index 9ca3fad7..ff3b42c4 100644 --- a/app/src/store/info.js +++ b/app/src/store/info.js @@ -103,7 +103,6 @@ export default { 'CONNECT' ({ commit, dispatch }) { commit('SET_CONNECTED', true) dispatch('GET_YUNOHOST_INFOS') - router.push(router.currentRoute.query.redirect || { name: 'home' }) }, 'RESET_CONNECTED' ({ commit }) { diff --git a/app/src/views/Login.vue b/app/src/views/Login.vue index 190447d6..db50f4e6 100644 --- a/app/src/views/Login.vue +++ b/app/src/views/Login.vue @@ -33,7 +33,8 @@ export default { name: 'Login', props: { - skipInstallCheck: { type: Boolean, default: false } + skipInstallCheck: { type: Boolean, default: false }, + forceReload: { type: Boolean, default: false } }, data () { @@ -47,7 +48,13 @@ export default { methods: { login () { - this.$store.dispatch('LOGIN', this.password).catch(err => { + this.$store.dispatch('LOGIN', this.password).then(() => { + if (this.forceReload) { + window.location.href = '/yunohost/admin/' + } else { + this.$router.push(this.$router.currentRoute.query.redirect || { name: 'home' }) + } + }).catch(err => { if (err.name !== 'APIUnauthorizedError') throw err this.isValid = false }) From c259a97a746a9af50b84023953e290835ef0520f Mon Sep 17 00:00:00 2001 From: axolotle Date: Wed, 10 Nov 2021 19:14:50 +0100 Subject: [PATCH 4/7] add ReconnectingDisplay and reconnection mecanism --- app/src/api/api.js | 29 +++++++ app/src/i18n/locales/en.json | 11 +++ app/src/store/info.js | 15 +++- .../views/_partials/ReconnectingDisplay.vue | 79 +++++++++++++++++++ app/src/views/_partials/index.js | 1 + 5 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 app/src/views/_partials/ReconnectingDisplay.vue diff --git a/app/src/api/api.js b/app/src/api/api.js index 86507812..c33986e5 100644 --- a/app/src/api/api.js +++ b/app/src/api/api.js @@ -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} + */ + 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) + }) } } diff --git a/app/src/i18n/locales/en.json b/app/src/i18n/locales/en.json index 6437172d..394335c5 100644 --- a/app/src/i18n/locales/en.json +++ b/app/src/i18n/locales/en.json @@ -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}'", diff --git a/app/src/store/info.js b/app/src/store/info.js index ff3b42c4..d65af363 100644 --- a/app/src/store/info.js +++ b/app/src/store/info.js @@ -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 => { diff --git a/app/src/views/_partials/ReconnectingDisplay.vue b/app/src/views/_partials/ReconnectingDisplay.vue new file mode 100644 index 00000000..125dd097 --- /dev/null +++ b/app/src/views/_partials/ReconnectingDisplay.vue @@ -0,0 +1,79 @@ + + + diff --git a/app/src/views/_partials/index.js b/app/src/views/_partials/index.js index dfcda23a..7ea629c8 100644 --- a/app/src/views/_partials/index.js +++ b/app/src/views/_partials/index.js @@ -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' From 335d38bd5dd7d0211b2e365f4aa67625ca951c63 Mon Sep 17 00:00:00 2001 From: axolotle Date: Wed, 10 Nov 2021 19:15:50 +0100 Subject: [PATCH 5/7] update ViewLockOverlay to receive ReconnectingDisplay and use reconnection in SystemUpdate --- app/src/views/_partials/ViewLockOverlay.vue | 12 ++++++++---- app/src/views/update/SystemUpdate.vue | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/src/views/_partials/ViewLockOverlay.vue b/app/src/views/_partials/ViewLockOverlay.vue index a4f61faf..efe6ba66 100644 --- a/app/src/views/_partials/ViewLockOverlay.vue +++ b/app/src/views/_partials/ViewLockOverlay.vue @@ -2,7 +2,7 @@ @@ -20,7 +20,7 @@ From 0b99e58e6d29b31bcdcaa74b91830916f00fda2f Mon Sep 17 00:00:00 2001 From: axolotle Date: Wed, 10 Nov 2021 20:40:35 +0100 Subject: [PATCH 7/7] update SystemUpgrade and ToolPower component to use ReconnectingDisplay --- app/src/i18n/locales/en.json | 7 +- app/src/views/tool/ToolPower.vue | 98 +++++++-------------------- app/src/views/update/SystemUpdate.vue | 2 +- 3 files changed, 27 insertions(+), 80 deletions(-) diff --git a/app/src/i18n/locales/en.json b/app/src/i18n/locales/en.json index 394335c5..54faf2f6 100644 --- a/app/src/i18n/locales/en.json +++ b/app/src/i18n/locales/en.json @@ -27,6 +27,8 @@ "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.", + "reboot": "Your server is rebooting and will not be reachable for some time. A login prompt will be available as soon as the server is reachable.", + "shutdown": "Your server is shutting down and is no longer reachable. Turn it back on and a login prompt will be available as soon as the server is reachable.", "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" @@ -484,15 +486,10 @@ "tools_adminpw": "Change administration password", "tools_adminpw_current": "Current password", "tools_adminpw_current_placeholder": "Enter your current password", - "tools_power_up": "Your server seems to be accessible, you can now try to login.", "tools_reboot": "Reboot your server", "tools_reboot_btn": "Reboot", - "tools_reboot_done": "Rebooting...", - "tools_rebooting": "Your server is rebooting. To return to the web administration interface you need to wait for your server to be up. You can wait for the login form to show up or check by refreshing this page (F5).", "tools_shutdown": "Shutdown your server", "tools_shutdown_btn": "Shutdown", - "tools_shutdown_done": "Shutting down...", - "tools_shuttingdown": "Your server is powering off. As long as your server is off, you won't be able to use the web administration.", "tools_shutdown_reboot": "Shutdown/Reboot", "tools_webadmin": { "language": "Language", diff --git a/app/src/views/tool/ToolPower.vue b/app/src/views/tool/ToolPower.vue index cd2df63d..119daa4b 100644 --- a/app/src/views/tool/ToolPower.vue +++ b/app/src/views/tool/ToolPower.vue @@ -1,63 +1,35 @@