Skip to content

Commit dd0633c

Browse files
committed
refactor: improve error messaging during failure to load config
1 parent f88422e commit dd0633c

File tree

7 files changed

+57
-73
lines changed

7 files changed

+57
-73
lines changed

API.md

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ This document describes the available endpoints and their usage. All endpoints r
117117
- [Connection Events](#connection-events)
118118
- [`config_update`](#config_update)
119119
- [`connected`](#connected)
120-
- [`active_queue`](#active_queue)
121120
- [Logging Events](#logging-events)
122121
- [`log_info`](#log_info)
123122
- [`log_success`](#log_success)
@@ -2742,29 +2741,6 @@ Emitted when a client successfully connects to the WebSocket.
27422741

27432742
---
27442743

2745-
##### `active_queue`
2746-
2747-
Emitted periodically with the current active queue status.
2748-
2749-
**Event**:
2750-
```json
2751-
{
2752-
"event": "active_queue",
2753-
"data": {
2754-
"queue": [
2755-
{
2756-
"id": "abc123",
2757-
"status": "downloading",
2758-
"progress": 45.6,
2759-
...
2760-
}
2761-
]
2762-
}
2763-
}
2764-
```
2765-
2766-
---
2767-
27682744
#### Logging Events
27692745

27702746
##### `log_info`

app/library/Events.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ class Events:
2525

2626
CONNECTED: str = "connected"
2727

28-
CONFIGURATION: str = "configuration"
2928
CONFIG_UPDATE: str = "config_update"
30-
ACTIVE_QUEUE: str = "active_queue"
3129

3230
LOG_INFO: str = "log_info"
3331
LOG_WARNING: str = "log_warning"
@@ -79,10 +77,8 @@ def frontend() -> list:
7977
8078
"""
8179
return [
82-
Events.CONFIGURATION,
8380
Events.CONFIG_UPDATE,
8481
Events.CONNECTED,
85-
Events.ACTIVE_QUEUE,
8682
Events.LOG_INFO,
8783
Events.LOG_WARNING,
8884
Events.LOG_ERROR,

ui/app/layouts/default.vue

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,20 @@
135135
<div>
136136
<NuxtLoadingIndicator />
137137
<NuxtPage v-if="config.is_loaded" :isLoading="loadingImage" @reload_bg="() => loadImage(true)" />
138-
<Message v-if="!config.is_loaded" class="is-info mt-5" title="Loading Configuration"
139-
icon="fas fa-spinner fa-spin">
140-
<p>This usually takes less than a second. If this is taking too long,
141-
<NuxtLink class="button is-text p-0" @click="config.loadConfig">reload configuration</NuxtLink>.
138+
<Message v-if="!config.is_loaded" class="mt-5"
139+
:class="{ 'is-info': config.is_loading, 'is-danger': !config.is_loading }"
140+
:title="config.is_loading ? 'Loading configuration...' : 'Failed to load configuration'"
141+
:icon="config.is_loading ? 'fas fa-spinner fa-spin' : 'fas fa-triangle-exclamation'">
142+
<p v-if="config.is_loading">
143+
This usually takes less than a few seconds. If this is taking too long,
144+
<NuxtLink class="button is-text p-0" @click="reloadPage">click here</NuxtLink> to reload the
145+
page.
146+
</p>
147+
<p v-if="!config.is_loading">
148+
Failed to load the application configuration. This likely indicates a problem with the backend. Try to
149+
<NuxtLink class="button is-text p-0" @click="() => config.loadConfig(true)">reload
150+
configuration</NuxtLink> or <NuxtLink class="button is-text p-0" @click="reloadPage">reload the page
151+
</NuxtLink>.
142152
</p>
143153
<template v-if="socket.error">
144154
<hr>

ui/app/stores/ConfigStore.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import type { Preset } from '~/types/presets';
55
import type { ConfigFeature, ConfigUpdateAction } from '~/types/sockets';
66
import { request } from '~/utils';
77

8+
let last_reload = 0;
9+
const CONFIG_TTL = 10;
10+
811
export const useConfigStore = defineStore('config', () => {
912
const state = reactive<ConfigState>({
1013
showForm: useStorage('showForm', true),
@@ -54,14 +57,23 @@ export const useConfigStore = defineStore('config', () => {
5457
is_loading: false,
5558
});
5659

57-
const loadConfig = async () => {
60+
const loadConfig = async (force: boolean = false) => {
5861
if (state.is_loading) {
5962
return;
6063
}
64+
const now = Date.now()
65+
66+
if (state.is_loaded && !force && last_reload > 0) {
67+
const age = (now - last_reload) / 1000
68+
if (age < CONFIG_TTL) {
69+
return
70+
}
71+
}
72+
6173
state.is_loaded = false;
6274
state.is_loading = true;
6375
try {
64-
const resp = await request('/api/system/configuration');
76+
const resp = await request('/api/system/configuration', { timeout: 10 });
6577
if (!resp.ok) {
6678
return;
6779
}
@@ -79,11 +91,12 @@ export const useConfigStore = defineStore('config', () => {
7991
}
8092

8193
setAll(data);
94+
state.is_loaded = true;
95+
last_reload = now;
8296
} catch (e: any) {
83-
console.error('Failed to load configuration', e);
97+
console.error(`Failed to load configuration: ${e}`);
8498
}
8599
finally {
86-
state.is_loaded = true;
87100
state.is_loading = false;
88101
}
89102
}

ui/app/stores/SocketStore.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ref, readonly } from 'vue'
22
import { defineStore } from 'pinia'
3-
import type { ConfigState } from "~/types/config";
43
import type { StoreItem } from "~/types/store";
54
import type {
65
ConfigUpdatePayload,
@@ -263,14 +262,7 @@ export const useSocketStore = defineStore('socket', () => {
263262
setupVisibilityListener()
264263
}
265264

266-
on('configuration', (data: WSEP['configuration']) => {
267-
config.setAll({
268-
app: data.data.config,
269-
presets: data.data.presets,
270-
dl_fields: data.data.dl_fields,
271-
paused: Boolean(data.data.paused)
272-
} as unknown as Partial<ConfigState>)
273-
})
265+
on('connect', () => config.loadConfig(false))
274266

275267
on('connected', (data: WSEP['connected']) => {
276268
if (!data?.data) {
@@ -288,13 +280,6 @@ export const useSocketStore = defineStore('socket', () => {
288280
error.value = null
289281
})
290282

291-
on('active_queue', (data: WSEP['active_queue']) => {
292-
if (!data.data.queue) {
293-
return
294-
}
295-
stateStore.addAll('queue', data.data.queue || {})
296-
})
297-
298283
on('item_added', (data: WSEP['item_added']) => {
299284
stateStore.add('queue', data.data._id, data.data)
300285
toast.success(`Item queued: ${ag(stateStore.get('queue', data.data._id, {} as StoreItem), 'title')}`)

ui/app/types/sockets.d.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { AppConfig, ConfigState } from "./config"
21
import type { StoreItem } from "./store"
32

43
export type Event = {
@@ -21,20 +20,11 @@ export type WSEP = {
2120
connect: null
2221
disconnect: null
2322
connect_error: { message?: string }
24-
configuration: EventPayload<{
25-
config: AppConfig
26-
presets: ConfigState['presets']
27-
dl_fields: ConfigState['dl_fields']
28-
paused: boolean
29-
}>
3023
connected: EventPayload<{
3124
folders?: string[]
3225
history_count?: number
3326
queue?: Record<string, StoreItem>
3427
}>
35-
active_queue: EventPayload<{
36-
queue?: Record<string, StoreItem>
37-
}>
3828
item_added: EventPayload<StoreItem>
3929
item_updated: EventPayload<StoreItem>
4030
item_cancelled: EventPayload<StoreItem>

ui/app/utils/index.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -228,25 +228,39 @@ const encodePath = (item: string): string => {
228228
* @param options - Optional fetch options, automatically extended with common headers and credentials.
229229
* @returns The fetch Response promise.
230230
*/
231-
const request = (url: string, options: RequestInit = {}): Promise<Response> => {
232-
options.method = options.method || 'GET'
233-
options.headers = options.headers || {}; (options as any).withCredentials = true
231+
const request = (url: string, options: RequestInit & { timeout?: number } = {}): Promise<Response> => {
232+
const { timeout, ...fetchOptions } = options
234233

235-
if (undefined === (options.headers as Record<string, any>)['Content-Type']) {
234+
fetchOptions.method = fetchOptions.method || 'GET'
235+
fetchOptions.headers = fetchOptions.headers || {};
236+
(fetchOptions as any).withCredentials = true
237+
238+
if (undefined === (fetchOptions.headers as Record<string, any>)['Content-Type']) {
236239
if (!(options?.body instanceof FormData)) {
237-
; (options.headers as Record<string, any>)['Content-Type'] = 'application/json'
240+
; (fetchOptions.headers as Record<string, any>)['Content-Type'] = 'application/json'
238241
}
239242
}
240243

241-
if (undefined === (options.headers as Record<string, any>)['Accept']) {
242-
; (options.headers as Record<string, any>)['Accept'] = 'application/json'
244+
if (undefined === (fetchOptions.headers as Record<string, any>)['Accept']) {
245+
; (fetchOptions.headers as Record<string, any>)['Accept'] = 'application/json'
243246
}
244247

245248
if (url.startsWith('/')) {
246-
options.credentials = 'same-origin'
249+
fetchOptions.credentials = 'same-origin'
247250
}
248251

249-
return fetch(url.startsWith('/') ? uri(url) : url, options)
252+
let controller: AbortController | undefined
253+
let timer: ReturnType<typeof setTimeout> | undefined
254+
255+
if (typeof timeout === 'number' && timeout > 0) {
256+
controller = new AbortController()
257+
fetchOptions.signal = controller.signal
258+
timer = setTimeout(() => controller!.abort(`Request timed out.`), timeout*1000)
259+
}
260+
261+
return fetch(url.startsWith('/') ? uri(url) : url, fetchOptions).finally(() => {
262+
if (timer) { clearTimeout(timer) }
263+
})
250264
}
251265

252266
/**
@@ -879,10 +893,10 @@ const parse_api_error = async (json: unknown): Promise<string> => {
879893
}
880894

881895
if (payload.error) {
882-
return String(payload.error+(extra_detail ? ` - ${extra_detail}` : ''))
896+
return String(payload.error + (extra_detail ? ` - ${extra_detail}` : ''))
883897
}
884898
if (payload.message) {
885-
return String(payload.message+(extra_detail ? ` - ${extra_detail}` : ''))
899+
return String(payload.message + (extra_detail ? ` - ${extra_detail}` : ''))
886900
}
887901

888902
if (extra_detail) {

0 commit comments

Comments
 (0)