Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fce361e5bd | ||
|
|
ed6d6e8b9d | ||
|
|
406709f66b | ||
|
|
3ac45a2211 | ||
|
|
716f886780 | ||
|
|
4ef498f0c3 | ||
|
|
5e764e61ba | ||
|
|
1466632fd6 | ||
|
|
0c43e5c3f4 | ||
|
|
7e9d581277 | ||
|
|
c928972137 | ||
|
|
78884e3806 | ||
|
|
f6ac7e047e | ||
|
|
e1fdf3fb28 | ||
|
|
e4510cbc18 |
@@ -101,8 +101,6 @@ Additionally, you can create policies to limit the privileges for `console` user
|
||||
To run the server:
|
||||
|
||||
```
|
||||
export CONSOLE_HMAC_JWT_SECRET=YOURJWTSIGNINGSECRET
|
||||
|
||||
#required to encrypt jwet payload
|
||||
export CONSOLE_PBKDF_PASSPHRASE=SECRET
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
`Console` will authenticate against `Kubernetes`using bearer tokens via HTTP `Authorization` header. The user will provide this token once
|
||||
in the login form, Console will validate it against Kubernetes (list apis) and if valid will generate and return a new Console sessions
|
||||
with encrypted claims (the user Service account token will be inside the JWT in the data field)
|
||||
with encrypted claims (the user Service account token will be inside the session encrypted token
|
||||
|
||||
# Kubernetes
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.4.1
|
||||
image: minio/console:v0.4.3
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
args:
|
||||
- server
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.4.1
|
||||
image: minio/console:v0.4.3
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_OPERATOR_MODE
|
||||
|
||||
80
models/object_legal_hold_status.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// ObjectLegalHoldStatus object legal hold status
|
||||
//
|
||||
// swagger:model objectLegalHoldStatus
|
||||
type ObjectLegalHoldStatus string
|
||||
|
||||
const (
|
||||
|
||||
// ObjectLegalHoldStatusEnabled captures enum value "enabled"
|
||||
ObjectLegalHoldStatusEnabled ObjectLegalHoldStatus = "enabled"
|
||||
|
||||
// ObjectLegalHoldStatusDisabled captures enum value "disabled"
|
||||
ObjectLegalHoldStatusDisabled ObjectLegalHoldStatus = "disabled"
|
||||
)
|
||||
|
||||
// for schema
|
||||
var objectLegalHoldStatusEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []ObjectLegalHoldStatus
|
||||
if err := json.Unmarshal([]byte(`["enabled","disabled"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
objectLegalHoldStatusEnum = append(objectLegalHoldStatusEnum, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (m ObjectLegalHoldStatus) validateObjectLegalHoldStatusEnum(path, location string, value ObjectLegalHoldStatus) error {
|
||||
if err := validate.EnumCase(path, location, value, objectLegalHoldStatusEnum, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates this object legal hold status
|
||||
func (m ObjectLegalHoldStatus) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
// value enum
|
||||
if err := m.validateObjectLegalHoldStatusEnum("", "body", m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
83
models/put_object_legal_hold_request.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// PutObjectLegalHoldRequest put object legal hold request
|
||||
//
|
||||
// swagger:model putObjectLegalHoldRequest
|
||||
type PutObjectLegalHoldRequest struct {
|
||||
|
||||
// status
|
||||
// Required: true
|
||||
Status ObjectLegalHoldStatus `json:"status"`
|
||||
}
|
||||
|
||||
// Validate validates this put object legal hold request
|
||||
func (m *PutObjectLegalHoldRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateStatus(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PutObjectLegalHoldRequest) validateStatus(formats strfmt.Registry) error {
|
||||
|
||||
if err := m.Status.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("status")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *PutObjectLegalHoldRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *PutObjectLegalHoldRequest) UnmarshalBinary(b []byte) error {
|
||||
var res PutObjectLegalHoldRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -23,10 +23,9 @@ import (
|
||||
"github.com/minio/minio/pkg/env"
|
||||
)
|
||||
|
||||
// ConsoleSTSAndJWTDurationSeconds returns the default session duration for the STS requested tokens and the generated JWTs.
|
||||
// Ideally both values should match so jwt and Minio sts sessions expires at the same time.
|
||||
func GetConsoleSTSAndJWTDurationInSeconds() int {
|
||||
duration, err := strconv.Atoi(env.Get(ConsoleSTSAndJWTDurationSeconds, "3600"))
|
||||
// ConsoleSTSDurationSeconds returns the default session duration for the STS requested tokens.
|
||||
func GetConsoleSTSDurationInSeconds() int {
|
||||
duration, err := strconv.Atoi(env.Get(ConsoleSTSDurationSeconds, "3600"))
|
||||
if err != nil {
|
||||
duration = 3600
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package token
|
||||
|
||||
const (
|
||||
ConsoleSTSAndJWTDurationSeconds = "CONSOLE_STS_AND_JWT_DURATION_SECONDS"
|
||||
ConsolePBKDFPassphrase = "CONSOLE_PBKDF_PASSPHRASE"
|
||||
ConsolePBKDFSalt = "CONSOLE_PBKDF_SALT"
|
||||
ConsoleSTSDurationSeconds = "CONSOLE_STS_DURATION_SECONDS"
|
||||
ConsolePBKDFPassphrase = "CONSOLE_PBKDF_PASSPHRASE"
|
||||
ConsolePBKDFSalt = "CONSOLE_PBKDF_SALT"
|
||||
)
|
||||
|
||||
@@ -60,17 +60,17 @@ func TestJWTAuthenticate(t *testing.T) {
|
||||
funcAssert.Equal(claims.SecretAccessKey, creds.SecretAccessKey)
|
||||
funcAssert.Equal(claims.SessionToken, creds.SessionToken)
|
||||
}
|
||||
// Test-2 : SessionTokenAuthenticate() return an error because of a tampered jwt
|
||||
// Test-2 : SessionTokenAuthenticate() return an error because of a tampered token
|
||||
if _, err := SessionTokenAuthenticate(badToken); err != nil {
|
||||
funcAssert.Equal("session token internal data is malformed", err.Error())
|
||||
}
|
||||
// Test-3 : SessionTokenAuthenticate() return an error because of an empty jwt
|
||||
// Test-3 : SessionTokenAuthenticate() return an error because of an empty token
|
||||
if _, err := SessionTokenAuthenticate(""); err != nil {
|
||||
funcAssert.Equal("session token missing", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsJWTValid(t *testing.T) {
|
||||
func TestSessionTokenValid(t *testing.T) {
|
||||
funcAssert := assert.New(t)
|
||||
// Test-1 : SessionTokenAuthenticate() provided token is valid
|
||||
funcAssert.Equal(true, IsSessionTokenValid(goodToken))
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
"local-storage-fallback": "^4.1.1",
|
||||
"lodash": "^4.17.19",
|
||||
"moment": "^2.24.0",
|
||||
"npm": "^6.14.4",
|
||||
"react": "^16.13.1",
|
||||
"react-app-rewire-hot-loader": "^2.0.1",
|
||||
"react-app-rewired": "^2.1.6",
|
||||
@@ -40,7 +39,7 @@
|
||||
"react-moment": "^0.9.7",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "3.4.1",
|
||||
"react-scripts": "3.4.4",
|
||||
"recharts": "^1.8.5",
|
||||
"redux": "^4.0.4",
|
||||
"redux-thunk": "^2.3.0",
|
||||
|
||||
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 47 KiB |
@@ -15,7 +15,13 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import { Redirect, Route, Router, Switch } from "react-router-dom";
|
||||
import {
|
||||
Redirect,
|
||||
Route,
|
||||
Router,
|
||||
Switch,
|
||||
BrowserRouter,
|
||||
} from "react-router-dom";
|
||||
import history from "./history";
|
||||
import Login from "./screens/LoginPage/LoginPage";
|
||||
import Console from "./screens/Console/Console";
|
||||
@@ -27,6 +33,22 @@ import { userLoggedIn } from "./actions";
|
||||
import LoginCallback from "./screens/LoginPage/LoginCallback";
|
||||
import { hot } from "react-hot-loader/root";
|
||||
|
||||
interface ProtectedRouteProps {
|
||||
loggedIn: boolean;
|
||||
component: any;
|
||||
}
|
||||
|
||||
export class ProtectedRoute extends React.Component<ProtectedRouteProps> {
|
||||
render() {
|
||||
const Component = this.props.component;
|
||||
return this.props.loggedIn ? (
|
||||
<Component />
|
||||
) : (
|
||||
<Redirect to={{ pathname: "/login" }} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const isLoggedIn = () => {
|
||||
return (
|
||||
storage.getItem("token") !== undefined &&
|
||||
@@ -47,29 +69,14 @@ interface RoutesProps {
|
||||
}
|
||||
|
||||
class Routes extends React.Component<RoutesProps> {
|
||||
componentDidMount(): void {
|
||||
if (isLoggedIn()) {
|
||||
this.props.userLoggedIn(true);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const loggedIn = isLoggedIn();
|
||||
return (
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route exact path="/oauth_callback" component={LoginCallback} />
|
||||
<Route exact path="/login" component={Login} />
|
||||
{this.props.loggedIn ? (
|
||||
<Switch>
|
||||
<Route path="/*" component={Console} />
|
||||
<Route component={NotFoundPage} />
|
||||
</Switch>
|
||||
) : (
|
||||
<Switch>
|
||||
<Route exact path="/" component={Login} />
|
||||
<Redirect to="/" />
|
||||
</Switch>
|
||||
)}
|
||||
<ProtectedRoute component={Console} loggedIn={loggedIn} />
|
||||
</Switch>
|
||||
</Router>
|
||||
);
|
||||
|
||||
34
portal-ui/src/icons/AddIcon.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import { SvgIcon } from "@material-ui/core";
|
||||
class AddIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon viewBox="0 0 12 12">
|
||||
<path
|
||||
fill="#081c42"
|
||||
className="a"
|
||||
d="M-13160.269,1885.114h-3.235v-4.381h-4.382V1877.5h4.382v-4.381h3.235v4.381h4.383v3.238h-4.383v4.38Z"
|
||||
transform="translate(13167.886 -1873.114)"
|
||||
/>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddIcon;
|
||||
33
portal-ui/src/icons/RemoveIcon.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import { SvgIcon } from "@material-ui/core";
|
||||
class RemoveIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon viewBox="0 0 11.656 3.101">
|
||||
<path
|
||||
fill="#081c42"
|
||||
d="M-13157.172,1879.551h-11.656v-3.1h11.656v3.1Z"
|
||||
transform="translate(13168.828 -1876.449)"
|
||||
/>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RemoveIcon;
|
||||
@@ -103,6 +103,7 @@ const AddBucket = ({
|
||||
const [bName, setBName] = useState<string>(bucketName);
|
||||
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||
const [addError, setAddError] = useState<string>("");
|
||||
const [sendEnabled, setSendEnabled] = useState<boolean>(false);
|
||||
|
||||
const addRecord = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
@@ -141,10 +142,34 @@ const AddBucket = ({
|
||||
const [value] = useDebounce(bName, 1000);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("called");
|
||||
addBucketName(value);
|
||||
}, [value]);
|
||||
|
||||
const resetForm = () => {
|
||||
setBName("");
|
||||
addBucketVersioned(false);
|
||||
addBucketQuota(false);
|
||||
addBucketQuotaType("hard");
|
||||
addBucketQuotaSize("1");
|
||||
addBucketQuotaUnit("TiB");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let valid = false;
|
||||
|
||||
if (bName.trim() !== "") {
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (enableQuota && valid) {
|
||||
if (quotaSize.trim() === "" || parseInt(quotaSize) === 0) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
setSendEnabled(valid);
|
||||
}, [bName, versioned, quotaType, quotaSize, quotaUnit, enableQuota]);
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
title="Create Bucket"
|
||||
@@ -196,7 +221,8 @@ const AddBucket = ({
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
addBucketVersioned(event.target.checked);
|
||||
}}
|
||||
label={"Turn On Versioning"}
|
||||
label={"Versioning"}
|
||||
indicatorLabel={"On"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -209,6 +235,7 @@ const AddBucket = ({
|
||||
addBucketQuota(event.target.checked);
|
||||
}}
|
||||
label={"Enable Bucket Quota"}
|
||||
indicatorLabel={"On"}
|
||||
/>
|
||||
</Grid>
|
||||
{enableQuota && (
|
||||
@@ -264,11 +291,19 @@ const AddBucket = ({
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={resetForm}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading}
|
||||
disabled={addLoading || !sendEnabled}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
||||
@@ -16,20 +16,16 @@
|
||||
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { Button } from "@material-ui/core";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import { BucketObject, BucketObjectsList } from "./types";
|
||||
import api from "../../../../../../common/api";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import TableWrapper from "../../../../Common/TableWrapper/TableWrapper";
|
||||
import { MinTablePaginationActions } from "../../../../../../common/MinTablePaginationActions";
|
||||
import { CreateIcon } from "../../.././../../../icons";
|
||||
import { niceBytes } from "../../../../../../common/utils";
|
||||
import Moment from "react-moment";
|
||||
import DeleteObject from "./DeleteObject";
|
||||
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
@@ -37,6 +33,11 @@ import {
|
||||
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../../../../Common/PageHeader/PageHeader";
|
||||
import storage from "local-storage-fallback";
|
||||
import { isNullOrUndefined } from "util";
|
||||
import { Button, Input } from "@material-ui/core";
|
||||
import * as reactMoment from "react-moment";
|
||||
import { CreateIcon } from "../../../../../../icons";
|
||||
import Snackbar from "@material-ui/core/Snackbar";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -88,6 +89,8 @@ interface IListObjectsState {
|
||||
selectedObject: string;
|
||||
selectedBucket: string;
|
||||
filterObjects: string;
|
||||
openSnackbar: boolean;
|
||||
snackBarMessage: string;
|
||||
}
|
||||
|
||||
class ListObjects extends React.Component<
|
||||
@@ -104,6 +107,8 @@ class ListObjects extends React.Component<
|
||||
selectedObject: "",
|
||||
selectedBucket: "",
|
||||
filterObjects: "",
|
||||
openSnackbar: false,
|
||||
snackBarMessage: "",
|
||||
};
|
||||
|
||||
fetchRecords = () => {
|
||||
@@ -141,6 +146,73 @@ class ListObjects extends React.Component<
|
||||
});
|
||||
}
|
||||
|
||||
showSnackBarMessage(text: string) {
|
||||
this.setState({ openSnackbar: true, snackBarMessage: text });
|
||||
}
|
||||
|
||||
closeSnackBar() {
|
||||
this.setState({ openSnackbar: false, snackBarMessage: `` });
|
||||
}
|
||||
|
||||
upload(e: any, bucketName: string, path: string) {
|
||||
let listObjects = this;
|
||||
if (isNullOrUndefined(e) || isNullOrUndefined(e.target)) {
|
||||
return;
|
||||
}
|
||||
const token: string = storage.getItem("token")!;
|
||||
e.preventDefault();
|
||||
let file = e.target.files[0];
|
||||
const fileName = file.name;
|
||||
|
||||
const objectName = `${path}${fileName}`;
|
||||
let uploadUrl = `/api/v1/buckets/${bucketName}/objects/upload?prefix=${objectName}`;
|
||||
let xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open("POST", uploadUrl, true);
|
||||
xhr.setRequestHeader("Authorization", `Bearer ${token}`);
|
||||
|
||||
xhr.withCredentials = false;
|
||||
xhr.onload = function (event) {
|
||||
// TODO: handle status
|
||||
if (xhr.status == 401 || xhr.status == 403) {
|
||||
listObjects.showSnackBarMessage(
|
||||
"An error occurred while uploading the file."
|
||||
);
|
||||
}
|
||||
if (xhr.status == 500) {
|
||||
listObjects.showSnackBarMessage(
|
||||
"An error occurred while uploading the file."
|
||||
);
|
||||
}
|
||||
if (xhr.status == 200) {
|
||||
listObjects.showSnackBarMessage("Object uploaded successfully.");
|
||||
listObjects.fetchRecords();
|
||||
}
|
||||
};
|
||||
|
||||
xhr.upload.addEventListener("error", (event) => {
|
||||
// TODO: handle error
|
||||
this.showSnackBarMessage("An error occurred while uploading the file.");
|
||||
});
|
||||
|
||||
xhr.upload.addEventListener("progress", (event) => {
|
||||
// TODO: handle progress with event.loaded, event.total
|
||||
});
|
||||
|
||||
xhr.onerror = () => {
|
||||
listObjects.showSnackBarMessage(
|
||||
"An error occurred while uploading the file."
|
||||
);
|
||||
};
|
||||
|
||||
var formData = new FormData();
|
||||
var blobFile = new Blob([file]);
|
||||
|
||||
formData.append("upfile", blobFile);
|
||||
xhr.send(formData);
|
||||
e.target.value = null;
|
||||
}
|
||||
|
||||
download(bucketName: string, objectName: string) {
|
||||
var anchor = document.createElement("a");
|
||||
document.body.appendChild(anchor);
|
||||
@@ -155,7 +227,7 @@ class ListObjects extends React.Component<
|
||||
xhr.setRequestHeader("Authorization", `Bearer ${token}`);
|
||||
xhr.responseType = "blob";
|
||||
|
||||
xhr.onload = function(e) {
|
||||
xhr.onload = function (e) {
|
||||
if (this.status == 200) {
|
||||
var blob = new Blob([this.response], {
|
||||
type: "octet/stream",
|
||||
@@ -184,9 +256,11 @@ class ListObjects extends React.Component<
|
||||
selectedBucket,
|
||||
deleteOpen,
|
||||
filterObjects,
|
||||
snackBarMessage,
|
||||
openSnackbar,
|
||||
} = this.state;
|
||||
const displayParsedDate = (date: string) => {
|
||||
return <Moment>{date}</Moment>;
|
||||
return <reactMoment.default>{date}</reactMoment.default>;
|
||||
};
|
||||
|
||||
const confirmDeleteObject = (object: string) => {
|
||||
@@ -197,6 +271,25 @@ class ListObjects extends React.Component<
|
||||
this.download(selectedBucket, object);
|
||||
};
|
||||
|
||||
const uploadObject = (e: any): void => {
|
||||
// TODO: handle deeper paths/folders
|
||||
let file = e.target.files[0];
|
||||
this.showSnackBarMessage(`Uploading: ${file.name}`);
|
||||
this.upload(e, selectedBucket, "");
|
||||
};
|
||||
|
||||
const snackBarAction = (
|
||||
<Button
|
||||
color="secondary"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
this.closeSnackBar();
|
||||
}}
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
);
|
||||
|
||||
const tableActions = [
|
||||
{ type: "download", onClick: downloadObject, sendOnlyId: true },
|
||||
{ type: "delete", onClick: confirmDeleteObject, sendOnlyId: true },
|
||||
@@ -226,6 +319,11 @@ class ListObjects extends React.Component<
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Snackbar
|
||||
open={openSnackbar}
|
||||
message={snackBarMessage}
|
||||
action={snackBarAction}
|
||||
/>
|
||||
<PageHeader label="Objects" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
@@ -249,6 +347,23 @@ class ListObjects extends React.Component<
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
component="label"
|
||||
>
|
||||
Upload Object
|
||||
<Input
|
||||
type="file"
|
||||
onChange={(e) => uploadObject(e)}
|
||||
id="file-input"
|
||||
style={{ display: "none" }}
|
||||
/>
|
||||
</Button>
|
||||
</>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2019 MinIO, Inc.
|
||||
// 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
|
||||
@@ -13,21 +13,32 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { useState, useEffect, createRef, ChangeEvent } from "react";
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
createRef,
|
||||
useLayoutEffect,
|
||||
ChangeEvent,
|
||||
useRef,
|
||||
} from "react";
|
||||
import get from "lodash/get";
|
||||
import debounce from "lodash/debounce";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import get from "lodash/get";
|
||||
import InputBoxWrapper from "../InputBoxWrapper/InputBoxWrapper";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
import { InputLabel, Tooltip } from "@material-ui/core";
|
||||
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
import InputBoxWrapper from "../InputBoxWrapper/InputBoxWrapper";
|
||||
import AddIcon from "../../../../../icons/AddIcon";
|
||||
|
||||
interface ICSVMultiSelector {
|
||||
elements: string;
|
||||
name: string;
|
||||
label: string;
|
||||
tooltip?: string;
|
||||
commonPlaceholder?: string;
|
||||
classes: any;
|
||||
withBorder?: boolean;
|
||||
onChange: (elements: string) => void;
|
||||
}
|
||||
|
||||
@@ -35,16 +46,13 @@ const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic,
|
||||
...tooltipHelper,
|
||||
inputLabel: {
|
||||
...fieldBasic.inputLabel,
|
||||
width: 116,
|
||||
},
|
||||
inputContainer: {
|
||||
inputWithBorder: {
|
||||
border: "1px solid #EAEAEA",
|
||||
padding: 15,
|
||||
height: 150,
|
||||
overflowY: "auto",
|
||||
padding: 15,
|
||||
position: "relative",
|
||||
border: "1px solid #c4c4c4",
|
||||
marginTop: 15,
|
||||
},
|
||||
labelContainer: {
|
||||
display: "flex",
|
||||
@@ -56,7 +64,9 @@ const CSVMultiSelector = ({
|
||||
name,
|
||||
label,
|
||||
tooltip = "",
|
||||
commonPlaceholder = "",
|
||||
onChange,
|
||||
withBorder = false,
|
||||
classes,
|
||||
}: ICSVMultiSelector) => {
|
||||
const [currentElements, setCurrentElements] = useState<string[]>([""]);
|
||||
@@ -75,29 +85,37 @@ const CSVMultiSelector = ({
|
||||
|
||||
setCurrentElements(elementsSplit);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [elements, currentElements]);
|
||||
|
||||
// Use effect to send new values to onChange
|
||||
useEffect(() => {
|
||||
const elementsString = currentElements
|
||||
.filter((element) => element.trim() !== "")
|
||||
.join(",");
|
||||
onChange(elementsString);
|
||||
const refScroll = bottomList.current;
|
||||
if (refScroll) {
|
||||
refScroll.scrollIntoView(false);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentElements]);
|
||||
|
||||
// We avoid multiple re-renders / hang issue typing too fast
|
||||
const firstUpdate = useRef(true);
|
||||
useLayoutEffect(() => {
|
||||
if (firstUpdate.current) {
|
||||
firstUpdate.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
debouncedOnChange();
|
||||
}, [currentElements]);
|
||||
|
||||
// If the last input is not empty, we add a new one
|
||||
const addEmptyLine = (elementsUp: string[]) => {
|
||||
if (elementsUp[elementsUp.length - 1].trim() !== "") {
|
||||
elementsUp.push("");
|
||||
const refScroll = bottomList.current;
|
||||
|
||||
if (refScroll) {
|
||||
refScroll.scrollIntoView(false);
|
||||
}
|
||||
const cpList = [...elementsUp];
|
||||
cpList.push("");
|
||||
setCurrentElements(cpList);
|
||||
}
|
||||
|
||||
return elementsUp;
|
||||
};
|
||||
|
||||
// Onchange function for input box, we get the dataset-index & only update that value in the array
|
||||
@@ -108,10 +126,18 @@ const CSVMultiSelector = ({
|
||||
const index = get(e.target, "dataset.index", 0);
|
||||
updatedElement[index] = e.target.value;
|
||||
|
||||
updatedElement = addEmptyLine(updatedElement);
|
||||
setCurrentElements(updatedElement);
|
||||
};
|
||||
|
||||
// Debounce for On Change
|
||||
const debouncedOnChange = debounce(() => {
|
||||
const elementsString = currentElements
|
||||
.filter((element) => element.trim() !== "")
|
||||
.join(",");
|
||||
|
||||
onChange(elementsString);
|
||||
}, 500);
|
||||
|
||||
const inputs = currentElements.map((element, index) => {
|
||||
return (
|
||||
<InputBoxWrapper
|
||||
@@ -122,6 +148,11 @@ const CSVMultiSelector = ({
|
||||
onChange={onChangeElement}
|
||||
index={index}
|
||||
key={`csv-${name}-${index.toString()}`}
|
||||
placeholder={commonPlaceholder}
|
||||
overlayIcon={index === currentElements.length - 1 ? <AddIcon /> : null}
|
||||
overlayAction={() => {
|
||||
addEmptyLine(currentElements);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -139,7 +170,11 @@ const CSVMultiSelector = ({
|
||||
</div>
|
||||
)}
|
||||
</InputLabel>
|
||||
<Grid item xs={12} className={classes.inputContainer}>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${withBorder ? classes.inputWithBorder : ""}`}
|
||||
>
|
||||
{inputs}
|
||||
<div ref={bottomList} />
|
||||
</Grid>
|
||||
@@ -147,5 +182,4 @@ const CSVMultiSelector = ({
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(CSVMultiSelector);
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { Controlled as CodeMirror } from "react-codemirror2";
|
||||
import { InputLabel, Tooltip } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { fieldBasic } from "../common/styleLibrary";
|
||||
import "./ConsoleCodeMirror.css";
|
||||
|
||||
require("codemirror/mode/javascript/javascript");
|
||||
|
||||
interface ICodeWrapper {
|
||||
value: string;
|
||||
label?: string;
|
||||
tooltip?: string;
|
||||
classes: any;
|
||||
onChange?: (editor: any, data: any, value: string) => any;
|
||||
onBeforeChange: (editor: any, data: any, value: string) => any;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic,
|
||||
});
|
||||
|
||||
const CodeMirrorWrapper = ({
|
||||
value,
|
||||
label = "",
|
||||
tooltip = "",
|
||||
classes,
|
||||
onChange = () => {},
|
||||
onBeforeChange,
|
||||
readOnly = false,
|
||||
}: ICodeWrapper) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<InputLabel className={classes.inputLabel}>
|
||||
<span>{label}</span>
|
||||
{tooltip !== "" && (
|
||||
<div className={classes.tooltipContainer}>
|
||||
<Tooltip title={tooltip} placement="top-start">
|
||||
<HelpIcon className={classes.tooltip} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</InputLabel>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<CodeMirror
|
||||
value={value}
|
||||
options={{
|
||||
mode: "javascript",
|
||||
lineNumbers: true,
|
||||
readOnly,
|
||||
}}
|
||||
onBeforeChange={onBeforeChange}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(CodeMirrorWrapper);
|
||||
@@ -0,0 +1,353 @@
|
||||
/* BASICS */
|
||||
|
||||
.CodeMirror {
|
||||
/* Set height, width, borders, and global font properties here */
|
||||
font-family: monospace;
|
||||
height: 300px;
|
||||
color: #fff;
|
||||
background: #081C42;
|
||||
direction: ltr;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* PADDING */
|
||||
|
||||
.CodeMirror-lines {
|
||||
padding: 4px 0; /* Vertical padding around content */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
padding: 0 4px; /* Horizontal padding of content */
|
||||
}
|
||||
|
||||
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
background-color: rgba(255,255,255,0.8); /* The little square between H and V scrollbars */
|
||||
}
|
||||
|
||||
/* GUTTER */
|
||||
|
||||
.CodeMirror-gutters {
|
||||
border-right: 1px solid #ddd;
|
||||
background-color: #ffffff80;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.CodeMirror-linenumbers {}
|
||||
.CodeMirror-linenumber {
|
||||
padding: 0 3px 0 5px;
|
||||
min-width: 20px;
|
||||
white-space: nowrap;
|
||||
color: #000;
|
||||
font-size: 10px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.CodeMirror-guttermarker { color: black; }
|
||||
.CodeMirror-guttermarker-subtle { color: #999; }
|
||||
|
||||
/* CURSOR */
|
||||
|
||||
.CodeMirror-cursor {
|
||||
border-left: 1px solid white;
|
||||
border-right: none;
|
||||
width: 0;
|
||||
}
|
||||
/* Shown when moving in bi-directional text */
|
||||
.CodeMirror div.CodeMirror-secondarycursor {
|
||||
border-left: 1px solid silver;
|
||||
}
|
||||
.cm-fat-cursor .CodeMirror-cursor {
|
||||
width: auto;
|
||||
border: 0 !important;
|
||||
background: #7e7;
|
||||
}
|
||||
.cm-fat-cursor div.CodeMirror-cursors {
|
||||
z-index: 1;
|
||||
}
|
||||
.cm-fat-cursor-mark {
|
||||
background-color: rgba(20, 255, 20, 0.5);
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
}
|
||||
.cm-animate-fat-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
background-color: #7e7;
|
||||
}
|
||||
@-moz-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
@-webkit-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
@keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
|
||||
/* Can style cursor different in overwrite (non-insert) mode */
|
||||
.CodeMirror-overwrite .CodeMirror-cursor {}
|
||||
|
||||
.cm-tab { display: inline-block; text-decoration: inherit; }
|
||||
|
||||
.CodeMirror-rulers {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: -50px; bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.CodeMirror-ruler {
|
||||
border-left: 1px solid #ccc;
|
||||
top: 0; bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* DEFAULT THEME */
|
||||
|
||||
.cm-s-default .cm-header {color: #fff;}
|
||||
.cm-s-default .cm-quote {color: #fff;}
|
||||
.cm-negative {color: #fff;}
|
||||
.cm-positive {color: #fff;}
|
||||
.cm-header, .cm-strong {font-weight: bold;}
|
||||
.cm-em {font-style: italic;}
|
||||
.cm-link {text-decoration: underline;}
|
||||
.cm-strikethrough {text-decoration: line-through;}
|
||||
|
||||
.cm-s-default .cm-keyword {color: #fff;}
|
||||
.cm-s-default .cm-atom {color: #fff;}
|
||||
.cm-s-default .cm-number {color: #fff;}
|
||||
.cm-s-default .cm-def {color: #fff;}
|
||||
.cm-s-default .cm-variable,
|
||||
.cm-s-default .cm-punctuation,
|
||||
.cm-s-default .cm-property,
|
||||
.cm-s-default .cm-operator {}
|
||||
.cm-s-default .cm-variable-2 {color: #fff;}
|
||||
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #fff;}
|
||||
.cm-s-default .cm-comment {color: #fff;}
|
||||
.cm-s-default .cm-string {color: #fff;}
|
||||
.cm-s-default .cm-string-2 {color: #fff;}
|
||||
.cm-s-default .cm-meta {color: #fff;}
|
||||
.cm-s-default .cm-qualifier {color: #fff;}
|
||||
.cm-s-default .cm-builtin {color: #fff;}
|
||||
.cm-s-default .cm-bracket {color: #fff;}
|
||||
.cm-s-default .cm-tag {color: #fff;}
|
||||
.cm-s-default .cm-attribute {color: #fff;}
|
||||
.cm-s-default .cm-hr {color: #fff;}
|
||||
.cm-s-default .cm-link {color: #fff;}
|
||||
|
||||
.cm-s-default .cm-error {color: #fff;}
|
||||
.cm-invalidchar {color: #fff;}
|
||||
|
||||
.CodeMirror-composing { border-bottom: 2px solid; }
|
||||
|
||||
/* Default styles for common addons */
|
||||
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {color: #fff;}
|
||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #fff;}
|
||||
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
|
||||
.CodeMirror-activeline-background {background: #e8f2ff;}
|
||||
|
||||
/* STOP */
|
||||
|
||||
/* The rest of this file contains styles related to the mechanics of
|
||||
the editor. You probably shouldn't touch them. */
|
||||
|
||||
.CodeMirror {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow: scroll !important; /* Things will break if this is overridden */
|
||||
/* 50px is the magic margin used to hide the element's real scrollbars */
|
||||
/* See overflow: hidden in .CodeMirror */
|
||||
margin-bottom: -50px; margin-right: -50px;
|
||||
padding-bottom: 50px;
|
||||
height: 100%;
|
||||
outline: none; /* Prevent dragging from highlighting the element */
|
||||
position: relative;
|
||||
}
|
||||
.CodeMirror-sizer {
|
||||
position: relative;
|
||||
border-right: 50px solid transparent;
|
||||
}
|
||||
|
||||
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||
before actual scrolling happens, thus preventing shaking and
|
||||
flickering artifacts. */
|
||||
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
display: none;
|
||||
}
|
||||
.CodeMirror-vscrollbar {
|
||||
right: 0; top: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.CodeMirror-hscrollbar {
|
||||
bottom: 0; left: 0;
|
||||
overflow-y: hidden;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.CodeMirror-scrollbar-filler {
|
||||
right: 0; bottom: 0;
|
||||
}
|
||||
.CodeMirror-gutter-filler {
|
||||
left: 0; bottom: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
position: absolute; left: 0; top: 0;
|
||||
min-height: 100%;
|
||||
z-index: 3;
|
||||
}
|
||||
.CodeMirror-gutter {
|
||||
white-space: normal;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-bottom: -50px;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.CodeMirror-gutter-background {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
z-index: 4;
|
||||
}
|
||||
.CodeMirror-gutter-elt {
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
z-index: 4;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
|
||||
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
|
||||
|
||||
.CodeMirror-lines {
|
||||
cursor: text;
|
||||
min-height: 1px; /* prevents collapsing before first draw */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
/* Reset some styles that the rest of the page might have set */
|
||||
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
||||
border-width: 0;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
margin: 0;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-font-variant-ligatures: contextual;
|
||||
font-variant-ligatures: contextual;
|
||||
}
|
||||
.CodeMirror-wrap pre.CodeMirror-line,
|
||||
.CodeMirror-wrap pre.CodeMirror-line-like {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.CodeMirror-linebackground {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: 0; bottom: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-linewidget {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0.1px; /* Force widget margins to stay inside of the container */
|
||||
}
|
||||
|
||||
.CodeMirror-widget {}
|
||||
|
||||
.CodeMirror-rtl pre { direction: rtl; }
|
||||
|
||||
.CodeMirror-code {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Force content-box sizing for the elements where we expect it */
|
||||
.CodeMirror-scroll,
|
||||
.CodeMirror-sizer,
|
||||
.CodeMirror-gutter,
|
||||
.CodeMirror-gutters,
|
||||
.CodeMirror-linenumber {
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.CodeMirror-measure {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror-cursor {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.CodeMirror-measure pre { position: static; }
|
||||
|
||||
div.CodeMirror-cursors {
|
||||
visibility: hidden;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
div.CodeMirror-dragcursors {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.CodeMirror-focused div.CodeMirror-cursors {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.CodeMirror-selected { background: #d9d9d9; }
|
||||
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
||||
.CodeMirror-crosshair { cursor: crosshair; }
|
||||
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
|
||||
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
|
||||
|
||||
.cm-searching {
|
||||
background-color: #ffa;
|
||||
background-color: rgba(255, 255, 0, .4);
|
||||
}
|
||||
|
||||
/* Used to force a border model for a node */
|
||||
.cm-force-border { padding-right: .1px; }
|
||||
|
||||
@media print {
|
||||
/* Hide the cursor when printing */
|
||||
.CodeMirror div.CodeMirror-cursors {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* See issue #2901 */
|
||||
.cm-tab-wrap-hack:after { content: ''; }
|
||||
|
||||
/* Help users use markselection to safely style text background */
|
||||
span.CodeMirror-selectedtext { background: none; }
|
||||
@@ -0,0 +1,158 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
import React from "react";
|
||||
import {
|
||||
Grid,
|
||||
InputLabel,
|
||||
TextField,
|
||||
TextFieldProps,
|
||||
Tooltip,
|
||||
} from "@material-ui/core";
|
||||
import { OutlinedInputProps } from "@material-ui/core/OutlinedInput";
|
||||
import {
|
||||
createStyles,
|
||||
makeStyles,
|
||||
Theme,
|
||||
withStyles,
|
||||
} from "@material-ui/core/styles";
|
||||
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
|
||||
interface CommentBoxProps {
|
||||
label: string;
|
||||
classes: any;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
value: string | boolean;
|
||||
id: string;
|
||||
name: string;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
index?: number;
|
||||
error?: string;
|
||||
required?: boolean;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic,
|
||||
...tooltipHelper,
|
||||
inputLabel: {
|
||||
...fieldBasic.inputLabel,
|
||||
marginBottom: 16,
|
||||
fontSize: 14,
|
||||
},
|
||||
textBoxContainer: {
|
||||
flexGrow: 1,
|
||||
position: "relative",
|
||||
},
|
||||
errorState: {
|
||||
color: "#b53b4b",
|
||||
fontSize: 14,
|
||||
position: "absolute",
|
||||
top: 7,
|
||||
right: 7,
|
||||
},
|
||||
cssOutlinedInput: {
|
||||
borderColor: "#9C9C9C",
|
||||
padding: 16,
|
||||
},
|
||||
rootContainer: {
|
||||
"& .MuiOutlinedInput-inputMultiline": {
|
||||
...fieldBasic.inputLabel,
|
||||
fontSize: 13,
|
||||
minHeight: 150,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const CommentBoxWrapper = ({
|
||||
label,
|
||||
onChange,
|
||||
value,
|
||||
id,
|
||||
name,
|
||||
disabled = false,
|
||||
tooltip = "",
|
||||
index = 0,
|
||||
error = "",
|
||||
required = false,
|
||||
placeholder = "",
|
||||
classes,
|
||||
}: CommentBoxProps) => {
|
||||
let inputProps: any = { "data-index": index };
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.fieldContainer} ${
|
||||
error !== "" ? classes.errorInField : ""
|
||||
}`}
|
||||
>
|
||||
{label !== "" && (
|
||||
<InputLabel
|
||||
htmlFor={id}
|
||||
className={`${error !== "" ? classes.fieldLabelError : ""} ${
|
||||
classes.inputLabel
|
||||
}`}
|
||||
>
|
||||
<span>
|
||||
{label}
|
||||
{required ? "*" : ""}
|
||||
</span>
|
||||
{tooltip !== "" && (
|
||||
<div className={classes.tooltipContainer}>
|
||||
<Tooltip title={tooltip} placement="top-start">
|
||||
<HelpIcon className={classes.tooltip} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</InputLabel>
|
||||
)}
|
||||
|
||||
<div className={classes.textBoxContainer}>
|
||||
<TextField
|
||||
id={id}
|
||||
name={name}
|
||||
fullWidth
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
multiline
|
||||
inputProps={inputProps}
|
||||
error={error !== ""}
|
||||
helperText={error}
|
||||
placeholder={placeholder}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
notchedOutline: classes.cssOutlinedInput,
|
||||
root: classes.rootContainer,
|
||||
},
|
||||
}}
|
||||
variant="outlined"
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(CommentBoxWrapper);
|
||||
@@ -22,7 +22,7 @@ import { actionsTray, fieldBasic } from "../common/styleLibrary";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
|
||||
interface IFormSwitch {
|
||||
label: string;
|
||||
label?: string;
|
||||
classes: any;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
value: string | boolean;
|
||||
@@ -31,7 +31,9 @@ interface IFormSwitch {
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
index?: number;
|
||||
indicatorLabel?: string;
|
||||
checked: boolean;
|
||||
switchOnly?: boolean;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -82,7 +84,7 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
actionsTitle: {
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
color: "#081C42",
|
||||
fontSize: 16,
|
||||
alignSelf: "center",
|
||||
},
|
||||
@@ -95,7 +97,7 @@ const styles = (theme: Theme) =>
|
||||
"& .input": {
|
||||
"&::placeholder": {
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
color: "#081C42",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -107,6 +109,15 @@ const styles = (theme: Theme) =>
|
||||
paddingBottom: 14,
|
||||
marginBottom: 20,
|
||||
},
|
||||
indicatorLabel: {
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
color: "#081C42",
|
||||
margin: "0 8px 0 10px",
|
||||
},
|
||||
switchContainer: {
|
||||
display: "flex",
|
||||
},
|
||||
...actionsTray,
|
||||
...fieldBasic,
|
||||
});
|
||||
@@ -127,7 +138,7 @@ const StyledSwitch = withStyles({
|
||||
color: "#fff",
|
||||
},
|
||||
"&$checked + $track": {
|
||||
backgroundColor: "#000",
|
||||
backgroundColor: "#081C42",
|
||||
opacity: 1,
|
||||
height: 15,
|
||||
},
|
||||
@@ -135,14 +146,14 @@ const StyledSwitch = withStyles({
|
||||
checked: {},
|
||||
track: {
|
||||
height: 15,
|
||||
backgroundColor: "#000",
|
||||
backgroundColor: "#081C42",
|
||||
opacity: 1,
|
||||
padding: 0,
|
||||
marginTop: 1.5,
|
||||
},
|
||||
thumb: {
|
||||
backgroundColor: "#fff",
|
||||
border: "#000 1px solid",
|
||||
border: "#081C42 1px solid",
|
||||
boxShadow: "none",
|
||||
width: 18,
|
||||
height: 18,
|
||||
@@ -152,16 +163,44 @@ const StyledSwitch = withStyles({
|
||||
})(Switch);
|
||||
|
||||
const FormSwitchWrapper = ({
|
||||
label,
|
||||
label = "",
|
||||
onChange,
|
||||
value,
|
||||
id,
|
||||
name,
|
||||
checked = false,
|
||||
disabled = false,
|
||||
switchOnly = false,
|
||||
tooltip = "",
|
||||
indicatorLabel = "",
|
||||
classes,
|
||||
}: IFormSwitch) => {
|
||||
const switchComponent = (
|
||||
<React.Fragment>
|
||||
<div className={classes.switchContainer}>
|
||||
<StyledSwitch
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
color="primary"
|
||||
name={name}
|
||||
inputProps={{ "aria-label": "primary checkbox" }}
|
||||
disabled={disabled}
|
||||
disableRipple
|
||||
disableFocusRipple
|
||||
disableTouchRipple
|
||||
value={value}
|
||||
/>
|
||||
{indicatorLabel !== "" && (
|
||||
<span className={classes.indicatorLabel}>{indicatorLabel}</span>
|
||||
)}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
if (switchOnly) {
|
||||
return switchComponent;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={`${classes.wrapperContainer}`}>
|
||||
@@ -177,21 +216,7 @@ const FormSwitchWrapper = ({
|
||||
)}
|
||||
</InputLabel>
|
||||
)}
|
||||
|
||||
<div className={classes.textBoxContainer}>
|
||||
<StyledSwitch
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
color="primary"
|
||||
name={name}
|
||||
inputProps={{ "aria-label": "primary checkbox" }}
|
||||
disabled={disabled}
|
||||
disableRipple
|
||||
disableFocusRipple
|
||||
disableTouchRipple
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
{switchComponent}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Grid,
|
||||
IconButton,
|
||||
InputLabel,
|
||||
TextField,
|
||||
TextFieldProps,
|
||||
@@ -49,6 +50,8 @@ interface InputBoxProps {
|
||||
placeholder?: string;
|
||||
min?: string;
|
||||
max?: string;
|
||||
overlayIcon?: any;
|
||||
overlayAction?: () => void;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -57,7 +60,10 @@ const styles = (theme: Theme) =>
|
||||
...tooltipHelper,
|
||||
textBoxContainer: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
textBoxWithIcon: {
|
||||
position: "relative",
|
||||
paddingRight: 25,
|
||||
},
|
||||
errorState: {
|
||||
color: "#b53b4b",
|
||||
@@ -66,6 +72,15 @@ const styles = (theme: Theme) =>
|
||||
top: 7,
|
||||
right: 7,
|
||||
},
|
||||
overlayAction: {
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 15,
|
||||
"& svg": {
|
||||
maxWidth: 15,
|
||||
maxHeight: 15,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const inputStyles = makeStyles((theme: Theme) =>
|
||||
@@ -76,11 +91,21 @@ const inputStyles = makeStyles((theme: Theme) =>
|
||||
borderColor: "#9c9c9c",
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
"&.MuiInput-underline::before": {
|
||||
borderColor: "#eaeaea",
|
||||
borderBottomStyle: "solid",
|
||||
},
|
||||
},
|
||||
input: {
|
||||
padding: "15px 5px 10px",
|
||||
padding: "15px 30px 10px 5px",
|
||||
color: "#393939",
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
"&:placeholder": {
|
||||
color: "#393939",
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
error: {
|
||||
color: "#b53b4b",
|
||||
@@ -117,6 +142,8 @@ const InputBoxWrapper = ({
|
||||
placeholder = "",
|
||||
min,
|
||||
max,
|
||||
overlayIcon = null,
|
||||
overlayAction,
|
||||
classes,
|
||||
}: InputBoxProps) => {
|
||||
let inputProps: any = { "data-index": index };
|
||||
@@ -174,12 +201,28 @@ const InputBoxWrapper = ({
|
||||
error={error !== ""}
|
||||
helperText={error}
|
||||
placeholder={placeholder}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
className={classes.inputRebase}
|
||||
/>
|
||||
</div>
|
||||
{overlayIcon && (
|
||||
<div className={classes.overlayAction}>
|
||||
<IconButton
|
||||
onClick={
|
||||
overlayAction
|
||||
? () => {
|
||||
overlayAction();
|
||||
}
|
||||
: () => null
|
||||
}
|
||||
size={"small"}
|
||||
disableFocusRipple={false}
|
||||
disableRipple={false}
|
||||
disableTouchRipple={false}
|
||||
>
|
||||
{overlayIcon}
|
||||
</IconButton>
|
||||
</div>
|
||||
)}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
withStyles,
|
||||
makeStyles,
|
||||
} from "@material-ui/core/styles";
|
||||
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
|
||||
import { fieldBasic, radioIcons, tooltipHelper } from "../common/styleLibrary";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
|
||||
export interface SelectorTypes {
|
||||
@@ -49,8 +49,20 @@ const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic,
|
||||
...tooltipHelper,
|
||||
radioBoxContainer: {
|
||||
flexGrow: 1,
|
||||
radioBoxContainer: {},
|
||||
fieldContainer: {
|
||||
...fieldBasic.fieldContainer,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
borderBottom: "#9c9c9c 1px solid",
|
||||
paddingBottom: 10,
|
||||
marginTop: 11,
|
||||
},
|
||||
checkedOption: {
|
||||
"& .MuiFormControlLabel-label": {
|
||||
color: "#000",
|
||||
fontWeight: 700,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -60,31 +72,7 @@ const radioStyles = makeStyles({
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
borderRadius: "100%",
|
||||
width: 14,
|
||||
height: 14,
|
||||
border: "1px solid #000",
|
||||
},
|
||||
checkedIcon: {
|
||||
borderRadius: "100%",
|
||||
width: 14,
|
||||
height: 14,
|
||||
border: "1px solid #000",
|
||||
padding: 4,
|
||||
position: "relative",
|
||||
"&::after": {
|
||||
content: '" "',
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: "100%",
|
||||
display: "block",
|
||||
position: "absolute",
|
||||
backgroundColor: "#000",
|
||||
top: 2,
|
||||
left: 2,
|
||||
},
|
||||
},
|
||||
...radioIcons,
|
||||
});
|
||||
|
||||
const RadioButton = (props: RadioProps) => {
|
||||
@@ -95,8 +83,8 @@ const RadioButton = (props: RadioProps) => {
|
||||
className={classes.root}
|
||||
disableRipple
|
||||
color="default"
|
||||
checkedIcon={<span className={classes.checkedIcon} />}
|
||||
icon={<span className={classes.icon} />}
|
||||
checkedIcon={<span className={classes.radioSelectedIcon} />}
|
||||
icon={<span className={classes.radioUnselectedIcon} />}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -143,6 +131,11 @@ export const RadioGroupSelector = ({
|
||||
value={selectorOption.value}
|
||||
control={<RadioButton />}
|
||||
label={selectorOption.label}
|
||||
className={
|
||||
selectorOption.value === currentSelection
|
||||
? classes.checkedOption
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -20,7 +20,6 @@ export const fieldBasic = {
|
||||
inputLabel: {
|
||||
fontWeight: 600,
|
||||
marginRight: 10,
|
||||
width: 160,
|
||||
fontSize: 15,
|
||||
color: "#000",
|
||||
textAlign: "left" as const,
|
||||
@@ -36,6 +35,7 @@ export const fieldBasic = {
|
||||
},
|
||||
fieldContainer: {
|
||||
marginBottom: 20,
|
||||
position: "relative" as const,
|
||||
},
|
||||
tooltipContainer: {
|
||||
marginLeft: 5,
|
||||
@@ -53,6 +53,30 @@ export const modalBasic = {
|
||||
formSlider: {
|
||||
marginLeft: 0,
|
||||
},
|
||||
clearButton: {
|
||||
border: "0",
|
||||
backgroundColor: "transparent",
|
||||
color: "#393939",
|
||||
fontWeight: 600,
|
||||
fontSize: 14,
|
||||
marginRight: 10,
|
||||
outline: "0",
|
||||
padding: "16px 25px 16px 25px",
|
||||
cursor: "pointer",
|
||||
},
|
||||
floatingEnabled: {
|
||||
position: "absolute" as const,
|
||||
right: 58,
|
||||
zIndex: 1000,
|
||||
marginTop: -38,
|
||||
},
|
||||
configureString: {
|
||||
border: "#EAEAEA 1px solid",
|
||||
borderRadius: 4,
|
||||
padding: "24px 50px",
|
||||
overflowY: "auto" as const,
|
||||
height: 170,
|
||||
},
|
||||
};
|
||||
|
||||
export const tooltipHelper = {
|
||||
@@ -76,6 +100,21 @@ export const checkboxIcons = {
|
||||
},
|
||||
};
|
||||
|
||||
const radioBasic = {
|
||||
width: 12,
|
||||
height: 12,
|
||||
borderRadius: "100%",
|
||||
};
|
||||
|
||||
export const radioIcons = {
|
||||
radioUnselectedIcon: { ...radioBasic, border: "1px solid #000" },
|
||||
radioSelectedIcon: {
|
||||
...radioBasic,
|
||||
border: "1px solid #000",
|
||||
backgroundColor: "#000",
|
||||
},
|
||||
};
|
||||
|
||||
export const containerForHeader = (bottomSpacing: any) => ({
|
||||
container: {
|
||||
padding: "110px 33px 30px",
|
||||
@@ -106,7 +145,7 @@ export const actionsTray = {
|
||||
export const searchField = {
|
||||
searchField: {
|
||||
flexGrow: 1,
|
||||
marginRight: 30,
|
||||
height: 40,
|
||||
background: "#FFFFFF",
|
||||
borderRadius: 5,
|
||||
border: "#EAEDEE 1px solid",
|
||||
@@ -137,5 +176,6 @@ export const predefinedList = {
|
||||
color: "#393939",
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
minHeight: 41,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -29,11 +29,12 @@ interface IModalProps {
|
||||
modalOpen: boolean;
|
||||
title: string;
|
||||
children: any;
|
||||
wideLimit?: boolean;
|
||||
}
|
||||
|
||||
const baseCloseLine = {
|
||||
content: '" "',
|
||||
borderLeft: "2px solid #707070",
|
||||
borderLeft: "2px solid #9C9C9C",
|
||||
height: 33,
|
||||
width: 1,
|
||||
position: "absolute",
|
||||
@@ -61,10 +62,10 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
modalCloseIcon: {
|
||||
fontSize: 35,
|
||||
color: "#707070",
|
||||
color: "#9C9C9C",
|
||||
fontWeight: 300,
|
||||
"&:hover": {
|
||||
color: "#000",
|
||||
color: "#9C9C9C",
|
||||
},
|
||||
},
|
||||
closeIcon: {
|
||||
@@ -77,7 +78,7 @@ const styles = (theme: Theme) =>
|
||||
transform: "rotate(-45deg)",
|
||||
},
|
||||
"&:hover::before, &:hover::after": {
|
||||
borderColor: "#000",
|
||||
borderColor: "#9C9C9C",
|
||||
},
|
||||
width: 24,
|
||||
height: 24,
|
||||
@@ -95,6 +96,10 @@ const styles = (theme: Theme) =>
|
||||
modalContent: {
|
||||
padding: "0 50px",
|
||||
},
|
||||
customDialogSize: {
|
||||
width: "100%",
|
||||
maxWidth: 765,
|
||||
},
|
||||
});
|
||||
|
||||
const ModalWrapper = ({
|
||||
@@ -103,15 +108,22 @@ const ModalWrapper = ({
|
||||
title,
|
||||
children,
|
||||
classes,
|
||||
wideLimit = true,
|
||||
}: IModalProps) => {
|
||||
const customSize = wideLimit
|
||||
? {
|
||||
classes: {
|
||||
paper: classes.customDialogSize,
|
||||
},
|
||||
}
|
||||
: { maxWidth: "md" as const, fullWidth: true };
|
||||
return (
|
||||
<Dialog
|
||||
open={modalOpen}
|
||||
onClose={onClose}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
maxWidth={"md"}
|
||||
fullWidth
|
||||
{...customSize}
|
||||
>
|
||||
<div className={classes.dialogContainer}>
|
||||
<div className={classes.closeContainer}>
|
||||
|
||||
@@ -33,7 +33,10 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { TablePaginationActionsProps } from "@material-ui/core/TablePagination/TablePaginationActions";
|
||||
import TableActionButton from "./TableActionButton";
|
||||
import history from "../../../../history";
|
||||
import { checkboxIcons } from "../FormComponents/common/styleLibrary";
|
||||
import {
|
||||
checkboxIcons,
|
||||
radioIcons,
|
||||
} from "../FormComponents/common/styleLibrary";
|
||||
|
||||
//Interfaces for table Items
|
||||
|
||||
@@ -49,6 +52,7 @@ interface IColumns {
|
||||
elementKey: string;
|
||||
sortable?: boolean;
|
||||
renderFunction?: (input: any) => any;
|
||||
renderFullObject?: boolean;
|
||||
globalClass?: any;
|
||||
}
|
||||
|
||||
@@ -80,6 +84,8 @@ interface TableWrapperProps {
|
||||
entityName: string;
|
||||
selectedItems?: string[];
|
||||
stickyHeader?: boolean;
|
||||
radioSelection?: boolean;
|
||||
customEmptyMessage?: string;
|
||||
paginatorConfig?: IPaginatorConfig;
|
||||
}
|
||||
|
||||
@@ -103,7 +109,8 @@ const styles = (theme: Theme) =>
|
||||
padding: "19px 38px",
|
||||
minHeight: "200px",
|
||||
boxShadow: "none",
|
||||
border: "#e7e7e7 1px solid",
|
||||
border: "#EAEDEE 1px solid",
|
||||
borderRadius: 3,
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
@@ -159,6 +166,7 @@ const styles = (theme: Theme) =>
|
||||
cursor: "pointer",
|
||||
},
|
||||
...checkboxIcons,
|
||||
...radioIcons,
|
||||
});
|
||||
|
||||
// Function that renders Title Columns
|
||||
@@ -186,9 +194,11 @@ const rowColumnsMap = (
|
||||
const itemElement = isString(itemData)
|
||||
? itemData
|
||||
: get(itemData, column.elementKey, null); // If the element is just a string, we render it as it is
|
||||
const renderConst = column.renderFullObject ? itemData : itemElement;
|
||||
|
||||
const renderElement = column.renderFunction
|
||||
? column.renderFunction(itemElement)
|
||||
: itemElement; // If render function is set, we send the value to the function.
|
||||
? column.renderFunction(renderConst)
|
||||
: renderConst; // If render function is set, we send the value to the function.
|
||||
return (
|
||||
<TableCell
|
||||
key={`tbRE-${column.elementKey}-${index}`}
|
||||
@@ -239,6 +249,8 @@ const TableWrapper = ({
|
||||
idField,
|
||||
classes,
|
||||
stickyHeader = false,
|
||||
radioSelection = false,
|
||||
customEmptyMessage = "",
|
||||
paginatorConfig,
|
||||
}: TableWrapperProps) => {
|
||||
const findView = itemActions
|
||||
@@ -333,8 +345,24 @@ const TableWrapper = ({
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
checkedIcon={<span className={classes.checkedIcon} />}
|
||||
icon={<span className={classes.unCheckedIcon} />}
|
||||
checkedIcon={
|
||||
<span
|
||||
className={
|
||||
radioSelection
|
||||
? classes.radioSelectedIcon
|
||||
: classes.checkedIcon
|
||||
}
|
||||
/>
|
||||
}
|
||||
icon={
|
||||
<span
|
||||
className={
|
||||
radioSelection
|
||||
? classes.radioUnselectedIcon
|
||||
: classes.unCheckedIcon
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
@@ -362,7 +390,13 @@ const TableWrapper = ({
|
||||
</Table>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
{!isLoading && <div>{`There are no ${entityName} yet.`}</div>}
|
||||
{!isLoading && (
|
||||
<div>
|
||||
{customEmptyMessage !== ""
|
||||
? customEmptyMessage
|
||||
: `There are no ${entityName} yet.`}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
@@ -22,6 +22,8 @@ import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import CSVMultiSelector from "../Common/FormComponents/CSVMultiSelector/CSVMultiSelector";
|
||||
import CommentBoxWrapper from "../Common/FormComponents/CommentBoxWrapper/CommentBoxWrapper";
|
||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
interface IConfGenericProps {
|
||||
onChange: (newValue: IElementValue[]) => void;
|
||||
@@ -94,20 +96,21 @@ const ConfTargetGeneric = ({
|
||||
const fieldDefinition = (field: KVField, item: number) => {
|
||||
switch (field.type) {
|
||||
case "on|off":
|
||||
const value = valueHolder[item] ? valueHolder[item].value : "false";
|
||||
|
||||
return (
|
||||
<RadioGroupSelector
|
||||
currentSelection={valueHolder[item] ? valueHolder[item].value : ""}
|
||||
<FormSwitchWrapper
|
||||
indicatorLabel="On"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.checked ? "true" : "false";
|
||||
setValueElement(field.name, value, item);
|
||||
}}
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
value={"switch_on"}
|
||||
tooltip={field.tooltip}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValueElement(field.name, e.target.value, item)
|
||||
}
|
||||
selectorOptions={[
|
||||
{ label: "On", value: "true" },
|
||||
{ label: "Off", value: "false" },
|
||||
]}
|
||||
checked={value === "true"}
|
||||
/>
|
||||
);
|
||||
case "csv":
|
||||
@@ -120,6 +123,22 @@ const ConfTargetGeneric = ({
|
||||
setValueElement(field.name, value, item)
|
||||
}
|
||||
tooltip={field.tooltip}
|
||||
commonPlaceholder={field.placeholder}
|
||||
withBorder={!!field.withBorder}
|
||||
/>
|
||||
);
|
||||
case "comment":
|
||||
return (
|
||||
<CommentBoxWrapper
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
label={field.label}
|
||||
tooltip={field.tooltip}
|
||||
value={valueHolder[item] ? valueHolder[item].value : ""}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValueElement(field.name, e.target.value, item)
|
||||
}
|
||||
placeholder={field.placeholder}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
@@ -134,6 +153,7 @@ const ConfTargetGeneric = ({
|
||||
setValueElement(field.name, e.target.value, item)
|
||||
}
|
||||
multiline={!!field.multiline}
|
||||
placeholder={field.placeholder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,12 @@ import Grid from "@material-ui/core/Grid";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import { IElementValue } from "../types";
|
||||
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
modalBasic,
|
||||
predefinedList,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import CommentBoxWrapper from "../../Common/FormComponents/CommentBoxWrapper/CommentBoxWrapper";
|
||||
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
interface IConfMySqlProps {
|
||||
onChange: (newValue: IElementValue[]) => void;
|
||||
@@ -31,6 +36,7 @@ interface IConfMySqlProps {
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...modalBasic,
|
||||
...predefinedList,
|
||||
});
|
||||
|
||||
const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
@@ -104,44 +110,41 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
setDsnString(cs);
|
||||
}, [user, dbName, password, port, host, setDsnString, configToDsnString]);
|
||||
|
||||
const switcherChangeEvt = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.checked) {
|
||||
// build dsn_string
|
||||
const cs = configToDsnString();
|
||||
setDsnString(cs);
|
||||
} else {
|
||||
// parse dsn_string
|
||||
const kv = parseDsnString(dsnString, [
|
||||
"host",
|
||||
"port",
|
||||
"dbname",
|
||||
"user",
|
||||
"password",
|
||||
]);
|
||||
setHostname(kv.get("host") ? kv.get("host") + "" : "");
|
||||
setPort(kv.get("port") ? kv.get("port") + "" : "");
|
||||
setDbName(kv.get("dbname") ? kv.get("dbname") + "" : "");
|
||||
setUser(kv.get("user") ? kv.get("user") + "" : "");
|
||||
setPassword(kv.get("password") ? kv.get("password") + "" : "");
|
||||
}
|
||||
|
||||
setUseDsnString(event.target.checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid container className={classes.formScrollable}>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={useDsnString}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.checked) {
|
||||
// build dsn_string
|
||||
const cs = configToDsnString();
|
||||
setDsnString(cs);
|
||||
} else {
|
||||
// parse dsn_string
|
||||
const kv = parseDsnString(dsnString, [
|
||||
"host",
|
||||
"port",
|
||||
"dbname",
|
||||
"user",
|
||||
"password",
|
||||
]);
|
||||
setHostname(kv.get("host") ? kv.get("host") + "" : "");
|
||||
setPort(kv.get("port") ? kv.get("port") + "" : "");
|
||||
setDbName(kv.get("dbname") ? kv.get("dbname") + "" : "");
|
||||
setUser(kv.get("user") ? kv.get("user") + "" : "");
|
||||
setPassword(
|
||||
kv.get("password") ? kv.get("password") + "" : ""
|
||||
);
|
||||
}
|
||||
|
||||
setUseDsnString(event.target.checked);
|
||||
}}
|
||||
name="checkedB"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Enter DSN String"
|
||||
className={classes.formSlider}
|
||||
<FormSwitchWrapper
|
||||
label={"Enter DNS String"}
|
||||
checked={useDsnString}
|
||||
id="checkedB"
|
||||
name="checkedB"
|
||||
onChange={switcherChangeEvt}
|
||||
value={"dnsString"}
|
||||
indicatorLabel={"On"}
|
||||
/>
|
||||
</Grid>
|
||||
{useDsnString ? (
|
||||
@@ -160,62 +163,78 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="host"
|
||||
name="host"
|
||||
label="Host"
|
||||
value={host}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setHostname(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="db-name"
|
||||
name="db-name"
|
||||
label="DB Name"
|
||||
value={dbName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setDbName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="port"
|
||||
name="port"
|
||||
label="Port"
|
||||
value={port}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPort(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.configureString}>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="host"
|
||||
name="host"
|
||||
label=""
|
||||
placeholder="Enter Host"
|
||||
value={host}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setHostname(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="db-name"
|
||||
name="db-name"
|
||||
label=""
|
||||
placeholder="Enter DB Name"
|
||||
value={dbName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setDbName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="port"
|
||||
name="port"
|
||||
label=""
|
||||
placeholder="Enter Port"
|
||||
value={port}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPort(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="user"
|
||||
name="user"
|
||||
label="User"
|
||||
value={user}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setUser(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="user"
|
||||
name="user"
|
||||
label=""
|
||||
placeholder="Enter User"
|
||||
value={user}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setUser(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="password"
|
||||
name="password"
|
||||
label=""
|
||||
placeholder="Enter Password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPassword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Connection String
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{dsnString}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="password"
|
||||
name="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPassword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<br />
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
@@ -224,6 +243,7 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
id="table"
|
||||
name="table"
|
||||
label="Table"
|
||||
placeholder="Enter Table Name"
|
||||
value={table}
|
||||
tooltip="DB table name to store/update events, table is auto-created"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -252,6 +272,7 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
id="queue-dir"
|
||||
name="queue_dir"
|
||||
label="Queue Dir"
|
||||
placeholder="Enter Queue Dir"
|
||||
value={queueDir}
|
||||
tooltip="staging dir for undelivered messages e.g. '/home/events'"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -264,6 +285,7 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
id="queue-limit"
|
||||
name="queue_limit"
|
||||
label="Queue Limit"
|
||||
placeholder="Enter Queue Limit"
|
||||
type="number"
|
||||
value={queueLimit}
|
||||
tooltip="maximum limit for undelivered messages, defaults to '10000'"
|
||||
@@ -273,11 +295,11 @@ const ConfMySql = ({ onChange, classes }: IConfMySqlProps) => {
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
<CommentBoxWrapper
|
||||
id="comment"
|
||||
name="comment"
|
||||
label="Comment"
|
||||
multiline={true}
|
||||
placeholder="Enter Comment"
|
||||
value={comment}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setComment(e.target.value);
|
||||
|
||||
@@ -22,7 +22,12 @@ import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBo
|
||||
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import { IElementValue } from "../types";
|
||||
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
modalBasic,
|
||||
predefinedList,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import CommentBoxWrapper from "../../Common/FormComponents/CommentBoxWrapper/CommentBoxWrapper";
|
||||
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
interface IConfPostgresProps {
|
||||
onChange: (newValue: IElementValue[]) => void;
|
||||
@@ -32,6 +37,7 @@ interface IConfPostgresProps {
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...modalBasic,
|
||||
...predefinedList,
|
||||
});
|
||||
|
||||
const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
@@ -45,7 +51,7 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
const [port, setPort] = useState<string>("");
|
||||
const [user, setUser] = useState<string>("");
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [sslMode, setSslMode] = useState<string>("require");
|
||||
const [sslMode, setSslMode] = useState<string>(" ");
|
||||
|
||||
const [table, setTable] = useState<string>("");
|
||||
const [format, setFormat] = useState<string>("namespace");
|
||||
@@ -126,8 +132,11 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
if (port !== "") {
|
||||
strValue = `${strValue} port=${port}`;
|
||||
}
|
||||
if (sslMode !== " ") {
|
||||
strValue = `${strValue} sslmode=${sslMode}`;
|
||||
}
|
||||
|
||||
strValue = `${strValue} sslmode=${sslMode}`;
|
||||
strValue = `${strValue} `;
|
||||
|
||||
return strValue.trim();
|
||||
}, [host, dbName, user, password, port, sslMode]);
|
||||
@@ -169,48 +178,44 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
configToString,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (useConnectionString) {
|
||||
// build connection_string
|
||||
const cs = configToString();
|
||||
setConnectionString(cs);
|
||||
|
||||
return;
|
||||
}
|
||||
// parse connection_string
|
||||
const kv = parseConnectionString(connectionString, [
|
||||
"host",
|
||||
"port",
|
||||
"dbname",
|
||||
"user",
|
||||
"password",
|
||||
"sslmode",
|
||||
]);
|
||||
setHostname(kv.get("host") ? kv.get("host") + "" : "");
|
||||
setPort(kv.get("port") ? kv.get("port") + "" : "");
|
||||
setDbName(kv.get("dbname") ? kv.get("dbname") + "" : "");
|
||||
setUser(kv.get("user") ? kv.get("user") + "" : "");
|
||||
setPassword(kv.get("password") ? kv.get("password") + "" : "");
|
||||
setSslMode(kv.get("sslmode") ? kv.get("sslmode") + "" : " ");
|
||||
}, [useConnectionString]);
|
||||
|
||||
return (
|
||||
<Grid container className={classes.formScrollable}>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={useConnectionString}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.checked) {
|
||||
// build connection_string
|
||||
const cs = configToString();
|
||||
setConnectionString(cs);
|
||||
} else {
|
||||
// parse connection_string
|
||||
const kv = parseConnectionString(connectionString, [
|
||||
"host",
|
||||
"port",
|
||||
"dbname",
|
||||
"user",
|
||||
"password",
|
||||
"sslmode",
|
||||
]);
|
||||
setHostname(kv.get("host") ? kv.get("host") + "" : "");
|
||||
setPort(kv.get("port") ? kv.get("port") + "" : "");
|
||||
setDbName(kv.get("dbname") ? kv.get("dbname") + "" : "");
|
||||
setUser(kv.get("user") ? kv.get("user") + "" : "");
|
||||
setPassword(
|
||||
kv.get("password") ? kv.get("password") + "" : ""
|
||||
);
|
||||
setSslMode(
|
||||
kv.get("sslmode") ? kv.get("sslmode") + "" : "require"
|
||||
);
|
||||
}
|
||||
|
||||
setUseConnectionString(event.target.checked);
|
||||
}}
|
||||
name="checkedB"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Enter Connection String"
|
||||
className={classes.formSlider}
|
||||
<FormSwitchWrapper
|
||||
label={"Manually Configure String"}
|
||||
checked={useConnectionString}
|
||||
id="manualString"
|
||||
name="manualString"
|
||||
onChange={(e) => {
|
||||
setUseConnectionString(e.target.checked);
|
||||
}}
|
||||
value={"manualString"}
|
||||
indicatorLabel={"On"}
|
||||
/>
|
||||
</Grid>
|
||||
{useConnectionString ? (
|
||||
@@ -229,81 +234,97 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="host"
|
||||
name="host"
|
||||
label="Host"
|
||||
value={host}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setHostname(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Grid item xs={12} className={classes.configureString}>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="host"
|
||||
name="host"
|
||||
label=""
|
||||
placeholder="Enter Host"
|
||||
value={host}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setHostname(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="db-name"
|
||||
name="db-name"
|
||||
label=""
|
||||
placeholder="Enter DB Name"
|
||||
value={dbName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setDbName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="port"
|
||||
name="port"
|
||||
label=""
|
||||
placeholder="Enter Port"
|
||||
value={port}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPort(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SelectWrapper
|
||||
value={sslMode}
|
||||
label=""
|
||||
id="sslmode"
|
||||
name="sslmode"
|
||||
onChange={(e): void => {
|
||||
if (e.target.value !== undefined) {
|
||||
setSslMode(e.target.value + "");
|
||||
}
|
||||
}}
|
||||
options={[
|
||||
{ label: "Enter SSL Mode", value: " " },
|
||||
{ label: "Require", value: "require" },
|
||||
{ label: "Disable", value: "disable" },
|
||||
{ label: "Verify CA", value: "verify-ca" },
|
||||
{ label: "Verify Full", value: "verify-full" },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="user"
|
||||
name="user"
|
||||
label=""
|
||||
placeholder="Enter User"
|
||||
value={user}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setUser(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="password"
|
||||
name="password"
|
||||
label=""
|
||||
type="password"
|
||||
placeholder="Enter Password"
|
||||
value={password}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPassword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Connection String
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{connectionString}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="db-name"
|
||||
name="db-name"
|
||||
label="DB Name"
|
||||
value={dbName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setDbName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="port"
|
||||
name="port"
|
||||
label="Port"
|
||||
value={port}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPort(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<SelectWrapper
|
||||
value={sslMode}
|
||||
label="SSL Mode"
|
||||
id="sslmode"
|
||||
name="sslmode"
|
||||
onChange={(e): void => {
|
||||
if (e.target.value !== undefined) {
|
||||
setSslMode(e.target.value + "");
|
||||
}
|
||||
}}
|
||||
options={[
|
||||
{ label: "Require", value: "require" },
|
||||
{ label: "Disable", value: "disable" },
|
||||
{ label: "Verify CA", value: "verify-ca" },
|
||||
{ label: "Verify Full", value: "verify-full" },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="user"
|
||||
name="user"
|
||||
label="User"
|
||||
value={user}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setUser(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="password"
|
||||
name="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPassword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<br />
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
@@ -312,6 +333,7 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
id="table"
|
||||
name="table"
|
||||
label="Table"
|
||||
placeholder={"Enter Table Name"}
|
||||
value={table}
|
||||
tooltip="DB table name to store/update events, table is auto-created"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -340,6 +362,7 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
id="queue-dir"
|
||||
name="queue_dir"
|
||||
label="Queue Dir"
|
||||
placeholder="Enter Queue Directory"
|
||||
value={queueDir}
|
||||
tooltip="staging dir for undelivered messages e.g. '/home/events'"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -352,6 +375,7 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
id="queue-limit"
|
||||
name="queue_limit"
|
||||
label="Queue Limit"
|
||||
placeholder="Enter Queue Limit"
|
||||
type="number"
|
||||
value={queueLimit}
|
||||
tooltip="maximum limit for undelivered messages, defaults to '10000'"
|
||||
@@ -361,11 +385,11 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
<CommentBoxWrapper
|
||||
id="comment"
|
||||
name="comment"
|
||||
label="Comment"
|
||||
multiline={true}
|
||||
placeholder="Enter Comment"
|
||||
value={comment}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setComment(e.target.value);
|
||||
|
||||
@@ -27,7 +27,9 @@ export type KVFieldType =
|
||||
| "duration"
|
||||
| "uri"
|
||||
| "sentence"
|
||||
| "csv";
|
||||
| "csv"
|
||||
| "comment"
|
||||
| "switch";
|
||||
|
||||
export interface KVField {
|
||||
name: string;
|
||||
@@ -37,6 +39,8 @@ export interface KVField {
|
||||
type: KVFieldType;
|
||||
options?: SelectorTypes[];
|
||||
multiline?: boolean;
|
||||
placeholder?: string;
|
||||
withBorder?: boolean;
|
||||
}
|
||||
|
||||
export interface IConfigurationElement {
|
||||
|
||||
@@ -71,19 +71,6 @@ import ObjectBrowser from "./ObjectBrowser/ObjectBrowser";
|
||||
import ListObjects from "./Buckets/ListBuckets/Objects/ListObjects/ListObjects";
|
||||
import License from "./License/License";
|
||||
|
||||
function Copyright() {
|
||||
return (
|
||||
<Typography variant="body2" color="textSecondary" align="center">
|
||||
{"Copyright © "}
|
||||
<Link color="inherit" href="https://material-ui.com/">
|
||||
MinIO
|
||||
</Link>{" "}
|
||||
{new Date().getFullYear()}
|
||||
{"."}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
const drawerWidth = 245;
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -385,9 +372,7 @@ const Console = ({
|
||||
/>
|
||||
))}
|
||||
{allowedRoutes.length > 0 ? (
|
||||
<Route exact path="/">
|
||||
<Redirect to={allowedRoutes[0].path} />
|
||||
</Route>
|
||||
<Redirect to={allowedRoutes[0].path} />
|
||||
) : null}
|
||||
</Switch>
|
||||
</Router>
|
||||
|
||||
@@ -19,12 +19,16 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
modalBasic,
|
||||
predefinedList,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import api from "../../../common/api";
|
||||
import UsersSelectors from "./UsersSelectors";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
interface IGroupProps {
|
||||
open: boolean;
|
||||
@@ -54,6 +58,7 @@ const styles = (theme: Theme) =>
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...predefinedList,
|
||||
});
|
||||
|
||||
const AddGroup = ({
|
||||
@@ -64,11 +69,12 @@ const AddGroup = ({
|
||||
}: IGroupProps) => {
|
||||
//Local States
|
||||
const [groupName, setGroupName] = useState<string>("");
|
||||
const [groupEnabled, setGroupEnabled] = useState<string>("");
|
||||
const [groupEnabled, setGroupEnabled] = useState<boolean>(false);
|
||||
const [saving, isSaving] = useState<boolean>(false);
|
||||
const [addError, setError] = useState<string>("");
|
||||
const [selectedUsers, setSelectedUsers] = useState<string[]>([]);
|
||||
const [loadingGroup, isLoadingGroup] = useState<boolean>(false);
|
||||
const [validGroup, setValidGroup] = useState<boolean>(false);
|
||||
|
||||
//Effects
|
||||
useEffect(() => {
|
||||
@@ -80,6 +86,10 @@ const AddGroup = ({
|
||||
}
|
||||
}, [selectedGroup]);
|
||||
|
||||
useEffect(() => {
|
||||
setValidGroup(groupName.trim() !== "");
|
||||
}, [groupName, selectedUsers]);
|
||||
|
||||
useEffect(() => {
|
||||
if (saving) {
|
||||
const saveRecord = () => {
|
||||
@@ -88,7 +98,7 @@ const AddGroup = ({
|
||||
.invoke("PUT", `/api/v1/groups/${groupName}`, {
|
||||
group: groupName,
|
||||
members: selectedUsers,
|
||||
status: groupEnabled,
|
||||
status: groupEnabled ? "enabled" : "disabled",
|
||||
})
|
||||
.then((res) => {
|
||||
isSaving(false);
|
||||
@@ -133,7 +143,7 @@ const AddGroup = ({
|
||||
api
|
||||
.invoke("GET", `/api/v1/groups/${selectedGroup}`)
|
||||
.then((res: MainGroupProps) => {
|
||||
setGroupEnabled(res.status);
|
||||
setGroupEnabled(res.status === "enabled");
|
||||
setGroupName(res.name);
|
||||
setSelectedUsers(res.members);
|
||||
})
|
||||
@@ -153,12 +163,35 @@ const AddGroup = ({
|
||||
isSaving(true);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
if (selectedGroup === null) {
|
||||
setGroupName("");
|
||||
}
|
||||
|
||||
setSelectedUsers([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={closeModalAndRefresh}
|
||||
title={selectedGroup !== null ? `Group Edit - ${groupName}` : "Add Group"}
|
||||
title={selectedGroup !== null ? `Edit Group` : "Create Group"}
|
||||
>
|
||||
{selectedGroup !== null && (
|
||||
<div className={classes.floatingEnabled}>
|
||||
<FormSwitchWrapper
|
||||
indicatorLabel={"Enabled"}
|
||||
checked={groupEnabled}
|
||||
value={"group_enabled"}
|
||||
id="group-status"
|
||||
name="group-status"
|
||||
onChange={(e) => {
|
||||
setGroupEnabled(e.target.checked);
|
||||
}}
|
||||
switchOnly
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<form noValidate autoComplete="off" onSubmit={setSaving}>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
@@ -174,31 +207,13 @@ const AddGroup = ({
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{selectedGroup !== null ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<RadioGroupSelector
|
||||
currentSelection={groupEnabled}
|
||||
id="group-status"
|
||||
name="group-status"
|
||||
label="Status"
|
||||
onChange={(e) => {
|
||||
setGroupEnabled(e.target.value);
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ label: "Enabled", value: "enabled" },
|
||||
{ label: "Disabled", value: "disabled" },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
{selectedGroup === null ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="group-name"
|
||||
name="group-name"
|
||||
label="Name"
|
||||
label="Group Name"
|
||||
value={groupName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setGroupName(e.target.value);
|
||||
@@ -206,23 +221,38 @@ const AddGroup = ({
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Group Name
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{selectedGroup}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<UsersSelectors
|
||||
selectedUsers={selectedUsers}
|
||||
setSelectedUsers={setSelectedUsers}
|
||||
editMode={selectedGroup !== null}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={resetForm}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={saving}
|
||||
disabled={saving || !validGroup}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
||||
@@ -28,11 +28,16 @@ import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import {
|
||||
actionsTray,
|
||||
predefinedList,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
interface IGroupsProps {
|
||||
classes: any;
|
||||
selectedUsers: string[];
|
||||
setSelectedUsers: any;
|
||||
editMode?: boolean;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -41,10 +46,11 @@ const styles = (theme: Theme) =>
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
paper: {
|
||||
// padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
paddingTop: 15,
|
||||
boxShadow: "none",
|
||||
},
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
@@ -70,36 +76,43 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "left",
|
||||
"& button": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
},
|
||||
filterField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
width: "100%",
|
||||
zIndex: 500,
|
||||
},
|
||||
noFound: {
|
||||
textAlign: "center",
|
||||
padding: "10px 0",
|
||||
},
|
||||
tableContainer: {
|
||||
maxHeight: 250,
|
||||
maxHeight: 200,
|
||||
},
|
||||
stickyHeader: {
|
||||
backgroundColor: "#fff",
|
||||
},
|
||||
actionsTitle: {
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
fontSize: 16,
|
||||
alignSelf: "center",
|
||||
},
|
||||
tableBlock: {
|
||||
marginTop: 15,
|
||||
},
|
||||
filterField: {
|
||||
width: 375,
|
||||
fontWeight: 600,
|
||||
"& .input": {
|
||||
"&::placeholder": {
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
},
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
});
|
||||
|
||||
const UsersSelectors = ({
|
||||
classes,
|
||||
selectedUsers,
|
||||
setSelectedUsers,
|
||||
editMode = false,
|
||||
}: IGroupsProps) => {
|
||||
//Local States
|
||||
const [records, setRecords] = useState<any[]>([]);
|
||||
@@ -166,21 +179,22 @@ const UsersSelectors = ({
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title>Assign Users</Title>
|
||||
{error !== "" ? <div>{error}</div> : <React.Fragment />}
|
||||
<Grid item xs={12}>
|
||||
<Paper className={classes.paper}>
|
||||
{loading && <LinearProgress />}
|
||||
{error !== "" ? <div>{error}</div> : <React.Fragment />}
|
||||
{records != null && records.length > 0 ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<span className={classes.actionsTitle}>
|
||||
{editMode ? "Edit" : "Assign"} Members
|
||||
</span>
|
||||
<TextField
|
||||
placeholder="Filter Groups"
|
||||
className={classes.filterField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
@@ -192,7 +206,7 @@ const UsersSelectors = ({
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12} className={classes.tableBlock}>
|
||||
<TableWrapper
|
||||
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
|
||||
onSelect={selectionChanged}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { useEffect } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||
import { AppState } from "../../../store";
|
||||
import { connect } from "react-redux";
|
||||
@@ -21,42 +21,51 @@ import { logMessageReceived, logResetMessages } from "./actions";
|
||||
import { LogMessage } from "./types";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { timeFromDate } from "../../../common/utils";
|
||||
import { isNullOrUndefined } from "util";
|
||||
import { wsProtocol } from "../../../utils/wsUtils";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { Button, Grid } from "@material-ui/core";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
logList: {
|
||||
background: "white",
|
||||
maxHeight: "400px",
|
||||
background: "#fff",
|
||||
minHeight: 400,
|
||||
height: "calc(100vh - 270px)",
|
||||
overflow: "auto",
|
||||
"& ul": {
|
||||
margin: "4px",
|
||||
padding: "0px",
|
||||
},
|
||||
"& ul li": {
|
||||
listStyle: "none",
|
||||
margin: "0px",
|
||||
padding: "0px",
|
||||
borderBottom: "1px solid #dedede",
|
||||
},
|
||||
fontSize: 13,
|
||||
padding: "25px 45px",
|
||||
border: "1px solid #EAEDEE",
|
||||
borderRadius: 4,
|
||||
},
|
||||
tab: {
|
||||
padding: "25px",
|
||||
paddingLeft: 25,
|
||||
},
|
||||
logerror: {
|
||||
color: "#A52A2A",
|
||||
},
|
||||
logerror_tab: {
|
||||
color: "#A52A2A",
|
||||
padding: "25px",
|
||||
paddingLeft: 25,
|
||||
},
|
||||
ansidefault: {
|
||||
color: "black",
|
||||
color: "#000",
|
||||
},
|
||||
highlight: {
|
||||
"& span": {
|
||||
backgroundColor: "#082F5238",
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
@@ -73,6 +82,8 @@ const Logs = ({
|
||||
logResetMessages,
|
||||
messages,
|
||||
}: ILogs) => {
|
||||
const [highlight, setHighlight] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
logResetMessages();
|
||||
const url = new URL(window.location.toString());
|
||||
@@ -128,84 +139,145 @@ const Logs = ({
|
||||
|
||||
const renderError = (logElement: LogMessage) => {
|
||||
let errorElems = [];
|
||||
if (!isNullOrUndefined(logElement.error)) {
|
||||
if (logElement.error !== null && logElement.error !== undefined) {
|
||||
if (logElement.api && logElement.api.name) {
|
||||
const errorText = `API: ${logElement.api.name}`;
|
||||
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
|
||||
errorElems.push(
|
||||
<li key={`api-${logElement.key}`}>
|
||||
<span className={classes.logerror}>API: {logElement.api.name}</span>
|
||||
</li>
|
||||
<div
|
||||
key={`api-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<br />
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.time) {
|
||||
const errorText = `Time: ${timeFromDate(logElement.time)}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<li key={`time-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
Time: {timeFromDate(logElement.time)}
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
key={`time-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.deploymentid) {
|
||||
const errorText = `DeploymentID: ${logElement.deploymentid}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<li key={`deploytmentid-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
DeploymentID: {logElement.deploymentid}
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
key={`deploytmentid-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.requestID) {
|
||||
const errorText = `RequestID: ${logElement.requestID}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<li key={`requestid-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
RequestID: {logElement.requestID}
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
key={`requestid-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.remotehost) {
|
||||
const errorText = `RemoteHost: ${logElement.remotehost}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<li key={`remotehost-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
RemoteHost: {logElement.remotehost}
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
key={`remotehost-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.host) {
|
||||
const errorText = `Host: ${logElement.host}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<li key={`host-${logElement.key}`}>
|
||||
<span className={classes.logerror}>Host: {logElement.host}</span>
|
||||
</li>
|
||||
<div
|
||||
key={`host-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.userAgent) {
|
||||
const errorText = `UserAgent: ${logElement.userAgent}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<li key={`useragent-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
UserAgent: {logElement.userAgent}
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
key={`useragent-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.error.message) {
|
||||
const errorText = `Error: ${logElement.error.message}`;
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<li key={`message-${logElement.key}`}>
|
||||
<span className={classes.logerror}>
|
||||
Error: {logElement.error.message}
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
key={`message-${logElement.key}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (logElement.error.source) {
|
||||
// for all sources add padding
|
||||
for (let s in logElement.error.source) {
|
||||
const errorText = logElement.error.source[s];
|
||||
const highlightedLine =
|
||||
highlight !== ""
|
||||
? errorText.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
errorElems.push(
|
||||
<li key={`source-${logElement.key}-${s}`}>
|
||||
<span className={classes.logerror_tab}>
|
||||
{logElement.error.source[s]}
|
||||
</span>
|
||||
</li>
|
||||
<div
|
||||
key={`source-${logElement.key}-${s}`}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.logerror_tab}>{errorText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -226,38 +298,72 @@ const Logs = ({
|
||||
// only to the first match.
|
||||
let substr = logMessage.replace(tColorRegex, "");
|
||||
|
||||
// in case highlight is set, we select the line that contains the requested string
|
||||
let highlightedLine =
|
||||
highlight !== ""
|
||||
? logMessage.toLowerCase().includes(highlight.toLowerCase())
|
||||
: false;
|
||||
|
||||
// if starts with multiple spaces add padding
|
||||
if (substr.startsWith(" ")) {
|
||||
return (
|
||||
<li key={logElement.key}>
|
||||
<div
|
||||
key={logElement.key}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.tab}>{substr}</span>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
} else if (!isNullOrUndefined(logElement.error)) {
|
||||
} else if (logElement.error !== null && logElement.error !== undefined) {
|
||||
// list error message and all sources and error elems
|
||||
return renderError(logElement);
|
||||
} else {
|
||||
// for all remaining set default class
|
||||
return (
|
||||
<li key={logElement.key}>
|
||||
<div
|
||||
key={logElement.key}
|
||||
className={`${highlightedLine ? classes.highlight : ""}`}
|
||||
>
|
||||
<span className={classes.ansidefault}>{substr}</span>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderLines = messages.map((m) => {
|
||||
return renderLog(m);
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageHeader label="Logs" />
|
||||
<Grid container>
|
||||
<Grid className={classes.container} item xs={12}>
|
||||
<div className={classes.logList}>
|
||||
<ul>
|
||||
{messages.map((m) => {
|
||||
return renderLog(m);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Highlight Line"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setHighlight(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.logList}>{renderLines}</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
removeEmptyFields,
|
||||
} from "../Configurations/utils";
|
||||
import { IElementValue } from "../Configurations/types";
|
||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -60,6 +61,69 @@ const styles = (theme: Theme) =>
|
||||
logoButton: {
|
||||
height: "80px",
|
||||
},
|
||||
lambdaNotif: {
|
||||
border: "#393939 1px solid",
|
||||
borderRadius: 5,
|
||||
width: 101,
|
||||
height: 91,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginBottom: 16,
|
||||
cursor: "pointer",
|
||||
"& img": {
|
||||
maxWidth: 71,
|
||||
maxHeight: 71,
|
||||
},
|
||||
},
|
||||
iconContainer: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
width: 455,
|
||||
justifyContent: "space-between",
|
||||
flexWrap: "wrap",
|
||||
},
|
||||
nonIconContainer: {
|
||||
marginBottom: 16,
|
||||
"& button": {
|
||||
marginRight: 16,
|
||||
},
|
||||
},
|
||||
pickTitle: {
|
||||
fontWeight: 600,
|
||||
color: "#393939",
|
||||
fontSize: 14,
|
||||
marginBottom: 16,
|
||||
},
|
||||
lambdaFormIndicator: {
|
||||
display: "flex",
|
||||
marginBottom: 40,
|
||||
},
|
||||
lambdaName: {
|
||||
fontSize: 18,
|
||||
fontWeight: 700,
|
||||
color: "#000",
|
||||
marginBottom: 6,
|
||||
},
|
||||
lambdaSubname: {
|
||||
fontSize: 12,
|
||||
color: "#000",
|
||||
fontWeight: 600,
|
||||
},
|
||||
lambdaIcon: {
|
||||
borderRadius: 5,
|
||||
border: "#393939 1px solid",
|
||||
width: 53,
|
||||
height: 48,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 16,
|
||||
"& img": {
|
||||
width: 38,
|
||||
},
|
||||
},
|
||||
...modalBasic,
|
||||
});
|
||||
|
||||
interface IAddNotificationEndpointProps {
|
||||
@@ -136,186 +200,111 @@ const AddNotificationEndpoint = ({
|
||||
}
|
||||
}
|
||||
|
||||
let targetTitle = "";
|
||||
switch (service) {
|
||||
case notifyNsq:
|
||||
targetTitle = "NSQ";
|
||||
break;
|
||||
case notifyWebhooks:
|
||||
targetTitle = "Webhooks";
|
||||
break;
|
||||
case notifyElasticsearch:
|
||||
targetTitle = "Elastic Search";
|
||||
break;
|
||||
case notifyNats:
|
||||
targetTitle = "NATS";
|
||||
break;
|
||||
case notifyMqtt:
|
||||
targetTitle = "MQTT";
|
||||
break;
|
||||
case notifyRedis:
|
||||
targetTitle = "Redis";
|
||||
break;
|
||||
case notifyKafka:
|
||||
targetTitle = "Kafka";
|
||||
break;
|
||||
case notifyPostgres:
|
||||
targetTitle = "Postgres";
|
||||
break;
|
||||
case notifyMysql:
|
||||
targetTitle = "Mysql";
|
||||
break;
|
||||
case notifyAmqp:
|
||||
targetTitle = "AMQP";
|
||||
break;
|
||||
}
|
||||
const servicesList = [
|
||||
{
|
||||
actionTrigger: notifyPostgres,
|
||||
targetTitle: "Postgres SQL",
|
||||
logo: "/postgres.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyKafka,
|
||||
targetTitle: "Kafka",
|
||||
logo: "/kafka.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyAmqp,
|
||||
targetTitle: "AMQP",
|
||||
logo: "/amqp.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyMqtt,
|
||||
targetTitle: "MQTT",
|
||||
logo: "/mqtt.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyRedis,
|
||||
targetTitle: "Redis",
|
||||
logo: "/redis.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyNats,
|
||||
targetTitle: "NATS",
|
||||
logo: "/nats.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyMysql,
|
||||
targetTitle: "Mysql",
|
||||
logo: "/mysql.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyElasticsearch,
|
||||
targetTitle: "Elastic Search",
|
||||
logo: "/elasticsearch.png",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyWebhooks,
|
||||
targetTitle: "Webhook",
|
||||
logo: "",
|
||||
},
|
||||
{
|
||||
actionTrigger: notifyNsq,
|
||||
targetTitle: "NSQ",
|
||||
logo: "",
|
||||
},
|
||||
];
|
||||
|
||||
const nonLogos = servicesList.filter((elService) => elService.logo === "");
|
||||
const withLogos = servicesList.filter((elService) => elService.logo !== "");
|
||||
|
||||
const targetElement = servicesList.find(
|
||||
(element) => element.actionTrigger === service
|
||||
);
|
||||
|
||||
const goBack = () => {
|
||||
setService("");
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={closeModalAndRefresh}
|
||||
title={`Add Lambda Notification Target ${targetTitle}`}
|
||||
>
|
||||
<ModalWrapper modalOpen={open} onClose={closeModalAndRefresh} title={""}>
|
||||
{service === "" && (
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<p>Pick a supported service:</p>
|
||||
<table className={classes.chooseTable} style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyPostgres);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/postgres.png"
|
||||
className={classes.logoButton}
|
||||
alt="postgres"
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyKafka);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/kafka.png"
|
||||
className={classes.logoButton}
|
||||
alt="kafka"
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyAmqp);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/amqp.png"
|
||||
className={classes.logoButton}
|
||||
alt="amqp"
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyMqtt);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/mqtt.png"
|
||||
className={classes.logoButton}
|
||||
alt="mqtt"
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyRedis);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/redis.png"
|
||||
className={classes.logoButton}
|
||||
alt="redis"
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyNats);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/nats.png"
|
||||
className={classes.logoButton}
|
||||
alt="nats"
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyMysql);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/mysql.png"
|
||||
className={classes.logoButton}
|
||||
alt="mysql"
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyElasticsearch);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="/elasticsearch.png"
|
||||
className={classes.logoButton}
|
||||
alt="elasticsearch"
|
||||
/>
|
||||
</Button>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyWebhooks);
|
||||
}}
|
||||
>
|
||||
Webhook
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setService(notifyNsq);
|
||||
}}
|
||||
>
|
||||
NSQ
|
||||
</Button>
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className={classes.pickTitle}>Pick a supported service:</div>
|
||||
<div className={classes.nonIconContainer}>
|
||||
{nonLogos.map((item) => {
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
key={`non-icon-${item.targetTitle}`}
|
||||
onClick={() => {
|
||||
setService(item.actionTrigger);
|
||||
}}
|
||||
>
|
||||
{item.targetTitle.toUpperCase()}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={classes.iconContainer}>
|
||||
{withLogos.map((item) => {
|
||||
return (
|
||||
<a
|
||||
key={`icon-${item.targetTitle}`}
|
||||
className={classes.lambdaNotif}
|
||||
onClick={() => {
|
||||
setService(item.actionTrigger);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={item.logo}
|
||||
className={classes.logoButton}
|
||||
alt={item.targetTitle}
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
@@ -342,10 +331,37 @@ const AddNotificationEndpoint = ({
|
||||
</Grid>
|
||||
)}
|
||||
<form noValidate onSubmit={submitForm}>
|
||||
<Grid item xs={12} className={classes.lambdaFormIndicator}>
|
||||
{targetElement && targetElement.logo !== "" && (
|
||||
<div className={classes.lambdaIcon}>
|
||||
<img
|
||||
src={targetElement.logo}
|
||||
alt={targetElement.targetTitle}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={classes.lambdaTitle}>
|
||||
<div className={classes.lambdaName}>
|
||||
{targetElement ? targetElement.targetTitle : ""}
|
||||
</div>
|
||||
<div className={classes.lambdaSubname}>
|
||||
Add Lambda Notification Target
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{srvComponent}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={goBack}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
|
||||
@@ -16,19 +16,18 @@
|
||||
|
||||
import React from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { UnControlled as CodeMirror } from "react-codemirror2";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import api from "../../../common/api";
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import "codemirror/theme/material.css";
|
||||
import { Policy } from "./types";
|
||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
fieldBasic,
|
||||
modalBasic,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
|
||||
require("codemirror/mode/javascript/javascript");
|
||||
import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -39,26 +38,11 @@ const styles = (theme: Theme) =>
|
||||
minHeight: 400,
|
||||
width: "100%",
|
||||
},
|
||||
codeMirror: {
|
||||
fontSize: 14,
|
||||
"& .CodeMirror": {
|
||||
color: "#fff",
|
||||
backgroundColor: "#081C42",
|
||||
},
|
||||
"& .CodeMirror-gutter": {
|
||||
backgroundColor: "#081C4280",
|
||||
},
|
||||
"& .CodeMirror-linenumber": {
|
||||
color: "#000",
|
||||
fontSize: 10,
|
||||
height: 20,
|
||||
lineHeight: "20px",
|
||||
},
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...fieldBasic,
|
||||
});
|
||||
|
||||
interface IAddPolicyProps {
|
||||
@@ -121,13 +105,26 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
|
||||
if (policyEdit) {
|
||||
this.setState({
|
||||
policyName: policyEdit.name,
|
||||
policyDefinition: policyEdit
|
||||
? JSON.stringify(JSON.parse(policyEdit.policy), null, 4)
|
||||
: "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resetForm() {
|
||||
this.setState({
|
||||
policyName: "",
|
||||
policyDefinition: "",
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, open, policyEdit } = this.props;
|
||||
const { addLoading, addError, policyName } = this.state;
|
||||
const { addLoading, addError, policyName, policyDefinition } = this.state;
|
||||
|
||||
const validSave = policyName.trim() !== "";
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
@@ -163,6 +160,7 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
|
||||
id="policy-name"
|
||||
name="policy-name"
|
||||
label="Policy Name"
|
||||
placeholder="Enter Policy Name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ policyName: e.target.value });
|
||||
}}
|
||||
@@ -173,31 +171,32 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<CodeMirror
|
||||
className={classes.codeMirror}
|
||||
value={
|
||||
policyEdit
|
||||
? JSON.stringify(JSON.parse(policyEdit.policy), null, 4)
|
||||
: ""
|
||||
}
|
||||
options={{
|
||||
mode: "javascript",
|
||||
lineNumbers: true,
|
||||
}}
|
||||
onChange={(editor, data, value) => {
|
||||
this.setState({ policyDefinition: value });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<CodeMirrorWrapper
|
||||
label="Write Policy"
|
||||
value={policyDefinition}
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
this.setState({ policyDefinition: value });
|
||||
}}
|
||||
readOnly={!!policyEdit}
|
||||
/>
|
||||
</Grid>
|
||||
{!policyEdit && (
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={() => {
|
||||
this.resetForm();
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading}
|
||||
disabled={addLoading || !validSave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
LinearProgress
|
||||
LinearProgress,
|
||||
} from "@material-ui/core";
|
||||
import api from "../../../common/api";
|
||||
import { PolicyList } from "./types";
|
||||
@@ -32,8 +32,8 @@ import Typography from "@material-ui/core/Typography";
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red"
|
||||
}
|
||||
color: "red",
|
||||
},
|
||||
});
|
||||
|
||||
interface IDeletePolicyProps {
|
||||
@@ -54,7 +54,7 @@ class DeletePolicy extends React.Component<
|
||||
> {
|
||||
state: IDeletePolicyState = {
|
||||
deleteLoading: false,
|
||||
deleteError: ""
|
||||
deleteError: "",
|
||||
};
|
||||
removeRecord() {
|
||||
const { deleteLoading } = this.state;
|
||||
@@ -69,17 +69,17 @@ class DeletePolicy extends React.Component<
|
||||
this.setState(
|
||||
{
|
||||
deleteLoading: false,
|
||||
deleteError: ""
|
||||
deleteError: "",
|
||||
},
|
||||
() => {
|
||||
this.props.closeDeleteModalAndRefresh(true);
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
this.setState({
|
||||
deleteLoading: false,
|
||||
deleteError: err
|
||||
deleteError: err,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -98,7 +98,7 @@ class DeletePolicy extends React.Component<
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">Delete Bucket</DialogTitle>
|
||||
<DialogTitle id="alert-dialog-title">Delete Policy</DialogTitle>
|
||||
<DialogContent>
|
||||
{deleteLoading && <LinearProgress />}
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
|
||||
204
portal-ui/src/screens/Console/Policies/PolicySelectors.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { LinearProgress } from "@material-ui/core";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import api from "../../../common/api";
|
||||
import { policySort } from "../../../utils/sortFunctions";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
|
||||
import { PolicyList } from "./types";
|
||||
|
||||
interface ISelectPolicyProps {
|
||||
classes: any;
|
||||
selectedPolicy?: string;
|
||||
setSelectedPolicy: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
paper: {
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
paddingTop: 15,
|
||||
boxShadow: "none",
|
||||
},
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
},
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
tableToolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(0),
|
||||
},
|
||||
wrapCell: {
|
||||
maxWidth: "200px",
|
||||
whiteSpace: "normal",
|
||||
wordWrap: "break-word",
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
"& tr": {
|
||||
"& th": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
},
|
||||
noFound: {
|
||||
textAlign: "center",
|
||||
padding: "10px 0",
|
||||
},
|
||||
tableContainer: {
|
||||
maxHeight: 200,
|
||||
},
|
||||
stickyHeader: {
|
||||
backgroundColor: "#fff",
|
||||
},
|
||||
actionsTitle: {
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
fontSize: 16,
|
||||
alignSelf: "center",
|
||||
},
|
||||
tableBlock: {
|
||||
marginTop: 15,
|
||||
},
|
||||
filterField: {
|
||||
width: 375,
|
||||
fontWeight: 600,
|
||||
"& .input": {
|
||||
"&::placeholder": {
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
},
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
});
|
||||
|
||||
const PolicySelectors = ({
|
||||
classes,
|
||||
selectedPolicy = "",
|
||||
setSelectedPolicy,
|
||||
}: ISelectPolicyProps) => {
|
||||
// Local State
|
||||
const [records, setRecords] = useState<any[]>([]);
|
||||
const [loading, isLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string>("");
|
||||
const [filter, setFilter] = useState<string>("");
|
||||
|
||||
//Effects
|
||||
useEffect(() => {
|
||||
isLoading(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
fetchPolicies();
|
||||
}
|
||||
}, [loading]);
|
||||
|
||||
const fetchPolicies = () => {
|
||||
isLoading(true);
|
||||
|
||||
api
|
||||
.invoke("GET", `/api/v1/policies?limit=1000`)
|
||||
.then((res: PolicyList) => {
|
||||
const policies = res.policies === null ? [] : res.policies;
|
||||
isLoading(false);
|
||||
setRecords(policies.sort(policySort));
|
||||
setError("");
|
||||
})
|
||||
.catch((err) => {
|
||||
isLoading(false);
|
||||
setError(err);
|
||||
});
|
||||
};
|
||||
|
||||
const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const targetD = e.target;
|
||||
const value = targetD.value;
|
||||
|
||||
setSelectedPolicy(value);
|
||||
};
|
||||
|
||||
const filteredRecords = records.filter((elementItem) =>
|
||||
elementItem.name.includes(filter)
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Paper className={classes.paper}>
|
||||
{loading && <LinearProgress />}
|
||||
{error !== "" && <div>{error}</div>}
|
||||
{records != null && records.length > 0 ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<span className={classes.actionsTitle}>Assign Policies</span>
|
||||
<TextField
|
||||
placeholder="Filter by Policy"
|
||||
className={classes.filterField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setFilter(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.tableBlock}>
|
||||
<TableWrapper
|
||||
columns={[{ label: "Policy", elementKey: "name" }]}
|
||||
onSelect={selectionChanged}
|
||||
selectedItems={[selectedPolicy]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
radioSelection
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<div className={classes.noFound}>No Policies Available</div>
|
||||
)}
|
||||
</Paper>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(PolicySelectors);
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
Button,
|
||||
@@ -28,13 +29,17 @@ import {
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
modalBasic,
|
||||
predefinedList,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { User } from "../Users/types";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import { Policy, PolicyList } from "./types";
|
||||
import api from "../../../common/api";
|
||||
import { policySort } from "../../../utils/sortFunctions";
|
||||
import { Group } from "../Groups/types";
|
||||
import PolicySelectors from "./PolicySelectors";
|
||||
|
||||
interface ISetPolicyProps {
|
||||
classes: any;
|
||||
@@ -47,6 +52,7 @@ interface ISetPolicyProps {
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...modalBasic,
|
||||
...predefinedList,
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
@@ -60,28 +66,12 @@ const SetPolicy = ({
|
||||
open,
|
||||
}: ISetPolicyProps) => {
|
||||
//Local States
|
||||
const [records, setRecords] = useState<Policy[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [actualPolicy, setActualPolicy] = useState<string>("");
|
||||
const [selectedPolicy, setSelectedPolicy] = useState<string>("");
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const fetchRecords = () => {
|
||||
setLoading(true);
|
||||
|
||||
api
|
||||
.invoke("GET", `/api/v1/policies?limit=1000`)
|
||||
.then((res: PolicyList) => {
|
||||
const policies = res.policies === null ? [] : res.policies;
|
||||
setLoading(false);
|
||||
setRecords(policies.sort(policySort));
|
||||
setError("");
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoading(false);
|
||||
setError(err);
|
||||
});
|
||||
};
|
||||
|
||||
const setPolicyAction = (policyName: string) => {
|
||||
const setPolicyAction = () => {
|
||||
let entity = "user";
|
||||
let value = null;
|
||||
if (selectedGroup !== null) {
|
||||
@@ -96,7 +86,7 @@ const SetPolicy = ({
|
||||
setLoading(true);
|
||||
|
||||
api
|
||||
.invoke("PUT", `/api/v1/set-policy/${policyName}`, {
|
||||
.invoke("PUT", `/api/v1/set-policy/${selectedPolicy}`, {
|
||||
entityName: value,
|
||||
entityType: entity,
|
||||
})
|
||||
@@ -111,71 +101,89 @@ const SetPolicy = ({
|
||||
});
|
||||
};
|
||||
|
||||
const fetchGroupInformation = () => {
|
||||
if (selectedGroup) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/groups/${selectedGroup}`)
|
||||
.then((res: any) => {
|
||||
const groupPolicy = get(res, "policy", "");
|
||||
setActualPolicy(groupPolicy);
|
||||
setSelectedPolicy(groupPolicy);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError(err);
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const resetSelection = () => {
|
||||
setSelectedPolicy(actualPolicy);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
fetchRecords();
|
||||
if (selectedGroup !== null) {
|
||||
fetchGroupInformation();
|
||||
return;
|
||||
}
|
||||
|
||||
const userPolicy = get(selectedUser, "policy", "");
|
||||
setActualPolicy(userPolicy);
|
||||
setSelectedPolicy(userPolicy);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const userName = get(selectedUser, "accessKey", "");
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
onClose={() => {
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
modalOpen={open}
|
||||
title={
|
||||
selectedUser !== null ? "Set Policy to User" : "Set Policy to Group"
|
||||
}
|
||||
title="Set Policies"
|
||||
>
|
||||
<Grid container className={classes.formScrollable}>
|
||||
<Grid item xs={12}>
|
||||
<TableContainer component={Paper}>
|
||||
<Table
|
||||
className={classes.table}
|
||||
size="small"
|
||||
aria-label="a dense table"
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Policy</TableCell>
|
||||
<TableCell align="right"></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{records.map((row) => (
|
||||
<TableRow key={row.name}>
|
||||
<TableCell component="th" scope="row">
|
||||
{row.name}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size={"small"}
|
||||
onClick={() => {
|
||||
setPolicyAction(row.name);
|
||||
}}
|
||||
>
|
||||
Set
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Selected {selectedGroup !== null ? "Group" : "User"}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{selectedGroup !== null ? selectedGroup : userName}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Current Policy
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{actualPolicy}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<PolicySelectors
|
||||
selectedPolicy={selectedPolicy}
|
||||
setSelectedPolicy={setSelectedPolicy}
|
||||
/>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={resetSelection}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
type="button"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
disabled={loading}
|
||||
onClick={setPolicyAction}
|
||||
>
|
||||
Cancel
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{loading && (
|
||||
|
||||
@@ -16,19 +16,14 @@
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { UnControlled as CodeMirror } from "react-codemirror2";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { Button, LinearProgress, Tooltip } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import api from "../../../common/api";
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import "codemirror/theme/material.css";
|
||||
import { NewServiceAccount } from "../Common/CredentialsPrompt/types";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
|
||||
require("codemirror/mode/javascript/javascript");
|
||||
import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -39,9 +34,6 @@ const styles = (theme: Theme) =>
|
||||
minHeight: 400,
|
||||
width: "100%",
|
||||
},
|
||||
codeMirror: {
|
||||
fontSize: 14,
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
@@ -92,6 +84,10 @@ const AddServiceAccount = ({
|
||||
setAddSending(true);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setPolicyDefinition("");
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
@@ -120,29 +116,24 @@ const AddServiceAccount = ({
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<Typography component="h5">
|
||||
Optional Policy
|
||||
<Tooltip
|
||||
title="A policy that restricts this service account can be attached."
|
||||
placement="top-start"
|
||||
>
|
||||
<HelpIcon />
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
<CodeMirror
|
||||
className={classes.codeMirror}
|
||||
options={{
|
||||
mode: "javascript",
|
||||
lineNumbers: true,
|
||||
}}
|
||||
onChange={(editor, data, value) => {
|
||||
setPolicyDefinition(value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<CodeMirrorWrapper
|
||||
value={policyDefinition}
|
||||
label="Optional Policy"
|
||||
tooltip="A policy that restricts this service account can be attached."
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
setPolicyDefinition(value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={resetForm}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
|
||||
@@ -2113,6 +2113,7 @@ const AddTenant = ({
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
wideLimit={false}
|
||||
>
|
||||
{addSending && (
|
||||
<Grid item xs={12}>
|
||||
|
||||
@@ -248,7 +248,7 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
entity="Tenant"
|
||||
/>
|
||||
)}
|
||||
<PageHeader label={"Users"} />
|
||||
<PageHeader label={"Tenants"} />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
|
||||
@@ -26,12 +26,13 @@ import { wsProtocol } from "../../../utils/wsUtils";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
logList: {
|
||||
background: "white",
|
||||
maxHeight: "400px",
|
||||
height: "400px",
|
||||
overflow: "auto",
|
||||
"& ul": {
|
||||
margin: "4px",
|
||||
@@ -101,20 +102,50 @@ const Trace = ({
|
||||
<PageHeader label={"Trace"} />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<div className={classes.logList}>
|
||||
<ul>
|
||||
{messages.map((m) => {
|
||||
return (
|
||||
<li key={m.key}>
|
||||
{timeFromDate(m.time)} - {m.api}[{m.statusCode}{" "}
|
||||
{m.statusMsg}] {m.api} {m.host} {m.client}{" "}
|
||||
{m.callStats.duration} ↑ {niceBytes(m.callStats.rx + "")} ↓{" "}
|
||||
{niceBytes(m.callStats.tx + "")}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={[
|
||||
{
|
||||
label: "Time",
|
||||
elementKey: "time",
|
||||
renderFunction: (time: Date) => {
|
||||
const timeParse = new Date(time);
|
||||
return timeFromDate(timeParse);
|
||||
},
|
||||
},
|
||||
{ label: "Name", elementKey: "api" },
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "",
|
||||
renderFunction: (fullElement: TraceMessage) =>
|
||||
`${fullElement.statusCode} ${fullElement.statusMsg}`,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Location",
|
||||
elementKey: "configuration_id",
|
||||
renderFunction: (fullElement: TraceMessage) =>
|
||||
`${fullElement.host} ${fullElement.client}`,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{ label: "Load Time", elementKey: "callStats.duration" },
|
||||
{
|
||||
label: "Upload",
|
||||
elementKey: "callStats.rx",
|
||||
renderFunction: niceBytes,
|
||||
},
|
||||
{
|
||||
label: "Download",
|
||||
elementKey: "callStats.tx",
|
||||
renderFunction: niceBytes,
|
||||
},
|
||||
]}
|
||||
isLoading={false}
|
||||
records={messages}
|
||||
entityName="Traces"
|
||||
idField="api"
|
||||
customEmptyMessage="There are no traced Elements yet"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -60,6 +60,7 @@ const AddToGroup = ({
|
||||
}: IAddToGroup) => {
|
||||
//Local States
|
||||
const [saving, isSaving] = useState<boolean>(false);
|
||||
const [accepted, setAccepted] = useState<boolean>(false);
|
||||
const [updatingError, setError] = useState<string>("");
|
||||
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
|
||||
|
||||
@@ -75,7 +76,7 @@ const AddToGroup = ({
|
||||
.then((res) => {
|
||||
isSaving(false);
|
||||
setError("");
|
||||
closeModalAndRefresh(true);
|
||||
setAccepted(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
isSaving(false);
|
||||
@@ -102,62 +103,100 @@ const AddToGroup = ({
|
||||
isSaving(true);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setSelectedGroups([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
closeModalAndRefresh(false);
|
||||
closeModalAndRefresh(accepted);
|
||||
}}
|
||||
title="Add Users to Group"
|
||||
title={
|
||||
accepted
|
||||
? "The selected users were added to the following groups."
|
||||
: "Add Users to Group"
|
||||
}
|
||||
>
|
||||
<form noValidate autoComplete="off" onSubmit={setSaving}>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
{updatingError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{updatingError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{accepted ? (
|
||||
<React.Fragment>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Selected Users
|
||||
Groups
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{selectedGroups.join(", ")}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Users
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{checkedUsers.join(", ")}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<GroupsSelectors
|
||||
selectedGroups={selectedGroups}
|
||||
setSelectedGroups={setSelectedGroups}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={saving}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{saving && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<form noValidate autoComplete="off" onSubmit={setSaving}>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
{updatingError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{updatingError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Selected Users
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{checkedUsers.join(", ")}
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<GroupsSelectors
|
||||
selectedGroups={selectedGroups}
|
||||
setSelectedGroups={setSelectedGroups}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={resetForm}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={saving || selectedGroups.length < 1}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{saving && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
)}
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,13 +19,17 @@ import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
modalBasic,
|
||||
predefinedList,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { User } from "./types";
|
||||
import api from "../../../common/api";
|
||||
import GroupsSelectors from "./GroupsSelectors";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -42,6 +46,7 @@ const styles = (theme: Theme) =>
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...predefinedList,
|
||||
});
|
||||
|
||||
interface IAddUserContentProps {
|
||||
@@ -57,7 +62,8 @@ interface IAddUserContentState {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
selectedGroups: string[];
|
||||
enabled: string;
|
||||
currentGroups: string[];
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
class AddUserContent extends React.Component<
|
||||
@@ -69,8 +75,9 @@ class AddUserContent extends React.Component<
|
||||
addError: "",
|
||||
accessKey: "",
|
||||
secretKey: "",
|
||||
enabled: "enabled",
|
||||
enabled: false,
|
||||
selectedGroups: [],
|
||||
currentGroups: [],
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
@@ -103,7 +110,7 @@ class AddUserContent extends React.Component<
|
||||
if (selectedUser !== null) {
|
||||
api
|
||||
.invoke("PUT", `/api/v1/users/${selectedUser.accessKey}`, {
|
||||
status: enabled,
|
||||
status: enabled ? "enabled" : "disabled",
|
||||
groups: selectedGroups,
|
||||
})
|
||||
.then((res) => {
|
||||
@@ -167,7 +174,8 @@ class AddUserContent extends React.Component<
|
||||
addError: "",
|
||||
accessKey: res.accessKey,
|
||||
selectedGroups: res.memberOf || [],
|
||||
enabled: res.status,
|
||||
currentGroups: res.memberOf || [],
|
||||
enabled: res.status === "enabled",
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -178,6 +186,16 @@ class AddUserContent extends React.Component<
|
||||
});
|
||||
}
|
||||
|
||||
resetForm() {
|
||||
if (this.props.selectedUser !== null) {
|
||||
this.setState({ selectedGroups: [] });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ accessKey: "", secretKey: "", selectedGroups: [] });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, selectedUser } = this.props;
|
||||
const {
|
||||
@@ -186,9 +204,14 @@ class AddUserContent extends React.Component<
|
||||
accessKey,
|
||||
secretKey,
|
||||
selectedGroups,
|
||||
currentGroups,
|
||||
enabled,
|
||||
} = this.state;
|
||||
|
||||
const sendEnabled =
|
||||
accessKey.trim() !== "" &&
|
||||
((secretKey.trim() !== "" && selectedUser === null) ||
|
||||
selectedUser !== null);
|
||||
return (
|
||||
<ModalWrapper
|
||||
onClose={() => {
|
||||
@@ -197,6 +220,22 @@ class AddUserContent extends React.Component<
|
||||
modalOpen={this.props.open}
|
||||
title={selectedUser !== null ? "Edit User" : "Create User"}
|
||||
>
|
||||
{selectedUser !== null && (
|
||||
<div className={classes.floatingEnabled}>
|
||||
<FormSwitchWrapper
|
||||
indicatorLabel={"Enabled"}
|
||||
checked={enabled}
|
||||
value={"user_enabled"}
|
||||
id="user-status"
|
||||
name="user-status"
|
||||
onChange={(e) => {
|
||||
this.setState({ enabled: e.target.checked });
|
||||
}}
|
||||
switchOnly
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<React.Fragment>
|
||||
<form
|
||||
noValidate
|
||||
@@ -231,19 +270,14 @@ class AddUserContent extends React.Component<
|
||||
/>
|
||||
|
||||
{selectedUser !== null ? (
|
||||
<RadioGroupSelector
|
||||
currentSelection={enabled}
|
||||
id="user-status"
|
||||
name="user-status"
|
||||
label="Status"
|
||||
onChange={(e) => {
|
||||
this.setState({ enabled: e.target.value });
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ label: "Enabled", value: "enabled" },
|
||||
{ label: "Disabled", value: "disabled" },
|
||||
]}
|
||||
/>
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||
Current Groups
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.predefinedList}>
|
||||
{currentGroups.join(", ")}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<InputBoxWrapper
|
||||
id="standard-multiline-static"
|
||||
@@ -269,11 +303,21 @@ class AddUserContent extends React.Component<
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={() => {
|
||||
this.resetForm();
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading}
|
||||
disabled={addLoading || !sendEnabled}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2019 MinIO, Inc.
|
||||
// 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
|
||||
@@ -283,6 +283,7 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
||||
selectedGroup={null}
|
||||
closeModalAndRefresh={() => {
|
||||
this.setState({ setPolicyOpen: false });
|
||||
this.fetchRecords();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface User {
|
||||
enabled: boolean;
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
policy?: string;
|
||||
}
|
||||
|
||||
export interface UsersList {
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Button, Grid, Typography, TextField } from "@material-ui/core";
|
||||
import {
|
||||
Button,
|
||||
Grid,
|
||||
Typography,
|
||||
TextField,
|
||||
InputBase,
|
||||
} from "@material-ui/core";
|
||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||
import { AppState } from "../../../store";
|
||||
import { connect } from "react-redux";
|
||||
@@ -25,14 +31,23 @@ import { niceBytes, timeFromDate } from "../../../common/utils";
|
||||
import { wsProtocol } from "../../../utils/wsUtils";
|
||||
import api from "../../../common/api";
|
||||
import { FormControl, MenuItem, Select } from "@material-ui/core";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
fieldBasic,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
watchList: {
|
||||
background: "white",
|
||||
maxHeight: "400px",
|
||||
height: "400px",
|
||||
overflow: "auto",
|
||||
"& ul": {
|
||||
margin: "4px",
|
||||
@@ -45,31 +60,34 @@ const styles = (theme: Theme) =>
|
||||
borderBottom: "1px solid #dedede",
|
||||
},
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
},
|
||||
inputField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
marginLeft: 10,
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
},
|
||||
fieldContainer: {
|
||||
background: "#FFFFFF",
|
||||
padding: 0,
|
||||
borderRadius: 5,
|
||||
marginLeft: 10,
|
||||
textAlign: "left",
|
||||
minWidth: "206px",
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
searchPrefix: {
|
||||
flexGrow: 1,
|
||||
marginLeft: 15,
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const SelectStyled = withStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
width: 450,
|
||||
lineHeight: 1,
|
||||
"label + &": {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
"& .MuiSelect-select:focus": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
input: {
|
||||
fontSize: 13,
|
||||
lineHeight: 15,
|
||||
},
|
||||
})
|
||||
)(InputBase);
|
||||
|
||||
interface IWatch {
|
||||
classes: any;
|
||||
watchMessageReceived: typeof watchMessageReceived;
|
||||
@@ -156,6 +174,7 @@ const Watch = ({
|
||||
label: bucketName.name,
|
||||
value: bucketName.name,
|
||||
}));
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageHeader label="Watch" />
|
||||
@@ -170,8 +189,9 @@ const Watch = ({
|
||||
onChange={(e) => {
|
||||
setBucketName(e.target.value as string);
|
||||
}}
|
||||
className={classes.fieldContainer}
|
||||
className={classes.searchField}
|
||||
disabled={start}
|
||||
input={<SelectStyled />}
|
||||
>
|
||||
<MenuItem
|
||||
value={bucketName}
|
||||
@@ -192,7 +212,7 @@ const Watch = ({
|
||||
</FormControl>
|
||||
<TextField
|
||||
placeholder="Prefix"
|
||||
className={classes.inputField}
|
||||
className={`${classes.searchField} ${classes.searchPrefix}`}
|
||||
id="prefix-resource"
|
||||
label=""
|
||||
disabled={start}
|
||||
@@ -205,7 +225,7 @@ const Watch = ({
|
||||
/>
|
||||
<TextField
|
||||
placeholder="Suffix"
|
||||
className={classes.inputField}
|
||||
className={`${classes.searchField} ${classes.searchPrefix}`}
|
||||
id="suffix-resource"
|
||||
label=""
|
||||
disabled={start}
|
||||
@@ -229,18 +249,27 @@ const Watch = ({
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<div className={classes.watchList}>
|
||||
<ul>
|
||||
{messages.map((m) => {
|
||||
return (
|
||||
<li key={m.key}>
|
||||
{timeFromDate(m.Time)} - {niceBytes(m.Size + "")} - {m.Type}{" "}
|
||||
- {m.Path}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<TableWrapper
|
||||
columns={[
|
||||
{
|
||||
label: "Time",
|
||||
elementKey: "Time",
|
||||
renderFunction: timeFromDate,
|
||||
},
|
||||
{
|
||||
label: "Size",
|
||||
elementKey: "Size",
|
||||
renderFunction: niceBytes,
|
||||
},
|
||||
{ label: "Type", elementKey: "Type" },
|
||||
{ label: "Path", elementKey: "Path" },
|
||||
]}
|
||||
records={messages}
|
||||
entityName={"Watch"}
|
||||
customEmptyMessage={"No Changes at this time"}
|
||||
idField={"watch_table"}
|
||||
isLoading={false}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
3681
portal-ui/yarn.lock
@@ -646,7 +646,6 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
},
|
||||
Immutable: &imm,
|
||||
Data: map[string][]byte{
|
||||
"CONSOLE_HMAC_JWT_SECRET": []byte(RandomCharString(16)),
|
||||
"CONSOLE_PBKDF_PASSPHRASE": []byte(RandomCharString(16)),
|
||||
"CONSOLE_PBKDF_SALT": []byte(RandomCharString(8)),
|
||||
"CONSOLE_ACCESS_KEY": []byte(consoleAccess),
|
||||
@@ -680,7 +679,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
return nil, prepareError(errorGeneric)
|
||||
}
|
||||
|
||||
const consoleVersion = "minio/console:v0.4.1"
|
||||
const consoleVersion = "minio/console:v0.4.3"
|
||||
minInst.Spec.Console = &operator.ConsoleConfiguration{
|
||||
Replicas: 1,
|
||||
Image: consoleVersion,
|
||||
|
||||
@@ -1016,7 +1016,7 @@ func Test_UpdateTenantAction(t *testing.T) {
|
||||
},
|
||||
params: admin_api.UpdateTenantParams{
|
||||
Body: &models.UpdateTenantRequest{
|
||||
ConsoleImage: "minio/console:v0.4.1",
|
||||
ConsoleImage: "minio/console:v0.4.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -58,6 +58,8 @@ type MinioClient interface {
|
||||
listObjects(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo
|
||||
getObjectRetention(ctx context.Context, bucketName, objectName, versionID string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error)
|
||||
getObjectLegalHold(ctx context.Context, bucketName, objectName string, opts minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error)
|
||||
putObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error)
|
||||
putObjectLegalHold(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error
|
||||
}
|
||||
|
||||
// Interface implementation
|
||||
@@ -128,6 +130,14 @@ func (c minioClient) getObjectLegalHold(ctx context.Context, bucketName, objectN
|
||||
return c.client.GetObjectLegalHold(ctx, bucketName, objectName, opts)
|
||||
}
|
||||
|
||||
func (c minioClient) putObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) {
|
||||
return c.client.PutObject(ctx, bucketName, objectName, reader, objectSize, opts)
|
||||
}
|
||||
|
||||
func (c minioClient) putObjectLegalHold(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error {
|
||||
return c.client.PutObjectLegalHold(ctx, bucketName, objectName, opts)
|
||||
}
|
||||
|
||||
// MCClient interface with all functions to be implemented
|
||||
// by mock when testing, it should include all mc/S3Client respective api calls
|
||||
// that are used within this project.
|
||||
@@ -138,6 +148,7 @@ type MCClient interface {
|
||||
remove(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error
|
||||
list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent
|
||||
get(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *probe.Error)
|
||||
shareDownload(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error)
|
||||
}
|
||||
|
||||
// Interface implementation
|
||||
@@ -178,6 +189,10 @@ func (c mcClient) get(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *
|
||||
return c.client.Get(ctx, opts)
|
||||
}
|
||||
|
||||
func (c mcClient) shareDownload(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) {
|
||||
return c.client.ShareDownload(ctx, versionID, expires)
|
||||
}
|
||||
|
||||
// ConsoleCredentials interface with all functions to be implemented
|
||||
// by mock when testing, it should include all needed consoleCredentials.Login api calls
|
||||
// that are used within this project.
|
||||
@@ -255,7 +270,7 @@ func newConsoleCredentials(accessKey, secretKey, location string) (*credentials.
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
Location: location,
|
||||
DurationSeconds: xjwt.GetConsoleSTSAndJWTDurationInSeconds(),
|
||||
DurationSeconds: xjwt.GetConsoleSTSDurationInSeconds(),
|
||||
}
|
||||
stsClient := PrepareSTSClient(false)
|
||||
stsAssumeRole := &credentials.STSAssumeRole{
|
||||
@@ -269,23 +284,14 @@ func newConsoleCredentials(accessKey, secretKey, location string) (*credentials.
|
||||
}
|
||||
}
|
||||
|
||||
// GetClaimsFromJWT decrypt and returns the claims associated to a provided jwt
|
||||
func GetClaimsFromJWT(jwt string) (*auth.DecryptedClaims, error) {
|
||||
claims, err := auth.SessionTokenAuthenticate(jwt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// getConsoleCredentialsFromSession returns the *consoleCredentials.Login associated to the
|
||||
// provided jwt, this is useful for running the Expire() or IsExpired() operations
|
||||
// provided session token, this is useful for running the Expire() or IsExpired() operations
|
||||
func getConsoleCredentialsFromSession(claims *models.Principal) *credentials.Credentials {
|
||||
return credentials.NewStaticV4(claims.AccessKeyID, claims.SecretAccessKey, claims.SessionToken)
|
||||
}
|
||||
|
||||
// newMinioClient creates a new MinIO client based on the consoleCredentials extracted
|
||||
// from the provided jwt
|
||||
// from the provided session token
|
||||
func newMinioClient(claims *models.Principal) (*minio.Client, error) {
|
||||
creds := getConsoleCredentialsFromSession(claims)
|
||||
stsClient := PrepareSTSClient(false)
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/auth"
|
||||
|
||||
@@ -61,7 +63,7 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
// Applies when the "x-token" header is set
|
||||
|
||||
api.KeyAuth = func(token string, scopes []string) (*models.Principal, error) {
|
||||
// we are validating the jwt by decrypting the claims inside, if the operation succed that means the jwt
|
||||
// we are validating the session token by decrypting the claims inside, if the operation succeed that means the jwt
|
||||
// was generated and signed by us in the first place
|
||||
claims, err := auth.SessionTokenAuthenticate(token)
|
||||
if err != nil {
|
||||
@@ -229,8 +231,9 @@ func wrapHandlerSinglePageApplication(h http.Handler) http.HandlerFunc {
|
||||
nfrw := ¬FoundRedirectRespWr{ResponseWriter: w}
|
||||
h.ServeHTTP(nfrw, r)
|
||||
if nfrw.status == 404 {
|
||||
log.Printf("Redirecting %s to index.html.", r.RequestURI)
|
||||
http.Redirect(w, r, "/index.html", http.StatusFound)
|
||||
indexPage, _ := portalUI.Asset("build/index.html")
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader(indexPage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
// - multipart/form-data
|
||||
//
|
||||
// Produces:
|
||||
// - application/octet-stream
|
||||
|
||||
@@ -469,6 +469,144 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/objects/legalhold": {
|
||||
"put": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Put Object's legalhold status",
|
||||
"operationId": "PutObjectLegalHold",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "bucket_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "prefix",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "version_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/putObjectLegalHoldRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response."
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/objects/share": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Shares an Object on a url",
|
||||
"operationId": "ShareObject",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "bucket_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "prefix",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "version_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "expires",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/objects/upload": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Uploads an Object.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "file",
|
||||
"name": "upfile",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "bucket_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "prefix",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response."
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/replication": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -3605,6 +3743,13 @@ func init() {
|
||||
"get"
|
||||
]
|
||||
},
|
||||
"objectLegalHoldStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
]
|
||||
},
|
||||
"parityResponse": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -3735,6 +3880,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"putObjectLegalHoldRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"status": {
|
||||
"$ref": "#/definitions/objectLegalHoldStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"remoteBucket": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -4920,6 +5076,144 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/objects/legalhold": {
|
||||
"put": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Put Object's legalhold status",
|
||||
"operationId": "PutObjectLegalHold",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "bucket_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "prefix",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "version_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/putObjectLegalHoldRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response."
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/objects/share": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Shares an Object on a url",
|
||||
"operationId": "ShareObject",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "bucket_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "prefix",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "version_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "expires",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/objects/upload": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"multipart/form-data"
|
||||
],
|
||||
"tags": [
|
||||
"UserAPI"
|
||||
],
|
||||
"summary": "Uploads an Object.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "file",
|
||||
"name": "upfile",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "bucket_name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "prefix",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response."
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/buckets/{bucket_name}/replication": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -8535,6 +8829,13 @@ func init() {
|
||||
"get"
|
||||
]
|
||||
},
|
||||
"objectLegalHoldStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
]
|
||||
},
|
||||
"parityResponse": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -8643,6 +8944,17 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"putObjectLegalHoldRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"status"
|
||||
],
|
||||
"properties": {
|
||||
"status": {
|
||||
"$ref": "#/definitions/objectLegalHoldStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"remoteBucket": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -87,6 +87,10 @@ func prepareError(err ...error) *models.Error {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err[0]).Code == "InvalidAccessKeyId" {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// console invalid session error
|
||||
if madmin.ToErrorResponse(err[0]).Code == "XMinioAdminNoSuchUser" {
|
||||
errorCode = 401
|
||||
|
||||
@@ -58,7 +58,8 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
APIKeyAuthenticator: security.APIKeyAuth,
|
||||
BearerAuthenticator: security.BearerAuth,
|
||||
|
||||
JSONConsumer: runtime.JSONConsumer(),
|
||||
JSONConsumer: runtime.JSONConsumer(),
|
||||
MultipartformConsumer: runtime.DiscardConsumer,
|
||||
|
||||
BinProducer: runtime.ByteStreamProducer(),
|
||||
JSONProducer: runtime.JSONProducer(),
|
||||
@@ -213,12 +214,18 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
AdminAPIPolicyInfoHandler: admin_api.PolicyInfoHandlerFunc(func(params admin_api.PolicyInfoParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.PolicyInfo has not yet been implemented")
|
||||
}),
|
||||
UserAPIPostBucketsBucketNameObjectsUploadHandler: user_api.PostBucketsBucketNameObjectsUploadHandlerFunc(func(params user_api.PostBucketsBucketNameObjectsUploadParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.PostBucketsBucketNameObjectsUpload has not yet been implemented")
|
||||
}),
|
||||
AdminAPIProfilingStartHandler: admin_api.ProfilingStartHandlerFunc(func(params admin_api.ProfilingStartParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.ProfilingStart has not yet been implemented")
|
||||
}),
|
||||
AdminAPIProfilingStopHandler: admin_api.ProfilingStopHandlerFunc(func(params admin_api.ProfilingStopParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.ProfilingStop has not yet been implemented")
|
||||
}),
|
||||
UserAPIPutObjectLegalHoldHandler: user_api.PutObjectLegalHoldHandlerFunc(func(params user_api.PutObjectLegalHoldParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.PutObjectLegalHold has not yet been implemented")
|
||||
}),
|
||||
UserAPIRemoteBucketDetailsHandler: user_api.RemoteBucketDetailsHandlerFunc(func(params user_api.RemoteBucketDetailsParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.RemoteBucketDetails has not yet been implemented")
|
||||
}),
|
||||
@@ -249,6 +256,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
AdminAPISetPolicyHandler: admin_api.SetPolicyHandlerFunc(func(params admin_api.SetPolicyParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.SetPolicy has not yet been implemented")
|
||||
}),
|
||||
UserAPIShareObjectHandler: user_api.ShareObjectHandlerFunc(func(params user_api.ShareObjectParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation user_api.ShareObject has not yet been implemented")
|
||||
}),
|
||||
AdminAPITenantAddZoneHandler: admin_api.TenantAddZoneHandlerFunc(func(params admin_api.TenantAddZoneParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.TenantAddZone has not yet been implemented")
|
||||
}),
|
||||
@@ -310,6 +320,9 @@ type ConsoleAPI struct {
|
||||
// JSONConsumer registers a consumer for the following mime types:
|
||||
// - application/json
|
||||
JSONConsumer runtime.Consumer
|
||||
// MultipartformConsumer registers a consumer for the following mime types:
|
||||
// - multipart/form-data
|
||||
MultipartformConsumer runtime.Consumer
|
||||
|
||||
// BinProducer registers a producer for the following mime types:
|
||||
// - application/octet-stream
|
||||
@@ -425,10 +438,14 @@ type ConsoleAPI struct {
|
||||
AdminAPINotificationEndpointListHandler admin_api.NotificationEndpointListHandler
|
||||
// AdminAPIPolicyInfoHandler sets the operation handler for the policy info operation
|
||||
AdminAPIPolicyInfoHandler admin_api.PolicyInfoHandler
|
||||
// UserAPIPostBucketsBucketNameObjectsUploadHandler sets the operation handler for the post buckets bucket name objects upload operation
|
||||
UserAPIPostBucketsBucketNameObjectsUploadHandler user_api.PostBucketsBucketNameObjectsUploadHandler
|
||||
// AdminAPIProfilingStartHandler sets the operation handler for the profiling start operation
|
||||
AdminAPIProfilingStartHandler admin_api.ProfilingStartHandler
|
||||
// AdminAPIProfilingStopHandler sets the operation handler for the profiling stop operation
|
||||
AdminAPIProfilingStopHandler admin_api.ProfilingStopHandler
|
||||
// UserAPIPutObjectLegalHoldHandler sets the operation handler for the put object legal hold operation
|
||||
UserAPIPutObjectLegalHoldHandler user_api.PutObjectLegalHoldHandler
|
||||
// UserAPIRemoteBucketDetailsHandler sets the operation handler for the remote bucket details operation
|
||||
UserAPIRemoteBucketDetailsHandler user_api.RemoteBucketDetailsHandler
|
||||
// AdminAPIRemoveGroupHandler sets the operation handler for the remove group operation
|
||||
@@ -449,6 +466,8 @@ type ConsoleAPI struct {
|
||||
AdminAPISetConfigHandler admin_api.SetConfigHandler
|
||||
// AdminAPISetPolicyHandler sets the operation handler for the set policy operation
|
||||
AdminAPISetPolicyHandler admin_api.SetPolicyHandler
|
||||
// UserAPIShareObjectHandler sets the operation handler for the share object operation
|
||||
UserAPIShareObjectHandler user_api.ShareObjectHandler
|
||||
// AdminAPITenantAddZoneHandler sets the operation handler for the tenant add zone operation
|
||||
AdminAPITenantAddZoneHandler admin_api.TenantAddZoneHandler
|
||||
// AdminAPITenantInfoHandler sets the operation handler for the tenant info operation
|
||||
@@ -528,6 +547,9 @@ func (o *ConsoleAPI) Validate() error {
|
||||
if o.JSONConsumer == nil {
|
||||
unregistered = append(unregistered, "JSONConsumer")
|
||||
}
|
||||
if o.MultipartformConsumer == nil {
|
||||
unregistered = append(unregistered, "MultipartformConsumer")
|
||||
}
|
||||
|
||||
if o.BinProducer == nil {
|
||||
unregistered = append(unregistered, "BinProducer")
|
||||
@@ -690,12 +712,18 @@ func (o *ConsoleAPI) Validate() error {
|
||||
if o.AdminAPIPolicyInfoHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.PolicyInfoHandler")
|
||||
}
|
||||
if o.UserAPIPostBucketsBucketNameObjectsUploadHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.PostBucketsBucketNameObjectsUploadHandler")
|
||||
}
|
||||
if o.AdminAPIProfilingStartHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.ProfilingStartHandler")
|
||||
}
|
||||
if o.AdminAPIProfilingStopHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.ProfilingStopHandler")
|
||||
}
|
||||
if o.UserAPIPutObjectLegalHoldHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.PutObjectLegalHoldHandler")
|
||||
}
|
||||
if o.UserAPIRemoteBucketDetailsHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.RemoteBucketDetailsHandler")
|
||||
}
|
||||
@@ -726,6 +754,9 @@ func (o *ConsoleAPI) Validate() error {
|
||||
if o.AdminAPISetPolicyHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.SetPolicyHandler")
|
||||
}
|
||||
if o.UserAPIShareObjectHandler == nil {
|
||||
unregistered = append(unregistered, "user_api.ShareObjectHandler")
|
||||
}
|
||||
if o.AdminAPITenantAddZoneHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.TenantAddZoneHandler")
|
||||
}
|
||||
@@ -794,6 +825,8 @@ func (o *ConsoleAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consum
|
||||
switch mt {
|
||||
case "application/json":
|
||||
result["application/json"] = o.JSONConsumer
|
||||
case "multipart/form-data":
|
||||
result["multipart/form-data"] = o.MultipartformConsumer
|
||||
}
|
||||
|
||||
if c, ok := o.customConsumers[mt]; ok {
|
||||
@@ -1056,11 +1089,19 @@ func (o *ConsoleAPI) initHandlerCache() {
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/buckets/{bucket_name}/objects/upload"] = user_api.NewPostBucketsBucketNameObjectsUpload(o.context, o.UserAPIPostBucketsBucketNameObjectsUploadHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/profiling/start"] = admin_api.NewProfilingStart(o.context, o.AdminAPIProfilingStartHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["POST"]["/profiling/stop"] = admin_api.NewProfilingStop(o.context, o.AdminAPIProfilingStopHandler)
|
||||
if o.handlers["PUT"] == nil {
|
||||
o.handlers["PUT"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["PUT"]["/buckets/{bucket_name}/objects/legalhold"] = user_api.NewPutObjectLegalHold(o.context, o.UserAPIPutObjectLegalHoldHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
@@ -1101,6 +1142,10 @@ func (o *ConsoleAPI) initHandlerCache() {
|
||||
o.handlers["PUT"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["PUT"]["/set-policy/{name}"] = admin_api.NewSetPolicy(o.context, o.AdminAPISetPolicyHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/buckets/{bucket_name}/objects/share"] = user_api.NewShareObject(o.context, o.UserAPIShareObjectHandler)
|
||||
if o.handlers["POST"] == nil {
|
||||
o.handlers["POST"] = make(map[string]http.Handler)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// PostBucketsBucketNameObjectsUploadHandlerFunc turns a function with the right signature into a post buckets bucket name objects upload handler
|
||||
type PostBucketsBucketNameObjectsUploadHandlerFunc func(PostBucketsBucketNameObjectsUploadParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn PostBucketsBucketNameObjectsUploadHandlerFunc) Handle(params PostBucketsBucketNameObjectsUploadParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// PostBucketsBucketNameObjectsUploadHandler interface for that can handle valid post buckets bucket name objects upload params
|
||||
type PostBucketsBucketNameObjectsUploadHandler interface {
|
||||
Handle(PostBucketsBucketNameObjectsUploadParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewPostBucketsBucketNameObjectsUpload creates a new http.Handler for the post buckets bucket name objects upload operation
|
||||
func NewPostBucketsBucketNameObjectsUpload(ctx *middleware.Context, handler PostBucketsBucketNameObjectsUploadHandler) *PostBucketsBucketNameObjectsUpload {
|
||||
return &PostBucketsBucketNameObjectsUpload{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*PostBucketsBucketNameObjectsUpload swagger:route POST /buckets/{bucket_name}/objects/upload UserAPI postBucketsBucketNameObjectsUpload
|
||||
|
||||
Uploads an Object.
|
||||
|
||||
*/
|
||||
type PostBucketsBucketNameObjectsUpload struct {
|
||||
Context *middleware.Context
|
||||
Handler PostBucketsBucketNameObjectsUploadHandler
|
||||
}
|
||||
|
||||
func (o *PostBucketsBucketNameObjectsUpload) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewPostBucketsBucketNameObjectsUploadParams()
|
||||
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
r = aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// NewPostBucketsBucketNameObjectsUploadParams creates a new PostBucketsBucketNameObjectsUploadParams object
|
||||
// no default values defined in spec.
|
||||
func NewPostBucketsBucketNameObjectsUploadParams() PostBucketsBucketNameObjectsUploadParams {
|
||||
|
||||
return PostBucketsBucketNameObjectsUploadParams{}
|
||||
}
|
||||
|
||||
// PostBucketsBucketNameObjectsUploadParams contains all the bound params for the post buckets bucket name objects upload operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters PostBucketsBucketNameObjectsUpload
|
||||
type PostBucketsBucketNameObjectsUploadParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
*/
|
||||
Prefix string
|
||||
/*
|
||||
Required: true
|
||||
In: formData
|
||||
*/
|
||||
Upfile io.ReadCloser
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewPostBucketsBucketNameObjectsUploadParams() beforehand.
|
||||
func (o *PostBucketsBucketNameObjectsUploadParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
qs := runtime.Values(r.URL.Query())
|
||||
|
||||
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
||||
if err != http.ErrNotMultipart {
|
||||
return errors.New(400, "%v", err)
|
||||
} else if err := r.ParseForm(); err != nil {
|
||||
return errors.New(400, "%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
|
||||
if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qPrefix, qhkPrefix, _ := qs.GetOK("prefix")
|
||||
if err := o.bindPrefix(qPrefix, qhkPrefix, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
upfile, upfileHeader, err := r.FormFile("upfile")
|
||||
if err != nil {
|
||||
res = append(res, errors.New(400, "reading file %q failed: %v", "upfile", err))
|
||||
} else if err := o.bindUpfile(upfile, upfileHeader); err != nil {
|
||||
// Required: true
|
||||
res = append(res, err)
|
||||
} else {
|
||||
o.Upfile = &runtime.File{Data: upfile, Header: upfileHeader}
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindBucketName binds and validates parameter BucketName from path.
|
||||
func (o *PostBucketsBucketNameObjectsUploadParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
|
||||
o.BucketName = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPrefix binds and validates parameter Prefix from query.
|
||||
func (o *PostBucketsBucketNameObjectsUploadParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
return errors.Required("prefix", "query", rawData)
|
||||
}
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// AllowEmptyValue: false
|
||||
if err := validate.RequiredString("prefix", "query", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Prefix = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindUpfile binds file parameter Upfile.
|
||||
//
|
||||
// The only supported validations on files are MinLength and MaxLength
|
||||
func (o *PostBucketsBucketNameObjectsUploadParams) bindUpfile(file multipart.File, header *multipart.FileHeader) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// PostBucketsBucketNameObjectsUploadOKCode is the HTTP code returned for type PostBucketsBucketNameObjectsUploadOK
|
||||
const PostBucketsBucketNameObjectsUploadOKCode int = 200
|
||||
|
||||
/*PostBucketsBucketNameObjectsUploadOK A successful response.
|
||||
|
||||
swagger:response postBucketsBucketNameObjectsUploadOK
|
||||
*/
|
||||
type PostBucketsBucketNameObjectsUploadOK struct {
|
||||
}
|
||||
|
||||
// NewPostBucketsBucketNameObjectsUploadOK creates PostBucketsBucketNameObjectsUploadOK with default headers values
|
||||
func NewPostBucketsBucketNameObjectsUploadOK() *PostBucketsBucketNameObjectsUploadOK {
|
||||
|
||||
return &PostBucketsBucketNameObjectsUploadOK{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *PostBucketsBucketNameObjectsUploadOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(200)
|
||||
}
|
||||
|
||||
/*PostBucketsBucketNameObjectsUploadDefault Generic error response.
|
||||
|
||||
swagger:response postBucketsBucketNameObjectsUploadDefault
|
||||
*/
|
||||
type PostBucketsBucketNameObjectsUploadDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewPostBucketsBucketNameObjectsUploadDefault creates PostBucketsBucketNameObjectsUploadDefault with default headers values
|
||||
func NewPostBucketsBucketNameObjectsUploadDefault(code int) *PostBucketsBucketNameObjectsUploadDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &PostBucketsBucketNameObjectsUploadDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the post buckets bucket name objects upload default response
|
||||
func (o *PostBucketsBucketNameObjectsUploadDefault) WithStatusCode(code int) *PostBucketsBucketNameObjectsUploadDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the post buckets bucket name objects upload default response
|
||||
func (o *PostBucketsBucketNameObjectsUploadDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the post buckets bucket name objects upload default response
|
||||
func (o *PostBucketsBucketNameObjectsUploadDefault) WithPayload(payload *models.Error) *PostBucketsBucketNameObjectsUploadDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the post buckets bucket name objects upload default response
|
||||
func (o *PostBucketsBucketNameObjectsUploadDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *PostBucketsBucketNameObjectsUploadDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PostBucketsBucketNameObjectsUploadURL generates an URL for the post buckets bucket name objects upload operation
|
||||
type PostBucketsBucketNameObjectsUploadURL struct {
|
||||
BucketName string
|
||||
|
||||
Prefix string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *PostBucketsBucketNameObjectsUploadURL) WithBasePath(bp string) *PostBucketsBucketNameObjectsUploadURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *PostBucketsBucketNameObjectsUploadURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *PostBucketsBucketNameObjectsUploadURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/buckets/{bucket_name}/objects/upload"
|
||||
|
||||
bucketName := o.BucketName
|
||||
if bucketName != "" {
|
||||
_path = strings.Replace(_path, "{bucket_name}", bucketName, -1)
|
||||
} else {
|
||||
return nil, errors.New("bucketName is required on PostBucketsBucketNameObjectsUploadURL")
|
||||
}
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
prefixQ := o.Prefix
|
||||
if prefixQ != "" {
|
||||
qs.Set("prefix", prefixQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *PostBucketsBucketNameObjectsUploadURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *PostBucketsBucketNameObjectsUploadURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *PostBucketsBucketNameObjectsUploadURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on PostBucketsBucketNameObjectsUploadURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on PostBucketsBucketNameObjectsUploadURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *PostBucketsBucketNameObjectsUploadURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
90
restapi/operations/user_api/put_object_legal_hold.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// PutObjectLegalHoldHandlerFunc turns a function with the right signature into a put object legal hold handler
|
||||
type PutObjectLegalHoldHandlerFunc func(PutObjectLegalHoldParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn PutObjectLegalHoldHandlerFunc) Handle(params PutObjectLegalHoldParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// PutObjectLegalHoldHandler interface for that can handle valid put object legal hold params
|
||||
type PutObjectLegalHoldHandler interface {
|
||||
Handle(PutObjectLegalHoldParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewPutObjectLegalHold creates a new http.Handler for the put object legal hold operation
|
||||
func NewPutObjectLegalHold(ctx *middleware.Context, handler PutObjectLegalHoldHandler) *PutObjectLegalHold {
|
||||
return &PutObjectLegalHold{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*PutObjectLegalHold swagger:route PUT /buckets/{bucket_name}/objects/legalhold UserAPI putObjectLegalHold
|
||||
|
||||
Put Object's legalhold status
|
||||
|
||||
*/
|
||||
type PutObjectLegalHold struct {
|
||||
Context *middleware.Context
|
||||
Handler PutObjectLegalHoldHandler
|
||||
}
|
||||
|
||||
func (o *PutObjectLegalHold) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewPutObjectLegalHoldParams()
|
||||
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
r = aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
185
restapi/operations/user_api/put_object_legal_hold_parameters.go
Normal file
@@ -0,0 +1,185 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// NewPutObjectLegalHoldParams creates a new PutObjectLegalHoldParams object
|
||||
// no default values defined in spec.
|
||||
func NewPutObjectLegalHoldParams() PutObjectLegalHoldParams {
|
||||
|
||||
return PutObjectLegalHoldParams{}
|
||||
}
|
||||
|
||||
// PutObjectLegalHoldParams contains all the bound params for the put object legal hold operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters PutObjectLegalHold
|
||||
type PutObjectLegalHoldParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: body
|
||||
*/
|
||||
Body *models.PutObjectLegalHoldRequest
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
*/
|
||||
Prefix string
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
*/
|
||||
VersionID string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewPutObjectLegalHoldParams() beforehand.
|
||||
func (o *PutObjectLegalHoldParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
qs := runtime.Values(r.URL.Query())
|
||||
|
||||
if runtime.HasBody(r) {
|
||||
defer r.Body.Close()
|
||||
var body models.PutObjectLegalHoldRequest
|
||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||
if err == io.EOF {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
} else {
|
||||
res = append(res, errors.NewParseError("body", "body", "", err))
|
||||
}
|
||||
} else {
|
||||
// validate body object
|
||||
if err := body.Validate(route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
o.Body = &body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = append(res, errors.Required("body", "body", ""))
|
||||
}
|
||||
rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
|
||||
if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qPrefix, qhkPrefix, _ := qs.GetOK("prefix")
|
||||
if err := o.bindPrefix(qPrefix, qhkPrefix, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qVersionID, qhkVersionID, _ := qs.GetOK("version_id")
|
||||
if err := o.bindVersionID(qVersionID, qhkVersionID, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindBucketName binds and validates parameter BucketName from path.
|
||||
func (o *PutObjectLegalHoldParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
|
||||
o.BucketName = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPrefix binds and validates parameter Prefix from query.
|
||||
func (o *PutObjectLegalHoldParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
return errors.Required("prefix", "query", rawData)
|
||||
}
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// AllowEmptyValue: false
|
||||
if err := validate.RequiredString("prefix", "query", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Prefix = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindVersionID binds and validates parameter VersionID from query.
|
||||
func (o *PutObjectLegalHoldParams) bindVersionID(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
return errors.Required("version_id", "query", rawData)
|
||||
}
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// AllowEmptyValue: false
|
||||
if err := validate.RequiredString("version_id", "query", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.VersionID = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
113
restapi/operations/user_api/put_object_legal_hold_responses.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// PutObjectLegalHoldOKCode is the HTTP code returned for type PutObjectLegalHoldOK
|
||||
const PutObjectLegalHoldOKCode int = 200
|
||||
|
||||
/*PutObjectLegalHoldOK A successful response.
|
||||
|
||||
swagger:response putObjectLegalHoldOK
|
||||
*/
|
||||
type PutObjectLegalHoldOK struct {
|
||||
}
|
||||
|
||||
// NewPutObjectLegalHoldOK creates PutObjectLegalHoldOK with default headers values
|
||||
func NewPutObjectLegalHoldOK() *PutObjectLegalHoldOK {
|
||||
|
||||
return &PutObjectLegalHoldOK{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *PutObjectLegalHoldOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(200)
|
||||
}
|
||||
|
||||
/*PutObjectLegalHoldDefault Generic error response.
|
||||
|
||||
swagger:response putObjectLegalHoldDefault
|
||||
*/
|
||||
type PutObjectLegalHoldDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewPutObjectLegalHoldDefault creates PutObjectLegalHoldDefault with default headers values
|
||||
func NewPutObjectLegalHoldDefault(code int) *PutObjectLegalHoldDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &PutObjectLegalHoldDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the put object legal hold default response
|
||||
func (o *PutObjectLegalHoldDefault) WithStatusCode(code int) *PutObjectLegalHoldDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the put object legal hold default response
|
||||
func (o *PutObjectLegalHoldDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the put object legal hold default response
|
||||
func (o *PutObjectLegalHoldDefault) WithPayload(payload *models.Error) *PutObjectLegalHoldDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the put object legal hold default response
|
||||
func (o *PutObjectLegalHoldDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *PutObjectLegalHoldDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
133
restapi/operations/user_api/put_object_legal_hold_urlbuilder.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PutObjectLegalHoldURL generates an URL for the put object legal hold operation
|
||||
type PutObjectLegalHoldURL struct {
|
||||
BucketName string
|
||||
|
||||
Prefix string
|
||||
VersionID string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *PutObjectLegalHoldURL) WithBasePath(bp string) *PutObjectLegalHoldURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *PutObjectLegalHoldURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *PutObjectLegalHoldURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/buckets/{bucket_name}/objects/legalhold"
|
||||
|
||||
bucketName := o.BucketName
|
||||
if bucketName != "" {
|
||||
_path = strings.Replace(_path, "{bucket_name}", bucketName, -1)
|
||||
} else {
|
||||
return nil, errors.New("bucketName is required on PutObjectLegalHoldURL")
|
||||
}
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
prefixQ := o.Prefix
|
||||
if prefixQ != "" {
|
||||
qs.Set("prefix", prefixQ)
|
||||
}
|
||||
|
||||
versionIDQ := o.VersionID
|
||||
if versionIDQ != "" {
|
||||
qs.Set("version_id", versionIDQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *PutObjectLegalHoldURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *PutObjectLegalHoldURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *PutObjectLegalHoldURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on PutObjectLegalHoldURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on PutObjectLegalHoldURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *PutObjectLegalHoldURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
90
restapi/operations/user_api/share_object.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// ShareObjectHandlerFunc turns a function with the right signature into a share object handler
|
||||
type ShareObjectHandlerFunc func(ShareObjectParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn ShareObjectHandlerFunc) Handle(params ShareObjectParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// ShareObjectHandler interface for that can handle valid share object params
|
||||
type ShareObjectHandler interface {
|
||||
Handle(ShareObjectParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewShareObject creates a new http.Handler for the share object operation
|
||||
func NewShareObject(ctx *middleware.Context, handler ShareObjectHandler) *ShareObject {
|
||||
return &ShareObject{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*ShareObject swagger:route GET /buckets/{bucket_name}/objects/share UserAPI shareObject
|
||||
|
||||
Shares an Object on a url
|
||||
|
||||
*/
|
||||
type ShareObject struct {
|
||||
Context *middleware.Context
|
||||
Handler ShareObjectHandler
|
||||
}
|
||||
|
||||
func (o *ShareObject) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewShareObjectParams()
|
||||
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
r = aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
182
restapi/operations/user_api/share_object_parameters.go
Normal file
@@ -0,0 +1,182 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// NewShareObjectParams creates a new ShareObjectParams object
|
||||
// no default values defined in spec.
|
||||
func NewShareObjectParams() ShareObjectParams {
|
||||
|
||||
return ShareObjectParams{}
|
||||
}
|
||||
|
||||
// ShareObjectParams contains all the bound params for the share object operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters ShareObject
|
||||
type ShareObjectParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
BucketName string
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
Expires *string
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
*/
|
||||
Prefix string
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
*/
|
||||
VersionID string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewShareObjectParams() beforehand.
|
||||
func (o *ShareObjectParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
qs := runtime.Values(r.URL.Query())
|
||||
|
||||
rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name")
|
||||
if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qExpires, qhkExpires, _ := qs.GetOK("expires")
|
||||
if err := o.bindExpires(qExpires, qhkExpires, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qPrefix, qhkPrefix, _ := qs.GetOK("prefix")
|
||||
if err := o.bindPrefix(qPrefix, qhkPrefix, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qVersionID, qhkVersionID, _ := qs.GetOK("version_id")
|
||||
if err := o.bindVersionID(qVersionID, qhkVersionID, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindBucketName binds and validates parameter BucketName from path.
|
||||
func (o *ShareObjectParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
|
||||
o.BucketName = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindExpires binds and validates parameter Expires from query.
|
||||
func (o *ShareObjectParams) bindExpires(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
|
||||
o.Expires = &raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPrefix binds and validates parameter Prefix from query.
|
||||
func (o *ShareObjectParams) bindPrefix(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
return errors.Required("prefix", "query", rawData)
|
||||
}
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// AllowEmptyValue: false
|
||||
if err := validate.RequiredString("prefix", "query", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.Prefix = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindVersionID binds and validates parameter VersionID from query.
|
||||
func (o *ShareObjectParams) bindVersionID(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
return errors.Required("version_id", "query", rawData)
|
||||
}
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// AllowEmptyValue: false
|
||||
if err := validate.RequiredString("version_id", "query", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.VersionID = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
131
restapi/operations/user_api/share_object_responses.go
Normal file
@@ -0,0 +1,131 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// ShareObjectOKCode is the HTTP code returned for type ShareObjectOK
|
||||
const ShareObjectOKCode int = 200
|
||||
|
||||
/*ShareObjectOK A successful response.
|
||||
|
||||
swagger:response shareObjectOK
|
||||
*/
|
||||
type ShareObjectOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload string `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewShareObjectOK creates ShareObjectOK with default headers values
|
||||
func NewShareObjectOK() *ShareObjectOK {
|
||||
|
||||
return &ShareObjectOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the share object o k response
|
||||
func (o *ShareObjectOK) WithPayload(payload string) *ShareObjectOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the share object o k response
|
||||
func (o *ShareObjectOK) SetPayload(payload string) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *ShareObjectOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
|
||||
/*ShareObjectDefault Generic error response.
|
||||
|
||||
swagger:response shareObjectDefault
|
||||
*/
|
||||
type ShareObjectDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewShareObjectDefault creates ShareObjectDefault with default headers values
|
||||
func NewShareObjectDefault(code int) *ShareObjectDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &ShareObjectDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the share object default response
|
||||
func (o *ShareObjectDefault) WithStatusCode(code int) *ShareObjectDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the share object default response
|
||||
func (o *ShareObjectDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the share object default response
|
||||
func (o *ShareObjectDefault) WithPayload(payload *models.Error) *ShareObjectDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the share object default response
|
||||
func (o *ShareObjectDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *ShareObjectDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
142
restapi/operations/user_api/share_object_urlbuilder.go
Normal file
@@ -0,0 +1,142 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package user_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ShareObjectURL generates an URL for the share object operation
|
||||
type ShareObjectURL struct {
|
||||
BucketName string
|
||||
|
||||
Expires *string
|
||||
Prefix string
|
||||
VersionID string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *ShareObjectURL) WithBasePath(bp string) *ShareObjectURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *ShareObjectURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *ShareObjectURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/buckets/{bucket_name}/objects/share"
|
||||
|
||||
bucketName := o.BucketName
|
||||
if bucketName != "" {
|
||||
_path = strings.Replace(_path, "{bucket_name}", bucketName, -1)
|
||||
} else {
|
||||
return nil, errors.New("bucketName is required on ShareObjectURL")
|
||||
}
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
var expiresQ string
|
||||
if o.Expires != nil {
|
||||
expiresQ = *o.Expires
|
||||
}
|
||||
if expiresQ != "" {
|
||||
qs.Set("expires", expiresQ)
|
||||
}
|
||||
|
||||
prefixQ := o.Prefix
|
||||
if prefixQ != "" {
|
||||
qs.Set("prefix", prefixQ)
|
||||
}
|
||||
|
||||
versionIDQ := o.VersionID
|
||||
if versionIDQ != "" {
|
||||
qs.Set("version_id", versionIDQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *ShareObjectURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *ShareObjectURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *ShareObjectURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on ShareObjectURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on ShareObjectURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *ShareObjectURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -72,12 +72,12 @@ func login(credentials ConsoleCredentials, actions []string) (*string, error) {
|
||||
return nil, err
|
||||
}
|
||||
// if we made it here, the consoleCredentials work, generate a jwt with claims
|
||||
jwt, err := auth.NewEncryptedTokenForClient(&tokens, actions)
|
||||
token, err := auth.NewEncryptedTokenForClient(&tokens, actions)
|
||||
if err != nil {
|
||||
log.Println("error authenticating user", err)
|
||||
return nil, errInvalidCredentials
|
||||
}
|
||||
return &jwt, nil
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
func getConfiguredRegionForLogin(client MinioAdmin) (string, error) {
|
||||
@@ -224,19 +224,19 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
actions := acl.GetActionsStringFromPolicy(policy)
|
||||
// User was created correctly, create a new session/JWT
|
||||
// User was created correctly, create a new session
|
||||
creds, err := newConsoleCredentials(accessKey, secretKey, location)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
credentials := consoleCredentials{consoleCredentials: creds}
|
||||
jwt, err := login(credentials, actions)
|
||||
token, err := login(credentials, actions)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
SessionID: *jwt,
|
||||
SessionID: *token,
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
@@ -251,13 +251,13 @@ func getLoginOperatorResponse(lmr *models.LoginOperatorRequest) (*models.LoginRe
|
||||
}
|
||||
credentials := consoleCredentials{consoleCredentials: creds}
|
||||
var actions []string
|
||||
jwt, err := login(credentials, actions)
|
||||
token, err := login(credentials, actions)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
SessionID: *jwt,
|
||||
SessionID: *token,
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ func TestLogin(t *testing.T) {
|
||||
SignerType: 0,
|
||||
}, nil
|
||||
}
|
||||
jwt, err := login(consoleCredentials, []string{""})
|
||||
funcAssert.NotEmpty(jwt, "JWT was returned empty")
|
||||
token, err := login(consoleCredentials, []string{""})
|
||||
funcAssert.NotEmpty(token, "Token was returned empty")
|
||||
funcAssert.Nil(err, "error creating a session")
|
||||
|
||||
// Test Case 2: Invalid credentials
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
@@ -75,6 +77,28 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) {
|
||||
}
|
||||
})
|
||||
})
|
||||
// upload object
|
||||
api.UserAPIPostBucketsBucketNameObjectsUploadHandler = user_api.PostBucketsBucketNameObjectsUploadHandlerFunc(func(params user_api.PostBucketsBucketNameObjectsUploadParams, session *models.Principal) middleware.Responder {
|
||||
if err := getUploadObjectResponse(session, params); err != nil {
|
||||
return user_api.NewPostBucketsBucketNameObjectsUploadDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewPostBucketsBucketNameObjectsUploadOK()
|
||||
})
|
||||
// get share object url
|
||||
api.UserAPIShareObjectHandler = user_api.ShareObjectHandlerFunc(func(params user_api.ShareObjectParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getShareObjectResponse(session, params)
|
||||
if err != nil {
|
||||
return user_api.NewShareObjectDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewShareObjectOK().WithPayload(*resp)
|
||||
})
|
||||
// set object legalhold status
|
||||
api.UserAPIPutObjectLegalHoldHandler = user_api.PutObjectLegalHoldHandlerFunc(func(params user_api.PutObjectLegalHoldParams, session *models.Principal) middleware.Responder {
|
||||
if err := getSetObjectLegalHoldResponse(session, params); err != nil {
|
||||
return user_api.NewPutObjectLegalHoldDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewPutObjectLegalHoldOK()
|
||||
})
|
||||
}
|
||||
|
||||
// getListObjectsResponse returns a list of objects
|
||||
@@ -139,10 +163,9 @@ func listBucketObjects(ctx context.Context, client MinioClient, bucketName strin
|
||||
legalHoldStatus, err := client.getObjectLegalHold(ctx, bucketName, lsObj.Key, minio.GetObjectLegalHoldOptions{VersionID: lsObj.VersionID})
|
||||
if err != nil {
|
||||
errResp := minio.ToErrorResponse(probe.NewError(err).ToGoError())
|
||||
if errResp.Code != "NoSuchObjectLockConfiguration" {
|
||||
if errResp.Code != "InvalidRequest" && errResp.Code != "NoSuchObjectLockConfiguration" {
|
||||
log.Printf("error getting legal hold status for %s : %s", lsObj.VersionID, err)
|
||||
}
|
||||
|
||||
} else {
|
||||
if legalHoldStatus != nil {
|
||||
obj.LegalHoldStatus = string(*legalHoldStatus)
|
||||
@@ -246,7 +269,6 @@ func deleteMultipleObjects(ctx context.Context, client MCClient, recursive bool)
|
||||
contentCh := make(chan *mc.ClientContent, 1)
|
||||
|
||||
errorCh := client.remove(ctx, isIncomplete, isRemoveBucket, isBypass, contentCh)
|
||||
OUTER_LOOP:
|
||||
for content := range client.list(ctx, listOpts) {
|
||||
if content.Err != nil {
|
||||
switch content.Err.ToGoError().(type) {
|
||||
@@ -270,7 +292,7 @@ OUTER_LOOP:
|
||||
// Ignore Permission error.
|
||||
continue
|
||||
}
|
||||
break OUTER_LOOP
|
||||
return pErr.Cause
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,6 +337,108 @@ func deleteSingleObject(ctx context.Context, client MCClient, bucket, object str
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUploadObjectResponse(session *models.Principal, params user_api.PostBucketsBucketNameObjectsUploadParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
// get size from request form
|
||||
var objectSize int64
|
||||
if params.HTTPRequest.MultipartForm == nil {
|
||||
return prepareError(errors.New("request MultipartForm is nil"))
|
||||
}
|
||||
if file, ok := params.HTTPRequest.MultipartForm.File["upfile"]; ok {
|
||||
if len(file) > 0 {
|
||||
objectSize = file[0].Size
|
||||
} else {
|
||||
return prepareError(errors.New("file not present in request"))
|
||||
}
|
||||
} else {
|
||||
return prepareError(errors.New("`upfile` should be on MultipartForm.File map"))
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
if err := uploadObject(ctx, minioClient, params.BucketName, params.Prefix, objectSize, params.Upfile); err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func uploadObject(ctx context.Context, client MinioClient, bucketName, prefix string, objectSize int64, object io.ReadCloser) error {
|
||||
_, err := client.putObject(ctx, bucketName, prefix, object, objectSize, minio.PutObjectOptions{ContentType: "application/octet-stream"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getShareObjectResponse returns a share object url
|
||||
func getShareObjectResponse(session *models.Principal, params user_api.ShareObjectParams) (*string, *models.Error) {
|
||||
ctx := context.Background()
|
||||
s3Client, err := newS3BucketClient(session, params.BucketName, params.Prefix)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
var expireDuration string
|
||||
if params.Expires != nil {
|
||||
expireDuration = *params.Expires
|
||||
}
|
||||
url, err := getShareObjectURL(ctx, mcClient, params.VersionID, expireDuration)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return url, nil
|
||||
}
|
||||
|
||||
func getShareObjectURL(ctx context.Context, client MCClient, versionID string, duration string) (url *string, err error) {
|
||||
// default duration 7d if not defined
|
||||
if strings.TrimSpace(duration) == "" {
|
||||
duration = "168h"
|
||||
}
|
||||
|
||||
expiresDuration, err := time.ParseDuration(duration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objURL, pErr := client.shareDownload(ctx, versionID, expiresDuration)
|
||||
if pErr != nil {
|
||||
return nil, pErr.Cause
|
||||
}
|
||||
return &objURL, nil
|
||||
}
|
||||
|
||||
func getSetObjectLegalHoldResponse(session *models.Principal, params user_api.PutObjectLegalHoldParams) *models.Error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
err = setObjectLegalHold(ctx, minioClient, params.BucketName, params.Prefix, params.VersionID, params.Body.Status)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setObjectLegalHold(ctx context.Context, client MinioClient, bucketName, prefix, versionID string, status models.ObjectLegalHoldStatus) error {
|
||||
var lstatus minio.LegalHoldStatus
|
||||
if status == models.ObjectLegalHoldStatusEnabled {
|
||||
lstatus = minio.LegalHoldEnabled
|
||||
} else {
|
||||
lstatus = minio.LegalHoldDisabled
|
||||
}
|
||||
return client.putObjectLegalHold(ctx, bucketName, prefix, minio.PutObjectLegalHoldOptions{VersionID: versionID, Status: &lstatus})
|
||||
}
|
||||
|
||||
// newClientURL returns an abstracted URL for filesystems and object storage.
|
||||
func newClientURL(urlStr string) *mc.ClientURL {
|
||||
scheme, rest := getScheme(urlStr)
|
||||
|
||||
@@ -29,15 +29,19 @@ import (
|
||||
mc "github.com/minio/mc/cmd"
|
||||
"github.com/minio/mc/pkg/probe"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var minioListObjectsMock func(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo
|
||||
var minioGetObjectLegalHoldMock func(ctx context.Context, bucketName, objectName string, opts minio.GetObjectLegalHoldOptions) (status *minio.LegalHoldStatus, err error)
|
||||
var minioGetObjectRetentionMock func(ctx context.Context, bucketName, objectName, versionID string) (mode *minio.RetentionMode, retainUntilDate *time.Time, err error)
|
||||
var minioPutObjectMock func(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error)
|
||||
var minioPutObjectLegalHoldMock func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error
|
||||
|
||||
var mcListMock func(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent
|
||||
var mcRemoveMock func(ctx context.Context, isIncomplete, isRemoveBucket, isBypass bool, contentCh <-chan *mc.ClientContent) <-chan *probe.Error
|
||||
var mcGetMock func(ctx context.Context, opts mc.GetOptions) (io.ReadCloser, *probe.Error)
|
||||
var mcShareDownloadMock func(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error)
|
||||
|
||||
// mock functions for minioClientMock
|
||||
func (ac minioClientMock) listObjects(ctx context.Context, bucket string, opts minio.ListObjectsOptions) <-chan minio.ObjectInfo {
|
||||
@@ -51,6 +55,14 @@ func (ac minioClientMock) getObjectRetention(ctx context.Context, bucketName, ob
|
||||
return minioGetObjectRetentionMock(ctx, bucketName, objectName, versionID)
|
||||
}
|
||||
|
||||
func (ac minioClientMock) putObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts minio.PutObjectOptions) (info minio.UploadInfo, err error) {
|
||||
return minioPutObjectMock(ctx, bucketName, objectName, reader, objectSize, opts)
|
||||
}
|
||||
|
||||
func (ac minioClientMock) putObjectLegalHold(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error {
|
||||
return minioPutObjectLegalHoldMock(ctx, bucketName, objectName, opts)
|
||||
}
|
||||
|
||||
// mock functions for s3ClientMock
|
||||
func (c s3ClientMock) list(ctx context.Context, opts mc.ListOptions) <-chan *mc.ClientContent {
|
||||
return mcListMock(ctx, opts)
|
||||
@@ -63,6 +75,10 @@ func (c s3ClientMock) get(ctx context.Context, opts mc.GetOptions) (io.ReadClose
|
||||
return mcGetMock(ctx, opts)
|
||||
}
|
||||
|
||||
func (c s3ClientMock) shareDownload(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) {
|
||||
return mcShareDownloadMock(ctx, versionID, expires)
|
||||
}
|
||||
|
||||
func Test_listObjects(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
t1 := time.Now()
|
||||
@@ -540,3 +556,151 @@ func Test_deleteObjects(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_shareObject(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
ctx := context.Background()
|
||||
client := s3ClientMock{}
|
||||
type args struct {
|
||||
versionID string
|
||||
expires string
|
||||
shareFunc func(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error)
|
||||
}
|
||||
tests := []struct {
|
||||
test string
|
||||
args args
|
||||
wantError error
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
test: "Get share object url",
|
||||
args: args{
|
||||
versionID: "2121434",
|
||||
expires: "30s",
|
||||
shareFunc: func(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) {
|
||||
return "http://someurl", nil
|
||||
},
|
||||
},
|
||||
wantError: nil,
|
||||
expected: "http://someurl",
|
||||
},
|
||||
{
|
||||
test: "handle invalid expire duration",
|
||||
args: args{
|
||||
versionID: "2121434",
|
||||
expires: "invalid",
|
||||
shareFunc: func(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) {
|
||||
return "http://someurl", nil
|
||||
},
|
||||
},
|
||||
wantError: errors.New("time: invalid duration invalid"),
|
||||
},
|
||||
{
|
||||
test: "handle empty expire duration",
|
||||
args: args{
|
||||
versionID: "2121434",
|
||||
expires: "",
|
||||
shareFunc: func(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) {
|
||||
return "http://someurl", nil
|
||||
},
|
||||
},
|
||||
wantError: nil,
|
||||
expected: "http://someurl",
|
||||
},
|
||||
{
|
||||
test: "handle error on share func",
|
||||
args: args{
|
||||
versionID: "2121434",
|
||||
expires: "3h",
|
||||
shareFunc: func(ctx context.Context, versionID string, expires time.Duration) (string, *probe.Error) {
|
||||
return "", probe.NewError(errors.New("probe error"))
|
||||
},
|
||||
},
|
||||
wantError: errors.New("probe error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.test, func(t *testing.T) {
|
||||
mcShareDownloadMock = tt.args.shareFunc
|
||||
url, err := getShareObjectURL(ctx, client, tt.args.versionID, tt.args.expires)
|
||||
if tt.wantError != nil {
|
||||
if !reflect.DeepEqual(err, tt.wantError) {
|
||||
t.Errorf("getShareObjectURL() error: %v, wantErr: %v", err, tt.wantError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
assert.Equal(*url, tt.expected)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_putObjectLegalHold(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := minioClientMock{}
|
||||
type args struct {
|
||||
bucket string
|
||||
prefix string
|
||||
versionID string
|
||||
status models.ObjectLegalHoldStatus
|
||||
legalHoldFunc func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error
|
||||
}
|
||||
tests := []struct {
|
||||
test string
|
||||
args args
|
||||
wantError error
|
||||
}{
|
||||
{
|
||||
test: "Put Object Legal hold enabled status",
|
||||
args: args{
|
||||
bucket: "buck1",
|
||||
versionID: "someversion",
|
||||
prefix: "folder/file.txt",
|
||||
status: models.ObjectLegalHoldStatusEnabled,
|
||||
legalHoldFunc: func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantError: nil,
|
||||
},
|
||||
{
|
||||
test: "Put Object Legal hold disabled status",
|
||||
args: args{
|
||||
bucket: "buck1",
|
||||
versionID: "someversion",
|
||||
prefix: "folder/file.txt",
|
||||
status: models.ObjectLegalHoldStatusDisabled,
|
||||
legalHoldFunc: func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantError: nil,
|
||||
},
|
||||
{
|
||||
test: "Handle error on legalhold func",
|
||||
args: args{
|
||||
bucket: "buck1",
|
||||
versionID: "someversion",
|
||||
prefix: "folder/file.txt",
|
||||
status: models.ObjectLegalHoldStatusDisabled,
|
||||
legalHoldFunc: func(ctx context.Context, bucketName, objectName string, opts minio.PutObjectLegalHoldOptions) error {
|
||||
return errors.New("new error")
|
||||
},
|
||||
},
|
||||
wantError: errors.New("new error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.test, func(t *testing.T) {
|
||||
minioPutObjectLegalHoldMock = tt.args.legalHoldFunc
|
||||
err := setObjectLegalHold(ctx, client, tt.args.bucket, tt.args.prefix, tt.args.versionID, tt.args.status)
|
||||
if !reflect.DeepEqual(err, tt.wantError) {
|
||||
t.Errorf("setObjectLegalHold() error: %v, wantErr: %v", err, tt.wantError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func registerSessionHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
}
|
||||
|
||||
// getSessionResponse parse the jwt of the current session and returns a list of allowed actions to render in the UI
|
||||
// getSessionResponse parse the token of the current session and returns a list of allowed actions to render in the UI
|
||||
func getSessionResponse(session *models.Principal) (*models.SessionResponse, *models.Error) {
|
||||
// serialize output
|
||||
if session == nil {
|
||||
|
||||
@@ -107,6 +107,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
errors.ServeError(w, req, errors.New(http.StatusUnauthorized, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// upgrades the HTTP server connection to the WebSocket protocol.
|
||||
conn, err := upgrader.Upgrade(w, req, nil)
|
||||
if err != nil {
|
||||
|
||||
107
swagger.yml
@@ -285,6 +285,34 @@ paths:
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/buckets/{bucket_name}/objects/upload:
|
||||
post:
|
||||
summary: Uploads an Object.
|
||||
consumes:
|
||||
- multipart/form-data
|
||||
parameters:
|
||||
- in: formData
|
||||
name: upfile
|
||||
type: file
|
||||
required: true
|
||||
- name: bucket_name
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
- name: prefix
|
||||
in: query
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/buckets/{bucket_name}/objects/download:
|
||||
get:
|
||||
summary: Download Object
|
||||
@@ -312,6 +340,71 @@ paths:
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/buckets/{bucket_name}/objects/share:
|
||||
get:
|
||||
summary: Shares an Object on a url
|
||||
operationId: ShareObject
|
||||
parameters:
|
||||
- name: bucket_name
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
- name: prefix
|
||||
in: query
|
||||
required: true
|
||||
type: string
|
||||
- name: version_id
|
||||
in: query
|
||||
required: true
|
||||
type: string
|
||||
- name: expires
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
type: string
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/buckets/{bucket_name}/objects/legalhold:
|
||||
put:
|
||||
summary: Put Object's legalhold status
|
||||
operationId: PutObjectLegalHold
|
||||
parameters:
|
||||
- name: bucket_name
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
- name: prefix
|
||||
in: query
|
||||
required: true
|
||||
type: string
|
||||
- name: version_id
|
||||
in: query
|
||||
required: true
|
||||
type: string
|
||||
- name: body
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/putObjectLegalHoldRequest"
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- UserAPI
|
||||
|
||||
/buckets/{name}/set-policy:
|
||||
put:
|
||||
summary: Bucket Set Policy
|
||||
@@ -3224,3 +3317,17 @@ definitions:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
objectLegalHoldStatus:
|
||||
type: string
|
||||
enum:
|
||||
- enabled
|
||||
- disabled
|
||||
|
||||
putObjectLegalHoldRequest:
|
||||
type: object
|
||||
required:
|
||||
- status
|
||||
properties:
|
||||
status:
|
||||
$ref: "#/definitions/objectLegalHoldStatus"
|
||||
|
||||