Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63350e5492 | ||
|
|
255c128b67 | ||
|
|
06f333395e | ||
|
|
3cd024ea2c | ||
|
|
9c0a407db6 | ||
|
|
dc3c619f3f | ||
|
|
5000aafba6 | ||
|
|
b9f2a39d50 | ||
|
|
df321191f4 | ||
|
|
547eb41e96 | ||
|
|
afbb83e081 | ||
|
|
b599968570 | ||
|
|
24cc60f34e | ||
|
|
f967058409 | ||
|
|
078e09ba76 | ||
|
|
d6f97841d4 | ||
|
|
619ac124b3 | ||
|
|
a2180e123d | ||
|
|
0325bb7e2d | ||
|
|
fce361e5bd | ||
|
|
ed6d6e8b9d | ||
|
|
406709f66b | ||
|
|
3ac45a2211 |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.14.x]
|
go-version: [1.15.x]
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
- name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ before:
|
|||||||
hooks:
|
hooks:
|
||||||
# you may remove this if you don't use vgo
|
# you may remove this if you don't use vgo
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
|
- docker build -f Dockerfile.assets -t consoleassets .
|
||||||
|
- docker create --name extract consoleassets
|
||||||
|
- docker cp extract:/app/bindata_assetfs.go ./portal-ui/
|
||||||
|
- docker rm extract
|
||||||
|
|
||||||
builds:
|
builds:
|
||||||
-
|
-
|
||||||
|
|||||||
29
Dockerfile
29
Dockerfile
@@ -1,4 +1,25 @@
|
|||||||
FROM golang:1.13
|
FROM golang:1.15 as binlayer
|
||||||
|
|
||||||
|
RUN go get github.com/go-bindata/go-bindata/... && go get github.com/elazarl/go-bindata-assetfs/...
|
||||||
|
|
||||||
|
FROM node:10 as uilayer
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=binlayer /go/bin/go-bindata-assetfs /bin/
|
||||||
|
COPY --from=binlayer /go/bin/go-bindata /bin/
|
||||||
|
|
||||||
|
COPY ./portal-ui/package.json ./
|
||||||
|
COPY ./portal-ui/yarn.lock ./
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
COPY ./portal-ui .
|
||||||
|
|
||||||
|
RUN yarn install && make build-static
|
||||||
|
|
||||||
|
USER node
|
||||||
|
|
||||||
|
FROM golang:1.15 as golayer
|
||||||
|
|
||||||
RUN apt-get update -y && apt-get install -y ca-certificates
|
RUN apt-get update -y && apt-get install -y ca-certificates
|
||||||
|
|
||||||
@@ -12,6 +33,8 @@ RUN go mod download
|
|||||||
ADD . /go/src/github.com/minio/console/
|
ADD . /go/src/github.com/minio/console/
|
||||||
WORKDIR /go/src/github.com/minio/console/
|
WORKDIR /go/src/github.com/minio/console/
|
||||||
|
|
||||||
|
COPY --from=uilayer /app/bindata_assetfs.go /go/src/github.com/minio/console/portal-ui/
|
||||||
|
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
|
|
||||||
RUN go build -ldflags "-w -s" -a -o console ./cmd/console
|
RUN go build -ldflags "-w -s" -a -o console ./cmd/console
|
||||||
@@ -20,7 +43,7 @@ FROM scratch
|
|||||||
MAINTAINER MinIO Development "dev@min.io"
|
MAINTAINER MinIO Development "dev@min.io"
|
||||||
EXPOSE 9090
|
EXPOSE 9090
|
||||||
|
|
||||||
COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
COPY --from=golayer /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
COPY --from=0 /go/src/github.com/minio/console/console .
|
COPY --from=golayer /go/src/github.com/minio/console/console .
|
||||||
|
|
||||||
ENTRYPOINT ["/console"]
|
ENTRYPOINT ["/console"]
|
||||||
|
|||||||
20
Dockerfile.assets
Normal file
20
Dockerfile.assets
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
FROM golang:1.15 as binlayer
|
||||||
|
|
||||||
|
RUN go get github.com/go-bindata/go-bindata/... && go get github.com/elazarl/go-bindata-assetfs/...
|
||||||
|
|
||||||
|
FROM node:10 as uilayer
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=binlayer /go/bin/go-bindata-assetfs /bin/
|
||||||
|
COPY --from=binlayer /go/bin/go-bindata /bin/
|
||||||
|
|
||||||
|
COPY ./portal-ui/package.json ./
|
||||||
|
COPY ./portal-ui/yarn.lock ./
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
COPY ./portal-ui .
|
||||||
|
|
||||||
|
RUN yarn install && make build-static
|
||||||
|
|
||||||
|
USER node
|
||||||
36
README.md
36
README.md
@@ -113,11 +113,41 @@ export CONSOLE_MINIO_SERVER=http://localhost:9000
|
|||||||
./console server
|
./console server
|
||||||
```
|
```
|
||||||
|
|
||||||
## Connect Console to a Minio using TLS and a self-signed certificate
|
## Run Console with TLS enable
|
||||||
|
|
||||||
|
Copy your `public.crt` and `private.key` to `~/.console/certs`, then:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./console server
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, `Console` has support for multiple certificates, clients can request them using `SNI`. It expects the following structure:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
certs/
|
||||||
|
│
|
||||||
|
├─ public.crt
|
||||||
|
├─ private.key
|
||||||
|
│
|
||||||
|
├─ example.com/
|
||||||
|
│ │
|
||||||
|
│ ├─ public.crt
|
||||||
|
│ └─ private.key
|
||||||
|
└─ foobar.org/
|
||||||
|
│
|
||||||
|
├─ public.crt
|
||||||
|
└─ private.key
|
||||||
|
...
|
||||||
|
|
||||||
```
|
```
|
||||||
...
|
|
||||||
export CONSOLE_MINIO_SERVER_TLS_ROOT_CAS=<certificate_file_name>
|
Therefore, we read all filenames in the cert directory and check
|
||||||
|
for each directory whether it contains a public.crt and private.key.
|
||||||
|
|
||||||
|
## Connect Console to a Minio using TLS and a self-signed certificate
|
||||||
|
|
||||||
|
Copy the MinIO `ca.crt` under `~/.console/certs/CAs`, then:
|
||||||
|
```
|
||||||
export CONSOLE_MINIO_SERVER=https://localhost:9000
|
export CONSOLE_MINIO_SERVER=https://localhost:9000
|
||||||
./console server
|
./console server
|
||||||
```
|
```
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -20,12 +20,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/go-openapi/loads"
|
"github.com/go-openapi/loads"
|
||||||
"github.com/jessevdk/go-flags"
|
"github.com/jessevdk/go-flags"
|
||||||
"github.com/minio/cli"
|
"github.com/minio/cli"
|
||||||
|
"github.com/minio/console/pkg/certs"
|
||||||
"github.com/minio/console/restapi"
|
"github.com/minio/console/restapi"
|
||||||
"github.com/minio/console/restapi/operations"
|
"github.com/minio/console/restapi/operations"
|
||||||
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
certsx "github.com/minio/minio/pkg/certs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// starts the server
|
// starts the server
|
||||||
@@ -56,14 +60,9 @@ var serverCmd = cli.Command{
|
|||||||
Usage: "HTTPS server port",
|
Usage: "HTTPS server port",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "tls-certificate",
|
Name: "certs-dir",
|
||||||
Value: "",
|
Value: certs.GlobalCertsCADir.Get(),
|
||||||
Usage: "filename of public cert",
|
Usage: "path to certs directory",
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "tls-key",
|
|
||||||
Value: "",
|
|
||||||
Usage: "filename of private key",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -82,7 +81,9 @@ func startServer(ctx *cli.Context) error {
|
|||||||
parser := flags.NewParser(server, flags.Default)
|
parser := flags.NewParser(server, flags.Default)
|
||||||
parser.ShortDescription = "MinIO Console Server"
|
parser.ShortDescription = "MinIO Console Server"
|
||||||
parser.LongDescription = swaggerSpec.Spec().Info.Description
|
parser.LongDescription = swaggerSpec.Spec().Info.Description
|
||||||
|
|
||||||
server.ConfigureFlags()
|
server.ConfigureFlags()
|
||||||
|
|
||||||
for _, optsGroup := range api.CommandLineOptionsGroups {
|
for _, optsGroup := range api.CommandLineOptionsGroups {
|
||||||
_, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options)
|
_, err := parser.AddGroup(optsGroup.ShortDescription, optsGroup.LongDescription, optsGroup.Options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -106,12 +107,19 @@ func startServer(ctx *cli.Context) error {
|
|||||||
restapi.Hostname = ctx.String("host")
|
restapi.Hostname = ctx.String("host")
|
||||||
restapi.Port = fmt.Sprintf("%v", ctx.Int("port"))
|
restapi.Port = fmt.Sprintf("%v", ctx.Int("port"))
|
||||||
|
|
||||||
tlsCertificatePath := ctx.String("tls-certificate")
|
// Set all certs and CAs directories.
|
||||||
tlsCertificateKeyPath := ctx.String("tls-key")
|
certs.GlobalCertsDir, _ = certs.NewConfigDirFromCtx(ctx, "certs-dir", certs.DefaultCertsDir.Get)
|
||||||
|
certs.GlobalCertsCADir = &certs.ConfigDir{Path: filepath.Join(certs.GlobalCertsDir.Get(), certs.CertsCADir)}
|
||||||
|
logger.FatalIf(certs.MkdirAllIgnorePerm(certs.GlobalCertsCADir.Get()), "Unable to create certs CA directory at %s", certs.GlobalCertsCADir.Get())
|
||||||
|
|
||||||
if tlsCertificatePath != "" && tlsCertificateKeyPath != "" {
|
// load all CAs from ~/.console/certs/CAs
|
||||||
server.TLSCertificate = flags.Filename(tlsCertificatePath)
|
restapi.GlobalRootCAs, err = certsx.GetRootCAs(certs.GlobalCertsCADir.Get())
|
||||||
server.TLSCertificateKey = flags.Filename(tlsCertificateKeyPath)
|
logger.FatalIf(err, "Failed to read root CAs (%v)", err)
|
||||||
|
// load all certs from ~/.console/certs
|
||||||
|
restapi.GlobalPublicCerts, restapi.GlobalTLSCertsManager, err = certs.GetTLSConfig()
|
||||||
|
logger.FatalIf(err, "Unable to load the TLS configuration")
|
||||||
|
|
||||||
|
if len(restapi.GlobalPublicCerts) > 0 && restapi.GlobalRootCAs != nil {
|
||||||
// If TLS certificates are provided enforce the HTTPS schema, meaning console will redirect
|
// If TLS certificates are provided enforce the HTTPS schema, meaning console will redirect
|
||||||
// plain HTTP connections to HTTPS server
|
// plain HTTP connections to HTTPS server
|
||||||
server.EnabledListeners = []string{"http", "https"}
|
server.EnabledListeners = []string{"http", "https"}
|
||||||
|
|||||||
5
go.mod
5
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/minio/console
|
module github.com/minio/console
|
||||||
|
|
||||||
go 1.13
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||||
@@ -19,7 +19,8 @@ require (
|
|||||||
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821
|
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821
|
||||||
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544
|
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544
|
||||||
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089
|
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089
|
||||||
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c
|
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||||
github.com/secure-io/sio-go v0.3.1
|
github.com/secure-io/sio-go v0.3.1
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
|
|||||||
11
go.sum
11
go.sum
@@ -60,10 +60,12 @@ github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESN
|
|||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/Azure/go-autorest v14.1.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v14.1.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
|
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
||||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||||
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.10.2 h1:NuSF3gXetiHyUbVdneJMEVyPUYAe5wh+aN08JYAf1tI=
|
||||||
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
|
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||||
@@ -72,11 +74,13 @@ github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMl
|
|||||||
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
|
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.9.1 h1:xjPqigMQe2+0DAJ5A6MLUPp5D2r2Io8qHCuCMMI/yJU=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.9.1/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
|
github.com/Azure/go-autorest/autorest/adal v0.9.1/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
||||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
|
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
|
||||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||||
|
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
|
||||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||||
@@ -86,8 +90,10 @@ github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocm
|
|||||||
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||||
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
||||||
|
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||||
|
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
@@ -540,6 +546,7 @@ github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
|
|||||||
github.com/googleapis/gnostic v0.2.2 h1:DcFegQ7+ECdmkJMfVwWlC+89I4esJ7p8nkGt9ainGDk=
|
github.com/googleapis/gnostic v0.2.2 h1:DcFegQ7+ECdmkJMfVwWlC+89I4esJ7p8nkGt9ainGDk=
|
||||||
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||||
github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||||
|
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
|
||||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
@@ -798,8 +805,8 @@ github.com/minio/minio-go/v7 v7.0.2/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6J
|
|||||||
github.com/minio/minio-go/v7 v7.0.5-0.20200811211821-14ed05478889/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
|
github.com/minio/minio-go/v7 v7.0.5-0.20200811211821-14ed05478889/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
|
||||||
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089 h1:9DDs/Gc3fNHOQxQmwIFWs7YDLMTBh59r2XQ6RqEUA1I=
|
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089 h1:9DDs/Gc3fNHOQxQmwIFWs7YDLMTBh59r2XQ6RqEUA1I=
|
||||||
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
|
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
|
||||||
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c h1:OIKdzEJDFmUokbJ1rIdlr3kcfsBfXelYgSCTN/+Ppec=
|
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b h1:ggfD6V3nodTzhHJHCYIT1F897gscrz+hHsan0a2Wtqw=
|
||||||
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c/go.mod h1:6lavbNo2YuJWeQR5bZYsEWdbpRCO2KrTyfQ0PtC/AN4=
|
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b/go.mod h1:At+++4/6W5BEXK11tN5DKIvkJKhYBZybbb5zmxb0LQI=
|
||||||
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
|
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
|
||||||
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
|
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
|
||||||
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
serviceAccountName: console-sa
|
serviceAccountName: console-sa
|
||||||
containers:
|
containers:
|
||||||
- name: console
|
- name: console
|
||||||
image: minio/console:v0.4.2
|
image: minio/console:v0.4.4
|
||||||
imagePullPolicy: "IfNotPresent"
|
imagePullPolicy: "IfNotPresent"
|
||||||
args:
|
args:
|
||||||
- server
|
- server
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
serviceAccountName: console-sa
|
serviceAccountName: console-sa
|
||||||
containers:
|
containers:
|
||||||
- name: console
|
- name: console
|
||||||
image: minio/console:v0.4.2
|
image: minio/console:v0.4.4
|
||||||
imagePullPolicy: "IfNotPresent"
|
imagePullPolicy: "IfNotPresent"
|
||||||
env:
|
env:
|
||||||
- name: CONSOLE_OPERATOR_MODE
|
- name: CONSOLE_OPERATOR_MODE
|
||||||
|
|||||||
63
models/bucket_encryption_info.go
Normal file
63
models/bucket_encryption_info.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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketEncryptionInfo bucket encryption info
|
||||||
|
//
|
||||||
|
// swagger:model bucketEncryptionInfo
|
||||||
|
type BucketEncryptionInfo struct {
|
||||||
|
|
||||||
|
// algorithm
|
||||||
|
Algorithm string `json:"algorithm,omitempty"`
|
||||||
|
|
||||||
|
// kms master key ID
|
||||||
|
KmsMasterKeyID string `json:"kmsMasterKeyID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket encryption info
|
||||||
|
func (m *BucketEncryptionInfo) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *BucketEncryptionInfo) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *BucketEncryptionInfo) UnmarshalBinary(b []byte) error {
|
||||||
|
var res BucketEncryptionInfo
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
89
models/bucket_encryption_request.go
Normal file
89
models/bucket_encryption_request.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketEncryptionRequest bucket encryption request
|
||||||
|
//
|
||||||
|
// swagger:model bucketEncryptionRequest
|
||||||
|
type BucketEncryptionRequest struct {
|
||||||
|
|
||||||
|
// enc type
|
||||||
|
EncType BucketEncryptionType `json:"encType,omitempty"`
|
||||||
|
|
||||||
|
// kms key ID
|
||||||
|
KmsKeyID string `json:"kmsKeyID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket encryption request
|
||||||
|
func (m *BucketEncryptionRequest) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateEncType(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *BucketEncryptionRequest) validateEncType(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.EncType) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.EncType.Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("encType")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *BucketEncryptionRequest) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *BucketEncryptionRequest) UnmarshalBinary(b []byte) error {
|
||||||
|
var res BucketEncryptionRequest
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
80
models/bucket_encryption_type.go
Normal file
80
models/bucket_encryption_type.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// Code generated by go-swagger; DO NOT EDIT.
|
||||||
|
|
||||||
|
// This file is part of MinIO Console Server
|
||||||
|
// Copyright (c) 2020 MinIO, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketEncryptionType bucket encryption type
|
||||||
|
//
|
||||||
|
// swagger:model bucketEncryptionType
|
||||||
|
type BucketEncryptionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// BucketEncryptionTypeSseS3 captures enum value "sse-s3"
|
||||||
|
BucketEncryptionTypeSseS3 BucketEncryptionType = "sse-s3"
|
||||||
|
|
||||||
|
// BucketEncryptionTypeSseKms captures enum value "sse-kms"
|
||||||
|
BucketEncryptionTypeSseKms BucketEncryptionType = "sse-kms"
|
||||||
|
)
|
||||||
|
|
||||||
|
// for schema
|
||||||
|
var bucketEncryptionTypeEnum []interface{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var res []BucketEncryptionType
|
||||||
|
if err := json.Unmarshal([]byte(`["sse-s3","sse-kms"]`), &res); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range res {
|
||||||
|
bucketEncryptionTypeEnum = append(bucketEncryptionTypeEnum, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m BucketEncryptionType) validateBucketEncryptionTypeEnum(path, location string, value BucketEncryptionType) error {
|
||||||
|
if err := validate.EnumCase(path, location, value, bucketEncryptionTypeEnum, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket encryption type
|
||||||
|
func (m BucketEncryptionType) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
// value enum
|
||||||
|
if err := m.validateBucketEncryptionTypeEnum("", "body", m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -65,6 +65,9 @@ type BucketObject struct {
|
|||||||
// size
|
// size
|
||||||
Size int64 `json:"size,omitempty"`
|
Size int64 `json:"size,omitempty"`
|
||||||
|
|
||||||
|
// tags
|
||||||
|
Tags map[string]string `json:"tags,omitempty"`
|
||||||
|
|
||||||
// user tags
|
// user tags
|
||||||
UserTags map[string]string `json:"user_tags,omitempty"`
|
UserTags map[string]string `json:"user_tags,omitempty"`
|
||||||
|
|
||||||
|
|||||||
@@ -93,9 +93,6 @@ type CreateTenantRequest struct {
|
|||||||
// secret key
|
// secret key
|
||||||
SecretKey string `json:"secret_key,omitempty"`
|
SecretKey string `json:"secret_key,omitempty"`
|
||||||
|
|
||||||
// service name
|
|
||||||
ServiceName string `json:"service_name,omitempty"`
|
|
||||||
|
|
||||||
// tls
|
// tls
|
||||||
TLS *TLSConfiguration `json:"tls,omitempty"`
|
TLS *TLSConfiguration `json:"tls,omitempty"`
|
||||||
|
|
||||||
|
|||||||
80
models/object_legal_hold_status.go
Normal file
80
models/object_legal_hold_status.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// Code generated by go-swagger; DO NOT EDIT.
|
||||||
|
|
||||||
|
// This file is part of MinIO Console Server
|
||||||
|
// Copyright (c) 2020 MinIO, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObjectLegalHoldStatus object legal hold status
|
||||||
|
//
|
||||||
|
// swagger:model objectLegalHoldStatus
|
||||||
|
type ObjectLegalHoldStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// ObjectLegalHoldStatusEnabled captures enum value "enabled"
|
||||||
|
ObjectLegalHoldStatusEnabled ObjectLegalHoldStatus = "enabled"
|
||||||
|
|
||||||
|
// ObjectLegalHoldStatusDisabled captures enum value "disabled"
|
||||||
|
ObjectLegalHoldStatusDisabled ObjectLegalHoldStatus = "disabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// for schema
|
||||||
|
var objectLegalHoldStatusEnum []interface{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var res []ObjectLegalHoldStatus
|
||||||
|
if err := json.Unmarshal([]byte(`["enabled","disabled"]`), &res); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range res {
|
||||||
|
objectLegalHoldStatusEnum = append(objectLegalHoldStatusEnum, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ObjectLegalHoldStatus) validateObjectLegalHoldStatusEnum(path, location string, value ObjectLegalHoldStatus) error {
|
||||||
|
if err := validate.EnumCase(path, location, value, objectLegalHoldStatusEnum, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this object legal hold status
|
||||||
|
func (m ObjectLegalHoldStatus) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
// value enum
|
||||||
|
if err := m.validateObjectLegalHoldStatusEnum("", "body", m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
80
models/object_retention_mode.go
Normal file
80
models/object_retention_mode.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// Code generated by go-swagger; DO NOT EDIT.
|
||||||
|
|
||||||
|
// This file is part of MinIO Console Server
|
||||||
|
// Copyright (c) 2020 MinIO, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObjectRetentionMode object retention mode
|
||||||
|
//
|
||||||
|
// swagger:model objectRetentionMode
|
||||||
|
type ObjectRetentionMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// ObjectRetentionModeGovernance captures enum value "governance"
|
||||||
|
ObjectRetentionModeGovernance ObjectRetentionMode = "governance"
|
||||||
|
|
||||||
|
// ObjectRetentionModeCompliance captures enum value "compliance"
|
||||||
|
ObjectRetentionModeCompliance ObjectRetentionMode = "compliance"
|
||||||
|
)
|
||||||
|
|
||||||
|
// for schema
|
||||||
|
var objectRetentionModeEnum []interface{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var res []ObjectRetentionMode
|
||||||
|
if err := json.Unmarshal([]byte(`["governance","compliance"]`), &res); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range res {
|
||||||
|
objectRetentionModeEnum = append(objectRetentionModeEnum, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m ObjectRetentionMode) validateObjectRetentionModeEnum(path, location string, value ObjectRetentionMode) error {
|
||||||
|
if err := validate.EnumCase(path, location, value, objectRetentionModeEnum, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this object retention mode
|
||||||
|
func (m ObjectRetentionMode) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
// value enum
|
||||||
|
if err := m.validateObjectRetentionModeEnum("", "body", m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
83
models/put_object_legal_hold_request.go
Normal file
83
models/put_object_legal_hold_request.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Code generated by go-swagger; DO NOT EDIT.
|
||||||
|
|
||||||
|
// This file is part of MinIO Console Server
|
||||||
|
// Copyright (c) 2020 MinIO, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
// This file was generated by the swagger tool.
|
||||||
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PutObjectLegalHoldRequest put object legal hold request
|
||||||
|
//
|
||||||
|
// swagger:model putObjectLegalHoldRequest
|
||||||
|
type PutObjectLegalHoldRequest struct {
|
||||||
|
|
||||||
|
// status
|
||||||
|
// Required: true
|
||||||
|
Status ObjectLegalHoldStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this put object legal hold request
|
||||||
|
func (m *PutObjectLegalHoldRequest) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateStatus(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PutObjectLegalHoldRequest) validateStatus(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := m.Status.Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("status")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *PutObjectLegalHoldRequest) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *PutObjectLegalHoldRequest) UnmarshalBinary(b []byte) error {
|
||||||
|
var res PutObjectLegalHoldRequest
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
104
models/put_object_retention_request.go
Normal file
104
models/put_object_retention_request.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PutObjectRetentionRequest put object retention request
|
||||||
|
//
|
||||||
|
// swagger:model putObjectRetentionRequest
|
||||||
|
type PutObjectRetentionRequest struct {
|
||||||
|
|
||||||
|
// expires
|
||||||
|
// Required: true
|
||||||
|
Expires *string `json:"expires"`
|
||||||
|
|
||||||
|
// governance bypass
|
||||||
|
GovernanceBypass bool `json:"governance_bypass,omitempty"`
|
||||||
|
|
||||||
|
// mode
|
||||||
|
// Required: true
|
||||||
|
Mode ObjectRetentionMode `json:"mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this put object retention request
|
||||||
|
func (m *PutObjectRetentionRequest) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateExpires(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateMode(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PutObjectRetentionRequest) validateExpires(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("expires", "body", m.Expires); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PutObjectRetentionRequest) validateMode(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := m.Mode.Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("mode")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *PutObjectRetentionRequest) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *PutObjectRetentionRequest) UnmarshalBinary(b []byte) error {
|
||||||
|
var res PutObjectRetentionRequest
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
60
models/put_object_tags_request.go
Normal file
60
models/put_object_tags_request.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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PutObjectTagsRequest put object tags request
|
||||||
|
//
|
||||||
|
// swagger:model putObjectTagsRequest
|
||||||
|
type PutObjectTagsRequest struct {
|
||||||
|
|
||||||
|
// tags
|
||||||
|
Tags map[string]string `json:"tags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this put object tags request
|
||||||
|
func (m *PutObjectTagsRequest) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *PutObjectTagsRequest) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *PutObjectTagsRequest) UnmarshalBinary(b []byte) error {
|
||||||
|
var res PutObjectTagsRequest
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -22,27 +22,26 @@ import (
|
|||||||
|
|
||||||
// endpoints definition
|
// endpoints definition
|
||||||
var (
|
var (
|
||||||
configuration = "/configurations-list"
|
configuration = "/configurations-list"
|
||||||
users = "/users"
|
users = "/users"
|
||||||
groups = "/groups"
|
groups = "/groups"
|
||||||
iamPolicies = "/policies"
|
iamPolicies = "/policies"
|
||||||
dashboard = "/dashboard"
|
dashboard = "/dashboard"
|
||||||
profiling = "/profiling"
|
profiling = "/profiling"
|
||||||
trace = "/trace"
|
watch = "/watch"
|
||||||
logs = "/logs"
|
notifications = "/notification-endpoints"
|
||||||
watch = "/watch"
|
buckets = "/buckets"
|
||||||
notifications = "/notification-endpoints"
|
bucketsDetail = "/buckets/:bucketName"
|
||||||
buckets = "/buckets"
|
serviceAccounts = "/service-accounts"
|
||||||
bucketsDetail = "/buckets/:bucketName"
|
tenants = "/tenants"
|
||||||
serviceAccounts = "/service-accounts"
|
tenantsDetail = "/namespaces/:tenantNamespace/tenants/:tenantName"
|
||||||
tenants = "/tenants"
|
heal = "/heal"
|
||||||
tenantsDetail = "/namespaces/:tenantNamespace/tenants/:tenantName"
|
remoteBuckets = "/remote-buckets"
|
||||||
heal = "/heal"
|
replication = "/replication"
|
||||||
remoteBuckets = "/remote-buckets"
|
objectBrowser = "/object-browser/:bucket/*"
|
||||||
replication = "/replication"
|
objectBrowserBucket = "/object-browser/:bucket"
|
||||||
objectBrowser = "/object-browser/:bucket?"
|
mainObjectBrowser = "/object-browser"
|
||||||
mainObjectBrowser = "/object-browser"
|
license = "/license"
|
||||||
license = "/license"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigurationActionSet struct {
|
type ConfigurationActionSet struct {
|
||||||
@@ -60,16 +59,6 @@ var configurationActionSet = ConfigurationActionSet{
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
// logsActionSet contains the list of admin actions required for this endpoint to work
|
|
||||||
var logsActionSet = ConfigurationActionSet{
|
|
||||||
actionTypes: iampolicy.NewActionSet(
|
|
||||||
iampolicy.AllAdminActions,
|
|
||||||
),
|
|
||||||
actions: iampolicy.NewActionSet(
|
|
||||||
iampolicy.ConsoleLogAdminAction,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// dashboardActionSet contains the list of admin actions required for this endpoint to work
|
// dashboardActionSet contains the list of admin actions required for this endpoint to work
|
||||||
var dashboardActionSet = ConfigurationActionSet{
|
var dashboardActionSet = ConfigurationActionSet{
|
||||||
actionTypes: iampolicy.NewActionSet(
|
actionTypes: iampolicy.NewActionSet(
|
||||||
@@ -118,16 +107,6 @@ var profilingActionSet = ConfigurationActionSet{
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
// traceActionSet contains the list of admin actions required for this endpoint to work
|
|
||||||
var traceActionSet = ConfigurationActionSet{
|
|
||||||
actionTypes: iampolicy.NewActionSet(
|
|
||||||
iampolicy.AllAdminActions,
|
|
||||||
),
|
|
||||||
actions: iampolicy.NewActionSet(
|
|
||||||
iampolicy.TraceAdminAction,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// usersActionSet contains the list of admin actions required for this endpoint to work
|
// usersActionSet contains the list of admin actions required for this endpoint to work
|
||||||
var usersActionSet = ConfigurationActionSet{
|
var usersActionSet = ConfigurationActionSet{
|
||||||
actionTypes: iampolicy.NewActionSet(
|
actionTypes: iampolicy.NewActionSet(
|
||||||
@@ -245,25 +224,24 @@ var licenseActionSet = ConfigurationActionSet{
|
|||||||
|
|
||||||
// endpointRules contains the mapping between endpoints and ActionSets, additional rules can be added here
|
// endpointRules contains the mapping between endpoints and ActionSets, additional rules can be added here
|
||||||
var endpointRules = map[string]ConfigurationActionSet{
|
var endpointRules = map[string]ConfigurationActionSet{
|
||||||
configuration: configurationActionSet,
|
configuration: configurationActionSet,
|
||||||
users: usersActionSet,
|
users: usersActionSet,
|
||||||
groups: groupsActionSet,
|
groups: groupsActionSet,
|
||||||
iamPolicies: iamPoliciesActionSet,
|
iamPolicies: iamPoliciesActionSet,
|
||||||
dashboard: dashboardActionSet,
|
dashboard: dashboardActionSet,
|
||||||
profiling: profilingActionSet,
|
profiling: profilingActionSet,
|
||||||
trace: traceActionSet,
|
watch: watchActionSet,
|
||||||
logs: logsActionSet,
|
notifications: notificationsActionSet,
|
||||||
watch: watchActionSet,
|
buckets: bucketsActionSet,
|
||||||
notifications: notificationsActionSet,
|
bucketsDetail: bucketsActionSet,
|
||||||
buckets: bucketsActionSet,
|
serviceAccounts: serviceAccountsActionSet,
|
||||||
bucketsDetail: bucketsActionSet,
|
heal: healActionSet,
|
||||||
serviceAccounts: serviceAccountsActionSet,
|
remoteBuckets: remoteBucketsActionSet,
|
||||||
heal: healActionSet,
|
replication: replicationActionSet,
|
||||||
remoteBuckets: remoteBucketsActionSet,
|
objectBrowser: objectBrowserActionSet,
|
||||||
replication: replicationActionSet,
|
mainObjectBrowser: objectBrowserActionSet,
|
||||||
objectBrowser: objectBrowserActionSet,
|
objectBrowserBucket: objectBrowserActionSet,
|
||||||
mainObjectBrowser: objectBrowserActionSet,
|
license: licenseActionSet,
|
||||||
license: licenseActionSet,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// operatorRules contains the mapping between endpoints and ActionSets for operator only mode
|
// operatorRules contains the mapping between endpoints and ActionSets for operator only mode
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
[]string{"admin:ServerInfo"},
|
[]string{"admin:ServerInfo"},
|
||||||
},
|
},
|
||||||
want: 5,
|
want: 6,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "policies endpoint",
|
name: "policies endpoint",
|
||||||
@@ -63,7 +63,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
"admin:ListUserPolicies",
|
"admin:ListUserPolicies",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: 5,
|
want: 6,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all admin endpoints",
|
name: "all admin endpoints",
|
||||||
@@ -72,7 +72,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
"admin:*",
|
"admin:*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: 16,
|
want: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all s3 endpoints",
|
name: "all s3 endpoints",
|
||||||
@@ -81,7 +81,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
"s3:*",
|
"s3:*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: 7,
|
want: 8,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all admin and s3 endpoints",
|
name: "all admin and s3 endpoints",
|
||||||
@@ -91,7 +91,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
"s3:*",
|
"s3:*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: 19,
|
want: 18,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no endpoints",
|
name: "no endpoints",
|
||||||
|
|||||||
222
pkg/certs/certs.go
Normal file
222
pkg/certs/certs.go
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
// 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 certs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/minio/cli"
|
||||||
|
"github.com/minio/minio/cmd/config"
|
||||||
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
"github.com/minio/minio/pkg/certs"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetCertificateFunc = certs.GetCertificateFunc
|
||||||
|
|
||||||
|
// ConfigDir - points to a user set directory.
|
||||||
|
type ConfigDir struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get - returns current directory.
|
||||||
|
func (dir *ConfigDir) Get() string {
|
||||||
|
return dir.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultConfigDir() string {
|
||||||
|
homeDir, err := homedir.Dir()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return filepath.Join(homeDir, DefaultConsoleConfigDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultCertsDir() string {
|
||||||
|
return filepath.Join(getDefaultConfigDir(), CertsDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultCertsCADir() string {
|
||||||
|
return filepath.Join(getDefaultCertsDir(), CertsCADir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isFile - returns whether given Path is a file or not.
|
||||||
|
func isFile(path string) bool {
|
||||||
|
if fi, err := os.Stat(path); err == nil {
|
||||||
|
return fi.Mode().IsRegular()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultCertsDir certs directory.
|
||||||
|
DefaultCertsDir = &ConfigDir{Path: getDefaultCertsDir()}
|
||||||
|
// DefaultCertsCADir CA directory.
|
||||||
|
DefaultCertsCADir = &ConfigDir{Path: getDefaultCertsCADir()}
|
||||||
|
// GlobalCertsDir points to current certs directory set by user with --certs-dir
|
||||||
|
GlobalCertsDir = DefaultCertsDir
|
||||||
|
// GlobalCertsCADir points to relative Path to certs directory and is <value-of-certs-dir>/CAs
|
||||||
|
GlobalCertsCADir = DefaultCertsCADir
|
||||||
|
)
|
||||||
|
|
||||||
|
// MkdirAllIgnorePerm attempts to create all directories, ignores any permission denied errors.
|
||||||
|
func MkdirAllIgnorePerm(path string) error {
|
||||||
|
err := os.MkdirAll(path, 0700)
|
||||||
|
if err != nil {
|
||||||
|
// It is possible in kubernetes like deployments this directory
|
||||||
|
// is already mounted and is not writable, ignore any write errors.
|
||||||
|
if os.IsPermission(err) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigDirFromCtx(ctx *cli.Context, option string, getDefaultDir func() string) (*ConfigDir, bool) {
|
||||||
|
var dir string
|
||||||
|
var dirSet bool
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.IsSet(option):
|
||||||
|
dir = ctx.String(option)
|
||||||
|
dirSet = true
|
||||||
|
case ctx.GlobalIsSet(option):
|
||||||
|
dir = ctx.GlobalString(option)
|
||||||
|
dirSet = true
|
||||||
|
// cli package does not expose parent's option option. Below code is workaround.
|
||||||
|
if dir == "" || dir == getDefaultDir() {
|
||||||
|
dirSet = false // Unset to false since GlobalIsSet() true is a false positive.
|
||||||
|
if ctx.Parent().GlobalIsSet(option) {
|
||||||
|
dir = ctx.Parent().GlobalString(option)
|
||||||
|
dirSet = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Neither local nor global option is provided. In this case, try to use
|
||||||
|
// default directory.
|
||||||
|
dir = getDefaultDir()
|
||||||
|
if dir == "" {
|
||||||
|
logger.FatalIf(errors.New("invalid arguments specified"), "%s option must be provided", option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dir == "" {
|
||||||
|
logger.FatalIf(errors.New("empty directory"), "%s directory cannot be empty", option)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disallow relative paths, figure out absolute paths.
|
||||||
|
dirAbs, err := filepath.Abs(dir)
|
||||||
|
logger.FatalIf(err, "Unable to fetch absolute path for %s=%s", option, dir)
|
||||||
|
logger.FatalIf(MkdirAllIgnorePerm(dirAbs), "Unable to create directory specified %s=%s", option, dir)
|
||||||
|
|
||||||
|
return &ConfigDir{Path: dirAbs}, dirSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPublicCertFile() string {
|
||||||
|
return filepath.Join(GlobalCertsDir.Get(), PublicCertFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrivateKeyFile() string {
|
||||||
|
return filepath.Join(GlobalCertsDir.Get(), PrivateKeyFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTLSConfig() (x509Certs []*x509.Certificate, manager *certs.Manager, err error) {
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if !(isFile(getPublicCertFile()) && isFile(getPrivateKeyFile())) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if x509Certs, err = config.ParsePublicCertFile(getPublicCertFile()); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
manager, err = certs.NewManager(ctx, getPublicCertFile(), getPrivateKeyFile(), config.LoadX509KeyPair)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//Console has support for multiple certificates. It expects the following structure:
|
||||||
|
// certs/
|
||||||
|
// │
|
||||||
|
// ├─ public.crt
|
||||||
|
// ├─ private.key
|
||||||
|
// │
|
||||||
|
// ├─ example.com/
|
||||||
|
// │ │
|
||||||
|
// │ ├─ public.crt
|
||||||
|
// │ └─ private.key
|
||||||
|
// └─ foobar.org/
|
||||||
|
// │
|
||||||
|
// ├─ public.crt
|
||||||
|
// └─ private.key
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
//Therefore, we read all filenames in the cert directory and check
|
||||||
|
//for each directory whether it contains a public.crt and private.key.
|
||||||
|
// If so, we try to add it to certificate manager.
|
||||||
|
root, err := os.Open(GlobalCertsDir.Get())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer root.Close()
|
||||||
|
|
||||||
|
files, err := root.Readdir(-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
// Ignore all
|
||||||
|
// - regular files
|
||||||
|
// - "CAs" directory
|
||||||
|
// - any directory which starts with ".."
|
||||||
|
if file.Mode().IsRegular() || file.Name() == "CAs" || strings.HasPrefix(file.Name(), "..") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
|
file, err = os.Stat(filepath.Join(root.Name(), file.Name()))
|
||||||
|
if err != nil {
|
||||||
|
// not accessible ignore
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !file.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
certFile = filepath.Join(root.Name(), file.Name(), PublicCertFile)
|
||||||
|
keyFile = filepath.Join(root.Name(), file.Name(), PrivateKeyFile)
|
||||||
|
)
|
||||||
|
if !isFile(certFile) || !isFile(keyFile) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err = manager.AddCertificate(certFile, keyFile); err != nil {
|
||||||
|
err = fmt.Errorf("unable to load TLS certificate '%s,%s': %w", certFile, keyFile, err)
|
||||||
|
logger.LogIf(ctx, err, logger.Application)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x509Certs, manager, nil
|
||||||
|
}
|
||||||
34
pkg/certs/const.go
Normal file
34
pkg/certs/const.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// This file is part of MinIO Console Server
|
||||||
|
// Copyright (c) 2020 MinIO, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package certs
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default minio configuration directory where below configuration files/directories are stored.
|
||||||
|
DefaultConsoleConfigDir = ".console"
|
||||||
|
|
||||||
|
// Directory contains below files/directories for HTTPS configuration.
|
||||||
|
CertsDir = "certs"
|
||||||
|
|
||||||
|
// Directory contains all CA certificates other than system defaults for HTTPS.
|
||||||
|
CertsCADir = "CAs"
|
||||||
|
|
||||||
|
// Public certificate file for HTTPS.
|
||||||
|
PublicCertFile = "public.crt"
|
||||||
|
|
||||||
|
// Private key file for HTTPS.
|
||||||
|
PrivateKeyFile = "private.key"
|
||||||
|
)
|
||||||
File diff suppressed because one or more lines are too long
@@ -28,7 +28,6 @@
|
|||||||
"local-storage-fallback": "^4.1.1",
|
"local-storage-fallback": "^4.1.1",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"npm": "^6.14.4",
|
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-app-rewire-hot-loader": "^2.0.1",
|
"react-app-rewire-hot-loader": "^2.0.1",
|
||||||
"react-app-rewired": "^2.1.6",
|
"react-app-rewired": "^2.1.6",
|
||||||
@@ -40,7 +39,7 @@
|
|||||||
"react-moment": "^0.9.7",
|
"react-moment": "^0.9.7",
|
||||||
"react-redux": "^7.1.3",
|
"react-redux": "^7.1.3",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.4",
|
||||||
"recharts": "^1.8.5",
|
"recharts": "^1.8.5",
|
||||||
"redux": "^4.0.4",
|
"redux": "^4.0.4",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
|
|||||||
6
portal-ui/public/images/ob_bucket_clear.svg
Normal file
6
portal-ui/public/images/ob_bucket_clear.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="11.174" height="11" viewBox="0 0 11.174 11">
|
||||||
|
<defs>
|
||||||
|
<style>.a{fill:none;stroke:#081c42;stroke-linecap:round;}</style>
|
||||||
|
</defs>
|
||||||
|
<path class="a" d="M8.392,10H1.608L0,0H10Z" transform="translate(0.587 0.5)"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 279 B |
6
portal-ui/public/images/ob_bucket_filled.svg
Normal file
6
portal-ui/public/images/ob_bucket_filled.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="11.174" height="11" viewBox="0 0 11.174 11">
|
||||||
|
<defs>
|
||||||
|
<style>.a{fill:#081c42;stroke:#081c42;stroke-linecap:round;}</style>
|
||||||
|
</defs>
|
||||||
|
<path class="a" d="M8.392,10H1.608L0,0H10Z" transform="translate(0.587 0.5)"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 282 B |
10
portal-ui/public/images/ob_file_clear.svg
Normal file
10
portal-ui/public/images/ob_file_clear.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="11.442" height="15.302" viewBox="0 0 11.442 15.302">
|
||||||
|
<defs>
|
||||||
|
<style>.a,.b{fill:none;stroke:#081c42;}.b{stroke-linejoin:round;}</style>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(0.5 0.5)">
|
||||||
|
<path class="a" d="M-12060-11667.842v14.261h10.442v-10.591l-3.671-3.67Z"
|
||||||
|
transform="translate(12059.999 11667.883)"/>
|
||||||
|
<path class="b" d="M-12051.353-11664.255v-3.639l3.528,3.639Z" transform="translate(12058.188 11667.894)"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 515 B |
10
portal-ui/public/images/ob_file_filled.svg
Normal file
10
portal-ui/public/images/ob_file_filled.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="11.442" height="15.302" viewBox="0 0 11.442 15.302">
|
||||||
|
<defs>
|
||||||
|
<style>.a,.b{fill:#081c42;stroke:#081c42;}.b{stroke-linejoin:round;fill:#fff}</style>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(0.5 0.5)">
|
||||||
|
<path class="a" d="M-12060-11667.842v14.261h10.442v-10.591l-3.671-3.67Z"
|
||||||
|
transform="translate(12059.999 11667.883)"/>
|
||||||
|
<path class="b" d="M-12051.353-11664.255v-3.639l3.528,3.639Z" transform="translate(12058.188 11667.894)"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 527 B |
10
portal-ui/public/images/ob_folder_clear.svg
Normal file
10
portal-ui/public/images/ob_folder_clear.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="15.999" height="13.999" viewBox="0 0 15.999 13.999">
|
||||||
|
<defs>
|
||||||
|
<style>.a{fill:none;stroke-linecap:square;}.b,.c{stroke:none;}.c{fill:#081c42;}</style>
|
||||||
|
</defs>
|
||||||
|
<g class="a" transform="translate(-0.001 0.001)">
|
||||||
|
<path class="b" d="M0,14V0H8.572V2.411H16V14Z"/>
|
||||||
|
<path class="c"
|
||||||
|
d="M 15.00020027160645 12.99860000610352 L 15.00020027160645 3.411099910736084 L 8.571599960327148 3.411099910736084 L 7.571600437164307 3.411099910736084 L 7.571600437164307 2.411099910736084 L 7.571600437164307 0.9990998506546021 L 1.000900268554688 0.9990998506546021 L 1.000900268554688 2.411099910736084 L 1.000900268554688 12.99860000610352 L 15.00020027160645 12.99860000610352 M 16.00020027160645 13.99860000610352 L 0.0009002700680866838 13.99860000610352 L 0.0009002700680866838 2.411099910736084 L 0.0009002700680866838 -0.0009001312428154051 L 8.571599960327148 -0.0009001312428154051 L 8.571599960327148 2.411099910736084 L 16.00020027160645 2.411099910736084 L 16.00020027160645 13.99860000610352 Z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
10
portal-ui/public/images/ob_folder_filled.svg
Normal file
10
portal-ui/public/images/ob_folder_filled.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="15.999" height="13.999" viewBox="0 0 15.999 13.999">
|
||||||
|
<defs>
|
||||||
|
<style>.a{fill:none;stroke-linecap:square;}.b,.c{stroke:none;fill:#081c42}.c{fill:#081c42;}</style>
|
||||||
|
</defs>
|
||||||
|
<g class="a" transform="translate(-0.001 0.001)">
|
||||||
|
<path class="b" d="M0,14V0H8.572V2.411H16V14Z"/>
|
||||||
|
<path class="c"
|
||||||
|
d="M 15.00020027160645 12.99860000610352 L 15.00020027160645 3.411099910736084 L 8.571599960327148 3.411099910736084 L 7.571600437164307 3.411099910736084 L 7.571600437164307 2.411099910736084 L 7.571600437164307 0.9990998506546021 L 1.000900268554688 0.9990998506546021 L 1.000900268554688 2.411099910736084 L 1.000900268554688 12.99860000610352 L 15.00020027160645 12.99860000610352 M 16.00020027160645 13.99860000610352 L 0.0009002700680866838 13.99860000610352 L 0.0009002700680866838 2.411099910736084 L 0.0009002700680866838 -0.0009001312428154051 L 8.571599960327148 -0.0009001312428154051 L 8.571599960327148 2.411099910736084 L 16.00020027160645 2.411099910736084 L 16.00020027160645 13.99860000610352 Z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -15,17 +15,10 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import { Redirect, Route, Router, Switch } from "react-router-dom";
|
||||||
Redirect,
|
|
||||||
Route,
|
|
||||||
Router,
|
|
||||||
Switch,
|
|
||||||
BrowserRouter,
|
|
||||||
} from "react-router-dom";
|
|
||||||
import history from "./history";
|
import history from "./history";
|
||||||
import Login from "./screens/LoginPage/LoginPage";
|
import Login from "./screens/LoginPage/LoginPage";
|
||||||
import Console from "./screens/Console/Console";
|
import Console from "./screens/Console/Console";
|
||||||
import NotFoundPage from "./screens/NotFoundPage";
|
|
||||||
import storage from "local-storage-fallback";
|
import storage from "local-storage-fallback";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { AppState } from "./store";
|
import { AppState } from "./store";
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import storage from "local-storage-fallback";
|
import storage from "local-storage-fallback";
|
||||||
import { ICapacity, IStorageType, IZoneModel } from "./types";
|
import { ICapacity, IZoneModel } from "./types";
|
||||||
|
|
||||||
const minStReq = 1073741824; // Minimal Space required for MinIO
|
const minStReq = 1073741824; // Minimal Space required for MinIO
|
||||||
const minMemReq = 2147483648; // Minimal Memory required for MinIO in bytes
|
const minMemReq = 2147483648; // Minimal Memory required for MinIO in bytes
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// This file is part of MinIO Console Server
|
// 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
|
// 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
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
@@ -15,21 +15,36 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {SvgIcon} from "@material-ui/core";
|
import { SvgIcon } from "@material-ui/core";
|
||||||
class CreateIcon extends React.Component {
|
class CreateIcon extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (<SvgIcon>
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
<SvgIcon>
|
||||||
<title>ic_h_create-new_sl</title>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
|
||||||
<g id="Layer_2" data-name="Layer 2">
|
<g
|
||||||
<g id="Layer_1-2" data-name="Layer 1">
|
id="Group_55"
|
||||||
<path className="cls-1"
|
data-name="Group 55"
|
||||||
d="M0,0V16H16V0ZM11.886,9.048H9.048v2.838h-2.1V9.048H4.114v-2.1H6.952V4.114h2.1V6.952h2.838Z"/>
|
transform="translate(1002 -2555)"
|
||||||
</g>
|
>
|
||||||
</g>
|
<rect
|
||||||
</svg>
|
id="Rectangle_29"
|
||||||
</SvgIcon>)
|
width="2"
|
||||||
}
|
height="12"
|
||||||
|
transform="translate(-997 2555)"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
id="Rectangle_30"
|
||||||
|
width="2"
|
||||||
|
height="12"
|
||||||
|
transform="translate(-990 2560) rotate(90)"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</SvgIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CreateIcon;
|
export default CreateIcon;
|
||||||
|
|||||||
41
portal-ui/src/icons/UploadFile.tsx
Normal file
41
portal-ui/src/icons/UploadFile.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// 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 UploadFile extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<SvgIcon>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.996">
|
||||||
|
<g transform="translate(-63.686 -70.783)">
|
||||||
|
<path
|
||||||
|
className="a"
|
||||||
|
d="M74.736,79.879v1.95h-9.1v-1.95h-1.95v3.9h13v-3.9Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
className="a"
|
||||||
|
d="M69.211,80.533h1.95V73.861h1.525l-2.5-3.078-2.5,3.078h1.525Z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</SvgIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UploadFile;
|
||||||
@@ -23,7 +23,6 @@ import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
|||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import CheckboxWrapper from "../../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
|
|
||||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
import { factorForDropdown, getBytes } from "../../../../common/utils";
|
import { factorForDropdown, getBytes } from "../../../../common/utils";
|
||||||
import { AppState } from "../../../../store";
|
import { AppState } from "../../../../store";
|
||||||
@@ -68,7 +67,7 @@ const styles = (theme: Theme) =>
|
|||||||
interface IAddBucketProps {
|
interface IAddBucketProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
closeModalAndRefresh: () => void;
|
closeModalAndRefresh: (refresh: boolean) => void;
|
||||||
addBucketName: typeof addBucketName;
|
addBucketName: typeof addBucketName;
|
||||||
addBucketVersioned: typeof addBucketVersioned;
|
addBucketVersioned: typeof addBucketVersioned;
|
||||||
addBucketQuota: typeof addBucketQuota;
|
addBucketQuota: typeof addBucketQuota;
|
||||||
@@ -131,7 +130,7 @@ const AddBucket = ({
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
setAddLoading(false);
|
setAddLoading(false);
|
||||||
setAddError("");
|
setAddError("");
|
||||||
closeModalAndRefresh();
|
closeModalAndRefresh(true);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setAddLoading(false);
|
setAddLoading(false);
|
||||||
@@ -143,7 +142,7 @@ const AddBucket = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
addBucketName(value);
|
addBucketName(value);
|
||||||
}, [value]);
|
}, [value, addBucketName]);
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
setBName("");
|
setBName("");
|
||||||
@@ -176,7 +175,7 @@ const AddBucket = ({
|
|||||||
modalOpen={open}
|
modalOpen={open}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setAddError("");
|
setAddError("");
|
||||||
closeModalAndRefresh();
|
closeModalAndRefresh(false);
|
||||||
}}
|
}}
|
||||||
aria-labelledby="alert-dialog-title"
|
aria-labelledby="alert-dialog-title"
|
||||||
aria-describedby="alert-dialog-description"
|
aria-describedby="alert-dialog-description"
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import { Button } from "@material-ui/core";
|
import { Button } from "@material-ui/core";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
@@ -93,7 +92,6 @@ const ListBuckets = ({
|
|||||||
const [totalRecords, setTotalRecords] = useState<number>(0);
|
const [totalRecords, setTotalRecords] = useState<number>(0);
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
const [deleteError, setDeleteError] = useState<string>("");
|
|
||||||
const [page, setPage] = useState<number>(0);
|
const [page, setPage] = useState<number>(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||||
@@ -136,10 +134,13 @@ const ListBuckets = ({
|
|||||||
}
|
}
|
||||||
}, [loading, page, rowsPerPage]);
|
}, [loading, page, rowsPerPage]);
|
||||||
|
|
||||||
const closeAddModalAndRefresh = () => {
|
const closeAddModalAndRefresh = (refresh: boolean) => {
|
||||||
addBucketOpen(false);
|
addBucketOpen(false);
|
||||||
addBucketReset();
|
addBucketReset();
|
||||||
setLoading(true);
|
|
||||||
|
if (refresh) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||||
@@ -184,28 +185,26 @@ const ListBuckets = ({
|
|||||||
return <Moment>{date}</Moment>;
|
return <Moment>{date}</Moment>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredRecords = records
|
const filteredRecords = records.filter((b: Bucket) => {
|
||||||
.filter((b: Bucket) => {
|
if (filterBuckets === "") {
|
||||||
if (filterBuckets === "") {
|
return true;
|
||||||
|
} else {
|
||||||
|
if (b.name.indexOf(filterBuckets) >= 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (b.name.indexOf(filterBuckets) >= 0) {
|
return false;
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.slice(offset, offset + rowsPerPage);
|
});
|
||||||
|
|
||||||
|
const showInPage = filteredRecords.slice(offset, offset + rowsPerPage);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{addBucketModalOpen && (
|
{addBucketModalOpen && (
|
||||||
<AddBucket
|
<AddBucket
|
||||||
open={addBucketModalOpen}
|
open={addBucketModalOpen}
|
||||||
closeModalAndRefresh={() => {
|
closeModalAndRefresh={closeAddModalAndRefresh}
|
||||||
closeAddModalAndRefresh();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{deleteOpen && (
|
{deleteOpen && (
|
||||||
@@ -219,6 +218,7 @@ const ListBuckets = ({
|
|||||||
)}
|
)}
|
||||||
<PageHeader label={"Buckets"} />
|
<PageHeader label={"Buckets"} />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
|
{error !== "" && <span className={classes.error}>{error}</span>}
|
||||||
<Grid item xs={12} className={classes.container}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<TextField
|
<TextField
|
||||||
@@ -269,13 +269,13 @@ const ListBuckets = ({
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
records={filteredRecords}
|
records={showInPage}
|
||||||
entityName="Buckets"
|
entityName="Buckets"
|
||||||
idField="name"
|
idField="name"
|
||||||
paginatorConfig={{
|
paginatorConfig={{
|
||||||
rowsPerPageOptions: [5, 10, 25],
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
colSpan: 3,
|
colSpan: 3,
|
||||||
count: totalRecords,
|
count: filteredRecords.length,
|
||||||
rowsPerPage: rowsPerPage,
|
rowsPerPage: rowsPerPage,
|
||||||
page: page,
|
page: page,
|
||||||
SelectProps: {
|
SelectProps: {
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
// 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 ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
|
||||||
|
import { Button, Grid, LinearProgress } from "@material-ui/core";
|
||||||
|
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createFolder } from "../../../../ObjectBrowser/actions";
|
||||||
|
|
||||||
|
interface ICreateFolder {
|
||||||
|
classes: any;
|
||||||
|
modalOpen: boolean;
|
||||||
|
folderName: string;
|
||||||
|
createFolder: (newFolder: string) => any;
|
||||||
|
onClose: () => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
buttonContainer: {
|
||||||
|
textAlign: "right",
|
||||||
|
},
|
||||||
|
pathLabel: {
|
||||||
|
marginTop: 0,
|
||||||
|
marginBottom: 32,
|
||||||
|
},
|
||||||
|
...modalBasic,
|
||||||
|
});
|
||||||
|
|
||||||
|
const CreateFolderModal = ({
|
||||||
|
modalOpen,
|
||||||
|
folderName,
|
||||||
|
onClose,
|
||||||
|
createFolder,
|
||||||
|
classes,
|
||||||
|
}: ICreateFolder) => {
|
||||||
|
const [pathUrl, setPathUrl] = useState("");
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
setPathUrl("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const createProcess = () => {
|
||||||
|
createFolder(pathUrl);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const folderTruncated = folderName.split("/").slice(2).join("/");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<ModalWrapper modalOpen={modalOpen} title="Add Folder" onClose={onClose}>
|
||||||
|
<Grid container>
|
||||||
|
<h3 className={classes.pathLabel}>
|
||||||
|
Current Path: {folderTruncated}/
|
||||||
|
</h3>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
value={pathUrl}
|
||||||
|
label={"Folder Path"}
|
||||||
|
id={"folderPath"}
|
||||||
|
name={"folderPath"}
|
||||||
|
placeholder={"Enter Folder Path"}
|
||||||
|
onChange={(e) => {
|
||||||
|
setPathUrl(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.buttonContainer}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
color="primary"
|
||||||
|
className={classes.clearButton}
|
||||||
|
onClick={resetForm}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={pathUrl.trim() === ""}
|
||||||
|
onClick={createProcess}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ModalWrapper>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
createFolder,
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(null, mapDispatchToProps);
|
||||||
|
|
||||||
|
export default connector(withStyles(styles)(CreateFolderModal));
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
@@ -21,7 +22,6 @@ import InputAdornment from "@material-ui/core/InputAdornment";
|
|||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
import { BucketObject, BucketObjectsList } from "./types";
|
import { BucketObject, BucketObjectsList } from "./types";
|
||||||
import api from "../../../../../../common/api";
|
import api from "../../../../../../common/api";
|
||||||
import React from "react";
|
|
||||||
import TableWrapper from "../../../../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../../../../Common/TableWrapper/TableWrapper";
|
||||||
import { niceBytes } from "../../../../../../common/utils";
|
import { niceBytes } from "../../../../../../common/utils";
|
||||||
import DeleteObject from "./DeleteObject";
|
import DeleteObject from "./DeleteObject";
|
||||||
@@ -29,6 +29,7 @@ import DeleteObject from "./DeleteObject";
|
|||||||
import {
|
import {
|
||||||
actionsTray,
|
actionsTray,
|
||||||
containerForHeader,
|
containerForHeader,
|
||||||
|
objectBrowserCommon,
|
||||||
searchField,
|
searchField,
|
||||||
} from "../../../../Common/FormComponents/common/styleLibrary";
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
import PageHeader from "../../../../Common/PageHeader/PageHeader";
|
import PageHeader from "../../../../Common/PageHeader/PageHeader";
|
||||||
@@ -38,6 +39,23 @@ import { Button, Input } from "@material-ui/core";
|
|||||||
import * as reactMoment from "react-moment";
|
import * as reactMoment from "react-moment";
|
||||||
import { CreateIcon } from "../../../../../../icons";
|
import { CreateIcon } from "../../../../../../icons";
|
||||||
import Snackbar from "@material-ui/core/Snackbar";
|
import Snackbar from "@material-ui/core/Snackbar";
|
||||||
|
import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs";
|
||||||
|
import get from "lodash/get";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import { addRoute, setAllRoutes } from "../../../../ObjectBrowser/actions";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { ObjectBrowserState, Route } from "../../../../ObjectBrowser/reducers";
|
||||||
|
import CreateFolderModal from "./CreateFolderModal";
|
||||||
|
import { create } from "domain";
|
||||||
|
import UploadFile from "../../../../../../icons/UploadFile";
|
||||||
|
|
||||||
|
const commonIcon = {
|
||||||
|
backgroundRepeat: "no-repeat",
|
||||||
|
backgroundPosition: "center center",
|
||||||
|
width: 16,
|
||||||
|
height: 40,
|
||||||
|
marginRight: 10,
|
||||||
|
};
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -69,93 +87,125 @@ const styles = (theme: Theme) =>
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
fileName: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
iconFolder: {
|
||||||
|
backgroundImage: "url(/images/ob_folder_clear.svg)",
|
||||||
|
...commonIcon,
|
||||||
|
},
|
||||||
|
iconFile: {
|
||||||
|
backgroundImage: "url(/images/ob_file_clear.svg)",
|
||||||
|
...commonIcon,
|
||||||
|
},
|
||||||
|
buttonsContainer: {
|
||||||
|
"& .MuiButtonBase-root": {
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"@global": {
|
||||||
|
".rowElementRaw:hover .iconFileElm": {
|
||||||
|
backgroundImage: "url(/images/ob_file_filled.svg)",
|
||||||
|
},
|
||||||
|
".rowElementRaw:hover .iconFolderElm": {
|
||||||
|
backgroundImage: "url(/images/ob_folder_filled.svg)",
|
||||||
|
},
|
||||||
|
},
|
||||||
...actionsTray,
|
...actionsTray,
|
||||||
...searchField,
|
...searchField,
|
||||||
|
...objectBrowserCommon,
|
||||||
...containerForHeader(theme.spacing(4)),
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IListObjectsProps {
|
interface IListObjectsProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
match: any;
|
match: any;
|
||||||
|
addRoute: (param1: string, param2: string) => any;
|
||||||
|
setAllRoutes: (path: string) => any;
|
||||||
|
routesList: Route[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IListObjectsState {
|
interface ObjectBrowserReducer {
|
||||||
records: BucketObject[];
|
objectBrowser: ObjectBrowserState;
|
||||||
totalRecords: number;
|
|
||||||
loading: boolean;
|
|
||||||
error: string;
|
|
||||||
deleteOpen: boolean;
|
|
||||||
deleteError: string;
|
|
||||||
selectedObject: string;
|
|
||||||
selectedBucket: string;
|
|
||||||
filterObjects: string;
|
|
||||||
openSnackbar: boolean;
|
|
||||||
snackBarMessage: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListObjects extends React.Component<
|
const ListObjects = ({
|
||||||
IListObjectsProps,
|
classes,
|
||||||
IListObjectsState
|
match,
|
||||||
> {
|
addRoute,
|
||||||
state: IListObjectsState = {
|
setAllRoutes,
|
||||||
records: [],
|
routesList,
|
||||||
totalRecords: 0,
|
}: IListObjectsProps) => {
|
||||||
loading: false,
|
const [records, setRecords] = useState<BucketObject[]>([]);
|
||||||
error: "",
|
const [totalRecords, setTotalRecords] = useState<number>(0);
|
||||||
deleteOpen: false,
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
deleteError: "",
|
const [error, setError] = useState<string>("");
|
||||||
selectedObject: "",
|
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||||
selectedBucket: "",
|
const [createFolderOpen, setCreateFolderOpen] = useState<boolean>(false);
|
||||||
filterObjects: "",
|
const [deleteError, setDeleteError] = useState<string>("");
|
||||||
openSnackbar: false,
|
const [selectedObject, setSelectedObject] = useState<string>("");
|
||||||
snackBarMessage: "",
|
const [selectedBucket, setSelectedBucket] = useState<string>("");
|
||||||
|
const [filterObjects, setFilterObjects] = useState<string>("");
|
||||||
|
const [openSnackbar, setOpenSnackbar] = useState<boolean>(false);
|
||||||
|
const [snackBarMessage, setSnackbarMessage] = useState<string>("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const bucketName = match.params["bucket"];
|
||||||
|
const internalPaths = match.params[0];
|
||||||
|
|
||||||
|
let extraPath = "";
|
||||||
|
if (internalPaths) {
|
||||||
|
extraPath = `?prefix=${internalPaths}/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/buckets/${bucketName}/objects${extraPath}`)
|
||||||
|
.then((res: BucketObjectsList) => {
|
||||||
|
setLoading(false);
|
||||||
|
setSelectedBucket(bucketName);
|
||||||
|
setRecords(res.objects || []);
|
||||||
|
setTotalRecords(!res.objects ? 0 : res.total);
|
||||||
|
setError("");
|
||||||
|
// TODO:
|
||||||
|
// if we get 0 results, and page > 0 , go down 1 page
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
setLoading(false);
|
||||||
|
setError(err);
|
||||||
|
});
|
||||||
|
}, [loading, match]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const url = get(match, "url", "/object-browser");
|
||||||
|
if (url !== routesList[routesList.length - 1].route) {
|
||||||
|
setAllRoutes(url);
|
||||||
|
}
|
||||||
|
}, [match, routesList, setAllRoutes]);
|
||||||
|
|
||||||
|
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||||
|
setDeleteOpen(false);
|
||||||
|
|
||||||
|
if (refresh) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchRecords = () => {
|
const closeAddFolderModal = () => {
|
||||||
this.setState({ loading: true }, () => {
|
setCreateFolderOpen(false);
|
||||||
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 {
|
const showSnackBarMessage = (text: string) => {
|
||||||
this.fetchRecords();
|
setSnackbarMessage(text);
|
||||||
}
|
setOpenSnackbar(true);
|
||||||
|
};
|
||||||
|
|
||||||
closeDeleteModalAndRefresh(refresh: boolean) {
|
const closeSnackBar = () => {
|
||||||
this.setState({ deleteOpen: false }, () => {
|
setSnackbarMessage("");
|
||||||
if (refresh) {
|
setOpenSnackbar(false);
|
||||||
this.fetchRecords();
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showSnackBarMessage(text: string) {
|
const upload = (e: any, bucketName: string, path: string) => {
|
||||||
this.setState({ openSnackbar: true, snackBarMessage: text });
|
|
||||||
}
|
|
||||||
|
|
||||||
closeSnackBar() {
|
|
||||||
this.setState({ openSnackbar: false, snackBarMessage: `` });
|
|
||||||
}
|
|
||||||
|
|
||||||
upload(e: any, bucketName: string, path: string) {
|
|
||||||
let listObjects = this;
|
|
||||||
if (isNullOrUndefined(e) || isNullOrUndefined(e.target)) {
|
if (isNullOrUndefined(e) || isNullOrUndefined(e.target)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -174,25 +224,21 @@ class ListObjects extends React.Component<
|
|||||||
xhr.withCredentials = false;
|
xhr.withCredentials = false;
|
||||||
xhr.onload = function (event) {
|
xhr.onload = function (event) {
|
||||||
// TODO: handle status
|
// TODO: handle status
|
||||||
if (xhr.status == 401 || xhr.status == 403) {
|
if (xhr.status === 401 || xhr.status === 403) {
|
||||||
listObjects.showSnackBarMessage(
|
showSnackBarMessage("An error occurred while uploading the file.");
|
||||||
"An error occurred while uploading the file."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (xhr.status == 500) {
|
if (xhr.status === 500) {
|
||||||
listObjects.showSnackBarMessage(
|
showSnackBarMessage("An error occurred while uploading the file.");
|
||||||
"An error occurred while uploading the file."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (xhr.status == 200) {
|
if (xhr.status === 200) {
|
||||||
listObjects.showSnackBarMessage("Object uploaded successfully.");
|
showSnackBarMessage("Object uploaded successfully.");
|
||||||
listObjects.fetchRecords();
|
setLoading(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.upload.addEventListener("error", (event) => {
|
xhr.upload.addEventListener("error", (event) => {
|
||||||
// TODO: handle error
|
// TODO: handle error
|
||||||
this.showSnackBarMessage("An error occurred while uploading the file.");
|
showSnackBarMessage("An error occurred while uploading the file.");
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.upload.addEventListener("progress", (event) => {
|
xhr.upload.addEventListener("progress", (event) => {
|
||||||
@@ -200,24 +246,22 @@ class ListObjects extends React.Component<
|
|||||||
});
|
});
|
||||||
|
|
||||||
xhr.onerror = () => {
|
xhr.onerror = () => {
|
||||||
listObjects.showSnackBarMessage(
|
showSnackBarMessage("An error occurred while uploading the file.");
|
||||||
"An error occurred while uploading the file."
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var formData = new FormData();
|
const formData = new FormData();
|
||||||
var blobFile = new Blob([file]);
|
const blobFile = new Blob([file]);
|
||||||
|
|
||||||
formData.append("upfile", blobFile);
|
formData.append("upfile", blobFile);
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
e.target.value = null;
|
e.target.value = null;
|
||||||
}
|
};
|
||||||
|
|
||||||
download(bucketName: string, objectName: string) {
|
const download = (bucketName: string, objectName: string) => {
|
||||||
var anchor = document.createElement("a");
|
const anchor = document.createElement("a");
|
||||||
document.body.appendChild(anchor);
|
document.body.appendChild(anchor);
|
||||||
const token: string = storage.getItem("token")!;
|
const token: string = storage.getItem("token")!;
|
||||||
var xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
xhr.open(
|
xhr.open(
|
||||||
"GET",
|
"GET",
|
||||||
@@ -228,11 +272,11 @@ class ListObjects extends React.Component<
|
|||||||
xhr.responseType = "blob";
|
xhr.responseType = "blob";
|
||||||
|
|
||||||
xhr.onload = function (e) {
|
xhr.onload = function (e) {
|
||||||
if (this.status == 200) {
|
if (this.status === 200) {
|
||||||
var blob = new Blob([this.response], {
|
const blob = new Blob([this.response], {
|
||||||
type: "octet/stream",
|
type: "octet/stream",
|
||||||
});
|
});
|
||||||
var blobUrl = window.URL.createObjectURL(blob);
|
const blobUrl = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
anchor.href = blobUrl;
|
anchor.href = blobUrl;
|
||||||
anchor.download = objectName;
|
anchor.download = objectName;
|
||||||
@@ -243,158 +287,228 @@ class ListObjects extends React.Component<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.send();
|
xhr.send();
|
||||||
}
|
};
|
||||||
|
|
||||||
bucketFilter(): void {}
|
const displayParsedDate = (date: string) => {
|
||||||
|
return <reactMoment.default>{date}</reactMoment.default>;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
const confirmDeleteObject = (object: string) => {
|
||||||
const { classes } = this.props;
|
setDeleteOpen(true);
|
||||||
const {
|
setSelectedObject(object);
|
||||||
records,
|
};
|
||||||
loading,
|
|
||||||
selectedObject,
|
|
||||||
selectedBucket,
|
|
||||||
deleteOpen,
|
|
||||||
filterObjects,
|
|
||||||
snackBarMessage,
|
|
||||||
openSnackbar,
|
|
||||||
} = this.state;
|
|
||||||
const displayParsedDate = (date: string) => {
|
|
||||||
return <reactMoment.default>{date}</reactMoment.default>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmDeleteObject = (object: string) => {
|
const downloadObject = (object: string) => {
|
||||||
this.setState({ deleteOpen: true, selectedObject: object });
|
download(selectedBucket, object);
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadObject = (object: string) => {
|
const openPath = (idElement: string) => {
|
||||||
this.download(selectedBucket, object);
|
const currentPath = get(match, "url", "/object-browser");
|
||||||
};
|
|
||||||
|
|
||||||
const uploadObject = (e: any): void => {
|
// Element is a folder, we redirect to it
|
||||||
// TODO: handle deeper paths/folders
|
if (idElement.endsWith("/")) {
|
||||||
let file = e.target.files[0];
|
const idElementClean = idElement
|
||||||
this.showSnackBarMessage(`Uploading: ${file.name}`);
|
.substr(0, idElement.length - 1)
|
||||||
this.upload(e, selectedBucket, "");
|
.split("/");
|
||||||
};
|
const lastIndex = idElementClean.length - 1;
|
||||||
|
const newPath = `${currentPath}/${idElementClean[lastIndex]}`;
|
||||||
|
|
||||||
const snackBarAction = (
|
addRoute(newPath, idElementClean[lastIndex]);
|
||||||
<Button
|
return;
|
||||||
color="secondary"
|
}
|
||||||
size="small"
|
|
||||||
onClick={() => {
|
|
||||||
this.closeSnackBar();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Dismiss
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
|
|
||||||
const tableActions = [
|
// Element is a file. we open details here
|
||||||
{ type: "download", onClick: downloadObject, sendOnlyId: true },
|
// TODO: Add details open function here.
|
||||||
{ type: "delete", onClick: confirmDeleteObject, sendOnlyId: true },
|
//console.log("object", idElementClean);
|
||||||
];
|
};
|
||||||
|
|
||||||
const filteredRecords = records.filter((b: BucketObject) => {
|
const uploadObject = (e: any): void => {
|
||||||
if (filterObjects === "") {
|
// Handle of deeper routes.
|
||||||
return true;
|
const currentPath = routesList[routesList.length - 1].route;
|
||||||
} else {
|
const splitPaths = currentPath
|
||||||
if (b.name.indexOf(filterObjects) >= 0) {
|
.split("/")
|
||||||
return true;
|
.filter((item) => item.trim() !== "");
|
||||||
} else {
|
|
||||||
return false;
|
let path = "";
|
||||||
}
|
|
||||||
}
|
if (splitPaths.length > 2) {
|
||||||
});
|
path = `${splitPaths.slice(2).join("/")}/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = e.target.files[0];
|
||||||
|
showSnackBarMessage(`Uploading: ${file.name}`);
|
||||||
|
upload(e, selectedBucket, path);
|
||||||
|
};
|
||||||
|
|
||||||
|
const snackBarAction = (
|
||||||
|
<Button
|
||||||
|
color="secondary"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
closeSnackBar();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Dismiss
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableActions = [
|
||||||
|
{ type: "view", onClick: openPath, sendOnlyId: true },
|
||||||
|
{ type: "download", onClick: downloadObject, sendOnlyId: true },
|
||||||
|
{ type: "delete", onClick: confirmDeleteObject, sendOnlyId: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const displayName = (element: string) => {
|
||||||
|
let elementString = element;
|
||||||
|
let icon = `${classes.iconFile} iconFileElm`;
|
||||||
|
// Element is a folder
|
||||||
|
if (element.endsWith("/")) {
|
||||||
|
icon = `${classes.iconFolder} iconFolderElm`;
|
||||||
|
elementString = element.substr(0, element.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitItem = elementString.split("/");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<div className={classes.fileName}>
|
||||||
{deleteOpen && (
|
<div className={icon} />
|
||||||
<DeleteObject
|
<span>{splitItem[splitItem.length - 1]}</span>
|
||||||
deleteOpen={deleteOpen}
|
</div>
|
||||||
selectedBucket={selectedBucket}
|
);
|
||||||
selectedObject={selectedObject}
|
};
|
||||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
|
||||||
this.closeDeleteModalAndRefresh(refresh);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Snackbar
|
|
||||||
open={openSnackbar}
|
|
||||||
message={snackBarMessage}
|
|
||||||
action={snackBarAction}
|
|
||||||
/>
|
|
||||||
<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>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<>
|
const filteredRecords = records.filter((b: BucketObject) => {
|
||||||
<Button
|
if (filterObjects === "") {
|
||||||
variant="contained"
|
return true;
|
||||||
color="primary"
|
} else {
|
||||||
startIcon={<CreateIcon />}
|
if (b.name.indexOf(filterObjects) >= 0) {
|
||||||
component="label"
|
return true;
|
||||||
>
|
} else {
|
||||||
Upload Object
|
return false;
|
||||||
<Input
|
}
|
||||||
type="file"
|
}
|
||||||
onChange={(e) => uploadObject(e)}
|
});
|
||||||
id="file-input"
|
|
||||||
style={{ display: "none" }}
|
return (
|
||||||
/>
|
<React.Fragment>
|
||||||
</Button>
|
{deleteOpen && (
|
||||||
</>
|
<DeleteObject
|
||||||
</Grid>
|
deleteOpen={deleteOpen}
|
||||||
<Grid item xs={12}>
|
selectedBucket={selectedBucket}
|
||||||
<br />
|
selectedObject={selectedObject}
|
||||||
</Grid>
|
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||||
<Grid item xs={12}>
|
/>
|
||||||
<TableWrapper
|
)}
|
||||||
itemActions={tableActions}
|
{createFolderOpen && (
|
||||||
columns={[
|
<CreateFolderModal
|
||||||
{ label: "Name", elementKey: "name" },
|
modalOpen={createFolderOpen}
|
||||||
{
|
folderName={routesList[routesList.length - 1].route}
|
||||||
label: "Last Modified",
|
onClose={closeAddFolderModal}
|
||||||
elementKey: "last_modified",
|
/>
|
||||||
renderFunction: displayParsedDate,
|
)}
|
||||||
},
|
<Snackbar
|
||||||
{
|
open={openSnackbar}
|
||||||
label: "Size",
|
message={snackBarMessage}
|
||||||
elementKey: "size",
|
action={snackBarAction}
|
||||||
renderFunction: niceBytes,
|
/>
|
||||||
},
|
<PageHeader label="Object Browser" />
|
||||||
]}
|
<Grid container>
|
||||||
isLoading={loading}
|
<Grid item xs={12} className={classes.container}>
|
||||||
entityName="Objects"
|
<Grid item xs={12} className={classes.obTitleSection}>
|
||||||
idField="name"
|
<div>
|
||||||
records={filteredRecords}
|
<BrowserBreadcrumbs />
|
||||||
/>
|
</div>
|
||||||
</Grid>
|
<div className={classes.buttonsContainer}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<CreateIcon />}
|
||||||
|
component="label"
|
||||||
|
onClick={() => {
|
||||||
|
setCreateFolderOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create Folder
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<UploadFile />}
|
||||||
|
component="label"
|
||||||
|
>
|
||||||
|
File
|
||||||
|
<Input
|
||||||
|
type="file"
|
||||||
|
onChange={(e) => uploadObject(e)}
|
||||||
|
id="file-input"
|
||||||
|
style={{ display: "none" }}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
|
<TextField
|
||||||
|
placeholder="Search Objects"
|
||||||
|
className={classes.searchField}
|
||||||
|
id="search-resource"
|
||||||
|
label=""
|
||||||
|
onChange={(val) => {
|
||||||
|
setFilterObjects(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",
|
||||||
|
renderFunction: displayName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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>
|
</Grid>
|
||||||
</React.Fragment>
|
</Grid>
|
||||||
);
|
</React.Fragment>
|
||||||
}
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default withStyles(styles)(ListObjects);
|
const mapStateToProps = ({ objectBrowser }: ObjectBrowserReducer) => ({
|
||||||
|
routesList: get(objectBrowser, "routesList", []),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
addRoute,
|
||||||
|
setAllRoutes,
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
|
export default withRouter(connector(withStyles(styles)(ListObjects)));
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState } from "react";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
@@ -22,7 +22,6 @@ import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
|||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
|
||||||
import { Button, LinearProgress } from "@material-ui/core";
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
import { IRemoteBucket } from "../types";
|
import { IRemoteBucket } from "../types";
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
// This file is part of MinIO Console Server
|
||||||
|
// Copyright (c) 2020 MinIO, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import api from "../../../../common/api";
|
||||||
|
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||||
|
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
|
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
errorBlock: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
minTableHeader: {
|
||||||
|
color: "#393939",
|
||||||
|
"& tr": {
|
||||||
|
"& th": {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
textAlign: "right",
|
||||||
|
},
|
||||||
|
...modalBasic,
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IEnableBucketEncryptionProps {
|
||||||
|
classes: any;
|
||||||
|
open: boolean;
|
||||||
|
selectedBucket: string;
|
||||||
|
closeModalAndRefresh: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IEnableBucketEncryptionState {
|
||||||
|
loading: boolean;
|
||||||
|
encryptionError: string;
|
||||||
|
kmsKeyID: string;
|
||||||
|
suffix: string;
|
||||||
|
encryptionType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnableBucketEncryption extends React.Component<
|
||||||
|
IEnableBucketEncryptionProps,
|
||||||
|
IEnableBucketEncryptionState
|
||||||
|
> {
|
||||||
|
state: IEnableBucketEncryptionState = {
|
||||||
|
loading: false,
|
||||||
|
encryptionError: "",
|
||||||
|
kmsKeyID: "",
|
||||||
|
suffix: "",
|
||||||
|
encryptionType: "sse-s3",
|
||||||
|
};
|
||||||
|
|
||||||
|
enableBucketEncryption(event: React.FormEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
const { kmsKeyID, loading, encryptionType } = this.state;
|
||||||
|
const { selectedBucket } = this.props;
|
||||||
|
if (loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api
|
||||||
|
.invoke("POST", `/api/v1/buckets/${selectedBucket}/encryption/enable`, {
|
||||||
|
encType: encryptionType,
|
||||||
|
kmsKeyID: kmsKeyID,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
loading: false,
|
||||||
|
encryptionError: "",
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.props.closeModalAndRefresh();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
this.setState({ encryptionError: err });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes, open } = this.props;
|
||||||
|
const { loading, encryptionError, kmsKeyID, encryptionType } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalWrapper
|
||||||
|
modalOpen={open}
|
||||||
|
onClose={() => {
|
||||||
|
this.setState({ encryptionError: "" }, () => {
|
||||||
|
this.props.closeModalAndRefresh();
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
title="Enable Bucket Encryption"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
noValidate
|
||||||
|
autoComplete="off"
|
||||||
|
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
this.enableBucketEncryption(e);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12} className={classes.formScrollable}>
|
||||||
|
{encryptionError !== "" && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography
|
||||||
|
component="p"
|
||||||
|
variant="body1"
|
||||||
|
className={classes.errorBlock}
|
||||||
|
>
|
||||||
|
{encryptionError}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<SelectWrapper
|
||||||
|
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||||
|
this.setState({ encryptionType: e.target.value as string });
|
||||||
|
}}
|
||||||
|
id="select-encryption-type"
|
||||||
|
name="select-encryption-type"
|
||||||
|
label={"Encryption Type"}
|
||||||
|
value={encryptionType}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: "SSE-S3",
|
||||||
|
value: "sse-s3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "SSE-KMS",
|
||||||
|
value: "sse-kms",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
{encryptionType === "sse-kms" && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="kms-key-id"
|
||||||
|
name="kms-key-id"
|
||||||
|
label="KMS Key ID"
|
||||||
|
value={kmsKeyID}
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({ kmsKeyID: e.target.value });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.buttonContainer}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
{loading && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<LinearProgress />
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</form>
|
||||||
|
</ModalWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(EnableBucketEncryption);
|
||||||
@@ -94,7 +94,7 @@ class SetAccessPolicy extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { classes, open, actualPolicy } = this.props;
|
const { classes, open } = this.props;
|
||||||
const { addLoading, addError, accessPolicy } = this.state;
|
const { addLoading, addError, accessPolicy } = this.state;
|
||||||
return (
|
return (
|
||||||
<ModalWrapper
|
<ModalWrapper
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
BucketEvent,
|
BucketEvent,
|
||||||
BucketEventList,
|
BucketEventList,
|
||||||
BucketInfo,
|
BucketInfo,
|
||||||
|
BucketEncryptionInfo,
|
||||||
BucketList,
|
BucketList,
|
||||||
BucketReplication,
|
BucketReplication,
|
||||||
BucketReplicationDestination,
|
BucketReplicationDestination,
|
||||||
@@ -34,8 +35,7 @@ import {
|
|||||||
BucketReplicationRuleDeleteMarker,
|
BucketReplicationRuleDeleteMarker,
|
||||||
BucketVersioning,
|
BucketVersioning,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import { Box, Button } from "@material-ui/core";
|
import { Button } from "@material-ui/core";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import SetAccessPolicy from "./SetAccessPolicy";
|
import SetAccessPolicy from "./SetAccessPolicy";
|
||||||
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
||||||
import { CreateIcon } from "../../../../icons";
|
import { CreateIcon } from "../../../../icons";
|
||||||
@@ -46,6 +46,8 @@ import { niceBytes } from "../../../../common/utils";
|
|||||||
import AddReplicationModal from "./AddReplicationModal";
|
import AddReplicationModal from "./AddReplicationModal";
|
||||||
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
||||||
import PageHeader from "../../Common/PageHeader/PageHeader";
|
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||||
|
import Checkbox from "@material-ui/core/Checkbox";
|
||||||
|
import EnableBucketEncryption from "./EnableBucketEncryption";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -191,6 +193,7 @@ interface IViewBucketState {
|
|||||||
rowsPerPage: number;
|
rowsPerPage: number;
|
||||||
curTab: number;
|
curTab: number;
|
||||||
addScreenOpen: boolean;
|
addScreenOpen: boolean;
|
||||||
|
enableEncryptionScreenOpen: boolean;
|
||||||
deleteOpen: boolean;
|
deleteOpen: boolean;
|
||||||
selectedBucket: string;
|
selectedBucket: string;
|
||||||
selectedEvent: BucketEvent | null;
|
selectedEvent: BucketEvent | null;
|
||||||
@@ -199,6 +202,7 @@ interface IViewBucketState {
|
|||||||
replicationSet: boolean;
|
replicationSet: boolean;
|
||||||
openSetReplication: boolean;
|
openSetReplication: boolean;
|
||||||
isVersioned: boolean;
|
isVersioned: boolean;
|
||||||
|
encryptionEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||||
@@ -218,6 +222,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
curTab: 0,
|
curTab: 0,
|
||||||
rowsPerPage: 10,
|
rowsPerPage: 10,
|
||||||
addScreenOpen: false,
|
addScreenOpen: false,
|
||||||
|
enableEncryptionScreenOpen: false,
|
||||||
deleteOpen: false,
|
deleteOpen: false,
|
||||||
selectedBucket: "",
|
selectedBucket: "",
|
||||||
selectedEvent: null,
|
selectedEvent: null,
|
||||||
@@ -226,6 +231,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
replicationSet: false,
|
replicationSet: false,
|
||||||
openSetReplication: false,
|
openSetReplication: false,
|
||||||
isVersioned: false,
|
isVersioned: false,
|
||||||
|
encryptionEnabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchEvents() {
|
fetchEvents() {
|
||||||
@@ -325,6 +331,21 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchBucketEncryptionInfo() {
|
||||||
|
const { match } = this.props;
|
||||||
|
const bucketName = match.params["bucketName"];
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/buckets/${bucketName}/encryption/info`)
|
||||||
|
.then((res: BucketEncryptionInfo) => {
|
||||||
|
if (res.algorithm) {
|
||||||
|
this.setState({ encryptionEnabled: true });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
closeAddModalAndRefresh() {
|
closeAddModalAndRefresh() {
|
||||||
this.setState({ setAccessPolicyScreenOpen: false }, () => {
|
this.setState({ setAccessPolicyScreenOpen: false }, () => {
|
||||||
this.loadInfo();
|
this.loadInfo();
|
||||||
@@ -343,6 +364,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
this.loadInfo();
|
this.loadInfo();
|
||||||
this.fetchEvents();
|
this.fetchEvents();
|
||||||
this.fetchBucketsSize();
|
this.fetchBucketsSize();
|
||||||
|
this.fetchBucketEncryptionInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
bucketFilter(): void {}
|
bucketFilter(): void {}
|
||||||
@@ -360,14 +382,15 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
rowsPerPage,
|
rowsPerPage,
|
||||||
deleteOpen,
|
deleteOpen,
|
||||||
addScreenOpen,
|
addScreenOpen,
|
||||||
|
enableEncryptionScreenOpen,
|
||||||
selectedEvent,
|
selectedEvent,
|
||||||
bucketSize,
|
bucketSize,
|
||||||
loadingSize,
|
loadingSize,
|
||||||
replicationSet,
|
|
||||||
openSetReplication,
|
openSetReplication,
|
||||||
isVersioned,
|
isVersioned,
|
||||||
replicationRules,
|
replicationRules,
|
||||||
curTab,
|
curTab,
|
||||||
|
encryptionEnabled,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const offset = page * rowsPerPage;
|
const offset = page * rowsPerPage;
|
||||||
@@ -415,6 +438,23 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
this.setState({ openSetReplication: open });
|
this.setState({ openSetReplication: open });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEncryptionCheckbox = (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
if (event.target.checked) {
|
||||||
|
this.setState({ enableEncryptionScreenOpen: true });
|
||||||
|
} else {
|
||||||
|
api
|
||||||
|
.invoke("POST", `/api/v1/buckets/${bucketName}/encryption/disable`)
|
||||||
|
.then(() => {
|
||||||
|
this.setState({ encryptionEnabled: false });
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
this.setState({ error: err });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
|
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
|
||||||
|
|
||||||
const filteredRecords = records.slice(offset, offset + rowsPerPage);
|
const filteredRecords = records.slice(offset, offset + rowsPerPage);
|
||||||
@@ -432,6 +472,16 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{enableEncryptionScreenOpen && (
|
||||||
|
<EnableBucketEncryption
|
||||||
|
open={enableEncryptionScreenOpen}
|
||||||
|
selectedBucket={bucketName}
|
||||||
|
closeModalAndRefresh={() => {
|
||||||
|
this.setState({ enableEncryptionScreenOpen: false });
|
||||||
|
this.fetchBucketEncryptionInfo();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{setAccessPolicyScreenOpen && (
|
{setAccessPolicyScreenOpen && (
|
||||||
<SetAccessPolicy
|
<SetAccessPolicy
|
||||||
bucketName={bucketName}
|
bucketName={bucketName}
|
||||||
@@ -490,6 +540,17 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
</div>
|
</div>
|
||||||
<div>Versioning:</div>
|
<div>Versioning:</div>
|
||||||
<div>{isVersioned ? "Yes" : "No"} </div>
|
<div>{isVersioned ? "Yes" : "No"} </div>
|
||||||
|
<div>Encryption:</div>
|
||||||
|
<div>
|
||||||
|
<Checkbox
|
||||||
|
color="primary"
|
||||||
|
inputProps={{
|
||||||
|
"aria-label": "secondary checkbox",
|
||||||
|
}}
|
||||||
|
onChange={(event) => handleEncryptionCheckbox(event)}
|
||||||
|
checked={encryptionEnabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ export interface Bucket {
|
|||||||
creation_date: Date;
|
creation_date: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BucketEncryptionInfo {
|
||||||
|
algorithm: string;
|
||||||
|
kmsMasterKeyID: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface BucketInfo {
|
export interface BucketInfo {
|
||||||
name: string;
|
name: string;
|
||||||
access: string;
|
access: string;
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ const CSVMultiSelector = ({
|
|||||||
firstUpdate.current = false;
|
firstUpdate.current = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
debouncedOnChange();
|
debouncedOnChange();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [currentElements]);
|
}, [currentElements]);
|
||||||
|
|
||||||
// If the last input is not empty, we add a new one
|
// If the last input is not empty, we add a new one
|
||||||
|
|||||||
@@ -14,21 +14,8 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import { Checkbox, Grid, InputLabel, Tooltip } from "@material-ui/core";
|
||||||
Checkbox,
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
Grid,
|
|
||||||
InputLabel,
|
|
||||||
TextField,
|
|
||||||
TextFieldProps,
|
|
||||||
Tooltip,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import { OutlinedInputProps } from "@material-ui/core/OutlinedInput";
|
|
||||||
import {
|
|
||||||
createStyles,
|
|
||||||
makeStyles,
|
|
||||||
Theme,
|
|
||||||
withStyles,
|
|
||||||
} from "@material-ui/core/styles";
|
|
||||||
import {
|
import {
|
||||||
checkboxIcons,
|
checkboxIcons,
|
||||||
fieldBasic,
|
fieldBasic,
|
||||||
@@ -54,8 +41,14 @@ const styles = (theme: Theme) =>
|
|||||||
...fieldBasic,
|
...fieldBasic,
|
||||||
...tooltipHelper,
|
...tooltipHelper,
|
||||||
...checkboxIcons,
|
...checkboxIcons,
|
||||||
labelContainer: {
|
fieldContainer: {
|
||||||
flexGrow: 1,
|
...fieldBasic.fieldContainer,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
alignItems: "center",
|
||||||
|
margin: "15px 0",
|
||||||
|
marginBottom: 0,
|
||||||
|
flexBasis: "initial",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,19 +66,7 @@ const CheckboxWrapper = ({
|
|||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Grid item xs={12} className={classes.fieldContainer}>
|
<Grid item xs={12} className={classes.fieldContainer}>
|
||||||
{label !== "" && (
|
<div>
|
||||||
<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.labelContainer}>
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name={name}
|
name={name}
|
||||||
id={id}
|
id={id}
|
||||||
@@ -99,6 +80,18 @@ const CheckboxWrapper = ({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{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>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,20 +14,8 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import { Grid, InputLabel, TextField, Tooltip } from "@material-ui/core";
|
||||||
Grid,
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
InputLabel,
|
|
||||||
TextField,
|
|
||||||
TextFieldProps,
|
|
||||||
Tooltip,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import { OutlinedInputProps } from "@material-ui/core/OutlinedInput";
|
|
||||||
import {
|
|
||||||
createStyles,
|
|
||||||
makeStyles,
|
|
||||||
Theme,
|
|
||||||
withStyles,
|
|
||||||
} from "@material-ui/core/styles";
|
|
||||||
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
|
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
|
||||||
import HelpIcon from "@material-ui/icons/Help";
|
import HelpIcon from "@material-ui/icons/Help";
|
||||||
|
|
||||||
@@ -105,12 +93,7 @@ const CommentBoxWrapper = ({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{label !== "" && (
|
{label !== "" && (
|
||||||
<InputLabel
|
<InputLabel htmlFor={id} className={classes.inputLabel}>
|
||||||
htmlFor={id}
|
|
||||||
className={`${error !== "" ? classes.fieldLabelError : ""} ${
|
|
||||||
classes.inputLabel
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span>
|
<span>
|
||||||
{label}
|
{label}
|
||||||
{required ? "*" : ""}
|
{required ? "*" : ""}
|
||||||
|
|||||||
@@ -76,6 +76,12 @@ const styles = (theme: Theme) =>
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
|
fieldBottom: {
|
||||||
|
borderBottom: "#9c9c9c 1px solid",
|
||||||
|
},
|
||||||
|
fileInputField: {
|
||||||
|
margin: "13px 0",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const FileSelector = ({
|
const FileSelector = ({
|
||||||
@@ -98,7 +104,7 @@ const FileSelector = ({
|
|||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
xs={12}
|
xs={12}
|
||||||
className={`${classes.fieldContainer} ${
|
className={`${classes.fieldBottom} ${classes.fieldContainer} ${
|
||||||
error !== "" ? classes.errorInField : ""
|
error !== "" ? classes.errorInField : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -137,6 +143,7 @@ const FileSelector = ({
|
|||||||
accept={accept}
|
accept={accept}
|
||||||
required={required}
|
required={required}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
className={classes.fileInputField}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{value !== "" && (
|
{value !== "" && (
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { InputLabel, Switch, Tooltip } from "@material-ui/core";
|
import { InputLabel, Switch, Tooltip } from "@material-ui/core";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ const inputStyles = makeStyles((theme: Theme) =>
|
|||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
color: "#b53b4b",
|
color: "#b53b4b",
|
||||||
boxShadow: "inset 0px 0px 1px 1px #b53b4b",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -166,12 +165,7 @@ const InputBoxWrapper = ({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{label !== "" && (
|
{label !== "" && (
|
||||||
<InputLabel
|
<InputLabel htmlFor={id} className={classes.inputLabel}>
|
||||||
htmlFor={id}
|
|
||||||
className={`${error !== "" ? classes.fieldLabelError : ""} ${
|
|
||||||
classes.inputLabel
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span>
|
<span>
|
||||||
{label}
|
{label}
|
||||||
{required ? "*" : ""}
|
{required ? "*" : ""}
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ export const actionsTray = {
|
|||||||
export const searchField = {
|
export const searchField = {
|
||||||
searchField: {
|
searchField: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
|
height: 40,
|
||||||
background: "#FFFFFF",
|
background: "#FFFFFF",
|
||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
border: "#EAEDEE 1px solid",
|
border: "#EAEDEE 1px solid",
|
||||||
@@ -178,3 +179,31 @@ export const predefinedList = {
|
|||||||
minHeight: 41,
|
minHeight: 41,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const objectBrowserCommon = {
|
||||||
|
obTitleSection: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 22,
|
||||||
|
color: "#000",
|
||||||
|
fontWeight: 600,
|
||||||
|
height: 40,
|
||||||
|
lineHeight: "40px",
|
||||||
|
},
|
||||||
|
breadcrumbs: {
|
||||||
|
fontSize: 10,
|
||||||
|
color: "#000",
|
||||||
|
marginTop: 2,
|
||||||
|
"& a": {
|
||||||
|
textDecoration: "none",
|
||||||
|
color: "#000",
|
||||||
|
"&:hover": {
|
||||||
|
textDecoration: "underline",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const styles = (theme: Theme) =>
|
|||||||
minWidth: 180,
|
minWidth: 180,
|
||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
"& ul": {
|
"& ul": {
|
||||||
padding: 15,
|
padding: "0 15px 0 0",
|
||||||
|
|
||||||
"& li": {
|
"& li": {
|
||||||
listStyle: "lower-roman",
|
listStyle: "lower-roman",
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const WizardPage = ({ classes, page, pageChange }: IWizardPage) => {
|
|||||||
break;
|
break;
|
||||||
case "to":
|
case "to":
|
||||||
pageChange(btn.toPage || 0);
|
pageChange(btn.toPage || 0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Dialog, DialogContent, DialogTitle } from "@material-ui/core";
|
import { Dialog, DialogContent, DialogTitle } from "@material-ui/core";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import {
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
createStyles,
|
|
||||||
makeStyles,
|
|
||||||
Theme,
|
|
||||||
withStyles,
|
|
||||||
} from "@material-ui/core/styles";
|
|
||||||
|
|
||||||
interface IModalProps {
|
interface IModalProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
|
|||||||
@@ -7,13 +7,17 @@ const DeleteIcon = ({ active = false }: IIcon) => {
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="16"
|
width="16"
|
||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 10.402 13"
|
||||||
>
|
>
|
||||||
<g transform="translate(-1225 -657)">
|
<g transform="translate(0.004 -28.959)">
|
||||||
<path
|
<path
|
||||||
fill={active ? selected : unSelected}
|
fill={active ? selected : unSelected}
|
||||||
d="M0,8H0a8,8,0,0,0,8,8H8a8,8,0,0,0,8-8h0A8,8,0,0,0,8,0H8A8,8,0,0,0,0,8Zm10.007,3.489L8,9.482,5.993,11.489,4.511,10.007,6.518,8,4.511,5.993,5.993,4.511,8,6.518l2.007-2.007,1.482,1.482L9.482,8l2.007,2.007Z"
|
d="M6.757,29.959v-1H3.636v1H0v1H10.4v-1Z"
|
||||||
transform="translate(1225 657)"
|
/>
|
||||||
|
<path
|
||||||
|
fill={active ? selected : unSelected}
|
||||||
|
d="M0,31.957l1.672,10H8.724l1.673-10ZM3.412,40.2,2.86,33.722h.653l.553,6.472Zm3.569,0H6.328l.551-6.472h.654Z"
|
||||||
|
transform="translate(0 0)"
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ interface IColumns {
|
|||||||
renderFunction?: (input: any) => any;
|
renderFunction?: (input: any) => any;
|
||||||
renderFullObject?: boolean;
|
renderFullObject?: boolean;
|
||||||
globalClass?: any;
|
globalClass?: any;
|
||||||
|
rowClass?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPaginatorConfig {
|
interface IPaginatorConfig {
|
||||||
@@ -89,12 +90,18 @@ interface TableWrapperProps {
|
|||||||
paginatorConfig?: IPaginatorConfig;
|
paginatorConfig?: IPaginatorConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
const borderColor = "#eaeaea";
|
const borderColor = "#9c9c9c80";
|
||||||
|
|
||||||
const rowText = {
|
const rowText = {
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
borderColor: borderColor,
|
borderColor: borderColor,
|
||||||
|
borderWidth: "0.5px",
|
||||||
|
height: 40,
|
||||||
|
transitionDuration: "0.3s",
|
||||||
|
padding: "initial",
|
||||||
|
paddingRight: 6,
|
||||||
|
paddingLeft: 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
@@ -107,10 +114,18 @@ const styles = (theme: Theme) =>
|
|||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
padding: "19px 38px",
|
padding: "19px 38px",
|
||||||
minHeight: "200px",
|
|
||||||
boxShadow: "none",
|
boxShadow: "none",
|
||||||
border: "#EAEDEE 1px solid",
|
border: "#EAEDEE 1px solid",
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
|
minHeight: "calc(100vh - 340px)",
|
||||||
|
},
|
||||||
|
allTableSettings: {
|
||||||
|
"& .MuiTableCell-sizeSmall:last-child": {
|
||||||
|
paddingRight: "initial",
|
||||||
|
},
|
||||||
|
"& .MuiTableCell-body.MuiTableCell-sizeSmall:last-child": {
|
||||||
|
paddingRight: 6,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
minTableHeader: {
|
minTableHeader: {
|
||||||
color: "#393939",
|
color: "#393939",
|
||||||
@@ -118,13 +133,15 @@ const styles = (theme: Theme) =>
|
|||||||
"& th": {
|
"& th": {
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
paddingBottom: 15,
|
borderColor: "#39393980",
|
||||||
borderColor: borderColor,
|
borderWidth: "0.5px",
|
||||||
|
padding: "6px 0 10px",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rowUnselected: {
|
rowUnselected: {
|
||||||
...rowText,
|
...rowText,
|
||||||
|
color: "#393939",
|
||||||
},
|
},
|
||||||
rowSelected: {
|
rowSelected: {
|
||||||
...rowText,
|
...rowText,
|
||||||
@@ -137,8 +154,12 @@ const styles = (theme: Theme) =>
|
|||||||
padding: "5px 38px",
|
padding: "5px 38px",
|
||||||
},
|
},
|
||||||
checkBoxHeader: {
|
checkBoxHeader: {
|
||||||
|
width: 50,
|
||||||
|
textAlign: "left",
|
||||||
|
paddingRight: 10,
|
||||||
"&.MuiTableCell-paddingCheckbox": {
|
"&.MuiTableCell-paddingCheckbox": {
|
||||||
paddingBottom: 9,
|
paddingBottom: 4,
|
||||||
|
paddingLeft: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actionsContainer: {
|
actionsContainer: {
|
||||||
@@ -150,6 +171,7 @@ const styles = (theme: Theme) =>
|
|||||||
},
|
},
|
||||||
checkBoxRow: {
|
checkBoxRow: {
|
||||||
borderColor: borderColor,
|
borderColor: borderColor,
|
||||||
|
padding: "0 10px 0 0",
|
||||||
},
|
},
|
||||||
loadingBox: {
|
loadingBox: {
|
||||||
paddingTop: "100px",
|
paddingTop: "100px",
|
||||||
@@ -160,6 +182,10 @@ const styles = (theme: Theme) =>
|
|||||||
|
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
backgroundColor: "#ececec",
|
backgroundColor: "#ececec",
|
||||||
|
|
||||||
|
"& td": {
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rowClickable: {
|
rowClickable: {
|
||||||
@@ -202,7 +228,9 @@ const rowColumnsMap = (
|
|||||||
return (
|
return (
|
||||||
<TableCell
|
<TableCell
|
||||||
key={`tbRE-${column.elementKey}-${index}`}
|
key={`tbRE-${column.elementKey}-${index}`}
|
||||||
className={isSelected ? classes.rowSelected : classes.rowUnselected}
|
className={`${column.rowClass} ${
|
||||||
|
isSelected ? classes.rowSelected : classes.rowUnselected
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{renderElement}
|
{renderElement}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -285,15 +313,15 @@ const TableWrapper = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
{records && !isLoading && records.length > 0 ? (
|
{records && !isLoading && records.length > 0 ? (
|
||||||
<Table size="small" stickyHeader={stickyHeader}>
|
<Table
|
||||||
|
size="small"
|
||||||
|
stickyHeader={stickyHeader}
|
||||||
|
className={classes.allTableSettings}
|
||||||
|
>
|
||||||
<TableHead className={classes.minTableHeader}>
|
<TableHead className={classes.minTableHeader}>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{onSelect && selectedItems && (
|
{onSelect && selectedItems && (
|
||||||
<TableCell
|
<TableCell align="center" className={classes.checkBoxHeader}>
|
||||||
padding="checkbox"
|
|
||||||
align="center"
|
|
||||||
className={classes.checkBoxHeader}
|
|
||||||
>
|
|
||||||
Select
|
Select
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
@@ -324,17 +352,13 @@ const TableWrapper = ({
|
|||||||
key={`tb-${entityName}-${index.toString()}`}
|
key={`tb-${entityName}-${index.toString()}`}
|
||||||
className={`${findView ? classes.rowClickable : ""} ${
|
className={`${findView ? classes.rowClickable : ""} ${
|
||||||
classes.rowElement
|
classes.rowElement
|
||||||
}`}
|
} rowElementRaw`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
clickAction(record);
|
clickAction(record);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{onSelect && selectedItems && (
|
{onSelect && selectedItems && (
|
||||||
<TableCell
|
<TableCell align="center" className={classes.checkBoxRow}>
|
||||||
padding="checkbox"
|
|
||||||
align="center"
|
|
||||||
className={classes.checkBoxRow}
|
|
||||||
>
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
value={isString(record) ? record : record[idField]}
|
value={isString(record) ? record : record[idField]}
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import Grid from "@material-ui/core/Grid";
|
|||||||
import { IElementValue, KVField } from "./types";
|
import { IElementValue, KVField } from "./types";
|
||||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
|
||||||
import CSVMultiSelector from "../Common/FormComponents/CSVMultiSelector/CSVMultiSelector";
|
import CSVMultiSelector from "../Common/FormComponents/CSVMultiSelector/CSVMultiSelector";
|
||||||
import CommentBoxWrapper from "../Common/FormComponents/CommentBoxWrapper/CommentBoxWrapper";
|
import CommentBoxWrapper from "../Common/FormComponents/CommentBoxWrapper/CommentBoxWrapper";
|
||||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||||
@@ -79,6 +78,7 @@ const ConfTargetGeneric = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
setValueHolder(values);
|
setValueHolder(values);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [fields, defaultVals]);
|
}, [fields, defaultVals]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import React, { useState } from "react";
|
|||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import { TextField } from "@material-ui/core";
|
import { TextField } from "@material-ui/core";
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
@@ -63,7 +62,6 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
|||||||
configuration_id: "",
|
configuration_id: "",
|
||||||
configuration_label: "",
|
configuration_label: "",
|
||||||
});
|
});
|
||||||
const [error, setError] = useState("");
|
|
||||||
const [filter, setFilter] = useState("");
|
const [filter, setFilter] = useState("");
|
||||||
|
|
||||||
const tableActions = [
|
const tableActions = [
|
||||||
@@ -103,7 +101,6 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
|||||||
<PageHeader label="Configurations List" />
|
<PageHeader label="Configurations List" />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12} className={classes.container}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
{error !== "" && <Grid container>{error}</Grid>}
|
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Filter"
|
placeholder="Filter"
|
||||||
|
|||||||
@@ -94,10 +94,9 @@ const panels = {
|
|||||||
|
|
||||||
const WebhookPanel = ({ match, classes }: IWebhookPanel) => {
|
const WebhookPanel = ({ match, classes }: IWebhookPanel) => {
|
||||||
const [addWebhookOpen, setAddWebhookOpen] = useState<boolean>(false);
|
const [addWebhookOpen, setAddWebhookOpen] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string>("");
|
|
||||||
const [filter, setFilter] = useState<string>("");
|
const [filter, setFilter] = useState<string>("");
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const [webhooks, setWebhooks] = useState<IWebhook[]>([]);
|
// const [webhooks, setWebhooks] = useState<IWebhook[]>([]);
|
||||||
|
|
||||||
const pathIn = get(match, "path", "");
|
const pathIn = get(match, "path", "");
|
||||||
const panelToDisplay = pathIn.split("/");
|
const panelToDisplay = pathIn.split("/");
|
||||||
@@ -107,6 +106,8 @@ const WebhookPanel = ({ match, classes }: IWebhookPanel) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const webhooks: IWebhook[] = [];
|
||||||
|
|
||||||
const filteredRecords: IWebhook[] = webhooks.filter((elementItem) =>
|
const filteredRecords: IWebhook[] = webhooks.filter((elementItem) =>
|
||||||
elementItem.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())
|
elementItem.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())
|
||||||
);
|
);
|
||||||
@@ -137,7 +138,6 @@ const WebhookPanel = ({ match, classes }: IWebhookPanel) => {
|
|||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
{error !== "" && <Grid container>{error}</Grid>}
|
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Filter"
|
placeholder="Filter"
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { FormControlLabel, Switch } from "@material-ui/core";
|
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { FormControlLabel, Switch } from "@material-ui/core";
|
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||||
@@ -201,6 +200,8 @@ const ConfPostgres = ({ onChange, classes }: IConfPostgresProps) => {
|
|||||||
setUser(kv.get("user") ? kv.get("user") + "" : "");
|
setUser(kv.get("user") ? kv.get("user") + "" : "");
|
||||||
setPassword(kv.get("password") ? kv.get("password") + "" : "");
|
setPassword(kv.get("password") ? kv.get("password") + "" : "");
|
||||||
setSslMode(kv.get("sslmode") ? kv.get("sslmode") + "" : " ");
|
setSslMode(kv.get("sslmode") ? kv.get("sslmode") + "" : " ");
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [useConnectionString]);
|
}, [useConnectionString]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -16,27 +16,12 @@
|
|||||||
|
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
createStyles,
|
|
||||||
StyledProps,
|
|
||||||
Theme,
|
|
||||||
withStyles,
|
|
||||||
} from "@material-ui/core/styles";
|
|
||||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||||
import Drawer from "@material-ui/core/Drawer";
|
import Drawer from "@material-ui/core/Drawer";
|
||||||
import Box from "@material-ui/core/Box";
|
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import Container from "@material-ui/core/Container";
|
import Container from "@material-ui/core/Container";
|
||||||
import Link from "@material-ui/core/Link";
|
|
||||||
import history from "../../history";
|
import history from "../../history";
|
||||||
import {
|
import { Redirect, Route, Router, Switch } from "react-router-dom";
|
||||||
Redirect,
|
|
||||||
Route,
|
|
||||||
RouteComponentProps,
|
|
||||||
Router,
|
|
||||||
Switch,
|
|
||||||
withRouter,
|
|
||||||
} from "react-router-dom";
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { AppState } from "../../store";
|
import { AppState } from "../../store";
|
||||||
import {
|
import {
|
||||||
@@ -44,7 +29,6 @@ import {
|
|||||||
serverNeedsRestart,
|
serverNeedsRestart,
|
||||||
setMenuOpen,
|
setMenuOpen,
|
||||||
} from "../../actions";
|
} from "../../actions";
|
||||||
import { ThemedComponentProps } from "@material-ui/core/styles/withTheme";
|
|
||||||
import Buckets from "./Buckets/Buckets";
|
import Buckets from "./Buckets/Buckets";
|
||||||
import Policies from "./Policies/Policies";
|
import Policies from "./Policies/Policies";
|
||||||
import Permissions from "./Permissions/Permissions";
|
import Permissions from "./Permissions/Permissions";
|
||||||
@@ -58,8 +42,6 @@ import ListNotificationEndpoints from "./NotificationEndopoints/ListNotification
|
|||||||
import ConfigurationsList from "./Configurations/ConfigurationPanels/ConfigurationsList";
|
import ConfigurationsList from "./Configurations/ConfigurationPanels/ConfigurationsList";
|
||||||
import { Button, LinearProgress } from "@material-ui/core";
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
|
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
|
||||||
import Trace from "./Trace/Trace";
|
|
||||||
import Logs from "./Logs/Logs";
|
|
||||||
import Heal from "./Heal/Heal";
|
import Heal from "./Heal/Heal";
|
||||||
import Watch from "./Watch/Watch";
|
import Watch from "./Watch/Watch";
|
||||||
import ListTenants from "./Tenants/ListTenants/ListTenants";
|
import ListTenants from "./Tenants/ListTenants/ListTenants";
|
||||||
@@ -147,6 +129,8 @@ const styles = (theme: Theme) =>
|
|||||||
container: {
|
container: {
|
||||||
paddingBottom: theme.spacing(4),
|
paddingBottom: theme.spacing(4),
|
||||||
margin: 0,
|
margin: 0,
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: "initial",
|
||||||
},
|
},
|
||||||
paper: {
|
paper: {
|
||||||
padding: theme.spacing(2),
|
padding: theme.spacing(2),
|
||||||
@@ -248,7 +232,11 @@ const Console = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: ListObjects,
|
component: ListObjects,
|
||||||
path: "/object-browser/:bucket?",
|
path: "/object-browser/:bucket",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: ListObjects,
|
||||||
|
path: "/object-browser/:bucket/*",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: Watch,
|
component: Watch,
|
||||||
@@ -266,14 +254,6 @@ const Console = ({
|
|||||||
component: Policies,
|
component: Policies,
|
||||||
path: "/policies",
|
path: "/policies",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
component: Trace,
|
|
||||||
path: "/trace",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
component: Logs,
|
|
||||||
path: "/logs",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
component: Heal,
|
component: Heal,
|
||||||
path: "/heal",
|
path: "/heal",
|
||||||
@@ -319,7 +299,7 @@ const Console = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{session.status == "ok" ? (
|
{session.status === "ok" ? (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Drawer
|
<Drawer
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ import clsx from "clsx";
|
|||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import NetworkCheckIcon from "@material-ui/icons/NetworkCheck";
|
|
||||||
import PieChartIcon from "@material-ui/icons/PieChart";
|
|
||||||
import ViewHeadlineIcon from "@material-ui/icons/ViewHeadline";
|
|
||||||
import { Usage } from "./types";
|
import { Usage } from "./types";
|
||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
import { niceBytes } from "../../../common/utils";
|
import { niceBytes } from "../../../common/utils";
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import api from "../../../common/api";
|
|||||||
import UsersSelectors from "./UsersSelectors";
|
import UsersSelectors from "./UsersSelectors";
|
||||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
|
||||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||||
|
|
||||||
interface IGroupProps {
|
interface IGroupProps {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
|||||||
import { LinearProgress } from "@material-ui/core";
|
import { LinearProgress } from "@material-ui/core";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Title from "../../../common/Title";
|
|
||||||
import { UsersList } from "../Users/types";
|
import { UsersList } from "../Users/types";
|
||||||
import { usersSort } from "../../../utils/sortFunctions";
|
import { usersSort } from "../../../utils/sortFunctions";
|
||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
@@ -28,10 +27,7 @@ import TextField from "@material-ui/core/TextField";
|
|||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
import {
|
import { actionsTray } from "../Common/FormComponents/common/styleLibrary";
|
||||||
actionsTray,
|
|
||||||
predefinedList,
|
|
||||||
} from "../Common/FormComponents/common/styleLibrary";
|
|
||||||
|
|
||||||
interface IGroupsProps {
|
interface IGroupsProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
import { HorizontalBar } from "react-chartjs-2";
|
import { HorizontalBar } from "react-chartjs-2";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import { Button, Grid, TextField, InputBase } from "@material-ui/core";
|
||||||
Button,
|
|
||||||
Grid,
|
|
||||||
Typography,
|
|
||||||
TextField,
|
|
||||||
Checkbox,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { wsProtocol } from "../../../utils/wsUtils";
|
import { wsProtocol } from "../../../utils/wsUtils";
|
||||||
@@ -15,8 +9,13 @@ import { FormControl, MenuItem, Select } from "@material-ui/core";
|
|||||||
import { BucketList, Bucket } from "../Watch/types";
|
import { BucketList, Bucket } from "../Watch/types";
|
||||||
import { HealStatus, colorH } from "./types";
|
import { HealStatus, colorH } from "./types";
|
||||||
import { niceBytes } from "../../../common/utils";
|
import { niceBytes } from "../../../common/utils";
|
||||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
import {
|
||||||
|
actionsTray,
|
||||||
|
containerForHeader,
|
||||||
|
searchField,
|
||||||
|
} from "../Common/FormComponents/common/styleLibrary";
|
||||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
import CheckboxWrapper from "../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -35,31 +34,27 @@ const styles = (theme: Theme) =>
|
|||||||
borderBottom: "1px solid #dedede",
|
borderBottom: "1px solid #dedede",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actionsTray: {
|
graphContainer: {
|
||||||
textAlign: "right",
|
backgroundColor: "#fff",
|
||||||
"& button": {
|
border: "#EAEDEE 1px solid",
|
||||||
marginLeft: 10,
|
borderRadius: 3,
|
||||||
},
|
padding: "19px 38px",
|
||||||
},
|
},
|
||||||
inputField: {
|
scanInfo: {
|
||||||
background: "#FFFFFF",
|
marginTop: 20,
|
||||||
padding: 12,
|
display: "flex",
|
||||||
borderRadius: 5,
|
flexDirection: "row",
|
||||||
marginLeft: 10,
|
justifyContent: "space-between",
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
|
||||||
},
|
},
|
||||||
fieldContainer: {
|
scanData: {
|
||||||
background: "#FFFFFF",
|
fontSize: 13,
|
||||||
padding: 0,
|
|
||||||
borderRadius: 5,
|
|
||||||
marginLeft: 10,
|
|
||||||
textAlign: "left",
|
|
||||||
minWidth: "206",
|
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
|
||||||
},
|
},
|
||||||
lastElementWPadding: {
|
inlineCheckboxes: {
|
||||||
paddingRight: "78",
|
display: "flex",
|
||||||
|
justifyContent: "flex-start",
|
||||||
},
|
},
|
||||||
|
...actionsTray,
|
||||||
|
...searchField,
|
||||||
...containerForHeader(theme.spacing(4)),
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -67,6 +62,26 @@ interface IHeal {
|
|||||||
classes: any;
|
classes: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SelectStyled = withStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
root: {
|
||||||
|
width: 450,
|
||||||
|
lineHeight: 1,
|
||||||
|
marginRight: 15,
|
||||||
|
"label + &": {
|
||||||
|
marginTop: theme.spacing(3),
|
||||||
|
},
|
||||||
|
"& .MuiSelect-select:focus": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
fontSize: 13,
|
||||||
|
lineHeight: 15,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)(InputBase);
|
||||||
|
|
||||||
const Heal = ({ classes }: IHeal) => {
|
const Heal = ({ classes }: IHeal) => {
|
||||||
const [start, setStart] = useState(false);
|
const [start, setStart] = useState(false);
|
||||||
const [bucketName, setBucketName] = useState("Select Bucket");
|
const [bucketName, setBucketName] = useState("Select Bucket");
|
||||||
@@ -171,7 +186,7 @@ const Heal = ({ classes }: IHeal) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [start]);
|
}, [start, bucketName, forceStart, forceStop, prefix, recursive]);
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
labels: ["Green", "Yellow", "Red", "Grey"],
|
labels: ["Green", "Yellow", "Red", "Grey"],
|
||||||
@@ -210,8 +225,8 @@ const Heal = ({ classes }: IHeal) => {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setBucketName(e.target.value as string);
|
setBucketName(e.target.value as string);
|
||||||
}}
|
}}
|
||||||
className={classes.fieldContainer}
|
className={classes.searchField}
|
||||||
disabled={false}
|
input={<SelectStyled />}
|
||||||
>
|
>
|
||||||
<MenuItem value="" key={`select-bucket-name-default`}>
|
<MenuItem value="" key={`select-bucket-name-default`}>
|
||||||
Select Bucket
|
Select Bucket
|
||||||
@@ -228,7 +243,7 @@ const Heal = ({ classes }: IHeal) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Prefix"
|
placeholder="Prefix"
|
||||||
className={classes.inputField}
|
className={classes.searchField}
|
||||||
id="prefix-resource"
|
id="prefix-resource"
|
||||||
label=""
|
label=""
|
||||||
disabled={false}
|
disabled={false}
|
||||||
@@ -248,76 +263,75 @@ const Heal = ({ classes }: IHeal) => {
|
|||||||
>
|
>
|
||||||
Start
|
Start
|
||||||
</Button>
|
</Button>
|
||||||
<Grid item xs={12}>
|
</Grid>
|
||||||
<span>{"Recursive"}</span>
|
<Grid item xs={12} className={classes.inlineCheckboxes}>
|
||||||
<Checkbox
|
<CheckboxWrapper
|
||||||
name="recursive"
|
name="recursive"
|
||||||
id="recursive"
|
id="recursive"
|
||||||
value="recursive"
|
value="recursive"
|
||||||
color="primary"
|
checked={recursive}
|
||||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
onChange={(e) => {
|
||||||
checked={recursive}
|
setRecursive(e.target.checked);
|
||||||
onChange={(e) => {
|
}}
|
||||||
setRecursive(e.target.checked);
|
disabled={false}
|
||||||
}}
|
label="Recursive"
|
||||||
disabled={false}
|
/>
|
||||||
/>
|
<CheckboxWrapper
|
||||||
<span>{"Force Start"}</span>
|
name="forceStart"
|
||||||
<Checkbox
|
id="forceStart"
|
||||||
name="recursive"
|
value="forceStart"
|
||||||
id="recursive"
|
checked={forceStart}
|
||||||
value="recursive"
|
onChange={(e) => {
|
||||||
color="primary"
|
setForceStart(e.target.checked);
|
||||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
}}
|
||||||
checked={forceStart}
|
disabled={false}
|
||||||
onChange={(e) => {
|
label="Force Start"
|
||||||
setForceStart(e.target.checked);
|
/>
|
||||||
}}
|
<CheckboxWrapper
|
||||||
disabled={false}
|
name="forceStop"
|
||||||
/>
|
id="forceStop"
|
||||||
<span>{"Force Stop"}</span>
|
value="forceStop"
|
||||||
<Checkbox
|
checked={forceStop}
|
||||||
name="recursive"
|
onChange={(e) => {
|
||||||
id="recursive"
|
setForceStop(e.target.checked);
|
||||||
value="recursive"
|
}}
|
||||||
color="primary"
|
disabled={false}
|
||||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
label="Force Stop"
|
||||||
checked={forceStop}
|
/>
|
||||||
onChange={(e) => {
|
</Grid>
|
||||||
setForceStop(e.target.checked);
|
<Grid item xs={12}>
|
||||||
}}
|
<br />
|
||||||
disabled={false}
|
</Grid>
|
||||||
/>
|
<Grid item xs={12} className={classes.graphContainer}>
|
||||||
<span className={classes.lastElementWPadding}>{""}</span>
|
<HorizontalBar
|
||||||
|
data={data}
|
||||||
|
width={80}
|
||||||
|
height={30}
|
||||||
|
options={{
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: "Item's Health Status [%]",
|
||||||
|
fontSize: 20,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: "right",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Grid item xs={12} className={classes.scanInfo}>
|
||||||
|
<div className={classes.scanData}>
|
||||||
|
<strong>Size scanned:</strong> {hStatus.sizeScanned}
|
||||||
|
</div>
|
||||||
|
<div className={classes.scanData}>
|
||||||
|
<strong>Objects healed:</strong> {hStatus.objectsHealed} /{" "}
|
||||||
|
{hStatus.objectsScanned}
|
||||||
|
</div>
|
||||||
|
<div className={classes.scanData}>
|
||||||
|
<strong>Healing time:</strong> {hStatus.healDuration}s
|
||||||
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
</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>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -19,16 +19,11 @@ import { connect } from "react-redux";
|
|||||||
import { NavLink } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
import ListItem from "@material-ui/core/ListItem";
|
import ListItem from "@material-ui/core/ListItem";
|
||||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
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 DescriptionIcon from "@material-ui/icons/Description";
|
||||||
import FileCopyIcon from "@material-ui/icons/FileCopy";
|
|
||||||
import Collapse from "@material-ui/core/Collapse";
|
import Collapse from "@material-ui/core/Collapse";
|
||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from "@material-ui/core/ListItemText";
|
||||||
import List from "@material-ui/core/List";
|
import List from "@material-ui/core/List";
|
||||||
import { Divider, Typography, withStyles } from "@material-ui/core";
|
import { Divider, withStyles } from "@material-ui/core";
|
||||||
import { ExitToApp } from "@material-ui/icons";
|
|
||||||
import storage from "local-storage-fallback";
|
|
||||||
import { createStyles, Theme } from "@material-ui/core/styles";
|
import { createStyles, Theme } from "@material-ui/core/styles";
|
||||||
import history from "../../../history";
|
import history from "../../../history";
|
||||||
import logo from "../../../icons/minio_console_logo.svg";
|
import logo from "../../../icons/minio_console_logo.svg";
|
||||||
@@ -48,13 +43,11 @@ import {
|
|||||||
LambdaNotificationsIcon,
|
LambdaNotificationsIcon,
|
||||||
MirroringIcon,
|
MirroringIcon,
|
||||||
ServiceAccountsIcon,
|
ServiceAccountsIcon,
|
||||||
TraceIcon,
|
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
WarpIcon,
|
WarpIcon,
|
||||||
} from "../../../icons";
|
} from "../../../icons";
|
||||||
import { clearSession } from "../../../common/utils";
|
import { clearSession } from "../../../common/utils";
|
||||||
import HealIcon from "../../../icons/HealIcon";
|
import HealIcon from "../../../icons/HealIcon";
|
||||||
import ConsoleIcon from "../../../icons/ConsoleIcon";
|
|
||||||
import LicenseIcon from "../../../icons/LicenseIcon";
|
import LicenseIcon from "../../../icons/LicenseIcon";
|
||||||
import LogoutIcon from "../../../icons/LogoutIcon";
|
import LogoutIcon from "../../../icons/LogoutIcon";
|
||||||
|
|
||||||
@@ -247,14 +240,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
name: "IAM Policies",
|
name: "IAM Policies",
|
||||||
icon: <IAMPoliciesIcon />,
|
icon: <IAMPoliciesIcon />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
group: "Tools",
|
|
||||||
type: "item",
|
|
||||||
component: NavLink,
|
|
||||||
to: "/logs",
|
|
||||||
name: "Logs",
|
|
||||||
icon: <ConsoleIcon />,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
group: "Tools",
|
group: "Tools",
|
||||||
type: "item",
|
type: "item",
|
||||||
@@ -263,14 +248,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
name: "Watch",
|
name: "Watch",
|
||||||
icon: <WatchIcon />,
|
icon: <WatchIcon />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
group: "Tools",
|
|
||||||
type: "item",
|
|
||||||
component: NavLink,
|
|
||||||
to: "/trace",
|
|
||||||
name: "Trace",
|
|
||||||
icon: <TraceIcon />,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
group: "Tools",
|
group: "Tools",
|
||||||
type: "item",
|
type: "item",
|
||||||
@@ -362,7 +339,7 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
(menuItem: any) => menuItem.type !== "title"
|
(menuItem: any) => menuItem.type !== "title"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (countableElements.length == 0) {
|
if (countableElements.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,6 +406,7 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ const styles = (theme: Theme) =>
|
|||||||
height: "80px",
|
height: "80px",
|
||||||
},
|
},
|
||||||
lambdaNotif: {
|
lambdaNotif: {
|
||||||
|
backgroundColor: "#fff",
|
||||||
border: "#393939 1px solid",
|
border: "#393939 1px solid",
|
||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
width: 101,
|
width: 101,
|
||||||
@@ -289,7 +290,7 @@ const AddNotificationEndpoint = ({
|
|||||||
<div className={classes.iconContainer}>
|
<div className={classes.iconContainer}>
|
||||||
{withLogos.map((item) => {
|
{withLogos.map((item) => {
|
||||||
return (
|
return (
|
||||||
<a
|
<button
|
||||||
key={`icon-${item.targetTitle}`}
|
key={`icon-${item.targetTitle}`}
|
||||||
className={classes.lambdaNotif}
|
className={classes.lambdaNotif}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -301,7 +302,7 @@ const AddNotificationEndpoint = ({
|
|||||||
className={classes.logoButton}
|
className={classes.logoButton}
|
||||||
alt={item.targetTitle}
|
alt={item.targetTitle}
|
||||||
/>
|
/>
|
||||||
</a>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { TextField } from "@material-ui/core";
|
|||||||
import { red } from "@material-ui/core/colors";
|
import { red } from "@material-ui/core/colors";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
|
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
|
||||||
|
|||||||
@@ -15,11 +15,11 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import { connect } from "react-redux";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import AddBucket from "../Buckets/ListBuckets/AddBucket";
|
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
@@ -28,13 +28,16 @@ import { CreateIcon } from "../../../icons";
|
|||||||
import { niceBytes } from "../../../common/utils";
|
import { niceBytes } from "../../../common/utils";
|
||||||
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
|
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
|
||||||
import { Bucket, BucketList } from "../Buckets/types";
|
import { Bucket, BucketList } from "../Buckets/types";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
|
||||||
import api from "../../../common/api";
|
|
||||||
import history from "../../../history";
|
|
||||||
import {
|
import {
|
||||||
actionsTray,
|
actionsTray,
|
||||||
|
objectBrowserCommon,
|
||||||
searchField,
|
searchField,
|
||||||
} from "../Common/FormComponents/common/styleLibrary";
|
} from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import { addRoute, resetRoutesList } from "./actions";
|
||||||
|
import BrowserBreadcrumbs from "./BrowserBreadcrumbs";
|
||||||
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
|
import AddBucket from "../Buckets/ListBuckets/AddBucket";
|
||||||
|
import api from "../../../common/api";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -68,20 +71,47 @@ const styles = (theme: Theme) =>
|
|||||||
},
|
},
|
||||||
usedSpaceCol: {
|
usedSpaceCol: {
|
||||||
width: 150,
|
width: 150,
|
||||||
|
textAlign: "right",
|
||||||
},
|
},
|
||||||
subTitleLabel: {
|
subTitleLabel: {
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
},
|
},
|
||||||
|
bucketName: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
iconBucket: {
|
||||||
|
backgroundImage: "url(/images/ob_bucket_clear.svg)",
|
||||||
|
backgroundRepeat: "no-repeat",
|
||||||
|
backgroundPosition: "center center",
|
||||||
|
width: 16,
|
||||||
|
height: 40,
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
"@global": {
|
||||||
|
".rowElementRaw:hover .iconBucketElm": {
|
||||||
|
backgroundImage: "url(/images/ob_bucket_filled.svg)",
|
||||||
|
},
|
||||||
|
},
|
||||||
...actionsTray,
|
...actionsTray,
|
||||||
...searchField,
|
...searchField,
|
||||||
|
...objectBrowserCommon,
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IBrowseBucketsProps {
|
interface IBrowseBucketsProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
|
addRoute: (path: string, label: string) => any;
|
||||||
|
resetRoutesList: (doVar: boolean) => any;
|
||||||
|
match: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
const BrowseBuckets = ({
|
||||||
|
classes,
|
||||||
|
match,
|
||||||
|
addRoute,
|
||||||
|
resetRoutesList,
|
||||||
|
}: IBrowseBucketsProps) => {
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [page, setPage] = useState<number>(0);
|
const [page, setPage] = useState<number>(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||||
@@ -92,6 +122,10 @@ const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
|||||||
|
|
||||||
const offset = page * rowsPerPage;
|
const offset = page * rowsPerPage;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
resetRoutesList(true);
|
||||||
|
}, [match]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
api
|
api
|
||||||
@@ -119,21 +153,24 @@ const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
|||||||
setError(err);
|
setError(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [loading]);
|
}, [loading, offset, rowsPerPage, page]);
|
||||||
|
|
||||||
const closeAddModalAndRefresh = () => {
|
const closeAddModalAndRefresh = (refresh: boolean) => {
|
||||||
setAddScreenOpen(false);
|
setAddScreenOpen(false);
|
||||||
setLoading(false);
|
|
||||||
|
if (refresh) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredRecords = records
|
const filteredRecords = records.filter((b: Bucket) => {
|
||||||
.filter((b: Bucket) => {
|
if (filterBuckets === "") {
|
||||||
if (filterBuckets === "") {
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
return b.name.indexOf(filterBuckets) >= 0;
|
||||||
return b.name.indexOf(filterBuckets) >= 0;
|
});
|
||||||
})
|
|
||||||
.slice(offset, offset + rowsPerPage);
|
const showInPage = filteredRecords.slice(offset, offset + rowsPerPage);
|
||||||
|
|
||||||
const handleChangePage = (event: unknown, newPage: number) => {
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
setPage(newPage);
|
setPage(newPage);
|
||||||
@@ -147,6 +184,22 @@ const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
|||||||
setRowsPerPage(rPP);
|
setRowsPerPage(rPP);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleViewChange = (idElement: string) => {
|
||||||
|
const currentPath = get(match, "url", "/object-browser");
|
||||||
|
const newPath = `${currentPath}/${idElement}`;
|
||||||
|
|
||||||
|
addRoute(newPath, idElement);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderBucket = (bucketName: string) => {
|
||||||
|
return (
|
||||||
|
<div className={classes.bucketName}>
|
||||||
|
<div className={`${classes.iconBucket} iconBucketElm`} />
|
||||||
|
<span>{bucketName}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{addScreenOpen && (
|
{addScreenOpen && (
|
||||||
@@ -156,10 +209,24 @@ const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={2} className={classes.subTitleLabel}>
|
<Grid item xs={12} className={classes.obTitleSection}>
|
||||||
<Typography variant="h6">Buckets</Typography>
|
<div>
|
||||||
|
<BrowserBreadcrumbs />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<CreateIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
setAddScreenOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create Bucket
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={10} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Search Buckets"
|
placeholder="Search Buckets"
|
||||||
className={classes.searchField}
|
className={classes.searchField}
|
||||||
@@ -177,16 +244,6 @@ const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
startIcon={<CreateIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
setAddScreenOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add Bucket
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
@@ -195,19 +252,28 @@ const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
|||||||
{error !== "" && <span className={classes.errorBlock}>{error}</span>}
|
{error !== "" && <span className={classes.errorBlock}>{error}</span>}
|
||||||
<TableWrapper
|
<TableWrapper
|
||||||
itemActions={[
|
itemActions={[
|
||||||
{ type: "view", to: `/object-browser`, sendOnlyId: true },
|
{
|
||||||
|
type: "view",
|
||||||
|
sendOnlyId: true,
|
||||||
|
onClick: handleViewChange,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
columns={[
|
columns={[
|
||||||
{ label: "Name", elementKey: "name" },
|
{
|
||||||
|
label: "Name",
|
||||||
|
elementKey: "name",
|
||||||
|
renderFunction: renderBucket,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Used Space",
|
label: "Used Space",
|
||||||
elementKey: "size",
|
elementKey: "size",
|
||||||
renderFunction: niceBytes,
|
renderFunction: niceBytes,
|
||||||
globalClass: classes.usedSpaceCol,
|
globalClass: classes.usedSpaceCol,
|
||||||
|
rowClass: classes.usedSpaceCol,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
records={filteredRecords}
|
records={showInPage}
|
||||||
entityName="Buckets"
|
entityName="Buckets"
|
||||||
idField="name"
|
idField="name"
|
||||||
paginatorConfig={{
|
paginatorConfig={{
|
||||||
@@ -231,4 +297,11 @@ const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withStyles(styles)(BrowseBuckets);
|
const mapDispatchToProps = {
|
||||||
|
addRoute,
|
||||||
|
resetRoutesList,
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(null, mapDispatchToProps);
|
||||||
|
|
||||||
|
export default withRouter(connector(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 Grid from "@material-ui/core/Grid";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { withStyles } from "@material-ui/core";
|
||||||
|
import { createStyles, Theme } from "@material-ui/core/styles";
|
||||||
|
import { removeRouteLevel } from "./actions";
|
||||||
|
import { ObjectBrowserState, Route } from "./reducers";
|
||||||
|
import { objectBrowserCommon } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
interface ObjectBrowserReducer {
|
||||||
|
objectBrowser: ObjectBrowserState;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IObjectBrowser {
|
||||||
|
classes: any;
|
||||||
|
objectsList: Route[];
|
||||||
|
removeRouteLevel: (path: string) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
...objectBrowserCommon,
|
||||||
|
});
|
||||||
|
|
||||||
|
const BrowserBreadcrumbs = ({
|
||||||
|
classes,
|
||||||
|
objectsList,
|
||||||
|
removeRouteLevel,
|
||||||
|
}: IObjectBrowser) => {
|
||||||
|
const listBreadcrumbs = objectsList.map((objectItem, index) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={`breadcrumbs-${index.toString()}`}>
|
||||||
|
<Link
|
||||||
|
to={objectItem.route}
|
||||||
|
onClick={() => {
|
||||||
|
removeRouteLevel(objectItem.route);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{objectItem.label}
|
||||||
|
</Link>
|
||||||
|
{index < objectsList.length - 1 && <span> / </span>}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<div className={classes.sectionTitle}>
|
||||||
|
{objectsList && objectsList.length > 0
|
||||||
|
? objectsList.slice(-1)[0].label
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.breadcrumbs}>
|
||||||
|
{listBreadcrumbs}
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = ({ objectBrowser }: ObjectBrowserReducer) => ({
|
||||||
|
objectsList: get(objectBrowser, "routesList", []),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
removeRouteLevel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
|
export default connector(withStyles(styles)(BrowserBreadcrumbs));
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { Grid, Typography } from "@material-ui/core";
|
import { Grid } from "@material-ui/core";
|
||||||
import BrowseBuckets from "./BrowseBuckets";
|
import BrowseBuckets from "./BrowseBuckets";
|
||||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
@@ -73,7 +73,7 @@ const styles = (theme: Theme) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ObjectBrowser = ({ match, classes }: IObjectBrowserProps) => {
|
const ObjectBrowser = ({ match, classes }: IObjectBrowserProps) => {
|
||||||
const pathIn = get(match, "path", "");
|
const pathIn = get(match, "url", "");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|||||||
93
portal-ui/src/screens/Console/ObjectBrowser/actions.ts
Normal file
93
portal-ui/src/screens/Console/ObjectBrowser/actions.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// 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 OBJECT_BROWSER_ADD_ROUTE = "OBJECT_BROWSER/ADD_ROUTE";
|
||||||
|
export const OBJECT_BROWSER_RESET_ROUTES_LIST =
|
||||||
|
"OBJECT_BROWSER/RESET_ROUTES_LIST";
|
||||||
|
export const OBJECT_BROWSER_REMOVE_ROUTE_LEVEL =
|
||||||
|
"OBJECT_BROWSER/REMOVE_ROUTE_LEVEL";
|
||||||
|
export const OBJECT_BROWSER_SET_ALL_ROUTES = "OBJECT_BROWSER/SET_ALL_ROUTES";
|
||||||
|
export const OBJECT_BROWSER_CREATE_FOLDER = "OBJECT_BROWSER/CREATE_FOLDER";
|
||||||
|
|
||||||
|
interface AddRouteAction {
|
||||||
|
type: typeof OBJECT_BROWSER_ADD_ROUTE;
|
||||||
|
route: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResetRoutesList {
|
||||||
|
type: typeof OBJECT_BROWSER_RESET_ROUTES_LIST;
|
||||||
|
reset: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RemoveRouteLevel {
|
||||||
|
type: typeof OBJECT_BROWSER_REMOVE_ROUTE_LEVEL;
|
||||||
|
toRoute: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetAllRoutes {
|
||||||
|
type: typeof OBJECT_BROWSER_SET_ALL_ROUTES;
|
||||||
|
currentRoute: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateFolder {
|
||||||
|
type: typeof OBJECT_BROWSER_CREATE_FOLDER;
|
||||||
|
newRoute: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObjectBrowserActionTypes =
|
||||||
|
| AddRouteAction
|
||||||
|
| ResetRoutesList
|
||||||
|
| RemoveRouteLevel
|
||||||
|
| SetAllRoutes
|
||||||
|
| CreateFolder;
|
||||||
|
|
||||||
|
export const addRoute = (route: string, label: string) => {
|
||||||
|
return {
|
||||||
|
type: OBJECT_BROWSER_ADD_ROUTE,
|
||||||
|
route,
|
||||||
|
label,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resetRoutesList = (reset: boolean) => {
|
||||||
|
console.log("RESET");
|
||||||
|
return {
|
||||||
|
type: OBJECT_BROWSER_RESET_ROUTES_LIST,
|
||||||
|
reset,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeRouteLevel = (toRoute: string) => {
|
||||||
|
return {
|
||||||
|
type: OBJECT_BROWSER_REMOVE_ROUTE_LEVEL,
|
||||||
|
toRoute,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setAllRoutes = (currentRoute: string) => {
|
||||||
|
return {
|
||||||
|
type: OBJECT_BROWSER_SET_ALL_ROUTES,
|
||||||
|
currentRoute,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFolder = (newRoute: string) => {
|
||||||
|
return {
|
||||||
|
type: OBJECT_BROWSER_CREATE_FOLDER,
|
||||||
|
newRoute,
|
||||||
|
};
|
||||||
|
};
|
||||||
114
portal-ui/src/screens/Console/ObjectBrowser/reducers.ts
Normal file
114
portal-ui/src/screens/Console/ObjectBrowser/reducers.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// 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 history from "../../../history";
|
||||||
|
|
||||||
|
import {
|
||||||
|
OBJECT_BROWSER_ADD_ROUTE,
|
||||||
|
OBJECT_BROWSER_CREATE_FOLDER,
|
||||||
|
OBJECT_BROWSER_REMOVE_ROUTE_LEVEL,
|
||||||
|
OBJECT_BROWSER_RESET_ROUTES_LIST,
|
||||||
|
OBJECT_BROWSER_SET_ALL_ROUTES,
|
||||||
|
ObjectBrowserActionTypes,
|
||||||
|
} from "./actions";
|
||||||
|
|
||||||
|
export interface Route {
|
||||||
|
route: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObjectBrowserState {
|
||||||
|
routesList: Route[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialRoute = [{ route: "/object-browser", label: "All Buckets" }];
|
||||||
|
|
||||||
|
const initialState: ObjectBrowserState = {
|
||||||
|
routesList: initialRoute,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function objectBrowserReducer(
|
||||||
|
state = initialState,
|
||||||
|
action: ObjectBrowserActionTypes
|
||||||
|
): ObjectBrowserState {
|
||||||
|
switch (action.type) {
|
||||||
|
case OBJECT_BROWSER_ADD_ROUTE:
|
||||||
|
const newRouteList = [
|
||||||
|
...state.routesList,
|
||||||
|
{ route: action.route, label: action.label },
|
||||||
|
];
|
||||||
|
history.push(action.route);
|
||||||
|
|
||||||
|
return { ...state, routesList: newRouteList };
|
||||||
|
case OBJECT_BROWSER_RESET_ROUTES_LIST:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
routesList: [...initialRoute],
|
||||||
|
};
|
||||||
|
case OBJECT_BROWSER_REMOVE_ROUTE_LEVEL:
|
||||||
|
const indexOfTopPath =
|
||||||
|
state.routesList.findIndex(
|
||||||
|
(element) => element.route === action.toRoute
|
||||||
|
) + 1;
|
||||||
|
const newRouteLevels = state.routesList.slice(0, indexOfTopPath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
routesList: newRouteLevels,
|
||||||
|
};
|
||||||
|
case OBJECT_BROWSER_SET_ALL_ROUTES:
|
||||||
|
const splitRoutes = action.currentRoute.split("/");
|
||||||
|
const routesArray: Route[] = [];
|
||||||
|
let initRoute = initialRoute[0].route;
|
||||||
|
|
||||||
|
splitRoutes.forEach((route) => {
|
||||||
|
if (route !== "" && route !== "object-browser") {
|
||||||
|
initRoute = `${initRoute}/${route}`;
|
||||||
|
routesArray.push({ route: initRoute, label: route });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const newSetOfRoutes = [...initialRoute, ...routesArray];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
routesList: newSetOfRoutes,
|
||||||
|
};
|
||||||
|
case OBJECT_BROWSER_CREATE_FOLDER:
|
||||||
|
const newFoldersRoutes = [...state.routesList];
|
||||||
|
let lastRoute = state.routesList[state.routesList.length - 1].route;
|
||||||
|
|
||||||
|
const splitElements = action.newRoute.split("/");
|
||||||
|
|
||||||
|
splitElements.forEach((element) => {
|
||||||
|
const folderTrim = element.trim();
|
||||||
|
if (folderTrim !== "") {
|
||||||
|
lastRoute = `${lastRoute}/${folderTrim}`;
|
||||||
|
|
||||||
|
const newItem = { route: lastRoute, label: folderTrim };
|
||||||
|
newFoldersRoutes.push(newItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
history.push(lastRoute);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
routesList: newFoldersRoutes,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@ import get from "lodash/get";
|
|||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { Button } from "@material-ui/core";
|
import { Button } from "@material-ui/core";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
@@ -78,7 +77,6 @@ interface IPoliciesProps {
|
|||||||
|
|
||||||
const Policies = ({ classes }: IPoliciesProps) => {
|
const Policies = ({ classes }: IPoliciesProps) => {
|
||||||
const [records, setRecords] = useState<Policy[]>([]);
|
const [records, setRecords] = useState<Policy[]>([]);
|
||||||
const [totalRecords, setTotalRecords] = useState<number>(0);
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||||
@@ -100,7 +98,6 @@ const Policies = ({ classes }: IPoliciesProps) => {
|
|||||||
.invoke("GET", `/api/v1/policies?offset=${offset}&limit=${rowsPerPage}`)
|
.invoke("GET", `/api/v1/policies?offset=${offset}&limit=${rowsPerPage}`)
|
||||||
.then((res: PolicyList) => {
|
.then((res: PolicyList) => {
|
||||||
const policies = get(res, "policies", []);
|
const policies = get(res, "policies", []);
|
||||||
const total = get(res, "total", 0);
|
|
||||||
|
|
||||||
policies.sort((pa, pb) => {
|
policies.sort((pa, pb) => {
|
||||||
if (pa.name > pb.name) {
|
if (pa.name > pb.name) {
|
||||||
@@ -116,7 +113,6 @@ const Policies = ({ classes }: IPoliciesProps) => {
|
|||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setRecords(policies);
|
setRecords(policies);
|
||||||
setTotalRecords(total);
|
|
||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
// if we get 0 results, and page > 0 , go down 1 page
|
// if we get 0 results, and page > 0 , go down 1 page
|
||||||
@@ -137,15 +133,7 @@ const Policies = ({ classes }: IPoliciesProps) => {
|
|||||||
setError(err);
|
setError(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [
|
}, [loading, setLoading, setRecords, setError, setPage, page, rowsPerPage]);
|
||||||
loading,
|
|
||||||
setLoading,
|
|
||||||
setRecords,
|
|
||||||
setTotalRecords,
|
|
||||||
setError,
|
|
||||||
setPage,
|
|
||||||
setError,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const fetchRecords = () => {
|
const fetchRecords = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -256,6 +244,11 @@ const Policies = ({ classes }: IPoliciesProps) => {
|
|||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{error && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
{error}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TableWrapper
|
<TableWrapper
|
||||||
itemActions={tableActions}
|
itemActions={tableActions}
|
||||||
|
|||||||
@@ -14,20 +14,10 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import {
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
Button,
|
|
||||||
LinearProgress,
|
|
||||||
Paper,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import {
|
import {
|
||||||
modalBasic,
|
modalBasic,
|
||||||
@@ -35,10 +25,7 @@ import {
|
|||||||
} from "../Common/FormComponents/common/styleLibrary";
|
} from "../Common/FormComponents/common/styleLibrary";
|
||||||
import { User } from "../Users/types";
|
import { User } from "../Users/types";
|
||||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||||
import { Policy, PolicyList } from "./types";
|
|
||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
import { policySort } from "../../../utils/sortFunctions";
|
|
||||||
import { Group } from "../Groups/types";
|
|
||||||
import PolicySelectors from "./PolicySelectors";
|
import PolicySelectors from "./PolicySelectors";
|
||||||
|
|
||||||
interface ISetPolicyProps {
|
interface ISetPolicyProps {
|
||||||
@@ -132,7 +119,8 @@ const SetPolicy = ({
|
|||||||
setActualPolicy(userPolicy);
|
setActualPolicy(userPolicy);
|
||||||
setSelectedPolicy(userPolicy);
|
setSelectedPolicy(userPolicy);
|
||||||
}
|
}
|
||||||
}, [open]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [open, selectedGroup, selectedUser]);
|
||||||
|
|
||||||
const userName = get(selectedUser, "accessKey", "");
|
const userName = get(selectedUser, "accessKey", "");
|
||||||
|
|
||||||
@@ -144,6 +132,7 @@ const SetPolicy = ({
|
|||||||
modalOpen={open}
|
modalOpen={open}
|
||||||
title="Set Policies"
|
title="Set Policies"
|
||||||
>
|
>
|
||||||
|
{error !== "" && <span className={classes.error}>{error}</span>}
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Grid item xs={12} className={classes.predefinedTitle}>
|
<Grid item xs={12} className={classes.predefinedTitle}>
|
||||||
Selected {selectedGroup !== null ? "Group" : "User"}
|
Selected {selectedGroup !== null ? "Group" : "User"}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { Button, LinearProgress, Tooltip } from "@material-ui/core";
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||||
|
|||||||
@@ -100,6 +100,13 @@ const styles = (theme: Theme) =>
|
|||||||
color: "#dc1f2e",
|
color: "#dc1f2e",
|
||||||
fontSize: "0.75rem",
|
fontSize: "0.75rem",
|
||||||
},
|
},
|
||||||
|
h3Section: {
|
||||||
|
marginTop: 0,
|
||||||
|
},
|
||||||
|
descriptionText: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: "#777777",
|
||||||
|
},
|
||||||
...modalBasic,
|
...modalBasic,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -265,6 +272,8 @@ const AddTenant = ({
|
|||||||
/*Calculate Allocation*/
|
/*Calculate Allocation*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
validateClusterSize();
|
validateClusterSize();
|
||||||
|
setECParityChoices([]);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [nodes, volumeSize, sizeFactor]);
|
}, [nodes, volumeSize, sizeFactor]);
|
||||||
|
|
||||||
const validateClusterSize = () => {
|
const validateClusterSize = () => {
|
||||||
@@ -660,6 +669,7 @@ const AddTenant = ({
|
|||||||
setEncryptionValid(Object.keys(commonVal).length === 0);
|
setEncryptionValid(Object.keys(commonVal).length === 0);
|
||||||
|
|
||||||
setValidationErrors(commonVal);
|
setValidationErrors(commonVal);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
enableEncryption,
|
enableEncryption,
|
||||||
encryptionType,
|
encryptionType,
|
||||||
@@ -744,7 +754,7 @@ const AddTenant = ({
|
|||||||
},
|
},
|
||||||
resources: {
|
resources: {
|
||||||
requests: {
|
requests: {
|
||||||
memory: parseInt(getBytes(memoryNode, "Gi")),
|
memory: parseInt(getBytes(memoryNode, "GiB")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
affinity: hardCodedAffinity,
|
affinity: hardCodedAffinity,
|
||||||
@@ -920,6 +930,7 @@ const AddTenant = ({
|
|||||||
setAddError(err);
|
setAddError(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [addSending]);
|
}, [addSending]);
|
||||||
|
|
||||||
const storeCertInObject = (certName: string, certValue: string) => {
|
const storeCertInObject = (certName: string, certValue: string) => {
|
||||||
@@ -945,8 +956,10 @@ const AddTenant = ({
|
|||||||
componentRender: (
|
componentRender: (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={classes.headerElement}>
|
<div className={classes.headerElement}>
|
||||||
<h3>Name Tenant</h3>
|
<h3 className={classes.h3Section}>Name Tenant</h3>
|
||||||
<span>How would you like to name this new tenant?</span>
|
<span className={classes.descriptionText}>
|
||||||
|
How would you like to name this new tenant?
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<InputBoxWrapper
|
<InputBoxWrapper
|
||||||
@@ -981,7 +994,6 @@ const AddTenant = ({
|
|||||||
id="storage_class"
|
id="storage_class"
|
||||||
name="storage_class"
|
name="storage_class"
|
||||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||||
console.log(e.target.value as string);
|
|
||||||
setSelectedStorageClass(e.target.value as string);
|
setSelectedStorageClass(e.target.value as string);
|
||||||
}}
|
}}
|
||||||
label="Storage Class"
|
label="Storage Class"
|
||||||
@@ -992,7 +1004,7 @@ const AddTenant = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
<span>
|
<span className={classes.descriptionText}>
|
||||||
Check 'Advanced Mode' for additional configuration options, such
|
Check 'Advanced Mode' for additional configuration options, such
|
||||||
as IDP, Disk Encryption, and customized TLS/SSL Certificates.
|
as IDP, Disk Encryption, and customized TLS/SSL Certificates.
|
||||||
<br />
|
<br />
|
||||||
@@ -1028,8 +1040,10 @@ const AddTenant = ({
|
|||||||
componentRender: (
|
componentRender: (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={classes.headerElement}>
|
<div className={classes.headerElement}>
|
||||||
<h3>Configure</h3>
|
<h3 className={classes.h3Section}>Configure</h3>
|
||||||
<span>Basic configurations for tenant management</span>
|
<span className={classes.descriptionText}>
|
||||||
|
Basic configurations for tenant management
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
@@ -1111,8 +1125,8 @@ const AddTenant = ({
|
|||||||
componentRender: (
|
componentRender: (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={classes.headerElement}>
|
<div className={classes.headerElement}>
|
||||||
<h3>IDP</h3>
|
<h3 className={classes.h3Section}>IDP</h3>
|
||||||
<span>
|
<span className={classes.descriptionText}>
|
||||||
Access to the tenant can be controlled via an external Identity
|
Access to the tenant can be controlled via an external Identity
|
||||||
Manager.
|
Manager.
|
||||||
</span>
|
</span>
|
||||||
@@ -1299,7 +1313,7 @@ const AddTenant = ({
|
|||||||
componentRender: (
|
componentRender: (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={classes.headerElement}>
|
<div className={classes.headerElement}>
|
||||||
<h3>Security</h3>
|
<h3 className={classes.h3Section}>Security</h3>
|
||||||
</div>
|
</div>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<FormSwitchWrapper
|
<FormSwitchWrapper
|
||||||
@@ -1421,8 +1435,10 @@ const AddTenant = ({
|
|||||||
componentRender: (
|
componentRender: (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={classes.headerElement}>
|
<div className={classes.headerElement}>
|
||||||
<h3>Encryption</h3>
|
<h3 className={classes.h3Section}>Encryption</h3>
|
||||||
<span>How would you like to encrypt the information at rest.</span>
|
<span className={classes.descriptionText}>
|
||||||
|
How would you like to encrypt the information at rest.
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<FormSwitchWrapper
|
<FormSwitchWrapper
|
||||||
@@ -1883,8 +1899,10 @@ const AddTenant = ({
|
|||||||
componentRender: (
|
componentRender: (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={classes.headerElement}>
|
<div className={classes.headerElement}>
|
||||||
<h3>Tenant Size</h3>
|
<h3 className={classes.h3Section}>Tenant Size</h3>
|
||||||
<span>Please select the desired capacity</span>
|
<span className={classes.descriptionText}>
|
||||||
|
Please select the desired capacity
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className={classes.error}>{distribution.error}</span>
|
<span className={classes.error}>{distribution.error}</span>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
@@ -1923,7 +1941,7 @@ const AddTenant = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className={classes.sizeFactorContainer}>
|
<div className={classes.sizeFactorContainer}>
|
||||||
<SelectWrapper
|
<SelectWrapper
|
||||||
label=""
|
label={"Unit"}
|
||||||
id="size_factor"
|
id="size_factor"
|
||||||
name="size_factor"
|
name="size_factor"
|
||||||
value={sizeFactor}
|
value={sizeFactor}
|
||||||
@@ -1963,7 +1981,7 @@ const AddTenant = ({
|
|||||||
value={ecParity}
|
value={ecParity}
|
||||||
options={ecParityChoices}
|
options={ecParityChoices}
|
||||||
/>
|
/>
|
||||||
<span>
|
<span className={classes.descriptionText}>
|
||||||
Please select the desired parity. This setting will change the
|
Please select the desired parity. This setting will change the
|
||||||
max usable capacity in the cluster
|
max usable capacity in the cluster
|
||||||
</span>
|
</span>
|
||||||
@@ -2011,8 +2029,10 @@ const AddTenant = ({
|
|||||||
componentRender: (
|
componentRender: (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className={classes.headerElement}>
|
<div className={classes.headerElement}>
|
||||||
<h3>Review</h3>
|
<h3 className={classes.h3Section}>Review</h3>
|
||||||
<span>Review the details of the new tenant</span>
|
<span className={classes.descriptionText}>
|
||||||
|
Review the details of the new tenant
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{addError !== "" && (
|
{addError !== "" && (
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ const DeleteTenant = ({
|
|||||||
setDeleteError(err);
|
setDeleteError(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [deleteLoading]);
|
}, [deleteLoading]);
|
||||||
|
|
||||||
const removeRecord = () => {
|
const removeRecord = () => {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
@@ -34,7 +33,11 @@ import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
|||||||
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
|
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
|
||||||
import history from "../../../../history";
|
import history from "../../../../history";
|
||||||
import RefreshIcon from "@material-ui/icons/Refresh";
|
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||||
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
import {
|
||||||
|
actionsTray,
|
||||||
|
containerForHeader,
|
||||||
|
searchField,
|
||||||
|
} from "../../Common/FormComponents/common/styleLibrary";
|
||||||
import PageHeader from "../../Common/PageHeader/PageHeader";
|
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
interface ITenantsList {
|
interface ITenantsList {
|
||||||
@@ -71,18 +74,8 @@ const styles = (theme: Theme) =>
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actionsTray: {
|
...actionsTray,
|
||||||
textAlign: "right",
|
...searchField,
|
||||||
"& button": {
|
|
||||||
marginLeft: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
searchField: {
|
|
||||||
background: "#FFFFFF",
|
|
||||||
padding: 12,
|
|
||||||
borderRadius: 5,
|
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
|
||||||
},
|
|
||||||
...containerForHeader(theme.spacing(4)),
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -93,7 +86,6 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const [filterTenants, setFilterTenants] = useState<string>("");
|
const [filterTenants, setFilterTenants] = useState<string>("");
|
||||||
const [records, setRecords] = useState<any[]>([]);
|
const [records, setRecords] = useState<any[]>([]);
|
||||||
const [offset, setOffset] = useState<number>(0);
|
|
||||||
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||||
const [page, setPage] = useState<number>(0);
|
const [page, setPage] = useState<number>(0);
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
@@ -154,17 +146,15 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
setRowsPerPage(rPP);
|
setRowsPerPage(rPP);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openLink = (link: string) => {
|
|
||||||
window.open(link, "_blank");
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableActions = [
|
const tableActions = [
|
||||||
{ type: "view", onClick: redirectToTenantDetails },
|
{ type: "view", onClick: redirectToTenantDetails },
|
||||||
{ type: "delete", onClick: confirmDeleteTenant },
|
{ type: "delete", onClick: confirmDeleteTenant },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const globalOffset = 0;
|
||||||
|
|
||||||
const filteredRecords = records
|
const filteredRecords = records
|
||||||
.slice(offset, offset + rowsPerPage)
|
.slice(globalOffset, globalOffset + rowsPerPage)
|
||||||
.filter((b: any) => {
|
.filter((b: any) => {
|
||||||
if (filterTenants === "") {
|
if (filterTenants === "") {
|
||||||
return true;
|
return true;
|
||||||
@@ -252,17 +242,6 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12} className={classes.container}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<IconButton
|
|
||||||
color="primary"
|
|
||||||
aria-label="Refresh Tenant List"
|
|
||||||
component="span"
|
|
||||||
onClick={() => {
|
|
||||||
setIsLoading(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RefreshIcon />
|
|
||||||
</IconButton>
|
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Search Tenants"
|
placeholder="Search Tenants"
|
||||||
className={classes.searchField}
|
className={classes.searchField}
|
||||||
@@ -280,6 +259,16 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
aria-label="Refresh Tenant List"
|
||||||
|
component="span"
|
||||||
|
onClick={() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RefreshIcon />
|
||||||
|
</IconButton>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -291,6 +280,11 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
Create Tenant
|
Create Tenant
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{error !== "" && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
{error}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -3,13 +3,8 @@ import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
|||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import {
|
import { niceBytes } from "../../../../common/utils";
|
||||||
factorForDropdown,
|
|
||||||
getTotalSize,
|
|
||||||
niceBytes,
|
|
||||||
} from "../../../../common/utils";
|
|
||||||
import { Button, LinearProgress } from "@material-ui/core";
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
import { IAddZoneRequest, ITenant } from "../ListTenants/types";
|
import { IAddZoneRequest, ITenant } from "../ListTenants/types";
|
||||||
|
|||||||
@@ -15,24 +15,22 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||||
import { AppState } from "../../../store";
|
import { AppState } from "../../../../../store";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { logMessageReceived, logResetMessages } from "./actions";
|
import { logMessageReceived, logResetMessages } from "./actions";
|
||||||
import { LogMessage } from "./types";
|
import { LogMessage } from "./types";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { timeFromDate } from "../../../common/utils";
|
import { timeFromDate } from "../../../../../common/utils";
|
||||||
import { wsProtocol } from "../../../utils/wsUtils";
|
import { wsProtocol } from "../../../../../utils/wsUtils";
|
||||||
import {
|
import {
|
||||||
actionsTray,
|
actionsTray,
|
||||||
containerForHeader,
|
containerForHeader,
|
||||||
searchField,
|
searchField,
|
||||||
} from "../Common/FormComponents/common/styleLibrary";
|
} from "../../../Common/FormComponents/common/styleLibrary";
|
||||||
import { Button, Grid } from "@material-ui/core";
|
import { Grid } from "@material-ui/core";
|
||||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
import { CreateIcon } from "../../../icons";
|
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -74,6 +72,8 @@ interface ILogs {
|
|||||||
logMessageReceived: typeof logMessageReceived;
|
logMessageReceived: typeof logMessageReceived;
|
||||||
logResetMessages: typeof logResetMessages;
|
logResetMessages: typeof logResetMessages;
|
||||||
messages: LogMessage[];
|
messages: LogMessage[];
|
||||||
|
namespace: string;
|
||||||
|
tenant: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Logs = ({
|
const Logs = ({
|
||||||
@@ -81,6 +81,8 @@ const Logs = ({
|
|||||||
logMessageReceived,
|
logMessageReceived,
|
||||||
logResetMessages,
|
logResetMessages,
|
||||||
messages,
|
messages,
|
||||||
|
namespace,
|
||||||
|
tenant,
|
||||||
}: ILogs) => {
|
}: ILogs) => {
|
||||||
const [highlight, setHighlight] = useState("");
|
const [highlight, setHighlight] = useState("");
|
||||||
|
|
||||||
@@ -93,7 +95,7 @@ const Logs = ({
|
|||||||
const wsProt = wsProtocol(url.protocol);
|
const wsProt = wsProtocol(url.protocol);
|
||||||
|
|
||||||
const c = new W3CWebSocket(
|
const c = new W3CWebSocket(
|
||||||
`${wsProt}://${url.hostname}:${port}/ws/console`
|
`${wsProt}://${url.hostname}:${port}/ws/console/${namespace}/${tenant}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let interval: any | null = null;
|
let interval: any | null = null;
|
||||||
@@ -122,20 +124,7 @@ const Logs = ({
|
|||||||
console.log("closing websockets");
|
console.log("closing websockets");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [logMessageReceived]);
|
}, [logMessageReceived, logResetMessages]);
|
||||||
|
|
||||||
// replaces a character of a string with other at a given index
|
|
||||||
const replaceWeirdChar = (
|
|
||||||
origString: string,
|
|
||||||
replaceChar: string,
|
|
||||||
index: number
|
|
||||||
) => {
|
|
||||||
let firstPart = origString.substr(0, index);
|
|
||||||
let lastPart = origString.substr(index + 1);
|
|
||||||
|
|
||||||
let newString = firstPart + replaceChar + lastPart;
|
|
||||||
return newString;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderError = (logElement: LogMessage) => {
|
const renderError = (logElement: LogMessage) => {
|
||||||
let errorElems = [];
|
let errorElems = [];
|
||||||
@@ -335,38 +324,33 @@ const Logs = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<Grid item xs={12}>
|
||||||
<PageHeader label="Logs" />
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<Grid container>
|
<TextField
|
||||||
<Grid item xs={12} className={classes.container}>
|
placeholder="Highlight Line"
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
className={classes.searchField}
|
||||||
<TextField
|
id="search-resource"
|
||||||
placeholder="Highlight Line"
|
label=""
|
||||||
className={classes.searchField}
|
onChange={(val) => {
|
||||||
id="search-resource"
|
setHighlight(val.target.value);
|
||||||
label=""
|
}}
|
||||||
onChange={(val) => {
|
InputProps={{
|
||||||
setHighlight(val.target.value);
|
disableUnderline: true,
|
||||||
}}
|
startAdornment: (
|
||||||
InputProps={{
|
<InputAdornment position="start">
|
||||||
disableUnderline: true,
|
<SearchIcon />
|
||||||
startAdornment: (
|
</InputAdornment>
|
||||||
<InputAdornment position="start">
|
),
|
||||||
<SearchIcon />
|
}}
|
||||||
</InputAdornment>
|
/>
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<br />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<div className={classes.logList}>{renderLines}</div>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<div className={classes.logList}>{renderLines}</div>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,8 +31,9 @@ import AddZoneModal from "./AddZoneModal";
|
|||||||
import AddBucket from "../../Buckets/ListBuckets/AddBucket";
|
import AddBucket from "../../Buckets/ListBuckets/AddBucket";
|
||||||
import ReplicationSetup from "./ReplicationSetup";
|
import ReplicationSetup from "./ReplicationSetup";
|
||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
import { BucketInfo } from "../../Buckets/types";
|
|
||||||
import { ITenant, IZone } from "../ListTenants/types";
|
import { ITenant, IZone } from "../ListTenants/types";
|
||||||
|
import Logs from "./Logs/Logs";
|
||||||
|
import Trace from "./Trace/Trace";
|
||||||
|
|
||||||
interface ITenantDetailsProps {
|
interface ITenantDetailsProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
@@ -94,8 +95,6 @@ const styles = (theme: Theme) =>
|
|||||||
const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||||
const [capacity, setCapacity] = useState<number>(0);
|
const [capacity, setCapacity] = useState<number>(0);
|
||||||
const [externalIDP, setExternalIDP] = useState<boolean>(false);
|
|
||||||
const [externalKMS, setExternalKMS] = useState<boolean>(false);
|
|
||||||
const [zoneCount, setZoneCount] = useState<number>(0);
|
const [zoneCount, setZoneCount] = useState<number>(0);
|
||||||
const [zones, setZones] = useState<IZone[]>([]);
|
const [zones, setZones] = useState<IZone[]>([]);
|
||||||
const [instances, setInstances] = useState<number>(0);
|
const [instances, setInstances] = useState<number>(0);
|
||||||
@@ -103,7 +102,6 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
const [addZoneOpen, setAddZone] = useState<boolean>(false);
|
const [addZoneOpen, setAddZone] = useState<boolean>(false);
|
||||||
const [addBucketOpen, setAddBucketOpen] = useState<boolean>(false);
|
const [addBucketOpen, setAddBucketOpen] = useState<boolean>(false);
|
||||||
const [addReplicationOpen, setAddReplicationOpen] = useState<boolean>(false);
|
const [addReplicationOpen, setAddReplicationOpen] = useState<boolean>(false);
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
const [tenant, setTenant] = useState<ITenant | null>(null);
|
const [tenant, setTenant] = useState<ITenant | null>(null);
|
||||||
|
|
||||||
@@ -131,8 +129,6 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
const tenantName = match.params["tenantName"];
|
const tenantName = match.params["tenantName"];
|
||||||
const tenantNamespace = match.params["tenantNamespace"];
|
const tenantNamespace = match.params["tenantNamespace"];
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
api
|
api
|
||||||
.invoke(
|
.invoke(
|
||||||
"GET",
|
"GET",
|
||||||
@@ -140,7 +136,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
)
|
)
|
||||||
.then((res: ITenant) => {
|
.then((res: ITenant) => {
|
||||||
const resZones = !res.zones ? [] : res.zones;
|
const resZones = !res.zones ? [] : res.zones;
|
||||||
const total = res.volume_count * res.volume_size;
|
|
||||||
let totalInstances = 0;
|
let totalInstances = 0;
|
||||||
let totalVolumes = 0;
|
let totalVolumes = 0;
|
||||||
let count = 1;
|
let count = 1;
|
||||||
@@ -165,16 +161,15 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
|
|
||||||
setTenant(res);
|
setTenant(res);
|
||||||
setError("");
|
setError("");
|
||||||
setLoading(false);
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setError(err);
|
setError(err);
|
||||||
setLoading(false);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadInfo();
|
loadInfo();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -204,6 +199,11 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
{`Tenant > ${match.params["tenantName"]}`}
|
{`Tenant > ${match.params["tenantName"]}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{error !== "" && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
{error}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -239,6 +239,8 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
aria-label="tenant-tabs"
|
aria-label="tenant-tabs"
|
||||||
>
|
>
|
||||||
<Tab label="Zones" />
|
<Tab label="Zones" />
|
||||||
|
<Tab label="Logs" />
|
||||||
|
<Tab label="Trace" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6} className={classes.actionsTray}>
|
<Grid item xs={6} className={classes.actionsTray}>
|
||||||
@@ -257,42 +259,50 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TableWrapper
|
{selectedTab === 0 && (
|
||||||
itemActions={[
|
<TableWrapper
|
||||||
{
|
itemActions={[
|
||||||
type: "delete",
|
{
|
||||||
onClick: (element) => {
|
type: "delete",
|
||||||
console.log(element);
|
onClick: (element) => {
|
||||||
|
console.log(element);
|
||||||
|
},
|
||||||
|
sendOnlyId: true,
|
||||||
},
|
},
|
||||||
sendOnlyId: true,
|
]}
|
||||||
},
|
columns={[
|
||||||
]}
|
{ label: "Name", elementKey: "name" },
|
||||||
columns={[
|
{ label: "Capacity", elementKey: "capacity" },
|
||||||
{ label: "Name", elementKey: "name" },
|
{ label: "# of Instances", elementKey: "servers" },
|
||||||
{ label: "Capacity", elementKey: "capacity" },
|
{ label: "# of Drives", elementKey: "volumes" },
|
||||||
{ label: "# of Instances", elementKey: "servers" },
|
]}
|
||||||
{ label: "# of Drives", elementKey: "volumes" },
|
isLoading={false}
|
||||||
]}
|
records={zones}
|
||||||
isLoading={false}
|
entityName="Zones"
|
||||||
records={zones}
|
idField="name"
|
||||||
entityName="Zones"
|
paginatorConfig={{
|
||||||
idField="name"
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
paginatorConfig={{
|
colSpan: 3,
|
||||||
rowsPerPageOptions: [5, 10, 25],
|
count: zoneCount,
|
||||||
colSpan: 3,
|
rowsPerPage: 10,
|
||||||
count: zoneCount,
|
page: 0,
|
||||||
rowsPerPage: 10,
|
SelectProps: {
|
||||||
page: 0,
|
inputProps: { "aria-label": "rows per page" },
|
||||||
SelectProps: {
|
native: true,
|
||||||
inputProps: { "aria-label": "rows per page" },
|
},
|
||||||
native: true,
|
ActionsComponent: MinTablePaginationActions,
|
||||||
},
|
onChangePage: () => {},
|
||||||
ActionsComponent: MinTablePaginationActions,
|
onChangeRowsPerPage: () => {},
|
||||||
onChangePage: () => {},
|
}}
|
||||||
onChangeRowsPerPage: () => {},
|
/>
|
||||||
}}
|
)}
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{selectedTab === 1 && tenant !== null && (
|
||||||
|
<Logs namespace={tenant.namespace} tenant={tenant.name} />
|
||||||
|
)}
|
||||||
|
{selectedTab === 2 && tenant !== null && (
|
||||||
|
<Trace namespace={tenant.namespace} tenant={tenant.name} />
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,17 +16,17 @@
|
|||||||
|
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||||
import { AppState } from "../../../store";
|
import { AppState } from "../../../../../store";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { traceMessageReceived, traceResetMessages } from "./actions";
|
import { traceMessageReceived, traceResetMessages } from "./actions";
|
||||||
import { TraceMessage } from "./types";
|
import { TraceMessage } from "./types";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { niceBytes, timeFromDate } from "../../../common/utils";
|
import { niceBytes, timeFromDate } from "../../../../../common/utils";
|
||||||
import { wsProtocol } from "../../../utils/wsUtils";
|
import { wsProtocol } from "../../../../../utils/wsUtils";
|
||||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
import { containerForHeader } from "../../../Common/FormComponents/common/styleLibrary";
|
||||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
import PageHeader from "../../../Common/PageHeader/PageHeader";
|
||||||
import { Grid } from "@material-ui/core";
|
import { Grid } from "@material-ui/core";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../../../Common/TableWrapper/TableWrapper";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -45,6 +45,12 @@ const styles = (theme: Theme) =>
|
|||||||
borderBottom: "1px solid #dedede",
|
borderBottom: "1px solid #dedede",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
sizeItem: {
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
timeItem: {
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
...containerForHeader(theme.spacing(4)),
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,6 +59,8 @@ interface ITrace {
|
|||||||
traceMessageReceived: typeof traceMessageReceived;
|
traceMessageReceived: typeof traceMessageReceived;
|
||||||
traceResetMessages: typeof traceResetMessages;
|
traceResetMessages: typeof traceResetMessages;
|
||||||
messages: TraceMessage[];
|
messages: TraceMessage[];
|
||||||
|
namespace: string;
|
||||||
|
tenant: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Trace = ({
|
const Trace = ({
|
||||||
@@ -60,6 +68,8 @@ const Trace = ({
|
|||||||
traceMessageReceived,
|
traceMessageReceived,
|
||||||
traceResetMessages,
|
traceResetMessages,
|
||||||
messages,
|
messages,
|
||||||
|
namespace,
|
||||||
|
tenant,
|
||||||
}: ITrace) => {
|
}: ITrace) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
traceResetMessages();
|
traceResetMessages();
|
||||||
@@ -68,7 +78,9 @@ const Trace = ({
|
|||||||
const port = isDev ? "9090" : url.port;
|
const port = isDev ? "9090" : url.port;
|
||||||
|
|
||||||
const wsProt = wsProtocol(url.protocol);
|
const wsProt = wsProtocol(url.protocol);
|
||||||
const c = new W3CWebSocket(`${wsProt}://${url.hostname}:${port}/ws/trace`);
|
const c = new W3CWebSocket(
|
||||||
|
`${wsProt}://${url.hostname}:${port}/ws/trace/${namespace}/${tenant}`
|
||||||
|
);
|
||||||
|
|
||||||
let interval: any | null = null;
|
let interval: any | null = null;
|
||||||
if (c !== null) {
|
if (c !== null) {
|
||||||
@@ -95,60 +107,62 @@ const Trace = ({
|
|||||||
console.log("closing websockets");
|
console.log("closing websockets");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [traceMessageReceived]);
|
}, [traceMessageReceived, traceResetMessages]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<Grid item xs={12}>
|
||||||
<PageHeader label={"Trace"} />
|
<TableWrapper
|
||||||
<Grid container>
|
itemActions={[]}
|
||||||
<Grid item xs={12} className={classes.container}>
|
columns={[
|
||||||
<TableWrapper
|
{
|
||||||
itemActions={[]}
|
label: "Time",
|
||||||
columns={[
|
elementKey: "time",
|
||||||
{
|
renderFunction: (time: Date) => {
|
||||||
label: "Time",
|
const timeParse = new Date(time);
|
||||||
elementKey: "time",
|
return timeFromDate(timeParse);
|
||||||
renderFunction: (time: Date) => {
|
},
|
||||||
const timeParse = new Date(time);
|
globalClass: classes.timeItem,
|
||||||
return timeFromDate(timeParse);
|
},
|
||||||
},
|
{ label: "Name", elementKey: "api" },
|
||||||
},
|
{
|
||||||
{ label: "Name", elementKey: "api" },
|
label: "Status",
|
||||||
{
|
elementKey: "",
|
||||||
label: "Status",
|
renderFunction: (fullElement: TraceMessage) =>
|
||||||
elementKey: "",
|
`${fullElement.statusCode} ${fullElement.statusMsg}`,
|
||||||
renderFunction: (fullElement: TraceMessage) =>
|
renderFullObject: true,
|
||||||
`${fullElement.statusCode} ${fullElement.statusMsg}`,
|
},
|
||||||
renderFullObject: true,
|
{
|
||||||
},
|
label: "Location",
|
||||||
{
|
elementKey: "configuration_id",
|
||||||
label: "Location",
|
renderFunction: (fullElement: TraceMessage) =>
|
||||||
elementKey: "configuration_id",
|
`${fullElement.host} ${fullElement.client}`,
|
||||||
renderFunction: (fullElement: TraceMessage) =>
|
renderFullObject: true,
|
||||||
`${fullElement.host} ${fullElement.client}`,
|
},
|
||||||
renderFullObject: true,
|
{
|
||||||
},
|
label: "Load Time",
|
||||||
{ label: "Load Time", elementKey: "callStats.duration" },
|
elementKey: "callStats.duration",
|
||||||
{
|
globalClass: classes.timeItem,
|
||||||
label: "Upload",
|
},
|
||||||
elementKey: "callStats.rx",
|
{
|
||||||
renderFunction: niceBytes,
|
label: "Upload",
|
||||||
},
|
elementKey: "callStats.rx",
|
||||||
{
|
renderFunction: niceBytes,
|
||||||
label: "Download",
|
globalClass: classes.sizeItem,
|
||||||
elementKey: "callStats.tx",
|
},
|
||||||
renderFunction: niceBytes,
|
{
|
||||||
},
|
label: "Download",
|
||||||
]}
|
elementKey: "callStats.tx",
|
||||||
isLoading={false}
|
renderFunction: niceBytes,
|
||||||
records={messages}
|
globalClass: classes.sizeItem,
|
||||||
entityName="Traces"
|
},
|
||||||
idField="api"
|
]}
|
||||||
customEmptyMessage="There are no traced Elements yet"
|
isLoading={false}
|
||||||
/>
|
records={messages}
|
||||||
</Grid>
|
entityName="Traces"
|
||||||
</Grid>
|
idField="api"
|
||||||
</React.Fragment>
|
customEmptyMessage="There are no traced Elements yet"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,7 +24,6 @@ import {
|
|||||||
} from "../Common/FormComponents/common/styleLibrary";
|
} from "../Common/FormComponents/common/styleLibrary";
|
||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
import GroupsSelectors from "./GroupsSelectors";
|
import GroupsSelectors from "./GroupsSelectors";
|
||||||
import Title from "../../../common/Title";
|
|
||||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||||
|
|
||||||
interface IAddToGroup {
|
interface IAddToGroup {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import api from "../../../common/api";
|
|||||||
import GroupsSelectors from "./GroupsSelectors";
|
import GroupsSelectors from "./GroupsSelectors";
|
||||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
|
||||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
|||||||
import { LinearProgress } from "@material-ui/core";
|
import { LinearProgress } from "@material-ui/core";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Title from "../../../common/Title";
|
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
|
|||||||
@@ -17,13 +17,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
import {
|
import { Button, Grid, TextField, InputAdornment } from "@material-ui/core";
|
||||||
Button,
|
|
||||||
Grid,
|
|
||||||
Typography,
|
|
||||||
TextField,
|
|
||||||
InputAdornment,
|
|
||||||
} from "@material-ui/core";
|
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
import GroupIcon from "@material-ui/icons/Group";
|
import GroupIcon from "@material-ui/icons/Group";
|
||||||
import { User, UsersList } from "./types";
|
import { User, UsersList } from "./types";
|
||||||
@@ -34,7 +28,6 @@ import AddUser from "./AddUser";
|
|||||||
import DeleteUser from "./DeleteUser";
|
import DeleteUser from "./DeleteUser";
|
||||||
import AddToGroup from "./AddToGroup";
|
import AddToGroup from "./AddToGroup";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
import DescriptionIcon from "@material-ui/icons/Description";
|
|
||||||
import SetPolicy from "../Policies/SetPolicy";
|
import SetPolicy from "../Policies/SetPolicy";
|
||||||
import {
|
import {
|
||||||
actionsTray,
|
actionsTray,
|
||||||
@@ -183,7 +176,6 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
|||||||
const { classes } = this.props;
|
const { classes } = this.props;
|
||||||
const {
|
const {
|
||||||
records,
|
records,
|
||||||
totalRecords,
|
|
||||||
addScreenOpen,
|
addScreenOpen,
|
||||||
loading,
|
loading,
|
||||||
page,
|
page,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Button, Grid, Typography, TextField } from "@material-ui/core";
|
import { Button, Grid, TextField, InputBase } from "@material-ui/core";
|
||||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||||
import { AppState } from "../../../store";
|
import { AppState } from "../../../store";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -25,8 +25,13 @@ import { niceBytes, timeFromDate } from "../../../common/utils";
|
|||||||
import { wsProtocol } from "../../../utils/wsUtils";
|
import { wsProtocol } from "../../../utils/wsUtils";
|
||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
import { FormControl, MenuItem, Select } from "@material-ui/core";
|
import { FormControl, MenuItem, Select } from "@material-ui/core";
|
||||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
import {
|
||||||
|
actionsTray,
|
||||||
|
containerForHeader,
|
||||||
|
searchField,
|
||||||
|
} from "../Common/FormComponents/common/styleLibrary";
|
||||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -45,31 +50,34 @@ const styles = (theme: Theme) =>
|
|||||||
borderBottom: "1px solid #dedede",
|
borderBottom: "1px solid #dedede",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actionsTray: {
|
searchPrefix: {
|
||||||
textAlign: "right",
|
flexGrow: 1,
|
||||||
"& button": {
|
marginLeft: 15,
|
||||||
marginLeft: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
inputField: {
|
|
||||||
background: "#FFFFFF",
|
|
||||||
padding: 12,
|
|
||||||
borderRadius: 5,
|
|
||||||
marginLeft: 10,
|
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
|
||||||
},
|
|
||||||
fieldContainer: {
|
|
||||||
background: "#FFFFFF",
|
|
||||||
padding: 0,
|
|
||||||
borderRadius: 5,
|
|
||||||
marginLeft: 10,
|
|
||||||
textAlign: "left",
|
|
||||||
minWidth: "206px",
|
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
|
||||||
},
|
},
|
||||||
|
...actionsTray,
|
||||||
|
...searchField,
|
||||||
...containerForHeader(theme.spacing(4)),
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const SelectStyled = withStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
root: {
|
||||||
|
width: 450,
|
||||||
|
lineHeight: 1,
|
||||||
|
"label + &": {
|
||||||
|
marginTop: theme.spacing(3),
|
||||||
|
},
|
||||||
|
"& .MuiSelect-select:focus": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
fontSize: 13,
|
||||||
|
lineHeight: 15,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)(InputBase);
|
||||||
|
|
||||||
interface IWatch {
|
interface IWatch {
|
||||||
classes: any;
|
classes: any;
|
||||||
watchMessageReceived: typeof watchMessageReceived;
|
watchMessageReceived: typeof watchMessageReceived;
|
||||||
@@ -150,12 +158,21 @@ const Watch = ({
|
|||||||
// reset start status
|
// reset start status
|
||||||
setStart(false);
|
setStart(false);
|
||||||
}
|
}
|
||||||
}, [watchMessageReceived, start]);
|
}, [
|
||||||
|
watchMessageReceived,
|
||||||
|
start,
|
||||||
|
bucketList,
|
||||||
|
bucketName,
|
||||||
|
prefix,
|
||||||
|
suffix,
|
||||||
|
watchResetMessages,
|
||||||
|
]);
|
||||||
|
|
||||||
const bucketNames = bucketList.map((bucketName) => ({
|
const bucketNames = bucketList.map((bucketName) => ({
|
||||||
label: bucketName.name,
|
label: bucketName.name,
|
||||||
value: bucketName.name,
|
value: bucketName.name,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<PageHeader label="Watch" />
|
<PageHeader label="Watch" />
|
||||||
@@ -170,8 +187,9 @@ const Watch = ({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setBucketName(e.target.value as string);
|
setBucketName(e.target.value as string);
|
||||||
}}
|
}}
|
||||||
className={classes.fieldContainer}
|
className={classes.searchField}
|
||||||
disabled={start}
|
disabled={start}
|
||||||
|
input={<SelectStyled />}
|
||||||
>
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
value={bucketName}
|
value={bucketName}
|
||||||
@@ -192,7 +210,7 @@ const Watch = ({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Prefix"
|
placeholder="Prefix"
|
||||||
className={classes.inputField}
|
className={`${classes.searchField} ${classes.searchPrefix}`}
|
||||||
id="prefix-resource"
|
id="prefix-resource"
|
||||||
label=""
|
label=""
|
||||||
disabled={start}
|
disabled={start}
|
||||||
@@ -205,7 +223,7 @@ const Watch = ({
|
|||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Suffix"
|
placeholder="Suffix"
|
||||||
className={classes.inputField}
|
className={`${classes.searchField} ${classes.searchPrefix}`}
|
||||||
id="suffix-resource"
|
id="suffix-resource"
|
||||||
label=""
|
label=""
|
||||||
disabled={start}
|
disabled={start}
|
||||||
@@ -229,18 +247,27 @@ const Watch = ({
|
|||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
<div className={classes.watchList}>
|
<TableWrapper
|
||||||
<ul>
|
columns={[
|
||||||
{messages.map((m) => {
|
{
|
||||||
return (
|
label: "Time",
|
||||||
<li key={m.key}>
|
elementKey: "Time",
|
||||||
{timeFromDate(m.Time)} - {niceBytes(m.Size + "")} - {m.Type}{" "}
|
renderFunction: timeFromDate,
|
||||||
- {m.Path}
|
},
|
||||||
</li>
|
{
|
||||||
);
|
label: "Size",
|
||||||
})}
|
elementKey: "Size",
|
||||||
</ul>
|
renderFunction: niceBytes,
|
||||||
</div>
|
},
|
||||||
|
{ label: "Type", elementKey: "Type" },
|
||||||
|
{ label: "Path", elementKey: "Path" },
|
||||||
|
]}
|
||||||
|
records={messages}
|
||||||
|
entityName={"Watch"}
|
||||||
|
customEmptyMessage={"No Changes at this time"}
|
||||||
|
idField={"watch_table"}
|
||||||
|
isLoading={false}
|
||||||
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { FC, useEffect } from "react";
|
import React, { FC, useEffect } from "react"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
import { RouteComponentProps } from "react-router";
|
import { RouteComponentProps } from "react-router";
|
||||||
import storage from "local-storage-fallback";
|
import storage from "local-storage-fallback";
|
||||||
import api from "../../common/api";
|
import api from "../../common/api";
|
||||||
@@ -36,6 +36,7 @@ const LoginCallback: FC<RouteComponentProps> = ({ location }) => {
|
|||||||
.catch((res: any) => {
|
.catch((res: any) => {
|
||||||
window.location.href = "/login";
|
window.location.href = "/login";
|
||||||
});
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,22 +16,32 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import request from "superagent";
|
import request from "superagent";
|
||||||
import storage from "local-storage-fallback";
|
|
||||||
import { connect, ConnectedProps } from "react-redux";
|
import { connect, ConnectedProps } from "react-redux";
|
||||||
import ErrorIcon from "@material-ui/icons/Error";
|
import ErrorIcon from "@material-ui/icons/Error";
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { CircularProgress, Paper } from "@material-ui/core";
|
import {
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
CircularProgress,
|
||||||
|
LinearProgress,
|
||||||
|
Paper,
|
||||||
|
TextFieldProps,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import {
|
||||||
|
createStyles,
|
||||||
|
makeStyles,
|
||||||
|
Theme,
|
||||||
|
withStyles,
|
||||||
|
} from "@material-ui/core/styles";
|
||||||
import { SystemState } from "../../types";
|
import { SystemState } from "../../types";
|
||||||
import { userLoggedIn } from "../../actions";
|
import { userLoggedIn } from "../../actions";
|
||||||
import api from "../../common/api";
|
import api from "../../common/api";
|
||||||
import { ILoginDetails, loginStrategyType } from "./types";
|
import { ILoginDetails, loginStrategyType } from "./types";
|
||||||
import { setSession } from "../../common/utils";
|
import { setSession } from "../../common/utils";
|
||||||
import history from "../../history";
|
import history from "../../history";
|
||||||
import { Error } from "@material-ui/icons";
|
import { isBoolean } from "util";
|
||||||
|
import { OutlinedInputProps } from "@material-ui/core/OutlinedInput";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -123,8 +133,33 @@ const styles = (theme: Theme) =>
|
|||||||
jwtInput: {
|
jwtInput: {
|
||||||
marginTop: 45,
|
marginTop: 45,
|
||||||
},
|
},
|
||||||
|
linearPredef: {
|
||||||
|
height: 10,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const inputStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
disabled: {
|
||||||
|
"&.MuiInput-underline::before": {
|
||||||
|
borderColor: "#eaeaea",
|
||||||
|
borderBottomStyle: "solid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
function LoginField(props: TextFieldProps) {
|
||||||
|
const classes = inputStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
InputProps={{ classes } as Partial<OutlinedInputProps>}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const mapState = (state: SystemState) => ({
|
const mapState = (state: SystemState) => ({
|
||||||
loggedIn: state.loggedIn,
|
loggedIn: state.loggedIn,
|
||||||
});
|
});
|
||||||
@@ -154,11 +189,11 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
const [jwt, setJwt] = useState<string>("");
|
const [jwt, setJwt] = useState<string>("");
|
||||||
const [secretKey, setSecretKey] = useState<string>("");
|
const [secretKey, setSecretKey] = useState<string>("");
|
||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [loginStrategy, setLoginStrategy] = useState<ILoginDetails>({
|
const [loginStrategy, setLoginStrategy] = useState<ILoginDetails>({
|
||||||
loginStrategy: loginStrategyType.unknown,
|
loginStrategy: loginStrategyType.unknown,
|
||||||
redirect: "",
|
redirect: "",
|
||||||
});
|
});
|
||||||
|
const [loginSending, setLoginSending] = useState<boolean>(false);
|
||||||
|
|
||||||
const loginStrategyEndpoints: LoginStrategyRoutes = {
|
const loginStrategyEndpoints: LoginStrategyRoutes = {
|
||||||
form: "/api/v1/login",
|
form: "/api/v1/login",
|
||||||
@@ -170,23 +205,20 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchConfiguration = () => {
|
const fetchConfiguration = () => {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
api
|
api
|
||||||
.invoke("GET", "/api/v1/login")
|
.invoke("GET", "/api/v1/login")
|
||||||
.then((loginDetails: ILoginDetails) => {
|
.then((loginDetails: ILoginDetails) => {
|
||||||
setLoading(false);
|
|
||||||
setLoginStrategy(loginDetails);
|
setLoginStrategy(loginDetails);
|
||||||
setError("");
|
setError("");
|
||||||
})
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
setLoading(false);
|
|
||||||
setError(err);
|
setError(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const formSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const formSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
setLoginSending(true);
|
||||||
request
|
request
|
||||||
.post(
|
.post(
|
||||||
loginStrategyEndpoints[loginStrategy.loginStrategy] || "/api/v1/login"
|
loginStrategyEndpoints[loginStrategy.loginStrategy] || "/api/v1/login"
|
||||||
@@ -198,6 +230,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
// store the jwt token
|
// store the jwt token
|
||||||
setSession(bodyResponse.sessionId);
|
setSession(bodyResponse.sessionId);
|
||||||
} else if (bodyResponse.error) {
|
} else if (bodyResponse.error) {
|
||||||
|
setLoginSending(false);
|
||||||
// throw will be moved to catch block once bad login returns 403
|
// throw will be moved to catch block once bad login returns 403
|
||||||
throw bodyResponse.error;
|
throw bodyResponse.error;
|
||||||
}
|
}
|
||||||
@@ -208,6 +241,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
history.push("/");
|
history.push("/");
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
setLoginSending(false);
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -232,7 +266,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
<form className={classes.form} noValidate onSubmit={formSubmit}>
|
<form className={classes.form} noValidate onSubmit={formSubmit}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<LoginField
|
||||||
fullWidth
|
fullWidth
|
||||||
id="accessKey"
|
id="accessKey"
|
||||||
value={accessKey}
|
value={accessKey}
|
||||||
@@ -242,10 +276,11 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
label="Enter Access Key"
|
label="Enter Access Key"
|
||||||
name="accessKey"
|
name="accessKey"
|
||||||
autoComplete="username"
|
autoComplete="username"
|
||||||
|
disabled={loginSending}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TextField
|
<LoginField
|
||||||
fullWidth
|
fullWidth
|
||||||
value={secretKey}
|
value={secretKey}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
@@ -256,6 +291,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
type="password"
|
type="password"
|
||||||
id="secretKey"
|
id="secretKey"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
|
disabled={loginSending}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -265,11 +301,14 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
className={classes.submit}
|
className={classes.submit}
|
||||||
disabled={secretKey === "" || accessKey === ""}
|
disabled={secretKey === "" || accessKey === "" || loginSending}
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.linearPredef}>
|
||||||
|
{loginSending && <LinearProgress />}
|
||||||
|
</Grid>
|
||||||
<Grid item xs={12} className={classes.disclaimer}>
|
<Grid item xs={12} className={classes.disclaimer}>
|
||||||
<strong>Don't have an access key?</strong>
|
<strong>Don't have an access key?</strong>
|
||||||
<br />
|
<br />
|
||||||
@@ -321,7 +360,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
<form className={classes.form} noValidate onSubmit={formSubmit}>
|
<form className={classes.form} noValidate onSubmit={formSubmit}>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item xs={12} className={classes.jwtInput}>
|
<Grid item xs={12} className={classes.jwtInput}>
|
||||||
<TextField
|
<LoginField
|
||||||
required
|
required
|
||||||
fullWidth
|
fullWidth
|
||||||
id="jwt"
|
id="jwt"
|
||||||
@@ -332,6 +371,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
label="JWT"
|
label="JWT"
|
||||||
name="jwt"
|
name="jwt"
|
||||||
autoComplete="Service Account JWT Token"
|
autoComplete="Service Account JWT Token"
|
||||||
|
disabled={loginSending}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -341,11 +381,14 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
className={classes.submit}
|
className={classes.submit}
|
||||||
disabled={jwt === ""}
|
disabled={jwt === "" || loginSending}
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.linearPredef}>
|
||||||
|
{loginSending && <LinearProgress />}
|
||||||
|
</Grid>
|
||||||
<Grid item xs={12} className={classes.disclaimer}>
|
<Grid item xs={12} className={classes.disclaimer}>
|
||||||
<strong>Don't have an access key?</strong>
|
<strong>Don't have an access key?</strong>
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user