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