@samitouri / QOSami-HFS / commits / fe9c29f6

admin/internet: better error message

Massimo Melina committed Sep 13, 2023 at 22:37 UTC fe9c29f66e92d342752dd11b1ef188eb3f3342aa
4 files changed +13 -10
admin/src/InternetPage.ts
+2 -2
@@ -21,7 +21,7 @@ export default function InternetPage() {
21 const { data: status, reload: reloadStatus } = useApiEx('get_status')
22 const localColor = with_([status?.http?.error, status?.https?.error], ([h, s]) =>
23 h && s ? 'error' : h || s ? 'warning' : 'success')
24 - const { data: nat, reload, error, loading } = useApiEx('get_nat')
24 + const { data: nat, reload, error, loading, element } = useApiEx('get_nat')
25 const port = nat?.internalPort
26 const wrongMap = nat?.mapped && nat.mapped.private.port !== port
27 const doubleNat = nat?.externalIp && nat.externalIp !== nat.publicIp
@@ -51,7 +51,7 @@ export default function InternetPage() {
51 }
52
53 function networkBox() {
54 - if (error) return "Error"
54 + if (error) return element
55 if (!nat) return h(CircularProgress)
56 return h(Flex, { justifyContent: 'space-around', alignItems: 'center', maxWidth: '40em' },
57 h(Device, { name: "Local network", icon: HomeWorkTwoTone, color: localColor, ip: nat?.localIp,
admin/src/LangPage.ts
+1 -1
@@ -88,7 +88,7 @@ function ForceLang({ langs }: { langs: string[] }) {
88 const K = 'force_lang'
89 const { data, reload, loading } = useApiEx('get_config', { only: [K] })
90 const [lang, setLang] = useState()
91 - useEffect(() => setLang(loading ? lang : data[K]), [loading])
91 + useEffect(() => setLang(data?.[K] ?? lang), [data])
92 const [saving, setSaving] = useState<string>()
93
94 return h(SelectField as Field<string>, {
admin/src/api.ts
+4 -2
@@ -1,7 +1,7 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import { createElement as h, useEffect, useMemo, useRef, useState } from 'react'
4 -import { Dict, err2msg, Falsy, IconBtn, spinner, useStateMounted, wantArray } from './misc'
4 +import { Dict, err2msg, Falsy, IconBtn, spinner, useStateMounted, wantArray, xlate } from './misc'
5 import { Alert } from '@mui/material'
6 import _ from 'lodash'
7 import { state } from './state'
@@ -19,6 +19,7 @@ setDefaultApiCallOptions({
19 }
20 })
21
22 +const ERRORS = { timeout: "Operation timeout" }
23 // expand useApi with things that cannot be shared with Frontend
24 export function useApiEx<T=any>(...args: Parameters<typeof useApi>) {
25 const res = useApi<T>(...args)
@@ -26,7 +27,8 @@ export function useApiEx<T=any>(...args: Parameters<typeof useApi>) {
27 ...res,
28 element: useMemo(() =>
29 !args[0] ? null
29 - : res.error ? h(Alert, { severity: 'error' }, String(res.error), h(IconBtn, { icon: Refresh, onClick: res.reload, sx: { m: '-8px 0 -8px 16px' } }))
30 + : res.error ? h(Alert, { severity: 'error' }, xlate(String(res.error), ERRORS),
31 + h(IconBtn, { icon: Refresh, onClick: res.reload, sx: { m:'-10px 0 -8px 16px' } }) )
32 : res.loading ? spinner()
33 : null,
34 Object.values(res))
shared/api.ts
+6 -5
@@ -9,7 +9,7 @@ export const API_URL = '/~/api/'
9 const timeoutByApi: Dict = {
10 loginSrp1: 90, // support antibrute
11 update: 600, // download can be lengthy
12 - get_nat: 20, // wait more mostly for debug purposes, as we don't want this to take this long
12 + get_nat: 10,
13 get_status: 20 // can be lengthy on slow machines because of the find-process-on-busy-port feature
14 }
15
@@ -28,8 +28,9 @@ export function apiCall<T=any>(cmd: string, params?: Dict, options: ApiCallOptio
28 _.defaults(options, defaultApiCallOptions)
29 const stop = options.modal?.(cmd, params)
30 const controller = new AbortController()
31 + let aborted = ''
32 if (options.timeout !== false)
32 - setTimeout(() => controller.abort('timeout'), 1000*(timeoutByApi[cmd] ?? options.timeout ?? 10))
33 + setTimeout(() => controller.abort(aborted='timeout'), 1000*(timeoutByApi[cmd] ?? options.timeout ?? 10))
34 return Object.assign(fetch(getPrefixUrl() + API_URL + cmd, {
35 method: 'POST',
36 headers: { 'content-type': 'application/json', 'x-hfs-anti-csrf': '1' },
@@ -51,10 +52,10 @@ export function apiCall<T=any>(cmd: string, params?: Dict, options: ApiCallOptio
52 stop?.()
53 if (err?.message?.includes('fetch'))
54 throw Error("Network error")
54 - throw err
55 + throw aborted || err
56 }), {
57 abort() {
57 - controller.abort('cancel')
58 + controller.abort(aborted='cancel')
59 }
60 })
61 }
@@ -96,7 +97,7 @@ export function useApi<T=any>(cmd: string | Falsy, params?: object) {
97 const reload = useCallback(() => loadingRef.current
98 || setForcer(v => v+1) || (reloadingRef.current = pendingPromise()),
99 [setForcer])
99 - return { data, error, reload, loading: data === undefined || Boolean(loadingRef.current || reloadingRef.current) }
100 + return { data, error, reload, loading: Boolean(loadingRef.current || reloadingRef.current) }
101 }
102
103 type EventHandler = (type:string, data?:any) => void