feat: do api url check before anything else and move ready check to wrapper component

This commit is contained in:
kolaente 2021-10-31 21:04:00 +01:00
parent eef2a3d7cc
commit dc30de9176
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
5 changed files with 180 additions and 149 deletions

View File

@ -1,26 +1,27 @@
<template> <template>
<div :class="{'is-touch': isTouch}" v-if="ready"> <ready>
<div :class="{'is-hidden': !online}"> <div :class="{'is-touch': isTouch}">
<!-- This is a workaround to get the sw to "see" the to-be-cached version of the offline background image --> <div :class="{'is-hidden': !online}">
<div class="offline" style="height: 0;width: 0;"></div> <!-- This is a workaround to get the sw to "see" the to-be-cached version of the offline background image -->
<top-navigation v-if="authUser"/> <div class="offline" style="height: 0;width: 0;"></div>
<content-auth v-if="authUser"/> <top-navigation v-if="authUser"/>
<content-link-share v-else-if="authLinkShare"/> <content-auth v-if="authUser"/>
<content-no-auth v-else/> <content-link-share v-else-if="authLinkShare"/>
<notification/> <content-no-auth v-else/>
</div> <notification/>
<div class="app offline" v-if="!online"> </div>
<div class="offline-message"> <div class="app offline" v-if="!online">
<h1>You are offline.</h1> <div class="offline-message">
<p>Please check your network connection and try again.</p> <h1>You are offline.</h1>
<p>Please check your network connection and try again.</p>
</div>
</div> </div>
</div>
<transition name="fade"> <transition name="fade">
<keyboard-shortcuts v-if="keyboardShortcutsActive"/> <keyboard-shortcuts v-if="keyboardShortcutsActive"/>
</transition> </transition>
</div> </div>
<vikunja-loading v-else/> </ready>
</template> </template>
<script> <script>
@ -37,18 +38,18 @@ import ContentLinkShare from './components/home/contentLinkShare'
import ContentNoAuth from './components/home/contentNoAuth' import ContentNoAuth from './components/home/contentNoAuth'
import {setLanguage} from './i18n' import {setLanguage} from './i18n'
import AccountDeleteService from '@/services/accountDelete' import AccountDeleteService from '@/services/accountDelete'
import VikunjaLoading from '@/components/misc/vikunja-loading' import Ready from '@/components/misc/ready'
export default defineComponent({ export default defineComponent({
name: 'app', name: 'app',
components: { components: {
VikunjaLoading,
ContentNoAuth, ContentNoAuth,
ContentLinkShare, ContentLinkShare,
ContentAuth, ContentAuth,
TopNavigation, TopNavigation,
KeyboardShortcuts, KeyboardShortcuts,
Notification, Notification,
Ready,
}, },
beforeMount() { beforeMount() {
this.setupOnlineStatus() this.setupOnlineStatus()
@ -57,7 +58,6 @@ export default defineComponent({
this.setupAccountDeletionVerification() this.setupAccountDeletionVerification()
}, },
beforeCreate() { beforeCreate() {
this.$store.dispatch('loadApp')
setLanguage() setLanguage()
}, },
created() { created() {
@ -73,7 +73,6 @@ export default defineComponent({
...mapState({ ...mapState({
online: ONLINE, online: ONLINE,
keyboardShortcutsActive: KEYBOARD_SHORTCUTS_ACTIVE, keyboardShortcutsActive: KEYBOARD_SHORTCUTS_ACTIVE,
ready: 'vikunjaReady',
}), }),
...mapGetters('auth', [ ...mapGetters('auth', [
'authUser', 'authUser',

View File

@ -26,7 +26,7 @@
<i18n-t keypath="apiConfig.signInOn"> <i18n-t keypath="apiConfig.signInOn">
<span class="url" v-tooltip="apiUrl"> {{ apiDomain }} </span> <span class="url" v-tooltip="apiUrl"> {{ apiDomain }} </span>
</i18n-t> </i18n-t>
<br /> <br/>
<a @click="() => (configureApi = true)">{{ $t('apiConfig.change') }}</a> <a @click="() => (configureApi = true)">{{ $t('apiConfig.change') }}</a>
</div> </div>
@ -46,9 +46,8 @@
</template> </template>
<script> <script>
import { parseURL } from 'ufo' import {parseURL} from 'ufo'
import {checkAndSetApiUrl} from '../../helpers/checkAndSetApiUrl'
const API_DEFAULT_PORT = 3456
export default { export default {
name: 'apiConfig', name: 'apiConfig',
@ -77,121 +76,23 @@ export default {
return return
} }
let urlToCheck = this.apiUrl checkAndSetApiUrl(this.apiUrl, () => this.$store.dispatch('config/update'))
// Check if the url has an http prefix
if (
!urlToCheck.startsWith('http://') &&
!urlToCheck.startsWith('https://')
) {
urlToCheck = `http://${urlToCheck}`
}
urlToCheck = new URL(urlToCheck)
const origUrlToCheck = urlToCheck
const oldUrl = window.API_URL
window.API_URL = urlToCheck.toString()
// Check if the api is reachable at the provided url
this.$store
.dispatch('config/update')
.catch((e) => {
// Check if it is reachable at /api/v1 and http
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
throw e
})
.catch((e) => {
// Check if it has a port and if not check if it is reachable at https
if (urlToCheck.protocol === 'http:') {
urlToCheck.protocol = 'https:'
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
throw e
})
.catch((e) => {
// Check if it is reachable at /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
throw e
})
.catch((e) => {
// Check if it is reachable at port API_DEFAULT_PORT and https
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'https:'
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
throw e
})
.catch((e) => {
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
throw e
})
.catch((e) => {
// Check if it is reachable at port API_DEFAULT_PORT and http
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'http:'
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
throw e
})
.catch((e) => {
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and http
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return this.$store.dispatch('config/update')
}
throw e
})
.catch(() => { .catch(() => {
// Still not found, url is still invalid // Still not found, url is still invalid
this.successMsg = '' this.successMsg = ''
this.errorMsg = this.$t('apiConfig.error', {domain: this.apiDomain}) this.errorMsg = this.$t('apiConfig.error', {domain: this.apiDomain})
window.API_URL = oldUrl
}) })
.then((r) => { .then(url => {
if (typeof r !== 'undefined') { if (url === '') {
// Set it + save it to local storage to save us the hoops return
this.errorMsg = ''
this.successMsg = this.$t('apiConfig.success', {domain: this.apiDomain})
localStorage.setItem('API_URL', window.API_URL)
this.configureApi = false
this.apiUrl = window.API_URL
this.$emit('foundApi', this.apiUrl)
} }
// Set it + save it to local storage to save us the hoops
this.errorMsg = ''
this.successMsg = this.$t('apiConfig.success', {domain: this.apiDomain})
this.configureApi = false
this.apiUrl = url
this.$emit('foundApi', this.apiUrl)
}) })
}, },
}, },
@ -200,15 +101,15 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.api-config { .api-config {
margin-bottom: .75rem; margin-bottom: .75rem;
} }
.api-url-info { .api-url-info {
font-size: .9rem; font-size: .9rem;
text-align: right; text-align: right;
span.url { span.url {
border-bottom: 1px dashed $primary; border-bottom: 1px dashed $primary;
} }
} }
</style> </style>

View File

@ -1,5 +1,11 @@
<template> <template>
<section class="vikunja-loading"> <template v-if="ready">
<slot/>
</template>
<section v-else-if="error !== ''">
{{ error }}
</section>
<section class="vikunja-loading" v-else>
<img alt="Vikunja" :src="logoUrl" width="100" height="100"/> <img alt="Vikunja" :src="logoUrl" width="100" height="100"/>
<p> <p>
<span class="loader-container is-loading-small is-loading"></span> <span class="loader-container is-loading-small is-loading"></span>
@ -10,14 +16,27 @@
<script> <script>
import logoUrl from '@/assets/logo.svg' import logoUrl from '@/assets/logo.svg'
import {setLanguage} from '@/i18n'
export default { export default {
name: 'vikunja-loading', name: 'ready',
data() { data() {
return { return {
logoUrl, logoUrl,
error: '',
} }
}, },
mounted() {
this.$store.dispatch('loadApp')
.catch(e => {
this.error = e
})
},
computed: {
ready() {
return this.$store.state.vikunjaReady
},
},
} }
</script> </script>
@ -29,14 +48,14 @@ export default {
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
flex-direction: column; flex-direction: column;
img { img {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.loader-container { .loader-container {
margin-right: 1rem; margin-right: 1rem;
&.is-loading:after { &.is-loading:after {
border-left-color: $grey-400; border-left-color: $grey-400;
border-bottom-color: $grey-400; border-bottom-color: $grey-400;

View File

@ -0,0 +1,111 @@
const API_DEFAULT_PORT = '3456'
export const checkAndSetApiUrl = (url: string, updateConfig: () => Promise<void>): Promise<string> => {
// Check if the url has an http prefix
if (
!url.startsWith('http://') &&
!url.startsWith('https://')
) {
url = `http://${url}`
}
const urlToCheck: URL = new URL(url)
const origUrlToCheck = urlToCheck
const oldUrl = window.API_URL
window.API_URL = urlToCheck.toString()
// Check if the api is reachable at the provided url
return updateConfig()
.catch(e => {
// Check if it is reachable at /api/v1 and http
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it has a port and if not check if it is reachable at https
if (urlToCheck.protocol === 'http:') {
urlToCheck.protocol = 'https:'
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at port API_DEFAULT_PORT and https
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'https:'
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at port API_DEFAULT_PORT and http
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'http:'
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and http
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
window.API_URL = oldUrl
throw e
})
.then(r => {
if (typeof r !== 'undefined') {
localStorage.setItem('API_URL', window.API_URL)
return window.API_URL
}
return ''
})
}

View File

@ -19,6 +19,7 @@ import attachments from './modules/attachments'
import labels from './modules/labels' import labels from './modules/labels'
import ListService from '../services/list' import ListService from '../services/list'
import {checkAndSetApiUrl} from '../helpers/checkAndSetApiUrl'
export const store = createStore({ export const store = createStore({
strict: import.meta.env.DEV, strict: import.meta.env.DEV,
@ -143,7 +144,7 @@ export const store = createStore({
commit(CURRENT_LIST, currentList) commit(CURRENT_LIST, currentList)
}, },
async loadApp({commit, dispatch}) { async loadApp({commit, dispatch}) {
await dispatch('config/update') await checkAndSetApiUrl(window.API_URL, () => dispatch('config/update'))
await dispatch('auth/checkAuth') await dispatch('auth/checkAuth')
commit('vikunjaReady', true) commit('vikunjaReady', true)
}, },