Merge pull request #152 from Tsu-ba-me/issues/135-server-power-control

Web UI: add power controls for server VMs
main
Digimer 4 years ago committed by GitHub
commit 4c03811a62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 82
      cgi-bin/set_power
  2. 8
      share/words.xml
  3. 193
      striker-ui/components/Servers.tsx
  4. 1
      striker-ui/lib/consts/DEFAULT_THEME.ts
  5. 2
      striker-ui/out/_next/static/3u8VlnDkIB4kvVnXXJdo9/_buildManifest.js
  6. 0
      striker-ui/out/_next/static/3u8VlnDkIB4kvVnXXJdo9/_ssgManifest.js
  7. 1
      striker-ui/out/_next/static/chunks/576-8c091667959361a02026.js
  8. 1
      striker-ui/out/_next/static/chunks/642-0e4040fe0a744c110cab.js
  9. 2
      striker-ui/out/_next/static/chunks/pages/_app-88ba5c92fa3ac1d52da7.js
  10. 1
      striker-ui/out/_next/static/chunks/pages/index-312a12570203472e8a0b.js
  11. 1
      striker-ui/out/_next/static/chunks/pages/index-ca8a2930d2c5ccf8a7f5.js
  12. 4
      striker-ui/out/index.html
  13. 37
      striker-ui/package-lock.json
  14. 1
      striker-ui/package.json

@ -20,6 +20,8 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
my $anvil = Anvil::Tools->new();
$anvil->Log->level({ set => 2 });
sub handle_invalid_uuid
{
my $parameters = shift;
@ -114,6 +116,56 @@ sub set_host_power
}
}
sub set_server_power
{
my $parameters = shift;
my $server_uuid = $parameters->{server_uuid};
my $on = $parameters->{on};
my $query = "
SELECT
server_anvil_uuid
FROM
servers
WHERE
server_uuid = ".$anvil->Database->quote($server_uuid)."
;";
my $results = $anvil->Database->query({ query => $query, source => $THIS_FILE, line => __LINE__ });
my $count = @{$results};
if ($count == 1)
{
my $row = $results->[0];
my $anvil_uuid = $row->[0];
my $host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid, host_uuid => $host_uuid, server_uuid => $server_uuid } });
if ($on)
{
$anvil->Database->insert_or_update_jobs({
job_command => $anvil->data->{path}{exe}{'anvil-boot-server'}." --server-uuid ".$server_uuid,
job_host_uuid => $host_uuid,
job_description => "job_0341",
job_name => "cgi-bin::set_power::server::on",
job_progress => 0,
job_title => "job_0340"
});
}
else
{
$anvil->Database->insert_or_update_jobs({
job_command => $anvil->data->{path}{exe}{'anvil-shutdown-server'}." --server-uuid ".$server_uuid,
job_host_uuid => $host_uuid,
job_description => "job_0343",
job_name => "cgi-bin::set_power::server::off",
job_progress => 0,
job_title => "job_0342"
});
}
}
}
$anvil->Get->switches;
$anvil->Database->connect;
@ -166,20 +218,23 @@ if (not $is_decode_json_success)
});
}
my $anvil_uuid = exists $request_body->{anvil_uuid} ? $request_body->{anvil_uuid} : $anvil->data->{switches}{'anvil-uuid'};
my $host_uuid = exists $request_body->{host_uuid} ? $request_body->{host_uuid} : $anvil->data->{switches}{'host-uuid'};
my $is_on = exists $request_body->{is_on} ? $request_body->{is_on} : $anvil->data->{switches}{'is-on'};
my $anvil_uuid_variable_name = "anvil UUID";
my $host_uuid_variable_name = "host UUID";
my $anvil_uuid = exists $request_body->{anvil_uuid} ? $request_body->{anvil_uuid} : $anvil->data->{switches}{'anvil-uuid'};
my $host_uuid = exists $request_body->{host_uuid} ? $request_body->{host_uuid} : $anvil->data->{switches}{'host-uuid'};
my $server_uuid_list = exists $request_body->{server_uuid_list} ? $request_body->{server_uuid_list} : [ $anvil->data->{switches}{'server-uuid'} ];
my $is_on = exists $request_body->{is_on} ? $request_body->{is_on} : $anvil->data->{switches}{'is-on'};
my $anvil_uuid_variable_name = "anvil UUID";
my $host_uuid_variable_name = "host UUID";
my $server_uuid_list_variable_name = "server UUID list";
$anvil->Log->variables({
source => $THIS_FILE,
line => __LINE__,
level => 2,
list => {
anvil_uuid => $anvil_uuid,
host_uuid => $host_uuid,
is_on => $is_on
anvil_uuid => $anvil_uuid,
host_uuid => $host_uuid,
server_uuid_list => $server_uuid_list,
is_on => $is_on
}
});
@ -205,5 +260,16 @@ elsif ($host_uuid)
handle_invalid_uuid({ name => $host_uuid_variable_name, uuid => $host_uuid });
}
}
elsif ($server_uuid_list)
{
foreach (@{$server_uuid_list})
{
my $server_uuid = $_;
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, list => { server_uuid => $server_uuid } });
set_server_power({ server_uuid => $server_uuid, on => $is_on });
}
}
print JSON->new->utf8->encode($response_body)."\n";

