Allow setting api url from the login screen #264
|
@ -158,7 +158,6 @@
|
|||
</a>
|
||||
|
||||
<aside class="menu namespaces-lists">
|
||||
<div :class="{ 'is-loading': namespaceService.loading}" class="spinner"></div>
|
||||
<template v-for="n in namespaces">
|
||||
<div :key="n.id">
|
||||
<router-link
|
||||
|
@ -320,7 +319,6 @@
|
|||
import router from './router'
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
import NamespaceService from './services/namespace'
|
||||
import authTypes from './models/authTypes'
|
||||
import Rights from './models/rights.json'
|
||||
|
||||
|
@ -337,7 +335,6 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
namespaceService: NamespaceService,
|
||||
menuActive: true,
|
||||
currentDate: new Date(),
|
||||
userMenuActive: false,
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
<template>
|
||||
<div class="api-config">
|
||||
<div v-if="configureApi">
|
||||
<label class="label" for="api-url">Vikunja URL</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<input
|
||||
class="input" id="api-url"
|
||||
placeholder="eg. https://localhost:3456"
|
||||
required
|
||||
type="url"
|
||||
v-focus
|
||||
v-model="apiUrl"
|
||||
@keyup.enter="setApiUrl"
|
||||
/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-primary" @click="setApiUrl" :disabled="apiUrl === ''">
|
||||
Change
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="api-url-info" v-else>
|
||||
Sign in to your Vikunja account on <span v-tooltip="apiUrl">{{ apiDomain() }}</span><br/>
|
||||
<a @click="() => configureApi = true">change</a>
|
||||
</div>
|
||||
|
||||
<div class="notification is-success mt-2" v-if="successMsg !== '' && errorMsg === ''">
|
||||
{{ successMsg }}
|
||||
</div>
|
||||
<div class="notification is-danger mt-2" v-if="errorMsg !== '' && successMsg === ''">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'apiConfig',
|
||||
data() {
|
||||
return {
|
||||
configureApi: false,
|
||||
apiUrl: '',
|
||||
errorMsg: '',
|
||||
successMsg: '',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.apiUrl = window.API_URL
|
||||
if (this.apiUrl === '') {
|
||||
this.configureApi = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
apiDomain() {
|
||||
if (window.API_URL.startsWith('/api/v1')) {
|
||||
return window.location.host
|
||||
}
|
||||
const urlParts = window.API_URL.replace('http://', '').replace('https://', '').split(/[/?#]/)
|
||||
return urlParts[0]
|
||||
},
|
||||
setApiUrl() {
|
||||
if (this.apiUrl === '') {
|
||||
return
|
||||
}
|
||||
|
||||
let urlToCheck = this.apiUrl
|
||||
|
||||
// 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')
|
||||
}
|
||||
return Promise.reject(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')
|
||||
}
|
||||
return Promise.reject(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')
|
||||
}
|
||||
return Promise.reject(e)
|
||||
})
|
||||
.catch(e => {
|
||||
// Check if it is reachable at port 3456 and https
|
||||
if (urlToCheck.port !== 3456) {
|
||||
urlToCheck.protocol = 'https:'
|
||||
urlToCheck.port = 3456
|
||||
window.API_URL = urlToCheck.toString()
|
||||
return this.$store.dispatch('config/update')
|
||||
}
|
||||
return Promise.reject(e)
|
||||
})
|
||||
.catch(e => {
|
||||
// Check if it is reachable at :3456 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')
|
||||
}
|
||||
return Promise.reject(e)
|
||||
})
|
||||
.catch(e => {
|
||||
// Check if it is reachable at port 3456 and http
|
||||
if (urlToCheck.port !== 3456) {
|
||||
urlToCheck.protocol = 'http:'
|
||||
urlToCheck.port = 3456
|
||||
window.API_URL = urlToCheck.toString()
|
||||
return this.$store.dispatch('config/update')
|
||||
}
|
||||
return Promise.reject(e)
|
||||
})
|
||||
.catch(e => {
|
||||
// Check if it is reachable at :3456 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')
|
||||
}
|
||||
return Promise.reject(e)
|
||||
})
|
||||
.catch(() => {
|
||||
// Still not found, url is still invalid
|
||||
this.successMsg = ''
|
||||
this.errorMsg = `Could not find or use Vikunja installation at "${this.apiDomain()}".`
|
||||
window.API_URL = oldUrl
|
||||
})
|
||||
.then(r => {
|
||||
if (typeof r !== 'undefined') {
|
||||
// Set it + save it to local storage to save us the hoops
|
||||
this.errorMsg = ''
|
||||
this.successMsg = `Using Vikunja installation at "${this.apiDomain()}".`
|
||||
localStorage.setItem('API_URL', window.API_URL)
|
||||
this.configureApi = false
|
||||
this.apiUrl = window.API_URL
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,5 +1,7 @@
|
|||
import axios from 'axios'
|
||||
|
||||
export const HTTP = axios.create({
|
||||
baseURL: window.API_URL,
|
||||
})
|
||||
export const HTTPFactory = () => {
|
||||
return axios.create({
|
||||
baseURL: window.API_URL,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -73,6 +73,12 @@ import {store} from './store'
|
|||
|
||||
console.info(`Vikunja frontend version ${VERSION}`)
|
||||
|
||||
// Check if we have an api url in local storage and use it if that's the case
|
||||
const apiUrlFromStorage = localStorage.getItem('API_URL')
|
||||
if (apiUrlFromStorage !== null) {
|
||||
window.API_URL = apiUrlFromStorage
|
||||
}
|
||||
|
||||
// Make sure the api url does not contain a / at the end
|
||||
if (window.API_URL.substr(window.API_URL.length - 1, window.API_URL.length) === '/') {
|
||||
window.API_URL = window.API_URL.substr(0, window.API_URL.length - 1)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {HTTP} from '@/http-common'
|
||||
import {HTTPFactory} from '@/http-common'
|
||||
import {ERROR_MESSAGE, LOADING} from '../mutation-types'
|
||||
import UserModel from '../../models/user'
|
||||
|
||||
|
@ -32,6 +32,7 @@ export default {
|
|||
actions: {
|
||||
// Logs a user in with a set of credentials.
|
||||
login(ctx, credentials) {
|
||||
const HTTP = HTTPFactory()
|
||||
ctx.commit(LOADING, true, {root: true})
|
||||
|
||||
// Delete an eventually preexisting old token
|
||||
|
@ -78,6 +79,7 @@ export default {
|
|||
// Registers a new user and logs them in.
|
||||
// Not sure if this is the right place to put the logic in, maybe a seperate js component would be better suited.
|
||||
register(ctx, credentials) {
|
||||
const HTTP = HTTPFactory()
|
||||
return HTTP.post('register', {
|
||||
username: credentials.username,
|
||||
email: credentials.email,
|
||||
|
@ -98,6 +100,7 @@ export default {
|
|||
},
|
||||
|
||||
linkShareAuth(ctx, hash) {
|
||||
const HTTP = HTTPFactory()
|
||||
return HTTP.post('/shares/' + hash + '/auth')
|
||||
.then(r => {
|
||||
localStorage.setItem('token', r.data.token)
|
||||
|
@ -128,6 +131,7 @@ export default {
|
|||
},
|
||||
// Renews the api token and saves it to local storage
|
||||
renewToken(ctx) {
|
||||
const HTTP = HTTPFactory()
|
||||
if (!ctx.state.authenticated) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {CONFIG} from '../mutation-types'
|
||||
import {HTTP} from '@/http-common'
|
||||
import {HTTPFactory} from '@/http-common'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
|
@ -40,10 +40,14 @@ export default {
|
|||
},
|
||||
actions: {
|
||||
update(ctx) {
|
||||
HTTP.get('info')
|
||||
const HTTP = HTTPFactory()
|
||||
|
||||
return HTTP.get('info')
|
||||
.then(r => {
|
||||
ctx.commit(CONFIG, r.data)
|
||||
return Promise.resolve(r)
|
||||
})
|
||||
.catch(e => Promise.reject(e))
|
||||
},
|
||||
},
|
||||
}
|
|
@ -21,3 +21,4 @@
|
|||
@import 'namespaces';
|
||||
@import 'legal';
|
||||
@import 'keyboard-shortcuts';
|
||||
@import 'api-config';
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
.api-config {
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
|
||||
.api-url-info {
|
||||
font-size: .9rem;
|
||||
text-align: right;
|
||||
|
||||
span {
|
||||
border-bottom: 1px dashed $primary;
|
||||
}
|
||||
}
|
|
@ -76,6 +76,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.field.has-addons .button {
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.input,
|
||||
.textarea {
|
||||
transition: all $transition;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<div class="notification is-success has-text-centered" v-if="confirmedEmailSuccess">
|
||||
You successfully confirmed your email! You can log in now.
|
||||
</div>
|
||||
<api-config/>
|
||||
<form @submit.prevent="submit" id="loginform">
|
||||
<div class="field">
|
||||
<label class="label" for="username">Username</label>
|
||||
|
@ -76,13 +77,15 @@
|
|||
import {mapState} from 'vuex'
|
||||
|
||||
import router from '../../router'
|
||||
import {HTTP} from '@/http-common'
|
||||
import {HTTPFactory} from '@/http-common'
|
||||
import message from '../../message'
|
||||
import {ERROR_MESSAGE, LOADING} from '@/store/mutation-types'
|
||||
import legal from '../../components/misc/legal'
|
||||
import ApiConfig from '@/components/misc/api-config'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ApiConfig,
|
||||
legal,
|
||||
},
|
||||
data() {
|
||||
|
@ -91,6 +94,7 @@ export default {
|
|||
}
|
||||
},
|
||||
beforeMount() {
|
||||
const HTTP = HTTPFactory()
|
||||
// Try to verify the email
|
||||
// FIXME: Why is this here? Can we find a better place for this?
|
||||
let emailVerifyToken = localStorage.getItem('emailConfirmToken')
|
||||
|
|
Reference in New Issue