Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c14142e19 | ||
|
|
1caa3f2ce8 | ||
|
|
6501a4b13f | ||
|
|
2f51621e69 | ||
|
|
7e6e64c729 | ||
|
|
9007c7dd14 | ||
|
|
850fd3e371 | ||
|
|
6d8f1c439e | ||
|
|
7166717688 | ||
|
|
f91346dc5b | ||
|
|
dccdfb5533 | ||
|
|
4f065bdedf | ||
|
|
4a02c5848b | ||
|
|
e16a926ef8 | ||
|
|
78f4978a9a | ||
|
|
42d617caf9 | ||
|
|
28eb8784a9 | ||
|
|
fcf5d5c9f7 | ||
|
|
a42f1ff4ee | ||
|
|
98f897ed5b | ||
|
|
7afd608faa | ||
|
|
8313a62f17 | ||
|
|
459e2bf61c | ||
|
|
858d363e97 | ||
|
|
47704189d1 | ||
|
|
b72d424ec9 | ||
|
|
86426e95f7 | ||
|
|
e5f7870f5e | ||
|
|
c0ee739624 | ||
|
|
1dc99498d9 | ||
|
|
319d96c725 | ||
|
|
6d58290a89 | ||
|
|
666904f902 | ||
|
|
064533d8aa | ||
|
|
1768af9026 | ||
|
|
cb7513e9f0 | ||
|
|
645b45cf35 | ||
|
|
9f6d965ba2 | ||
|
|
5348400665 | ||
|
|
812fd5f253 | ||
|
|
da9b393e1b | ||
|
|
aeaa1a23ce | ||
|
|
a8d403a216 | ||
|
|
7bd898b2c7 | ||
|
|
dad66db49a | ||
|
|
adf3f929a4 | ||
|
|
3b23e877b5 | ||
|
|
af4bebb6eb | ||
|
|
8530eb5368 | ||
|
|
0ba1e76400 | ||
|
|
94096ee657 | ||
|
|
c59387c2b4 | ||
|
|
c5a3eff745 | ||
|
|
624891ae1f | ||
|
|
83435e1ab9 | ||
|
|
2b4606e773 | ||
|
|
30f5943f8a | ||
|
|
412ac0a603 | ||
|
|
b2aa1349f8 | ||
|
|
8b62aec7fb | ||
|
|
83fe33b499 | ||
|
|
54d0a1d342 | ||
|
|
c59737a71d | ||
|
|
7c2ba707eb | ||
|
|
545a890c45 |
6
.github/workflows/go.yml
vendored
6
.github/workflows/go.yml
vendored
@@ -3,10 +3,10 @@ name: Go
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.13.x, 1.14.x]
|
||||
go-version: [1.14.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
|
||||
@@ -76,21 +76,19 @@ func newApp(name string) *cli.App {
|
||||
|
||||
findClosestCommands := func(command string) []string {
|
||||
var closestCommands []string
|
||||
for _, value := range commandsTree.PrefixMatch(command) {
|
||||
closestCommands = append(closestCommands, value.(string))
|
||||
}
|
||||
closestCommands = append(closestCommands, commandsTree.PrefixMatch(command)...)
|
||||
|
||||
sort.Strings(closestCommands)
|
||||
// Suggest other close commands - allow missed, wrongly added and
|
||||
// even transposed characters
|
||||
for _, value := range commandsTree.Walk(commandsTree.Root()) {
|
||||
if sort.SearchStrings(closestCommands, value.(string)) < len(closestCommands) {
|
||||
if sort.SearchStrings(closestCommands, value) < len(closestCommands) {
|
||||
continue
|
||||
}
|
||||
// 2 is arbitrary and represents the max
|
||||
// allowed number of typed errors
|
||||
if words.DamerauLevenshteinDistance(command, value.(string)) < 2 {
|
||||
closestCommands = append(closestCommands, value.(string))
|
||||
if words.DamerauLevenshteinDistance(command, value) < 2 {
|
||||
closestCommands = append(closestCommands, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
go.mod
14
go.mod
@@ -16,17 +16,17 @@ require (
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/minio/cli v1.22.0
|
||||
github.com/minio/kes v0.11.0
|
||||
github.com/minio/mc v0.0.0-20200808005614-7e52c104bee1
|
||||
github.com/minio/minio v0.0.0-20200808024306-2a9819aff876
|
||||
github.com/minio/minio-go/v7 v7.0.5-0.20200807085956-d7db33ea7618
|
||||
github.com/minio/operator v0.0.0-20200806194125-c2ff646f4af1
|
||||
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821
|
||||
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544
|
||||
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089
|
||||
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/unrolled/secure v1.0.7
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
k8s.io/api v0.18.6
|
||||
k8s.io/apimachinery v0.18.6
|
||||
|
||||
@@ -7,7 +7,6 @@ rules:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
- secrets
|
||||
- pods
|
||||
- services
|
||||
- events
|
||||
@@ -18,6 +17,18 @@ rules:
|
||||
- create
|
||||
- list
|
||||
- patch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
- create
|
||||
- list
|
||||
- patch
|
||||
- deletecollection
|
||||
- delete
|
||||
- apiGroups:
|
||||
- "storage.k8s.io"
|
||||
resources:
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.3.14
|
||||
image: minio/console:v0.4.1
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
args:
|
||||
- server
|
||||
|
||||
@@ -14,6 +14,7 @@ rules:
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- deletecollection
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
@@ -22,6 +23,7 @@ rules:
|
||||
- services
|
||||
- events
|
||||
- resourcequotas
|
||||
- nodes
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
@@ -34,6 +36,8 @@ rules:
|
||||
- persistentvolumeclaims
|
||||
verbs:
|
||||
- deletecollection
|
||||
- list
|
||||
- get
|
||||
- apiGroups:
|
||||
- "storage.k8s.io"
|
||||
resources:
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.3.14
|
||||
image: minio/console:v0.4.1
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_OPERATOR_MODE
|
||||
|
||||
@@ -8,4 +8,4 @@ resources:
|
||||
- console-configmap.yaml
|
||||
- console-service.yaml
|
||||
- console-deployment.yaml
|
||||
- https://github.com/minio/operator/?ref=v3.0.10
|
||||
- https://github.com/minio/operator/?ref=v3.0.19
|
||||
|
||||
63
models/add_bucket_replication.go
Normal file
63
models/add_bucket_replication.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// AddBucketReplication add bucket replication
|
||||
//
|
||||
// swagger:model addBucketReplication
|
||||
type AddBucketReplication struct {
|
||||
|
||||
// arn
|
||||
Arn string `json:"arn,omitempty"`
|
||||
|
||||
// destination bucket
|
||||
DestinationBucket string `json:"destination_bucket,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this add bucket replication
|
||||
func (m *AddBucketReplication) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *AddBucketReplication) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *AddBucketReplication) UnmarshalBinary(b []byte) error {
|
||||
var res AddBucketReplication
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
96
models/bucket_object.go
Normal file
96
models/bucket_object.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// BucketObject bucket object
|
||||
//
|
||||
// swagger:model bucketObject
|
||||
type BucketObject struct {
|
||||
|
||||
// content type
|
||||
ContentType string `json:"content_type,omitempty"`
|
||||
|
||||
// expiration
|
||||
Expiration string `json:"expiration,omitempty"`
|
||||
|
||||
// expiration rule id
|
||||
ExpirationRuleID string `json:"expiration_rule_id,omitempty"`
|
||||
|
||||
// is delete marker
|
||||
IsDeleteMarker bool `json:"is_delete_marker,omitempty"`
|
||||
|
||||
// is latest
|
||||
IsLatest bool `json:"is_latest,omitempty"`
|
||||
|
||||
// last modified
|
||||
LastModified string `json:"last_modified,omitempty"`
|
||||
|
||||
// legal hold status
|
||||
LegalHoldStatus string `json:"legal_hold_status,omitempty"`
|
||||
|
||||
// name
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// retention mode
|
||||
RetentionMode string `json:"retention_mode,omitempty"`
|
||||
|
||||
// retention until date
|
||||
RetentionUntilDate string `json:"retention_until_date,omitempty"`
|
||||
|
||||
// size
|
||||
Size int64 `json:"size,omitempty"`
|
||||
|
||||
// user tags
|
||||
UserTags map[string]string `json:"user_tags,omitempty"`
|
||||
|
||||
// version id
|
||||
VersionID string `json:"version_id,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this bucket object
|
||||
func (m *BucketObject) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *BucketObject) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *BucketObject) UnmarshalBinary(b []byte) error {
|
||||
var res BucketObject
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
60
models/bucket_replication_destination.go
Normal file
60
models/bucket_replication_destination.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// BucketReplicationDestination bucket replication destination
|
||||
//
|
||||
// swagger:model bucketReplicationDestination
|
||||
type BucketReplicationDestination struct {
|
||||
|
||||
// bucket
|
||||
Bucket string `json:"bucket,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this bucket replication destination
|
||||
func (m *BucketReplicationDestination) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *BucketReplicationDestination) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *BucketReplicationDestination) UnmarshalBinary(b []byte) error {
|
||||
var res BucketReplicationDestination
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
97
models/bucket_replication_response.go
Normal file
97
models/bucket_replication_response.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// 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 (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// BucketReplicationResponse bucket replication response
|
||||
//
|
||||
// swagger:model bucketReplicationResponse
|
||||
type BucketReplicationResponse struct {
|
||||
|
||||
// rules
|
||||
Rules []*BucketReplicationRule `json:"rules"`
|
||||
}
|
||||
|
||||
// Validate validates this bucket replication response
|
||||
func (m *BucketReplicationResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateRules(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BucketReplicationResponse) validateRules(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Rules) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Rules); i++ {
|
||||
if swag.IsZero(m.Rules[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Rules[i] != nil {
|
||||
if err := m.Rules[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("rules" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *BucketReplicationResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *BucketReplicationResponse) UnmarshalBinary(b []byte) error {
|
||||
var res BucketReplicationResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
173
models/bucket_replication_rule.go
Normal file
173
models/bucket_replication_rule.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// 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/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// BucketReplicationRule bucket replication rule
|
||||
//
|
||||
// swagger:model bucketReplicationRule
|
||||
type BucketReplicationRule struct {
|
||||
|
||||
// delete marker replication
|
||||
DeleteMarkerReplication *BucketReplicationRuleMarker `json:"delete_marker_replication,omitempty"`
|
||||
|
||||
// destination
|
||||
Destination *BucketReplicationDestination `json:"destination,omitempty"`
|
||||
|
||||
// id
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// priority
|
||||
Priority int32 `json:"priority,omitempty"`
|
||||
|
||||
// status
|
||||
// Enum: [Enabled Disabled]
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this bucket replication rule
|
||||
func (m *BucketReplicationRule) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateDeleteMarkerReplication(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateDestination(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateStatus(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BucketReplicationRule) validateDeleteMarkerReplication(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.DeleteMarkerReplication) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.DeleteMarkerReplication != nil {
|
||||
if err := m.DeleteMarkerReplication.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("delete_marker_replication")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BucketReplicationRule) validateDestination(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Destination) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Destination != nil {
|
||||
if err := m.Destination.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("destination")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var bucketReplicationRuleTypeStatusPropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["Enabled","Disabled"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
bucketReplicationRuleTypeStatusPropEnum = append(bucketReplicationRuleTypeStatusPropEnum, v)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
// BucketReplicationRuleStatusEnabled captures enum value "Enabled"
|
||||
BucketReplicationRuleStatusEnabled string = "Enabled"
|
||||
|
||||
// BucketReplicationRuleStatusDisabled captures enum value "Disabled"
|
||||
BucketReplicationRuleStatusDisabled string = "Disabled"
|
||||
)
|
||||
|
||||
// prop value enum
|
||||
func (m *BucketReplicationRule) validateStatusEnum(path, location string, value string) error {
|
||||
if err := validate.EnumCase(path, location, value, bucketReplicationRuleTypeStatusPropEnum, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BucketReplicationRule) validateStatus(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Status) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
// value enum
|
||||
if err := m.validateStatusEnum("status", "body", m.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *BucketReplicationRule) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *BucketReplicationRule) UnmarshalBinary(b []byte) error {
|
||||
var res BucketReplicationRule
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
117
models/bucket_replication_rule_marker.go
Normal file
117
models/bucket_replication_rule_marker.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// BucketReplicationRuleMarker bucket replication rule marker
|
||||
//
|
||||
// swagger:model bucketReplicationRuleMarker
|
||||
type BucketReplicationRuleMarker struct {
|
||||
|
||||
// status
|
||||
// Enum: [Enabled Disabled]
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this bucket replication rule marker
|
||||
func (m *BucketReplicationRuleMarker) 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
|
||||
}
|
||||
|
||||
var bucketReplicationRuleMarkerTypeStatusPropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["Enabled","Disabled"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
bucketReplicationRuleMarkerTypeStatusPropEnum = append(bucketReplicationRuleMarkerTypeStatusPropEnum, v)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
// BucketReplicationRuleMarkerStatusEnabled captures enum value "Enabled"
|
||||
BucketReplicationRuleMarkerStatusEnabled string = "Enabled"
|
||||
|
||||
// BucketReplicationRuleMarkerStatusDisabled captures enum value "Disabled"
|
||||
BucketReplicationRuleMarkerStatusDisabled string = "Disabled"
|
||||
)
|
||||
|
||||
// prop value enum
|
||||
func (m *BucketReplicationRuleMarker) validateStatusEnum(path, location string, value string) error {
|
||||
if err := validate.EnumCase(path, location, value, bucketReplicationRuleMarkerTypeStatusPropEnum, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BucketReplicationRuleMarker) validateStatus(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Status) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
// value enum
|
||||
if err := m.validateStatusEnum("status", "body", m.Status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *BucketReplicationRuleMarker) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *BucketReplicationRuleMarker) UnmarshalBinary(b []byte) error {
|
||||
var res BucketReplicationRuleMarker
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
60
models/bucket_versioning_response.go
Normal file
60
models/bucket_versioning_response.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// BucketVersioningResponse bucket versioning response
|
||||
//
|
||||
// swagger:model bucketVersioningResponse
|
||||
type BucketVersioningResponse struct {
|
||||
|
||||
// is versioned
|
||||
IsVersioned bool `json:"is_versioned,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this bucket versioning response
|
||||
func (m *BucketVersioningResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *BucketVersioningResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *BucketVersioningResponse) UnmarshalBinary(b []byte) error {
|
||||
var res BucketVersioningResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
117
models/console_configuration.go
Normal file
117
models/console_configuration.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// ConsoleConfiguration console configuration
|
||||
//
|
||||
// swagger:model consoleConfiguration
|
||||
type ConsoleConfiguration struct {
|
||||
MetadataFields
|
||||
|
||||
// image
|
||||
Image string `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals this object from a JSON structure
|
||||
func (m *ConsoleConfiguration) UnmarshalJSON(raw []byte) error {
|
||||
// AO0
|
||||
var aO0 MetadataFields
|
||||
if err := swag.ReadJSON(raw, &aO0); err != nil {
|
||||
return err
|
||||
}
|
||||
m.MetadataFields = aO0
|
||||
|
||||
// AO1
|
||||
var dataAO1 struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
}
|
||||
if err := swag.ReadJSON(raw, &dataAO1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Image = dataAO1.Image
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON marshals this object to a JSON structure
|
||||
func (m ConsoleConfiguration) MarshalJSON() ([]byte, error) {
|
||||
_parts := make([][]byte, 0, 2)
|
||||
|
||||
aO0, err := swag.WriteJSON(m.MetadataFields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_parts = append(_parts, aO0)
|
||||
var dataAO1 struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
dataAO1.Image = m.Image
|
||||
|
||||
jsonDataAO1, errAO1 := swag.WriteJSON(dataAO1)
|
||||
if errAO1 != nil {
|
||||
return nil, errAO1
|
||||
}
|
||||
_parts = append(_parts, jsonDataAO1)
|
||||
return swag.ConcatJSON(_parts...), nil
|
||||
}
|
||||
|
||||
// Validate validates this console configuration
|
||||
func (m *ConsoleConfiguration) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
// validation for a type composition with MetadataFields
|
||||
if err := m.MetadataFields.Validate(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ConsoleConfiguration) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ConsoleConfiguration) UnmarshalBinary(b []byte) error {
|
||||
var res ConsoleConfiguration
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
162
models/create_remote_bucket.go
Normal file
162
models/create_remote_bucket.go
Normal file
@@ -0,0 +1,162 @@
|
||||
// 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"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// CreateRemoteBucket create remote bucket
|
||||
//
|
||||
// swagger:model createRemoteBucket
|
||||
type CreateRemoteBucket struct {
|
||||
|
||||
// access key
|
||||
// Required: true
|
||||
// Min Length: 3
|
||||
AccessKey *string `json:"accessKey"`
|
||||
|
||||
// region
|
||||
Region string `json:"region,omitempty"`
|
||||
|
||||
// secret key
|
||||
// Required: true
|
||||
// Min Length: 8
|
||||
SecretKey *string `json:"secretKey"`
|
||||
|
||||
// source bucket
|
||||
// Required: true
|
||||
SourceBucket *string `json:"sourceBucket"`
|
||||
|
||||
// target bucket
|
||||
// Required: true
|
||||
TargetBucket *string `json:"targetBucket"`
|
||||
|
||||
// target URL
|
||||
// Required: true
|
||||
TargetURL *string `json:"targetURL"`
|
||||
}
|
||||
|
||||
// Validate validates this create remote bucket
|
||||
func (m *CreateRemoteBucket) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateAccessKey(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateSecretKey(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateSourceBucket(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateTargetBucket(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateTargetURL(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateRemoteBucket) validateAccessKey(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("accessKey", "body", m.AccessKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.MinLength("accessKey", "body", string(*m.AccessKey), 3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateRemoteBucket) validateSecretKey(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("secretKey", "body", m.SecretKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.MinLength("secretKey", "body", string(*m.SecretKey), 8); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateRemoteBucket) validateSourceBucket(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("sourceBucket", "body", m.SourceBucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateRemoteBucket) validateTargetBucket(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("targetBucket", "body", m.TargetBucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateRemoteBucket) validateTargetURL(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("targetURL", "body", m.TargetURL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *CreateRemoteBucket) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *CreateRemoteBucket) UnmarshalBinary(b []byte) error {
|
||||
var res CreateRemoteBucket
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -42,12 +42,18 @@ type CreateTenantRequest struct {
|
||||
// annotations
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// console
|
||||
Console *ConsoleConfiguration `json:"console,omitempty"`
|
||||
|
||||
// console image
|
||||
ConsoleImage string `json:"console_image,omitempty"`
|
||||
|
||||
// enable console
|
||||
EnableConsole *bool `json:"enable_console,omitempty"`
|
||||
|
||||
// enable prometheus
|
||||
EnablePrometheus *bool `json:"enable_prometheus,omitempty"`
|
||||
|
||||
// enable tls
|
||||
EnableTLS *bool `json:"enable_tls,omitempty"`
|
||||
|
||||
@@ -69,6 +75,9 @@ type CreateTenantRequest struct {
|
||||
// image registry
|
||||
ImageRegistry *ImageRegistry `json:"image_registry,omitempty"`
|
||||
|
||||
// labels
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// mounth path
|
||||
MounthPath string `json:"mounth_path,omitempty"`
|
||||
|
||||
@@ -99,6 +108,10 @@ type CreateTenantRequest struct {
|
||||
func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateConsole(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateEncryption(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -133,6 +146,24 @@ func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateTenantRequest) validateConsole(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Console) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Console != nil {
|
||||
if err := m.Console.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("console")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateTenantRequest) validateEncryption(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Encryption) { // not required
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
//
|
||||
// swagger:model encryptionConfiguration
|
||||
type EncryptionConfiguration struct {
|
||||
MetadataFields
|
||||
|
||||
// aws
|
||||
Aws *AwsConfiguration `json:"aws,omitempty"`
|
||||
@@ -52,10 +53,100 @@ type EncryptionConfiguration struct {
|
||||
Vault *VaultConfiguration `json:"vault,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals this object from a JSON structure
|
||||
func (m *EncryptionConfiguration) UnmarshalJSON(raw []byte) error {
|
||||
// AO0
|
||||
var aO0 MetadataFields
|
||||
if err := swag.ReadJSON(raw, &aO0); err != nil {
|
||||
return err
|
||||
}
|
||||
m.MetadataFields = aO0
|
||||
|
||||
// AO1
|
||||
var dataAO1 struct {
|
||||
Aws *AwsConfiguration `json:"aws,omitempty"`
|
||||
|
||||
Client *KeyPairConfiguration `json:"client,omitempty"`
|
||||
|
||||
Gemalto *GemaltoConfiguration `json:"gemalto,omitempty"`
|
||||
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
Server *KeyPairConfiguration `json:"server,omitempty"`
|
||||
|
||||
Vault *VaultConfiguration `json:"vault,omitempty"`
|
||||
}
|
||||
if err := swag.ReadJSON(raw, &dataAO1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Aws = dataAO1.Aws
|
||||
|
||||
m.Client = dataAO1.Client
|
||||
|
||||
m.Gemalto = dataAO1.Gemalto
|
||||
|
||||
m.Image = dataAO1.Image
|
||||
|
||||
m.Server = dataAO1.Server
|
||||
|
||||
m.Vault = dataAO1.Vault
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON marshals this object to a JSON structure
|
||||
func (m EncryptionConfiguration) MarshalJSON() ([]byte, error) {
|
||||
_parts := make([][]byte, 0, 2)
|
||||
|
||||
aO0, err := swag.WriteJSON(m.MetadataFields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_parts = append(_parts, aO0)
|
||||
var dataAO1 struct {
|
||||
Aws *AwsConfiguration `json:"aws,omitempty"`
|
||||
|
||||
Client *KeyPairConfiguration `json:"client,omitempty"`
|
||||
|
||||
Gemalto *GemaltoConfiguration `json:"gemalto,omitempty"`
|
||||
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
Server *KeyPairConfiguration `json:"server,omitempty"`
|
||||
|
||||
Vault *VaultConfiguration `json:"vault,omitempty"`
|
||||
}
|
||||
|
||||
dataAO1.Aws = m.Aws
|
||||
|
||||
dataAO1.Client = m.Client
|
||||
|
||||
dataAO1.Gemalto = m.Gemalto
|
||||
|
||||
dataAO1.Image = m.Image
|
||||
|
||||
dataAO1.Server = m.Server
|
||||
|
||||
dataAO1.Vault = m.Vault
|
||||
|
||||
jsonDataAO1, errAO1 := swag.WriteJSON(dataAO1)
|
||||
if errAO1 != nil {
|
||||
return nil, errAO1
|
||||
}
|
||||
_parts = append(_parts, jsonDataAO1)
|
||||
return swag.ConcatJSON(_parts...), nil
|
||||
}
|
||||
|
||||
// Validate validates this encryption configuration
|
||||
func (m *EncryptionConfiguration) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
// validation for a type composition with MetadataFields
|
||||
if err := m.MetadataFields.Validate(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateAws(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
type Error struct {
|
||||
|
||||
// code
|
||||
Code int64 `json:"code,omitempty"`
|
||||
Code int32 `json:"code,omitempty"`
|
||||
|
||||
// message
|
||||
// Required: true
|
||||
|
||||
100
models/list_objects_response.go
Normal file
100
models/list_objects_response.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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 (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ListObjectsResponse list objects response
|
||||
//
|
||||
// swagger:model listObjectsResponse
|
||||
type ListObjectsResponse struct {
|
||||
|
||||
// list of resulting objects
|
||||
Objects []*BucketObject `json:"objects"`
|
||||
|
||||
// number of objects
|
||||
Total int64 `json:"total,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this list objects response
|
||||
func (m *ListObjectsResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateObjects(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ListObjectsResponse) validateObjects(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Objects) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Objects); i++ {
|
||||
if swag.IsZero(m.Objects[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Objects[i] != nil {
|
||||
if err := m.Objects[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("objects" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ListObjectsResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ListObjectsResponse) UnmarshalBinary(b []byte) error {
|
||||
var res ListObjectsResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
100
models/list_remote_buckets_response.go
Normal file
100
models/list_remote_buckets_response.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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 (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ListRemoteBucketsResponse list remote buckets response
|
||||
//
|
||||
// swagger:model listRemoteBucketsResponse
|
||||
type ListRemoteBucketsResponse struct {
|
||||
|
||||
// list of remote buckets
|
||||
Buckets []*RemoteBucket `json:"buckets"`
|
||||
|
||||
// number of remote buckets accessible to user
|
||||
Total int64 `json:"total,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this list remote buckets response
|
||||
func (m *ListRemoteBucketsResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateBuckets(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ListRemoteBucketsResponse) validateBuckets(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Buckets) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Buckets); i++ {
|
||||
if swag.IsZero(m.Buckets[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Buckets[i] != nil {
|
||||
if err := m.Buckets[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("buckets" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ListRemoteBucketsResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ListRemoteBucketsResponse) UnmarshalBinary(b []byte) error {
|
||||
var res ListRemoteBucketsResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -37,6 +37,12 @@ type MakeBucketRequest struct {
|
||||
// name
|
||||
// Required: true
|
||||
Name *string `json:"name"`
|
||||
|
||||
// quota
|
||||
Quota *SetBucketQuota `json:"quota,omitempty"`
|
||||
|
||||
// versioning
|
||||
Versioning bool `json:"versioning,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this make bucket request
|
||||
@@ -47,6 +53,10 @@ func (m *MakeBucketRequest) Validate(formats strfmt.Registry) error {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateQuota(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
@@ -62,6 +72,24 @@ func (m *MakeBucketRequest) validateName(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MakeBucketRequest) validateQuota(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Quota) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Quota != nil {
|
||||
if err := m.Quota.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("quota")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *MakeBucketRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
|
||||
60
models/max_allocatable_mem_response.go
Normal file
60
models/max_allocatable_mem_response.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// MaxAllocatableMemResponse max allocatable mem response
|
||||
//
|
||||
// swagger:model maxAllocatableMemResponse
|
||||
type MaxAllocatableMemResponse struct {
|
||||
|
||||
// max memory
|
||||
MaxMemory int64 `json:"max_memory,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this max allocatable mem response
|
||||
func (m *MaxAllocatableMemResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *MaxAllocatableMemResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *MaxAllocatableMemResponse) UnmarshalBinary(b []byte) error {
|
||||
var res MaxAllocatableMemResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
66
models/metadata_fields.go
Normal file
66
models/metadata_fields.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// MetadataFields metadata fields
|
||||
//
|
||||
// swagger:model metadataFields
|
||||
type MetadataFields struct {
|
||||
|
||||
// annotations
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// labels
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// node selector
|
||||
NodeSelector map[string]string `json:"node_selector,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this metadata fields
|
||||
func (m *MetadataFields) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *MetadataFields) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *MetadataFields) UnmarshalBinary(b []byte) error {
|
||||
var res MetadataFields
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
37
models/parity_response.go
Normal file
37
models/parity_response.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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/strfmt"
|
||||
)
|
||||
|
||||
// ParityResponse parity response
|
||||
//
|
||||
// swagger:model parityResponse
|
||||
type ParityResponse []string
|
||||
|
||||
// Validate validates this parity response
|
||||
func (m ParityResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
200
models/remote_bucket.go
Normal file
200
models/remote_bucket.go
Normal file
@@ -0,0 +1,200 @@
|
||||
// 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/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// RemoteBucket remote bucket
|
||||
//
|
||||
// swagger:model remoteBucket
|
||||
type RemoteBucket struct {
|
||||
|
||||
// access key
|
||||
// Required: true
|
||||
// Min Length: 3
|
||||
AccessKey *string `json:"accessKey"`
|
||||
|
||||
// remote a r n
|
||||
// Required: true
|
||||
RemoteARN *string `json:"remoteARN"`
|
||||
|
||||
// secret key
|
||||
// Min Length: 8
|
||||
SecretKey string `json:"secretKey,omitempty"`
|
||||
|
||||
// service
|
||||
// Enum: [replication]
|
||||
Service string `json:"service,omitempty"`
|
||||
|
||||
// source bucket
|
||||
// Required: true
|
||||
SourceBucket *string `json:"sourceBucket"`
|
||||
|
||||
// status
|
||||
Status string `json:"status,omitempty"`
|
||||
|
||||
// target bucket
|
||||
TargetBucket string `json:"targetBucket,omitempty"`
|
||||
|
||||
// target URL
|
||||
TargetURL string `json:"targetURL,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this remote bucket
|
||||
func (m *RemoteBucket) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateAccessKey(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateRemoteARN(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateSecretKey(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateService(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateSourceBucket(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *RemoteBucket) validateAccessKey(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("accessKey", "body", m.AccessKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.MinLength("accessKey", "body", string(*m.AccessKey), 3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *RemoteBucket) validateRemoteARN(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("remoteARN", "body", m.RemoteARN); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *RemoteBucket) validateSecretKey(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.SecretKey) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := validate.MinLength("secretKey", "body", string(m.SecretKey), 8); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var remoteBucketTypeServicePropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["replication"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
remoteBucketTypeServicePropEnum = append(remoteBucketTypeServicePropEnum, v)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
// RemoteBucketServiceReplication captures enum value "replication"
|
||||
RemoteBucketServiceReplication string = "replication"
|
||||
)
|
||||
|
||||
// prop value enum
|
||||
func (m *RemoteBucket) validateServiceEnum(path, location string, value string) error {
|
||||
if err := validate.EnumCase(path, location, value, remoteBucketTypeServicePropEnum, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *RemoteBucket) validateService(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Service) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
// value enum
|
||||
if err := m.validateServiceEnum("service", "body", m.Service); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *RemoteBucket) validateSourceBucket(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("sourceBucket", "body", m.SourceBucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *RemoteBucket) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *RemoteBucket) UnmarshalBinary(b []byte) error {
|
||||
var res RemoteBucket
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
137
models/set_bucket_quota.go
Normal file
137
models/set_bucket_quota.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// SetBucketQuota set bucket quota
|
||||
//
|
||||
// swagger:model setBucketQuota
|
||||
type SetBucketQuota struct {
|
||||
|
||||
// amount
|
||||
Amount int64 `json:"amount,omitempty"`
|
||||
|
||||
// enabled
|
||||
// Required: true
|
||||
Enabled *bool `json:"enabled"`
|
||||
|
||||
// quota type
|
||||
// Enum: [fifo hard]
|
||||
QuotaType string `json:"quota_type,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this set bucket quota
|
||||
func (m *SetBucketQuota) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateEnabled(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateQuotaType(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SetBucketQuota) validateEnabled(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("enabled", "body", m.Enabled); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var setBucketQuotaTypeQuotaTypePropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["fifo","hard"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
setBucketQuotaTypeQuotaTypePropEnum = append(setBucketQuotaTypeQuotaTypePropEnum, v)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
// SetBucketQuotaQuotaTypeFifo captures enum value "fifo"
|
||||
SetBucketQuotaQuotaTypeFifo string = "fifo"
|
||||
|
||||
// SetBucketQuotaQuotaTypeHard captures enum value "hard"
|
||||
SetBucketQuotaQuotaTypeHard string = "hard"
|
||||
)
|
||||
|
||||
// prop value enum
|
||||
func (m *SetBucketQuota) validateQuotaTypeEnum(path, location string, value string) error {
|
||||
if err := validate.EnumCase(path, location, value, setBucketQuotaTypeQuotaTypePropEnum, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SetBucketQuota) validateQuotaType(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.QuotaType) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
// value enum
|
||||
if err := m.validateQuotaTypeEnum("quota_type", "body", m.QuotaType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SetBucketQuota) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SetBucketQuota) UnmarshalBinary(b []byte) error {
|
||||
var res SetBucketQuota
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
60
models/set_bucket_versioning.go
Normal file
60
models/set_bucket_versioning.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SetBucketVersioning set bucket versioning
|
||||
//
|
||||
// swagger:model setBucketVersioning
|
||||
type SetBucketVersioning struct {
|
||||
|
||||
// versioning
|
||||
Versioning bool `json:"versioning,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this set bucket versioning
|
||||
func (m *SetBucketVersioning) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SetBucketVersioning) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SetBucketVersioning) UnmarshalBinary(b []byte) error {
|
||||
var res SetBucketVersioning
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -35,12 +35,21 @@ import (
|
||||
// swagger:model tenant
|
||||
type Tenant struct {
|
||||
|
||||
// console image
|
||||
ConsoleImage string `json:"console_image,omitempty"`
|
||||
|
||||
// creation date
|
||||
CreationDate string `json:"creation_date,omitempty"`
|
||||
|
||||
// current state
|
||||
CurrentState string `json:"currentState,omitempty"`
|
||||
|
||||
// deletion date
|
||||
DeletionDate string `json:"deletion_date,omitempty"`
|
||||
|
||||
// enable prometheus
|
||||
EnablePrometheus bool `json:"enable_prometheus,omitempty"`
|
||||
|
||||
// image
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ type TenantList struct {
|
||||
// current state
|
||||
CurrentState string `json:"currentState,omitempty"`
|
||||
|
||||
// deletion date
|
||||
DeletionDate string `json:"deletion_date,omitempty"`
|
||||
|
||||
// instance count
|
||||
InstanceCount int64 `json:"instance_count,omitempty"`
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ package models
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
@@ -37,7 +39,7 @@ type TLSConfiguration struct {
|
||||
Console *KeyPairConfiguration `json:"console,omitempty"`
|
||||
|
||||
// minio
|
||||
Minio *KeyPairConfiguration `json:"minio,omitempty"`
|
||||
Minio []*KeyPairConfiguration `json:"minio"`
|
||||
}
|
||||
|
||||
// Validate validates this tls configuration
|
||||
@@ -82,13 +84,20 @@ func (m *TLSConfiguration) validateMinio(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Minio != nil {
|
||||
if err := m.Minio.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("minio")
|
||||
}
|
||||
return err
|
||||
for i := 0; i < len(m.Minio); i++ {
|
||||
if swag.IsZero(m.Minio[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Minio[i] != nil {
|
||||
if err := m.Minio[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("minio" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -38,6 +38,9 @@ type UpdateTenantRequest struct {
|
||||
// Pattern: ^((.*?)/(.*?):(.+))$
|
||||
ConsoleImage string `json:"console_image,omitempty"`
|
||||
|
||||
// enable prometheus
|
||||
EnablePrometheus bool `json:"enable_prometheus,omitempty"`
|
||||
|
||||
// image
|
||||
// Pattern: ^((.*?)/(.*?):(.+))$
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
@@ -207,6 +207,12 @@ func (m *Zone) UnmarshalBinary(b []byte) error {
|
||||
// swagger:model ZoneVolumeConfiguration
|
||||
type ZoneVolumeConfiguration struct {
|
||||
|
||||
// annotations
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// labels
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// size
|
||||
// Required: true
|
||||
Size *int64 `json:"size"`
|
||||
|
||||
99
models/zone_update_request.go
Normal file
99
models/zone_update_request.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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 (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// ZoneUpdateRequest zone update request
|
||||
//
|
||||
// swagger:model zoneUpdateRequest
|
||||
type ZoneUpdateRequest struct {
|
||||
|
||||
// zones
|
||||
// Required: true
|
||||
Zones []*Zone `json:"zones"`
|
||||
}
|
||||
|
||||
// Validate validates this zone update request
|
||||
func (m *ZoneUpdateRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateZones(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ZoneUpdateRequest) validateZones(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("zones", "body", m.Zones); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Zones); i++ {
|
||||
if swag.IsZero(m.Zones[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Zones[i] != nil {
|
||||
if err := m.Zones[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("zones" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ZoneUpdateRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ZoneUpdateRequest) UnmarshalBinary(b []byte) error {
|
||||
var res ZoneUpdateRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
3
package-lock.json
generated
3
package-lock.json
generated
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
@@ -22,22 +22,27 @@ import (
|
||||
|
||||
// endpoints definition
|
||||
var (
|
||||
configuration = "/configurations-list"
|
||||
users = "/users"
|
||||
groups = "/groups"
|
||||
iamPolicies = "/policies"
|
||||
dashboard = "/dashboard"
|
||||
profiling = "/profiling"
|
||||
trace = "/trace"
|
||||
logs = "/logs"
|
||||
watch = "/watch"
|
||||
notifications = "/notification-endpoints"
|
||||
buckets = "/buckets"
|
||||
bucketsDetail = "/buckets/:bucketName"
|
||||
serviceAccounts = "/service-accounts"
|
||||
tenants = "/tenants"
|
||||
tenantsDetail = "/tenants/:tenantName"
|
||||
heal = "/heal"
|
||||
configuration = "/configurations-list"
|
||||
users = "/users"
|
||||
groups = "/groups"
|
||||
iamPolicies = "/policies"
|
||||
dashboard = "/dashboard"
|
||||
profiling = "/profiling"
|
||||
trace = "/trace"
|
||||
logs = "/logs"
|
||||
watch = "/watch"
|
||||
notifications = "/notification-endpoints"
|
||||
buckets = "/buckets"
|
||||
bucketsDetail = "/buckets/:bucketName"
|
||||
serviceAccounts = "/service-accounts"
|
||||
tenants = "/tenants"
|
||||
tenantsDetail = "/namespaces/:tenantNamespace/tenants/:tenantName"
|
||||
heal = "/heal"
|
||||
remoteBuckets = "/remote-buckets"
|
||||
replication = "/replication"
|
||||
objectBrowser = "/object-browser/:bucket?"
|
||||
mainObjectBrowser = "/object-browser"
|
||||
license = "/license"
|
||||
)
|
||||
|
||||
type ConfigurationActionSet struct {
|
||||
@@ -208,22 +213,57 @@ var healActionSet = ConfigurationActionSet{
|
||||
),
|
||||
}
|
||||
|
||||
var remoteBucketsActionSet = ConfigurationActionSet{
|
||||
actionTypes: iampolicy.NewActionSet(
|
||||
iampolicy.AllAdminActions,
|
||||
),
|
||||
actions: iampolicy.NewActionSet(
|
||||
iampolicy.ConfigUpdateAdminAction,
|
||||
),
|
||||
}
|
||||
|
||||
var replicationActionSet = ConfigurationActionSet{
|
||||
actionTypes: iampolicy.NewActionSet(
|
||||
iampolicy.AllAdminActions,
|
||||
),
|
||||
actions: iampolicy.NewActionSet(
|
||||
iampolicy.ConfigUpdateAdminAction,
|
||||
),
|
||||
}
|
||||
|
||||
// objectBrowserActionSet no actions needed for this module to work
|
||||
var objectBrowserActionSet = ConfigurationActionSet{
|
||||
actionTypes: iampolicy.NewActionSet(),
|
||||
actions: iampolicy.NewActionSet(),
|
||||
}
|
||||
|
||||
// licenseActionSet no actions needed for this module to work
|
||||
var licenseActionSet = ConfigurationActionSet{
|
||||
actionTypes: iampolicy.NewActionSet(),
|
||||
actions: iampolicy.NewActionSet(),
|
||||
}
|
||||
|
||||
// endpointRules contains the mapping between endpoints and ActionSets, additional rules can be added here
|
||||
var endpointRules = map[string]ConfigurationActionSet{
|
||||
configuration: configurationActionSet,
|
||||
users: usersActionSet,
|
||||
groups: groupsActionSet,
|
||||
iamPolicies: iamPoliciesActionSet,
|
||||
dashboard: dashboardActionSet,
|
||||
profiling: profilingActionSet,
|
||||
trace: traceActionSet,
|
||||
logs: logsActionSet,
|
||||
watch: watchActionSet,
|
||||
notifications: notificationsActionSet,
|
||||
buckets: bucketsActionSet,
|
||||
bucketsDetail: bucketsActionSet,
|
||||
serviceAccounts: serviceAccountsActionSet,
|
||||
heal: healActionSet,
|
||||
configuration: configurationActionSet,
|
||||
users: usersActionSet,
|
||||
groups: groupsActionSet,
|
||||
iamPolicies: iamPoliciesActionSet,
|
||||
dashboard: dashboardActionSet,
|
||||
profiling: profilingActionSet,
|
||||
trace: traceActionSet,
|
||||
logs: logsActionSet,
|
||||
watch: watchActionSet,
|
||||
notifications: notificationsActionSet,
|
||||
buckets: bucketsActionSet,
|
||||
bucketsDetail: bucketsActionSet,
|
||||
serviceAccounts: serviceAccountsActionSet,
|
||||
heal: healActionSet,
|
||||
remoteBuckets: remoteBucketsActionSet,
|
||||
replication: replicationActionSet,
|
||||
objectBrowser: objectBrowserActionSet,
|
||||
mainObjectBrowser: objectBrowserActionSet,
|
||||
license: licenseActionSet,
|
||||
}
|
||||
|
||||
// operatorRules contains the mapping between endpoints and ActionSets for operator only mode
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
args: args{
|
||||
[]string{"admin:ServerInfo"},
|
||||
},
|
||||
want: 2,
|
||||
want: 5,
|
||||
},
|
||||
{
|
||||
name: "policies endpoint",
|
||||
@@ -63,7 +63,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"admin:ListUserPolicies",
|
||||
},
|
||||
},
|
||||
want: 2,
|
||||
want: 5,
|
||||
},
|
||||
{
|
||||
name: "all admin endpoints",
|
||||
@@ -72,7 +72,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"admin:*",
|
||||
},
|
||||
},
|
||||
want: 11,
|
||||
want: 16,
|
||||
},
|
||||
{
|
||||
name: "all s3 endpoints",
|
||||
@@ -81,7 +81,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 4,
|
||||
want: 7,
|
||||
},
|
||||
{
|
||||
name: "all admin and s3 endpoints",
|
||||
@@ -91,7 +91,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 14,
|
||||
want: 19,
|
||||
},
|
||||
{
|
||||
name: "no endpoints",
|
||||
|
||||
@@ -155,10 +155,10 @@ const (
|
||||
// or data key provided as plaintext.
|
||||
//
|
||||
// The returned ciphertext data consists of:
|
||||
// iv | AEAD ID | nonce | encrypted data
|
||||
// 32 1 12 ~ len(data)
|
||||
// AEAD ID | iv | nonce | encrypted data
|
||||
// 1 16 12 ~ len(data)
|
||||
func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||
iv, err := sioutil.Random(32) // 32 bit IV
|
||||
iv, err := sioutil.Random(16) // 16 bytes IV
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||
}
|
||||
case c20p1305:
|
||||
var sealingKey []byte
|
||||
sealingKey, err = chacha20.HChaCha20(derivedKey, iv)
|
||||
sealingKey, err = chacha20.HChaCha20(derivedKey, iv) // HChaCha20 expects nonce of 16 bytes
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -202,11 +202,11 @@ func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||
|
||||
sealedBytes := aead.Seal(nil, nonce, plaintext, associatedData)
|
||||
|
||||
// ciphertext = iv | AEAD ID | nonce | sealed bytes
|
||||
// ciphertext = AEAD ID | iv | nonce | sealed bytes
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write(iv)
|
||||
buf.WriteByte(algorithm)
|
||||
buf.Write(iv)
|
||||
buf.Write(nonce)
|
||||
buf.Write(sealedBytes)
|
||||
|
||||
@@ -218,16 +218,16 @@ func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||
// and a pbkdf2 derived key
|
||||
func decrypt(ciphertext []byte, associatedData []byte) ([]byte, error) {
|
||||
var (
|
||||
iv [32]byte
|
||||
algorithm [1]byte
|
||||
iv [16]byte
|
||||
nonce [12]byte // This depends on the AEAD but both used ciphers have the same nonce length.
|
||||
)
|
||||
|
||||
r := bytes.NewReader(ciphertext)
|
||||
if _, err := io.ReadFull(r, iv[:]); err != nil {
|
||||
if _, err := io.ReadFull(r, algorithm[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, algorithm[:]); err != nil {
|
||||
if _, err := io.ReadFull(r, iv[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, nonce[:]); err != nil {
|
||||
@@ -249,7 +249,7 @@ func decrypt(ciphertext []byte, associatedData []byte) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
case c20p1305:
|
||||
sealingKey, err := chacha20.HChaCha20(derivedKey, iv[:])
|
||||
sealingKey, err := chacha20.HChaCha20(derivedKey, iv[:]) // HChaCha20 expects nonce of 16 bytes
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
219
pkg/utils/parity.go
Normal file
219
pkg/utils/parity.go
Normal file
@@ -0,0 +1,219 @@
|
||||
// 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 utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio/pkg/ellipses"
|
||||
)
|
||||
|
||||
// This file implements and supports ellipses pattern for
|
||||
// `minio server` command line arguments.
|
||||
|
||||
// Supported set sizes this is used to find the optimal
|
||||
// single set size.
|
||||
var setSizes = []uint64{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
|
||||
|
||||
// getDivisibleSize - returns a greatest common divisor of
|
||||
// all the ellipses sizes.
|
||||
func getDivisibleSize(totalSizes []uint64) (result uint64) {
|
||||
gcd := func(x, y uint64) uint64 {
|
||||
for y != 0 {
|
||||
x, y = y, x%y
|
||||
}
|
||||
return x
|
||||
}
|
||||
result = totalSizes[0]
|
||||
for i := 1; i < len(totalSizes); i++ {
|
||||
result = gcd(result, totalSizes[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// isValidSetSize - checks whether given count is a valid set size for erasure coding.
|
||||
var isValidSetSize = func(count uint64) bool {
|
||||
return (count >= setSizes[0] && count <= setSizes[len(setSizes)-1])
|
||||
}
|
||||
|
||||
// possibleSetCountsWithSymmetry returns symmetrical setCounts based on the
|
||||
// input argument patterns, the symmetry calculation is to ensure that
|
||||
// we also use uniform number of drives common across all ellipses patterns.
|
||||
func possibleSetCountsWithSymmetry(setCounts []uint64, argPatterns []ellipses.ArgPattern) []uint64 {
|
||||
var newSetCounts = make(map[uint64]struct{})
|
||||
for _, ss := range setCounts {
|
||||
var symmetry bool
|
||||
for _, argPattern := range argPatterns {
|
||||
for _, p := range argPattern {
|
||||
if uint64(len(p.Seq)) > ss {
|
||||
symmetry = uint64(len(p.Seq))%ss == 0
|
||||
} else {
|
||||
symmetry = ss%uint64(len(p.Seq)) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
// With no arg patterns, it is expected that user knows
|
||||
// the right symmetry, so either ellipses patterns are
|
||||
// provided (recommended) or no ellipses patterns.
|
||||
if _, ok := newSetCounts[ss]; !ok && (symmetry || argPatterns == nil) {
|
||||
newSetCounts[ss] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
setCounts = []uint64{}
|
||||
for setCount := range newSetCounts {
|
||||
setCounts = append(setCounts, setCount)
|
||||
}
|
||||
|
||||
// Not necessarily needed but it ensures to the readers
|
||||
// eyes that we prefer a sorted setCount slice for the
|
||||
// subsequent function to figure out the right common
|
||||
// divisor, it avoids loops.
|
||||
sort.Slice(setCounts, func(i, j int) bool {
|
||||
return setCounts[i] < setCounts[j]
|
||||
})
|
||||
|
||||
return setCounts
|
||||
}
|
||||
|
||||
func commonSetDriveCount(divisibleSize uint64, setCounts []uint64) (setSize uint64) {
|
||||
// prefers setCounts to be sorted for optimal behavior.
|
||||
if divisibleSize < setCounts[len(setCounts)-1] {
|
||||
return divisibleSize
|
||||
}
|
||||
|
||||
// Figure out largest value of total_drives_in_erasure_set which results
|
||||
// in least number of total_drives/total_drives_erasure_set ratio.
|
||||
prevD := divisibleSize / setCounts[0]
|
||||
for _, cnt := range setCounts {
|
||||
if divisibleSize%cnt == 0 {
|
||||
d := divisibleSize / cnt
|
||||
if d <= prevD {
|
||||
prevD = d
|
||||
setSize = cnt
|
||||
}
|
||||
}
|
||||
}
|
||||
return setSize
|
||||
}
|
||||
|
||||
// getSetIndexes returns list of indexes which provides the set size
|
||||
// on each index, this function also determines the final set size
|
||||
// The final set size has the affinity towards choosing smaller
|
||||
// indexes (total sets)
|
||||
func getSetIndexes(args []string, totalSizes []uint64, argPatterns []ellipses.ArgPattern) (setIndexes [][]uint64, err error) {
|
||||
if len(totalSizes) == 0 || len(args) == 0 {
|
||||
return nil, errors.New("invalid argument")
|
||||
}
|
||||
|
||||
setIndexes = make([][]uint64, len(totalSizes))
|
||||
for _, totalSize := range totalSizes {
|
||||
// Check if totalSize has minimum range upto setSize
|
||||
if totalSize < setSizes[0] {
|
||||
return nil, fmt.Errorf("incorrect number of endpoints provided %s", args)
|
||||
}
|
||||
}
|
||||
|
||||
commonSize := getDivisibleSize(totalSizes)
|
||||
possibleSetCounts := func(setSize uint64) (ss []uint64) {
|
||||
for _, s := range setSizes {
|
||||
if setSize%s == 0 {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
setCounts := possibleSetCounts(commonSize)
|
||||
if len(setCounts) == 0 {
|
||||
err = fmt.Errorf("incorrect number of endpoints provided %s, number of disks %d is not divisible by any supported erasure set sizes %d", args, commonSize, setSizes)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Returns possible set counts with symmetry.
|
||||
setCounts = possibleSetCountsWithSymmetry(setCounts, argPatterns)
|
||||
if len(setCounts) == 0 {
|
||||
err = fmt.Errorf("no symmetric distribution detected with input endpoints provided %s, disks %d cannot be spread symmetrically by any supported erasure set sizes %d", args, commonSize, setSizes)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Final set size with all the symmetry accounted for.
|
||||
setSize := commonSetDriveCount(commonSize, setCounts)
|
||||
|
||||
// Check whether setSize is with the supported range.
|
||||
if !isValidSetSize(setSize) {
|
||||
err = fmt.Errorf("incorrect number of endpoints provided %s, number of disks %d is not divisible by any supported erasure set sizes %d", args, commonSize, setSizes)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range totalSizes {
|
||||
for j := uint64(0); j < totalSizes[i]/setSize; j++ {
|
||||
setIndexes[i] = append(setIndexes[i], setSize)
|
||||
}
|
||||
}
|
||||
|
||||
return setIndexes, nil
|
||||
}
|
||||
|
||||
// Return the total size for each argument patterns.
|
||||
func getTotalSizes(argPatterns []ellipses.ArgPattern) []uint64 {
|
||||
var totalSizes []uint64
|
||||
for _, argPattern := range argPatterns {
|
||||
var totalSize uint64 = 1
|
||||
for _, p := range argPattern {
|
||||
totalSize = totalSize * uint64(len(p.Seq))
|
||||
}
|
||||
totalSizes = append(totalSizes, totalSize)
|
||||
}
|
||||
return totalSizes
|
||||
}
|
||||
|
||||
// PossibleParityValues returns possible parities for input args,
|
||||
// parties are calculated in uniform manner for one zone or
|
||||
// multiple zones, ensuring that parities returned are common
|
||||
// and applicable across all zones.
|
||||
func PossibleParityValues(args ...string) ([]string, error) {
|
||||
setIndexes, err := parseEndpointSet(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maximumParity := setIndexes[0][0] / 2
|
||||
var parities []string
|
||||
for maximumParity >= 2 {
|
||||
parities = append(parities, fmt.Sprintf("EC:%d", maximumParity))
|
||||
maximumParity--
|
||||
}
|
||||
return parities, nil
|
||||
}
|
||||
|
||||
// Parses all arguments and returns an endpointSet which is a collection
|
||||
// of endpoints following the ellipses pattern, this is what is used
|
||||
// by the object layer for initializing itself.
|
||||
func parseEndpointSet(args ...string) (setIndexes [][]uint64, err error) {
|
||||
var argPatterns = make([]ellipses.ArgPattern, len(args))
|
||||
for i, arg := range args {
|
||||
patterns, err := ellipses.FindEllipsesPatterns(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argPatterns[i] = patterns
|
||||
}
|
||||
|
||||
return getSetIndexes(args, getTotalSizes(argPatterns), argPatterns)
|
||||
}
|
||||
281
pkg/utils/parity_test.go
Normal file
281
pkg/utils/parity_test.go
Normal file
@@ -0,0 +1,281 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// All rights reserved
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/minio/pkg/ellipses"
|
||||
)
|
||||
|
||||
func TestGetDivisibleSize(t *testing.T) {
|
||||
testCases := []struct {
|
||||
totalSizes []uint64
|
||||
result uint64
|
||||
}{{[]uint64{24, 32, 16}, 8},
|
||||
{[]uint64{32, 8, 4}, 4},
|
||||
{[]uint64{8, 8, 8}, 8},
|
||||
{[]uint64{24}, 24},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run("", func(t *testing.T) {
|
||||
gotGCD := getDivisibleSize(testCase.totalSizes)
|
||||
if testCase.result != gotGCD {
|
||||
t.Errorf("Expected %v, got %v", testCase.result, gotGCD)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test tests calculating set indexes.
|
||||
func TestGetSetIndexes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
totalSizes []uint64
|
||||
indexes [][]uint64
|
||||
success bool
|
||||
}{
|
||||
// Invalid inputs.
|
||||
{
|
||||
[]string{"data{1...3}"},
|
||||
[]uint64{3},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"data/controller1/export{1...2}, data/controller2/export{1...4}, data/controller3/export{1...8}"},
|
||||
[]uint64{2, 4, 8},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...17}/export{1...52}"},
|
||||
[]uint64{14144},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Valid inputs.
|
||||
{
|
||||
[]string{"data{1...27}"},
|
||||
[]uint64{27},
|
||||
[][]uint64{{9, 9, 9}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"http://host{1...3}/data{1...180}"},
|
||||
[]uint64{540},
|
||||
[][]uint64{{15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"http://host{1...2}.rack{1...4}/data{1...180}"},
|
||||
[]uint64{1440},
|
||||
[][]uint64{{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"http://host{1...2}/data{1...180}"},
|
||||
[]uint64{360},
|
||||
[][]uint64{{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data/controller1/export{1...4}, data/controller2/export{1...8}, data/controller3/export{1...12}"},
|
||||
[]uint64{4, 8, 12},
|
||||
[][]uint64{{4}, {4, 4}, {4, 4, 4}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...64}"},
|
||||
[]uint64{64},
|
||||
[][]uint64{{16, 16, 16, 16}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...24}"},
|
||||
[]uint64{24},
|
||||
[][]uint64{{12, 12}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data/controller{1...11}/export{1...8}"},
|
||||
[]uint64{88},
|
||||
[][]uint64{{11, 11, 11, 11, 11, 11, 11, 11}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...4}"},
|
||||
[]uint64{4},
|
||||
[][]uint64{{4}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data/controller1/export{1...10}, data/controller2/export{1...10}, data/controller3/export{1...10}"},
|
||||
[]uint64{10, 10, 10},
|
||||
[][]uint64{{10}, {10}, {10}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...16}/export{1...52}"},
|
||||
[]uint64{832},
|
||||
[][]uint64{{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run("", func(t *testing.T) {
|
||||
var argPatterns = make([]ellipses.ArgPattern, len(testCase.args))
|
||||
for i, arg := range testCase.args {
|
||||
patterns, err := ellipses.FindEllipsesPatterns(arg)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure %s", err)
|
||||
}
|
||||
argPatterns[i] = patterns
|
||||
}
|
||||
gotIndexes, err := getSetIndexes(testCase.args, testCase.totalSizes, argPatterns)
|
||||
if err != nil && testCase.success {
|
||||
t.Errorf("Expected success but failed instead %s", err)
|
||||
}
|
||||
if err == nil && !testCase.success {
|
||||
t.Errorf("Expected failure but passed instead")
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.indexes, gotIndexes) {
|
||||
t.Errorf("Expected %v, got %v", testCase.indexes, gotIndexes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test tests possible parities returned for any input args
|
||||
func TestPossibleParities(t *testing.T) {
|
||||
testCases := []struct {
|
||||
arg string
|
||||
parities []string
|
||||
success bool
|
||||
}{
|
||||
// Tests invalid inputs.
|
||||
{
|
||||
"...",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// No range specified.
|
||||
{
|
||||
"{...}",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Invalid range.
|
||||
{
|
||||
"http://minio{2...3}/export/set{1...0}",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Range cannot be smaller than 4 minimum.
|
||||
{
|
||||
"/export{1..2}",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Unsupported characters.
|
||||
{
|
||||
"/export/test{1...2O}",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Tests valid inputs.
|
||||
{
|
||||
"{1...27}",
|
||||
[]string{"EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"/export/set{1...64}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// Valid input for distributed setup.
|
||||
{
|
||||
"http://minio{2...3}/export/set{1...64}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// Supporting some advanced cases.
|
||||
{
|
||||
"http://minio{1...64}.mydomain.net/data",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"http://rack{1...4}.mydomain.minio{1...16}/data",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// Supporting kubernetes cases.
|
||||
{
|
||||
"http://minio{0...15}.mydomain.net/data{0...1}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// No host regex, just disks.
|
||||
{
|
||||
"http://server1/data{1...32}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// No host regex, just disks with two position numerics.
|
||||
{
|
||||
"http://server1/data{01...32}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// More than 2 ellipses are supported as well.
|
||||
{
|
||||
"http://minio{2...3}/export/set{1...64}/test{1...2}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// More than 1 ellipses per argument for standalone setup.
|
||||
{
|
||||
"/export{1...10}/disk{1...10}",
|
||||
[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// IPv6 ellipses with hexadecimal expansion
|
||||
{
|
||||
"http://[2001:3984:3989::{1...a}]/disk{1...10}",
|
||||
[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// IPv6 ellipses with hexadecimal expansion with 3 position numerics.
|
||||
{
|
||||
"http://[2001:3984:3989::{001...00a}]/disk{1...10}",
|
||||
[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run("", func(t *testing.T) {
|
||||
gotPs, err := PossibleParityValues(testCase.arg)
|
||||
if err != nil && testCase.success {
|
||||
t.Errorf("Expected success but failed instead %s", err)
|
||||
}
|
||||
if err == nil && !testCase.success {
|
||||
t.Errorf("Expected failure but passed instead")
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.parities, gotPs) {
|
||||
t.Errorf("Expected %v, got %v", testCase.parities, gotPs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
15
policies/mcsTestUserAddOnly.json
Normal file
15
policies/mcsTestUserAddOnly.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "consoleTestUserAddOnly",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"admin:CreateUser"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": "2012-10-17"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
10
portal-ui/config-overrides.js
Normal file
10
portal-ui/config-overrides.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const rewireReactHotLoader = require('react-app-rewire-hot-loader');
|
||||
|
||||
/* config-overrides.js */
|
||||
module.exports = function override(config, env) {
|
||||
if (env === 'development') {
|
||||
config.resolve.alias['react-dom'] = '@hot-loader/react-dom';
|
||||
}
|
||||
config = rewireReactHotLoader(config, env);
|
||||
return config;
|
||||
};
|
||||
@@ -5,6 +5,7 @@
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.7.4",
|
||||
"@babel/plugin-transform-react-jsx-development": "^7.9.0",
|
||||
"@hot-loader/react-dom": "^16.9.0",
|
||||
"@material-ui/core": "^4.9.12",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@types/history": "^4.7.3",
|
||||
@@ -29,9 +30,13 @@
|
||||
"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",
|
||||
"react-async-hook": "^3.6.1",
|
||||
"react-chartjs-2": "^2.9.0",
|
||||
"react-codemirror2": "^7.1.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-hot-loader": "^4.13.0",
|
||||
"react-moment": "^0.9.7",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-router-dom": "^5.1.2",
|
||||
@@ -42,10 +47,11 @@
|
||||
"superagent": "^5.1.0",
|
||||
"typeface-roboto": "^0.0.75",
|
||||
"typescript": "3.6.4",
|
||||
"use-debounce": "^5.0.1",
|
||||
"websocket": "^1.0.31"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "PORT=5000 react-scripts start",
|
||||
"start": "PORT=5000 react-app-rewired start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
|
||||
67
portal-ui/public/images/BG_IllustrationDarker.svg
Normal file
67
portal-ui/public/images/BG_IllustrationDarker.svg
Normal file
@@ -0,0 +1,67 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 368.999 192.934">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1{opacity:0.35;}.cls-12,.cls-15,.cls-16,.cls-17,.cls-2,.cls-5,.cls-6,.cls-7,.cls-8{opacity:0.5;}.cls-10,.cls-11,.cls-12,.cls-13,.cls-14,.cls-15,.cls-16,.cls-17,.cls-3,.cls-4,.cls-5,.cls-6,.cls-7,.cls-9{fill:none;stroke:#707070;stroke-miterlimit:10;}.cls-4{stroke-width:1px;}.cls-10,.cls-11,.cls-5,.cls-9{stroke-width:1.2px;}.cls-5{stroke-dasharray:2.619
|
||||
2.182;}.cls-12,.cls-15,.cls-16,.cls-17,.cls-5,.cls-6,.cls-7,.cls-8{isolation:isolate;}.cls-6{stroke-width:1.6px;stroke-dasharray:2.144
|
||||
1.786;}.cls-7{stroke-width:1.6px;stroke-dasharray:2.23 1.858;}.cls-10{stroke-dasharray:2.646
|
||||
2.204;}.cls-11{stroke-dasharray:2.585 2.154;}.cls-12{stroke-width:1.8px;stroke-dasharray:2.484
|
||||
2.07;}.cls-13{stroke-dasharray:2.984 2.487;}.cls-14{stroke-dasharray:2.773
|
||||
2.311;}.cls-16{stroke-width:1.8px;}.cls-17{stroke-width:1.8px;}
|
||||
</style>
|
||||
</defs>
|
||||
<title>BG_Illustration</title>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<g id="BG_Illustration" data-name="BG Illustration" class="cls-1">
|
||||
<g id="Group_118" data-name="Group 118" class="cls-2">
|
||||
<path id="Path_56" data-name="Path 56" class="cls-3"
|
||||
d="M211.5,140.678l-52.726,29.078L79.687,126.139V29.652L132.411.571,211.5,44.188Z"/>
|
||||
<path id="Path_58" data-name="Path 58" class="cls-3"
|
||||
d="M158.776,169.756V73.271L211.5,44.193,158.776,73.271,79.688,29.654"/>
|
||||
<path id="Path_59" data-name="Path 59" class="cls-4" d="M84.681,41l69.1,38.11v79.3l-69.1-38.11Z"/>
|
||||
<line id="Line_37" data-name="Line 37" class="cls-4" x1="106.25" y1="52.782" x2="106.25"
|
||||
y2="132.086"/>
|
||||
<line id="Line_38" data-name="Line 38" class="cls-4" x1="153.783" y1="92.327" x2="106.25"
|
||||
y2="65.999"/>
|
||||
<line id="Line_39" data-name="Line 39" class="cls-4" x1="153.783" y1="105.545" x2="106.25"
|
||||
y2="79.217"/>
|
||||
<line id="Line_40" data-name="Line 40" class="cls-4" x1="153.783" y1="118.762" x2="106.25"
|
||||
y2="92.434"/>
|
||||
<line id="Line_41" data-name="Line 41" class="cls-4" x1="153.783" y1="131.979" x2="106.25"
|
||||
y2="105.651"/>
|
||||
<line id="Line_42" data-name="Line 42" class="cls-4" x1="153.783" y1="145.197" x2="106.25"
|
||||
y2="118.869"/>
|
||||
<path id="Path_60" data-name="Path 60" class="cls-4"
|
||||
d="M166.723,151.031l38.8-22.487V62.916L166.723,85.4Z"/>
|
||||
</g>
|
||||
<path id="Path_62" data-name="Path 62" class="cls-5" d="M117.106,148.062l-76.18,43.33"/>
|
||||
<path id="Path_63" data-name="Path 63" class="cls-6" d="M271.394,167.271l-44.483,25.3"/>
|
||||
<path id="Path_64" data-name="Path 64" class="cls-7" d="M190.722,155.708l61.951,36.031"/>
|
||||
<path id="Path_65" data-name="Path 65" class="cls-5" d="M237.7,36.385l28.182,17.229"/>
|
||||
<g id="Path_66" data-name="Path 66" class="cls-8">
|
||||
<line class="cls-9" x1="362.563" y1="69.327" x2="361.42" y2="68.688"/>
|
||||
<line class="cls-10" x1="359.496" y1="67.613" x2="305.418" y2="37.39"/>
|
||||
<polyline class="cls-9" points="304.456 36.852 303.313 36.213 302.158 36.83"/>
|
||||
<line class="cls-11" x1="300.258" y1="37.844" x2="213.418" y2="84.213"/>
|
||||
<line class="cls-9" x1="212.468" y1="84.72" x2="211.313" y2="85.337"/>
|
||||
</g>
|
||||
<path id="Path_67" data-name="Path 67" class="cls-12"
|
||||
d="M79.648,192.571,31.786,166.344h-.868l-23.579,14.2"/>
|
||||
<g id="Path_68" data-name="Path 68" class="cls-8">
|
||||
<line class="cls-3" x1="22.871" y1="84.641" x2="24.156" y2="83.867"/>
|
||||
<line class="cls-13" x1="26.286" y1="82.584" x2="48.654" y2="69.113"/>
|
||||
<polyline class="cls-3" points="49.719 68.471 51.004 67.698 52.307 68.441"/>
|
||||
<line class="cls-14" x1="54.315" y1="69.585" x2="75.395" y2="81.606"/>
|
||||
<line class="cls-3" x1="76.399" y1="82.178" x2="77.702" y2="82.921"/>
|
||||
</g>
|
||||
<circle id="Ellipse_11" data-name="Ellipse 11" class="cls-15" cx="4.092" cy="183.59" r="3.592"/>
|
||||
<circle id="Ellipse_12" data-name="Ellipse 12" class="cls-15" cx="274.986" cy="165.477" r="3.592"/>
|
||||
<ellipse id="Ellipse_13" data-name="Ellipse 13" class="cls-16" cx="364.957" cy="71.922" rx="3.592"
|
||||
ry="2.904"/>
|
||||
<circle id="Ellipse_14" data-name="Ellipse 14" class="cls-15" cx="19.279" cy="87.681" r="3.592"/>
|
||||
<ellipse id="Ellipse_15" data-name="Ellipse 15" class="cls-17" cx="234.106" cy="32.58" rx="3.592"
|
||||
ry="2.649"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -25,6 +25,7 @@ import { connect } from "react-redux";
|
||||
import { AppState } from "./store";
|
||||
import { userLoggedIn } from "./actions";
|
||||
import LoginCallback from "./screens/LoginPage/LoginCallback";
|
||||
import { hot } from "react-hot-loader/root";
|
||||
|
||||
const isLoggedIn = () => {
|
||||
return (
|
||||
@@ -75,4 +76,4 @@ class Routes extends React.Component<RoutesProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export default connector(Routes);
|
||||
export default hot(connector(Routes));
|
||||
|
||||
333
portal-ui/src/common/types.ts
Normal file
333
portal-ui/src/common/types.ts
Normal file
@@ -0,0 +1,333 @@
|
||||
// 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/>.
|
||||
|
||||
/* Copyright (c) 2020 MinIO, Inc. All rights reserved. */
|
||||
|
||||
export interface ITenantsObject {
|
||||
tenants: ITenant[];
|
||||
}
|
||||
|
||||
export interface ITenant {
|
||||
creation_date: string;
|
||||
deletion_date: string;
|
||||
currentState: string;
|
||||
image: string;
|
||||
console_image: string;
|
||||
instance_count: string;
|
||||
name: string;
|
||||
namespace?: string;
|
||||
total_size: string;
|
||||
used_size: string;
|
||||
volume_count: string;
|
||||
volume_size: string;
|
||||
volumes_per_server?: string;
|
||||
zone_count: string;
|
||||
zones?: IZoneModel[];
|
||||
used_capacity?: string;
|
||||
endpoint?: string;
|
||||
storage_class?: string;
|
||||
enable_prometheus: boolean;
|
||||
}
|
||||
|
||||
export interface IVolumeConfiguration {
|
||||
size: string;
|
||||
storage_class_name: string;
|
||||
labels?: any;
|
||||
}
|
||||
|
||||
export interface ITenantCreator {
|
||||
name: string;
|
||||
service_name: string;
|
||||
enable_console: boolean;
|
||||
enable_prometheus: boolean;
|
||||
enable_tls: boolean;
|
||||
access_key: string;
|
||||
secret_key: string;
|
||||
image: string;
|
||||
console_image: string;
|
||||
zones: IZoneModel[];
|
||||
namespace: string;
|
||||
erasureCodingParity: number;
|
||||
tls?: ITLSTenantConfiguration;
|
||||
encryption?: IEncryptionConfiguration;
|
||||
idp?: IIDPConfiguration;
|
||||
annotations?: Object;
|
||||
}
|
||||
|
||||
export interface ITenantUpdateObject {
|
||||
image: string;
|
||||
image_registry?: IRegistryObject;
|
||||
}
|
||||
|
||||
export interface IRegistryObject {
|
||||
registry: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ITenantUsage {
|
||||
used: string;
|
||||
disk_used: string;
|
||||
}
|
||||
|
||||
export interface IAffinityModel {
|
||||
podAntiAffinity: IPodAntiAffinityModel;
|
||||
}
|
||||
|
||||
export interface IPodAntiAffinityModel {
|
||||
requiredDuringSchedulingIgnoredDuringExecution: IPodAffinityTerm[];
|
||||
}
|
||||
|
||||
export interface IPodAffinityTerm {
|
||||
labelSelector: IPodAffinityTermLabelSelector;
|
||||
topologyKey: string;
|
||||
}
|
||||
|
||||
export interface IPodAffinityTermLabelSelector {
|
||||
matchExpressions: IMatchExpressionItem[];
|
||||
}
|
||||
|
||||
export interface IMatchExpressionItem {
|
||||
key: string;
|
||||
operator: string;
|
||||
values: string[];
|
||||
}
|
||||
|
||||
export interface ITolerationModel {
|
||||
effect: string;
|
||||
key: string;
|
||||
operator: string;
|
||||
value?: string;
|
||||
tolerationSeconds?: ITolerationSeconds;
|
||||
}
|
||||
|
||||
export interface ITolerationSeconds {
|
||||
seconds: number;
|
||||
}
|
||||
|
||||
export interface IResourceModel {
|
||||
requests: IResourceRequests;
|
||||
limits?: IResourceLimits;
|
||||
}
|
||||
|
||||
export interface IResourceRequests {
|
||||
memory: number;
|
||||
}
|
||||
|
||||
export interface IResourceLimits {
|
||||
memory: number;
|
||||
}
|
||||
|
||||
export interface ITLSTenantConfiguration {
|
||||
minio: ITLSConfiguration;
|
||||
console: ITLSConfiguration;
|
||||
}
|
||||
|
||||
export interface ITLSConfiguration {
|
||||
crt: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface IEncryptionConfiguration {
|
||||
server: ITLSConfiguration;
|
||||
client: ITLSConfiguration;
|
||||
master_key?: string;
|
||||
gemalto?: IGemaltoConfig;
|
||||
aws?: IAWSConfig;
|
||||
vault?: IVaultConfig;
|
||||
}
|
||||
|
||||
export interface IVaultConfig {
|
||||
endpoint: string;
|
||||
engine?: string;
|
||||
namespace?: string;
|
||||
prefix?: string;
|
||||
approle: IApproleConfig;
|
||||
tls: IVaultTLSConfig;
|
||||
status: IVaultStatusConfig;
|
||||
}
|
||||
|
||||
export interface IGemaltoConfig {
|
||||
keysecure: IKeysecureConfig;
|
||||
}
|
||||
|
||||
export interface IAWSConfig {
|
||||
secretsmanager: ISecretsManagerConfig;
|
||||
}
|
||||
|
||||
export interface IApproleConfig {
|
||||
engine: string;
|
||||
id: string;
|
||||
secret: string;
|
||||
retry: number;
|
||||
}
|
||||
|
||||
export interface IVaultTLSConfig {
|
||||
key: string;
|
||||
crt: string;
|
||||
ca: string;
|
||||
}
|
||||
|
||||
export interface IVaultStatusConfig {
|
||||
ping: number;
|
||||
}
|
||||
|
||||
export interface IKeysecureConfig {
|
||||
endpoint: string;
|
||||
credentials: IGemaltoCredentials;
|
||||
tls: IGemaltoTLS;
|
||||
}
|
||||
|
||||
export interface IGemaltoCredentials {
|
||||
token: string;
|
||||
domain: string;
|
||||
retry?: number;
|
||||
}
|
||||
|
||||
export interface IGemaltoTLS {
|
||||
ca: string;
|
||||
}
|
||||
|
||||
export interface ISecretsManagerConfig {
|
||||
endpoint: string;
|
||||
region: string;
|
||||
kmskey?: string;
|
||||
credentials: IAWSCredentials;
|
||||
}
|
||||
|
||||
export interface IAWSCredentials {
|
||||
accesskey: string;
|
||||
secretkey: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
export interface IIDPConfiguration {
|
||||
oidc?: IOpenIDConfiguration;
|
||||
active_directory: IActiveDirectoryConfiguration;
|
||||
}
|
||||
|
||||
export interface IOpenIDConfiguration {
|
||||
url: string;
|
||||
client_id: string;
|
||||
secret_id: string;
|
||||
}
|
||||
|
||||
export interface IActiveDirectoryConfiguration {
|
||||
url: string;
|
||||
skip_tls_verification: boolean;
|
||||
server_insecure: boolean;
|
||||
user_search_filter: string;
|
||||
group_Search_base_dn: string;
|
||||
group_search_filter: string;
|
||||
group_name_attribute: string;
|
||||
}
|
||||
|
||||
export interface IStorageDistribution {
|
||||
error: number;
|
||||
nodes: number;
|
||||
persistentVolumes: number;
|
||||
disks: number;
|
||||
pvSize: number;
|
||||
}
|
||||
|
||||
export interface IErasureCodeCalc {
|
||||
error: number;
|
||||
maxEC: string;
|
||||
erasureCodeSet: number;
|
||||
rawCapacity: string;
|
||||
storageFactors: IStorageFactors[];
|
||||
defaultEC: string;
|
||||
}
|
||||
|
||||
export interface IStorageFactors {
|
||||
erasureCode: string;
|
||||
storageFactor: number;
|
||||
maxCapacity: string;
|
||||
}
|
||||
|
||||
export interface ITenantHealthInList {
|
||||
name: string;
|
||||
namespace: string;
|
||||
status?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface ITenantsListHealthRequest {
|
||||
tenants: ITenantHealthInList[];
|
||||
}
|
||||
|
||||
export interface IMaxAllocatableMemoryRequest {
|
||||
num_nodes: number;
|
||||
}
|
||||
|
||||
export interface IMaxAllocatableMemoryResponse {
|
||||
max_memory: number;
|
||||
}
|
||||
|
||||
export interface IEncryptionUpdateRequest {
|
||||
encryption: IEncryptionConfiguration;
|
||||
}
|
||||
|
||||
export interface IArchivedTenantsList {
|
||||
tenants: IArchivedTenant[];
|
||||
}
|
||||
|
||||
export interface IArchivedTenant {
|
||||
namespace: string;
|
||||
tenant: string;
|
||||
number_volumes: number;
|
||||
capacity: number;
|
||||
}
|
||||
|
||||
export interface IZoneModel {
|
||||
name?: string;
|
||||
servers: number;
|
||||
volumes_per_server: number;
|
||||
volume_configuration: IVolumeConfiguration;
|
||||
affinity?: IAffinityModel;
|
||||
tolerations?: ITolerationModel[];
|
||||
resources?: IResourceModel;
|
||||
}
|
||||
|
||||
export interface IUpdateZone {
|
||||
zones: IZoneModel[];
|
||||
}
|
||||
|
||||
export interface INode {
|
||||
name: string;
|
||||
freeSpace: string;
|
||||
totalSpace: string;
|
||||
disks: IDisk[];
|
||||
}
|
||||
|
||||
export interface IStorageType {
|
||||
freeSpace: string;
|
||||
totalSpace: string;
|
||||
storageClasses: string[];
|
||||
nodes: INode[];
|
||||
schedulableNodes: INode[];
|
||||
}
|
||||
|
||||
export interface IDisk {
|
||||
name: string;
|
||||
freeSpace: string;
|
||||
totalSpace: string;
|
||||
}
|
||||
|
||||
export interface ICapacity {
|
||||
value: string;
|
||||
unit: string;
|
||||
}
|
||||
@@ -15,6 +15,10 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import storage from "local-storage-fallback";
|
||||
import { ICapacity, IStorageType, IZoneModel } from "./types";
|
||||
|
||||
const minStReq = 1073741824; // Minimal Space required for MinIO
|
||||
const minMemReq = 2147483648; // Minimal Memory required for MinIO in bytes
|
||||
|
||||
export const units = [
|
||||
"B",
|
||||
@@ -28,6 +32,8 @@ export const units = [
|
||||
"YiB",
|
||||
];
|
||||
export const k8sUnits = ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei"];
|
||||
export const k8sCalcUnits = ["B", ...k8sUnits];
|
||||
|
||||
export const niceBytes = (x: string) => {
|
||||
let l = 0,
|
||||
n = parseInt(x, 10) || 0;
|
||||
@@ -90,12 +96,19 @@ export const k8sfactorForDropdown = () => {
|
||||
};
|
||||
|
||||
//getBytes, converts from a value and a unit from units array to bytes
|
||||
export const getBytes = (value: string, unit: string) => {
|
||||
export const getBytes = (
|
||||
value: string,
|
||||
unit: string,
|
||||
fork8s: boolean = false
|
||||
) => {
|
||||
const vl: number = parseFloat(value);
|
||||
const powFactor = units.findIndex((element) => element === unit);
|
||||
|
||||
if (powFactor == -1) {
|
||||
return 0;
|
||||
const unitsTake = fork8s ? k8sCalcUnits : units;
|
||||
|
||||
const powFactor = unitsTake.findIndex((element) => element === unit);
|
||||
|
||||
if (powFactor === -1) {
|
||||
return "0";
|
||||
}
|
||||
const factor = Math.pow(1024, powFactor);
|
||||
const total = vl * factor;
|
||||
@@ -105,6 +118,220 @@ export const getBytes = (value: string, unit: string) => {
|
||||
|
||||
//getTotalSize gets the total size of a value & unit
|
||||
export const getTotalSize = (value: string, unit: string) => {
|
||||
const bytes = getBytes(value, unit).toString(10);
|
||||
const bytes = getBytes(value, unit, true).toString();
|
||||
return niceBytes(bytes);
|
||||
};
|
||||
|
||||
export const setMemoryResource = (
|
||||
memorySize: number,
|
||||
capacitySize: string,
|
||||
maxMemorySize: number
|
||||
) => {
|
||||
// value always comes as Gi
|
||||
const requestedSizeBytes = getBytes(memorySize.toString(10), "Gi", true);
|
||||
const memReqSize = parseInt(requestedSizeBytes, 10);
|
||||
if (maxMemorySize === 0) {
|
||||
return {
|
||||
error: "There is no memory available for the selected number of nodes",
|
||||
request: 0,
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (maxMemorySize < minMemReq) {
|
||||
return {
|
||||
error: "There are not enough memory resources available",
|
||||
request: 0,
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (memReqSize < minMemReq) {
|
||||
return {
|
||||
error: "The requested memory size must be greater than 2Gi",
|
||||
request: 0,
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
if (memReqSize > maxMemorySize) {
|
||||
return {
|
||||
error:
|
||||
"The requested memory is greater than the max available memory for the selected number of nodes",
|
||||
request: 0,
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const capSize = parseInt(capacitySize, 10);
|
||||
let memLimitSize = memReqSize;
|
||||
// set memory limit based on the capacitySize
|
||||
// if capacity size is lower than 1TiB we use the limit equal to request
|
||||
if (capSize >= parseInt(getBytes("1", "Pi", true), 10)) {
|
||||
memLimitSize = Math.max(
|
||||
memReqSize,
|
||||
parseInt(getBytes("64", "Gi", true), 10)
|
||||
);
|
||||
} else if (capSize >= parseInt(getBytes("100", "Ti"), 10)) {
|
||||
memLimitSize = Math.max(
|
||||
memReqSize,
|
||||
parseInt(getBytes("32", "Gi", true), 10)
|
||||
);
|
||||
} else if (capSize >= parseInt(getBytes("10", "Ti"), 10)) {
|
||||
memLimitSize = Math.max(
|
||||
memReqSize,
|
||||
parseInt(getBytes("16", "Gi", true), 10)
|
||||
);
|
||||
} else if (capSize >= parseInt(getBytes("1", "Ti"), 10)) {
|
||||
memLimitSize = Math.max(
|
||||
memReqSize,
|
||||
parseInt(getBytes("8", "Gi", true), 10)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
error: "",
|
||||
request: memReqSize,
|
||||
limit: memLimitSize,
|
||||
};
|
||||
};
|
||||
|
||||
export const calculateDistribution = (
|
||||
capacityToUse: ICapacity,
|
||||
forcedNodes: number = 0,
|
||||
limitSize: number = 0
|
||||
) => {
|
||||
let numberOfNodes = {};
|
||||
const requestedSizeBytes = getBytes(
|
||||
capacityToUse.value,
|
||||
capacityToUse.unit,
|
||||
true
|
||||
);
|
||||
|
||||
if (parseInt(requestedSizeBytes, 10) < minStReq) {
|
||||
return {
|
||||
error: "The zone size must be greater than 1Gi",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
pvSize: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (forcedNodes < 4) {
|
||||
return {
|
||||
error: "Number of nodes cannot be less than 4",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
pvSize: 0,
|
||||
};
|
||||
}
|
||||
|
||||
numberOfNodes = calculateStorage(requestedSizeBytes, forcedNodes, limitSize);
|
||||
|
||||
return numberOfNodes;
|
||||
};
|
||||
|
||||
const calculateStorage = (
|
||||
requestedBytes: string,
|
||||
forcedNodes: number,
|
||||
limitSize: number
|
||||
) => {
|
||||
// Size validation
|
||||
const intReqBytes = parseInt(requestedBytes, 10);
|
||||
const maxDiskSize = minStReq * 256; // 256 GiB
|
||||
|
||||
// We get the distribution
|
||||
return structureCalc(forcedNodes, intReqBytes, maxDiskSize, limitSize);
|
||||
};
|
||||
|
||||
const structureCalc = (
|
||||
nodes: number,
|
||||
desiredCapacity: number,
|
||||
maxDiskSize: number,
|
||||
maxClusterSize: number,
|
||||
disksPerNode: number = 0
|
||||
) => {
|
||||
if (
|
||||
isNaN(nodes) ||
|
||||
isNaN(desiredCapacity) ||
|
||||
isNaN(maxDiskSize) ||
|
||||
isNaN(maxClusterSize)
|
||||
) {
|
||||
return {
|
||||
error: "Some provided data is invalid, please try again.",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
volumePerDisk: 0,
|
||||
}; // Invalid Data
|
||||
}
|
||||
|
||||
let persistentVolumeSize = 0;
|
||||
let numberPersistentVolumes = 0;
|
||||
let volumesPerServer = 0;
|
||||
|
||||
if (disksPerNode === 0) {
|
||||
persistentVolumeSize = Math.floor(
|
||||
Math.min(desiredCapacity / Math.max(4, nodes), maxDiskSize)
|
||||
); // pVS = min((desiredCapacity / max(4 | nodes)) | maxDiskSize)
|
||||
|
||||
numberPersistentVolumes = desiredCapacity / persistentVolumeSize; // nPV = dC / pVS
|
||||
volumesPerServer = numberPersistentVolumes / nodes; // vPS = nPV / n
|
||||
}
|
||||
|
||||
if (disksPerNode) {
|
||||
volumesPerServer = disksPerNode;
|
||||
numberPersistentVolumes = volumesPerServer * nodes;
|
||||
persistentVolumeSize = Math.floor(
|
||||
desiredCapacity / numberPersistentVolumes
|
||||
);
|
||||
}
|
||||
|
||||
// Volumes are not exact, we force the volumes number & minimize the volume size
|
||||
if (volumesPerServer % 1 > 0) {
|
||||
volumesPerServer = Math.ceil(volumesPerServer); // Increment of volumes per server
|
||||
numberPersistentVolumes = volumesPerServer * nodes; // nPV = vPS * n
|
||||
persistentVolumeSize = Math.floor(
|
||||
desiredCapacity / numberPersistentVolumes
|
||||
); // pVS = dC / nPV
|
||||
|
||||
const limitSize = persistentVolumeSize * volumesPerServer * nodes; // lS = pVS * vPS * n
|
||||
|
||||
if (limitSize > maxClusterSize) {
|
||||
return {
|
||||
error: "We were not able to allocate this server.",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
volumePerDisk: 0,
|
||||
}; // Cannot allocate this server
|
||||
}
|
||||
}
|
||||
|
||||
if (persistentVolumeSize < minStReq) {
|
||||
return {
|
||||
error:
|
||||
"Disk Size with this combination would be less than 1Gi, please try another combination",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
volumePerDisk: 0,
|
||||
}; // Cannot allocate this volume size
|
||||
}
|
||||
|
||||
return {
|
||||
error: "",
|
||||
nodes,
|
||||
persistentVolumes: numberPersistentVolumes,
|
||||
disks: volumesPerServer,
|
||||
pvSize: persistentVolumeSize,
|
||||
};
|
||||
};
|
||||
|
||||
// Zone Name Generator
|
||||
export const generateZoneName = (zones: IZoneModel[]) => {
|
||||
const zoneCounter = zones.length;
|
||||
|
||||
return `zone-${zoneCounter}`;
|
||||
};
|
||||
|
||||
75
portal-ui/src/icons/AllBucketsIcon.tsx
Normal file
75
portal-ui/src/icons/AllBucketsIcon.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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 AllBucketsIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon viewBox="0 0 15.834 17.375">
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="a"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5"
|
||||
gradientUnits="objectBoundingBox"
|
||||
>
|
||||
<stop offset="0.044" stopColor="#362585" />
|
||||
<stop offset="0.301" stopColor="#281b6f" />
|
||||
<stop offset="1" stopColor="#1e1560" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="translate(0 0.375)">
|
||||
<circle
|
||||
style={{ opacity: 0.1, fill: "url(#a)" }}
|
||||
cx="6.625"
|
||||
cy="6.625"
|
||||
r="6.625"
|
||||
transform="translate(0 3.75)"
|
||||
/>
|
||||
<g transform="translate(3.092)">
|
||||
<ellipse
|
||||
style={{
|
||||
fill: "none",
|
||||
stroke: "#707070",
|
||||
strokeMiterlimit: 10,
|
||||
strokeWidth: "0.75px",
|
||||
}}
|
||||
cx="6.183"
|
||||
cy="1.244"
|
||||
rx="6.183"
|
||||
ry="1.244"
|
||||
transform="translate(0)"
|
||||
/>
|
||||
<path
|
||||
style={{
|
||||
fill: "none",
|
||||
stroke: "#707070",
|
||||
strokeMiterlimit: 10,
|
||||
strokeWidth: "0.75px",
|
||||
}}
|
||||
d="M-3722.174,1225.225l-1.687,10.292a.858.858,0,0,1-.578.669,12.182,12.182,0,0,1-3.918.647,12.187,12.187,0,0,1-3.894-.639.878.878,0,0,1-.6-.678q-.843-5.145-1.687-10.291"
|
||||
transform="translate(3734.541 -1223.981)"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AllBucketsIcon;
|
||||
37
portal-ui/src/icons/ConsoleIcon.tsx
Normal file
37
portal-ui/src/icons/ConsoleIcon.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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 ConsoleIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
|
||||
<g transform="translate(-518 -361)">
|
||||
<path
|
||||
d="M-126,0V10h10V0Zm1.5,8.5V2.95h7V8.5Z"
|
||||
transform="translate(644 361)"
|
||||
/>
|
||||
<rect width="2" height="1" transform="translate(520.272 364.772)" />
|
||||
</g>
|
||||
</svg>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ConsoleIcon;
|
||||
73
portal-ui/src/icons/EgressIcon.tsx
Normal file
73
portal-ui/src/icons/EgressIcon.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
// 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 EgressIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon viewBox="0 0 18.344 17.009">
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="a"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5"
|
||||
gradientUnits="objectBoundingBox"
|
||||
>
|
||||
<stop offset="0.044" stopColor="#362585" />
|
||||
<stop offset="0.301" stopColor="#281b6f" />
|
||||
<stop offset="1" stopColor="#1e1560" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="translate(0 0.25)">
|
||||
<ellipse
|
||||
style={{ opacity: 0.1, fill: "url(#a)" }}
|
||||
cx="7.462"
|
||||
cy="7.462"
|
||||
rx="7.462"
|
||||
ry="7.462"
|
||||
transform="translate(0 1.835)"
|
||||
/>
|
||||
<rect
|
||||
style={{
|
||||
fill: "none",
|
||||
stroke: "#707070",
|
||||
strokeMiterlimit: 10,
|
||||
strokeWidth: "0.5px",
|
||||
}}
|
||||
width="9.323"
|
||||
height="9.323"
|
||||
transform="translate(4.083)"
|
||||
/>
|
||||
<rect
|
||||
style={{
|
||||
fill: "none",
|
||||
stroke: "#707070",
|
||||
strokeMiterlimit: 10,
|
||||
strokeWidth: "0.5px",
|
||||
}}
|
||||
width="8.223"
|
||||
height="8.223"
|
||||
transform="translate(9.871 5.307)"
|
||||
/>
|
||||
</g>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default EgressIcon;
|
||||
53
portal-ui/src/icons/HealIcon.tsx
Normal file
53
portal-ui/src/icons/HealIcon.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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 HealIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.014 9.993">
|
||||
<path
|
||||
className="a"
|
||||
d="M9.162,5.971h0L8.192,5,9.346,3.846a2.257,2.257,0,0,0,0-3.192,2.311,2.311,0,0,0-3.192,0L5,1.808,4.029.837,3.846.654a2.311,2.311,0,0,0-3.192,0,2.257,2.257,0,0,0,0,3.192l.184.183h0L1.808,5,.654,6.154A2.257,2.257,0,0,0,3.846,9.346L5,8.192l.971.971.183.183A2.257,2.257,0,0,0,9.346,6.154Zm-2.29-4.6a1.27,1.27,0,0,1,1.757,0,1.242,1.242,0,0,1,0,1.757L7.475,4.283,5.717,2.525Zm-5.5,1.757A1.243,1.243,0,0,1,3.129,1.371l.183.183L1.555,3.312Zm1.757,5.5a1.27,1.27,0,0,1-1.757,0,1.242,1.242,0,0,1,0-1.757L2.525,5.717,4.283,7.475Zm2.843-.9-.254-.253L2.525,4.283l-.253-.254L4.029,2.272l.254.253L7.475,5.717l.253.254Zm2.657.9a1.271,1.271,0,0,1-1.757,0l-.183-.183L8.446,6.688l.183.183h0a1.241,1.241,0,0,1,0,1.757Z"
|
||||
transform="translate(0.007 -0.014)"
|
||||
/>
|
||||
<circle
|
||||
cx="0.5"
|
||||
cy="0.5"
|
||||
r="0.5"
|
||||
transform="translate(4.507 4.486)"
|
||||
/>
|
||||
<circle
|
||||
cx="0.5"
|
||||
cy="0.5"
|
||||
r="0.5"
|
||||
transform="translate(3.507 3.486)"
|
||||
/>
|
||||
<circle
|
||||
cx="0.5"
|
||||
cy="0.5"
|
||||
r="0.5"
|
||||
transform="translate(5.507 5.486)"
|
||||
/>
|
||||
</svg>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HealIcon;
|
||||
47
portal-ui/src/icons/LicenseIcon.tsx
Normal file
47
portal-ui/src/icons/LicenseIcon.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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 LicenseIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 11">
|
||||
<path fill="#fff" d="M11 11H0V2h11v9zM2 8v1h7V8zm0-3v1h5V5z"></path>
|
||||
<g
|
||||
fill="#07274a"
|
||||
stroke="#fdfdfd"
|
||||
strokeWidth="0.5"
|
||||
transform="translate(7)"
|
||||
>
|
||||
<circle cx="3" cy="3" r="3" stroke="none"></circle>
|
||||
<circle cx="3" cy="3" r="2.75" fill="none"></circle>
|
||||
</g>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
strokeWidth="0.5"
|
||||
d="M8.73 2.794l.954.953 1.471-1.471"
|
||||
></path>
|
||||
</svg>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default LicenseIcon;
|
||||
47
portal-ui/src/icons/LogoutIcon.tsx
Normal file
47
portal-ui/src/icons/LogoutIcon.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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 LogoutIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12.122 10.571">
|
||||
<g transform="translate(0 0.5)">
|
||||
<path
|
||||
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
|
||||
d="M4816.27,3755.205v-2.939h8.539v9.571h-8.539v-2.932"
|
||||
transform="translate(-4813.187 -3752.266)"
|
||||
/>
|
||||
<path
|
||||
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
|
||||
d="M4813.187,3757.052h8.081"
|
||||
transform="translate(-4813.187 -3752.266)"
|
||||
/>
|
||||
<path
|
||||
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
|
||||
d="M4806.5,3756.511l2.265,2.063-2.265,2.063"
|
||||
transform="translate(-4800.808 -3753.863)"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default LogoutIcon;
|
||||
64
portal-ui/src/icons/UsageIcon.tsx
Normal file
64
portal-ui/src/icons/UsageIcon.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 UsageIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<SvgIcon viewBox="0 0 16.172 17.187">
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="a"
|
||||
y1="0.5"
|
||||
x2="1"
|
||||
y2="0.5"
|
||||
gradientUnits="objectBoundingBox"
|
||||
>
|
||||
<stop offset="0.044" stopColor="#362585" />
|
||||
<stop offset="0.301" stopColor="#281b6f" />
|
||||
<stop offset="1" stopColor="#1e1560" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
style={{
|
||||
fill: "none",
|
||||
stroke: "#707070",
|
||||
strokeMiterlimit: 10,
|
||||
strokeWidth: "0.5px",
|
||||
}}
|
||||
d="M-4778.1,2239.582v6.425h6.425"
|
||||
transform="translate(4787.594 -2239.582)"
|
||||
/>
|
||||
<path
|
||||
fill={"#707070"}
|
||||
d="M-4784.238,2247.532v-.581c0-.027.009-.054.012-.081.039-.313.055-.632.121-.939a6.744,6.744,0,0,1,3.064-4.441,6.514,6.514,0,0,1,3.293-1.032,6.923,6.923,0,0,1,2.667.423,6.793,6.793,0,0,1,4.119,4.333,6.053,6.053,0,0,1,.279,1.337c.006.083.014.164.021.247v.86c-.011.131-.018.261-.032.392a6.494,6.494,0,0,1-.626,2.147,6.807,6.807,0,0,1-4.044,3.528,6.052,6.052,0,0,1-1.663.3,6.576,6.576,0,0,1-2.565-.325,6.73,6.73,0,0,1-3.947-3.451,6.627,6.627,0,0,1-.658-2.288C-4784.212,2247.816-4784.225,2247.674-4784.238,2247.532Zm13.025-.306c-.024-.309-.021-.661-.082-1a6.206,6.206,0,0,0-1.658-3.293,6.153,6.153,0,0,0-4.1-1.9,5.984,5.984,0,0,0-2.476.355,6.188,6.188,0,0,0-4.134,5.708,6.453,6.453,0,0,0,.228,1.881,6.127,6.127,0,0,0,1.984,3.052,6.046,6.046,0,0,0,3.806,1.445,6.043,6.043,0,0,0,1.235-.065,6.249,6.249,0,0,0,3.783-2.2,6.2,6.2,0,0,0,1.352-3.048C-4771.228,2247.863-4771.233,2247.563-4771.212,2247.226Z"
|
||||
transform="translate(4786.834 -2240.452)"
|
||||
/>
|
||||
<ellipse
|
||||
style={{ opacity: 0.1, fill: "url(#a)" }}
|
||||
cx="6.151"
|
||||
cy="6.151"
|
||||
rx="6.151"
|
||||
ry="6.151"
|
||||
transform="translate(0 4.886)"
|
||||
/>
|
||||
</SvgIcon>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UsageIcon;
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 81.879 23.119"><defs><style>.cls-1{fill:#1b1556;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M10.086,13.4,8.969,14.573a4.2,4.2,0,0,0-3.01-1.279,3.886,3.886,0,0,0-3.884,4.093,3.888,3.888,0,0,0,3.884,4.1A4.4,4.4,0,0,0,9,20.2l1.082,1.186A5.344,5.344,0,0,1,6,23.119a5.512,5.512,0,0,1-5.7-5.732A5.509,5.509,0,0,1,6,11.667,5.328,5.328,0,0,1,10.086,13.4Z"/><path class="cls-1" d="M23.608,17.387a5.637,5.637,0,0,1-5.8,5.732,5.628,5.628,0,0,1-5.791-5.732,5.626,5.626,0,0,1,5.791-5.72A5.635,5.635,0,0,1,23.608,17.387Zm-9.825,0a4.024,4.024,0,1,0,8.046,0,4.024,4.024,0,1,0-8.046,0Z"/><path class="cls-1" d="M36.862,16.073V22.9H35.129V16.643c0-2.093-1.163-3.325-3.174-3.325a3.24,3.24,0,0,0-3.371,3.372V22.9H26.839V11.888H28.56v1.569a4.354,4.354,0,0,1,3.709-1.79A4.261,4.261,0,0,1,36.862,16.073Z"/><path class="cls-1" d="M48.2,14.225a6.872,6.872,0,0,0-3.605-1.035c-1.569,0-2.6.663-2.6,1.732,0,.919.8,1.372,2.244,1.547l1.3.163c2.338.3,3.7,1.267,3.7,3.069,0,2.093-1.884,3.407-4.849,3.407A7.725,7.725,0,0,1,39.791,21.7l.8-1.3a5.8,5.8,0,0,0,3.815,1.2c1.86,0,3.034-.616,3.034-1.778,0-.884-.744-1.419-2.3-1.605l-1.314-.151c-2.477-.3-3.639-1.408-3.639-3.046,0-2.082,1.755-3.338,4.4-3.338a8.067,8.067,0,0,1,4.372,1.2Z"/><path class="cls-1" d="M63.033,17.387a5.8,5.8,0,0,1-11.593,0,5.8,5.8,0,0,1,11.593,0Zm-9.825,0a4.023,4.023,0,1,0,8.045,0,4.023,4.023,0,1,0-8.045,0Z"/><path class="cls-1" d="M68.008,22.9H66.264V6.155h1.744Z"/><path class="cls-1" d="M81.879,17.353a5.606,5.606,0,0,1-.035.65H73.019a3.743,3.743,0,0,0,3.9,3.593A5.1,5.1,0,0,0,80.4,20.213l.931,1.186a6.179,6.179,0,0,1-4.524,1.72A5.394,5.394,0,0,1,71.24,17.4a5.406,5.406,0,0,1,5.465-5.732C79.693,11.667,81.856,14,81.879,17.353ZM73.043,16.6h7.069a3.446,3.446,0,0,0-3.442-3.384A3.59,3.59,0,0,0,73.043,16.6Z"/><rect class="cls-1" x="13.484" y="0.12" width="2.328" height="6.875"/><path class="cls-1" d="M10.662.215,5.936,3.1a.21.21,0,0,1-.219,0L.992.215A.651.651,0,0,0,.654.12H.648A.648.648,0,0,0,0,.768v6.22H2.327V4.028a.233.233,0,0,1,.354-.2l2.648,1.62a.829.829,0,0,0,.853.008l2.8-1.639a.232.232,0,0,1,.35.2V6.988h2.327V.768A.648.648,0,0,0,11.006.12H11A.651.651,0,0,0,10.662.215Z"/><path class="cls-1" d="M27.422.12H25.061V3.25a.233.233,0,0,1-.342.205L18.6.2A.662.662,0,0,0,18.3.12h0a.648.648,0,0,0-.648.648v6.22h2.342V3.863a.233.233,0,0,1,.342-.206L26.47,6.915a.646.646,0,0,0,.3.076h0a.648.648,0,0,0,.648-.648Z"/><path class="cls-1" d="M29.252,7V.12h1.072V7Z"/><path class="cls-1" d="M36.629,7.119c-2.882,0-4.927-1.368-4.927-3.56S33.759,0,36.629,0s4.938,1.367,4.938,3.559S39.547,7.119,36.629,7.119Zm0-6.208c-2.143,0-3.794.936-3.794,2.648s1.651,2.648,3.794,2.648,3.805-.923,3.805-2.648S38.772.911,36.629.911Z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="121.755" height="29.822" viewBox="0 0 121.755 29.822"><defs><style>.a{fill:#fff;}</style></defs><g transform="translate(3016.56 -1037.757)"><path class="a" d="M-3000.913,1053.692l-1.772,1.194a6.088,6.088,0,0,0-5.135-2.652,6.348,6.348,0,0,0-6.522,6.654,6.348,6.348,0,0,0,6.522,6.654,6.031,6.031,0,0,0,5.124-2.64l1.735,1.266a8.126,8.126,0,0,1-6.859,3.411,8.422,8.422,0,0,1-8.74-8.691,8.422,8.422,0,0,1,8.74-8.691A7.963,7.963,0,0,1-3000.913,1053.692Z"/><path class="a" d="M-2980.919,1058.888a8.422,8.422,0,0,1-8.74,8.691,8.422,8.422,0,0,1-8.739-8.691,8.421,8.421,0,0,1,8.739-8.691A8.422,8.422,0,0,1-2980.919,1058.888Zm-15.261,0a6.347,6.347,0,0,0,6.521,6.654,6.347,6.347,0,0,0,6.521-6.654,6.347,6.347,0,0,0-6.521-6.654A6.347,6.347,0,0,0-2996.18,1058.888Z"/><path class="a" d="M-2962.831,1067.338h-1.917l-10.2-13.26-.012,13.248h-2.122v-16.888h1.917l10.21,13.26V1050.45h2.122Z"/><path class="a" d="M-2947.009,1053.777a8.835,8.835,0,0,0-5-1.555c-2.471,0-4.231,1.109-4.231,2.929,0,1.531,1.29,2.315,3.821,2.628l1.484.181c2.856.35,5.3,1.507,5.3,4.484,0,3.364-3.05,5.123-6.7,5.123a10.935,10.935,0,0,1-6.654-2.194l1.157-1.687a9.018,9.018,0,0,0,5.5,1.868c2.519,0,4.5-1.025,4.5-2.929,0-1.567-1.41-2.314-4.038-2.64l-1.567-.193c-2.784-.337-5-1.627-5-4.508,0-3.255,2.893-5.075,6.449-5.075a10.336,10.336,0,0,1,6.076,1.844Z"/><path class="a" d="M-2925.292,1058.888a8.422,8.422,0,0,1-8.74,8.691,8.422,8.422,0,0,1-8.739-8.691,8.421,8.421,0,0,1,8.739-8.691A8.422,8.422,0,0,1-2925.292,1058.888Zm-15.261,0a6.348,6.348,0,0,0,6.521,6.654,6.347,6.347,0,0,0,6.521-6.654,6.347,6.347,0,0,0-6.521-6.654A6.348,6.348,0,0,0-2940.553,1058.888Z"/><path class="a" d="M-2909.663,1067.326h-11.79V1050.45h2.122v14.863h9.668Z"/><path class="a" d="M-2894.8,1067.326h-11.982V1050.45h11.862v1.988h-9.74v5.389h9.427v2h-9.427v5.509h9.86Z"/><rect class="a" width="2.576" height="7.547" transform="translate(-3001.66 1037.924)"/><path class="a" d="M-3004.759,1037.995l-5.23,3.194a.229.229,0,0,1-.242,0l-5.23-3.194a.726.726,0,0,0-.374-.1h-.006a.717.717,0,0,0-.717.717v6.864h2.574v-3.257a.258.258,0,0,1,.392-.22l2.931,1.793a.919.919,0,0,0,.944.009l3.092-1.814a.258.258,0,0,1,.388.222v3.267h2.575v-6.864a.717.717,0,0,0-.717-.717h-.006A.723.723,0,0,0-3004.759,1037.995Z"/><path class="a" d="M-2986.212,1037.922h-2.613v3.463a.258.258,0,0,1-.379.228l-6.771-3.607a.723.723,0,0,0-.337-.084h0a.717.717,0,0,0-.717.717v6.832h2.592v-3.408a.258.258,0,0,1,.379-.227l6.8,3.606a.714.714,0,0,0,.336.083h0a.716.716,0,0,0,.717-.717v-6.886Z"/><path class="a" d="M-2984.121,1045.469v-7.547h1.2v7.547Z"/><path class="a" d="M-2976.024,1045.635c-3.189,0-5.451-1.513-5.451-3.939s2.276-3.939,5.451-3.939,5.466,1.513,5.466,3.939S-2972.794,1045.635-2976.024,1045.635Zm0-6.87c-2.371,0-4.2,1.036-4.2,2.931s1.826,2.93,4.2,2.93,4.212-1.022,4.212-2.93S-2973.652,1038.765-2976.024,1038.765Z"/></g></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Buckets Server
|
||||
// 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
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
createStyles,
|
||||
StyledProps,
|
||||
Theme,
|
||||
withStyles
|
||||
withStyles,
|
||||
} from "@material-ui/core/styles";
|
||||
|
||||
import history from "../../../history";
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
RouteComponentProps,
|
||||
Router,
|
||||
Switch,
|
||||
withRouter
|
||||
withRouter,
|
||||
} from "react-router-dom";
|
||||
import { connect } from "react-redux";
|
||||
import { AppState } from "../../../store";
|
||||
@@ -41,62 +41,62 @@ import ViewBucket from "./ViewBucket/ViewBucket";
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
display: "flex"
|
||||
display: "flex",
|
||||
},
|
||||
toolbar: {
|
||||
background: theme.palette.background.default,
|
||||
color: "black",
|
||||
paddingRight: 24 // keep right padding when drawer closed
|
||||
paddingRight: 24, // keep right padding when drawer closed
|
||||
},
|
||||
toolbarIcon: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
padding: "0 8px",
|
||||
...theme.mixins.toolbar
|
||||
...theme.mixins.toolbar,
|
||||
},
|
||||
appBar: {
|
||||
zIndex: theme.zIndex.drawer + 1,
|
||||
transition: theme.transitions.create(["width", "margin"], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen
|
||||
})
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
},
|
||||
|
||||
menuButton: {
|
||||
marginRight: 36
|
||||
marginRight: 36,
|
||||
},
|
||||
menuButtonHidden: {
|
||||
display: "none"
|
||||
display: "none",
|
||||
},
|
||||
title: {
|
||||
flexGrow: 1
|
||||
flexGrow: 1,
|
||||
},
|
||||
appBarSpacer: {
|
||||
height: "5px"
|
||||
height: "5px",
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
height: "100vh",
|
||||
overflow: "auto"
|
||||
overflow: "auto",
|
||||
},
|
||||
container: {
|
||||
paddingTop: theme.spacing(4),
|
||||
paddingBottom: theme.spacing(4)
|
||||
paddingBottom: theme.spacing(4),
|
||||
},
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column"
|
||||
flexDirection: "column",
|
||||
},
|
||||
fixedHeight: {
|
||||
minHeight: 240
|
||||
}
|
||||
minHeight: 240,
|
||||
},
|
||||
});
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
open: state.system.sidebarOpen
|
||||
open: state.system.sidebarOpen,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, { setMenuOpen });
|
||||
|
||||
@@ -14,7 +14,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 from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
@@ -23,6 +23,22 @@ import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import api from "../../../../common/api";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import CheckboxWrapper from "../../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
|
||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import { factorForDropdown, getBytes } from "../../../../common/utils";
|
||||
import { AppState } from "../../../../store";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
addBucketName,
|
||||
addBucketQuota,
|
||||
addBucketQuotaSize,
|
||||
addBucketQuotaType,
|
||||
addBucketQuotaUnit,
|
||||
addBucketVersioned,
|
||||
} from "../actions";
|
||||
import { useDebounce } from "use-debounce";
|
||||
import { MakeBucketRequest } from "../types";
|
||||
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -32,6 +48,20 @@ const styles = (theme: Theme) =>
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
multiContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center" as const,
|
||||
justifyContent: "flex-start" as const,
|
||||
},
|
||||
quotaSizeContainer: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
sizeFactorContainer: {
|
||||
flexGrow: 0,
|
||||
maxWidth: 80,
|
||||
marginLeft: 8,
|
||||
alignSelf: "flex-start" as const,
|
||||
},
|
||||
...modalBasic,
|
||||
});
|
||||
|
||||
@@ -39,119 +69,238 @@ interface IAddBucketProps {
|
||||
classes: any;
|
||||
open: boolean;
|
||||
closeModalAndRefresh: () => void;
|
||||
}
|
||||
|
||||
interface IAddBucketState {
|
||||
addLoading: boolean;
|
||||
addError: string;
|
||||
addBucketName: typeof addBucketName;
|
||||
addBucketVersioned: typeof addBucketVersioned;
|
||||
addBucketQuota: typeof addBucketQuota;
|
||||
addBucketQuotaType: typeof addBucketQuotaType;
|
||||
addBucketQuotaSize: typeof addBucketQuotaSize;
|
||||
addBucketQuotaUnit: typeof addBucketQuotaUnit;
|
||||
bucketName: string;
|
||||
versioned: boolean;
|
||||
enableQuota: boolean;
|
||||
quotaType: string;
|
||||
quotaSize: string;
|
||||
quotaUnit: string;
|
||||
}
|
||||
|
||||
class AddBucket extends React.Component<IAddBucketProps, IAddBucketState> {
|
||||
state: IAddBucketState = {
|
||||
addLoading: false,
|
||||
addError: "",
|
||||
bucketName: "",
|
||||
};
|
||||
const AddBucket = ({
|
||||
classes,
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
addBucketName,
|
||||
addBucketVersioned,
|
||||
addBucketQuota,
|
||||
addBucketQuotaType,
|
||||
addBucketQuotaSize,
|
||||
addBucketQuotaUnit,
|
||||
bucketName,
|
||||
versioned,
|
||||
enableQuota,
|
||||
quotaType,
|
||||
quotaSize,
|
||||
quotaUnit,
|
||||
}: IAddBucketProps) => {
|
||||
const [bName, setBName] = useState<string>(bucketName);
|
||||
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||
const [addError, setAddError] = useState<string>("");
|
||||
|
||||
addRecord(event: React.FormEvent) {
|
||||
const addRecord = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
const { bucketName, addLoading } = this.state;
|
||||
if (addLoading) {
|
||||
return;
|
||||
}
|
||||
this.setState({ addLoading: true }, () => {
|
||||
api
|
||||
.invoke("POST", "/api/v1/buckets", {
|
||||
name: bucketName,
|
||||
})
|
||||
.then((res) => {
|
||||
this.setState(
|
||||
{
|
||||
addLoading: false,
|
||||
addError: "",
|
||||
},
|
||||
() => {
|
||||
this.props.closeModalAndRefresh();
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
this.setState({
|
||||
addLoading: false,
|
||||
addError: err,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
setAddLoading(true);
|
||||
|
||||
render() {
|
||||
const { classes, open } = this.props;
|
||||
const { addLoading, addError, bucketName } = this.state;
|
||||
return (
|
||||
<ModalWrapper
|
||||
title="Create Bucket"
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
this.setState({ addError: "" }, () => {
|
||||
this.props.closeModalAndRefresh();
|
||||
});
|
||||
let request: MakeBucketRequest = {
|
||||
name: bucketName,
|
||||
versioning: versioned,
|
||||
};
|
||||
|
||||
if (enableQuota) {
|
||||
const amount = getBytes(quotaSize, quotaUnit, false);
|
||||
request.quota = {
|
||||
enabled: true,
|
||||
quota_type: quotaType,
|
||||
amount: parseInt(amount),
|
||||
};
|
||||
}
|
||||
|
||||
api
|
||||
.invoke("POST", "/api/v1/buckets", request)
|
||||
.then((res) => {
|
||||
setAddLoading(false);
|
||||
setAddError("");
|
||||
closeModalAndRefresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddLoading(false);
|
||||
setAddError(err);
|
||||
});
|
||||
};
|
||||
|
||||
const [value] = useDebounce(bName, 1000);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("called");
|
||||
addBucketName(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
title="Create Bucket"
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
setAddError("");
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
addRecord(e);
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="bucket-name"
|
||||
name="bucket-name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ bucketName: e.target.value });
|
||||
}}
|
||||
label="Bucket Name"
|
||||
value={bucketName}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="bucket-name"
|
||||
name="bucket-name"
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setBName(event.target.value);
|
||||
}}
|
||||
label="Bucket Name"
|
||||
value={bName}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="versioned"
|
||||
id="versioned"
|
||||
name="versioned"
|
||||
checked={versioned}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
addBucketVersioned(event.target.checked);
|
||||
}}
|
||||
label={"Turn On Versioning"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="bucket_quota"
|
||||
id="bucket_quota"
|
||||
name="bucket_quota"
|
||||
checked={enableQuota}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
addBucketQuota(event.target.checked);
|
||||
}}
|
||||
label={"Enable Bucket Quota"}
|
||||
/>
|
||||
</Grid>
|
||||
{enableQuota && (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<SelectWrapper
|
||||
value={quotaType}
|
||||
label="Quota Type"
|
||||
id="quota_type"
|
||||
name="quota_type"
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
addBucketQuotaType(e.target.value as string);
|
||||
}}
|
||||
options={[
|
||||
{ value: "hard", label: "Hard" },
|
||||
{ value: "fifo", label: "FIFO" },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.multiContainer}>
|
||||
<div className={classes.quotaSizeContainer}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="quota_size"
|
||||
name="quota_size"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
addBucketQuotaSize(e.target.value);
|
||||
}}
|
||||
label="Quota"
|
||||
value={quotaSize}
|
||||
required
|
||||
min="1"
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.sizeFactorContainer}>
|
||||
<SelectWrapper
|
||||
label=" "
|
||||
id="quota_unit"
|
||||
name="quota_unit"
|
||||
value={quotaUnit}
|
||||
onChange={(
|
||||
e: React.ChangeEvent<{ value: unknown }>
|
||||
) => {
|
||||
addBucketQuotaUnit(e.target.value as string);
|
||||
}}
|
||||
options={factorForDropdown()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</ModalWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(AddBucket);
|
||||
const mapState = (state: AppState) => ({
|
||||
addBucketModalOpen: state.buckets.open,
|
||||
bucketName: state.buckets.addBucketName,
|
||||
versioned: state.buckets.addBucketVersioning,
|
||||
enableQuota: state.buckets.addBucketQuotaEnabled,
|
||||
quotaType: state.buckets.addBucketQuotaType,
|
||||
quotaSize: state.buckets.addBucketQuotaSize,
|
||||
quotaUnit: state.buckets.addBucketQuotaUnit,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
addBucketName: addBucketName,
|
||||
addBucketVersioned: addBucketVersioned,
|
||||
addBucketQuota: addBucketQuota,
|
||||
addBucketQuotaType: addBucketQuotaType,
|
||||
addBucketQuotaSize: addBucketQuotaSize,
|
||||
addBucketQuotaUnit: addBucketQuotaUnit,
|
||||
});
|
||||
|
||||
export default connector(withStyles(styles)(AddBucket));
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
LinearProgress
|
||||
LinearProgress,
|
||||
} from "@material-ui/core";
|
||||
import api from "../../../../common/api";
|
||||
import { BucketList } from "../types";
|
||||
@@ -32,8 +32,8 @@ import Typography from "@material-ui/core/Typography";
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red"
|
||||
}
|
||||
color: "red",
|
||||
},
|
||||
});
|
||||
|
||||
interface IDeleteBucketProps {
|
||||
@@ -54,7 +54,7 @@ class DeleteBucket extends React.Component<
|
||||
> {
|
||||
state: IDeleteBucketState = {
|
||||
deleteLoading: false,
|
||||
deleteError: ""
|
||||
deleteError: "",
|
||||
};
|
||||
|
||||
removeRecord() {
|
||||
@@ -66,23 +66,23 @@ class DeleteBucket extends React.Component<
|
||||
this.setState({ deleteLoading: true }, () => {
|
||||
api
|
||||
.invoke("DELETE", `/api/v1/buckets/${selectedBucket}`, {
|
||||
name: selectedBucket
|
||||
name: selectedBucket,
|
||||
})
|
||||
.then((res: BucketList) => {
|
||||
this.setState(
|
||||
{
|
||||
deleteLoading: false,
|
||||
deleteError: ""
|
||||
deleteError: "",
|
||||
},
|
||||
() => {
|
||||
this.props.closeDeleteModalAndRefresh(true);
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
this.setState({
|
||||
deleteLoading: false,
|
||||
deleteError: err
|
||||
deleteError: err,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,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 from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { Button } from "@material-ui/core";
|
||||
@@ -31,6 +31,15 @@ import DeleteBucket from "./DeleteBucket";
|
||||
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
||||
import { CreateIcon } from "../../../../icons";
|
||||
import { niceBytes } from "../../../../common/utils";
|
||||
import { AppState } from "../../../../store";
|
||||
import { connect } from "react-redux";
|
||||
import { addBucketOpen, addBucketReset } from "../actions";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -62,188 +71,155 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
},
|
||||
searchField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface IListBucketsProps {
|
||||
classes: any;
|
||||
addBucketOpen: typeof addBucketOpen;
|
||||
addBucketModalOpen: boolean;
|
||||
addBucketReset: typeof addBucketReset;
|
||||
}
|
||||
|
||||
interface IListBucketsState {
|
||||
records: Bucket[];
|
||||
totalRecords: number;
|
||||
loading: boolean;
|
||||
error: string;
|
||||
deleteError: string;
|
||||
addScreenOpen: boolean;
|
||||
page: number;
|
||||
rowsPerPage: number;
|
||||
deleteOpen: boolean;
|
||||
selectedBucket: string;
|
||||
filterBuckets: string;
|
||||
}
|
||||
const ListBuckets = ({
|
||||
classes,
|
||||
addBucketOpen,
|
||||
addBucketModalOpen,
|
||||
addBucketReset,
|
||||
}: IListBucketsProps) => {
|
||||
const [records, setRecords] = useState<Bucket[]>([]);
|
||||
const [totalRecords, setTotalRecords] = useState<number>(0);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string>("");
|
||||
const [deleteError, setDeleteError] = useState<string>("");
|
||||
const [page, setPage] = useState<number>(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedBucket, setSelectedBucket] = useState<string>("");
|
||||
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||
|
||||
class ListBuckets extends React.Component<
|
||||
IListBucketsProps,
|
||||
IListBucketsState
|
||||
> {
|
||||
state: IListBucketsState = {
|
||||
records: [],
|
||||
totalRecords: 0,
|
||||
loading: false,
|
||||
error: "",
|
||||
deleteError: "",
|
||||
addScreenOpen: false,
|
||||
page: 0,
|
||||
rowsPerPage: 10,
|
||||
deleteOpen: false,
|
||||
selectedBucket: "",
|
||||
filterBuckets: "",
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
const fetchRecords = () => {
|
||||
setLoading(true);
|
||||
const offset = page * rowsPerPage;
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/buckets?offset=${offset}&limit=${rowsPerPage}`
|
||||
)
|
||||
.then((res: BucketList) => {
|
||||
setLoading(false);
|
||||
setRecords(res.buckets || []);
|
||||
setTotalRecords(!res.buckets ? 0 : res.total);
|
||||
setError("");
|
||||
// if we get 0 results, and page > 0 , go down 1 page
|
||||
if (
|
||||
(res.buckets === undefined ||
|
||||
res.buckets == null ||
|
||||
res.buckets.length === 0) &&
|
||||
page > 0
|
||||
) {
|
||||
const newPage = page - 1;
|
||||
setPage(newPage);
|
||||
setLoading(true);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setLoading(false);
|
||||
setError(err);
|
||||
});
|
||||
};
|
||||
fetchRecords();
|
||||
}
|
||||
}, [loading, page, rowsPerPage]);
|
||||
|
||||
const closeAddModalAndRefresh = () => {
|
||||
addBucketOpen(false);
|
||||
addBucketReset();
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
fetchRecords() {
|
||||
this.setState({ loading: true }, () => {
|
||||
const { page, rowsPerPage } = this.state;
|
||||
const offset = page * rowsPerPage;
|
||||
api
|
||||
.invoke("GET", `/api/v1/buckets?offset=${offset}&limit=${rowsPerPage}`)
|
||||
.then((res: BucketList) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
records: res.buckets || [],
|
||||
totalRecords: !res.buckets ? 0 : res.total,
|
||||
error: "",
|
||||
});
|
||||
// if we get 0 results, and page > 0 , go down 1 page
|
||||
if (
|
||||
(res.buckets === undefined ||
|
||||
res.buckets == null ||
|
||||
res.buckets.length === 0) &&
|
||||
page > 0
|
||||
) {
|
||||
const newPage = page - 1;
|
||||
this.setState({ page: newPage }, () => {
|
||||
this.fetchRecords();
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.setState({ loading: false, error: err });
|
||||
});
|
||||
});
|
||||
}
|
||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||
setDeleteOpen(false);
|
||||
if (refresh) {
|
||||
setLoading(true);
|
||||
}
|
||||
};
|
||||
|
||||
closeAddModalAndRefresh() {
|
||||
this.setState({ addScreenOpen: false }, () => {
|
||||
this.fetchRecords();
|
||||
});
|
||||
}
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
}, []);
|
||||
|
||||
closeDeleteModalAndRefresh(refresh: boolean) {
|
||||
this.setState({ deleteOpen: false }, () => {
|
||||
if (refresh) {
|
||||
this.fetchRecords();
|
||||
}
|
||||
});
|
||||
}
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
}, [page, rowsPerPage]);
|
||||
|
||||
componentDidMount(): void {
|
||||
this.fetchRecords();
|
||||
}
|
||||
const confirmDeleteBucket = (bucket: string) => {
|
||||
setDeleteOpen(true);
|
||||
setSelectedBucket(bucket);
|
||||
};
|
||||
|
||||
bucketFilter(): void {}
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
const {
|
||||
records,
|
||||
totalRecords,
|
||||
addScreenOpen,
|
||||
loading,
|
||||
page,
|
||||
rowsPerPage,
|
||||
deleteOpen,
|
||||
selectedBucket,
|
||||
filterBuckets,
|
||||
} = this.state;
|
||||
const handleChangeRowsPerPage = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const rPP = parseInt(event.target.value, 10);
|
||||
setPage(0);
|
||||
setRowsPerPage(rPP);
|
||||
};
|
||||
const tableActions = [
|
||||
{ type: "view", to: `/buckets`, sendOnlyId: true },
|
||||
{ type: "delete", onClick: confirmDeleteBucket, sendOnlyId: true },
|
||||
];
|
||||
|
||||
const offset = page * rowsPerPage;
|
||||
const offset = page * rowsPerPage;
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
this.setState({ page: newPage });
|
||||
};
|
||||
const displayParsedDate = (date: string) => {
|
||||
return <Moment>{date}</Moment>;
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const rPP = parseInt(event.target.value, 10);
|
||||
this.setState({ page: 0, rowsPerPage: rPP });
|
||||
};
|
||||
|
||||
const confirmDeleteBucket = (bucket: string) => {
|
||||
this.setState({ deleteOpen: true, selectedBucket: bucket });
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", to: `/buckets`, sendOnlyId: true },
|
||||
{ type: "delete", onClick: confirmDeleteBucket, sendOnlyId: true },
|
||||
];
|
||||
|
||||
const displayParsedDate = (date: string) => {
|
||||
return <Moment>{date}</Moment>;
|
||||
};
|
||||
|
||||
const filteredRecords = records
|
||||
.slice(offset, offset + rowsPerPage)
|
||||
.filter((b: Bucket) => {
|
||||
if (filterBuckets === "") {
|
||||
const filteredRecords = records
|
||||
.filter((b: Bucket) => {
|
||||
if (filterBuckets === "") {
|
||||
return true;
|
||||
} else {
|
||||
if (b.name.indexOf(filterBuckets) >= 0) {
|
||||
return true;
|
||||
} else {
|
||||
if (b.name.indexOf(filterBuckets) >= 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.slice(offset, offset + rowsPerPage);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{addScreenOpen && (
|
||||
<AddBucket
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
this.closeAddModalAndRefresh();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{deleteOpen && (
|
||||
<DeleteBucket
|
||||
deleteOpen={deleteOpen}
|
||||
selectedBucket={selectedBucket}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
this.closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Buckets</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
return (
|
||||
<React.Fragment>
|
||||
{addBucketModalOpen && (
|
||||
<AddBucket
|
||||
open={addBucketModalOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
closeAddModalAndRefresh();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{deleteOpen && (
|
||||
<DeleteBucket
|
||||
deleteOpen={deleteOpen}
|
||||
selectedBucket={selectedBucket}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label={"Buckets"} />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Buckets"
|
||||
@@ -251,9 +227,7 @@ class ListBuckets extends React.Component<
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
this.setState({
|
||||
filterBuckets: val.target.value,
|
||||
});
|
||||
setFilterBuckets(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
@@ -269,9 +243,7 @@ class ListBuckets extends React.Component<
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
addScreenOpen: true,
|
||||
});
|
||||
addBucketOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Bucket
|
||||
@@ -317,9 +289,18 @@ class ListBuckets extends React.Component<
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(ListBuckets);
|
||||
const mapState = (state: AppState) => ({
|
||||
addBucketModalOpen: state.buckets.open,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
addBucketOpen: addBucketOpen,
|
||||
addBucketReset: addBucketReset,
|
||||
});
|
||||
|
||||
export default connector(withStyles(styles)(ListBuckets));
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
// 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 { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import React from "react";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
LinearProgress,
|
||||
} from "@material-ui/core";
|
||||
import api from "../../../../../../common/api";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
});
|
||||
|
||||
interface IDeleteObjectProps {
|
||||
classes: any;
|
||||
closeDeleteModalAndRefresh: (refresh: boolean) => void;
|
||||
deleteOpen: boolean;
|
||||
selectedObject: string;
|
||||
selectedBucket: string;
|
||||
}
|
||||
|
||||
interface IDeleteObjectState {
|
||||
deleteLoading: boolean;
|
||||
deleteError: string;
|
||||
}
|
||||
|
||||
class DeleteObject extends React.Component<
|
||||
IDeleteObjectProps,
|
||||
IDeleteObjectState
|
||||
> {
|
||||
state: IDeleteObjectState = {
|
||||
deleteLoading: false,
|
||||
deleteError: "",
|
||||
};
|
||||
|
||||
removeRecord() {
|
||||
const { deleteLoading } = this.state;
|
||||
const { selectedObject, selectedBucket } = this.props;
|
||||
if (deleteLoading) {
|
||||
return;
|
||||
}
|
||||
var recursive = false;
|
||||
if (selectedObject.endsWith("/")) {
|
||||
recursive = true;
|
||||
}
|
||||
|
||||
this.setState({ deleteLoading: true }, () => {
|
||||
api
|
||||
.invoke(
|
||||
"DELETE",
|
||||
`/api/v1/buckets/${selectedBucket}/objects?path=${selectedObject}&recursive=${recursive}`
|
||||
)
|
||||
.then((res: any) => {
|
||||
this.setState(
|
||||
{
|
||||
deleteLoading: false,
|
||||
deleteError: "",
|
||||
},
|
||||
() => {
|
||||
this.props.closeDeleteModalAndRefresh(true);
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
this.setState({
|
||||
deleteLoading: false,
|
||||
deleteError: err,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, deleteOpen, selectedObject } = this.props;
|
||||
const { deleteLoading, deleteError } = this.state;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={deleteOpen}
|
||||
onClose={() => {
|
||||
this.setState({ deleteError: "" }, () => {
|
||||
this.props.closeDeleteModalAndRefresh(false);
|
||||
});
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">Delete</DialogTitle>
|
||||
<DialogContent>
|
||||
{deleteLoading && <LinearProgress />}
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Are you sure you want to delete: <b>{selectedObject}</b>?{" "}
|
||||
{deleteError !== "" && (
|
||||
<React.Fragment>
|
||||
<br />
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{deleteError}
|
||||
</Typography>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.setState({ deleteError: "" }, () => {
|
||||
this.props.closeDeleteModalAndRefresh(false);
|
||||
});
|
||||
}}
|
||||
color="primary"
|
||||
disabled={deleteLoading}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.setState({ deleteError: "" }, () => {
|
||||
this.removeRecord();
|
||||
});
|
||||
}}
|
||||
color="secondary"
|
||||
disabled={deleteLoading}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(DeleteObject);
|
||||
@@ -0,0 +1,285 @@
|
||||
// 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 { 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 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,
|
||||
searchField,
|
||||
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../../../../Common/PageHeader/PageHeader";
|
||||
import storage from "local-storage-fallback";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
paper: {
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
},
|
||||
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
},
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
tableToolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(0),
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
"& tr": {
|
||||
"& th": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface IListObjectsProps {
|
||||
classes: any;
|
||||
match: any;
|
||||
}
|
||||
|
||||
interface IListObjectsState {
|
||||
records: BucketObject[];
|
||||
totalRecords: number;
|
||||
loading: boolean;
|
||||
error: string;
|
||||
deleteOpen: boolean;
|
||||
deleteError: string;
|
||||
selectedObject: string;
|
||||
selectedBucket: string;
|
||||
filterObjects: string;
|
||||
}
|
||||
|
||||
class ListObjects extends React.Component<
|
||||
IListObjectsProps,
|
||||
IListObjectsState
|
||||
> {
|
||||
state: IListObjectsState = {
|
||||
records: [],
|
||||
totalRecords: 0,
|
||||
loading: false,
|
||||
error: "",
|
||||
deleteOpen: false,
|
||||
deleteError: "",
|
||||
selectedObject: "",
|
||||
selectedBucket: "",
|
||||
filterObjects: "",
|
||||
};
|
||||
|
||||
fetchRecords = () => {
|
||||
this.setState({ loading: true }, () => {
|
||||
const { match } = this.props;
|
||||
const bucketName = match.params["bucket"];
|
||||
api
|
||||
.invoke("GET", `/api/v1/buckets/${bucketName}/objects`)
|
||||
.then((res: BucketObjectsList) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
selectedBucket: bucketName,
|
||||
records: res.objects || [],
|
||||
totalRecords: !res.objects ? 0 : res.total,
|
||||
error: "",
|
||||
});
|
||||
// TODO:
|
||||
// if we get 0 results, and page > 0 , go down 1 page
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.setState({ loading: false, error: err });
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount(): void {
|
||||
this.fetchRecords();
|
||||
}
|
||||
|
||||
closeDeleteModalAndRefresh(refresh: boolean) {
|
||||
this.setState({ deleteOpen: false }, () => {
|
||||
if (refresh) {
|
||||
this.fetchRecords();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
download(bucketName: string, objectName: string) {
|
||||
var anchor = document.createElement("a");
|
||||
document.body.appendChild(anchor);
|
||||
const token: string = storage.getItem("token")!;
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open(
|
||||
"GET",
|
||||
`/api/v1/buckets/${bucketName}/objects/download?prefix=${objectName}`,
|
||||
true
|
||||
);
|
||||
xhr.setRequestHeader("Authorization", `Bearer ${token}`);
|
||||
xhr.responseType = "blob";
|
||||
|
||||
xhr.onload = function(e) {
|
||||
if (this.status == 200) {
|
||||
var blob = new Blob([this.response], {
|
||||
type: "octet/stream",
|
||||
});
|
||||
var blobUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
anchor.href = blobUrl;
|
||||
anchor.download = objectName;
|
||||
|
||||
anchor.click();
|
||||
window.URL.revokeObjectURL(blobUrl);
|
||||
anchor.remove();
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
bucketFilter(): void {}
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
const {
|
||||
records,
|
||||
loading,
|
||||
selectedObject,
|
||||
selectedBucket,
|
||||
deleteOpen,
|
||||
filterObjects,
|
||||
} = this.state;
|
||||
const displayParsedDate = (date: string) => {
|
||||
return <Moment>{date}</Moment>;
|
||||
};
|
||||
|
||||
const confirmDeleteObject = (object: string) => {
|
||||
this.setState({ deleteOpen: true, selectedObject: object });
|
||||
};
|
||||
|
||||
const downloadObject = (object: string) => {
|
||||
this.download(selectedBucket, object);
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "download", onClick: downloadObject, sendOnlyId: true },
|
||||
{ type: "delete", onClick: confirmDeleteObject, sendOnlyId: true },
|
||||
];
|
||||
|
||||
const filteredRecords = records.filter((b: BucketObject) => {
|
||||
if (filterObjects === "") {
|
||||
return true;
|
||||
} else {
|
||||
if (b.name.indexOf(filterObjects) >= 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{deleteOpen && (
|
||||
<DeleteObject
|
||||
deleteOpen={deleteOpen}
|
||||
selectedBucket={selectedBucket}
|
||||
selectedObject={selectedObject}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
this.closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label="Objects" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Objects"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
this.setState({
|
||||
filterObjects: val.target.value,
|
||||
});
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Name", elementKey: "name" },
|
||||
{
|
||||
label: "Last Modified",
|
||||
elementKey: "last_modified",
|
||||
renderFunction: displayParsedDate,
|
||||
},
|
||||
{
|
||||
label: "Size",
|
||||
elementKey: "size",
|
||||
renderFunction: niceBytes,
|
||||
},
|
||||
]}
|
||||
isLoading={loading}
|
||||
entityName="Objects"
|
||||
idField="name"
|
||||
records={filteredRecords}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withStyles(styles)(ListObjects);
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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/>.
|
||||
|
||||
export interface BucketObject {
|
||||
name: string;
|
||||
size: number;
|
||||
last_modified: Date;
|
||||
content_type: string;
|
||||
}
|
||||
|
||||
export interface BucketObjectsList {
|
||||
objects: BucketObject[];
|
||||
total: number;
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
// 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, { useState, useEffect } from "react";
|
||||
import get from "lodash/get";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import api from "../../../../common/api";
|
||||
import { IRemoteBucket } from "../types";
|
||||
|
||||
interface IReplicationModal {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: () => any;
|
||||
classes: any;
|
||||
bucketName: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
"& tr": {
|
||||
"& th": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
});
|
||||
|
||||
const AddReplicationModal = ({
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
classes,
|
||||
bucketName,
|
||||
}: IReplicationModal) => {
|
||||
const [addError, setAddError] = useState("");
|
||||
const [addLoading, setAddLoading] = useState(false);
|
||||
const [accessKey, setAccessKey] = useState("");
|
||||
const [secretKey, setSecretKey] = useState("");
|
||||
const [targetURL, setTargetURL] = useState("");
|
||||
const [targetBucket, setTargetBucket] = useState("");
|
||||
const [region, setRegion] = useState("");
|
||||
|
||||
const addRecord = () => {
|
||||
const remoteBucketInfo = {
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
sourceBucket: bucketName,
|
||||
targetURL: targetURL,
|
||||
targetBucket: targetBucket,
|
||||
region: region,
|
||||
};
|
||||
|
||||
api
|
||||
.invoke("POST", "/api/v1/remote-buckets", remoteBucketInfo)
|
||||
.then(() => {
|
||||
api
|
||||
.invoke("GET", "/api/v1/remote-buckets")
|
||||
.then((res: any) => {
|
||||
const remoteBuckets = get(res, "buckets", []);
|
||||
const remoteBucket = remoteBuckets.find(
|
||||
(itemRemote: IRemoteBucket) => {
|
||||
return itemRemote.sourceBucket === bucketName;
|
||||
}
|
||||
);
|
||||
if (remoteBucket && remoteBucket.remoteARN) {
|
||||
const remoteARN = remoteBucket.remoteARN;
|
||||
const replicationInfo = {
|
||||
destination_bucket: targetBucket,
|
||||
arn: remoteARN,
|
||||
};
|
||||
api
|
||||
.invoke(
|
||||
"POST",
|
||||
`/api/v1/buckets/${bucketName}/replication`,
|
||||
replicationInfo
|
||||
)
|
||||
.then(() => {
|
||||
setAddLoading(false);
|
||||
setAddError("");
|
||||
closeModalAndRefresh();
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddLoading(false);
|
||||
setAddError(err);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddError(err);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddLoading(false);
|
||||
setAddError(err);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
setAddError("");
|
||||
closeModalAndRefresh();
|
||||
}}
|
||||
title="Set Bucket Replication"
|
||||
>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setAddLoading(true);
|
||||
addRecord();
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="target"
|
||||
name="target"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccessKey(e.target.value);
|
||||
}}
|
||||
label="Access Key"
|
||||
value={accessKey}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="target"
|
||||
name="target"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSecretKey(e.target.value);
|
||||
}}
|
||||
label="Secret Key"
|
||||
value={secretKey}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="target"
|
||||
name="target"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTargetURL(e.target.value);
|
||||
}}
|
||||
placeholder="https://play.min.io:9000"
|
||||
label="Target URL"
|
||||
value={targetURL}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="target"
|
||||
name="target"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTargetBucket(e.target.value);
|
||||
}}
|
||||
label="Target Bucket"
|
||||
value={targetBucket}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="target"
|
||||
name="target"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setRegion(e.target.value);
|
||||
}}
|
||||
label="Region"
|
||||
value={region}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(AddReplicationModal);
|
||||
@@ -23,8 +23,18 @@ import Tabs from "@material-ui/core/Tabs";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||
import api from "../../../../common/api";
|
||||
import { BucketEvent, BucketEventList, BucketInfo, BucketList } from "../types";
|
||||
import { Button } from "@material-ui/core";
|
||||
import {
|
||||
BucketEvent,
|
||||
BucketEventList,
|
||||
BucketInfo,
|
||||
BucketList,
|
||||
BucketReplication,
|
||||
BucketReplicationDestination,
|
||||
BucketReplicationRule,
|
||||
BucketReplicationRuleDeleteMarker,
|
||||
BucketVersioning,
|
||||
} from "../types";
|
||||
import { Box, Button } from "@material-ui/core";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import SetAccessPolicy from "./SetAccessPolicy";
|
||||
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
||||
@@ -33,6 +43,9 @@ import AddEvent from "./AddEvent";
|
||||
import DeleteEvent from "./DeleteEvent";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import { niceBytes } from "../../../../common/utils";
|
||||
import AddReplicationModal from "./AddReplicationModal";
|
||||
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -117,6 +130,14 @@ const styles = (theme: Theme) =>
|
||||
capitalizeFirst: {
|
||||
textTransform: "capitalize",
|
||||
},
|
||||
doubleElement: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
tabPan: {
|
||||
marginTop: "5px",
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface IViewBucketProps {
|
||||
@@ -124,9 +145,40 @@ interface IViewBucketProps {
|
||||
match: any;
|
||||
}
|
||||
|
||||
interface TabPanelProps {
|
||||
children?: React.ReactNode;
|
||||
index: any;
|
||||
value: any;
|
||||
}
|
||||
|
||||
function TabPanel(props: TabPanelProps) {
|
||||
const { children, value, index, ...other } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
role="tabpanel"
|
||||
hidden={value !== index}
|
||||
id={`simple-tabpanel-${index}`}
|
||||
aria-labelledby={`simple-tab-${index}`}
|
||||
style={{ marginTop: "5px" }}
|
||||
{...other}
|
||||
>
|
||||
{value === index && <React.Fragment>{children}</React.Fragment>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function a11yProps(index: any) {
|
||||
return {
|
||||
id: `simple-tab-${index}`,
|
||||
"aria-controls": `simple-tabpanel-${index}`,
|
||||
};
|
||||
}
|
||||
|
||||
interface IViewBucketState {
|
||||
info: BucketInfo | null;
|
||||
records: BucketEvent[];
|
||||
replicationRules: BucketReplicationRule[];
|
||||
totalRecords: number;
|
||||
loadingBucket: boolean;
|
||||
loadingEvents: boolean;
|
||||
@@ -137,18 +189,23 @@ interface IViewBucketState {
|
||||
setAccessPolicyScreenOpen: boolean;
|
||||
page: number;
|
||||
rowsPerPage: number;
|
||||
curTab: number;
|
||||
addScreenOpen: boolean;
|
||||
deleteOpen: boolean;
|
||||
selectedBucket: string;
|
||||
selectedEvent: BucketEvent | null;
|
||||
bucketSize: string;
|
||||
errorSize: string;
|
||||
replicationSet: boolean;
|
||||
openSetReplication: boolean;
|
||||
isVersioned: boolean;
|
||||
}
|
||||
|
||||
class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
state: IViewBucketState = {
|
||||
info: null,
|
||||
records: [],
|
||||
replicationRules: [],
|
||||
totalRecords: 0,
|
||||
loadingBucket: true,
|
||||
loadingEvents: true,
|
||||
@@ -158,6 +215,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
errBucket: "",
|
||||
setAccessPolicyScreenOpen: false,
|
||||
page: 0,
|
||||
curTab: 0,
|
||||
rowsPerPage: 10,
|
||||
addScreenOpen: false,
|
||||
deleteOpen: false,
|
||||
@@ -165,6 +223,9 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
selectedEvent: null,
|
||||
bucketSize: "0",
|
||||
errorSize: "",
|
||||
replicationSet: false,
|
||||
openSetReplication: false,
|
||||
isVersioned: false,
|
||||
};
|
||||
|
||||
fetchEvents() {
|
||||
@@ -195,6 +256,29 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
.catch((err: any) => {
|
||||
this.setState({ loadingEvents: false, error: err });
|
||||
});
|
||||
|
||||
api
|
||||
.invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
|
||||
.then((res: BucketVersioning) => {
|
||||
this.setState({
|
||||
isVersioned: res.is_versioned,
|
||||
});
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.setState({ error: err });
|
||||
});
|
||||
|
||||
api
|
||||
.invoke("GET", `/api/v1/buckets/${bucketName}/replication`)
|
||||
.then((res: BucketReplication) => {
|
||||
const r = res.rules ? res.rules : [];
|
||||
this.setState({
|
||||
replicationRules: r,
|
||||
});
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.setState({ error: err });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -279,6 +363,11 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
selectedEvent,
|
||||
bucketSize,
|
||||
loadingSize,
|
||||
replicationSet,
|
||||
openSetReplication,
|
||||
isVersioned,
|
||||
replicationRules,
|
||||
curTab,
|
||||
} = this.state;
|
||||
|
||||
const offset = page * rowsPerPage;
|
||||
@@ -301,6 +390,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
};
|
||||
|
||||
let accessPolicy = "n/a";
|
||||
|
||||
if (info !== null) {
|
||||
accessPolicy = info.access;
|
||||
}
|
||||
@@ -309,9 +399,26 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
return <React.Fragment>{events.join(", ")}</React.Fragment>;
|
||||
};
|
||||
|
||||
const ruleDestDisplay = (events: BucketReplicationDestination) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{events.bucket.replace("arn:aws:s3:::", "")}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const ruleDelDisplay = (events: BucketReplicationRuleDeleteMarker) => {
|
||||
return <React.Fragment>{events.status}</React.Fragment>;
|
||||
};
|
||||
|
||||
const setOpenReplicationOpen = (open = false) => {
|
||||
this.setState({ openSetReplication: open });
|
||||
};
|
||||
|
||||
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
|
||||
|
||||
const filteredRecords = records.slice(offset, offset + rowsPerPage);
|
||||
const filteredRules = replicationRules.slice(offset, offset + rowsPerPage);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
@@ -335,130 +442,203 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{openSetReplication && (
|
||||
<AddReplicationModal
|
||||
closeModalAndRefresh={() => {
|
||||
setOpenReplicationOpen(false);
|
||||
this.fetchEvents();
|
||||
}}
|
||||
open={openSetReplication}
|
||||
bucketName={bucketName}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label={`Bucket > ${match.params["bucketName"]}`} />
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">
|
||||
Bucket > {match.params["bucketName"]}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.headerContainer}>
|
||||
<div>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<div className={classes.gridContainer}>
|
||||
<div>Access Policy:</div>
|
||||
<div className={classes.capitalizeFirst}>
|
||||
{loadingBucket ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
accessPolicy.toLowerCase()
|
||||
)}
|
||||
</div>
|
||||
<div>Reported Usage:</div>
|
||||
<div>
|
||||
{loadingSize ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
niceBytes(bucketSize)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</div>
|
||||
<div className={classes.masterActions}>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.headerContainer}>
|
||||
<div>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<div className={classes.gridContainer}>
|
||||
<div>Access Policy:</div>
|
||||
<div className={classes.capitalizeFirst}>
|
||||
{loadingBucket ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
accessPolicy.toLowerCase()
|
||||
)}
|
||||
</div>
|
||||
<div>Reported Usage:</div>
|
||||
<div>
|
||||
{loadingSize ? (
|
||||
<CircularProgress
|
||||
color="primary"
|
||||
size={16}
|
||||
variant="indeterminate"
|
||||
/>
|
||||
) : (
|
||||
niceBytes(bucketSize)
|
||||
)}
|
||||
</div>
|
||||
<div>Replication:</div>
|
||||
<div className={classes.doubleElement}>
|
||||
<span>{replicationRules.length ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
<div>Versioning:</div>
|
||||
<div>{isVersioned ? "Yes" : "No"} </div>
|
||||
</div>
|
||||
</Paper>
|
||||
</div>
|
||||
<div className={classes.masterActions}>
|
||||
<div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
setAccessPolicyScreenOpen: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Change Access Policy
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid container item xs={12}>
|
||||
<Grid item xs={6}>
|
||||
<Tabs
|
||||
value={curTab}
|
||||
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
|
||||
this.setState({ curTab: newValue });
|
||||
}}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
aria-label="cluster-tabs"
|
||||
>
|
||||
<Tab label="Events" {...a11yProps(0)} />
|
||||
<Tab label="Replication" {...a11yProps(1)} />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item xs={6} className={classes.actionsTray}>
|
||||
{curTab === 0 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
startIcon={<CreateIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
setAccessPolicyScreenOpen: true,
|
||||
addScreenOpen: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Change Access Policy
|
||||
Subscribe to Event
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Tabs
|
||||
value={0}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
aria-label="cluster-tabs"
|
||||
>
|
||||
<Tab label="Events" />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item xs={6} className={classes.actionsTray}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
addScreenOpen: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Subcribe to Event
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "SQS", elementKey: "arn" },
|
||||
{
|
||||
label: "Events",
|
||||
elementKey: "events",
|
||||
renderFunction: eventsDisplay,
|
||||
},
|
||||
{ label: "Prefix", elementKey: "prefix" },
|
||||
{ label: "Suffix", elementKey: "suffix" },
|
||||
]}
|
||||
isLoading={loadingEvents}
|
||||
records={filteredRecords}
|
||||
entityName="Events"
|
||||
idField="id"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: totalRecords,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: handleChangePage,
|
||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{curTab === 1 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
openSetReplication: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Add Replication Rule
|
||||
</Button>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel index={0} value={curTab}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "SQS", elementKey: "arn" },
|
||||
{
|
||||
label: "Events",
|
||||
elementKey: "events",
|
||||
renderFunction: eventsDisplay,
|
||||
},
|
||||
{ label: "Prefix", elementKey: "prefix" },
|
||||
{ label: "Suffix", elementKey: "suffix" },
|
||||
]}
|
||||
isLoading={loadingEvents}
|
||||
records={filteredRecords}
|
||||
entityName="Events"
|
||||
idField="id"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: totalRecords,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: handleChangePage,
|
||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel index={1} value={curTab}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "ID", elementKey: "id" },
|
||||
{
|
||||
label: "Priority",
|
||||
elementKey: "priority",
|
||||
},
|
||||
{
|
||||
label: "Destination",
|
||||
elementKey: "destination",
|
||||
renderFunction: ruleDestDisplay,
|
||||
},
|
||||
{
|
||||
label: "Delete Replication",
|
||||
elementKey: "delete_marker_replication",
|
||||
renderFunction: ruleDelDisplay,
|
||||
},
|
||||
{ label: "Status", elementKey: "status" },
|
||||
]}
|
||||
isLoading={loadingEvents}
|
||||
records={filteredRules}
|
||||
entityName="Replication Rules"
|
||||
idField="id"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: totalRecords,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: handleChangePage,
|
||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
|
||||
126
portal-ui/src/screens/Console/Buckets/actions.ts
Normal file
126
portal-ui/src/screens/Console/Buckets/actions.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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/>.
|
||||
|
||||
export const ADD_BUCKET_OPEN = "ADD_BUCKET_OPEN";
|
||||
export const ADD_BUCKET_NAME = "ADD_BUCKET_NAME";
|
||||
export const ADD_BUCKET_VERSIONED = "ADD_BUCKET_VERSIONED";
|
||||
export const ADD_BUCKET_QUOTA = "ADD_BUCKET_QUOTA";
|
||||
export const ADD_BUCKET_QUOTA_TYPE = "ADD_BUCKET_QUOTA_TYPE";
|
||||
export const ADD_BUCKET_QUOTA_SIZE = "ADD_BUCKET_QUOTA_SIZE";
|
||||
export const ADD_BUCKET_QUOTA_UNIT = "ADD_BUCKET_QUOTA_UNIT";
|
||||
export const ADD_BUCKET_RESET = "ADD_BUCKET_RESET";
|
||||
|
||||
interface AddBucketOpenAction {
|
||||
type: typeof ADD_BUCKET_OPEN;
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
interface AddBucketNameAction {
|
||||
type: typeof ADD_BUCKET_NAME;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface AddBucketVersionedAction {
|
||||
type: typeof ADD_BUCKET_VERSIONED;
|
||||
versioned: boolean;
|
||||
}
|
||||
|
||||
interface AddBucketQuotaAction {
|
||||
type: typeof ADD_BUCKET_QUOTA;
|
||||
quota: boolean;
|
||||
}
|
||||
|
||||
interface AddBucketQuotaTypeAction {
|
||||
type: typeof ADD_BUCKET_QUOTA_TYPE;
|
||||
quotaType: string;
|
||||
}
|
||||
|
||||
interface AddBucketQuotaSizeAction {
|
||||
type: typeof ADD_BUCKET_QUOTA_SIZE;
|
||||
quotaSize: string;
|
||||
}
|
||||
|
||||
interface AddBucketQuotaUnitAction {
|
||||
type: typeof ADD_BUCKET_QUOTA_UNIT;
|
||||
quotaUnit: string;
|
||||
}
|
||||
interface AddBucketResetAction {
|
||||
type: typeof ADD_BUCKET_RESET;
|
||||
}
|
||||
|
||||
export type BucketActionTypes =
|
||||
| AddBucketOpenAction
|
||||
| AddBucketNameAction
|
||||
| AddBucketVersionedAction
|
||||
| AddBucketQuotaAction
|
||||
| AddBucketQuotaTypeAction
|
||||
| AddBucketQuotaSizeAction
|
||||
| AddBucketQuotaUnitAction
|
||||
| AddBucketResetAction;
|
||||
|
||||
export function addBucketOpen(open: boolean) {
|
||||
return {
|
||||
type: ADD_BUCKET_OPEN,
|
||||
open: open,
|
||||
};
|
||||
}
|
||||
export function addBucketName(name: string) {
|
||||
return {
|
||||
type: ADD_BUCKET_NAME,
|
||||
name: name,
|
||||
};
|
||||
}
|
||||
|
||||
export function addBucketVersioned(versioned: boolean) {
|
||||
return {
|
||||
type: ADD_BUCKET_VERSIONED,
|
||||
versioned: versioned,
|
||||
};
|
||||
}
|
||||
|
||||
export function addBucketQuota(quota: boolean) {
|
||||
return {
|
||||
type: ADD_BUCKET_QUOTA,
|
||||
quota: quota,
|
||||
};
|
||||
}
|
||||
|
||||
export function addBucketQuotaType(quotaType: string) {
|
||||
return {
|
||||
type: ADD_BUCKET_QUOTA_TYPE,
|
||||
quotaType: quotaType,
|
||||
};
|
||||
}
|
||||
|
||||
export function addBucketQuotaSize(quotaSize: string) {
|
||||
return {
|
||||
type: ADD_BUCKET_QUOTA_SIZE,
|
||||
quotaSize: quotaSize,
|
||||
};
|
||||
}
|
||||
|
||||
export function addBucketQuotaUnit(quotaUnit: string) {
|
||||
return {
|
||||
type: ADD_BUCKET_QUOTA_UNIT,
|
||||
quotaUnit: quotaUnit,
|
||||
};
|
||||
}
|
||||
|
||||
export function addBucketReset() {
|
||||
return {
|
||||
type: ADD_BUCKET_RESET,
|
||||
};
|
||||
}
|
||||
102
portal-ui/src/screens/Console/Buckets/reducers.ts
Normal file
102
portal-ui/src/screens/Console/Buckets/reducers.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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 {
|
||||
ADD_BUCKET_NAME,
|
||||
ADD_BUCKET_OPEN,
|
||||
ADD_BUCKET_QUOTA,
|
||||
ADD_BUCKET_QUOTA_SIZE,
|
||||
ADD_BUCKET_QUOTA_TYPE,
|
||||
ADD_BUCKET_QUOTA_UNIT,
|
||||
ADD_BUCKET_RESET,
|
||||
ADD_BUCKET_VERSIONED,
|
||||
BucketActionTypes,
|
||||
} from "./actions";
|
||||
|
||||
export interface BucketsState {
|
||||
open: boolean;
|
||||
addBucketName: string;
|
||||
addBucketVersioning: boolean;
|
||||
addBucketQuotaEnabled: boolean;
|
||||
addBucketQuotaType: string;
|
||||
addBucketQuotaSize: string;
|
||||
addBucketQuotaUnit: string;
|
||||
}
|
||||
|
||||
const initialState: BucketsState = {
|
||||
open: false,
|
||||
addBucketName: "",
|
||||
addBucketVersioning: false,
|
||||
addBucketQuotaEnabled: false,
|
||||
addBucketQuotaType: "hard",
|
||||
addBucketQuotaSize: "1",
|
||||
addBucketQuotaUnit: "TiB",
|
||||
};
|
||||
|
||||
export function bucketsReducer(
|
||||
state = initialState,
|
||||
action: BucketActionTypes
|
||||
): BucketsState {
|
||||
switch (action.type) {
|
||||
case ADD_BUCKET_OPEN:
|
||||
return {
|
||||
...state,
|
||||
open: action.open,
|
||||
};
|
||||
case ADD_BUCKET_NAME:
|
||||
return {
|
||||
...state,
|
||||
addBucketName: action.name,
|
||||
};
|
||||
case ADD_BUCKET_VERSIONED:
|
||||
return {
|
||||
...state,
|
||||
addBucketVersioning: action.versioned,
|
||||
};
|
||||
case ADD_BUCKET_QUOTA:
|
||||
return {
|
||||
...state,
|
||||
addBucketQuotaEnabled: action.quota,
|
||||
};
|
||||
case ADD_BUCKET_QUOTA_TYPE:
|
||||
return {
|
||||
...state,
|
||||
addBucketQuotaType: action.quotaType,
|
||||
};
|
||||
case ADD_BUCKET_QUOTA_SIZE:
|
||||
return {
|
||||
...state,
|
||||
addBucketQuotaSize: action.quotaSize,
|
||||
};
|
||||
case ADD_BUCKET_QUOTA_UNIT:
|
||||
return {
|
||||
...state,
|
||||
addBucketQuotaUnit: action.quotaUnit,
|
||||
};
|
||||
case ADD_BUCKET_RESET:
|
||||
return {
|
||||
...state,
|
||||
addBucketName: "",
|
||||
addBucketVersioning: false,
|
||||
addBucketQuotaEnabled: false,
|
||||
addBucketQuotaType: "hard",
|
||||
addBucketQuotaSize: "1",
|
||||
addBucketQuotaUnit: "TiB",
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -45,3 +45,49 @@ export interface BucketEventList {
|
||||
export interface ArnList {
|
||||
arns: string[];
|
||||
}
|
||||
|
||||
export interface BucketVersioning {
|
||||
is_versioned: boolean;
|
||||
}
|
||||
|
||||
export interface BucketReplicationRuleDeleteMarker {
|
||||
status: string;
|
||||
}
|
||||
export interface BucketReplicationDestination {
|
||||
bucket: string;
|
||||
}
|
||||
export interface BucketReplicationRule {
|
||||
id: string;
|
||||
status: string;
|
||||
priority: number;
|
||||
delete_marker_replication: BucketReplicationRuleDeleteMarker;
|
||||
Destination: BucketReplicationDestination;
|
||||
}
|
||||
|
||||
export interface BucketReplication {
|
||||
rules: BucketReplicationRule[];
|
||||
}
|
||||
|
||||
export interface QuotaRequest {
|
||||
enabled: boolean;
|
||||
quota_type: string;
|
||||
amount: number;
|
||||
}
|
||||
|
||||
export interface MakeBucketRequest {
|
||||
name: string;
|
||||
versioning: boolean;
|
||||
quota?: QuotaRequest;
|
||||
}
|
||||
|
||||
export interface IRemoteBucket {
|
||||
name: string;
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
sourceBucket: string;
|
||||
targetURL: string;
|
||||
targetBucket: string;
|
||||
remoteARN: string;
|
||||
status: string;
|
||||
service: string;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { NewServiceAccount } from "./types";
|
||||
import { Button } from "@material-ui/core";
|
||||
@@ -67,6 +68,8 @@ const CredentialsPrompt = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const consoleCreds = get(newServiceAccount, "console", null);
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
@@ -87,6 +90,21 @@ const CredentialsPrompt = ({
|
||||
<b>Secret Key:</b> {newServiceAccount.secretKey}
|
||||
</li>
|
||||
</ul>
|
||||
{consoleCreds && (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<strong>Console Credentials</strong>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Access Key:</b> {consoleCreds.accessKey}
|
||||
</li>
|
||||
<li>
|
||||
<b>Secret Key:</b> {consoleCreds.secretKey}
|
||||
</li>
|
||||
</ul>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
@@ -99,11 +117,23 @@ const CredentialsPrompt = ({
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
let consoleExtras = {};
|
||||
|
||||
if (consoleCreds) {
|
||||
consoleExtras = {
|
||||
console: {
|
||||
access_key: consoleCreds.accessKey,
|
||||
secret_key: consoleCreds.secretKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
download(
|
||||
"credentials.json",
|
||||
JSON.stringify({
|
||||
access_key: newServiceAccount.accessKey,
|
||||
secret_key: newServiceAccount.secretKey,
|
||||
...consoleExtras,
|
||||
})
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -17,4 +17,10 @@
|
||||
export interface NewServiceAccount {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
console?: ConsoleSA;
|
||||
}
|
||||
|
||||
export interface ConsoleSA {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// 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, { useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import { Grid, InputLabel, Tooltip } from "@material-ui/core";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import AttachFileIcon from "@material-ui/icons/AttachFile";
|
||||
import CancelIcon from "@material-ui/icons/Cancel";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
|
||||
import { fileProcess } from "./utils";
|
||||
|
||||
interface InputBoxProps {
|
||||
label: string;
|
||||
classes: any;
|
||||
onChange: (e: string, i: string) => void;
|
||||
id: string;
|
||||
name: string;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
required?: boolean;
|
||||
error?: string;
|
||||
accept?: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic,
|
||||
...tooltipHelper,
|
||||
textBoxContainer: {
|
||||
flexGrow: 1,
|
||||
position: "relative",
|
||||
flexDirection: "column",
|
||||
},
|
||||
errorState: {
|
||||
color: "#b53b4b",
|
||||
fontSize: 14,
|
||||
position: "absolute",
|
||||
top: 7,
|
||||
right: 7,
|
||||
},
|
||||
errorText: {
|
||||
margin: "0",
|
||||
fontSize: "0.75rem",
|
||||
marginTop: 3,
|
||||
textAlign: "left",
|
||||
fontFamily: "Lato,sans-serif",
|
||||
fontWeight: 400,
|
||||
lineHeight: "1.66",
|
||||
color: "#dc1f2e",
|
||||
},
|
||||
valueString: {
|
||||
maxWidth: 350,
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
marginTop: 2,
|
||||
},
|
||||
fileReselect: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
});
|
||||
|
||||
const FileSelector = ({
|
||||
label,
|
||||
classes,
|
||||
onChange,
|
||||
id,
|
||||
name,
|
||||
disabled = false,
|
||||
tooltip = "",
|
||||
required,
|
||||
error = "",
|
||||
accept = "",
|
||||
value = "",
|
||||
}: InputBoxProps) => {
|
||||
const [showFileSelector, setShowSelector] = useState(false);
|
||||
|
||||
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>
|
||||
)}
|
||||
|
||||
{showFileSelector || value === "" ? (
|
||||
<div className={classes.textBoxContainer}>
|
||||
<input
|
||||
type="file"
|
||||
name={name}
|
||||
onChange={(e) => {
|
||||
const fileName = get(e, "target.files[0].name", "");
|
||||
fileProcess(e, (data: any) => {
|
||||
onChange(data, fileName);
|
||||
});
|
||||
}}
|
||||
accept={accept}
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
{value !== "" && (
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="upload picture"
|
||||
component="span"
|
||||
onClick={() => {
|
||||
setShowSelector(false);
|
||||
}}
|
||||
disableRipple={false}
|
||||
disableFocusRipple={false}
|
||||
>
|
||||
<CancelIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
{error !== "" && (
|
||||
<React.Fragment>
|
||||
<br />
|
||||
<span className={classes.errorText}>{error}</span>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className={classes.fileReselect}>
|
||||
<div className={classes.valueString}>{value}</div>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="upload picture"
|
||||
component="span"
|
||||
onClick={() => {
|
||||
setShowSelector(true);
|
||||
}}
|
||||
disableRipple={false}
|
||||
disableFocusRipple={false}
|
||||
>
|
||||
<AttachFileIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
)}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(FileSelector);
|
||||
@@ -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/>.
|
||||
|
||||
export const fileProcess = (evt: any, callback: any) => {
|
||||
const file = evt.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
reader.onload = () => {
|
||||
// reader.readAsDataURL(file) output will be something like: data:application/x-x509-ca-cert;base64,LS0tLS1CRUdJTiBDRVJUSU
|
||||
// we care only about the actual base64 part (everything after "data:application/x-x509-ca-cert;base64,")
|
||||
const fileBase64 = reader.result;
|
||||
if (fileBase64) {
|
||||
const fileArray = fileBase64.toString().split("base64,");
|
||||
|
||||
if (fileArray.length === 2) {
|
||||
callback(fileArray[1]);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,200 @@
|
||||
// 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, { useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { InputLabel, Switch, Tooltip } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { actionsTray, fieldBasic } from "../common/styleLibrary";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
|
||||
interface IFormSwitch {
|
||||
label: string;
|
||||
classes: any;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
value: string | boolean;
|
||||
id: string;
|
||||
name: string;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
index?: number;
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
wrapperContainer: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
borderBottom: "#9c9c9c 1px solid",
|
||||
paddingBottom: 14,
|
||||
marginBottom: 20,
|
||||
},
|
||||
...actionsTray,
|
||||
...fieldBasic,
|
||||
});
|
||||
|
||||
const StyledSwitch = withStyles({
|
||||
root: {
|
||||
alignItems: "flex-start",
|
||||
height: 18,
|
||||
padding: "0 12px",
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
},
|
||||
switchBase: {
|
||||
color: "#fff",
|
||||
padding: 0,
|
||||
top: "initial",
|
||||
"&$checked": {
|
||||
color: "#fff",
|
||||
},
|
||||
"&$checked + $track": {
|
||||
backgroundColor: "#000",
|
||||
opacity: 1,
|
||||
height: 15,
|
||||
},
|
||||
},
|
||||
checked: {},
|
||||
track: {
|
||||
height: 15,
|
||||
backgroundColor: "#000",
|
||||
opacity: 1,
|
||||
padding: 0,
|
||||
marginTop: 1.5,
|
||||
},
|
||||
thumb: {
|
||||
backgroundColor: "#fff",
|
||||
border: "#000 1px solid",
|
||||
boxShadow: "none",
|
||||
width: 18,
|
||||
height: 18,
|
||||
padding: 0,
|
||||
marginLeft: 10,
|
||||
},
|
||||
})(Switch);
|
||||
|
||||
const FormSwitchWrapper = ({
|
||||
label,
|
||||
onChange,
|
||||
value,
|
||||
id,
|
||||
name,
|
||||
checked = false,
|
||||
disabled = false,
|
||||
tooltip = "",
|
||||
classes,
|
||||
}: IFormSwitch) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={`${classes.wrapperContainer}`}>
|
||||
{label !== "" && (
|
||||
<InputLabel htmlFor={id} className={classes.inputLabel}>
|
||||
<span>{label}</span>
|
||||
{tooltip !== "" && (
|
||||
<div className={classes.tooltipContainer}>
|
||||
<Tooltip title={tooltip} placement="top-start">
|
||||
<HelpIcon className={classes.tooltip} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</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>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(FormSwitchWrapper);
|
||||
@@ -71,13 +71,16 @@ const styles = (theme: Theme) =>
|
||||
const inputStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
borderColor: "#393939",
|
||||
borderRadius: 0,
|
||||
"&::before": {
|
||||
borderColor: "#9c9c9c",
|
||||
},
|
||||
},
|
||||
input: {
|
||||
padding: "11px 20px",
|
||||
padding: "15px 5px 10px",
|
||||
color: "#393939",
|
||||
fontSize: 14,
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
},
|
||||
error: {
|
||||
color: "#b53b4b",
|
||||
@@ -160,7 +163,6 @@ const InputBoxWrapper = ({
|
||||
<InputField
|
||||
id={id}
|
||||
name={name}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
@@ -172,6 +174,10 @@ const InputBoxWrapper = ({
|
||||
error={error !== ""}
|
||||
helperText={error}
|
||||
placeholder={placeholder}
|
||||
InputLabelProps={{
|
||||
shrink: true,
|
||||
}}
|
||||
className={classes.inputRebase}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
|
||||
@@ -42,6 +42,7 @@ interface SelectProps {
|
||||
onChange: (
|
||||
e: React.ChangeEvent<{ name?: string | undefined; value: unknown }>
|
||||
) => void;
|
||||
disabled?: boolean;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
@@ -49,26 +50,25 @@ const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic,
|
||||
...tooltipHelper,
|
||||
inputLabel: {
|
||||
...fieldBasic.inputLabel,
|
||||
width: 116,
|
||||
},
|
||||
});
|
||||
|
||||
const SelectStyled = withStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
lineHeight: 1,
|
||||
"label + &": {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
},
|
||||
input: {
|
||||
borderRadius: 0,
|
||||
position: "relative",
|
||||
color: "#393939",
|
||||
fontSize: 14,
|
||||
padding: "11px 20px",
|
||||
border: "1px solid #c4c4c4",
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
padding: "15px 20px 10px 10px",
|
||||
borderBottom: "1px solid #9c9c9c",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
"&:hover": {
|
||||
borderColor: "#393939",
|
||||
},
|
||||
@@ -88,6 +88,7 @@ const SelectWrapper = ({
|
||||
label,
|
||||
tooltip = "",
|
||||
value,
|
||||
disabled = false,
|
||||
}: SelectProps) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
@@ -104,13 +105,14 @@ const SelectWrapper = ({
|
||||
)}
|
||||
</InputLabel>
|
||||
)}
|
||||
<FormControl variant="outlined" fullWidth>
|
||||
<FormControl fullWidth>
|
||||
<Select
|
||||
id={id}
|
||||
name={name}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
input={<SelectStyled />}
|
||||
disabled={disabled}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<MenuItem
|
||||
|
||||
@@ -18,28 +18,24 @@
|
||||
|
||||
export const fieldBasic = {
|
||||
inputLabel: {
|
||||
fontWeight: 500,
|
||||
fontWeight: 600,
|
||||
marginRight: 10,
|
||||
width: 160,
|
||||
fontSize: 14,
|
||||
color: "#393939",
|
||||
textAlign: "right" as const,
|
||||
display: "flex",
|
||||
textOverflow: "ellipsis",
|
||||
fontSize: 15,
|
||||
color: "#000",
|
||||
textAlign: "left" as const,
|
||||
overflow: "hidden",
|
||||
justifyContent: "flex-end",
|
||||
"& span": {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
display: "flex",
|
||||
},
|
||||
fieldLabelError: {
|
||||
paddingBottom: 22,
|
||||
},
|
||||
fieldContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: 10,
|
||||
marginBottom: 20,
|
||||
},
|
||||
tooltipContainer: {
|
||||
marginLeft: 5,
|
||||
@@ -66,16 +62,80 @@ export const tooltipHelper = {
|
||||
};
|
||||
|
||||
const checkBoxBasic = {
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderRadius: 3,
|
||||
width: 14,
|
||||
height: 14,
|
||||
borderRadius: 2,
|
||||
};
|
||||
|
||||
export const checkboxIcons = {
|
||||
unCheckedIcon: { ...checkBoxBasic, border: "1px solid #d0d0d0" },
|
||||
unCheckedIcon: { ...checkBoxBasic, border: "1px solid #c3c3c3" },
|
||||
checkedIcon: {
|
||||
...checkBoxBasic,
|
||||
border: "1px solid #201763",
|
||||
backgroundColor: "#201763",
|
||||
border: "1px solid #081C42",
|
||||
backgroundColor: "#081C42",
|
||||
},
|
||||
};
|
||||
|
||||
export const containerForHeader = (bottomSpacing: any) => ({
|
||||
container: {
|
||||
padding: "110px 33px 30px",
|
||||
paddingBottom: bottomSpacing,
|
||||
"& h6": {
|
||||
color: "#777777",
|
||||
fontSize: 14,
|
||||
},
|
||||
"& p": {
|
||||
"& span:not(*[class*='smallUnit'])": {
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const actionsTray = {
|
||||
actionsTray: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
"& button": {
|
||||
flexGrow: 0,
|
||||
marginLeft: 15,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const searchField = {
|
||||
searchField: {
|
||||
flexGrow: 1,
|
||||
marginRight: 30,
|
||||
background: "#FFFFFF",
|
||||
borderRadius: 5,
|
||||
border: "#EAEDEE 1px solid",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
padding: "0 16px",
|
||||
"& input": {
|
||||
fontSize: 14,
|
||||
color: "#000",
|
||||
"&::placeholder": {
|
||||
color: "#393939",
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const predefinedList = {
|
||||
predefinedTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
margin: "10px 0",
|
||||
},
|
||||
predefinedList: {
|
||||
backgroundColor: "#eaeaea",
|
||||
padding: "12px 10px",
|
||||
color: "#393939",
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
import React from "react";
|
||||
import { Dialog, DialogContent, DialogTitle } from "@material-ui/core";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
createStyles,
|
||||
makeStyles,
|
||||
Theme,
|
||||
withStyles,
|
||||
} from "@material-ui/core/styles";
|
||||
|
||||
interface IModalProps {
|
||||
classes: any;
|
||||
@@ -31,16 +36,16 @@ const baseCloseLine = {
|
||||
borderLeft: "2px solid #707070",
|
||||
height: 33,
|
||||
width: 1,
|
||||
position: "absolute"
|
||||
position: "absolute",
|
||||
};
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
dialogContainer: {
|
||||
padding: "12px 26px 22px"
|
||||
padding: "8px 15px 22px",
|
||||
},
|
||||
closeContainer: {
|
||||
textAlign: "right"
|
||||
textAlign: "right",
|
||||
},
|
||||
closeButton: {
|
||||
width: 45,
|
||||
@@ -48,40 +53,48 @@ const styles = (theme: Theme) =>
|
||||
padding: 0,
|
||||
backgroundColor: "initial",
|
||||
"&:hover": {
|
||||
backgroundColor: "initial"
|
||||
backgroundColor: "initial",
|
||||
},
|
||||
"&:active": {
|
||||
backgroundColor: "initial"
|
||||
}
|
||||
backgroundColor: "initial",
|
||||
},
|
||||
},
|
||||
modalCloseIcon: {
|
||||
fontSize: 35,
|
||||
color: "#707070",
|
||||
fontWeight: 300,
|
||||
"&:hover": {
|
||||
color: "#000"
|
||||
}
|
||||
color: "#000",
|
||||
},
|
||||
},
|
||||
closeIcon: {
|
||||
"&::before": {
|
||||
...baseCloseLine,
|
||||
transform: "rotate(45deg)"
|
||||
transform: "rotate(45deg)",
|
||||
},
|
||||
"&::after": {
|
||||
...baseCloseLine,
|
||||
transform: "rotate(-45deg)"
|
||||
transform: "rotate(-45deg)",
|
||||
},
|
||||
"&:hover::before, &:hover::after": {
|
||||
borderColor: "#000"
|
||||
borderColor: "#000",
|
||||
},
|
||||
width: 24,
|
||||
height: 24,
|
||||
display: "block",
|
||||
position: "relative"
|
||||
position: "relative",
|
||||
},
|
||||
titleClass: {
|
||||
padding: "0px 24px 12px"
|
||||
}
|
||||
padding: "0px 50px 12px",
|
||||
"& h2": {
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
fontSize: 22,
|
||||
},
|
||||
},
|
||||
modalContent: {
|
||||
padding: "0 50px",
|
||||
},
|
||||
});
|
||||
|
||||
const ModalWrapper = ({
|
||||
@@ -89,7 +102,7 @@ const ModalWrapper = ({
|
||||
modalOpen,
|
||||
title,
|
||||
children,
|
||||
classes
|
||||
classes,
|
||||
}: IModalProps) => {
|
||||
return (
|
||||
<Dialog
|
||||
@@ -114,7 +127,9 @@ const ModalWrapper = ({
|
||||
<DialogTitle id="alert-dialog-title" className={classes.titleClass}>
|
||||
{title}
|
||||
</DialogTitle>
|
||||
<DialogContent>{children}</DialogContent>
|
||||
<DialogContent className={classes.modalContent}>
|
||||
{children}
|
||||
</DialogContent>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import React from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
|
||||
interface IPageHeader {
|
||||
classes: any;
|
||||
label: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
headerContainer: {
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: 77,
|
||||
display: "flex",
|
||||
backgroundColor: "#fff",
|
||||
borderBottom: "#E3E3E3 1px solid",
|
||||
left: 0,
|
||||
},
|
||||
label: {
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
},
|
||||
labelStyle: {
|
||||
color: "#000",
|
||||
fontSize: 18,
|
||||
fontWeight: 700,
|
||||
marginLeft: 55,
|
||||
marginTop: 8,
|
||||
},
|
||||
});
|
||||
|
||||
const PageHeader = ({ classes, label }: IPageHeader) => {
|
||||
return (
|
||||
<Grid container className={classes.headerContainer}>
|
||||
<Grid item xs={12} className={classes.label}>
|
||||
<Typography variant="h4" className={classes.labelStyle}>
|
||||
{label}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(PageHeader);
|
||||
@@ -22,6 +22,8 @@ import DeleteIcon from "./TableActionIcons/DeleteIcon";
|
||||
import DescriptionIcon from "./TableActionIcons/DescriptionIcon";
|
||||
import CloudIcon from "./TableActionIcons/CloudIcon";
|
||||
import ConsoleIcon from "./TableActionIcons/ConsoleIcon";
|
||||
import GetAppIcon from "@material-ui/icons/GetApp";
|
||||
import SvgIcon from "@material-ui/core/SvgIcon";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
interface IActionButton {
|
||||
@@ -48,6 +50,10 @@ const defineIcon = (type: string, selected: boolean) => {
|
||||
return <CloudIcon active={selected} />;
|
||||
case "console":
|
||||
return <ConsoleIcon active={selected} />;
|
||||
case "download":
|
||||
return (
|
||||
<SvgIcon component={GetAppIcon} fontSize="small" color="primary" />
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -7,12 +7,13 @@ const PencilIcon = ({ active = false }: IIcon) => {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
viewBox="0 0 10 11.429"
|
||||
>
|
||||
<path
|
||||
fill={active ? selected : unSelected}
|
||||
d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"
|
||||
></path>
|
||||
d="M-43.375,11.429-48.35,8.664l-5.025,2.764V0h10Z"
|
||||
transform="translate(53.375)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,5 +2,5 @@ export interface IIcon {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export const unSelected = "#adadad";
|
||||
export const selected = "#201763";
|
||||
export const unSelected = "#081C42";
|
||||
export const selected = "#081C42";
|
||||
|
||||
@@ -49,6 +49,7 @@ interface IColumns {
|
||||
elementKey: string;
|
||||
sortable?: boolean;
|
||||
renderFunction?: (input: any) => any;
|
||||
globalClass?: any;
|
||||
}
|
||||
|
||||
interface IPaginatorConfig {
|
||||
@@ -101,6 +102,8 @@ const styles = (theme: Theme) =>
|
||||
flexDirection: "column",
|
||||
padding: "19px 38px",
|
||||
minHeight: "200px",
|
||||
boxShadow: "none",
|
||||
border: "#e7e7e7 1px solid",
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
@@ -118,7 +121,8 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
rowSelected: {
|
||||
...rowText,
|
||||
color: "#201763",
|
||||
color: "#081C42",
|
||||
fontWeight: 600,
|
||||
},
|
||||
paginatorContainer: {
|
||||
display: "flex",
|
||||
@@ -161,7 +165,10 @@ const styles = (theme: Theme) =>
|
||||
const titleColumnsMap = (columns: IColumns[]) => {
|
||||
return columns.map((column: IColumns, index: number) => {
|
||||
return (
|
||||
<TableCell key={`tbCT-${column.elementKey}-${index}`}>
|
||||
<TableCell
|
||||
key={`tbCT-${column.elementKey}-${index}`}
|
||||
className={column.globalClass}
|
||||
>
|
||||
{column.label}
|
||||
</TableCell>
|
||||
);
|
||||
@@ -265,7 +272,7 @@ const TableWrapper = ({
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
{records && records.length > 0 ? (
|
||||
{records && !isLoading && records.length > 0 ? (
|
||||
<Table size="small" stickyHeader={stickyHeader}>
|
||||
<TableHead className={classes.minTableHeader}>
|
||||
<TableRow>
|
||||
@@ -279,7 +286,10 @@ const TableWrapper = ({
|
||||
</TableCell>
|
||||
)}
|
||||
{titleColumnsMap(columns)}
|
||||
{itemActions && itemActions.length > 0 && (
|
||||
{((itemActions && itemActions.length > 1) ||
|
||||
(itemActions &&
|
||||
itemActions.length === 1 &&
|
||||
itemActions[0].type !== "view")) && (
|
||||
<TableCell
|
||||
align="center"
|
||||
className={classes.actionsContainer}
|
||||
@@ -329,7 +339,10 @@ const TableWrapper = ({
|
||||
</TableCell>
|
||||
)}
|
||||
{rowColumnsMap(columns, record, classes, isSelected)}
|
||||
{itemActions && itemActions.length > 0 && (
|
||||
{((itemActions && itemActions.length > 1) ||
|
||||
(itemActions &&
|
||||
itemActions.length === 1 &&
|
||||
itemActions[0].type !== "view")) && (
|
||||
<TableCell
|
||||
align="center"
|
||||
className={classes.actionsContainer}
|
||||
|
||||
@@ -27,6 +27,12 @@ import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import { configurationElements } from "../utils";
|
||||
import { IConfigurationElement } from "../types";
|
||||
import EditConfiguration from "../CustomForms/EditConfiguration";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||
|
||||
interface IListConfiguration {
|
||||
classes: any;
|
||||
@@ -43,21 +49,12 @@ const styles = (theme: Theme) =>
|
||||
keyName: {
|
||||
marginLeft: 5,
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
},
|
||||
searchField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
},
|
||||
iconText: {
|
||||
lineHeight: "24px",
|
||||
},
|
||||
...searchField,
|
||||
...actionsTray,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||
@@ -103,47 +100,44 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||
selectedConfiguration={selectedConfiguration}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label="Configurations List" />
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Configurations List</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Filter"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(event) => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Configuration", elementKey: "configuration_id" },
|
||||
]}
|
||||
isLoading={false}
|
||||
records={filteredRecords}
|
||||
entityName="Configurations"
|
||||
idField="configuration_id"
|
||||
/>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Filter"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(event) => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Configuration", elementKey: "configuration_id" },
|
||||
]}
|
||||
isLoading={false}
|
||||
records={filteredRecords}
|
||||
entityName="Configurations"
|
||||
idField="configuration_id"
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -51,8 +51,6 @@ import Permissions from "./Permissions/Permissions";
|
||||
import Dashboard from "./Dashboard/Dashboard";
|
||||
import Menu from "./Menu/Menu";
|
||||
import api from "../../common/api";
|
||||
import storage from "local-storage-fallback";
|
||||
import NotFoundPage from "../NotFoundPage";
|
||||
import ServiceAccounts from "./ServiceAccounts/ServiceAccounts";
|
||||
import Users from "./Users/Users";
|
||||
import Groups from "./Groups/Groups";
|
||||
@@ -69,6 +67,9 @@ import { ISessionResponse } from "./types";
|
||||
import { saveSessionResponse } from "./actions";
|
||||
import TenantDetails from "./Tenants/TenantDetails/TenantDetails";
|
||||
import { clearSession } from "../../common/utils";
|
||||
import ObjectBrowser from "./ObjectBrowser/ObjectBrowser";
|
||||
import ListObjects from "./Buckets/ListBuckets/Objects/ListObjects/ListObjects";
|
||||
import License from "./License/License";
|
||||
|
||||
function Copyright() {
|
||||
return (
|
||||
@@ -83,7 +84,7 @@ function Copyright() {
|
||||
);
|
||||
}
|
||||
|
||||
const drawerWidth = 254;
|
||||
const drawerWidth = 245;
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -135,6 +136,9 @@ const styles = (theme: Theme) =>
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
overflowX: "hidden",
|
||||
background:
|
||||
"transparent linear-gradient(90deg, #073052 0%, #081C42 100%) 0% 0% no-repeat padding-box",
|
||||
boxShadow: "0px 3px 7px #00000014",
|
||||
},
|
||||
drawerPaperClose: {
|
||||
overflowX: "hidden",
|
||||
@@ -147,17 +151,15 @@ const styles = (theme: Theme) =>
|
||||
width: theme.spacing(9),
|
||||
},
|
||||
},
|
||||
appBarSpacer: {
|
||||
height: "5px",
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
height: "100vh",
|
||||
overflow: "auto",
|
||||
position: "relative",
|
||||
},
|
||||
container: {
|
||||
paddingTop: theme.spacing(4),
|
||||
paddingBottom: theme.spacing(4),
|
||||
margin: 0,
|
||||
},
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
@@ -253,6 +255,14 @@ const Console = ({
|
||||
component: Buckets,
|
||||
path: "/buckets/:bucketName",
|
||||
},
|
||||
{
|
||||
component: ObjectBrowser,
|
||||
path: "/object-browser",
|
||||
},
|
||||
{
|
||||
component: ListObjects,
|
||||
path: "/object-browser/:bucket?",
|
||||
},
|
||||
{
|
||||
component: Watch,
|
||||
path: "/watch",
|
||||
@@ -311,7 +321,11 @@ const Console = ({
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/tenants/:tenantName",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName",
|
||||
},
|
||||
{
|
||||
component: License,
|
||||
path: "/license",
|
||||
},
|
||||
];
|
||||
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);
|
||||
@@ -359,8 +373,7 @@ const Console = ({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.appBarSpacer} />
|
||||
<Container maxWidth="lg" className={classes.container}>
|
||||
<Container className={classes.container}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
{allowedRoutes.map((route: any) => (
|
||||
|
||||
@@ -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
|
||||
@@ -27,6 +27,11 @@ import { Usage } from "./types";
|
||||
import api from "../../../common/api";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import { LinearProgress } from "@material-ui/core";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import AllBucketsIcon from "../../../icons/AllBucketsIcon";
|
||||
import UsageIcon from "../../../icons/UsageIcon";
|
||||
import EgressIcon from "../../../icons/EgressIcon";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -77,26 +82,24 @@ const styles = (theme: Theme) =>
|
||||
height: "100vh",
|
||||
overflow: "auto",
|
||||
},
|
||||
container: {
|
||||
paddingBottom: theme.spacing(4),
|
||||
"& h6": {
|
||||
color: "#777777",
|
||||
fontSize: 14,
|
||||
},
|
||||
"& p": {
|
||||
"& span": {
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
paper: {
|
||||
padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
border: "#eaedee 1px solid",
|
||||
borderRadius: 5,
|
||||
boxShadow: "none",
|
||||
},
|
||||
fixedHeight: {
|
||||
minHeight: 240,
|
||||
height: 165,
|
||||
minWidth: 247,
|
||||
marginRight: 20,
|
||||
padding: "25px 28px",
|
||||
"& svg": {
|
||||
maxHeight: 18,
|
||||
},
|
||||
},
|
||||
consumptionValue: {
|
||||
color: "#000000",
|
||||
@@ -107,6 +110,35 @@ const styles = (theme: Theme) =>
|
||||
marginRight: 10,
|
||||
color: "#777777",
|
||||
},
|
||||
notationContainer: {
|
||||
display: "flex",
|
||||
},
|
||||
dashboardBG: {
|
||||
width: 390,
|
||||
height: 255,
|
||||
zIndex: 500,
|
||||
position: "absolute",
|
||||
backgroundSize: "fill",
|
||||
|
||||
backgroundImage: "url(/images/BG_IllustrationDarker.svg)",
|
||||
backgroundPosition: "right bottom",
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundRepeat: "no-repeat",
|
||||
},
|
||||
dashboardContainer: {
|
||||
zIndex: 600,
|
||||
position: "absolute",
|
||||
},
|
||||
elementTitle: {
|
||||
fontWeight: 500,
|
||||
color: "#777777",
|
||||
fontSize: 14,
|
||||
marginTop: -9,
|
||||
},
|
||||
smallUnit: {
|
||||
fontSize: 20,
|
||||
},
|
||||
});
|
||||
|
||||
interface IDashboardProps {
|
||||
@@ -142,7 +174,19 @@ const Dashboard = ({ classes }: IDashboardProps) => {
|
||||
if (usage === undefined) {
|
||||
return "0";
|
||||
}
|
||||
return niceBytes(usage);
|
||||
|
||||
const niceBytesUsage = niceBytes(usage).split(" ");
|
||||
|
||||
if (niceBytesUsage.length !== 2) {
|
||||
return niceBytesUsage.join(" ");
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{niceBytesUsage[0]}
|
||||
<span className={classes.smallUnit}>{niceBytesUsage[1]}</span>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const prettyNumber = (usage: number | undefined) => {
|
||||
@@ -155,11 +199,10 @@ const Dashboard = ({ classes }: IDashboardProps) => {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid container>
|
||||
<PageHeader label="Dashboard" />
|
||||
<div className={classes.dashboardBG} />
|
||||
<Grid container className={classes.dashboardContainer}>
|
||||
<Grid container spacing={3} className={classes.container}>
|
||||
<Grid container>
|
||||
<Typography variant="h2">MinIO Console</Typography>
|
||||
</Grid>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
{loading ? (
|
||||
<Grid item xs={12} md={12} lg={12}>
|
||||
@@ -167,50 +210,53 @@ const Dashboard = ({ classes }: IDashboardProps) => {
|
||||
</Grid>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} md={4} lg={4}>
|
||||
<Grid item className={classes.notationContainer}>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<ViewHeadlineIcon />
|
||||
<AllBucketsIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="h6">Total Buckets</Typography>
|
||||
<Typography className={classes.elementTitle}>
|
||||
All buckets
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyNumber(usage.buckets) : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4} lg={4}>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<NetworkCheckIcon />
|
||||
<UsageIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="h6"> Total Objects</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyNumber(usage.objects) : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4} lg={4}>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<PieChartIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography variant="h6">Usage</Typography>
|
||||
<Typography className={classes.elementTitle}>
|
||||
Usage
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyUsage(usage.usage + "") : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Paper className={fixedHeightPaper}>
|
||||
<Grid container direction="row" alignItems="center">
|
||||
<Grid item className={classes.icon}>
|
||||
<EgressIcon />
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Typography className={classes.elementTitle}>
|
||||
{" "}
|
||||
Total Objects
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography className={classes.consumptionValue}>
|
||||
{usage ? prettyNumber(usage.objects) : 0}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
@@ -31,6 +31,12 @@ import AddGroup from "../Groups/AddGroup";
|
||||
import DeleteGroup from "./DeleteGroup";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import SetPolicy from "../Policies/SetPolicy";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
|
||||
interface IGroupsProps {
|
||||
classes: any;
|
||||
@@ -72,18 +78,9 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
},
|
||||
searchField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const Groups = ({ classes }: IGroupsProps) => {
|
||||
@@ -215,71 +212,68 @@ const Groups = ({ classes }: IGroupsProps) => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label={"Groups"} />
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Groups</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{error !== "" ? <Grid container>{error}</Grid> : <React.Fragment />}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Groups"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setFilter(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setSelectedGroup(null);
|
||||
setGroupOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Group
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
{error !== "" ? <Grid container>{error}</Grid> : <React.Fragment />}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Groups"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setFilter(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setSelectedGroup(null);
|
||||
setGroupOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Group
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Groups"
|
||||
idField=""
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: totalRecords,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: handleChangePage,
|
||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Groups"
|
||||
idField=""
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: totalRecords,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: handleChangePage,
|
||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -166,7 +166,7 @@ const UsersSelectors = ({
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title>Members</Title>
|
||||
<Title>Assign Users</Title>
|
||||
{error !== "" ? <div>{error}</div> : <React.Fragment />}
|
||||
<Grid item xs={12}>
|
||||
<Paper className={classes.paper}>
|
||||
|
||||
@@ -15,6 +15,8 @@ import { FormControl, MenuItem, Select } from "@material-ui/core";
|
||||
import { BucketList, Bucket } from "../Watch/types";
|
||||
import { HealStatus, colorH } from "./types";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -58,6 +60,7 @@ const styles = (theme: Theme) =>
|
||||
lastElementWPadding: {
|
||||
paddingRight: "78",
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface IHeal {
|
||||
@@ -195,130 +198,127 @@ const Heal = ({ classes }: IHeal) => {
|
||||
}));
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageHeader label="Heal" />
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Heal</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<FormControl variant="outlined">
|
||||
<Select
|
||||
id="bucket-name"
|
||||
name="bucket-name"
|
||||
value={bucketName}
|
||||
onChange={(e) => {
|
||||
setBucketName(e.target.value as string);
|
||||
}}
|
||||
className={classes.fieldContainer}
|
||||
disabled={false}
|
||||
>
|
||||
<MenuItem value="" key={`select-bucket-name-default`}>
|
||||
Select Bucket
|
||||
</MenuItem>
|
||||
{bucketNames.map((option) => (
|
||||
<MenuItem
|
||||
value={option.value}
|
||||
key={`select-bucket-name-${option.label}`}
|
||||
>
|
||||
{option.label}
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<FormControl variant="outlined">
|
||||
<Select
|
||||
id="bucket-name"
|
||||
name="bucket-name"
|
||||
value={bucketName}
|
||||
onChange={(e) => {
|
||||
setBucketName(e.target.value as string);
|
||||
}}
|
||||
className={classes.fieldContainer}
|
||||
disabled={false}
|
||||
>
|
||||
<MenuItem value="" key={`select-bucket-name-default`}>
|
||||
Select Bucket
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<TextField
|
||||
placeholder="Prefix"
|
||||
className={classes.inputField}
|
||||
id="prefix-resource"
|
||||
label=""
|
||||
disabled={false}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setPrefix(e.target.value);
|
||||
{bucketNames.map((option) => (
|
||||
<MenuItem
|
||||
value={option.value}
|
||||
key={`select-bucket-name-${option.label}`}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<TextField
|
||||
placeholder="Prefix"
|
||||
className={classes.inputField}
|
||||
id="prefix-resource"
|
||||
label=""
|
||||
disabled={false}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setPrefix(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={start}
|
||||
onClick={() => setStart(true)}
|
||||
>
|
||||
Start
|
||||
</Button>
|
||||
<Grid item xs={12}>
|
||||
<span>{"Recursive"}</span>
|
||||
<Checkbox
|
||||
name="recursive"
|
||||
id="recursive"
|
||||
value="recursive"
|
||||
color="primary"
|
||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||
checked={recursive}
|
||||
onChange={(e) => {
|
||||
setRecursive(e.target.checked);
|
||||
}}
|
||||
disabled={false}
|
||||
/>
|
||||
<span>{"Force Start"}</span>
|
||||
<Checkbox
|
||||
name="recursive"
|
||||
id="recursive"
|
||||
value="recursive"
|
||||
color="primary"
|
||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||
checked={forceStart}
|
||||
onChange={(e) => {
|
||||
setForceStart(e.target.checked);
|
||||
}}
|
||||
disabled={false}
|
||||
/>
|
||||
<span>{"Force Stop"}</span>
|
||||
<Checkbox
|
||||
name="recursive"
|
||||
id="recursive"
|
||||
value="recursive"
|
||||
color="primary"
|
||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||
checked={forceStop}
|
||||
onChange={(e) => {
|
||||
setForceStop(e.target.checked);
|
||||
}}
|
||||
disabled={false}
|
||||
/>
|
||||
<span className={classes.lastElementWPadding}>{""}</span>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<HorizontalBar
|
||||
data={data}
|
||||
width={100}
|
||||
height={30}
|
||||
options={{
|
||||
title: {
|
||||
display: true,
|
||||
text: "Item's Health Status [%]",
|
||||
fontSize: 20,
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: "right",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={start}
|
||||
onClick={() => setStart(true)}
|
||||
>
|
||||
Start
|
||||
</Button>
|
||||
<Grid item xs={12}>
|
||||
<span>{"Recursive"}</span>
|
||||
<Checkbox
|
||||
name="recursive"
|
||||
id="recursive"
|
||||
value="recursive"
|
||||
color="primary"
|
||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||
checked={recursive}
|
||||
onChange={(e) => {
|
||||
setRecursive(e.target.checked);
|
||||
}}
|
||||
disabled={false}
|
||||
/>
|
||||
<span>{"Force Start"}</span>
|
||||
<Checkbox
|
||||
name="recursive"
|
||||
id="recursive"
|
||||
value="recursive"
|
||||
color="primary"
|
||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||
checked={forceStart}
|
||||
onChange={(e) => {
|
||||
setForceStart(e.target.checked);
|
||||
}}
|
||||
disabled={false}
|
||||
/>
|
||||
<span>{"Force Stop"}</span>
|
||||
<Checkbox
|
||||
name="recursive"
|
||||
id="recursive"
|
||||
value="recursive"
|
||||
color="primary"
|
||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||
checked={forceStop}
|
||||
onChange={(e) => {
|
||||
setForceStop(e.target.checked);
|
||||
}}
|
||||
disabled={false}
|
||||
/>
|
||||
<span className={classes.lastElementWPadding}>{""}</span>
|
||||
<br />
|
||||
Size scanned: {hStatus.sizeScanned}
|
||||
<br />
|
||||
Objects healed: {hStatus.objectsHealed} / {hStatus.objectsScanned}
|
||||
<br />
|
||||
Healing time: {hStatus.healDuration}s
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<HorizontalBar
|
||||
data={data}
|
||||
width={100}
|
||||
height={30}
|
||||
options={{
|
||||
title: {
|
||||
display: true,
|
||||
text: "Item's Health Status [%]",
|
||||
fontSize: 20,
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
position: "right",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
Size scanned: {hStatus.sizeScanned}
|
||||
<br />
|
||||
Objects healed: {hStatus.objectsHealed} / {hStatus.objectsScanned}
|
||||
<br />
|
||||
Healing time: {hStatus.healDuration}s
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
335
portal-ui/src/screens/Console/License/License.tsx
Normal file
335
portal-ui/src/screens/Console/License/License.tsx
Normal file
@@ -0,0 +1,335 @@
|
||||
// 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 clsx from "clsx";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import { planDetails, planItems, planButtons } from "./utils";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
pageTitle: {
|
||||
fontSize: 18,
|
||||
marginBottom: 20,
|
||||
},
|
||||
paper: {
|
||||
padding: "20px 52px 20px 28px",
|
||||
},
|
||||
tableContainer: {
|
||||
marginLeft: 28,
|
||||
},
|
||||
detailsContainer: {
|
||||
textAlign: "center",
|
||||
paddingTop: 18,
|
||||
paddingBottom: 12,
|
||||
borderRadius: "3px 3px 0 0",
|
||||
marginLeft: 8,
|
||||
maxWidth: "calc(25% - 8px)",
|
||||
},
|
||||
detailsContainerBorder: {
|
||||
border: "1px solid #e2e2e2",
|
||||
borderBottom: 0,
|
||||
},
|
||||
detailsContainerBorderHighlighted: {
|
||||
border: "1px solid #9a93ad",
|
||||
borderBottom: 0,
|
||||
},
|
||||
detailsTitle: {
|
||||
fontSize: 17,
|
||||
fontWeight: 700,
|
||||
marginBottom: 26,
|
||||
},
|
||||
detailsPrice: {
|
||||
fontSize: 12,
|
||||
fontWeight: 700,
|
||||
marginBottom: 8,
|
||||
},
|
||||
detailsCapacityMax: {
|
||||
minHeight: 28,
|
||||
fontSize: 10,
|
||||
fontWeight: 700,
|
||||
marginBottom: 12,
|
||||
padding: "0% 15%",
|
||||
color: "#474747",
|
||||
},
|
||||
detailsCapacityMin: {
|
||||
fontSize: 10,
|
||||
},
|
||||
itemContainer: {
|
||||
height: 36,
|
||||
borderTop: "1px solid #e5e5e5",
|
||||
},
|
||||
itemContainerDetail: {
|
||||
height: 48,
|
||||
borderTop: "1px solid #e5e5e5",
|
||||
},
|
||||
item: {
|
||||
height: "100%",
|
||||
borderLeft: "1px solid #e2e2e2",
|
||||
borderRight: "1px solid #e2e2e2",
|
||||
textAlign: "center",
|
||||
fontSize: 10,
|
||||
fontWeight: 700,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
alignContent: "center",
|
||||
marginLeft: 8,
|
||||
maxWidth: "calc(25% - 8px)",
|
||||
},
|
||||
itemFirst: {
|
||||
borderLeft: 0,
|
||||
borderRight: 0,
|
||||
},
|
||||
itemHighlighted: {
|
||||
borderLeft: "1px solid #9a93ad",
|
||||
borderRight: "1px solid #9a93ad",
|
||||
},
|
||||
field: {
|
||||
textAlign: "left",
|
||||
fontWeight: 400,
|
||||
},
|
||||
checkIcon: {
|
||||
height: 12,
|
||||
color:
|
||||
"transparent linear-gradient(90deg, #073052 0%, #081c42 100%) 0% 0% no-repeat padding-box",
|
||||
},
|
||||
buttonContainer: {
|
||||
paddingTop: 8,
|
||||
paddingBottom: 24,
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
borderRadius: "0 0 3px 3px",
|
||||
border: "1px solid #e2e2e2",
|
||||
borderTop: 0,
|
||||
marginLeft: 8,
|
||||
maxWidth: "calc(25% - 8px)",
|
||||
},
|
||||
buttonContainerBlank: {
|
||||
border: 0,
|
||||
},
|
||||
buttonContainerHighlighted: {
|
||||
border: "1px solid #9a93ad",
|
||||
borderTop: 0,
|
||||
},
|
||||
button: {
|
||||
textTransform: "none",
|
||||
fontSize: 15,
|
||||
fontWeight: 700,
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface ILicense {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const License = ({ classes }: ILicense) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageHeader label="License" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Paper className={classes.paper}>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h6"
|
||||
className={classes.pageTitle}
|
||||
>
|
||||
Upgrade to commercial license
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid container item xs={12} className={classes.tableContainer}>
|
||||
<Grid container item xs={12}>
|
||||
<Grid item xs={3} className={classes.detailsContainer} />
|
||||
{planDetails.map((details: any) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(
|
||||
classes.detailsContainer,
|
||||
classes.detailsContainerBorder,
|
||||
{
|
||||
[classes.detailsContainerBorderHighlighted]:
|
||||
details.title !== "Community",
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Grid item xs={12} className={classes.detailsTitle}>
|
||||
{details.title}
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.detailsPrice}>
|
||||
{details.price}
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={classes.detailsCapacityMax}
|
||||
>
|
||||
{details.capacityMax || ""}
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={classes.detailsCapacityMin}
|
||||
>
|
||||
{details.capacityMin}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
{planItems.map((item: any) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={12}
|
||||
className={clsx(
|
||||
classes.itemContainer,
|
||||
item.communityDetail && classes.itemContainerDetail
|
||||
)}
|
||||
>
|
||||
<Grid
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(
|
||||
classes.item,
|
||||
classes.field,
|
||||
classes.itemFirst
|
||||
)}
|
||||
>
|
||||
{item.field}
|
||||
</Grid>
|
||||
<Grid container item xs={3} className={classes.item}>
|
||||
<Grid item xs={12}>
|
||||
{item.community === "N/A" ? (
|
||||
""
|
||||
) : item.community === "Yes" ? (
|
||||
<CheckCircleIcon className={classes.checkIcon} />
|
||||
) : (
|
||||
item.community
|
||||
)}
|
||||
</Grid>
|
||||
{item.communityDetail !== undefined && (
|
||||
<Grid item xs={12}>
|
||||
{item.communityDetail}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(classes.item, classes.itemHighlighted)}
|
||||
>
|
||||
<Grid item xs={12}>
|
||||
{item.standard === "N/A" ? (
|
||||
""
|
||||
) : item.standard === "Yes" ? (
|
||||
<CheckCircleIcon className={classes.checkIcon} />
|
||||
) : (
|
||||
item.standard
|
||||
)}
|
||||
</Grid>
|
||||
{item.standardDetail !== undefined && (
|
||||
<Grid item xs={12}>
|
||||
{item.standardDetail}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(classes.item, classes.itemHighlighted)}
|
||||
>
|
||||
<Grid item xs={12}>
|
||||
{item.enterprise === "N/A" ? (
|
||||
""
|
||||
) : item.enterprise === "Yes" ? (
|
||||
<CheckCircleIcon className={classes.checkIcon} />
|
||||
) : (
|
||||
item.enterprise
|
||||
)}
|
||||
</Grid>
|
||||
{item.enterpriseDetail !== undefined && (
|
||||
<Grid item xs={12}>
|
||||
{item.enterpriseDetail}
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
<Grid container item xs={12}>
|
||||
<Grid
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(
|
||||
classes.buttonContainer,
|
||||
classes.buttonContainerBlank
|
||||
)}
|
||||
/>
|
||||
{planButtons.map((button: any) => {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={3}
|
||||
className={clsx(classes.buttonContainer, {
|
||||
[classes.buttonContainerHighlighted]:
|
||||
button.text === "Subscribe",
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
variant={
|
||||
button.text === "Subscribe"
|
||||
? "contained"
|
||||
: "outlined"
|
||||
}
|
||||
color="primary"
|
||||
className={classes.button}
|
||||
target="_blank"
|
||||
href={button.link}
|
||||
>
|
||||
{button.text}
|
||||
</Button>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(License);
|
||||
119
portal-ui/src/screens/Console/License/utils.ts
Normal file
119
portal-ui/src/screens/Console/License/utils.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
// 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/>.
|
||||
|
||||
export const planDetails = [
|
||||
{
|
||||
title: "Community",
|
||||
price: "Free",
|
||||
capacityMin: "(No minimum)",
|
||||
},
|
||||
{
|
||||
title: "Standard",
|
||||
price: "$10/TB/month",
|
||||
capacityMax: "Up to 10PB. No additional charges for capacity over 10PB",
|
||||
capacityMin: "(25TB minimum)",
|
||||
},
|
||||
{
|
||||
title: "Enterprise",
|
||||
price: "$20/TB/month",
|
||||
capacityMax: "Up to 5PB. No additional charges for capacity over 5PB",
|
||||
capacityMin: "(100TB minimum)",
|
||||
},
|
||||
];
|
||||
|
||||
export const planItems = [
|
||||
{
|
||||
field: "License",
|
||||
community: "100% Open Source",
|
||||
communityDetail: "Apache License v2, GNU AGPL v3",
|
||||
standard: "Dual License",
|
||||
standardDetail: "Commercial + Open Source",
|
||||
enterprise: "Dual License",
|
||||
enterpriseDetail: "Commercial + Open Source",
|
||||
},
|
||||
{
|
||||
field: "Software Release",
|
||||
community: "Update to latest",
|
||||
standard: "1 Year Long Term Support",
|
||||
enterprise: "5 Years Long Term Support",
|
||||
},
|
||||
{
|
||||
field: "SLA",
|
||||
community: "No SLA",
|
||||
standard: "<24 hours",
|
||||
enterprise: "<1 hour",
|
||||
},
|
||||
{
|
||||
field: "Support",
|
||||
community: "Community:",
|
||||
communityDetail: "Public Slack Channel + Github Issues",
|
||||
standard: "24x7 L4 direct engineering",
|
||||
standardDetail: "Support via SUBNET",
|
||||
enterprise: "24x7 L4 direct engineering",
|
||||
enterpriseDetail: "Support via SUBNET",
|
||||
},
|
||||
{
|
||||
field: "Security Updates & Critical Bugs",
|
||||
community: "Self Update",
|
||||
standard: "Guided Update",
|
||||
enterprise: "Guided Update",
|
||||
},
|
||||
{
|
||||
field: "Panic Button",
|
||||
community: "N/A",
|
||||
standard: "1 per year",
|
||||
enterprise: "Unlimited",
|
||||
},
|
||||
{
|
||||
field: "Annual Architecture Review",
|
||||
community: "N/A",
|
||||
standard: "Yes",
|
||||
enterprise: "Yes",
|
||||
},
|
||||
{
|
||||
field: "Annual Performance Review",
|
||||
community: "N/A",
|
||||
standard: "Yes",
|
||||
enterprise: "Yes",
|
||||
},
|
||||
{
|
||||
field: "Indemnification",
|
||||
community: "N/A",
|
||||
standard: "N/A",
|
||||
enterprise: "Yes",
|
||||
},
|
||||
{
|
||||
field: "Security + Policy Review",
|
||||
community: "N/A",
|
||||
standard: "N/A",
|
||||
enterprise: "Yes",
|
||||
},
|
||||
];
|
||||
|
||||
export const planButtons = [
|
||||
{
|
||||
text: "Slack Community",
|
||||
link: "https://slack.min.io",
|
||||
},
|
||||
{
|
||||
text: "Subscribe",
|
||||
link: "https://min.io/pricing",
|
||||
},
|
||||
{
|
||||
text: "Subscribe",
|
||||
link: "https://min.io/pricing",
|
||||
},
|
||||
];
|
||||
@@ -23,6 +23,9 @@ 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 PageHeader from "../Common/PageHeader/PageHeader";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -54,6 +57,7 @@ const styles = (theme: Theme) =>
|
||||
ansidefault: {
|
||||
color: "black",
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface ILogs {
|
||||
@@ -243,16 +247,20 @@ const Logs = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Logs</h1>
|
||||
<div className={classes.logList}>
|
||||
<ul>
|
||||
{messages.map((m) => {
|
||||
return renderLog(m);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
import WebAssetIcon from "@material-ui/icons/WebAsset";
|
||||
import HealingIcon from "@material-ui/icons/Healing";
|
||||
import DescriptionIcon from "@material-ui/icons/Description";
|
||||
import FileCopyIcon from "@material-ui/icons/FileCopy";
|
||||
import Collapse from "@material-ui/core/Collapse";
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
import List from "@material-ui/core/List";
|
||||
@@ -51,48 +53,61 @@ import {
|
||||
WarpIcon,
|
||||
} from "../../../icons";
|
||||
import { clearSession } from "../../../common/utils";
|
||||
import HealIcon from "../../../icons/HealIcon";
|
||||
import ConsoleIcon from "../../../icons/ConsoleIcon";
|
||||
import LicenseIcon from "../../../icons/LicenseIcon";
|
||||
import LogoutIcon from "../../../icons/LogoutIcon";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
logo: {
|
||||
paddingTop: "42px",
|
||||
marginBottom: "20px",
|
||||
textAlign: "center",
|
||||
paddingTop: 25,
|
||||
marginBottom: 30,
|
||||
paddingLeft: 45,
|
||||
"& img": {
|
||||
width: "120px",
|
||||
width: 120,
|
||||
},
|
||||
},
|
||||
menuList: {
|
||||
"& .active": {
|
||||
borderTopLeftRadius: "3px",
|
||||
borderBottomLeftRadius: "3px",
|
||||
borderTopLeftRadius: 2,
|
||||
borderBottomLeftRadius: 2,
|
||||
color: "#fff",
|
||||
background:
|
||||
"transparent linear-gradient(90deg, #362585 0%, #362585 7%, #281B6F 39%, #1F1661 100%) 0% 0% no-repeat padding-box;",
|
||||
boxShadow: "4px 4px 4px #A5A5A512",
|
||||
fontWeight: 700,
|
||||
backgroundColor: "rgba(255, 255, 255, .18)",
|
||||
"& .MuiSvgIcon-root": {
|
||||
color: "white",
|
||||
},
|
||||
"& .MuiTypography-root": {
|
||||
color: "#fff",
|
||||
fontWeight: 700,
|
||||
},
|
||||
},
|
||||
paddingLeft: "30px",
|
||||
"& .MuiSvgIcon-root": {
|
||||
fontSize: 16,
|
||||
color: "#362585",
|
||||
color: "rgba(255, 255, 255, 0.8)",
|
||||
maxWidth: 14,
|
||||
},
|
||||
"& .MuiListItemIcon-root": {
|
||||
minWidth: "25px",
|
||||
minWidth: 25,
|
||||
},
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "12px",
|
||||
color: "#2e2e2e",
|
||||
fontSize: 12,
|
||||
color: "rgba(255, 255, 255, 0.8)",
|
||||
},
|
||||
"& .MuiListItem-gutters": {
|
||||
paddingRight: 0,
|
||||
fontWeight: 300,
|
||||
},
|
||||
"& .MuiListItem-root": {
|
||||
padding: "2px 0 2px 16px",
|
||||
marginBottom: 8,
|
||||
marginLeft: 30,
|
||||
width: "calc(100% - 30px)",
|
||||
},
|
||||
"& .MuiCollapse-container .MuiCollapse-wrapper .MuiCollapse-wrapperInner .MuiDivider-root": {
|
||||
backgroundColor: "rgba(112,112,112,0.5)",
|
||||
marginBottom: 12,
|
||||
height: 1,
|
||||
},
|
||||
},
|
||||
extraMargin: {
|
||||
@@ -101,31 +116,34 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
groupTitle: {
|
||||
color: "#220c7c",
|
||||
color: "#fff",
|
||||
fontSize: 10,
|
||||
textTransform: "uppercase",
|
||||
fontWeight: 700,
|
||||
marginBottom: 3,
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
subTitleMenu: {
|
||||
fontWeight: 700,
|
||||
marginLeft: 10,
|
||||
"&.MuiTypography-root": {
|
||||
fontSize: 13,
|
||||
color: "#220c7c",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
selectorArrow: {
|
||||
marginLeft: 3,
|
||||
marginRight: 20,
|
||||
marginTop: 1,
|
||||
display: "inline-block",
|
||||
width: 0,
|
||||
height: 0,
|
||||
borderStyle: "solid",
|
||||
borderWidth: "3px 2.5px 0 2.5px",
|
||||
borderColor: "#220C7C transparent transparent transparent",
|
||||
borderWidth: "4px 4px 0 4px",
|
||||
borderColor:
|
||||
"rgba(255, 255, 255, .29) transparent transparent transparent",
|
||||
transform: "rotateZ(0deg)",
|
||||
transitionDuration: "0.2s",
|
||||
},
|
||||
@@ -185,9 +203,9 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
group: "User",
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/buckets",
|
||||
name: "Buckets",
|
||||
icon: <BucketsIcon />,
|
||||
to: "/object-browser",
|
||||
name: "Object Browser",
|
||||
icon: <DescriptionIcon />,
|
||||
},
|
||||
{
|
||||
group: "User",
|
||||
@@ -197,6 +215,14 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
name: "Service Accounts",
|
||||
icon: <ServiceAccountsIcon />,
|
||||
},
|
||||
{
|
||||
group: "Admin",
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/buckets",
|
||||
name: "Buckets",
|
||||
icon: <BucketsIcon />,
|
||||
},
|
||||
{
|
||||
group: "Admin",
|
||||
type: "item",
|
||||
@@ -226,8 +252,8 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/logs",
|
||||
name: "Console Logs",
|
||||
icon: <WebAssetIcon />,
|
||||
name: "Logs",
|
||||
icon: <ConsoleIcon />,
|
||||
},
|
||||
{
|
||||
group: "Tools",
|
||||
@@ -251,13 +277,7 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
component: NavLink,
|
||||
to: "/heal",
|
||||
name: "Heal",
|
||||
icon: <HealingIcon />,
|
||||
},
|
||||
{
|
||||
group: "Admin",
|
||||
type: "title",
|
||||
name: "Configurations",
|
||||
component: Typography,
|
||||
icon: <HealIcon />,
|
||||
},
|
||||
{
|
||||
group: "Admin",
|
||||
@@ -266,7 +286,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
to: "/notification-endpoints",
|
||||
name: "Lambda Notifications",
|
||||
icon: <LambdaNotificationsIcon />,
|
||||
extraMargin: true,
|
||||
},
|
||||
{
|
||||
group: "Admin",
|
||||
@@ -275,7 +294,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
to: "/configurations-list",
|
||||
name: "Configurations List",
|
||||
icon: <ConfigurationsListIcon />,
|
||||
extraMargin: true,
|
||||
},
|
||||
{
|
||||
group: "Operator",
|
||||
@@ -301,6 +319,14 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
name: "Warp",
|
||||
icon: <WarpIcon />,
|
||||
},
|
||||
{
|
||||
group: "License",
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/license",
|
||||
name: "License",
|
||||
icon: <LicenseIcon />,
|
||||
},
|
||||
];
|
||||
|
||||
const allowedPages = pages.reduce((result: any, item: any, index: any) => {
|
||||
@@ -351,7 +377,7 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{groupMember.label}
|
||||
<span>{groupMember.label}</span>
|
||||
{groupMember.collapsible && (
|
||||
<span
|
||||
className={`${classes.selectorArrow} ${
|
||||
@@ -413,7 +439,7 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
||||
|
||||
<ListItem button onClick={logout}>
|
||||
<ListItemIcon>
|
||||
<ExitToApp />
|
||||
<LogoutIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Logout" />
|
||||
</ListItem>
|
||||
|
||||
@@ -20,4 +20,5 @@ export const menuGroups = [
|
||||
{ label: "Admin", group: "Admin", collapsible: true },
|
||||
{ label: "Tools", group: "Tools", collapsible: true },
|
||||
{ label: "Operator", group: "Operator", collapsible: true },
|
||||
{ label: "", group: "License", collapsible: false },
|
||||
];
|
||||
|
||||
@@ -27,7 +27,7 @@ import { MinTablePaginationActions } from "../../../common/MinTablePaginationAct
|
||||
import {
|
||||
NotificationEndpointItem,
|
||||
NotificationEndpointsList,
|
||||
TransformedEndpointItem
|
||||
TransformedEndpointItem,
|
||||
} from "./types";
|
||||
import { notificationTransform } from "./utils";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
@@ -35,6 +35,12 @@ import api from "../../../common/api";
|
||||
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import AddNotificationEndpoint from "./AddNotificationEndpoint";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
|
||||
interface IListNotificationEndpoints {
|
||||
classes: any;
|
||||
@@ -43,29 +49,20 @@ interface IListNotificationEndpoints {
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red"
|
||||
color: "red",
|
||||
},
|
||||
strongText: {
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10
|
||||
}
|
||||
},
|
||||
searchField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012"
|
||||
marginLeft: 5,
|
||||
},
|
||||
iconText: {
|
||||
lineHeight: "24px"
|
||||
}
|
||||
lineHeight: "24px",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
||||
@@ -96,7 +93,7 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
||||
setError("");
|
||||
setIsLoading(false);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
setError(err);
|
||||
setIsLoading(false);
|
||||
});
|
||||
@@ -115,8 +112,8 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
||||
type: "delete",
|
||||
onClick: (row: any) => {
|
||||
//confirmDeleteBucket(row.name);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const filteredRecords = records.filter((b: TransformedEndpointItem) => {
|
||||
@@ -136,7 +133,7 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<FiberManualRecordIcon
|
||||
@@ -158,83 +155,80 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label="Lambda Notification Targets" />
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Lambda Notification Targets</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Filter"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={event => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Notification Target
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Service", elementKey: "service_name" },
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
renderFunction: statusDisplay
|
||||
}
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Notification Endpoints"
|
||||
idField="service_name"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: totalRecords,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true
|
||||
},
|
||||
onChangePage: (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
},
|
||||
onChangeRowsPerPage: (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const rPP = parseInt(event.target.value, 10);
|
||||
setRowsPerPage(rPP);
|
||||
},
|
||||
ActionsComponent: MinTablePaginationActions
|
||||
}}
|
||||
/>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
{error !== "" && <Grid container>{error}</Grid>}
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Filter"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(event) => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Notification Target
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Service", elementKey: "service_name" },
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
renderFunction: statusDisplay,
|
||||
},
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Notification Endpoints"
|
||||
idField="service_name"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: totalRecords,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
},
|
||||
onChangeRowsPerPage: (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const rPP = parseInt(event.target.value, 10);
|
||||
setRowsPerPage(rPP);
|
||||
},
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
234
portal-ui/src/screens/Console/ObjectBrowser/BrowseBuckets.tsx
Normal file
234
portal-ui/src/screens/Console/ObjectBrowser/BrowseBuckets.tsx
Normal file
@@ -0,0 +1,234 @@
|
||||
// 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, { useState, useEffect } from "react";
|
||||
import get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import AddBucket from "../Buckets/ListBuckets/AddBucket";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
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 { Button } from "@material-ui/core";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
|
||||
import { Bucket, BucketList } from "../Buckets/types";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import api from "../../../common/api";
|
||||
import history from "../../../history";
|
||||
import {
|
||||
actionsTray,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
paper: {
|
||||
display: "flex",
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
},
|
||||
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
},
|
||||
errorBlock: {
|
||||
color: "red",
|
||||
},
|
||||
tableToolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(0),
|
||||
},
|
||||
minTableHeader: {
|
||||
color: "#393939",
|
||||
"& tr": {
|
||||
"& th": {
|
||||
fontWeight: "bold",
|
||||
},
|
||||
},
|
||||
},
|
||||
usedSpaceCol: {
|
||||
width: 150,
|
||||
},
|
||||
subTitleLabel: {
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
});
|
||||
|
||||
interface IBrowseBucketsProps {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [page, setPage] = useState<number>(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||
const [error, setError] = useState<string>("");
|
||||
const [records, setRecords] = useState<Bucket[]>([]);
|
||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||
|
||||
const offset = page * rowsPerPage;
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/buckets?offset=${offset}&limit=${rowsPerPage}`)
|
||||
.then((res: BucketList) => {
|
||||
const buckets = get(res, "buckets", []);
|
||||
|
||||
setLoading(false);
|
||||
setRecords(buckets);
|
||||
setError("");
|
||||
// if we get 0 results, and page > 0 , go down 1 page
|
||||
if (
|
||||
(res.buckets === undefined ||
|
||||
res.buckets == null ||
|
||||
res.buckets.length === 0) &&
|
||||
page > 0
|
||||
) {
|
||||
const newPage = page - 1;
|
||||
setPage(newPage);
|
||||
setLoading(true);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
setLoading(false);
|
||||
setError(err);
|
||||
});
|
||||
}
|
||||
}, [loading]);
|
||||
|
||||
const closeAddModalAndRefresh = () => {
|
||||
setAddScreenOpen(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const filteredRecords = records
|
||||
.filter((b: Bucket) => {
|
||||
if (filterBuckets === "") {
|
||||
return true;
|
||||
}
|
||||
return b.name.indexOf(filterBuckets) >= 0;
|
||||
})
|
||||
.slice(offset, offset + rowsPerPage);
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
const rPP = parseInt(event.target.value, 10);
|
||||
setPage(0);
|
||||
setRowsPerPage(rPP);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{addScreenOpen && (
|
||||
<AddBucket
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={closeAddModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={2} className={classes.subTitleLabel}>
|
||||
<Typography variant="h6">Buckets</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={10} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Buckets"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterBuckets(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Bucket
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{error !== "" && <span className={classes.errorBlock}>{error}</span>}
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{ type: "view", to: `/object-browser`, sendOnlyId: true },
|
||||
]}
|
||||
columns={[
|
||||
{ label: "Name", elementKey: "name" },
|
||||
{
|
||||
label: "Used Space",
|
||||
elementKey: "size",
|
||||
renderFunction: niceBytes,
|
||||
globalClass: classes.usedSpaceCol,
|
||||
},
|
||||
]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Buckets"
|
||||
idField="name"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: filteredRecords.length,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: handleChangePage,
|
||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(BrowseBuckets);
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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 get from "lodash/get";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Grid, Typography } from "@material-ui/core";
|
||||
import BrowseBuckets from "./BrowseBuckets";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
|
||||
interface IObjectBrowserProps {
|
||||
match: any;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
watchList: {
|
||||
background: "white",
|
||||
maxHeight: "400",
|
||||
overflow: "auto",
|
||||
"& ul": {
|
||||
margin: "4",
|
||||
padding: "0",
|
||||
},
|
||||
"& ul li": {
|
||||
listStyle: "none",
|
||||
margin: "0",
|
||||
padding: "0",
|
||||
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: "206",
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
},
|
||||
lastElementWPadding: {
|
||||
paddingRight: "78",
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const ObjectBrowser = ({ match, classes }: IObjectBrowserProps) => {
|
||||
const pathIn = get(match, "path", "");
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PageHeader label={"Object Browser"} />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
{pathIn === "/object-browser" && <BrowseBuckets />}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(ObjectBrowser);
|
||||
@@ -41,6 +41,19 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
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",
|
||||
|
||||
@@ -30,6 +30,12 @@ import AddPolicy from "./AddPolicy";
|
||||
import DeletePolicy from "./DeletePolicy";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import api from "../../../common/api";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -61,18 +67,9 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
},
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
},
|
||||
searchField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface IPoliciesProps {
|
||||
@@ -222,70 +219,67 @@ const Policies = ({ classes }: IPoliciesProps) => {
|
||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label="IAM Policies" />
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">IAM Policies</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Policies"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setPage(0);
|
||||
setFilterPolicies(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setPolicyEdit(null);
|
||||
}}
|
||||
>
|
||||
Create Policy
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loading}
|
||||
records={paginatedRecords}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: filteredRecords.length,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: handleChangePage,
|
||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Policies"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setPage(0);
|
||||
setFilterPolicies(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setPolicyEdit(null);
|
||||
}}
|
||||
>
|
||||
Create Policy
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loading}
|
||||
records={paginatedRecords}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: filteredRecords.length,
|
||||
rowsPerPage: rowsPerPage,
|
||||
page: page,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: handleChangePage,
|
||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user