Remove the need to enter api connection.
This commit is contained in:
@@ -1,274 +0,0 @@
|
||||
<template>
|
||||
<div class="columns is-multiline mb-2">
|
||||
|
||||
<div class="column is-6-tablet">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
Configure API Connection
|
||||
</p>
|
||||
<span class="card-header-icon">
|
||||
<span class="icon"><i class="fas fa-cog"/></span>
|
||||
</span>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<form id="api_connection" @submit.prevent="testApi">
|
||||
<div class="field">
|
||||
<label class="label" for="api_token">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-key"/></span>
|
||||
<span>API Token</span>
|
||||
</span>
|
||||
</label>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<input class="input" id="api_token" v-model="api_token" required placeholder="API Token..."
|
||||
:type="false === exposeToken ? 'password' : 'text'">
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="button" class="button is-primary" @click="exposeToken = !exposeToken"
|
||||
v-tooltip="'Show/Hide token'">
|
||||
<span class="icon" v-if="!exposeToken"><i class="fas fa-eye"/></span>
|
||||
<span class="icon" v-else><i class="fas fa-eye-slash"/></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help">
|
||||
You can obtain the <code>API TOKEN</code> by using the <code>system:apikey</code> command or by
|
||||
viewing the <code>/config/.env</code> inside <code>WS_DATA_PATH</code> variable and looking for
|
||||
the <code>WS_API_KEY=</code> key.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="api_url">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-link"/></span>
|
||||
<span>API URL</span>
|
||||
</span>
|
||||
</label>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" id="api_url" type="url" v-model="api_url" required
|
||||
placeholder="API URL... http://localhost:8081">
|
||||
<p class="help">
|
||||
Use <a href="javascript:void(0)" @click="setOrigin">current page URL</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="api_path">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-folder"/></span>
|
||||
<span>API Path</span>
|
||||
</span>
|
||||
</label>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" id="api_path" type="text" v-model="api_path" required
|
||||
placeholder="API Path... /v1/api">
|
||||
<p class="help">
|
||||
Use <a href="javascript:void(0)" @click="api_path = '/v1/api'">Set default API Path</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
WebUI Look & Feel
|
||||
</p>
|
||||
<span class="card-header-icon">
|
||||
<span class="icon"><i class="fas fa-paint-brush"/></span>
|
||||
</span>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="field">
|
||||
<label class="label" for="random_bg">Color scheme</label>
|
||||
<div class="control">
|
||||
<label for="auto" class="radio">
|
||||
<input id="auto" type="radio" v-model="webui_theme" value="auto"> System Default
|
||||
</label>
|
||||
<label for="light" class="radio">
|
||||
<input id="light" type="radio" v-model="webui_theme" value="light"> Light
|
||||
</label>
|
||||
<label for="dark" class="radio">
|
||||
<input id="dark" type="radio" v-model="webui_theme" value="dark"> Dark
|
||||
</label>
|
||||
</div>
|
||||
<p class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info"/></span>
|
||||
<span>Select the color scheme for the WebUI.</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="random_bg">Backgrounds</label>
|
||||
<div class="control">
|
||||
<input id="random_bg" type="checkbox" class="switch is-success" v-model="bg_enable">
|
||||
<label for="random_bg">Enable</label>
|
||||
<p class="help">Use random background image from your media backends.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="random_bg_opacity">
|
||||
Background Visibility: (<code>{{ bg_opacity }}</code>)
|
||||
</label>
|
||||
<div class="control">
|
||||
<input id="random_bg_opacity" style="width: 100%" type="range" v-model="bg_opacity" min="0.60"
|
||||
max="1.00" step="0.05">
|
||||
<p class="help">How visible the background image should be.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<div class="control">
|
||||
<button form="api_connection" type="submit" class="button is-primary is-fullwidth"
|
||||
:disabled="!api_url || !api_token">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-save"/></span>
|
||||
<span>Save</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12 mt-2">
|
||||
<Message title="Information" message_class="has-background-info-90 has-text-dark" icon="fas fa-info-circle">
|
||||
<ul>
|
||||
<li>
|
||||
These settings are stored locally in your browser. You need to re-add them if you access the
|
||||
<strong>WebUI</strong> from different browser.
|
||||
</li>
|
||||
<li>
|
||||
The very first time you access the <strong>WebUI</strong>, it will auto configure the connection if
|
||||
possible.
|
||||
</li>
|
||||
</ul>
|
||||
</Message>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from 'vue'
|
||||
import {useStorage} from '@vueuse/core'
|
||||
import {notification} from '~/utils/index'
|
||||
import awaiter from '~/utils/awaiter'
|
||||
|
||||
const emitter = defineEmits(['update'])
|
||||
|
||||
const real_api_user = useStorage('api_user', 'main')
|
||||
const real_api_url = useStorage('api_url', window.location.origin)
|
||||
const real_api_path = useStorage('api_path', '/v1/api')
|
||||
const real_api_token = useStorage('api_token', '')
|
||||
|
||||
const webui_theme = useStorage('theme', 'auto')
|
||||
|
||||
|
||||
const api_url = ref(toRaw(real_api_url.value))
|
||||
const api_path = ref(toRaw(real_api_path.value))
|
||||
const api_user = ref(toRaw(real_api_user.value))
|
||||
const api_token = ref(toRaw(real_api_token.value))
|
||||
const exposeToken = ref(false)
|
||||
|
||||
const bg_enable = useStorage('bg_enable', true)
|
||||
const bg_opacity = useStorage('bg_opacity', 0.95)
|
||||
|
||||
const testApi = async () => {
|
||||
try {
|
||||
const response = await fetch(`${api_url.value}${api_path.value}/system/version`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': `Bearer ${api_token.value}`
|
||||
}
|
||||
})
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
if (json.error) {
|
||||
notification('error', 'API Connection', `Error ${json.error.code} - ${json.error.message}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (200 === response.status) {
|
||||
real_api_url.value = api_url.value
|
||||
real_api_user.value = api_user.value
|
||||
real_api_path.value = api_path.value
|
||||
real_api_token.value = api_token.value
|
||||
}
|
||||
|
||||
const message = 200 === response.status ? `Status: OK` : `Status: ${response.status} - ${response.statusText}`;
|
||||
notification('success', 'API Connection', `${response.status}: ${message}`)
|
||||
if (200 === response.status) {
|
||||
await awaiter(() => '' !== real_api_token.value)
|
||||
emitter('update', {version: json.version})
|
||||
}
|
||||
} catch (e) {
|
||||
notification('error', 'API Connection', `Request error. ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if ('' === api_token.value) {
|
||||
await autoConfig()
|
||||
}
|
||||
})
|
||||
|
||||
const autoConfig = async () => {
|
||||
try {
|
||||
const response = await fetch(`${api_url.value}${api_path.value}/system/auto`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({'origin': window.location.origin})
|
||||
})
|
||||
|
||||
const json = await response.json()
|
||||
|
||||
if (200 !== response.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!api_url.value) {
|
||||
api_url.value = json.url
|
||||
}
|
||||
|
||||
if (!api_path.value) {
|
||||
api_path.value = json.path
|
||||
}
|
||||
|
||||
if (!api_token.value) {
|
||||
api_token.value = json.token
|
||||
}
|
||||
|
||||
await testApi();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
const setOrigin = () => api_url.value = window.location.origin;
|
||||
</script>
|
||||
64
frontend/components/Settings.vue
Normal file
64
frontend/components/Settings.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div class="columns is-multiline mb-2">
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">
|
||||
WebUI Look & Feel
|
||||
</p>
|
||||
<span class="card-header-icon">
|
||||
<span class="icon"><i class="fas fa-paint-brush"/></span>
|
||||
</span>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="field">
|
||||
<label class="label" for="random_bg">Color scheme</label>
|
||||
<div class="control">
|
||||
<label for="auto" class="radio">
|
||||
<input id="auto" type="radio" v-model="webui_theme" value="auto"> System Default
|
||||
</label>
|
||||
<label for="light" class="radio">
|
||||
<input id="light" type="radio" v-model="webui_theme" value="light"> Light
|
||||
</label>
|
||||
<label for="dark" class="radio">
|
||||
<input id="dark" type="radio" v-model="webui_theme" value="dark"> Dark
|
||||
</label>
|
||||
</div>
|
||||
<p class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info"/></span>
|
||||
<span>Select the color scheme for the WebUI.</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="random_bg">Backgrounds</label>
|
||||
<div class="control">
|
||||
<input id="random_bg" type="checkbox" class="switch is-success" v-model="bg_enable">
|
||||
<label for="random_bg">Enable</label>
|
||||
<p class="help">Use random background image from your media backends.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label" for="random_bg_opacity">
|
||||
Background Visibility: (<code>{{ bg_opacity }}</code>)
|
||||
</label>
|
||||
<div class="control">
|
||||
<input id="random_bg_opacity" style="width: 100%" type="range" v-model="bg_opacity" min="0.60"
|
||||
max="1.00" step="0.05">
|
||||
<p class="help">How visible the background image should be.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStorage} from '@vueuse/core'
|
||||
|
||||
const webui_theme = useStorage('theme', 'auto')
|
||||
const bg_enable = useStorage('bg_enable', true)
|
||||
const bg_opacity = useStorage('bg_opacity', 0.95)
|
||||
</script>
|
||||
@@ -149,6 +149,37 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-end pr-3">
|
||||
<template v-if="'mobile' === breakpoints.active().value">
|
||||
<div class="navbar-item">
|
||||
<NuxtLink class="button is-dark" to="/help">
|
||||
<span class="icon"><i class="fas fa-circle-question"/></span>
|
||||
<span>Guides</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="showUserSelection = !showUserSelection">
|
||||
<span class="icon"><i class="fas fa-users"/></span>
|
||||
<span>Change User</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="showSettings = !showSettings">
|
||||
<span class="icon"><i class="fas fa-cog"/></span>
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="logout">
|
||||
<span class="icon"><i class="fas fa-sign-out"/></span>
|
||||
<span>Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="'mobile' !== breakpoints.active().value">
|
||||
<div class="navbar-item">
|
||||
<NuxtLink class="button is-dark" v-tooltip="'Guides'" to="/help">
|
||||
<span class="icon"><i class="fas fa-circle-question"/></span>
|
||||
@@ -162,8 +193,8 @@
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<button class="button is-dark" @click="showConnection = !showConnection"
|
||||
v-tooltip="'Configure connection'">
|
||||
<button class="button is-dark" @click="showSettings = !showSettings"
|
||||
v-tooltip="'WebUI Settings'">
|
||||
<span class="icon"><i class="fas fa-cog"/></span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -173,14 +204,15 @@
|
||||
<span class="icon"><i class="fas fa-sign-out"/></span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div>
|
||||
<div>
|
||||
<Connection v-if="showConnection" @update="data => handleConnection(data)"/>
|
||||
<NuxtPage v-if="!showConnection"/>
|
||||
<Settings v-if="showSettings"/>
|
||||
<NuxtPage/>
|
||||
</div>
|
||||
|
||||
<div class="columns is-multiline is-mobile mt-3">
|
||||
@@ -229,12 +261,12 @@ import {useBreakpoints, useStorage} from '@vueuse/core'
|
||||
import request from '~/utils/request'
|
||||
import Markdown from '~/components/Markdown'
|
||||
import UserSelection from '~/components/UserSelection'
|
||||
import Connection from '~/components/Connection'
|
||||
import { useAuthStore } from '~/store/auth'
|
||||
import {useAuthStore} from '~/store/auth'
|
||||
import Settings from "~/components/Settings.vue";
|
||||
|
||||
const selectedTheme = useStorage('theme', 'auto')
|
||||
const showUserSelection = ref(false)
|
||||
const showConnection = ref(false)
|
||||
const showSettings = ref(false)
|
||||
|
||||
const auth = useAuthStore()
|
||||
const bg_enable = useStorage('bg_enable', true)
|
||||
@@ -353,11 +385,6 @@ const changeRoute = async (_, callback) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleConnection = data => {
|
||||
api_version.value = data.version
|
||||
showConnection.value = false
|
||||
}
|
||||
|
||||
watch(bgImage, async v => {
|
||||
if (false === bg_enable.value) {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user