mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Copying some settings values to status to avoid calling both /settings and /status.
This commit is contained in:
parent
78218d3559
commit
dbdcc7a57c
10 changed files with 105 additions and 68 deletions
|
|
@ -12,57 +12,53 @@ import {AppsRoute} from "./components/Apps";
|
|||
import {InventoryRoute} from "./components/Inventory";
|
||||
import {ArchiveRoute} from "./components/Archive";
|
||||
import {FilesRoute} from "./components/Files";
|
||||
import {useSettingsInterval} from "./hooks/customHooks";
|
||||
import {useStatusInterval} from "./hooks/customHooks";
|
||||
import {MapRoute} from "./components/Map";
|
||||
import {SettingsContext, ThemeContext} from "./contexts/contexts";
|
||||
import {StatusContext, ThemeContext} from "./contexts/contexts";
|
||||
import {ThemeWrapper} from "./components/Theme";
|
||||
import {Dashboard} from "./Dashboard";
|
||||
|
||||
function PageNotFound() {
|
||||
return (
|
||||
<Container fluid>
|
||||
<h1 className="display-4">Page Not Found!</h1>
|
||||
<p>The page you requested cannot be found</p>
|
||||
</Container>
|
||||
)
|
||||
return (<Container fluid>
|
||||
<h1 className="display-4">Page Not Found!</h1>
|
||||
<p>The page you requested cannot be found</p>
|
||||
</Container>)
|
||||
}
|
||||
|
||||
function Footer() {
|
||||
const {t} = useContext(ThemeContext);
|
||||
|
||||
const {version} = useContext(SettingsContext);
|
||||
const {status} = useContext(StatusContext);
|
||||
const {version} = status;
|
||||
return <Container textAlign='center' style={{marginTop: '1.5em', marginBottom: '1em'}}>
|
||||
<span {...t}>WROLPi v{version} <a href='https://github.com/lrnselfreliance/wrolpi'>GitHub</a></span>
|
||||
</Container>
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const {settings} = useSettingsInterval();
|
||||
const status = useStatusInterval();
|
||||
|
||||
return (
|
||||
<ThemeWrapper>
|
||||
<SettingsContext.Provider value={settings}>
|
||||
<header>
|
||||
<NavBar/>
|
||||
</header>
|
||||
<>
|
||||
<Routes>
|
||||
<Route path='/videos/video/:videoId' exact element={<VideoWrapper/>}/>
|
||||
<Route path='/videos/channel/:channelId/video/:videoId' exact element={<VideoWrapper/>}/>
|
||||
<Route path="/" exact element={<Dashboard/>}/>
|
||||
<Route path="/videos/*" element={<VideosRoute/>}/>
|
||||
<Route path="/admin/*" element={<Admin/>}/>
|
||||
<Route path="/apps/*" element={<AppsRoute/>}/>
|
||||
<Route path="/inventory/*" element={<InventoryRoute/>}/>
|
||||
<Route path='/archive/*' element={<ArchiveRoute/>}/>
|
||||
<Route path='/map/*' element={<MapRoute/>}/>
|
||||
<Route path='/files/*' element={<FilesRoute/>}/>
|
||||
<Route element={<PageNotFound/>}/>
|
||||
</Routes>
|
||||
</>
|
||||
<SemanticToastContainer position="top-right"/>
|
||||
<Footer/>
|
||||
</SettingsContext.Provider>
|
||||
</ThemeWrapper>
|
||||
);
|
||||
return (<ThemeWrapper>
|
||||
<StatusContext.Provider value={status}>
|
||||
<header>
|
||||
<NavBar/>
|
||||
</header>
|
||||
<>
|
||||
<Routes>
|
||||
<Route path='/videos/video/:videoId' exact element={<VideoWrapper/>}/>
|
||||
<Route path='/videos/channel/:channelId/video/:videoId' exact element={<VideoWrapper/>}/>
|
||||
<Route path="/" exact element={<Dashboard/>}/>
|
||||
<Route path="/videos/*" element={<VideosRoute/>}/>
|
||||
<Route path="/admin/*" element={<Admin/>}/>
|
||||
<Route path="/apps/*" element={<AppsRoute/>}/>
|
||||
<Route path="/inventory/*" element={<InventoryRoute/>}/>
|
||||
<Route path='/archive/*' element={<ArchiveRoute/>}/>
|
||||
<Route path='/map/*' element={<MapRoute/>}/>
|
||||
<Route path='/files/*' element={<FilesRoute/>}/>
|
||||
<Route element={<PageNotFound/>}/>
|
||||
</Routes>
|
||||
</>
|
||||
<SemanticToastContainer position="top-right"/>
|
||||
<Footer/>
|
||||
</StatusContext.Provider>
|
||||
</ThemeWrapper>);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {LoadStatistic, PageContainer, SearchInput, useTitle} from "./components/Common";
|
||||
import {useSearchFiles, useStatus} from "./hooks/customHooks";
|
||||
import {useSearchFiles} from "./hooks/customHooks";
|
||||
import React, {useContext, useState} from "react";
|
||||
import {SettingsContext} from "./contexts/contexts";
|
||||
import {StatusContext} from "./contexts/contexts";
|
||||
import {DownloadMenu} from "./components/Upload";
|
||||
import {FilesSearchView} from "./components/Files";
|
||||
import {Header, Segment, Statistic, StatisticGroup} from "./components/Theme";
|
||||
|
|
@ -14,10 +14,11 @@ export function Dashboard() {
|
|||
|
||||
const {searchStr, setSearchStr} = useSearchFiles();
|
||||
|
||||
const {status} = useContext(StatusContext);
|
||||
const wrol_mode = status ? status['wrol_mode'] : null;
|
||||
|
||||
const [downloadOpen, setDownloadOpen] = useState(false);
|
||||
const onDownloadOpen = (name) => setDownloadOpen(!!name);
|
||||
const {wrol_mode} = useContext(SettingsContext);
|
||||
|
||||
const downloads = <DownloadMenu onOpen={onDownloadOpen}/>;
|
||||
|
||||
// Only show dashboard parts if not searching.
|
||||
|
|
@ -32,8 +33,7 @@ export function Dashboard() {
|
|||
</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
return (<PageContainer>
|
||||
<SearchInput clearable
|
||||
searchStr={searchStr}
|
||||
onSubmit={setSearchStr}
|
||||
|
|
@ -43,12 +43,12 @@ export function Dashboard() {
|
|||
style={{marginBottom: '2em'}}
|
||||
/>
|
||||
{body}
|
||||
</PageContainer>
|
||||
)
|
||||
</PageContainer>)
|
||||
}
|
||||
|
||||
function DashboardStatus() {
|
||||
const {status} = useStatus();
|
||||
const {status} = useContext(StatusContext);
|
||||
|
||||
let percent = 0;
|
||||
let load = {};
|
||||
let cores = 0;
|
||||
|
|
@ -57,12 +57,11 @@ function DashboardStatus() {
|
|||
percent = status['cpu_info']['percent'];
|
||||
load = status['load'];
|
||||
cores = status['cpu_info']['cores'];
|
||||
pending_downloads = status['downloads']['pending_downloads'];
|
||||
}
|
||||
|
||||
const {download_manager_disabled, download_manager_stopped} = useContext(SettingsContext);
|
||||
if (pending_downloads === 0 && (download_manager_disabled || download_manager_stopped)) {
|
||||
pending_downloads = 'x';
|
||||
const {downloads} = status;
|
||||
if (downloads) {
|
||||
pending_downloads = downloads['disabled'] ? 'x' : downloads['pending'];
|
||||
}
|
||||
|
||||
return <Segment>
|
||||
|
|
@ -78,8 +77,8 @@ function DashboardStatus() {
|
|||
</StatisticGroup>
|
||||
|
||||
<Header as='h3'>Bandwidth</Header>
|
||||
{status && status['bandwidth'] ?
|
||||
status['bandwidth'].map(i => <BandwidthProgressCombined key={i['name']} bandwidth={i}/>) :
|
||||
{status && status['bandwidth'] ? status['bandwidth'].map(i => <BandwidthProgressCombined key={i['name']}
|
||||
bandwidth={i}/>) :
|
||||
<ProgressPlaceholder/>}
|
||||
</Link>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {Card, Container, Image, Input, Pagination, Responsive, TableCell, TableR
|
|||
import {Link, NavLink} from "react-router-dom";
|
||||
import Message from "semantic-ui-react/dist/commonjs/collections/Message";
|
||||
import {useDownloaders, useHotspot, useThrottle} from "../hooks/customHooks";
|
||||
import {darkTheme, SettingsContext, ThemeContext} from "../contexts/contexts";
|
||||
import {darkTheme, StatusContext, ThemeContext} from "../contexts/contexts";
|
||||
import {Button, CardIcon, Form, Header, Icon, Menu, Popup, Statistic} from "./Theme";
|
||||
|
||||
export const API_URI = `http://${window.location.host}/api`;
|
||||
|
|
@ -471,8 +471,8 @@ export function SearchInput({
|
|||
}
|
||||
|
||||
export function WROLModeMessage({content}) {
|
||||
const settings = useContext(SettingsContext);
|
||||
const wrol_mode = settings ? settings.wrol_mode : null;
|
||||
const {status} = useContext(StatusContext);
|
||||
const wrol_mode = status ? status.wrol_mode : null;
|
||||
|
||||
if (wrol_mode) {
|
||||
return <Message icon='lock' header='WROL Mode Enabled' content={content}/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, {useContext} from "react";
|
||||
import {NavLink} from "react-router-dom";
|
||||
import {Dropdown, Menu, Responsive} from "semantic-ui-react";
|
||||
import {SettingsContext, ThemeContext} from "../contexts/contexts";
|
||||
import {StatusContext, ThemeContext} from "../contexts/contexts";
|
||||
import Icon from "semantic-ui-react/dist/commonjs/elements/Icon";
|
||||
import {DarkModeToggle} from "./Common";
|
||||
|
||||
|
|
@ -64,8 +64,8 @@ function MenuLink(props) {
|
|||
}
|
||||
|
||||
export function NavBar() {
|
||||
const settings = useContext(SettingsContext);
|
||||
const wrol_mode = settings ? settings.wrol_mode : null;
|
||||
const {status} = useContext(StatusContext);
|
||||
const wrol_mode = status ? status['wrol_mode'] : null;
|
||||
const topNavText = wrol_mode ? <>WROLPi <Icon name='lock'/></> : 'WROLPi';
|
||||
const {i} = useContext(ThemeContext);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import {Container} from "semantic-ui-react";
|
||||
import {getSettings, saveSettings} from "../../api";
|
||||
import {getStatus, saveSettings} from "../../api";
|
||||
import {PageContainer, TabLinks, Toggle} from "../Common";
|
||||
import {Route, Routes} from "react-router-dom";
|
||||
import {ThemeContext} from "../../contexts/contexts";
|
||||
|
|
@ -20,8 +20,8 @@ class WROLMode extends React.Component {
|
|||
|
||||
async componentDidMount() {
|
||||
try {
|
||||
let settings = await getSettings();
|
||||
this.setState({ready: true, WROLMode: settings.wrol_mode});
|
||||
const {wrol_mode} = await getStatus();
|
||||
this.setState({ready: true, WROLMode: wrol_mode});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ class WROLMode extends React.Component {
|
|||
}
|
||||
|
||||
return <ThemeContext.Consumer>
|
||||
{({i, t}) => <Container fluid>
|
||||
{({t}) => <Container fluid>
|
||||
|
||||
<Header as="h1">WROL Mode</Header>
|
||||
<Header as='h4'>
|
||||
|
|
|
|||
|
|
@ -10,3 +10,5 @@ export const ThemeContext = React.createContext({
|
|||
setTheme: () => {
|
||||
},
|
||||
});
|
||||
|
||||
export const StatusContext = React.createContext({});
|
||||
|
|
|
|||
|
|
@ -656,6 +656,19 @@ export const useStatus = () => {
|
|||
return {status, fetchStatus}
|
||||
}
|
||||
|
||||
export const useStatusInterval = () => {
|
||||
const {status, fetchStatus} = useStatus();
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
fetchStatus();
|
||||
}, 1000 * 3);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return {status, fetchStatus};
|
||||
}
|
||||
|
||||
export const useVideoStatistics = () => {
|
||||
const [statistics, setStatistics] = useState({});
|
||||
|
||||
|
|
|
|||
|
|
@ -877,13 +877,26 @@ class DownloadManager:
|
|||
)
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def get_summary():
|
||||
def get_summary(self) -> dict:
|
||||
"""
|
||||
Get a summary of what Downloads are happening as well as the status of the DownloadManager.
|
||||
"""
|
||||
with get_db_curs() as curs:
|
||||
stmt = """SELECT COUNT(*) AS pending_downloads FROM download WHERE status='pending'"""
|
||||
stmt = """
|
||||
SELECT
|
||||
COUNT(*) FILTER (WHERE status = 'pending') AS pending_downloads,
|
||||
COUNT(*) FILTER (WHERE frequency IS NOT NULL) AS recurring_downloads
|
||||
FROM download
|
||||
"""
|
||||
curs.execute(stmt)
|
||||
result = [dict(i) for i in curs.fetchall()][0]
|
||||
return result
|
||||
counts = list(map(dict, curs.fetchall()))[0]
|
||||
summary = dict(
|
||||
pending=counts['pending_downloads'],
|
||||
recurring=counts['recurring_downloads'],
|
||||
disabled=self.disabled.is_set(),
|
||||
stopped=self.stopped.is_set(),
|
||||
)
|
||||
return summary
|
||||
|
||||
@optional_session
|
||||
def get_pending_downloads(self, session: Session) -> List[Download]:
|
||||
|
|
|
|||
|
|
@ -314,7 +314,17 @@ async def throttle_off(_: Request):
|
|||
async def get_status(_: Request):
|
||||
s = await status.get_status()
|
||||
downloads = download_manager.get_summary()
|
||||
ret = dict(downloads=downloads, **s.__dict__)
|
||||
ret = dict(
|
||||
bandwidth=s.bandwidth,
|
||||
cpu_info=s.cpu_info,
|
||||
downloads=downloads,
|
||||
drives=s.drives,
|
||||
hotspot_status=admin.hotspot_status().name,
|
||||
load=s.load,
|
||||
throttle_status=admin.throttle_status().name,
|
||||
version=__version__,
|
||||
wrol_mode=wrol_mode_enabled(),
|
||||
)
|
||||
return json_response(ret)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -310,6 +310,10 @@ def test_get_status(test_client, test_session):
|
|||
assert 'cpu_info' in response.json and isinstance(response.json['cpu_info'], dict)
|
||||
assert 'load' in response.json and isinstance(response.json['load'], dict)
|
||||
assert 'drives' in response.json and isinstance(response.json['drives'], list)
|
||||
assert 'downloads' in response.json and isinstance(response.json['downloads'], dict)
|
||||
assert 'hotspot_status' in response.json and isinstance(response.json['hotspot_status'], str)
|
||||
assert 'throttle_status' in response.json and isinstance(response.json['throttle_status'], str)
|
||||
assert 'version' in response.json and isinstance(response.json['version'], str)
|
||||
|
||||
|
||||
def test_post_download(test_session, test_client, test_download_manager_config):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue