281 lines
9.8 KiB
Vue
281 lines
9.8 KiB
Vue
<template>
|
|
<div>
|
|
<div class="columns is-multiline">
|
|
<div class="column is-12 is-clearfix is-unselectable">
|
|
<span id="env_page_title" class="title is-4">
|
|
<span class="icon"><i class="fas fa-exchange-alt"></i></span>
|
|
Add new client GUID link
|
|
</span>
|
|
<div class="is-hidden-mobile">
|
|
<span class="subtitle">
|
|
This page allows you to add a new client GUID link. The client GUID link is used to link the client/backend
|
|
GUID to the <code>WatchState</code> GUID or your custom GUID.
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column is-12">
|
|
<form id="page_form" @submit.prevent="addNewLink">
|
|
|
|
<div class="field">
|
|
<label class="label is-unselectable" for="form_select_type">Client</label>
|
|
<div class="control has-icons-left">
|
|
<div class="select is-fullwidth">
|
|
<select id="form_select_type" v-model="form.type">
|
|
<option value="" disabled>Select client type</option>
|
|
<option v-for="client in supported" :value="client" :key="`client-${client}`">
|
|
{{ ucFirst(client) }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div class="icon is-left">
|
|
<i class="fas fa-server"></i>
|
|
</div>
|
|
</div>
|
|
<p class="help">
|
|
<span class="icon"><i class="fas fa-info"></i></span>
|
|
<span>Select which client this link association for.</span>
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label is-unselectable" for="form_map_from">Link client GUID</label>
|
|
<div class="control has-icons-left">
|
|
<input class="input" id="form_map_from" type="text" v-model="form.map.from">
|
|
<div class="icon is-small is-left"><i class="fas fa-a"></i></div>
|
|
</div>
|
|
<p class="help">
|
|
<span class="icon"><i class="fas fa-info"></i></span>
|
|
<span>Write the <code>{{ form.type.length > 0 ? ucFirst(form.type) : 'client' }}</code> GUID
|
|
identifier.</span>
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label is-unselectable" for="form_map_to">To This GUID</label>
|
|
<div class="control has-icons-left">
|
|
<div class="select is-fullwidth">
|
|
<select id="form_map_to" v-model="form.map.to">
|
|
<option value="" disabled>Select the associated GUID</option>
|
|
<option v-for="(g) in guids" :value="g.guid" :key="`guid-${g.guid}`">
|
|
{{ g.guid }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div class="icon is-left">
|
|
<i class="fas fa-b"></i>
|
|
</div>
|
|
</div>
|
|
<p class="help">
|
|
<span class="icon"><i class="fas fa-info"></i></span>
|
|
<span>
|
|
Select which <code>WatchState</code> GUID should link with this
|
|
<code>{{ form.type.length > 0 ? ucFirst(form.type) : 'client' }}</code> GUID identifier.
|
|
</span>
|
|
</p>
|
|
</div>
|
|
|
|
<div class="field" v-if="'plex' === form.type">
|
|
<label class="label" for="backend_import">Is this a Plex legacy agent GUID?</label>
|
|
<div class="control">
|
|
<input id="backend_import" type="checkbox" class="switch is-success" v-model="form.options.legacy">
|
|
<label for="backend_import">Enable</label>
|
|
<p class="help">Plex legacy agents starts with <code>com.plexapp.agents.</code></p>
|
|
</div>
|
|
</div>
|
|
|
|
<template v-if="'plex' === form.type && true === form.options.legacy">
|
|
<div class="field">
|
|
<label class="label is-clickable is-unselectable" @click="toggleReplace = !toggleReplace">
|
|
<span class="icon">
|
|
<i class="fas" :class="{ 'fa-arrow-up': toggleReplace, 'fa-arrow-down': !toggleReplace }"></i>
|
|
</span>
|
|
Toggle Text replacement.
|
|
</label>
|
|
<p class="help">
|
|
<span class="icon"><i class="fas fa-info"></i></span>
|
|
<span>Text replacement only works for plex legacy agents.</span>
|
|
</p>
|
|
</div>
|
|
|
|
<template v-if="toggleReplace">
|
|
<div class="field">
|
|
<label class="label is-unselectable" for="form_replace_from">Search for</label>
|
|
<div class="control has-icons-left">
|
|
<input class="input" id="form_replace_from" type="text" v-model="form.replace.from">
|
|
<div class="icon is-small is-left"><i class="fas fa-passport"></i></div>
|
|
</div>
|
|
<p class="help">
|
|
<span class="icon"><i class="fas fa-info"></i></span>
|
|
<span>The text string to replace. Sometimes it's necessary to replace legacy agent GUID into
|
|
something else. Leave it empty to ignore it.</span>
|
|
</p>
|
|
</div>
|
|
<div class="field">
|
|
<label class="label is-unselectable" for="form_replace_to">Replace with</label>
|
|
<div class="control has-icons-left">
|
|
<input class="input" id="form_replace_to" type="text" v-model="form.replace.to">
|
|
<div class="icon is-small is-left"><i class="fas fa-passport"></i></div>
|
|
</div>
|
|
<p class="help">
|
|
<span class="icon"><i class="fas fa-info"></i></span>
|
|
<span>The string replacement. If <code>replace.from</code> is empty this field will be
|
|
ignored.</span>
|
|
</p>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
|
|
<div class="field is-grouped">
|
|
<div class="control is-expanded">
|
|
<button class="button is-fullwidth is-primary" type="submit"
|
|
:disabled="false === validForm || isSaving"
|
|
:class="{'is-loading':isSaving}">
|
|
<span class="icon-text">
|
|
<span class="icon"><i class="fas fa-save"></i></span>
|
|
<span>Save</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
<div class="control is-expanded">
|
|
<button class="button is-fullwidth is-danger" type="button" @click="navigateTo('/custom')">
|
|
<span class="icon-text">
|
|
<span class="icon"><i class="fas fa-cancel"></i></span>
|
|
<span>Cancel</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import 'assets/css/bulma-switch.css'
|
|
import request from '~/utils/request'
|
|
import {notification, parse_api_response} from '~/utils/index'
|
|
import {useStorage} from '@vueuse/core'
|
|
|
|
useHead({title: 'Add new client GUID link'})
|
|
|
|
const empty_form = {
|
|
type: '',
|
|
options: {
|
|
legacy: true,
|
|
},
|
|
map: {
|
|
from: '',
|
|
to: ''
|
|
},
|
|
replace: {
|
|
from: '',
|
|
to: ''
|
|
},
|
|
}
|
|
const show_page_tips = useStorage('show_page_tips', true)
|
|
const form = ref(JSON.parse(JSON.stringify(empty_form)))
|
|
const guids = ref([])
|
|
const supported = ref([])
|
|
const isSaving = ref(false)
|
|
const links = ref([])
|
|
const toggleReplace = ref(false)
|
|
|
|
onMounted(async () => {
|
|
try {
|
|
|
|
/** @type {Array<Promise<Response>>} */
|
|
const responses = await Promise.all([
|
|
request('/system/guids'),
|
|
request('/system/supported'),
|
|
request('/system/guids/custom'),
|
|
])
|
|
|
|
guids.value = await parse_api_response(responses[0]) ?? []
|
|
supported.value = await parse_api_response(responses[1]) ?? []
|
|
links.value = (await parse_api_response(responses[2])).links ?? []
|
|
|
|
} catch (e) {
|
|
notification('error', 'Error', `Request error. ${e.message}`, 5000)
|
|
}
|
|
})
|
|
|
|
const addNewLink = async () => {
|
|
if (!validForm.value) {
|
|
notification('error', 'Error', 'Invalid form data.', 5000)
|
|
return
|
|
}
|
|
|
|
let data = form.value
|
|
|
|
if (!supported.value.includes(data.type)) {
|
|
notification('error', 'Error', `Invalid client type.`, 5000)
|
|
return
|
|
}
|
|
|
|
if (!data.map.from) {
|
|
notification('error', 'Error', `map.from must not be empty.`, 5000)
|
|
return
|
|
}
|
|
|
|
if (!guids.value.find(g => g.guid === data.map.to)) {
|
|
notification('error', 'Error', `Invalid map.to value '${data.map.to}'.`, 5000)
|
|
return
|
|
}
|
|
|
|
for (let i = 0; i < links.value.length; i++) {
|
|
if (links.value[i].type === data.type && links.value[i].map.from === data.map.from) {
|
|
notification('error', 'Error', `Link with map.from '${data.map.from}' already exists.`, 5000)
|
|
return
|
|
}
|
|
}
|
|
|
|
let formData = {
|
|
type: data.type,
|
|
map: {
|
|
from: data.map.from,
|
|
to: data.map.to
|
|
}
|
|
}
|
|
|
|
if ('plex' === data.type) {
|
|
formData.options = {
|
|
legacy: Boolean(data.options.legacy),
|
|
}
|
|
|
|
if (data.replace.from && data.replace.to) {
|
|
formData.replace = {
|
|
from: data.replace.from,
|
|
to: data.replace.to
|
|
}
|
|
}
|
|
}
|
|
|
|
isSaving.value = true
|
|
|
|
try {
|
|
const response = await request(`/system/guids/custom/${formData.type}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(formData)
|
|
})
|
|
|
|
const json = await parse_api_response(response)
|
|
|
|
if (!response.ok) {
|
|
notification('error', 'Error', `${json.error.code}: ${json.error.message}`, 5000)
|
|
return
|
|
}
|
|
|
|
notification('success', 'Success', 'Successfully added new client link.', 5000)
|
|
await navigateTo('/custom')
|
|
} catch (e) {
|
|
notification('error', 'Error', `Request error. ${e.message}`, 5000)
|
|
} finally {
|
|
isSaving.value = false
|
|
}
|
|
}
|
|
|
|
const validForm = computed(() => !(!form.value.map.to || !form.value.map.from || !form.value.type))
|
|
</script>
|