diff --git a/src/api/controller/rootAdmin.js b/src/api/controller/rootAdmin.js index 67eef616e..ad6eb0ef1 100644 --- a/src/api/controller/rootAdmin.js +++ b/src/api/controller/rootAdmin.js @@ -13,6 +13,7 @@ import { import bullBoard from '../bullBoard'; import { insertConfigTenant } from '../services/configTenant'; import mongoClient from '../services/mongoClient'; +import os from 'os'; const app = new Koa(); app.use( @@ -136,11 +137,26 @@ const deleteTenant = async (ctx) => { } }; +const systemInfo = async (ctx) => { + const dbStats = await ctx.rootAdminDb.stats({ scale: 1024 }); + ctx.body = { + database: { + total: dbStats.fsTotalSize, + use: dbStats.fsUsedSize, + }, + loadavg: os.loadavg(), + totalmem: os.totalmem(), + freemem: os.freemem(), + }; +}; + app.use(route.get('/tenant', getTenant)); app.use(route.post('/tenant', postTenant)); app.use(route.put('/tenant/:id', putTenant)); app.use(route.delete('/tenant', deleteTenant)); +app.use(route.get('/system', systemInfo)); + app.use(async (ctx) => { ctx.status = 404; }); diff --git a/src/app/js/root-admin/SystemLoad.js b/src/app/js/root-admin/SystemLoad.js new file mode 100644 index 000000000..da3e79713 --- /dev/null +++ b/src/app/js/root-admin/SystemLoad.js @@ -0,0 +1,246 @@ +import React, { useEffect, useState } from 'react'; +import CircularProgress from '@mui/material/CircularProgress'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import PropTypes from 'prop-types'; +import { sizeConverter } from './rootAdminUtils'; +import { Tooltip } from '@mui/material'; +import MemoryIcon from '@mui/icons-material/Memory'; +import StorageIcon from '@mui/icons-material/Storage'; +import SdStorageIcon from '@mui/icons-material/SdStorage'; + +const greenColor = { + red: 19, + green: 233, + blue: 19, +}; +const yellowColor = { + red: 255, + green: 255, + blue: 0, +}; +const redColor = { + red: 255, + green: 0, + blue: 0, +}; + +// https://stackoverflow.com/questions/30143082/how-to-get-color-value-from-gradient-by-percentage-with-javascript +// https://gist.github.com/gskema/2f56dc2e087894ffc756c11e6de1b5ed +const colorGradient = (fadeFraction, rgbColor1, rgbColor2, rgbColor3) => { + let color1 = rgbColor1; + let color2 = rgbColor2; + let fade = fadeFraction; + + // Do we have 3 colours for the gradient? Need to adjust the params. + if (rgbColor3) { + fade = fade * 2; + + // Find which interval to use and adjust the fade percentage + if (fade >= 1) { + fade -= 1; + color1 = rgbColor2; + color2 = rgbColor3; + } + } + + const diffRed = color2.red - color1.red; + const diffGreen = color2.green - color1.green; + const diffBlue = color2.blue - color1.blue; + + const red = Math.floor(color1.red + diffRed * fade); + const green = Math.floor(color1.green + diffGreen * fade); + const blue = Math.floor(color1.blue + diffBlue * fade); + + return `rgb(${red},${green},${blue})`; +}; + +const CircularProgressWithLabel = ({ value, title }) => { + return ( + + + + + + + + + + {`${Math.round(value)}%`} + + + + + ); +}; + +CircularProgressWithLabel.propTypes = { + /** + * The value of the progress indicator for the determinate variant. + * Value between 0 and 100. + * @default 0 + */ + value: PropTypes.number.isRequired, + title: PropTypes.string.isRequired, +}; + +const SystemLoad = () => { + const [loadTitle, setLoadTitle] = useState(''); + const [loadAvg, setLoadAvg] = useState(0); + + const [memTitle, setMemTile] = useState(''); + const [memUsage, setMemUsage] = useState(0); + + const [storageTitle, setStorageTile] = useState(''); + const [storageUsage, setStorageUsage] = useState(0); + + const fetchSystemLoad = () => { + fetch('/rootAdmin/system', { + credentials: 'include', + }) + .then((response) => response.json()) + .then((data) => { + setLoadTitle( + `${data.loadavg[0]}% / 1 min, ${data.loadavg[1]}% / 5 min, ${data.loadavg[2]}% / 15 min`, + ); + setLoadAvg(data.loadavg[0]); + + const memPercent = + ((data.totalmem - data.freemem) / data.totalmem) * 100; + const totalMen = sizeConverter(data.totalmem / 1024); + const useMem = sizeConverter( + (data.totalmem - data.freemem) / 1024, + ); + + setMemUsage(memPercent); + setMemTile(`${useMem} / ${totalMen}`); + + const storagePercent = + (data.database.use / data.database.total) * 100; + const totalStorage = sizeConverter(data.database.total); + const useStorage = sizeConverter(data.database.use); + + setStorageUsage(storagePercent); + setStorageTile(`${useStorage} / ${totalStorage}`); + }); + }; + + // Fetch on loads + useEffect(() => { + fetchSystemLoad(); + }, []); + + // Fetch evey 5 seconds + useEffect(() => { + const timer = setInterval(() => { + fetchSystemLoad(); + }, 5000); + return () => { + clearInterval(timer); + }; + }, []); + + return ( + <> +
+ + +
+
+ + +
+
+ + +
+ + ); +}; + +export default SystemLoad; diff --git a/src/app/js/root-admin/Tenants.js b/src/app/js/root-admin/Tenants.js index 4f4a42052..6ad78d45b 100644 --- a/src/app/js/root-admin/Tenants.js +++ b/src/app/js/root-admin/Tenants.js @@ -26,6 +26,7 @@ import { Typography, } from '@mui/material'; import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings'; +import { sizeConverter } from './rootAdminUtils'; const baseUrl = getHost(); @@ -386,21 +387,7 @@ const Tenants = ({ handleLogout }) => { flex: 2, sortable: true, valueFormatter: (params) => { - if (params.value == null) { - return '-'; - } - - const mbSize = (params.value / 1024).toFixed(2); - - if (mbSize > 1024) { - return `${(mbSize / 1024).toFixed(2)} Gio`; - } - - if (mbSize > 1) { - return `${mbSize} Mio`; - } - - return `${params.value} Kio`; + return sizeConverter(params.value); }, }, { @@ -442,7 +429,13 @@ const Tenants = ({ handleLogout }) => { return ( <> -
+
row._id} rows={tenants} diff --git a/src/app/js/root-admin/index.js b/src/app/js/root-admin/index.js index aa9d25b25..12e600a19 100644 --- a/src/app/js/root-admin/index.js +++ b/src/app/js/root-admin/index.js @@ -23,6 +23,7 @@ import ExitToAppIcon from '@mui/icons-material/ExitToApp'; import Tenants from './Tenants'; import LoginForm from './LoginForm'; import { ROOT_ROLE } from '../../../common/tools/tenantTools'; +import SystemLoad from './SystemLoad'; const localesMUI = new Map([ ['fr', { ...frFR, ...frFRDatagrid }], @@ -31,7 +32,7 @@ const localesMUI = new Map([ const locale = getLocale(); -export default function RootAdmin() { +function RootAdmin() { const [isLoggedIn, setIsLoggedIn] = useState(false); const [role, setRole] = useState(''); @@ -68,25 +69,35 @@ export default function RootAdmin() { - + Configuration des instances {isLoggedIn && ( - + <> + + + )} diff --git a/src/app/js/root-admin/rootAdminUtils.js b/src/app/js/root-admin/rootAdminUtils.js new file mode 100644 index 000000000..ced652987 --- /dev/null +++ b/src/app/js/root-admin/rootAdminUtils.js @@ -0,0 +1,17 @@ +export const sizeConverter = (value) => { + if (value == null) { + return '-'; + } + + const mbSize = (value / 1024).toFixed(2); + + if (mbSize > 1024) { + return `${(mbSize / 1024).toFixed(2)} Gio`; + } + + if (mbSize > 1) { + return `${mbSize} Mio`; + } + + return `${value} Kio`; +}; diff --git a/src/app/root-admin.ejs b/src/app/root-admin.ejs index f1fed3da3..387abe87c 100755 --- a/src/app/root-admin.ejs +++ b/src/app/root-admin.ejs @@ -9,9 +9,14 @@ + -
+