mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Displaying plug icon when API fails to respond. Using window.apiDown to communicate to other hooks to stop fetching, the status interval will keep checking if the API is back up.
This commit is contained in:
parent
e7415873a5
commit
01338e4d89
7 changed files with 76 additions and 21 deletions
|
|
@ -13,7 +13,7 @@ import {FilesRoute} from "./components/Files";
|
|||
import {QueryProvider, StatusProvider} from "./hooks/customHooks";
|
||||
import {MapRoute} from "./components/Map";
|
||||
import {MediaContextProvider, mediaStyles, StatusContext, ThemeContext} from "./contexts/contexts";
|
||||
import {Header, Segment, ThemeProvider} from "./components/Theme";
|
||||
import {Header, ThemeProvider} from "./components/Theme";
|
||||
import {DashboardPage} from "./DashboardPage";
|
||||
import {DonatePage} from "./components/DonatePage";
|
||||
import {useEventsInterval} from "./Events";
|
||||
|
|
@ -21,8 +21,6 @@ import {SemanticToastContainer} from "react-semantic-toasts-2";
|
|||
import {FilePreviewProvider} from "./components/FilePreview";
|
||||
import {TagsProvider} from "./Tags";
|
||||
import {ZimRoute} from "./components/Zim";
|
||||
import {IframeViewer} from "./components/Common";
|
||||
import {HELP_VIEWER_URI} from "./components/Vars";
|
||||
|
||||
function PageNotFound() {
|
||||
const {t} = useContext(ThemeContext);
|
||||
|
|
@ -39,8 +37,12 @@ function Dot() {
|
|||
function Footer() {
|
||||
const {t} = useContext(ThemeContext);
|
||||
const {status} = useContext(StatusContext);
|
||||
let {version} = status;
|
||||
version = version ? `v${version}` : null;
|
||||
let version;
|
||||
try {
|
||||
version = status?.version ? `v${status.version}` : null;
|
||||
} catch (e) {
|
||||
// Not logging because this is not that important.
|
||||
}
|
||||
return <Container textAlign='center' style={{marginTop: '1.5em', marginBottom: '1em', ...t}}>
|
||||
<span {...t}>
|
||||
WROLPi {version} <Dot/>
|
||||
|
|
|
|||
|
|
@ -394,6 +394,8 @@ function DashboardStatus() {
|
|||
if (status && status['nic_bandwidth_stats']) {
|
||||
bandwidths = Object.entries(status['nic_bandwidth_stats'])
|
||||
.map(([name, bandwidth]) => <BandwidthProgressCombined key={name} bandwidth={bandwidth}/>);
|
||||
} else if (window.apiDown) { // apiDown is set in useStatus
|
||||
bandwidths = null;
|
||||
}
|
||||
|
||||
return <Segment>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import React, {useEffect} from 'react';
|
||||
import {useRecurringTimeout} from "./hooks/customHooks";
|
||||
import {getEvents} from "./api";
|
||||
import {ApiDownError, getEvents} from "./api";
|
||||
import {toast} from "react-semantic-toasts-2";
|
||||
import {StatusContext} from "./contexts/contexts";
|
||||
|
||||
const apiEventName = 'apiEvent';
|
||||
|
||||
|
|
@ -108,12 +109,19 @@ export function useEventsInterval() {
|
|||
}
|
||||
|
||||
const fetchEvents = async () => {
|
||||
if (window.apiDown) { // apiDown is set in useStatus
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await getEvents(now.current);
|
||||
setNow(response['now']);
|
||||
setEvents(response['events']);
|
||||
} catch (e) {
|
||||
setEvents(null);
|
||||
if (e instanceof ApiDownError) {
|
||||
// API is down, do not log this error.
|
||||
return;
|
||||
}
|
||||
// Ignore SyntaxError because they happen when the API is down.
|
||||
if (!(e instanceof SyntaxError)) {
|
||||
console.error(e);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, {useEffect} from "react";
|
||||
import {deleteTag, getTags, saveTag} from "./api";
|
||||
import {ApiDownError, deleteTag, getTags, saveTag} from "./api";
|
||||
import {Dimmer, Divider, Form, Grid, GridColumn, GridRow, Label, TableCell, TableRow,} from "semantic-ui-react";
|
||||
import {
|
||||
APIButton,
|
||||
|
|
@ -48,6 +48,9 @@ export function useTags() {
|
|||
const {getLocationStr} = React.useContext(QueryContext);
|
||||
|
||||
const fetchTags = async () => {
|
||||
if (window.apiDown) { // apiDown is set in useStatus
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const t = await getTags();
|
||||
setTags(t);
|
||||
|
|
@ -55,6 +58,10 @@ export function useTags() {
|
|||
} catch (e) {
|
||||
setTags(undefined);
|
||||
setTagNames(undefined);
|
||||
if (e instanceof ApiDownError) {
|
||||
// API is down, do not log this error.
|
||||
return;
|
||||
}
|
||||
// Ignore SyntaxError because they happen when the API is down.
|
||||
if (!(e instanceof SyntaxError)) {
|
||||
console.error(e);
|
||||
|
|
@ -62,6 +69,13 @@ export function useTags() {
|
|||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!window.apiDown) {
|
||||
// Fetch tags when the API comes back up.
|
||||
fetchTags();
|
||||
}
|
||||
}, [window.apiDown]);
|
||||
|
||||
const findTagByName = (name) => {
|
||||
if (!tags || tags.length === 0) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -20,22 +20,32 @@ function timeoutPromise(ms, promise) {
|
|||
})
|
||||
}
|
||||
|
||||
export class ApiDownError extends Error {
|
||||
}
|
||||
|
||||
const apiDownError = new ApiDownError('API is down');
|
||||
|
||||
async function apiCall(url, method, body, ms = 60_000) {
|
||||
let init = {method};
|
||||
if (body !== undefined) {
|
||||
init.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
// Create a fetch promise, this will be awaited by the timeout.
|
||||
let promise = fetch(url, init);
|
||||
|
||||
let promise;
|
||||
try {
|
||||
// Create a fetch promise, this will be awaited by the timeout.
|
||||
promise = fetch(url, init);
|
||||
|
||||
// await the response or error.
|
||||
const response = await timeoutPromise(ms, promise);
|
||||
if (200 <= response.status && response.status < 300) {
|
||||
// Request was successful.
|
||||
return response;
|
||||
}
|
||||
if (response.status === 502) {
|
||||
// API is down.
|
||||
throw apiDownError;
|
||||
}
|
||||
// Request encountered an error.
|
||||
let copy = response.clone();
|
||||
const content = await copy.json();
|
||||
|
|
@ -51,6 +61,10 @@ async function apiCall(url, method, body, ms = 60_000) {
|
|||
}
|
||||
return response;
|
||||
} catch (e) {
|
||||
if (e instanceof ApiDownError) {
|
||||
// Throw ApiDownError immediately without logging.
|
||||
throw e;
|
||||
}
|
||||
// Ignore SyntaxError because they happen when the API is down.
|
||||
if (!(e instanceof SyntaxError)) {
|
||||
console.error(e);
|
||||
|
|
@ -236,15 +250,6 @@ export async function downloadVideoMetadata(videoUrl, destination) {
|
|||
return await postDownload(downloadData);
|
||||
}
|
||||
|
||||
export async function getDirectories(search_str) {
|
||||
let form_data = {'search_str': search_str || null};
|
||||
const response = await apiPost(`${API_URI}/files/directories`, form_data);
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export async function getStatus() {
|
||||
const response = await apiGet(`${API_URI}/status`);
|
||||
if (response.status === 200) {
|
||||
|
|
|
|||
|
|
@ -178,7 +178,16 @@ export function NavBar() {
|
|||
<Icon loading name='circle notch' size='large'/>
|
||||
</Link>;
|
||||
|
||||
let apiDownIcon;
|
||||
if (window.apiDown) {
|
||||
apiDownIcon = <Popup
|
||||
content='API is not responding'
|
||||
trigger={<Icon name='plug'/>}
|
||||
/>
|
||||
}
|
||||
|
||||
const icons = <React.Fragment>
|
||||
<NavIconWrapper>{apiDownIcon}</NavIconWrapper>
|
||||
<NavIconWrapper>{processingIcon}</NavIconWrapper>
|
||||
<NavIconWrapper>{powerIcon}</NavIconWrapper>
|
||||
<NavIconWrapper>{warningIcon}</NavIconWrapper>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, {useContext, useEffect, useRef, useState} from "react";
|
||||
import {
|
||||
ApiDownError,
|
||||
createChannel,
|
||||
fetchDecoded,
|
||||
fetchDomains,
|
||||
|
|
@ -804,6 +805,9 @@ export const useSettings = () => {
|
|||
const [pending, setPending] = useState(false);
|
||||
|
||||
const fetchSettings = async () => {
|
||||
if (window.apiDown) {
|
||||
return;
|
||||
}
|
||||
setPending(true);
|
||||
try {
|
||||
setSettings(await getSettings())
|
||||
|
|
@ -840,9 +844,15 @@ export const useStatus = () => {
|
|||
|
||||
const fetchStatus = async () => {
|
||||
try {
|
||||
const s = await getStatus();
|
||||
setStatus(s);
|
||||
setStatus(await getStatus());
|
||||
window.apiDown = false;
|
||||
} catch (e) {
|
||||
if (e instanceof ApiDownError) {
|
||||
// API is down, do not log this error.
|
||||
window.apiDown = true;
|
||||
setStatus({});
|
||||
return;
|
||||
}
|
||||
// Ignore SyntaxError because they happen when the API is down.
|
||||
if (!(e instanceof SyntaxError)) {
|
||||
console.error(e);
|
||||
|
|
@ -850,6 +860,11 @@ export const useStatus = () => {
|
|||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Used to tell other hooks to stop fetching intervals.
|
||||
window.apiDown = false;
|
||||
}, []);
|
||||
|
||||
return {status, fetchStatus}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue