- This usually takes less than a second. If this is taking too long,
- reload configuration.
+
+
+ This usually takes less than a few seconds. If this is taking too long,
+ click here to reload the
+ page.
+
+
+ Failed to load the application configuration. This likely indicates a problem with the backend. Try to
+ config.loadConfig(true)">reload
+ configuration or reload the page
+ .
diff --git a/ui/app/stores/ConfigStore.ts b/ui/app/stores/ConfigStore.ts
index 132be20e..87aab84e 100644
--- a/ui/app/stores/ConfigStore.ts
+++ b/ui/app/stores/ConfigStore.ts
@@ -5,6 +5,9 @@ import type { Preset } from '~/types/presets';
import type { ConfigFeature, ConfigUpdateAction } from '~/types/sockets';
import { request } from '~/utils';
+let last_reload = 0;
+const CONFIG_TTL = 10;
+
export const useConfigStore = defineStore('config', () => {
const state = reactive({
showForm: useStorage('showForm', true),
@@ -54,14 +57,23 @@ export const useConfigStore = defineStore('config', () => {
is_loading: false,
});
- const loadConfig = async () => {
+ const loadConfig = async (force: boolean = false) => {
if (state.is_loading) {
return;
}
+ const now = Date.now()
+
+ if (state.is_loaded && !force && last_reload > 0) {
+ const age = (now - last_reload) / 1000
+ if (age < CONFIG_TTL) {
+ return
+ }
+ }
+
state.is_loaded = false;
state.is_loading = true;
try {
- const resp = await request('/api/system/configuration');
+ const resp = await request('/api/system/configuration', { timeout: 10 });
if (!resp.ok) {
return;
}
@@ -79,11 +91,12 @@ export const useConfigStore = defineStore('config', () => {
}
setAll(data);
+ state.is_loaded = true;
+ last_reload = now;
} catch (e: any) {
- console.error('Failed to load configuration', e);
+ console.error(`Failed to load configuration: ${e}`);
}
finally {
- state.is_loaded = true;
state.is_loading = false;
}
}
diff --git a/ui/app/stores/SocketStore.ts b/ui/app/stores/SocketStore.ts
index 5ea9445e..2a7414dd 100644
--- a/ui/app/stores/SocketStore.ts
+++ b/ui/app/stores/SocketStore.ts
@@ -1,6 +1,5 @@
import { ref, readonly } from 'vue'
import { defineStore } from 'pinia'
-import type { ConfigState } from "~/types/config";
import type { StoreItem } from "~/types/store";
import type {
ConfigUpdatePayload,
@@ -263,14 +262,7 @@ export const useSocketStore = defineStore('socket', () => {
setupVisibilityListener()
}
- on('configuration', (data: WSEP['configuration']) => {
- config.setAll({
- app: data.data.config,
- presets: data.data.presets,
- dl_fields: data.data.dl_fields,
- paused: Boolean(data.data.paused)
- } as unknown as Partial)
- })
+ on('connect', () => config.loadConfig(false))
on('connected', (data: WSEP['connected']) => {
if (!data?.data) {
@@ -288,13 +280,6 @@ export const useSocketStore = defineStore('socket', () => {
error.value = null
})
- on('active_queue', (data: WSEP['active_queue']) => {
- if (!data.data.queue) {
- return
- }
- stateStore.addAll('queue', data.data.queue || {})
- })
-
on('item_added', (data: WSEP['item_added']) => {
stateStore.add('queue', data.data._id, data.data)
toast.success(`Item queued: ${ag(stateStore.get('queue', data.data._id, {} as StoreItem), 'title')}`)
diff --git a/ui/app/types/sockets.d.ts b/ui/app/types/sockets.d.ts
index 463a4e76..ad576c98 100644
--- a/ui/app/types/sockets.d.ts
+++ b/ui/app/types/sockets.d.ts
@@ -1,4 +1,3 @@
-import type { AppConfig, ConfigState } from "./config"
import type { StoreItem } from "./store"
export type Event = {
@@ -21,20 +20,11 @@ export type WSEP = {
connect: null
disconnect: null
connect_error: { message?: string }
- configuration: EventPayload<{
- config: AppConfig
- presets: ConfigState['presets']
- dl_fields: ConfigState['dl_fields']
- paused: boolean
- }>
connected: EventPayload<{
folders?: string[]
history_count?: number
queue?: Record
}>
- active_queue: EventPayload<{
- queue?: Record
- }>
item_added: EventPayload
item_updated: EventPayload
item_cancelled: EventPayload
diff --git a/ui/app/utils/index.ts b/ui/app/utils/index.ts
index 6ccea475..dc08e6fa 100644
--- a/ui/app/utils/index.ts
+++ b/ui/app/utils/index.ts
@@ -228,25 +228,39 @@ const encodePath = (item: string): string => {
* @param options - Optional fetch options, automatically extended with common headers and credentials.
* @returns The fetch Response promise.
*/
-const request = (url: string, options: RequestInit = {}): Promise => {
- options.method = options.method || 'GET'
- options.headers = options.headers || {}; (options as any).withCredentials = true
+const request = (url: string, options: RequestInit & { timeout?: number } = {}): Promise => {
+ const { timeout, ...fetchOptions } = options
- if (undefined === (options.headers as Record)['Content-Type']) {
+ fetchOptions.method = fetchOptions.method || 'GET'
+ fetchOptions.headers = fetchOptions.headers || {};
+ (fetchOptions as any).withCredentials = true
+
+ if (undefined === (fetchOptions.headers as Record)['Content-Type']) {
if (!(options?.body instanceof FormData)) {
- ; (options.headers as Record)['Content-Type'] = 'application/json'
+ ; (fetchOptions.headers as Record)['Content-Type'] = 'application/json'
}
}
- if (undefined === (options.headers as Record)['Accept']) {
- ; (options.headers as Record)['Accept'] = 'application/json'
+ if (undefined === (fetchOptions.headers as Record)['Accept']) {
+ ; (fetchOptions.headers as Record)['Accept'] = 'application/json'
}
if (url.startsWith('/')) {
- options.credentials = 'same-origin'
+ fetchOptions.credentials = 'same-origin'
}
- return fetch(url.startsWith('/') ? uri(url) : url, options)
+ let controller: AbortController | undefined
+ let timer: ReturnType | undefined
+
+ if (typeof timeout === 'number' && timeout > 0) {
+ controller = new AbortController()
+ fetchOptions.signal = controller.signal
+ timer = setTimeout(() => controller!.abort(`Request timed out.`), timeout*1000)
+ }
+
+ return fetch(url.startsWith('/') ? uri(url) : url, fetchOptions).finally(() => {
+ if (timer) { clearTimeout(timer) }
+ })
}
/**
@@ -879,10 +893,10 @@ const parse_api_error = async (json: unknown): Promise => {
}
if (payload.error) {
- return String(payload.error+(extra_detail ? ` - ${extra_detail}` : ''))
+ return String(payload.error + (extra_detail ? ` - ${extra_detail}` : ''))
}
if (payload.message) {
- return String(payload.message+(extra_detail ? ` - ${extra_detail}` : ''))
+ return String(payload.message + (extra_detail ? ` - ${extra_detail}` : ''))
}
if (extra_detail) {