diff --git a/portal-ui/src/screens/Console/Buckets/ViewBucket/AddEvent.tsx b/portal-ui/src/screens/Console/Buckets/ViewBucket/AddEvent.tsx index 0670aafd7..994ac0b07 100644 --- a/portal-ui/src/screens/Console/Buckets/ViewBucket/AddEvent.tsx +++ b/portal-ui/src/screens/Console/Buckets/ViewBucket/AddEvent.tsx @@ -126,9 +126,13 @@ class AddEvent extends React.Component { api .invoke("GET", `/api/v1/admin/arns`) .then((res: ArnList) => { + let arns: string[] = []; + if (res.arns !== null) { + arns = res.arns; + } this.setState({ addLoading: false, - arnList: res.arns, + arnList: arns, addError: "" }); }) diff --git a/portal-ui/src/screens/Console/Dashboard/Dashboard.tsx b/portal-ui/src/screens/Console/Dashboard/Dashboard.tsx index bf8280bfa..ef39edead 100644 --- a/portal-ui/src/screens/Console/Dashboard/Dashboard.tsx +++ b/portal-ui/src/screens/Console/Dashboard/Dashboard.tsx @@ -14,232 +14,210 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React from "react"; -import { makeStyles, useTheme } from "@material-ui/core/styles"; +import React, { useEffect, useState } from "react"; +import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import clsx from "clsx"; import Grid from "@material-ui/core/Grid"; import Paper from "@material-ui/core/Paper"; import Typography from "@material-ui/core/Typography"; -import { - CartesianGrid, - Line, - LineChart, - ResponsiveContainer, - Legend, - Tooltip, - XAxis, - YAxis -} from "recharts"; -import NetworkCheckIcon from '@material-ui/icons/NetworkCheck'; -import PieChartIcon from '@material-ui/icons/PieChart'; -import ViewHeadlineIcon from '@material-ui/icons/ViewHeadline'; +import NetworkCheckIcon from "@material-ui/icons/NetworkCheck"; +import PieChartIcon from "@material-ui/icons/PieChart"; +import ViewHeadlineIcon from "@material-ui/icons/ViewHeadline"; +import { Usage } from "./types"; +import api from "../../../common/api"; -const useStyles = makeStyles(theme => ({ - root: { - display: "flex" - }, - toolbar: { - paddingRight: 24 // keep right padding when drawer closed - }, - toolbarIcon: { - display: "flex", - alignItems: "center", - justifyContent: "flex-end", - padding: "0 8px", - ...theme.mixins.toolbar - }, - appBar: { - zIndex: theme.zIndex.drawer + 1, - transition: theme.transitions.create(["width", "margin"], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen - }) - }, - - menuButton: { - marginRight: 36 - }, - menuButtonHidden: { - display: "none" - }, - title: { - flexGrow: 1 - }, - drawerPaperClose: { - overflowX: "hidden", - transition: theme.transitions.create("width", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen - }), - width: theme.spacing(7), - [theme.breakpoints.up("sm")]: { - width: theme.spacing(9) - } - }, - appBarSpacer: theme.mixins.toolbar, - content: { - flexGrow: 1, - height: "100vh", - overflow: "auto" - }, - container: { - paddingBottom: theme.spacing(4), - "& h6": { - color: "#777777", - fontSize: 14, +const styles = (theme: Theme) => + createStyles({ + root: { + display: "flex" }, - "& p": { - "& span": { - fontSize: 16, + toolbar: { + paddingRight: 24 // keep right padding when drawer closed + }, + toolbarIcon: { + display: "flex", + alignItems: "center", + justifyContent: "flex-end", + padding: "0 8px", + ...theme.mixins.toolbar + }, + appBar: { + zIndex: theme.zIndex.drawer + 1, + transition: theme.transitions.create(["width", "margin"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen + }) + }, + + menuButton: { + marginRight: 36 + }, + menuButtonHidden: { + display: "none" + }, + title: { + flexGrow: 1 + }, + drawerPaperClose: { + overflowX: "hidden", + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen + }), + width: theme.spacing(7), + [theme.breakpoints.up("sm")]: { + width: theme.spacing(9) + } + }, + appBarSpacer: theme.mixins.toolbar, + content: { + flexGrow: 1, + height: "100vh", + overflow: "auto" + }, + container: { + paddingBottom: theme.spacing(4), + "& h6": { + color: "#777777", + fontSize: 14 }, + "& p": { + "& span": { + fontSize: 16 + } + } }, - }, - paper: { - padding: theme.spacing(2), - display: "flex", - overflow: "auto", - flexDirection: "column" - }, - fixedHeight: { - minHeight: 240, - }, - consumptionValue: { - color: "#000000", - fontSize: "60px", - fontWeight: "bold", - }, - icon: { - marginRight: 10, - color: "#777777", - }, -})); + paper: { + padding: theme.spacing(2), + display: "flex", + overflow: "auto", + flexDirection: "column" + }, + fixedHeight: { + minHeight: 240 + }, + consumptionValue: { + color: "#000000", + fontSize: "60px", + fontWeight: "bold" + }, + icon: { + marginRight: 10, + color: "#777777" + } + }); -export default function Dashboard() { - const theme = useTheme(); - const classes = useStyles(theme); +interface IDashboardProps { + classes: any; +} + +const Dashboard = ({ classes }: IDashboardProps) => { const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight); + const [usage, setUsage] = useState(null); + const [loading, isLoading] = useState(true); + const [error, setError] = useState(""); - const data = [39,31,37,29,28,31,31,34,39,40,35,40,24,25,30,20,28,38,23,28,22,39,37,37,40,28,28,28,24,31].map((usage, day ) => ({ usage, day })); - const data2 = [25,32,21,40,31,30,23,40,26,32].map((usage, day ) => ({ usage, day })); + useEffect(() => { + if (loading) { + fetchUsage(); + } + }, [loading]); + + const fetchUsage = () => { + api + .invoke("GET", `/api/v1/admin/info`) + .then((res: Usage) => { + setUsage(res); + setError(""); + isLoading(false); + }) + .catch(err => { + setError(err); + isLoading(false); + }); + }; + const prettyUsage = (usage: string | undefined) => { + if (usage == undefined) { + return "0"; + } + return niceBytes(usage); + }; + const units = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + + const niceBytes = (x: string) => { + let l = 0, + n = parseInt(x, 10) || 0; + + while (n >= 1024 && ++l) { + n = n / 1024; + } + //include a decimal point and a tenths-place digit if presenting + //less than ten of KB or greater units + return n.toFixed(n < 10 && l > 0 ? 1 : 0) + " " + units[l]; + }; + const prettyNumber = (usage: number | undefined) => { + if (usage == undefined) { + return 0; + } + return usage; + }; return ( - - - - - - - + + + + + MinIO Console + + + + + + + + + Total Buckets + - - All Buckets + + {usage ? prettyNumber(usage.buckets) : 0} + + + + + + + + + + + Total Objects + - - 238 - - - - - - - + + {usage ? prettyNumber(usage.objects) : 0} + + + + + + + + + + + Usage + - - Usage - - - 375TB - - - - - - - - - - Egress this Month - - - 1.5TB - + + {usage ? prettyUsage(usage.usage + "") : 0} + + + - - - - Daily Average Usage - - - - - - - - - - - - - - - Daily Network Egress - - - - - - - - - - - - - - + ); -} +}; + +export default withStyles(styles)(Dashboard); diff --git a/portal-ui/src/screens/Console/Dashboard/types.tsx b/portal-ui/src/screens/Console/Dashboard/types.tsx new file mode 100644 index 000000000..9ab864942 --- /dev/null +++ b/portal-ui/src/screens/Console/Dashboard/types.tsx @@ -0,0 +1,21 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2020 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +export interface Usage { + usage: number; + buckets: number; + objects: number; +} diff --git a/portal-ui/src/screens/Console/Policies/AddPolicy.tsx b/portal-ui/src/screens/Console/Policies/AddPolicy.tsx index ba5db7bab..b28917481 100644 --- a/portal-ui/src/screens/Console/Policies/AddPolicy.tsx +++ b/portal-ui/src/screens/Console/Policies/AddPolicy.tsx @@ -32,7 +32,7 @@ import api from "../../../common/api"; import "codemirror/lib/codemirror.css"; import "codemirror/theme/material.css"; import PolicyBuilder from "./PolicyBuilder"; -import {Policy} from "./types"; +import { Policy } from "./types"; require("codemirror/mode/javascript/javascript"); const styles = (theme: Theme) => @@ -144,7 +144,6 @@ class AddPolicy extends React.Component { id="standard-basic" fullWidth label="Policy Name" - value={policyEdit ? policyEdit.name : ""} onChange={(e: React.ChangeEvent) => { this.setState({ policyName: e.target.value }); }} @@ -170,17 +169,19 @@ class AddPolicy extends React.Component {
- {!policyEdit && - - } + {!policyEdit && ( + + + + )} {addLoading && ( diff --git a/portal-ui/src/screens/Console/Policies/Policies.tsx b/portal-ui/src/screens/Console/Policies/Policies.tsx index c300bfdf3..70d324a4c 100644 --- a/portal-ui/src/screens/Console/Policies/Policies.tsx +++ b/portal-ui/src/screens/Console/Policies/Policies.tsx @@ -120,7 +120,7 @@ class Policies extends React.Component { deleteOpen: false, selectedPolicy: "", filterPolicies: "", - policyEdit: null, + policyEdit: null }; fetchRecords() { @@ -187,7 +187,7 @@ class Policies extends React.Component { deleteOpen, selectedPolicy, filterPolicies, - policyEdit, + policyEdit } = this.state; const offset = page * rowsPerPage; @@ -250,7 +250,7 @@ class Policies extends React.Component { onClick={() => { this.setState({ addScreenOpen: true, - policyEdit: null, + policyEdit: null }); }} > @@ -294,7 +294,7 @@ class Policies extends React.Component { onClick={() => { this.setState({ addScreenOpen: true, - policyEdit: row, + policyEdit: row }); }} > diff --git a/portal-ui/src/screens/Console/Policies/PolicyBuilder.tsx b/portal-ui/src/screens/Console/Policies/PolicyBuilder.tsx index a9b30af09..28cf2c076 100644 --- a/portal-ui/src/screens/Console/Policies/PolicyBuilder.tsx +++ b/portal-ui/src/screens/Console/Policies/PolicyBuilder.tsx @@ -15,16 +15,24 @@ // along with this program. If not, see . import React from "react"; -import {createStyles, Theme, withStyles} from "@material-ui/core/styles"; +import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; import Grid from "@material-ui/core/Grid"; import remove from "lodash/remove"; -import {Checkbox, FormControlLabel, FormGroup, FormLabel, Paper, Radio, RadioGroup} from "@material-ui/core"; -import {Statement} from "./types"; +import { + Checkbox, + FormControlLabel, + FormGroup, + FormLabel, + Paper, + Radio, + RadioGroup +} from "@material-ui/core"; +import { Statement } from "./types"; const styles = (theme: Theme) => createStyles({ root: { - flexGrow: 1, + flexGrow: 1 }, errorBlock: { color: "red" @@ -38,9 +46,9 @@ const styles = (theme: Theme) => }, paper: { padding: theme.spacing(1), - textAlign: 'center', - color: theme.palette.text.secondary, - }, + textAlign: "center", + color: theme.palette.text.secondary + } }); interface IPolicyBuilderProps { @@ -56,22 +64,29 @@ interface IPolicyBuilderState { currentStatementRead: boolean; } -class PolicyBuilder extends React.Component { +class PolicyBuilder extends React.Component< + IPolicyBuilderProps, + IPolicyBuilderState +> { state: IPolicyBuilderState = { policyString: "", statements: [], currentStatement: { effect: "", actions: [], - resources: [], + resources: [] }, currentStatementWrite: false, - currentStatementRead: false, + currentStatementRead: false }; render() { const { classes, policyDefinition } = this.props; - const { currentStatement,currentStatementWrite, currentStatementRead } = this.state; + const { + currentStatement, + currentStatementWrite, + currentStatementRead + } = this.state; console.log(currentStatement); return (
@@ -85,12 +100,25 @@ class PolicyBuilder extends React.Component, value: string) => { - this.setState( { currentStatement: { ...currentStatement, effect: value }}); + onChange={( + e: React.ChangeEvent, + value: string + ) => { + this.setState({ + currentStatement: { ...currentStatement, effect: value } + }); }} > - } label="Deny" /> - } label="Allow" /> + } + label="Deny" + /> + } + label="Allow" + /> @@ -102,16 +130,30 @@ class PolicyBuilder extends React.Component, checked: boolean) => { - const readActions = ["s3:ListBucket", "s3:GetObject", "s3:GetBucketLocation"]; + onChange={( + e: React.ChangeEvent, + checked: boolean + ) => { + const readActions = [ + "s3:ListBucket", + "s3:GetObject", + "s3:GetBucketLocation" + ]; let actions = currentStatement.actions; if (checked) { - actions.push(...readActions) + actions.push(...readActions); } else { - actions = remove(actions, (action) => readActions.includes(action)) + actions = remove(actions, action => + readActions.includes(action) + ); } - this.setState( { currentStatement: { ...currentStatement, actions: actions }}); - this.setState( { currentStatementRead: checked }); + this.setState({ + currentStatement: { + ...currentStatement, + actions: actions + } + }); + this.setState({ currentStatementRead: checked }); }} name="read" /> @@ -122,16 +164,26 @@ class PolicyBuilder extends React.Component, checked: boolean) => { + onChange={( + e: React.ChangeEvent, + checked: boolean + ) => { const writeActions = ["s3:PutObject"]; let actions = currentStatement.actions; if (checked) { - actions.push(...writeActions) + actions.push(...writeActions); } else { - actions = remove(actions, (action) => writeActions.includes(action)) + actions = remove(actions, action => + writeActions.includes(action) + ); } - this.setState( { currentStatement: { ...currentStatement, actions: actions }}); - this.setState( { currentStatementWrite: checked }); + this.setState({ + currentStatement: { + ...currentStatement, + actions: actions + } + }); + this.setState({ currentStatementWrite: checked }); }} name="write" /> @@ -150,7 +202,7 @@ class PolicyBuilder extends React.Component
- ) + ); } }