@ -1096,6 +1096,14 @@ It should be provisioned in the next minute or two.</key>
<key name="job_0338">Host Leave Cluster</key>
<!-- cgi-bin/set_membership,leave,job_description -->
<key name="job_0339">Make target host leave its anvil cluster.</key>
<!-- cgi-bin/set_power,server,on,job_title -->
<key name="job_0340">Power On Server VM</key>
<!-- cgi-bin/set_power,server,on,job_description -->
<key name="job_0341">Power on the target server VM by executing a start script on the first host within the cluster.</key>
<!-- cgi-bin/set_power,server,off,job_title -->
<key name="job_0342">Power Off Server VM</key>
<!-- cgi-bin/set_power,server,off,job_description -->
<key name="job_0343">Power off the target server VM by executing a stop script on the first a host within the cluster.</key>
<!-- Log entries -->
<key name="log_0001">Starting: [#!variable!program!#].</key>

@ -1,16 +1,40 @@
import { useContext } from 'react';
import { List, ListItem, Divider, Box } from '@material-ui/core';
import { useState, useContext } from 'react';
import {
List,
ListItem,
Divider,
Box,
IconButton,
Button,
Checkbox,
Menu,
MenuItem,
Typography,
} from '@material-ui/core';
import EditIcon from '@material-ui/icons/Edit';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import CheckIcon from '@material-ui/icons/Check';
import { makeStyles } from '@material-ui/core/styles';
import { Panel } from './Panels';
import PeriodicFetch from '../lib/fetchers/periodicFetch';
import { HeaderText, BodyText } from './Text';
import { HOVER, DIVIDER } from '../lib/consts/DEFAULT_THEME';
import {
HOVER,
DIVIDER,
TEXT,
BLUE,
RED,
GREY,
BLACK,
} from '../lib/consts/DEFAULT_THEME';
import { AnvilContext } from './AnvilContext';
import serverState from '../lib/consts/SERVERS';
import Decorator, { Colours } from './Decorator';
import Spinner from './Spinner';
import hostsSanitizer from '../lib/sanitizers/hostsSanitizer';
import putJSON from '../lib/fetchers/putJSON';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
@ -43,6 +67,43 @@ const useStyles = makeStyles((theme) => ({
hostBox: {
paddingTop: 0,
},
checkbox: {
paddingTop: '.8em',
},
menuItem: {
backgroundColor: TEXT,
paddingRight: '3em',
'&:hover': {
backgroundColor: TEXT,
},
},
editButton: {
borderRadius: 8,
backgroundColor: GREY,
'&:hover': {
backgroundColor: TEXT,
},
},
editButtonBox: {
paddingTop: '.3em',
},
dropdown: {
paddingTop: '.8em',
paddingBottom: '.8em',
},
power: {
color: BLACK,
},
on: {
color: BLUE,
},
off: {
color: RED,
},
all: {
paddingTop: '.5em',
paddingLeft: '.3em',
},
}));
const selectDecorator = (state: string): Colours => {
@ -58,7 +119,15 @@ const selectDecorator = (state: string): Colours => {
}
};
type ButtonLabels = 'on' | 'off';
const buttonLabels: ButtonLabels[] = ['on', 'off'];
const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [showCheckbox, setShowCheckbox] = useState<boolean>(false);
const [allSelected, setAllSelected] = useState<boolean>(false);
const [selected, setSelected] = useState<string[]>([]);
const { uuid } = useContext(AnvilContext);
const classes = useStyles();
@ -66,15 +135,113 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
`${process.env.NEXT_PUBLIC_API_URL}/get_servers?anvil_uuid=${uuid}`,
);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>): void => {
setAnchorEl(event.currentTarget);
};
const handlePower = (label: ButtonLabels) => {
setAnchorEl(null);
if (selected.length) {
putJSON('/set_power', {
server_uuid_list: selected,
is_on: label === 'on',
});
}
};
const handleChange = (server_uuid: string): void => {
const index = selected.indexOf(server_uuid);
if (index === -1) selected.push(server_uuid);
else selected.splice(index, 1);
setSelected([...selected]);
};
const anvilIndex = anvil.findIndex((a) => a.anvil_uuid === uuid);
const filteredHosts = hostsSanitizer(anvil[anvilIndex]?.hosts);
return (
<Panel>
<div className={classes.headerPadding}>
<HeaderText text="Servers" />
</div>
<Box className={classes.headerPadding} display="flex">
<Box flexGrow={1}>
<HeaderText text="Servers" />
</Box>
<Box className={classes.editButtonBox}>
<IconButton
className={classes.editButton}
style={{ color: BLACK }}
onClick={() => setShowCheckbox(!showCheckbox)}
>
{showCheckbox ? <CheckIcon /> : <EditIcon />}
</IconButton>
</Box>
</Box>
{showCheckbox && (
<>
<Box className={classes.headerPadding} display="flex">
<Box flexGrow={1} className={classes.dropdown}>
<Button
variant="contained"
startIcon={<MoreVertIcon />}
onClick={handleClick}
style={{ textTransform: 'none' }}
>
<Typography className={classes.power} variant="subtitle1">
Power
</Typography>
</Button>
<Menu
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={() => setAnchorEl(null)}
>
{buttonLabels.map((label: ButtonLabels) => {
return (
<MenuItem
onClick={() => handlePower(label)}
className={classes.menuItem}
key={label}
>
<Typography
className={classes[label]}
variant="subtitle1"
>
{label.replace(/^[a-z]/, (c) => c.toUpperCase())}
</Typography>
</MenuItem>
);
})}
</Menu>
</Box>
</Box>
<Box display="flex">
<Box>
<Checkbox
style={{ color: TEXT }}
color="secondary"
checked={allSelected}
onChange={() => {
if (!allSelected)
setSelected(
data.servers.map(
(server: AnvilServer) => server.server_uuid,
),
);
else setSelected([]);
setAllSelected(!allSelected);
}}
/>
</Box>
<Box className={classes.all}>
<BodyText text="All" />
</Box>
</Box>
</>
)}
{!isLoading ? (
<Box className={classes.root}>
<List component="nav">
@ -88,6 +255,20 @@ const Servers = ({ anvil }: { anvil: AnvilListItem[] }): JSX.Element => {
key={server.server_uuid}
>
<Box display="flex" flexDirection="row" width="100%">
{showCheckbox && (
<Box className={classes.checkbox}>
<Checkbox
style={{ color: TEXT }}
color="secondary"
checked={
selected.find(
(s) => s === server.server_uuid,
) !== undefined
}
onChange={() => handleChange(server.server_uuid)}
/>
</Box>
)}
<Box p={1}>
<Decorator
colour={selectDecorator(server.server_state)}

@ -12,5 +12,6 @@ export const UNSELECTED = '#666';
export const DIVIDER = '#888';
export const SELECTED_ANVIL = '#00ff00';
export const DISABLED = '#AAA';
export const BLACK = '#343434';
export const BORDER_RADIUS = '3px';

@ -1 +1 @@
self.__BUILD_MANIFEST={__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":["static/chunks/576-8c091667959361a02026.js","static/chunks/pages/index-312a12570203472e8a0b.js"],"/_error":["static/chunks/pages/_error-a9f53acb468cbab8a6cb.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
self.__BUILD_MANIFEST={__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":["static/chunks/642-0e4040fe0a744c110cab.js","static/chunks/pages/index-ca8a2930d2c5ccf8a7f5.js"],"/_error":["static/chunks/pages/_error-a9f53acb468cbab8a6cb.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,4 +1,4 @@
<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/_next/static/css/1b1a1a5807b24bb728c2.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1b1a1a5807b24bb728c2.css" data-n-g=""/><noscript data-n-css=""></noscript><link rel="preload" href="/_next/static/chunks/webpack-189c53927ffd3caf09c3.js" as="script"/><link rel="preload" href="/_next/static/chunks/framework-2191d16384373197bc0a.js" as="script"/><link rel="preload" href="/_next/static/chunks/main-6c0a2257b76a50556a7f.js" as="script"/><link rel="preload" href="/_next/static/chunks/pages/_app-b5d6858d1426469549f6.js" as="script"/><link rel="preload" href="/_next/static/chunks/576-8c091667959361a02026.js" as="script"/><link rel="preload" href="/_next/static/chunks/pages/index-312a12570203472e8a0b.js" as="script"/><style id="jss-server-side">.MuiButtonBase-root {
<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/_next/static/css/1b1a1a5807b24bb728c2.css" as="style"/><link rel="stylesheet" href="/_next/static/css/1b1a1a5807b24bb728c2.css" data-n-g=""/><noscript data-n-css=""></noscript><link rel="preload" href="/_next/static/chunks/webpack-189c53927ffd3caf09c3.js" as="script"/><link rel="preload" href="/_next/static/chunks/framework-2191d16384373197bc0a.js" as="script"/><link rel="preload" href="/_next/static/chunks/main-6c0a2257b76a50556a7f.js" as="script"/><link rel="preload" href="/_next/static/chunks/pages/_app-88ba5c92fa3ac1d52da7.js" as="script"/><link rel="preload" href="/_next/static/chunks/642-0e4040fe0a744c110cab.js" as="script"/><link rel="preload" href="/_next/static/chunks/pages/index-ca8a2930d2c5ccf8a7f5.js" as="script"/><style id="jss-server-side">.MuiButtonBase-root {
color: inherit;
border: 0;
cursor: pointer;
@ -526,4 +526,4 @@
.jss3 {
display: block;
}
}</style></head><body><div id="__next"><header class="MuiPaper-root MuiAppBar-root MuiAppBar-positionStatic MuiAppBar-colorPrimary jss4 MuiPaper-elevation4"><div class="MuiBox-root jss9"><div class="MuiBox-root jss10 jss6"><button class="MuiButtonBase-root MuiButton-root MuiButton-text" tabindex="0" type="button"><span class="MuiButton-label"><img alt="" src="/pngs/logo.png" width="160" height="40"/></span></button></div><div class="MuiBox-root jss11 jss6 jss7"><a href="/cgi-bin/striker?files=true"><img alt="" src="/pngs/files_on.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?jobs=true"><img alt="" src="/pngs/tasks_no-jobs_icon.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?configure=true"><img alt="" src="/pngs/configure_icon_on.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?striker=true"><img alt="" src="/pngs/striker_icon_on.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?anvil=true"><img alt="" src="/pngs/anvil_icon_on.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?email=true"><img alt="" src="/pngs/email_on.png" width="40em" height="40em"/></a><a href="https://alteeve.com/w/Support"><img alt="" src="/pngs/help_icon_on.png" width="40em" height="40em"/></a></div></div></header></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/","query":{},"buildId":"isEArxf7KboS1BZr7i3tW","nextExport":true,"autoExport":true,"isFallback":false}</script><script nomodule="" src="/_next/static/chunks/polyfills-eef578260fd80f8fff94.js"></script><script src="/_next/static/chunks/webpack-189c53927ffd3caf09c3.js" async=""></script><script src="/_next/static/chunks/framework-2191d16384373197bc0a.js" async=""></script><script src="/_next/static/chunks/main-6c0a2257b76a50556a7f.js" async=""></script><script src="/_next/static/chunks/pages/_app-b5d6858d1426469549f6.js" async=""></script><script src="/_next/static/chunks/576-8c091667959361a02026.js" async=""></script><script src="/_next/static/chunks/pages/index-312a12570203472e8a0b.js" async=""></script><script src="/_next/static/isEArxf7KboS1BZr7i3tW/_buildManifest.js" async=""></script><script src="/_next/static/isEArxf7KboS1BZr7i3tW/_ssgManifest.js" async=""></script></body></html>
}</style></head><body><div id="__next"><header class="MuiPaper-root MuiAppBar-root MuiAppBar-positionStatic MuiAppBar-colorPrimary jss4 MuiPaper-elevation4"><div class="MuiBox-root jss9"><div class="MuiBox-root jss10 jss6"><button class="MuiButtonBase-root MuiButton-root MuiButton-text" tabindex="0" type="button"><span class="MuiButton-label"><img alt="" src="/pngs/logo.png" width="160" height="40"/></span></button></div><div class="MuiBox-root jss11 jss6 jss7"><a href="/cgi-bin/striker?files=true"><img alt="" src="/pngs/files_on.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?jobs=true"><img alt="" src="/pngs/tasks_no-jobs_icon.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?configure=true"><img alt="" src="/pngs/configure_icon_on.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?striker=true"><img alt="" src="/pngs/striker_icon_on.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?anvil=true"><img alt="" src="/pngs/anvil_icon_on.png" width="40em" height="40em"/></a><a href="/cgi-bin/striker?email=true"><img alt="" src="/pngs/email_on.png" width="40em" height="40em"/></a><a href="https://alteeve.com/w/Support"><img alt="" src="/pngs/help_icon_on.png" width="40em" height="40em"/></a></div></div></header></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/","query":{},"buildId":"3u8VlnDkIB4kvVnXXJdo9","nextExport":true,"autoExport":true,"isFallback":false}</script><script nomodule="" src="/_next/static/chunks/polyfills-eef578260fd80f8fff94.js"></script><script src="/_next/static/chunks/webpack-189c53927ffd3caf09c3.js" async=""></script><script src="/_next/static/chunks/framework-2191d16384373197bc0a.js" async=""></script><script src="/_next/static/chunks/main-6c0a2257b76a50556a7f.js" async=""></script><script src="/_next/static/chunks/pages/_app-88ba5c92fa3ac1d52da7.js" async=""></script><script src="/_next/static/chunks/642-0e4040fe0a744c110cab.js" async=""></script><script src="/_next/static/chunks/pages/index-ca8a2930d2c5ccf8a7f5.js" async=""></script><script src="/_next/static/3u8VlnDkIB4kvVnXXJdo9/_buildManifest.js" async=""></script><script src="/_next/static/3u8VlnDkIB4kvVnXXJdo9/_ssgManifest.js" async=""></script></body></html>

@ -447,6 +447,14 @@
"react-transition-group": "^4.4.0"
}
},
"@material-ui/icons": {
"version": "4.11.2",
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz",
"integrity": "sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ==",
"requires": {
"@babel/runtime": "^7.4.4"
}
},
"@material-ui/styles": {
"version": "4.11.4",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz",
@ -2347,7 +2355,8 @@
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"dev": true
},
"locate-path": {
@ -4748,22 +4757,22 @@
}
},
"react": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz",
"integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==",
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"react-dom": {
"version": "17.0.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz",
"integrity": "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==",
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"scheduler": "^0.20.1"
"scheduler": "^0.20.2"
}
},
"react-is": {
@ -4999,9 +5008,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"scheduler": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.1.tgz",
"integrity": "sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@ -5393,9 +5402,9 @@
}
},
"swr": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/swr/-/swr-0.4.2.tgz",
"integrity": "sha512-SKGxcAfyijj/lE5ja5zVMDqJNudASH3WZPRUakDVOePTM18FnsXgugndjl9BSRwj+jokFCulMDe7F2pQL+VhEw==",
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/swr/-/swr-0.5.6.tgz",
"integrity": "sha512-Bmx3L4geMZjYT5S2Z6EE6/5Cx6v1Ka0LhqZKq8d6WL2eu9y6gHWz3dUzfIK/ymZVHVfwT/EweFXiYGgfifei3w==",
"requires": {
"dequal": "2.0.2"
}

@ -13,6 +13,7 @@
},
"dependencies": {
"@material-ui/core": "^4.11.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/styles": "^4.11.4",
"next": "^10.2.3",
"pretty-bytes": "^5.6.0",

Loading…
Cancel
Save