Remove the need to enter api connection.

This commit is contained in:
arabcoders
2025-05-14 23:24:52 +03:00
parent b0683d5dbc
commit bc3ece35e2
3 changed files with 112 additions and 295 deletions

View File

@@ -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>

View 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>

View File

@@ -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>
@@ -161,26 +192,27 @@
</button>
</div>
<div class="navbar-item">
<button class="button is-dark" @click="showConnection = !showConnection"
v-tooltip="'Configure connection'">
<span class="icon"><i class="fas fa-cog"/></span>
</button>
</div>
<div class="navbar-item">
<button class="button is-dark" @click="showSettings = !showSettings"
v-tooltip="'WebUI Settings'">
<span class="icon"><i class="fas fa-cog"/></span>
</button>
</div>
<div class="navbar-item">
<button class="button is-dark" @click="logout" v-tooltip="'Logout'">
<span class="icon"><i class="fas fa-sign-out"/></span>
</button>
</div>
<div class="navbar-item">
<button class="button is-dark" @click="logout" v-tooltip="'Logout'">
<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