diff --git a/.gitignore b/.gitignore index d45de5ca..787a0004 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ engine/bin/ /engine/configs/ci_checker.yml engine/meta + +ui/packages/shared/dist/ diff --git a/.gitlab/agents/k8s-cluster-1/config.yaml b/.gitlab/agents/k8s-cluster-1/config.yaml new file mode 100644 index 00000000..73481f44 --- /dev/null +++ b/.gitlab/agents/k8s-cluster-1/config.yaml @@ -0,0 +1,3 @@ +ci_access: + projects: + - id: postgres-ai/database-lab diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f32b4abf..0c4afb69 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -140,7 +140,6 @@ The [postgres-ai/database-lab](https://gitlab.com/postgres-ai/database-lab) repo - [Database Lab CLI](https://gitlab.com/postgres-ai/database-lab/-/tree/master/engine/cmd/cli) - [Database Lab UI](https://gitlab.com/postgres-ai/database-lab/-/tree/master/ui) - [Community Edition](https://gitlab.com/postgres-ai/database-lab/-/tree/master/ui/packages/ce) - - [Platform](https://gitlab.com/postgres-ai/database-lab/-/tree/master/ui/packages/platform) - [Shared components](https://gitlab.com/postgres-ai/database-lab/-/tree/master/ui/packages/shared) Components have a separate version, denoted by either: diff --git a/README.md b/README.md index 73b8d499..6df7f229 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Try it yourself right now: - Visit [Postgres.ai Console](https://console.postgres.ai/), set up your first organization and provision a DBLab Standard Edition (DBLab SE) to any cloud or on-prem - [Pricing](https://postgres.ai/pricing) (starting at $62/month) - [Doc: How to install DBLab SE](https://postgres.ai/docs/how-to-guides/administration/install-dle-from-postgres-ai) -- Demo: https://demo.aws.postgres.ai:446/instance (use the token `demo_token` to access) +- Demo: https://demo.dblab.dev (use the token `demo-token` to access) - Looking for a free version? Install DBLab Community Edition by [following this tutorial](https://postgres.ai/docs/tutorials/database-lab-tutorial) ## How it works @@ -92,7 +92,7 @@ Read more: - Theoretical max of snapshots/clones: 264 ([ZFS](https://en.wikipedia.org/wiki/ZFS), default) - Maximum size of PostgreSQL data directory: 256 quadrillion zebibytes, or 2128 bytes ([ZFS](https://en.wikipedia.org/wiki/ZFS), default) - Support & technologies - - Supported PostgreSQL versions: 9.6–15 + - Supported PostgreSQL versions: 9.6–17 - Thin cloning ([CoW](https://en.wikipedia.org/wiki/Copy-on-write)) technologies: [ZFS](https://en.wikipedia.org/wiki/ZFS) and [LVM](https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)) - UI for manual tasks and API & CLI for automation - Packaged in Docker containers for all components @@ -197,7 +197,7 @@ Reach out to the Postgres.ai team if you want a trial or commercial license that Many thanks to our amazing contributors! - + diff --git a/assets/database-lab-dark-mode.svg b/assets/database-lab-dark-mode.svg index a867914c..2db3bd73 100644 --- a/assets/database-lab-dark-mode.svg +++ b/assets/database-lab-dark-mode.svg @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/assets/database-lab-light-mode.svg b/assets/database-lab-light-mode.svg index 5a3c1e88..81ad331b 100644 --- a/assets/database-lab-light-mode.svg +++ b/assets/database-lab-light-mode.svg @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/assets/dle-simple.svg b/assets/dle-simple.svg index be858b03..76daec73 100644 --- a/assets/dle-simple.svg +++ b/assets/dle-simple.svg @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/assets/dle.svg b/assets/dle.svg index 9d056971..ab0b2f99 100644 --- a/assets/dle.svg +++ b/assets/dle.svg @@ -3,10 +3,10 @@ - - - - - - + + + + + + diff --git a/assets/dle_button.svg b/assets/dle_button.svg index 4efa2538..a03d399d 100644 --- a/assets/dle_button.svg +++ b/assets/dle_button.svg @@ -4,12 +4,12 @@ - - - - - - + + + + + + diff --git a/engine/.gitlab-ci.yml b/engine/.gitlab-ci.yml index cdf29be2..7b8f074f 100644 --- a/engine/.gitlab-ci.yml +++ b/engine/.gitlab-ci.yml @@ -1,5 +1,5 @@ default: - image: golang:1.20 + image: golang:1.23 stages: - test @@ -56,7 +56,7 @@ lint: ### Build binary. build-binary-alpine: <<: *only_engine - image: golang:1.20-alpine + image: golang:1.23-alpine stage: build-binary artifacts: paths: @@ -467,7 +467,12 @@ bash-test-15: bash-test-16: <<: *bash_test variables: - POSTGRES_VERSION: 16rc1 + POSTGRES_VERSION: 16 + +bash-test-17: + <<: *bash_test + variables: + POSTGRES_VERSION: 17 integration-test: services: diff --git a/engine/.golangci.yml b/engine/.golangci.yml index 1e9892c4..c8a38ec1 100644 --- a/engine/.golangci.yml +++ b/engine/.golangci.yml @@ -2,10 +2,9 @@ run: timeout: 2m issues-exit-code: 1 tests: true - skip-dirs: - - vendor output: - format: colored-line-number + formats: + - format: colored-line-number print-issued-lines: true print-linter-name: true @@ -22,10 +21,8 @@ linters-settings: gofmt: simplify: true gofumpt: - lang-version: "1.17" extra-rules: false gosimple: - go: "1.18" checks: [ "all" ] goimports: local-prefixes: gitlab.com/postgres-ai/database-lab @@ -37,14 +34,17 @@ linters-settings: lll: line-length: 140 tab-width: 1 - gomnd: - settings: - mnd: - ignored-functions: strconv.Format*,os.*,strconv.Parse*,strings.SplitN,bytes.SplitN + mnd: + ignored-functions: + - strconv.Format* + - os.* + - strconv.Parse* + - strings.SplitN + - bytes.SplitN revive: - min-confidence: 0.8 + confidence: 0.8 unused: - check-exported: false + exported-fields-are-used: false unparam: check-exported: false nakedret: @@ -72,15 +72,15 @@ linters: - goconst - gocritic - goimports - - gomnd - gosimple - govet - ineffassign - lll - - megacheck - misspell + - mnd - prealloc - revive + - staticcheck - stylecheck - unconvert - unused @@ -90,9 +90,8 @@ linters: disable: - depguard - gosec - - interfacer - gocyclo # currently unmaintained - presets: + #presets: fast: false issues: @@ -104,7 +103,9 @@ issues: - lll - errcheck - wsl - - gomnd + - mnd + exclude-dirs: + - vendor exclude-use-default: false max-issues-per-linter: 0 diff --git a/engine/Dockerfile.dblab-server-debug b/engine/Dockerfile.dblab-server-debug index 35181e62..af6b1f17 100644 --- a/engine/Dockerfile.dblab-server-debug +++ b/engine/Dockerfile.dblab-server-debug @@ -1,7 +1,7 @@ # How to start a container: https://postgres.ai/docs/how-to-guides/administration/engine-manage # Compile stage -FROM golang:1.18 AS build-env +FROM golang:1.23 AS build-env # Build Delve RUN go install github.com/go-delve/delve/cmd/dlv@latest diff --git a/engine/Makefile b/engine/Makefile index 50143634..84bf96de 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -34,7 +34,7 @@ help: ## Display the help message all: clean build ## Build all binary components of the project install-lint: ## Install the linter to $GOPATH/bin which is expected to be in $PATH - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.53.3 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.61.0 run-lint: ## Run linters golangci-lint run diff --git a/engine/api/swagger-spec/dblab_server_swagger.yaml b/engine/api/swagger-spec/dblab_server_swagger.yaml index 177438c5..5242ed8a 100644 --- a/engine/api/swagger-spec/dblab_server_swagger.yaml +++ b/engine/api/swagger-spec/dblab_server_swagger.yaml @@ -15,13 +15,13 @@ info: license: name: AGPL v3 / Database Lab License url: https://github.com/postgres-ai/database-lab-engine/blob/master/LICENSE - version: 3.4.0 + version: 3.5.0 externalDocs: description: DBLab Docs url: https://gitlab.com/postgres-ai/docs/tree/master/docs/database-lab servers: - - url: "https://demo.aws.postgres.ai:446/api" + - url: "https://demo.dblab.dev/api" description: "DBLab 3.x demo server; token: 'demo-token'" x-examples: Verification-Token: "demo-token" @@ -484,7 +484,7 @@ paths: schema: $ref: "#/components/schemas/Error" - /observation/download/{artifact_type}/{clone_id}/{session_id}: + /observation/download: get: tags: - Observation @@ -497,19 +497,19 @@ paths: schema: type: string required: true - - in: path + - in: query required: true name: "artifact_type" schema: type: "string" description: "Type of the requested artifact" - - in: path + - in: query required: true name: "clone_id" schema: type: "string" description: "Clone ID" - - in: path + - in: query required: true name: "session_id" schema: @@ -517,7 +517,7 @@ paths: description: "Session ID" responses: 200: - description: Downloaded the specified artificed of the specified + description: Downloaded the specified artifact of the specified observation session and clone 400: description: "Bad request" @@ -558,7 +558,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Instance' + $ref: "#/components/schemas/Error" example: code: "UNAUTHORIZED" message: "Check your verification token." @@ -613,7 +613,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Instance' + $ref: "#/components/schemas/Error" example: code: "UNAUTHORIZED" message: "Check your verification token." @@ -660,7 +660,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Instance' + $ref: "#/components/schemas/Error" example: code: "UNAUTHORIZED" message: "Check your verification token." @@ -693,7 +693,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Instance' + $ref: "#/components/schemas/Error" example: code: "UNAUTHORIZED" message: "Check your verification token." @@ -738,7 +738,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Instance' + $ref: "#/components/schemas/Error" example: code: "UNAUTHORIZED" message: "Check your verification token." @@ -762,6 +762,76 @@ paths: application/json: schema: $ref: "#/components/schemas/WSToken" + /admin/billing-status: + get: + tags: + - Admin + summary: Checks billing status + description: "" + operationId: billingStatus + parameters: + - in: header + name: Verification-Token + schema: + type: string + required: true + responses: + 200: + description: "Successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/BillingStatus" + 400: + description: "Bad request" + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 401: + description: Unauthorized access + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + code: "UNAUTHORIZED" + message: "Check your verification token." + /admin/activate: + post: + tags: + - Admin + summary: "Activate billing" + description: "Activates billing and sends usage statistics of the instance" + operationId: activateBilling + parameters: + - in: header + name: Verification-Token + schema: + type: string + required: true + responses: + 200: + description: "Successful operation" + content: + application/json: + schema: + $ref: "#/components/schemas/Engine" + 400: + description: "Bad request" + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 401: + description: Unauthorized access + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + code: "UNAUTHORIZED" + message: "Check your verification token." components: schemas: @@ -1245,7 +1315,6 @@ components: type: "string" dbVersion: type: "integer" - required: false tuningParams: type: "object" additionalProperties: diff --git a/engine/cmd/cli/main.go b/engine/cmd/cli/main.go index 205e10ab..5bd6c4ed 100644 --- a/engine/cmd/cli/main.go +++ b/engine/cmd/cli/main.go @@ -24,7 +24,7 @@ func main() { app := &cli.App{ Version: version.GetVersion(), CommandNotFound: func(c *cli.Context, command string) { - fmt.Fprintf(c.App.Writer, "[ERROR] Command %q not found.\n", command) + _, _ = fmt.Fprintf(c.App.Writer, "[ERROR] Command %q not found.\n", command) }, Before: loadEnvironmentParams, Commands: joinCommands( diff --git a/engine/configs/config.example.logical_generic.yml b/engine/configs/config.example.logical_generic.yml index 94a94474..e08d1c28 100644 --- a/engine/configs/config.example.logical_generic.yml +++ b/engine/configs/config.example.logical_generic.yml @@ -98,10 +98,13 @@ databaseContainer: &db_container # We need to specify which Postgres Docker image is to be used for that. # The default is the extended Postgres image built on top of the official Postgres image # (See https://postgres.ai/docs/database-lab/supported_databases). - # It is possible to choose any custom or official Docker image that runs Postgres. Our Dockerfile - # (See https://gitlab.com/postgres-ai/custom-images/-/tree/master/extended) - # is recommended in case if customization is needed. - dockerImage: "postgresai/extended-postgres:15-0.4.1" + # + # For paid customers, Postgres.AI maintains a set of images compatible with + # RDS and RDS Aurora, GCP CloudSQL, Heroku, Timescale Cloud, Supabase, PostGIS. + # + # IMPORTANT: Postgres version of this image should match the source's Postgres version. + # For logical mode, it is a recommendation. For physical mode, it is a *requirement*. + dockerImage: "postgresai/extended-postgres:16-0.5.0" # Container parameters, see # https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources diff --git a/engine/configs/config.example.logical_rds_iam.yml b/engine/configs/config.example.logical_rds_iam.yml index bfef499f..507779e4 100644 --- a/engine/configs/config.example.logical_rds_iam.yml +++ b/engine/configs/config.example.logical_rds_iam.yml @@ -97,10 +97,13 @@ databaseContainer: &db_container # We need to specify which Postgres Docker image is to be used for that. # The default is the extended Postgres image built on top of the official Postgres image # (See https://postgres.ai/docs/database-lab/supported_databases). - # It is possible to choose any custom or official Docker image that runs Postgres. Our Dockerfile - # (See https://gitlab.com/postgres-ai/custom-images/-/tree/master/extended) - # is recommended in case if customization is needed. - dockerImage: "postgresai/extended-postgres:15-0.4.1" + # + # For paid customers, Postgres.AI maintains a set of images compatible with + # RDS and RDS Aurora, GCP CloudSQL, Heroku, Timescale Cloud, Supabase, PostGIS. + # + # IMPORTANT: Postgres version of this image should match the source's Postgres version. + # For logical mode, it is a recommendation. For physical mode, it is a *requirement*. + dockerImage: "postgresai/extended-postgres:16-0.5.0" # Custom parameters for containers with PostgreSQL, see # https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources diff --git a/engine/configs/config.example.physical_generic.yml b/engine/configs/config.example.physical_generic.yml index 0e835a24..ebf29392 100644 --- a/engine/configs/config.example.physical_generic.yml +++ b/engine/configs/config.example.physical_generic.yml @@ -97,10 +97,13 @@ databaseContainer: &db_container # We need to specify which Postgres Docker image is to be used for that. # The default is the extended Postgres image built on top of the official Postgres image # (See https://postgres.ai/docs/database-lab/supported_databases). - # Any custom or official Docker image that runs Postgres. Our Dockerfile - # (See https://gitlab.com/postgres-ai/custom-images/-/tree/master/extended) - # is recommended in case if customization is needed. - dockerImage: "postgresai/extended-postgres:15-0.4.1" + # + # For paid customers, Postgres.AI maintains a set of images compatible with + # RDS and RDS Aurora, GCP CloudSQL, Heroku, Timescale Cloud, Supabase, PostGIS. + # + # IMPORTANT: Postgres version of this image should match the source's Postgres version. + # For logical mode, it is a recommendation. For physical mode, it is a *requirement*. + dockerImage: "postgresai/extended-postgres:16-0.5.0" # Custom parameters for containers with PostgreSQL, see # https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources diff --git a/engine/configs/config.example.physical_pgbackrest.yml b/engine/configs/config.example.physical_pgbackrest.yml index 8be51870..447272e9 100644 --- a/engine/configs/config.example.physical_pgbackrest.yml +++ b/engine/configs/config.example.physical_pgbackrest.yml @@ -97,10 +97,13 @@ databaseContainer: &db_container # We need to specify which Postgres Docker image is to be used for that. # The default is the extended Postgres image built on top of the official Postgres image # (See https://postgres.ai/docs/database-lab/supported_databases). - # Any custom or official Docker image that runs Postgres. Our Dockerfile - # (See https://gitlab.com/postgres-ai/custom-images/-/tree/master/extended) - # is recommended in case if customization is needed. - dockerImage: "postgresai/extended-postgres:15-0.4.1" + # + # For paid customers, Postgres.AI additionally maintains a set of images compatible with + # RDS and RDS Aurora, GCP CloudSQL, Heroku, Timescale Cloud, Supabase, PostGIS. + # + # IMPORTANT: Postgres version of this image should match the source's Postgres version. + # For logical mode, it is a recommendation. For physical mode, it is a *requirement*. + dockerImage: "postgresai/extended-postgres:16-0.5.0" # Custom parameters for containers with PostgreSQL, see # https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources diff --git a/engine/configs/config.example.physical_walg.yml b/engine/configs/config.example.physical_walg.yml index 2238195b..17a4167c 100644 --- a/engine/configs/config.example.physical_walg.yml +++ b/engine/configs/config.example.physical_walg.yml @@ -97,10 +97,13 @@ databaseContainer: &db_container # We need to specify which Postgres Docker image is to be used for that. # The default is the extended Postgres image built on top of the official Postgres image # (See https://postgres.ai/docs/database-lab/supported_databases). - # Any custom or official Docker image that runs Postgres. Our Dockerfile - # (See https://gitlab.com/postgres-ai/custom-images/-/tree/master/extended) - # is recommended in case if customization is needed. - dockerImage: "postgresai/extended-postgres:15-0.4.1" + # + # For paid customers, Postgres.AI additionally maintains a set of images compatible with + # RDS and RDS Aurora, GCP CloudSQL, Heroku, Timescale Cloud, Supabase, PostGIS. + # + # IMPORTANT: Postgres version of this image should match the source's Postgres version. + # For logical mode, it is a recommendation. For physical mode, it is a *requirement*. + dockerImage: "postgresai/extended-postgres:16-0.5.0" # Custom parameters for containers with PostgreSQL, see # https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources diff --git a/engine/configs/standard/postgres/default/17/pg_hba.conf b/engine/configs/standard/postgres/default/17/pg_hba.conf new file mode 100644 index 00000000..7f379dbb --- /dev/null +++ b/engine/configs/standard/postgres/default/17/pg_hba.conf @@ -0,0 +1,128 @@ +# PostgreSQL Client Authentication Configuration File +# =================================================== +# +# Refer to the "Client Authentication" section in the PostgreSQL +# documentation for a complete description of this file. A short +# synopsis follows. +# +# ---------------------- +# Authentication Records +# ---------------------- +# +# This file controls: which hosts are allowed to connect, how clients +# are authenticated, which PostgreSQL user names they can use, which +# databases they can access. Records take one of these forms: +# +# local DATABASE USER METHOD [OPTIONS] +# host DATABASE USER ADDRESS METHOD [OPTIONS] +# hostssl DATABASE USER ADDRESS METHOD [OPTIONS] +# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] +# hostgssenc DATABASE USER ADDRESS METHOD [OPTIONS] +# hostnogssenc DATABASE USER ADDRESS METHOD [OPTIONS] +# +# (The uppercase items must be replaced by actual values.) +# +# The first field is the connection type: +# - "local" is a Unix-domain socket +# - "host" is a TCP/IP socket (encrypted or not) +# - "hostssl" is a TCP/IP socket that is SSL-encrypted +# - "hostnossl" is a TCP/IP socket that is not SSL-encrypted +# - "hostgssenc" is a TCP/IP socket that is GSSAPI-encrypted +# - "hostnogssenc" is a TCP/IP socket that is not GSSAPI-encrypted +# +# DATABASE can be "all", "sameuser", "samerole", "replication", a +# database name, a regular expression (if it starts with a slash (/)) +# or a comma-separated list thereof. The "all" keyword does not match +# "replication". Access to replication must be enabled in a separate +# record (see example below). +# +# USER can be "all", a user name, a group name prefixed with "+", a +# regular expression (if it starts with a slash (/)) or a comma-separated +# list thereof. In both the DATABASE and USER fields you can also write +# a file name prefixed with "@" to include names from a separate file. +# +# ADDRESS specifies the set of hosts the record matches. It can be a +# host name, or it is made up of an IP address and a CIDR mask that is +# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that +# specifies the number of significant bits in the mask. A host name +# that starts with a dot (.) matches a suffix of the actual host name. +# Alternatively, you can write an IP address and netmask in separate +# columns to specify the set of hosts. Instead of a CIDR-address, you +# can write "samehost" to match any of the server's own IP addresses, +# or "samenet" to match any address in any subnet that the server is +# directly connected to. +# +# METHOD can be "trust", "reject", "md5", "password", "scram-sha-256", +# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert". +# Note that "password" sends passwords in clear text; "md5" or +# "scram-sha-256" are preferred since they send encrypted passwords. +# +# OPTIONS are a set of options for the authentication in the format +# NAME=VALUE. The available options depend on the different +# authentication methods -- refer to the "Client Authentication" +# section in the documentation for a list of which options are +# available for which authentication methods. +# +# Database and user names containing spaces, commas, quotes and other +# special characters must be quoted. Quoting one of the keywords +# "all", "sameuser", "samerole" or "replication" makes the name lose +# its special character, and just match a database or username with +# that name. +# +# --------------- +# Include Records +# --------------- +# +# This file allows the inclusion of external files or directories holding +# more records, using the following keywords: +# +# include FILE +# include_if_exists FILE +# include_dir DIRECTORY +# +# FILE is the file name to include, and DIR is the directory name containing +# the file(s) to include. Any file in a directory will be loaded if suffixed +# with ".conf". The files of a directory are ordered by name. +# include_if_exists ignores missing files. FILE and DIRECTORY can be +# specified as a relative or an absolute path, and can be double-quoted if +# they contain spaces. +# +# ------------- +# Miscellaneous +# ------------- +# +# This file is read on server startup and when the server receives a +# SIGHUP signal. If you edit the file on a running system, you have to +# SIGHUP the server for the changes to take effect, run "pg_ctl reload", +# or execute "SELECT pg_reload_conf()". +# +# ---------------------------------- +# Put your actual configuration here +# ---------------------------------- +# +# If you want to allow non-local connections, you need to add more +# "host" records. In that case you will also need to make PostgreSQL +# listen on a non-local interface via the listen_addresses +# configuration parameter, or via the -i or -h command line switches. + +# CAUTION: Configuring the system for local "trust" authentication +# allows any local user to connect as any PostgreSQL user, including +# the database superuser. If you do not trust all your local users, +# use another authentication method. + + +# TYPE DATABASE USER ADDRESS METHOD + +# "local" is for Unix domain socket connections only +local all all trust +# IPv4 local connections: +host all all 127.0.0.1/32 trust +# IPv6 local connections: +host all all ::1/128 trust +# Allow replication connections from localhost, by a user with the +# replication privilege. +local replication all trust +host replication all 127.0.0.1/32 trust +host replication all ::1/128 trust + +host all all all scram-sha-256 diff --git a/engine/configs/standard/postgres/default/17/postgresql.dblab.postgresql.conf b/engine/configs/standard/postgres/default/17/postgresql.dblab.postgresql.conf new file mode 100644 index 00000000..98e4a16e --- /dev/null +++ b/engine/configs/standard/postgres/default/17/postgresql.dblab.postgresql.conf @@ -0,0 +1,844 @@ +# ----------------------------- +# PostgreSQL configuration file +# ----------------------------- +# +# This file consists of lines of the form: +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are introduced with +# "#" anywhere on a line. The complete list of parameter names and allowed +# values can be found in the PostgreSQL documentation. +# +# The commented-out settings shown in this file represent the default values. +# Re-commenting a setting is NOT sufficient to revert it to the default value; +# you need to reload the server. +# +# This file is read on server startup and when the server receives a SIGHUP +# signal. If you edit the file on a running system, you have to SIGHUP the +# server for the changes to take effect, run "pg_ctl reload", or execute +# "SELECT pg_reload_conf()". Some parameters, which are marked below, +# require a server shutdown and restart to take effect. +# +# Any parameter can also be given as a command-line option to the server, e.g., +# "postgres -c log_connections=on". Some parameters can be changed at run time +# with the "SET" SQL command. +# +# Memory units: B = bytes Time units: us = microseconds +# kB = kilobytes ms = milliseconds +# MB = megabytes s = seconds +# GB = gigabytes min = minutes +# TB = terabytes h = hours +# d = days + + +#------------------------------------------------------------------------------ +# FILE LOCATIONS +#------------------------------------------------------------------------------ + +# The default values of these variables are driven from the -D command-line +# option or PGDATA environment variable, represented here as ConfigDir. + +#data_directory = 'ConfigDir' # use data in another directory + # (change requires restart) +#hba_file = 'ConfigDir/pg_hba.conf' # host-based authentication file + # (change requires restart) +#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file + # (change requires restart) + +# If external_pid_file is not explicitly set, no extra PID file is written. +#external_pid_file = '' # write an extra PID file + # (change requires restart) + + +#------------------------------------------------------------------------------ +# CONNECTIONS AND AUTHENTICATION +#------------------------------------------------------------------------------ + +# - Connection Settings - + +listen_addresses = '*' + # comma-separated list of addresses; + # defaults to 'localhost'; use '*' for all + # (change requires restart) +#port = 5432 # (change requires restart) +max_connections = 100 # (change requires restart) +#reserved_connections = 0 # (change requires restart) +#superuser_reserved_connections = 3 # (change requires restart) +#unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories + # (change requires restart) +#unix_socket_group = '' # (change requires restart) +#unix_socket_permissions = 0777 # begin with 0 to use octal notation + # (change requires restart) +#bonjour = off # advertise server via Bonjour + # (change requires restart) +#bonjour_name = '' # defaults to the computer name + # (change requires restart) + +# - TCP settings - +# see "man tcp" for details + +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; + # 0 selects the system default +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; + # 0 selects the system default +#tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; + # 0 selects the system default + +#client_connection_check_interval = 0 # time between checks for client + # disconnection while running queries; + # 0 for never + +# - Authentication - + +#authentication_timeout = 1min # 1s-600s +#password_encryption = scram-sha-256 # scram-sha-256 or md5 +#scram_iterations = 4096 + +# GSSAPI using Kerberos +#krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab' +#krb_caseins_users = off +#gss_accept_delegation = off + +# - SSL - + +#ssl = off +#ssl_ca_file = '' +#ssl_cert_file = 'server.crt' +#ssl_crl_file = '' +#ssl_crl_dir = '' +#ssl_key_file = 'server.key' +#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers +#ssl_prefer_server_ciphers = on +#ssl_ecdh_curve = 'prime256v1' +#ssl_min_protocol_version = 'TLSv1.2' +#ssl_max_protocol_version = '' +#ssl_dh_params_file = '' +#ssl_passphrase_command = '' +#ssl_passphrase_command_supports_reload = off + + +#------------------------------------------------------------------------------ +# RESOURCE USAGE (except WAL) +#------------------------------------------------------------------------------ + +# - Memory - + +shared_buffers = 128MB # min 128kB + # (change requires restart) +#huge_pages = try # on, off, or try + # (change requires restart) +#huge_page_size = 0 # zero for system default + # (change requires restart) +#temp_buffers = 8MB # min 800kB +#max_prepared_transactions = 0 # zero disables the feature + # (change requires restart) +# Caution: it is not advisable to set max_prepared_transactions nonzero unless +# you actively intend to use prepared transactions. +#work_mem = 4MB # min 64kB +#hash_mem_multiplier = 2.0 # 1-1000.0 multiplier on hash table work_mem +#maintenance_work_mem = 64MB # min 64kB +#autovacuum_work_mem = -1 # min 64kB, or -1 to use maintenance_work_mem +#logical_decoding_work_mem = 64MB # min 64kB +#max_stack_depth = 2MB # min 100kB +#shared_memory_type = mmap # the default is the first option + # supported by the operating system: + # mmap + # sysv + # windows + # (change requires restart) +dynamic_shared_memory_type = posix # the default is usually the first option + # supported by the operating system: + # posix + # sysv + # windows + # mmap + # (change requires restart) +#min_dynamic_shared_memory = 0MB # (change requires restart) +#vacuum_buffer_usage_limit = 2MB # size of vacuum and analyze buffer access strategy ring; + # 0 to disable vacuum buffer access strategy; + # range 128kB to 16GB + +# SLRU buffers (change requires restart) +#commit_timestamp_buffers = 0 # memory for pg_commit_ts (0 = auto) +#multixact_offset_buffers = 16 # memory for pg_multixact/offsets +#multixact_member_buffers = 32 # memory for pg_multixact/members +#notify_buffers = 16 # memory for pg_notify +#serializable_buffers = 32 # memory for pg_serial +#subtransaction_buffers = 0 # memory for pg_subtrans (0 = auto) +#transaction_buffers = 0 # memory for pg_xact (0 = auto) + +# - Disk - + +#temp_file_limit = -1 # limits per-process temp file space + # in kilobytes, or -1 for no limit + +#max_notify_queue_pages = 1048576 # limits the number of SLRU pages allocated + # for NOTIFY / LISTEN queue + +# - Kernel Resources - + +#max_files_per_process = 1000 # min 64 + # (change requires restart) + +# - Cost-Based Vacuum Delay - + +#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables) +#vacuum_cost_page_hit = 1 # 0-10000 credits +#vacuum_cost_page_miss = 2 # 0-10000 credits +#vacuum_cost_page_dirty = 20 # 0-10000 credits +#vacuum_cost_limit = 200 # 1-10000 credits + +# - Background Writer - + +#bgwriter_delay = 200ms # 10-10000ms between rounds +#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables +#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round +#bgwriter_flush_after = 512kB # measured in pages, 0 disables + +# - Asynchronous Behavior - + +#backend_flush_after = 0 # measured in pages, 0 disables +#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching +#maintenance_io_concurrency = 10 # 1-1000; 0 disables prefetching +#io_combine_limit = 128kB # usually 1-32 blocks (depends on OS) +#max_worker_processes = 8 # (change requires restart) +#max_parallel_workers_per_gather = 2 # limited by max_parallel_workers +#max_parallel_maintenance_workers = 2 # limited by max_parallel_workers +#max_parallel_workers = 8 # number of max_worker_processes that + # can be used in parallel operations +#parallel_leader_participation = on + + +#------------------------------------------------------------------------------ +# WRITE-AHEAD LOG +#------------------------------------------------------------------------------ + +# - Settings - + +#wal_level = replica # minimal, replica, or logical + # (change requires restart) +#fsync = on # flush data to disk for crash safety + # (turning this off can cause + # unrecoverable data corruption) +#synchronous_commit = on # synchronization level; + # off, local, remote_write, remote_apply, or on +#wal_sync_method = fsync # the default is the first option + # supported by the operating system: + # open_datasync + # fdatasync (default on Linux and FreeBSD) + # fsync + # fsync_writethrough + # open_sync +#full_page_writes = on # recover from partial page writes +#wal_log_hints = off # also do full page writes of non-critical updates + # (change requires restart) +#wal_compression = off # enables compression of full-page writes; + # off, pglz, lz4, zstd, or on +#wal_init_zero = on # zero-fill new WAL files +#wal_recycle = on # recycle WAL files +#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers + # (change requires restart) +#wal_writer_delay = 200ms # 1-10000 milliseconds +#wal_writer_flush_after = 1MB # measured in pages, 0 disables +#wal_skip_threshold = 2MB + +#commit_delay = 0 # range 0-100000, in microseconds +#commit_siblings = 5 # range 1-1000 + +# - Checkpoints - + +#checkpoint_timeout = 5min # range 30s-1d +#checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0 +#checkpoint_flush_after = 256kB # measured in pages, 0 disables +#checkpoint_warning = 30s # 0 disables +max_wal_size = 1GB +min_wal_size = 80MB + +# - Prefetching during recovery - + +#recovery_prefetch = try # prefetch pages referenced in the WAL? +#wal_decode_buffer_size = 512kB # lookahead window used for prefetching + # (change requires restart) + +# - Archiving - + +#archive_mode = off # enables archiving; off, on, or always + # (change requires restart) +#archive_library = '' # library to use to archive a WAL file + # (empty string indicates archive_command should + # be used) +#archive_command = '' # command to use to archive a WAL file + # placeholders: %p = path of file to archive + # %f = file name only + # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' +#archive_timeout = 0 # force a WAL file switch after this + # number of seconds; 0 disables + +# - Archive Recovery - + +# These are only used in recovery mode. + +#restore_command = '' # command to use to restore an archived WAL file + # placeholders: %p = path of file to restore + # %f = file name only + # e.g. 'cp /mnt/server/archivedir/%f %p' +#archive_cleanup_command = '' # command to execute at every restartpoint +#recovery_end_command = '' # command to execute at completion of recovery + +# - Recovery Target - + +# Set these only when performing a targeted recovery. + +#recovery_target = '' # 'immediate' to end recovery as soon as a + # consistent state is reached + # (change requires restart) +#recovery_target_name = '' # the named restore point to which recovery will proceed + # (change requires restart) +#recovery_target_time = '' # the time stamp up to which recovery will proceed + # (change requires restart) +#recovery_target_xid = '' # the transaction ID up to which recovery will proceed + # (change requires restart) +#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed + # (change requires restart) +#recovery_target_inclusive = on # Specifies whether to stop: + # just after the specified recovery target (on) + # just before the recovery target (off) + # (change requires restart) +#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID + # (change requires restart) +#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' + # (change requires restart) + +# - WAL Summarization - + +#summarize_wal = off # run WAL summarizer process? +#wal_summary_keep_time = '10d' # when to remove old summary files, 0 = never + + +#------------------------------------------------------------------------------ +# REPLICATION +#------------------------------------------------------------------------------ + +# - Sending Servers - + +# Set these on the primary and on any standby that will send replication data. + +#max_wal_senders = 10 # max number of walsender processes + # (change requires restart) +#max_replication_slots = 10 # max number of replication slots + # (change requires restart) +#wal_keep_size = 0 # in megabytes; 0 disables +#max_slot_wal_keep_size = -1 # in megabytes; -1 disables +#wal_sender_timeout = 60s # in milliseconds; 0 disables +#track_commit_timestamp = off # collect timestamp of transaction commit + # (change requires restart) + +# - Primary Server - + +# These settings are ignored on a standby server. + +#synchronous_standby_names = '' # standby servers that provide sync rep + # method to choose sync standbys, number of sync standbys, + # and comma-separated list of application_name + # from standby(s); '*' = all +#synchronized_standby_slots = '' # streaming replication standby server slot + # names that logical walsender processes will wait for + +# - Standby Servers - + +# These settings are ignored on a primary server. + +#primary_conninfo = '' # connection string to sending server +#primary_slot_name = '' # replication slot on sending server +#hot_standby = on # "off" disallows queries during recovery + # (change requires restart) +#max_standby_archive_delay = 30s # max delay before canceling queries + # when reading WAL from archive; + # -1 allows indefinite delay +#max_standby_streaming_delay = 30s # max delay before canceling queries + # when reading streaming WAL; + # -1 allows indefinite delay +#wal_receiver_create_temp_slot = off # create temp slot if primary_slot_name + # is not set +#wal_receiver_status_interval = 10s # send replies at least this often + # 0 disables +#hot_standby_feedback = off # send info from standby to prevent + # query conflicts +#wal_receiver_timeout = 60s # time that receiver waits for + # communication from primary + # in milliseconds; 0 disables +#wal_retrieve_retry_interval = 5s # time to wait before retrying to + # retrieve WAL after a failed attempt +#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery +#sync_replication_slots = off # enables slot synchronization on the physical standby from the primary + +# - Subscribers - + +# These settings are ignored on a publisher. + +#max_logical_replication_workers = 4 # taken from max_worker_processes + # (change requires restart) +#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers +#max_parallel_apply_workers_per_subscription = 2 # taken from max_logical_replication_workers + + +#------------------------------------------------------------------------------ +# QUERY TUNING +#------------------------------------------------------------------------------ + +# - Planner Method Configuration - + +#enable_async_append = on +#enable_bitmapscan = on +#enable_gathermerge = on +#enable_hashagg = on +#enable_hashjoin = on +#enable_incremental_sort = on +#enable_indexscan = on +#enable_indexonlyscan = on +#enable_material = on +#enable_memoize = on +#enable_mergejoin = on +#enable_nestloop = on +#enable_parallel_append = on +#enable_parallel_hash = on +#enable_partition_pruning = on +#enable_partitionwise_join = off +#enable_partitionwise_aggregate = off +#enable_presorted_aggregate = on +#enable_seqscan = on +#enable_sort = on +#enable_tidscan = on +#enable_group_by_reordering = on + +# - Planner Cost Constants - + +#seq_page_cost = 1.0 # measured on an arbitrary scale +#random_page_cost = 4.0 # same scale as above +#cpu_tuple_cost = 0.01 # same scale as above +#cpu_index_tuple_cost = 0.005 # same scale as above +#cpu_operator_cost = 0.0025 # same scale as above +#parallel_setup_cost = 1000.0 # same scale as above +#parallel_tuple_cost = 0.1 # same scale as above +#min_parallel_table_scan_size = 8MB +#min_parallel_index_scan_size = 512kB +#effective_cache_size = 4GB + +#jit_above_cost = 100000 # perform JIT compilation if available + # and query more expensive than this; + # -1 disables +#jit_inline_above_cost = 500000 # inline small functions if query is + # more expensive than this; -1 disables +#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if + # query is more expensive than this; + # -1 disables + +# - Genetic Query Optimizer - + +#geqo = on +#geqo_threshold = 12 +#geqo_effort = 5 # range 1-10 +#geqo_pool_size = 0 # selects default based on effort +#geqo_generations = 0 # selects default based on effort +#geqo_selection_bias = 2.0 # range 1.5-2.0 +#geqo_seed = 0.0 # range 0.0-1.0 + +# - Other Planner Options - + +#default_statistics_target = 100 # range 1-10000 +#constraint_exclusion = partition # on, off, or partition +#cursor_tuple_fraction = 0.1 # range 0.0-1.0 +#from_collapse_limit = 8 +#jit = on # allow JIT compilation +#join_collapse_limit = 8 # 1 disables collapsing of explicit + # JOIN clauses +#plan_cache_mode = auto # auto, force_generic_plan or + # force_custom_plan +#recursive_worktable_factor = 10.0 # range 0.001-1000000 + + +#------------------------------------------------------------------------------ +# REPORTING AND LOGGING +#------------------------------------------------------------------------------ + +# - Where to Log - + +#log_destination = 'stderr' # Valid values are combinations of + # stderr, csvlog, jsonlog, syslog, and + # eventlog, depending on platform. + # csvlog and jsonlog require + # logging_collector to be on. + +# This is used when logging to stderr: +#logging_collector = off # Enable capturing of stderr, jsonlog, + # and csvlog into log files. Required + # to be on for csvlogs and jsonlogs. + # (change requires restart) + +# These are only used if logging_collector is on: +#log_directory = 'log' # directory where log files are written, + # can be absolute or relative to PGDATA +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, + # can include strftime() escapes +#log_file_mode = 0600 # creation mode for log files, + # begin with 0 to use octal notation +#log_rotation_age = 1d # Automatic rotation of logfiles will + # happen after that time. 0 disables. +#log_rotation_size = 10MB # Automatic rotation of logfiles will + # happen after that much log output. + # 0 disables. +#log_truncate_on_rotation = off # If on, an existing log file with the + # same name as the new log file will be + # truncated rather than appended to. + # But such truncation only occurs on + # time-driven rotation, not on restarts + # or size-driven rotation. Default is + # off, meaning append to existing files + # in all cases. + +# These are relevant when logging to syslog: +#syslog_facility = 'LOCAL0' +#syslog_ident = 'postgres' +#syslog_sequence_numbers = on +#syslog_split_messages = on + +# This is only relevant when logging to eventlog (Windows): +# (change requires restart) +#event_source = 'PostgreSQL' + +# - When to Log - + +#log_min_messages = warning # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic + +#log_min_error_statement = error # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # info + # notice + # warning + # error + # log + # fatal + # panic (effectively off) + +#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements + # and their durations, > 0 logs only + # statements running at least this number + # of milliseconds + +#log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements + # and their durations, > 0 logs only a sample of + # statements running at least this number + # of milliseconds; + # sample fraction is determined by log_statement_sample_rate + +#log_statement_sample_rate = 1.0 # fraction of logged statements exceeding + # log_min_duration_sample to be logged; + # 1.0 logs all such statements, 0.0 never logs + + +#log_transaction_sample_rate = 0.0 # fraction of transactions whose statements + # are logged regardless of their duration; 1.0 logs all + # statements from all transactions, 0.0 never logs + +#log_startup_progress_interval = 10s # Time between progress updates for + # long-running startup operations. + # 0 disables the feature, > 0 indicates + # the interval in milliseconds. + +# - What to Log - + +#debug_print_parse = off +#debug_print_rewritten = off +#debug_print_plan = off +#debug_pretty_print = on +#log_autovacuum_min_duration = 10min # log autovacuum activity; + # -1 disables, 0 logs all actions and + # their durations, > 0 logs only + # actions running at least this number + # of milliseconds. +#log_checkpoints = on +#log_connections = off +#log_disconnections = off +#log_duration = off +#log_error_verbosity = default # terse, default, or verbose messages +#log_hostname = off +#log_line_prefix = '%m [%p] ' # special values: + # %a = application name + # %u = user name + # %d = database name + # %r = remote host and port + # %h = remote host + # %b = backend type + # %p = process ID + # %P = process ID of parallel group leader + # %t = timestamp without milliseconds + # %m = timestamp with milliseconds + # %n = timestamp with milliseconds (as a Unix epoch) + # %Q = query ID (0 if none or not computed) + # %i = command tag + # %e = SQL state + # %c = session ID + # %l = session line number + # %s = session start timestamp + # %v = virtual transaction ID + # %x = transaction ID (0 if none) + # %q = stop here in non-session + # processes + # %% = '%' + # e.g. '<%u%%%d> ' +#log_lock_waits = off # log lock waits >= deadlock_timeout +#log_recovery_conflict_waits = off # log standby recovery conflict waits + # >= deadlock_timeout +#log_parameter_max_length = -1 # when logging statements, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_parameter_max_length_on_error = 0 # when logging an error, limit logged + # bind-parameter values to N bytes; + # -1 means print in full, 0 disables +#log_statement = 'none' # none, ddl, mod, all +#log_replication_commands = off +#log_temp_files = -1 # log temporary files equal or larger + # than the specified size in kilobytes; + # -1 disables, 0 logs all temp files +log_timezone = 'Etc/UTC' + +# - Process Title - + +#cluster_name = '' # added to process titles if nonempty + # (change requires restart) +#update_process_title = on + + +#------------------------------------------------------------------------------ +# STATISTICS +#------------------------------------------------------------------------------ + +# - Cumulative Query and Index Statistics - + +#track_activities = on +#track_activity_query_size = 1024 # (change requires restart) +#track_counts = on +#track_io_timing = off +#track_wal_io_timing = off +#track_functions = none # none, pl, all +#stats_fetch_consistency = cache # cache, none, snapshot + + +# - Monitoring - + +#compute_query_id = auto +#log_statement_stats = off +#log_parser_stats = off +#log_planner_stats = off +#log_executor_stats = off + + +#------------------------------------------------------------------------------ +# AUTOVACUUM +#------------------------------------------------------------------------------ + +#autovacuum = on # Enable autovacuum subprocess? 'on' + # requires track_counts to also be on. +#autovacuum_max_workers = 3 # max number of autovacuum subprocesses + # (change requires restart) +#autovacuum_naptime = 1min # time between autovacuum runs +#autovacuum_vacuum_threshold = 50 # min number of row updates before + # vacuum +#autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts + # before vacuum; -1 disables insert + # vacuums +#autovacuum_analyze_threshold = 50 # min number of row updates before + # analyze +#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum +#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table + # size before insert vacuum +#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze +#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum + # (change requires restart) +#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age + # before forced vacuum + # (change requires restart) +#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for + # autovacuum, in milliseconds; + # -1 means use vacuum_cost_delay +#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for + # autovacuum, -1 means use + # vacuum_cost_limit + + +#------------------------------------------------------------------------------ +# CLIENT CONNECTION DEFAULTS +#------------------------------------------------------------------------------ + +# - Statement Behavior - + +#client_min_messages = notice # values in order of decreasing detail: + # debug5 + # debug4 + # debug3 + # debug2 + # debug1 + # log + # notice + # warning + # error +#search_path = '"$user", public' # schema names +#row_security = on +#default_table_access_method = 'heap' +#default_tablespace = '' # a tablespace name, '' uses the default +#default_toast_compression = 'pglz' # 'pglz' or 'lz4' +#temp_tablespaces = '' # a list of tablespace names, '' uses + # only default tablespace +#check_function_bodies = on +#default_transaction_isolation = 'read committed' +#default_transaction_read_only = off +#default_transaction_deferrable = off +#session_replication_role = 'origin' +#statement_timeout = 0 # in milliseconds, 0 is disabled +#transaction_timeout = 0 # in milliseconds, 0 is disabled +#lock_timeout = 0 # in milliseconds, 0 is disabled +#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled +#idle_session_timeout = 0 # in milliseconds, 0 is disabled +#vacuum_freeze_table_age = 150000000 +#vacuum_freeze_min_age = 50000000 +#vacuum_failsafe_age = 1600000000 +#vacuum_multixact_freeze_table_age = 150000000 +#vacuum_multixact_freeze_min_age = 5000000 +#vacuum_multixact_failsafe_age = 1600000000 +#bytea_output = 'hex' # hex, escape +#xmlbinary = 'base64' +#xmloption = 'content' +#gin_pending_list_limit = 4MB +#createrole_self_grant = '' # set and/or inherit +#event_triggers = on + +# - Locale and Formatting - + +datestyle = 'iso, mdy' +#intervalstyle = 'postgres' +timezone = 'Etc/UTC' +#timezone_abbreviations = 'Default' # Select the set of available time zone + # abbreviations. Currently, there are + # Default + # Australia (historical usage) + # India + # You can create your own file in + # share/timezonesets/. +#extra_float_digits = 1 # min -15, max 3; any value >0 actually + # selects precise output mode +#client_encoding = sql_ascii # actually, defaults to database + # encoding + +# These settings are initialized by initdb, but they can be changed. +lc_messages = 'en_US.utf8' # locale for system error message + # strings +lc_monetary = 'en_US.utf8' # locale for monetary formatting +lc_numeric = 'en_US.utf8' # locale for number formatting +lc_time = 'en_US.utf8' # locale for time formatting + +#icu_validation_level = warning # report ICU locale validation + # errors at the given level + +# default configuration for text search +default_text_search_config = 'pg_catalog.english' + +# - Shared Library Preloading - + +#local_preload_libraries = '' +#session_preload_libraries = '' +#shared_preload_libraries = '' # (change requires restart) +#jit_provider = 'llvmjit' # JIT library to use + +# - Other Defaults - + +#dynamic_library_path = '$libdir' +#extension_destdir = '' # prepend path when loading extensions + # and shared objects (added by Debian) +#gin_fuzzy_search_limit = 0 + + +#------------------------------------------------------------------------------ +# LOCK MANAGEMENT +#------------------------------------------------------------------------------ + +#deadlock_timeout = 1s +#max_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_transaction = 64 # min 10 + # (change requires restart) +#max_pred_locks_per_relation = -2 # negative values mean + # (max_pred_locks_per_transaction + # / -max_pred_locks_per_relation) - 1 +#max_pred_locks_per_page = 2 # min 0 + + +#------------------------------------------------------------------------------ +# VERSION AND PLATFORM COMPATIBILITY +#------------------------------------------------------------------------------ + +# - Previous PostgreSQL Versions - + +#array_nulls = on +#backslash_quote = safe_encoding # on, off, or safe_encoding +#escape_string_warning = on +#lo_compat_privileges = off +#quote_all_identifiers = off +#standard_conforming_strings = on +#synchronize_seqscans = on + +# - Other Platforms and Clients - + +#transform_null_equals = off +#allow_alter_system = on + + +#------------------------------------------------------------------------------ +# ERROR HANDLING +#------------------------------------------------------------------------------ + +#exit_on_error = off # terminate session on any error? +#restart_after_crash = on # reinitialize after backend crash? +#data_sync_retry = off # retry or panic on failure to fsync + # data? + # (change requires restart) +#recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+) + + +#------------------------------------------------------------------------------ +# CONFIG FILE INCLUDES +#------------------------------------------------------------------------------ + +# These options allow settings to be loaded from files other than the +# default postgresql.conf. Note that these are directives, not variable +# assignments, so they can usefully be given more than once. + +#include_dir = '...' # include files ending in '.conf' from + # a directory, e.g., 'conf.d' +#include_if_exists = '...' # include file only if it exists +#include = '...' # include file + + +#------------------------------------------------------------------------------ +# CUSTOMIZED OPTIONS +#------------------------------------------------------------------------------ + +# Add settings for extensions here diff --git a/engine/go.mod b/engine/go.mod index 934b525b..ec0ddf86 100644 --- a/engine/go.mod +++ b/engine/go.mod @@ -1,14 +1,14 @@ module gitlab.com/postgres-ai/database-lab/v3 -go 1.20 +go 1.23 require ( github.com/AlekSi/pointer v1.2.0 github.com/ahmetalpbalkan/dlog v0.0.0-20170105205344-4fb5f8204f26 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/aws/aws-sdk-go v1.44.309 - github.com/docker/cli v24.0.5+incompatible - github.com/docker/docker v24.0.5+incompatible + github.com/docker/cli v25.0.6+incompatible + github.com/docker/docker v25.0.6+incompatible github.com/docker/go-connections v0.4.0 github.com/docker/go-units v0.5.0 github.com/dustin/go-humanize v1.0.1 @@ -27,10 +27,11 @@ require ( github.com/sergi/go-diff v1.3.1 github.com/sethvargo/go-password v0.2.0 github.com/shirou/gopsutil v3.21.11+incompatible - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.12.0 github.com/urfave/cli/v2 v2.25.7 - golang.org/x/crypto v0.11.0 + github.com/wagslane/go-password-validator v0.3.0 + golang.org/x/crypto v0.14.0 golang.org/x/mod v0.12.0 golang.org/x/oauth2 v0.10.0 gopkg.in/yaml.v2 v2.4.0 @@ -42,9 +43,14 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/containerd/containerd v1.7.2 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -57,34 +63,37 @@ require ( github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.16.7 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/moby/patternmatcher v0.5.0 // indirect - github.com/moby/sys/mount v0.3.3 // indirect - github.com/moby/sys/mountinfo v0.6.2 // indirect github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc4 // indirect - github.com/opencontainers/runc v1.1.8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.1 // indirect - github.com/wagslane/go-password-validator v0.3.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect + go.opentelemetry.io/otel v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.18.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/tools v0.11.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e // indirect - google.golang.org/grpc v1.57.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/engine/go.sum b/engine/go.sum index e9618945..9be68150 100644 --- a/engine/go.sum +++ b/engine/go.sum @@ -22,6 +22,8 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -40,6 +42,7 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Flaque/filet v0.0.0-20201012163910-45f684403088 h1:PnnQln5IGbhLeJOi6hVs+lCeF+B1dRfFKPGXUAez0Ww= github.com/Flaque/filet v0.0.0-20201012163910-45f684403088/go.mod h1:TK+jB3mBs+8ZMWhU5BqZKnZWJ1MrLo8etNVg51ueTBo= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= @@ -57,6 +60,8 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= +github.com/Microsoft/hcsshim v0.10.0-rc.8/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -93,11 +98,12 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -107,6 +113,7 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= @@ -125,7 +132,6 @@ github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -158,6 +164,8 @@ github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0Z github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= @@ -194,9 +202,13 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= @@ -207,17 +219,17 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= -github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v25.0.6+incompatible h1:F1mCw1kUGixOkM8WQbcG5kniPvP8XCFxreFxl4b/UnY= +github.com/docker/cli v25.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= +github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= @@ -243,6 +255,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -263,6 +277,11 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= @@ -272,7 +291,9 @@ github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL9 github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= @@ -280,6 +301,7 @@ github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblf github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= @@ -331,6 +353,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v34 v34.0.0 h1:/siYFImY8KwGc5QD1gaPf+f8QX6tLwxNIco2RkYxoFA= github.com/google/go-github/v34 v34.0.0/go.mod h1:w/2qlrXUfty+lbyO6tatnzIw97v1CM+/jZcwXMDiPQQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -365,7 +389,10 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= @@ -380,7 +407,6 @@ github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -398,10 +424,10 @@ github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -434,6 +460,7 @@ github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -457,10 +484,14 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -492,14 +523,14 @@ github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQ github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= -github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -544,8 +575,6 @@ github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.8 h1:zICRlc+C1XzivLc3nzE+cbJV4LIi8tib6YG0MqC6OqA= -github.com/opencontainers/runc v1.1.8/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -559,6 +588,7 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -596,13 +626,14 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -610,7 +641,6 @@ github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiB github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= @@ -618,6 +648,7 @@ github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetS github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= @@ -633,6 +664,7 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -647,8 +679,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -659,8 +692,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -711,6 +744,22 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0 h1:6pu8ttx76BxHf+xz/H77AUZkPF3cwWzXqAUsXhVKI18= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0/go.mod h1:IOmXxPrxoxFMXdNy7lfDmE8MzE61YPcurbUm0SMjerI= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= +go.opentelemetry.io/otel/sdk v1.18.0 h1:e3bAB0wB3MljH38sHzpV/qWrOTCFrdZF2ct9F8rBkcY= +go.opentelemetry.io/otel/sdk v1.18.0/go.mod h1:1RCygWV7plY2KmdskZEDDBs4tJeHG92MdHZIluiYs/M= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -741,8 +790,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -813,8 +862,8 @@ golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -832,6 +881,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -906,13 +957,15 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -923,13 +976,15 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -981,7 +1036,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1023,8 +1077,10 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1039,7 +1095,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= +google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1061,6 +1118,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1085,8 +1144,10 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/engine/internal/embeddedui/embedded_ui.go b/engine/internal/embeddedui/embedded_ui.go index 2fae98ff..d678aab1 100644 --- a/engine/internal/embeddedui/embedded_ui.go +++ b/engine/internal/embeddedui/embedded_ui.go @@ -13,7 +13,6 @@ import ( "strconv" "time" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" @@ -133,7 +132,7 @@ func (ui *UIManager) Run(ctx context.Context) error { return fmt.Errorf("failed to connect UI container to the internal Docker network: %w", err) } - if err := ui.docker.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { + if err := ui.docker.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil { return fmt.Errorf("failed to start container %q: %w", containerID, err) } diff --git a/engine/internal/embeddedui/embedded_ui_integration_test.go b/engine/internal/embeddedui/embedded_ui_integration_test.go index 2df49cb4..f11a24d1 100644 --- a/engine/internal/embeddedui/embedded_ui_integration_test.go +++ b/engine/internal/embeddedui/embedded_ui_integration_test.go @@ -35,7 +35,7 @@ func TestStartExistingContainer(t *testing.T) { embeddedUI := New( Config{ // "mock" UI image - DockerImage: "gcr.io/google_containers/pause-amd64:3.0", + DockerImage: "alpine:3.19", }, engProps, runners.NewLocalRunner(false), diff --git a/engine/internal/provision/docker/docker.go b/engine/internal/provision/docker/docker.go index d1cc4585..e537e8b7 100644 --- a/engine/internal/provision/docker/docker.go +++ b/engine/internal/provision/docker/docker.go @@ -221,7 +221,7 @@ func RemoveContainer(r runners.Runner, cloneName string) (string, error) { // ListContainers lists container names. func ListContainers(r runners.Runner, clonePool string) ([]string, error) { - dockerListCmd := fmt.Sprintf(`docker container ls --filter "label=%s" --filter "label=%s" --all --format '{{.Names}}'`, + dockerListCmd := fmt.Sprintf(`docker container ls --filter "label=%s=%s" --all --format '{{.Names}}'`, LabelClone, clonePool) out, err := r.Run(dockerListCmd, false) diff --git a/engine/internal/provision/mode_local.go b/engine/internal/provision/mode_local.go index 54f4b3d8..9bbb9243 100644 --- a/engine/internal/provision/mode_local.go +++ b/engine/internal/provision/mode_local.go @@ -20,7 +20,7 @@ import ( "sync/atomic" "time" - "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" "github.com/pkg/errors" @@ -514,6 +514,7 @@ func (p *Provisioner) allocatePort() (uint, error) { if err := p.portChecker.checkPortAvailability(host, port); err != nil { log.Msg(fmt.Sprintf("port %d is not available: %v", port, err)) + attempts++ continue @@ -803,7 +804,7 @@ func (p *Provisioner) ReconnectClone(ctx context.Context, cloneName string) erro // StartCloneContainer starts clone container. func (p *Provisioner) StartCloneContainer(ctx context.Context, containerName string) error { - return p.dockerClient.ContainerStart(ctx, containerName, types.ContainerStartOptions{}) + return p.dockerClient.ContainerStart(ctx, containerName, container.StartOptions{}) } // DetectDBVersion detects version of the database. diff --git a/engine/internal/retrieval/engine/postgres/logical/dump.go b/engine/internal/retrieval/engine/postgres/logical/dump.go index 4250363d..5f36b9f5 100644 --- a/engine/internal/retrieval/engine/postgres/logical/dump.go +++ b/engine/internal/retrieval/engine/postgres/logical/dump.go @@ -321,7 +321,7 @@ func (d *DumpJob) Run(ctx context.Context) (err error) { log.Msg(fmt.Sprintf("Running container: %s. ID: %v", d.dumpContainerName(), containerID)) - if err := d.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { + if err := d.dockerClient.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil { collectDiagnostics(ctx, d.dockerClient, d.dumpContainerName(), dataDir) return errors.Wrapf(err, "failed to start container %q", d.dumpContainerName()) } diff --git a/engine/internal/retrieval/engine/postgres/logical/restore.go b/engine/internal/retrieval/engine/postgres/logical/restore.go index a56dc5ce..43b24440 100644 --- a/engine/internal/retrieval/engine/postgres/logical/restore.go +++ b/engine/internal/retrieval/engine/postgres/logical/restore.go @@ -245,7 +245,7 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) { log.Msg(fmt.Sprintf("Running container: %s. ID: %v", r.restoreContainerName(), containerID)) - if err := r.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { + if err := r.dockerClient.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil { return errors.Wrapf(err, "failed to start container %q", r.restoreContainerName()) } diff --git a/engine/internal/retrieval/engine/postgres/physical/physical.go b/engine/internal/retrieval/engine/postgres/physical/physical.go index 4d95ab46..2bfe97e4 100644 --- a/engine/internal/retrieval/engine/postgres/physical/physical.go +++ b/engine/internal/retrieval/engine/postgres/physical/physical.go @@ -229,7 +229,7 @@ func (r *RestoreJob) Run(ctx context.Context) (err error) { log.Msg(fmt.Sprintf("Running container: %s. ID: %v", r.restoreContainerName(), contID)) - if err = r.dockerClient.ContainerStart(ctx, contID, types.ContainerStartOptions{}); err != nil { + if err = r.dockerClient.ContainerStart(ctx, contID, container.StartOptions{}); err != nil { return errors.Wrapf(err, "failed to start container: %v", contID) } @@ -307,7 +307,7 @@ func (r *RestoreJob) startContainer(ctx context.Context, containerName string, c return "", fmt.Errorf("failed to create container %q %w", containerName, err) } - if err = r.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { + if err = r.dockerClient.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil { return "", errors.Wrapf(err, "failed to start container %s", containerName) } diff --git a/engine/internal/retrieval/engine/postgres/snapshot/logical.go b/engine/internal/retrieval/engine/postgres/snapshot/logical.go index 1be78d7e..c596d6d5 100644 --- a/engine/internal/retrieval/engine/postgres/snapshot/logical.go +++ b/engine/internal/retrieval/engine/postgres/snapshot/logical.go @@ -11,7 +11,6 @@ import ( "path" "time" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" @@ -248,7 +247,7 @@ func (s *LogicalInitial) runPreprocessingQueries(ctx context.Context, dataDir st log.Msg(fmt.Sprintf("Running container: %s. ID: %v", s.patchContainerName(), containerID)) - if err := s.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { + if err := s.dockerClient.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil { return errors.Wrap(err, "failed to start container") } diff --git a/engine/internal/retrieval/engine/postgres/snapshot/physical.go b/engine/internal/retrieval/engine/postgres/snapshot/physical.go index c13fb9fc..3a089a9a 100644 --- a/engine/internal/retrieval/engine/postgres/snapshot/physical.go +++ b/engine/internal/retrieval/engine/postgres/snapshot/physical.go @@ -568,7 +568,7 @@ func (p *PhysicalInitial) promoteInstance(ctx context.Context, clonePath string, if syState.Err != nil { recoveryConfig = buildRecoveryConfig(recoveryFileConfig, p.options.Promotion.Recovery) - if err := cfgManager.ApplyRecovery(recoveryFileConfig); err != nil { + if err := cfgManager.ApplyRecovery(recoveryConfig); err != nil { return errors.Wrap(err, "failed to apply recovery configuration") } } else if err := cfgManager.RemoveRecoveryConfig(); err != nil { @@ -628,7 +628,7 @@ func (p *PhysicalInitial) promoteInstance(ctx context.Context, clonePath string, log.Msg(fmt.Sprintf("Running container: %s. ID: %v", p.promoteContainerName(), containerID)) - if err := p.dockerClient.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { + if err := p.dockerClient.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil { return errors.Wrap(err, "failed to start container") } diff --git a/engine/internal/retrieval/engine/postgres/tools/cont/container.go b/engine/internal/retrieval/engine/postgres/tools/cont/container.go index 5baca962..a5d59a0c 100644 --- a/engine/internal/retrieval/engine/postgres/tools/cont/container.go +++ b/engine/internal/retrieval/engine/postgres/tools/cont/container.go @@ -104,7 +104,7 @@ func StopControlContainers(ctx context.Context, dockerClient *client.Client, dbC log.Msg("Removing control container:", containerName) - if err := dockerClient.ContainerRemove(ctx, controlCont.ID, types.ContainerRemoveOptions{ + if err := dockerClient.ContainerRemove(ctx, controlCont.ID, container.RemoveOptions{ RemoveVolumes: true, Force: true, }); err != nil { @@ -141,7 +141,7 @@ func cleanUpContainers(ctx context.Context, dockerCli *client.Client, instanceID for _, controlCont := range list { log.Msg("Removing container:", getContainerName(controlCont)) - if err := dockerCli.ContainerRemove(ctx, controlCont.ID, types.ContainerRemoveOptions{ + if err := dockerCli.ContainerRemove(ctx, controlCont.ID, container.RemoveOptions{ RemoveVolumes: true, Force: true, }); err != nil { @@ -160,7 +160,7 @@ func getContainerList(ctx context.Context, d *client.Client, instanceID string, }, }, pairs...) - return d.ContainerList(ctx, types.ContainerListOptions{ + return d.ContainerList(ctx, container.ListOptions{ Filters: filters.NewArgs(filterPairs...), }) } diff --git a/engine/internal/retrieval/engine/postgres/tools/db/image_content.go b/engine/internal/retrieval/engine/postgres/tools/db/image_content.go index 1f43342e..a66762c6 100644 --- a/engine/internal/retrieval/engine/postgres/tools/db/image_content.go +++ b/engine/internal/retrieval/engine/postgres/tools/db/image_content.go @@ -207,7 +207,7 @@ func createContainer(ctx context.Context, docker *client.Client, image string, p log.Msg(fmt.Sprintf("Running container: %s. ID: %v", containerName, containerID)) - if err := docker.ContainerStart(ctx, containerID, types.ContainerStartOptions{}); err != nil { + if err := docker.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil { return "", fmt.Errorf("failed to start container %q: %w", containerName, err) } diff --git a/engine/internal/retrieval/engine/postgres/tools/tools.go b/engine/internal/retrieval/engine/postgres/tools/tools.go index 6b196b62..4a0bbe2d 100644 --- a/engine/internal/retrieval/engine/postgres/tools/tools.go +++ b/engine/internal/retrieval/engine/postgres/tools/tools.go @@ -378,7 +378,7 @@ func CheckContainerReadiness(ctx context.Context, dockerClient *client.Client, c // PrintContainerLogs prints container output. func PrintContainerLogs(ctx context.Context, dockerClient *client.Client, containerID string) { - logs, err := dockerClient.ContainerLogs(ctx, containerID, types.ContainerLogsOptions{ + logs, err := dockerClient.ContainerLogs(ctx, containerID, container.LogsOptions{ Since: essentialLogsInterval, ShowStdout: true, ShowStderr: true, @@ -461,7 +461,7 @@ func RemoveContainer(ctx context.Context, dockerClient *client.Client, container log.Msg(fmt.Sprintf("Container %q has been stopped", containerID)) - if err := dockerClient.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{ + if err := dockerClient.ContainerRemove(ctx, containerID, container.RemoveOptions{ RemoveVolumes: true, Force: true, }); err != nil { @@ -638,7 +638,7 @@ func CreateContainerIfMissing(ctx context.Context, docker *client.Client, contai // ListContainersByLabel lists containers by label name and value. func ListContainersByLabel(ctx context.Context, docker *client.Client, filterArgs filters.Args) ([]string, error) { list, err := docker.ContainerList(ctx, - types.ContainerListOptions{ + container.ListOptions{ All: true, Filters: filterArgs, }) @@ -658,7 +658,7 @@ func ListContainersByLabel(ctx context.Context, docker *client.Client, filterArg // CopyContainerLogs collects container logs. func CopyContainerLogs(ctx context.Context, docker *client.Client, containerName, filePath string) error { - reader, err := docker.ContainerLogs(ctx, containerName, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Timestamps: true}) + reader, err := docker.ContainerLogs(ctx, containerName, container.LogsOptions{ShowStdout: true, ShowStderr: true, Timestamps: true}) if err != nil { return err diff --git a/engine/internal/runci/handlers.go b/engine/internal/runci/handlers.go index 8d12dc61..120b795a 100644 --- a/engine/internal/runci/handlers.go +++ b/engine/internal/runci/handlers.go @@ -203,7 +203,7 @@ func (s *Server) runCommands(ctx context.Context, clone *models.Clone, runID str log.Msg(fmt.Sprintf("Running container: %s. ID: %v", containerName, contRunner.ID)) - if err := s.docker.ContainerStart(ctx, contRunner.ID, types.ContainerStartOptions{}); err != nil { + if err := s.docker.ContainerStart(ctx, contRunner.ID, container.StartOptions{}); err != nil { return nil, errors.Wrapf(err, "failed to start container %q", containerName) } diff --git a/engine/internal/srv/server.go b/engine/internal/srv/server.go index e86d3232..04525053 100644 --- a/engine/internal/srv/server.go +++ b/engine/internal/srv/server.go @@ -187,7 +187,7 @@ func (s *Server) Reload(cfg srvCfg.Config) { // InitHandlers initializes handler functions of the HTTP server. func (s *Server) InitHandlers() { - r := mux.NewRouter().StrictSlash(true) + r := mux.NewRouter().StrictSlash(true).UseEncodedPath() authMW := mw.NewAuth(s.Config.VerificationToken, s.Platform) diff --git a/engine/internal/srv/ws.go b/engine/internal/srv/ws.go index 9b274c51..64f58211 100644 --- a/engine/internal/srv/ws.go +++ b/engine/internal/srv/ws.go @@ -7,7 +7,7 @@ import ( "net/http" "github.com/ahmetalpbalkan/dlog" - dockTypes "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" "github.com/gorilla/websocket" "gitlab.com/postgres-ai/database-lab/v3/internal/srv/api" @@ -68,7 +68,7 @@ func (s *Server) instanceLogs(w http.ResponseWriter, r *http.Request) { } }() - readCloser, err := s.docker.ContainerLogs(r.Context(), s.engProps.ContainerName, dockTypes.ContainerLogsOptions{ + readCloser, err := s.docker.ContainerLogs(r.Context(), s.engProps.ContainerName, container.LogsOptions{ ShowStdout: true, ShowStderr: true, Since: logsSinceInterval, diff --git a/engine/internal/validator/validator.go b/engine/internal/validator/validator.go index 6e50f0ef..87656c2b 100644 --- a/engine/internal/validator/validator.go +++ b/engine/internal/validator/validator.go @@ -7,6 +7,7 @@ package validator import ( "fmt" + "strings" "github.com/pkg/errors" passwordvalidator "github.com/wagslane/go-password-validator" @@ -34,6 +35,10 @@ func (v Service) ValidateCloneRequest(cloneRequest *types.CloneCreateRequest) er return errors.New("missing DB password") } + if cloneRequest.ID != "" && strings.Contains(cloneRequest.ID, "/") { + return errors.New("Clone ID cannot contain slash ('/'). Please choose another ID") + } + if err := passwordvalidator.Validate(cloneRequest.DB.Password, minEntropyBits); err != nil { return fmt.Errorf("password validation: %w", err) } diff --git a/engine/internal/validator/validator_test.go b/engine/internal/validator/validator_test.go index 03186875..a510319e 100644 --- a/engine/internal/validator/validator_test.go +++ b/engine/internal/validator/validator_test.go @@ -55,6 +55,13 @@ func TestValidationCloneRequestErrors(t *testing.T) { createRequest: types.CloneCreateRequest{DB: &types.DatabaseRequest{Password: "password"}}, error: "missing DB username", }, + { + createRequest: types.CloneCreateRequest{ + DB: &types.DatabaseRequest{Username: "user", Password: "password"}, + ID: "test/ID", + }, + error: "Clone ID cannot contain slash ('/'). Please choose another ID", + }, } for _, tc := range testCases { diff --git a/engine/pkg/util/projection/operations.go b/engine/pkg/util/projection/operations.go index db12ac88..7e966b7b 100644 --- a/engine/pkg/util/projection/operations.go +++ b/engine/pkg/util/projection/operations.go @@ -35,6 +35,7 @@ func Load(target interface{}, accessor Accessor, options LoadOptions) error { } else { field.Set(reflect.ValueOf(accessorValue)) } + return nil }, ) @@ -46,22 +47,28 @@ func Store(target interface{}, accessor Accessor, options StoreOptions) error { if !tag.matchesStore(options) { return nil } + var accessorValue interface{} + if tag.isPtr { if field.IsNil() { return nil } + accessorValue = field.Elem().Interface() } else { accessorValue = field.Interface() } + err := accessor.Set(FieldSet{ Path: tag.path, Value: accessorValue, Type: tag.fType, CreateKey: tag.createKey, }) + if err != nil { return err } + return nil }, ) diff --git a/engine/test/1.synthetic.sh b/engine/test/1.synthetic.sh index 92d2f167..5470eb51 100644 --- a/engine/test/1.synthetic.sh +++ b/engine/test/1.synthetic.sh @@ -45,8 +45,6 @@ for i in {1..300}; do sleep 1 done -check_database_readiness || (echo "test database is not ready" && exit 1) - # Restart container explicitly after initdb to make sure that the server will not receive a shutdown request and queries will not be interrupted. sudo docker restart dblab_pg_initdb @@ -55,8 +53,6 @@ for i in {1..300}; do sleep 1 done -check_database_readiness || (echo "test database is not ready" && exit 1) - # Create the test database sudo docker exec dblab_pg_initdb psql -U postgres -c 'create database test' diff --git a/engine/test/2.logical_generic.sh b/engine/test/2.logical_generic.sh index 73b5f2aa..eb185c7a 100644 --- a/engine/test/2.logical_generic.sh +++ b/engine/test/2.logical_generic.sh @@ -51,8 +51,6 @@ if [[ "${SOURCE_HOST}" = "172.17.0.1" ]]; then sleep 1 done - check_database_readiness || (echo "test database is not ready" && exit 1) - check_data_existence(){ sudo docker exec postgres"${POSTGRES_VERSION}" psql -d "${SOURCE_DBNAME}" -U postgres --command 'select from pgbench_accounts' > /dev/null 2>&1 return $? diff --git a/engine/test/4.physical_basebackup.sh b/engine/test/4.physical_basebackup.sh index 2af38d5f..f72508b5 100644 --- a/engine/test/4.physical_basebackup.sh +++ b/engine/test/4.physical_basebackup.sh @@ -50,8 +50,6 @@ if [[ "${SOURCE_HOST}" = "172.17.0.1" ]]; then sleep 1 done - check_database_readiness || (echo "test database is not ready" && exit 1) - # add "host replication" to pg_hba.conf sudo docker exec postgres"${POSTGRES_VERSION}" bash -c 'echo "host replication all 0.0.0.0/0 md5" >> $PGDATA/pg_hba.conf' # reload conf diff --git a/engine/test/_cleanup.sh b/engine/test/_cleanup.sh index b9c234a1..6fb304a7 100644 --- a/engine/test/_cleanup.sh +++ b/engine/test/_cleanup.sh @@ -6,14 +6,18 @@ DLE_TEST_POOL_NAME="test_dblab_pool" ZFS_FILE="$(pwd)/zfs_file" # Stop and remove test Docker containers -sudo docker ps -aq --filter label="test_dblab_pool" | xargs --no-run-if-empty sudo docker rm -f -sudo docker ps -aq --filter label="dblab_test" | xargs --no-run-if-empty sudo docker rm -f +sudo docker ps -aq --filter label="test_dblab_pool" | xargs --no-run-if-empty sudo docker rm -f \ + || echo "Failed to remove test Docker containers, continuing..." +sudo docker ps -aq --filter label="dblab_test" | xargs --no-run-if-empty sudo docker rm -f \ + || echo "Failed to remove dblab_test Docker containers, continuing..." # Remove unused Docker images -sudo docker images --filter=reference='registry.gitlab.com/postgres-ai/database-lab/dblab-server:*' -q | xargs --no-run-if-empty sudo docker rmi || echo "Docker image removal finished with errors but it is OK to ignore them." +sudo docker images --filter=reference='registry.gitlab.com/postgres-ai/database-lab/dblab-server:*' -q | xargs --no-run-if-empty sudo docker rmi \ + || echo "Docker image removal finished with errors but it is OK to ignore them." # Clean up data directory -sudo rm -rf ${DLE_TEST_MOUNT_DIR}/${DLE_TEST_POOL_NAME}/data/* +sudo rm -rf ${DLE_TEST_MOUNT_DIR}/${DLE_TEST_POOL_NAME}/data/* \ + || echo "Data directory cleanup finished with errors but continuing..." # Remove dump directory sudo umount ${DLE_TEST_MOUNT_DIR}/${DLE_TEST_POOL_NAME}/dump \ @@ -30,7 +34,9 @@ sudo zpool destroy test_dblab_pool \ || echo "Destroying ZFS storage pool finished with errors but it is OK to ignore them." # Remove ZFS FILE -sudo rm -f "${ZFS_FILE}" +sudo rm -f "${ZFS_FILE}" \ + || echo "Failed to remove ZFS file, but continuing..." # Remove CLI configuration -dblab config remove test || { echo "Cannot remove CLI configuration but this was optional (ignore the error)."; } +dblab config remove test \ + || echo "Removing CLI configuration finished with errors but it is OK to ignore them." diff --git a/translations/README.german.md b/translations/README.german.md index 069a46b1..4900b14c 100644 --- a/translations/README.german.md +++ b/translations/README.german.md @@ -80,7 +80,7 @@ Weiterlesen: - Blitzschnelles Klonen von Postgres-Datenbanken. Es wird ein paar Sekunden gebraucht, um einen neuen Klon zu erstellen, der bereit ist, Verbindungen und Abfragen zu akzeptieren, unabhängig von der Datenbankgröße. - Die theoretische maximale Anzahl von Snapshots und Klonen beträgt 264 ([ZFS](https://en.wikipedia.org/wiki/ZFS), Standard). - Theoretische maximale Größe des PostgreSQL-Datenverzeichnisses: 256 Billiarden Zebibyte oder 2128 Byte ([ZFS](https://en.wikipedia.org/wiki/ZFS), Standard). -- Unterstützte Hauptversionen von PostgreSQL: 9.6–14. +- Unterstützte Hauptversionen von PostgreSQL: 9.6–17. - Zwei Technologien werden unterstützt, um Thin Cloning zu ermöglichen ([CoW](https://en.wikipedia.org/wiki/Copy-on-write)): [ZFS](https://en.wikipedia.org/wiki/ ZFS) und [LVM](https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)). - Alle Komponenten sind in Docker-Containern verpackt. - UI macht die manuelle Arbeit bequemer. diff --git a/translations/README.portuguese-br.md b/translations/README.portuguese-br.md index 2e68bf8a..1ce67592 100644 --- a/translations/README.portuguese-br.md +++ b/translations/README.portuguese-br.md @@ -80,7 +80,7 @@ Leia mais: - Clonagem the bancos de dados Postgres ultrarrápidos - apenas alguns segundos para criar um novo clone pronto para aceitar conexões e queries, independentemente do tamanho do banco de dados. - O número máximo teórico de snapshots e clones é 264 ([ZFS](https://en.wikipedia.org/wiki/ZFS), default). - O número máximo teórico de do diretório de dados do PostgreSQL: 256 quatrilhões zebibytes, ou 2128 bytes ([ZFS](https://en.wikipedia.org/wiki/ZFS), default). -- Versões _major_ do PostgreSQL suportadas: 9.6–14. +- Versões _major_ do PostgreSQL suportadas: 9.6–17. - Duas tecnologias são suportadas para viabilizar o thin cloning ([CoW](https://en.wikipedia.org/wiki/Copy-on-write)): [ZFS](https://en.wikipedia.org/wiki/ZFS) e [LVM](https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)). - Todos os componentes estão empacotados em docker containers. - UI para tornar o trabalho manual mais conveniente. diff --git a/translations/README.russian.md b/translations/README.russian.md index 61d71325..8a4925d8 100644 --- a/translations/README.russian.md +++ b/translations/README.russian.md @@ -81,7 +81,7 @@ - Молниеносное клонирование БД Postgres - создание нового клона, готового к работе, всего за несколько секунд (вне зависимости от размера БД). - Максимальное теоретическое количество снимков: 264. ([ZFS](https://en.wikipedia.org/wiki/ZFS), вариант по умолчанию). - Максимальный теоретический размер директории данных PostgreSQL: 256 квадриллионов зебибайт или 2128 байт ([ZFS](https://en.wikipedia.org/wiki/ZFS), вариант по умолчанию). -- Поддерживаются все основные версии PostgreSQL: 9.6-14. +- Поддерживаются все основные версии PostgreSQL: 9.6-17. - Для реализации тонкого клонирования поддерживаются две технологии ([CoW](https://en.wikipedia.org/wiki/Copy-on-write)): [ZFS](https://en.wikipedia.org/wiki/ZFS) и [LVM](https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)). - Все компоненты работают в Docker-контейнерах. - UI для удобства ручных действий пользователя. diff --git a/translations/README.spanish.md b/translations/README.spanish.md index 62ddc4bd..903dca3e 100644 --- a/translations/README.spanish.md +++ b/translations/README.spanish.md @@ -80,7 +80,7 @@ Lee más: - Clonación ultrarrápida de bases de datos de Postgres: unos segundos para crear un nuevo clon listo para aceptar conexiones y consultas, independientemente del tamaño de la base de datos. - El número máximo teórico de instantáneas y clones es 264 ([ZFS](https://en.wikipedia.org/wiki/ZFS), predeterminado). - El tamaño máximo teórico del directorio de datos de PostgreSQL: 256 cuatrillones de zebibytes, o 2128 bytes ([ZFS](https://en.wikipedia.org/wiki/ZFS), predeterminado). -- Versiones principales de PostgreSQL admitidas: 9.6–14. +- Versiones principales de PostgreSQL admitidas: 9.6–17. - Se admiten dos tecnologías para permitir la clonación ligera ([CoW](https://en.wikipedia.org/wiki/Copy-on-write)): [ZFS](https://en.wikipedia.org/wiki/ZFS) y [LVM](https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)). - Todos los componentes están empaquetados en contenedores Docker. - Interfaz de usuario para que el trabajo manual sea más conveniente. diff --git a/translations/README.ukrainian.md b/translations/README.ukrainian.md index a7f6b682..402fec8e 100644 --- a/translations/README.ukrainian.md +++ b/translations/README.ukrainian.md @@ -81,7 +81,7 @@ - блискавичне клонування БД Postgres - створення нового клону, готового до роботи, всього за кілька секунд (незалежно від розміру БД). - Максимальна теоретична кількість знімків: 264. ([ZFS](https://en.wikipedia.org/wiki/ZFS), варіант за замовчуванням). - Максимальний теоретичний розмір директорії даних PostgreSQL: 256 квадрильйонів зебібайт або 2128 байт ([ZFS](https://en.wikipedia.org/wiki/ZFS), варіант за замовчуванням). -- Підтримуються усі основні версії PostgreSQL: 9.6-14. +- Підтримуються усі основні версії PostgreSQL: 9.6-17. - Для реалізації тонкого клонування підтримуються дві технології ([CoW](https://en.wikipedia.org/wiki/Copy-on-write)): [ZFS](https://en.wikipedia.org/wiki/ZFS ) та [LVM](https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)). - Усі компоненти працюють у Docker-контейнерах. - UI для зручності ручних дій користувача. diff --git a/ui/.dockerignore b/ui/.dockerignore index 88026b98..7e3cab0d 100644 --- a/ui/.dockerignore +++ b/ui/.dockerignore @@ -7,4 +7,3 @@ ui/node_modules/ ui/packages/ce/node_modules/ ui/packages/shared/node_modules/ -ui/packages/platform/node_modules/ diff --git a/ui/.gitlab-ci.yml b/ui/.gitlab-ci.yml index 06560ad5..71774968 100644 --- a/ui/.gitlab-ci.yml +++ b/ui/.gitlab-ci.yml @@ -1,6 +1,6 @@ include: - local: 'ui/packages/ce/.gitlab-ci.yml' - - local: 'ui/packages/platform/.gitlab-ci.yml' + - local: 'ui/packages/shared/.gitlab-ci.yml' .ui_checks: &ui_checks rules: @@ -31,7 +31,6 @@ check-code-style: script: - pnpm --dir ui/ i - pnpm --dir ui/ --filter @postgres.ai/ce lint - - pnpm --dir ui/ --filter @postgres.ai/platform lint interruptible: true cache: <<: *cache @@ -65,13 +64,14 @@ e2e-ce-ui-test: variables: CYPRESS_CACHE_FOLDER: '$CI_PROJECT_DIR/cache/Cypress' before_script: - - apt update && apt install curl - - apt install -y libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb + - apt update + - apt install -y curl libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb - npm install -g wait-on - npm install -g pnpm # TODO: Set up caching. # - pnpm config set store-dir /builds/postgres-ai/database-lab/.pnpm-store/ script: - pnpm --dir ui/ i --no-frozen-lockfile + - pnpm --dir ui/ --filter @postgres.ai/ce exec cypress install - pnpm --dir ui/ --filter @postgres.ai/ce start & wait-on http://localhost:3001 - pnpm --dir ui/ --filter @postgres.ai/ce cy:run diff --git a/ui/README.md b/ui/README.md index 6b214cf6..53d9a17d 100644 --- a/ui/README.md +++ b/ui/README.md @@ -1,6 +1,6 @@ -# Database Lab Engine and Database Lab Engine UI +# Database Lab Engine UI -## Database Lab - thin database clones for faster development +## DBLab - thin database clones and database branching for faster development _Proceed to [Database Lab Engine repository](https://gitlab.com/postgres-ai/database-lab) for more information about technology itself._ Database Lab Engine (DLE) is an open-source (Apache 2.0) technology that allows blazing-fast cloning of Postgres databases of any size in seconds. This helps solve many problems such as: @@ -15,7 +15,6 @@ As an example, cloning a 10 TiB PostgreSQL database can take less than 2 seconds ### List packages: -- `@postgres.ai/platform` - platform version of UI - `@postgres.ai/ce` - community edition version of UI - `@postgres.ai/shared` - common modules @@ -27,35 +26,17 @@ At the root: - ` -w ` - for specific package #### Examples - - `npm ci -ws` - install deps of all packages - `npm run build -ws` - build all packages -- `npm run start -w @postgres.ai/platform` - run platform UI locally in dev mode - `npm run start -w @postgres.ai/ce` - run community edition UI locally in dev mode _Important note: don't use commands for `@postgres.ai/shared` - it's dependent package, which can't be running or built_ -### How to start "platform" - -- `cd ui` -- `npm ci -ws` - install dependencies, must be done once to install dependencies for all packages -- `source packages/platform/deploy/configs/production.sh` - set up environment variables, should be run for each new terminal session -- `npm run start -w @postgres.ai/platform` - start dev server -- To sign in locally - sign in on [console.postgres.ai](https://console.postgres.ai) and copy `token` from Local Storage to your localhost's Local Storage - ### How to start "ce" - - `cd ui` - `npm ci -ws` - install dependencies, must be done once to install dependencies for all packages - `npm run start -w @postgres.ai/ce` - start dev server -### How to build "platform" - -- `cd ui` -- `npm ci -ws` - install dependencies, must be done once to install dependencies for all packages -- `source packages/platform/deploy/configs/production.sh` - set up environment variables, should be run for each new terminal session -- `npm run build -w @postgres.ai/platform` - ### How to build "ce" - `cd ui` @@ -92,5 +73,4 @@ Ways to resolve (ordered by preference): ## Moving to Typescript - `@postgres.ai/shared` is written on Typescript - `@postgres.ai/ce` is written on Typescript -- `@postgres.ai/platform` is written on JavaScript and patially on Typescript. The target - is moving `@postgres.ai/platform` to Typescript fully. It should takes approximately 120-160 hours. - There are potential problems with typing - old versions of packages may don't have their typings. Recommended to update them or replace. If it's impossible you can write your own typing in file named like `.d.ts` inside `src` directory of the selected package. diff --git a/ui/cspell.json b/ui/cspell.json index 4ef29a5a..64382e04 100644 --- a/ui/cspell.json +++ b/ui/cspell.json @@ -186,6 +186,24 @@ "pgnode", "pgbackrest", "vitabaks", - "distro" + "distro", + "pgaudit", + "pgrouting", + "timescaledb", + "citus", + "pgvector", + "partman", + "fstype", + "pgsql", + "sqlalchemy", + "tsql", + "TSQL", + "sparql", + "SPARQL", + "subtransactions", + "mbox", + "SIEM", + "toolcall", + "thinkblock" ] } diff --git a/ui/package.json b/ui/package.json index d6b63711..9e92dbc2 100644 --- a/ui/package.json +++ b/ui/package.json @@ -7,7 +7,48 @@ }, "scripts": { "preinstall": "npx only-allow pnpm", - "start:platform": "source ./packages/platform/deploy/configs/production.sh && npm run start -w @postgres.ai/platform", "start:ce": "npm run start -w @postgres.ai/ce" + }, + "pnpm": { + "overrides": { + "babel-loader@<9.1.3": ">=9.1.3", + "d3-color@<3.1.0": ">=3.1.0", + "node-forge@<1.3.0": ">=1.3.0", + "terser@>=5.0.0 <5.14.2": ">=5.14.2", + "loader-utils@<1.4.1": ">=1.4.1", + "loader-utils@>=2.0.0 <2.0.3": ">=2.0.3", + "webpack@>=5.0.0 <5.76.0": ">=5.76.0", + "postcss@<8.4.38": ">=8.4.38", + "postcss-scss@<4.0.9": ">=4.0.9", + "resolve-url-loader@<5.0.0": ">=5.0.0", + "loader-utils@>=3.0.0 <3.2.1": ">=3.2.1", + "loader-utils@>=2.0.0 <2.0.4": ">=2.0.4", + "loader-utils@>=1.0.0 <1.4.2": ">=1.4.2", + "moment@>=2.18.0 <2.29.4": ">=2.29.4", + "moment@<2.29.2": ">=2.29.2", + "word-wrap@<1.2.4": ">=1.2.4", + "nth-check@<2.0.1": ">=2.0.1", + "follow-redirects@<1.15.4": ">=1.15.4", + "qs@>=6.7.0 <6.7.3": ">=6.7.3", + "async@>=2.0.0 <2.6.4": ">=2.6.4", + "semver@>=7.0.0 <7.5.2": ">=7.5.2", + "semver@<5.7.2": ">=5.7.2", + "semver@>=6.0.0 <6.3.1": ">=6.3.1", + "minimatch@<3.0.5": ">=3.0.5", + "json5@<1.0.2": ">=1.0.2", + "json5@>=2.0.0 <2.2.2": ">=2.2.2", + "ip@<1.1.9": ">=1.1.9", + "browserify-sign@>=2.6.0 <=4.2.1": ">=4.2.2", + "@cypress/request@<=2.88.12": ">=3.0.0", + "webpack-dev-middleware@<=5.3.3": ">=5.3.4", + "express@<4.19.2": ">=4.19.2", + "follow-redirects@<=1.15.5": ">=1.15.6", + "@babel/traverse@<7.23.2": ">=7.23.2", + "bootstrap@>=4.0.0 <=4.6.2": ">=5.0.0", + "elliptic@>=4.0.0 <=6.5.6": ">=6.5.7", + "elliptic@>=2.0.0 <=6.5.6": ">=6.5.7", + "elliptic@>=5.2.1 <=6.5.6": ">=6.5.7", + "dompurify@<2.5.4": ">=2.5.4" + } } } diff --git a/ui/packages/ce/.dockerignore b/ui/packages/ce/.dockerignore index 19d960ff..00dbf44f 100644 --- a/ui/packages/ce/.dockerignore +++ b/ui/packages/ce/.dockerignore @@ -6,5 +6,4 @@ **/build/** /ui/node_modules/ /ui/packages/ce/node_modules/ -/ui/packages/shared/node_modules/ -/ui/packages/platform/node_modules/ +/ui/packages/shared/node_modules/ \ No newline at end of file diff --git a/ui/packages/ce/cypress/e2e/tabs.cy.js b/ui/packages/ce/cypress/e2e/tabs.cy.js index 77d8e082..d9c6dc59 100644 --- a/ui/packages/ce/cypress/e2e/tabs.cy.js +++ b/ui/packages/ce/cypress/e2e/tabs.cy.js @@ -1,22 +1,89 @@ /* eslint-disable no-undef */ -describe('Instance page should have "Configuration" tab with content', () => { - it('should have token in local storage', () => { - cy.window().then((win) => { - if (!win.localStorage.getItem('token')) { - win.localStorage.setItem('token', 'demo-token') - } - }) +Cypress.on('uncaught:exception', () => { + return false +}) + +// Function to set up intercepts for the requests +function setupIntercepts() { + const exceptions = [ + '/healthz', + '/instance/retrieval', + '/status', + '/admin/config', + ] + + cy.intercept('GET', '/healthz*', { + statusCode: 200, + body: { + edition: 'standard', + }, + }) + + cy.intercept('GET', '/instance/retrieval*', { + statusCode: 200, + body: { + status: 'inactive', + }, }) - it('should have "Configuration" tab with content', () => { + + cy.intercept('GET', '/status*', { + statusCode: 200, + body: { + status: { + code: 'OK', + message: 'Instance is ready', + }, + pools: [], + cloning: { + clones: [], + }, + retrieving: { + status: 'inactive', + }, + }, + }) + + // Intercept all fetch requests and return a 200 + cy.intercept('GET', '*', (req) => { + if ( + req.resourceType === 'fetch' && + exceptions.every((e) => !req.url.includes(e)) + ) { + req.reply({ + statusCode: 200, + body: { + status: 'active', + }, + }) + } + }) +} + +describe('Configuration tab', () => { + // It should intercept the requests + beforeEach(() => { + setupIntercepts() + }) + + it('should have a "Configuration" tab', () => { cy.visit('/', { retryOnStatusCodeFailure: true, onLoad: () => { - cy.get('.MuiTabs-flexContainer').contains('Configuration') - cy.get('.MuiBox-root') - .contains('p') - .should('have.length.greaterThan', 0) + cy.get('.MuiTabs-flexContainer') + .contains('Configuration', { timeout: 10000 }) + .should('be.visible') + .click({ force: true }) }, }) }) + + it('should have form inputs in the "Configuration" tab', () => { + cy.get('.MuiTabs-flexContainer') + .contains('Configuration', { timeout: 10000 }) + .should('be.visible') + .click({ force: true }) + + cy.get('button[type="button"]').should('exist') + }) }) diff --git a/ui/packages/ce/package.json b/ui/packages/ce/package.json index b1d4d19c..b63ae9c0 100644 --- a/ui/packages/ce/package.json +++ b/ui/packages/ce/package.json @@ -1,6 +1,6 @@ { "name": "@postgres.ai/ce", - "version": "1.0.0", + "version": "3.5.0", "private": true, "dependencies": { "@craco/craco": "^6.4.3", @@ -90,5 +90,5 @@ "stylelint-config-standard-scss": "^2.0.1", "stylelint-prettier": "^2.0.0" }, - "proxy": "https://demo.aws.postgres.ai:446/api" + "proxy": "https://demo.dblab.dev:446" } diff --git a/ui/packages/ce/src/App/Menu/Header/icons/index.tsx b/ui/packages/ce/src/App/Menu/Header/icons/index.tsx index b694a5cf..04efcec1 100644 --- a/ui/packages/ce/src/App/Menu/Header/icons/index.tsx +++ b/ui/packages/ce/src/App/Menu/Header/icons/index.tsx @@ -83,7 +83,7 @@ export const StarsIcon = ({ className }: { className?: string }) => ( xmlns="http://www.w3.org/2000/svg" className={className} > - + ( > diff --git a/ui/packages/platform/.dockerignore b/ui/packages/platform/.dockerignore deleted file mode 100644 index 19d960ff..00000000 --- a/ui/packages/platform/.dockerignore +++ /dev/null @@ -1,10 +0,0 @@ -**/node_modules/** -/.vscode/ -/.idea/ -/bin/ -/.git/ -**/build/** -/ui/node_modules/ -/ui/packages/ce/node_modules/ -/ui/packages/shared/node_modules/ -/ui/packages/platform/node_modules/ diff --git a/ui/packages/platform/.eslintrc b/ui/packages/platform/.eslintrc deleted file mode 100644 index 4cc3d5ca..00000000 --- a/ui/packages/platform/.eslintrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "root": true, - "extends": "react-app", - "rules": { - "@typescript-eslint/no-explicit-any": "error" - } -} diff --git a/ui/packages/platform/.gitlab-ci.yml b/ui/packages/platform/.gitlab-ci.yml deleted file mode 100644 index fc8c5776..00000000 --- a/ui/packages/platform/.gitlab-ci.yml +++ /dev/null @@ -1,128 +0,0 @@ -#-------------------------------------------------------------------------- -# Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai -# All Rights Reserved -# Unauthorized copying of this file, via any medium is strictly prohibited -# Proprietary and confidential -#-------------------------------------------------------------------------- - -# Conditions. -.only_ui_tag_release: &only_ui_tag_release - rules: - - if: $CI_COMMIT_TAG =~ /^ui\/[0-9.]+$/ - -.only_ui_staging: &only_ui_staging - rules: - - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - changes: - - ui/packages/platform/**/* - - ui/packages/shared/**/* - -.only_ui_feature: &only_ui_feature - rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" - changes: - - ui/packages/platform/**/* - - ui/packages/shared/**/* - when: manual - -.ui_cache: &ui_cache - image: node:lts-alpine - cache: - key: "$CI_COMMIT_REF_SLUG" - paths: - - .pnpm-store - policy: pull - -# Environments. -.environment_production: &env_production - environment: - name: production - url: https://postgres.ai - variables: - ENV: production - NAMESPACE: production - DOCKER_NAME: "gcr.io/postgres-ai/platform-web/cloud" - before_script: - - export UI_VERSION=$(echo ${CI_COMMIT_TAG#"ui/"}) - - export TAG="${DOCKER_NAME}:${UI_VERSION}-${CI_PIPELINE_IID}" - -.environment_staging: &env_staging - environment: - name: staging - url: https://console-v2.postgres.ai - variables: - ENV: staging - NAMESPACE: staging - DOCKER_NAME: "gcr.io/postgres-ai/platform-web/cloud" - TAG: "${DOCKER_NAME}:${NAMESPACE}-${CI_PIPELINE_IID}" - -.environment_dev: &env_dev - environment: - name: dev - url: https://console-dev.postgres.ai - variables: - ENV: dev - NAMESPACE: dev - DOCKER_NAME: "gcr.io/postgres-ai/platform-web/cloud" - TAG: "${DOCKER_NAME}:${NAMESPACE}-${CI_PIPELINE_IID}" - -# Jobs templates. -.build_definition: &build_definition - <<: *ui_cache - stage: build - image: docker:20.10.12 - services: - - docker:dind - script: - - apk add --no-cache bash - - bash ./ui/packages/platform/ci_docker_build_push.sh - needs: - - job: check-code-style - artifacts: false - -.deploy_definition: &deploy_definition - stage: deploy - image: dtzar/helm-kubectl:2.14.1 - script: - # Substitute env variables in deploy config. - - bash ./ui/packages/platform/do.sh subs_envs ./ui/packages/platform/deploy/platform-console.yaml /tmp/platform-console.yaml - # Deploy to k8s cluster. - - kubectl apply --filename /tmp/platform-console.yaml -n $NAMESPACE - -# Jobs. -# Production. -ui_build_platform_image_tag_release: - <<: *env_production - <<: *only_ui_tag_release - <<: *build_definition - -ui_deploy_platform_image_tag_release: - <<: *env_production - <<: *only_ui_tag_release - <<: *deploy_definition - -# Staging. -ui_build_platform_image_staging: - <<: *env_staging - <<: *only_ui_staging - <<: *build_definition - -ui_deploy_platform_image_staging: - <<: *env_staging - <<: *only_ui_staging - <<: *deploy_definition - -# Dev. -ui_build_platform_image_dev: - <<: *env_dev - <<: *only_ui_feature - <<: *build_definition - allow_failure: true # Workaround: https://gitlab.com/gitlab-org/gitlab/-/issues/20237 - -ui_deploy_platform_image_dev: - <<: *env_dev - <<: *only_ui_feature - <<: *deploy_definition - allow_failure: true - needs: - - ui_build_platform_image_dev diff --git a/ui/packages/platform/.npmrc b/ui/packages/platform/.npmrc deleted file mode 100644 index 4c2f52b3..00000000 --- a/ui/packages/platform/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -auto-install-peers=true -strict-peer-dependencies=false diff --git a/ui/packages/platform/.stylelintrc b/ui/packages/platform/.stylelintrc deleted file mode 100644 index f623bb4d..00000000 --- a/ui/packages/platform/.stylelintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "stylelint-config-sass-guidelines", - "rules": { - "selector-class-pattern": "^[a-z][a-zA-Z0-9]+$", - "order/order": null, - "order/properties-alphabetical-order": null - } -} diff --git a/ui/packages/platform/COPYRIGHT b/ui/packages/platform/COPYRIGHT deleted file mode 100644 index f41576fe..00000000 --- a/ui/packages/platform/COPYRIGHT +++ /dev/null @@ -1,6 +0,0 @@ -------------------------------------------------------------------------- -Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai -All Rights Reserved -Unauthorized copying of this file, via any medium is strictly prohibited -Proprietary and confidential -------------------------------------------------------------------------- diff --git a/ui/packages/platform/Dockerfile b/ui/packages/platform/Dockerfile deleted file mode 100644 index dad3e8c7..00000000 --- a/ui/packages/platform/Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -#-------------------------------------------------------------------------- -# Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai -# All Rights Reserved -# Unauthorized copying of this file, via any medium is strictly prohibited -# Proprietary and confidential -#-------------------------------------------------------------------------- - -# Build phase. -FROM node:16.14-alpine as build - -WORKDIR /app - -COPY ./ui/ . - -# RUN --mount=type=bind,id=pnpm,source=.pnpm-store,target=/app/.pnpm-store - -ARG ARG_REACT_APP_API_SERVER -ENV REACT_APP_API_SERVER=$ARG_REACT_APP_API_SERVER - -ARG ARG_PUBLIC_URL -ENV PUBLIC_URL=$ARG_PUBLIC_URL - -ARG ARG_REACT_APP_SIGNIN_URL -ENV REACT_APP_SIGNIN_URL=$ARG_REACT_APP_SIGNIN_URL - -ARG ARG_REACT_APP_AUTH_URL -ENV REACT_APP_AUTH_URL=$ARG_REACT_APP_AUTH_URL - -ARG ARG_REACT_APP_ROOT_URL -ENV REACT_APP_ROOT_URL=$ARG_REACT_APP_ROOT_URL - -ARG ARG_REACT_APP_WS_SERVER -ENV REACT_APP_WS_SERVER=$ARG_REACT_APP_WS_SERVER - -ARG ARG_REACT_APP_EXPLAIN_DEPESZ_SERVER -ENV REACT_APP_EXPLAIN_DEPESZ_SERVER=$ARG_REACT_APP_EXPLAIN_DEPESZ_SERVER - -ARG ARG_REACT_APP_EXPLAIN_PEV2_SERVER -ENV REACT_APP_EXPLAIN_PEV2_SERVER=$ARG_REACT_APP_EXPLAIN_PEV2_SERVER - -ARG ARG_REACT_APP_STRIPE_API_KEY -ENV REACT_APP_STRIPE_API_KEY=$ARG_REACT_APP_STRIPE_API_KEY - -ARG ARG_REACT_APP_SENTRY_DSN -ENV REACT_APP_SENTRY_DSN=$ARG_REACT_APP_SENTRY_DSN - -RUN apk add --no-cache --update git && \ - npm i -g pnpm@7.30.5; \ - pnpm config set store-dir /app/.pnpm-store; \ - pnpm set verify-store-integrity false; \ - pnpm --filter @postgres.ai/platform i; \ - pnpm --filter @postgres.ai/platform build - -# Run phase. -FROM nginx:1.20.1-alpine as run - -COPY --from=build /app/packages/platform/build /srv/platform -COPY ./ui/packages/platform/nginx.conf /etc/nginx/conf.d/platform.conf -RUN rm -rf /etc/nginx/conf.d/default.conf - -CMD ["nginx", "-g", "daemon off;"] diff --git a/ui/packages/platform/README.md b/ui/packages/platform/README.md deleted file mode 100644 index 3e1e6cbd..00000000 --- a/ui/packages/platform/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# platform-console - -To start: -Specify REST API server URL with `REACT_APP_API_SERVER` environment variable. - -``` -npm install -npm run build -npm run start -``` - -# Q&A - -## `meta.json` is missing, what to do? -Run `npm run build`. - - \ No newline at end of file diff --git a/ui/packages/platform/ci_docker_build_push.sh b/ui/packages/platform/ci_docker_build_push.sh deleted file mode 100644 index edd959f5..00000000 --- a/ui/packages/platform/ci_docker_build_push.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -docker_file=${DOCKER_FILE:-""} -tags=${TAG:-""} - -# Docker login for GCP. -echo $GCP_SERVICE_ACCOUNT | base64 -d > ./key.json -docker login -u _json_key --password-stdin https://gcr.io < ./key.json - -tags_build="" -tags_push="" - -IFS=',' read -ra ADDR string < $2 -} - -is_command_defined() { - type $1 2>/dev/null | grep -q 'is a function' -} - -# Parse command and arguments. -COMMAND=$1 -shift -ARGUMENTS=${@} - -# Run command. -is_command_defined $COMMAND -if [ $? -eq 0 ]; then - $COMMAND $ARGUMENTS -else - echo "Command not found" -fi diff --git a/ui/packages/platform/nginx.conf b/ui/packages/platform/nginx.conf deleted file mode 100644 index 3b65a273..00000000 --- a/ui/packages/platform/nginx.conf +++ /dev/null @@ -1,49 +0,0 @@ -server { - listen 3000; - server_name localhost; - root /srv/platform; - - # X-Frame-Options is to prevent from clickJacking attack. - # Makes impossible to use website in iframe. - add_header X-Frame-Options SAMEORIGIN; - - # Disable content-type sniffing on some browsers. - # Handle files strictly according to their mime types. - add_header X-Content-Type-Options nosniff; - - # Disable sending refferer to the downgraded security level. - # Example: https -> http. - add_header Referrer-Policy 'no-referrer-when-downgrade'; - - # Enable gzip compression only for static files. - gzip_static on; - - # Enables response header of "Vary: Accept-Encoding". - # It allows to serve both versions: compressed and not. - gzip_vary on; - - location / { - # No-cache doesn’t mean “don’t cache”, it means it must revalidate with the server before using the cached resource. - add_header Cache-Control 'no-cache'; - - # Enable entity tag to revalidate cache. - etag on; - - # Serve files. - try_files $uri $uri/ /index.html; - } - - location /static { - # This content can be cached as by user as by CDN's. - add_header Cache-Control 'public'; - - # Cache will be fresh for next 1 year. - expires 1y; - - # Disable logging static files requests. - access_log off; - - # Serve files. - try_files $uri $uri/; - } -} diff --git a/ui/packages/platform/package.json b/ui/packages/platform/package.json deleted file mode 100644 index 130eba28..00000000 --- a/ui/packages/platform/package.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "name": "@postgres.ai/platform", - "version": "1.0.0", - "private": true, - "dependencies": { - "@craco/craco": "^6.4.3", - "@emotion/cache": "^11.10.5", - "@emotion/react": "^11.10.5", - "@emotion/server": "^11.10.0", - "@emotion/styled": "^11.10.5", - "@juggle/resize-observer": "^3.3.1", - "@material-ui/core": "^4.12.3", - "@material-ui/icons": "^4.11.2", - "@material-ui/lab": "4.0.0-alpha.61", - "@material-ui/styles": "^4.11.4", - "@material-ui/system": "^4.12.2", - "@monaco-editor/react": "^4.4.5", - "@mui/material": "^5.10.12", - "@postgres.ai/ce": "link:../ce", - "@postgres.ai/platform": "link:./", - "@postgres.ai/shared": "link:../shared", - "@sentry/react": "^6.11.0", - "@sentry/tracing": "^6.11.0", - "@stripe/react-stripe-js": "^1.1.2", - "@stripe/stripe-js": "^1.9.0", - "@types/d3": "^7.4.0", - "@types/dompurify": "^2.3.4", - "@types/node": "^12.20.33", - "@types/qs": "^6.9.7", - "@types/react": "^17.0.5", - "@types/react-dom": "^17.0.3", - "@types/react-router": "^5.1.17", - "@types/react-router-dom": "^5.1.7", - "@types/react-syntax-highlighter": "^15.5.6", - "bootstrap": "^4.3.1", - "byte-size": "^7.0.1", - "classnames": "^2.3.1", - "clsx": "^1.1.1", - "copy-to-clipboard": "^3.3.1", - "create-file-webpack": "^1.0.2", - "crypto-browserify": "^3.12.0", - "d3": "^5.12.0", - "d3-flame-graph": "^2.1.3", - "date-fns": "^2.22.1", - "dompurify": "^2.0.12", - "es6-promise": "^4.2.8", - "formik": "^2.2.9", - "get-user-locale": "^1.4.0", - "jwt-decode": "^3.1.2", - "jwt-encode": "^1.0.1", - "lodash": "^4.17.15", - "md5": "^2.2.1", - "mobx": "^6.3.2", - "mobx-react-lite": "^3.2.0", - "moment": "^2.24.0", - "prop-types": "^15.7.2", - "qs": "^6.11.0", - "react": "^17.0.2", - "react-bootstrap": "^0.32.4", - "react-countdown-hook": "^1.1.0", - "react-div-100vh": "^0.6.0", - "react-dom": "^17.0.2", - "react-markdown": "^8.0.1", - "react-router": "^5.1.2", - "react-router-dom": "^5.1.2", - "react-router-hash-link": "^1.2.2", - "react-scripts": "^5.0.0", - "react-syntax-highlighter": "^15.5.0", - "reflux": "^6.4.1", - "rehype-raw": "^6.1.1", - "remark-gfm": "^3.0.1", - "stream-browserify": "^3.0.0", - "typeface-roboto": "0.0.75", - "typescript": "^4.4.4", - "use-interval": "^1.3.0", - "use-timer": "^2.0.1", - "uuid": "^3.3.2", - "whatwg-fetch": "^3.6.2", - "yup": "^0.32.11" - }, - "scripts": { - "start": "craco start", - "build": "craco build", - "test": "craco test", - "eject": "craco eject", - "lint": "npm run lint:code && npm run lint:styles && npm run lint:spelling", - "lint:code": "eslint './src'", - "lint:styles": "stylelint \"src/**/*.scss\"", - "lint:spelling": "cspell './src/**/*' --no-progress --no-summary" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "@babel/core": "^7.19.0", - "@babel/eslint-parser": "^7.18.9", - "@babel/eslint-plugin": "^7.18.10", - "@babel/preset-react": "^7.18.6", - "@tsconfig/recommended": "^1.0.1", - "@typescript-eslint/eslint-plugin": "^5.6.0", - "@typescript-eslint/parser": "^5.6.0", - "cspell": "^5.6.6", - "eslint": "^8.23.0", - "eslint-plugin-react": "^7.18.0", - "eslint-plugin-react-hooks": "^4.2.0", - "sass": "^1.37.5", - "stylelint": "^13.13.1", - "stylelint-config-sass-guidelines": "^8.0.0" - } -} diff --git a/ui/packages/platform/public/auth-gate.html b/ui/packages/platform/public/auth-gate.html deleted file mode 100644 index 9621ad54..00000000 --- a/ui/packages/platform/public/auth-gate.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Postgres.ai - - - - - - diff --git a/ui/packages/platform/public/favicon.ico b/ui/packages/platform/public/favicon.ico deleted file mode 100644 index 808c8a49..00000000 Binary files a/ui/packages/platform/public/favicon.ico and /dev/null differ diff --git a/ui/packages/platform/public/images/ansible.svg b/ui/packages/platform/public/images/ansible.svg deleted file mode 100644 index 7f0480fb..00000000 --- a/ui/packages/platform/public/images/ansible.svg +++ /dev/null @@ -1,2 +0,0 @@ - -Ansible icon \ No newline at end of file diff --git a/ui/packages/platform/public/images/avatar.jpg b/ui/packages/platform/public/images/avatar.jpg deleted file mode 100644 index deae82ed..00000000 Binary files a/ui/packages/platform/public/images/avatar.jpg and /dev/null differ diff --git a/ui/packages/platform/public/images/dblab.svg b/ui/packages/platform/public/images/dblab.svg deleted file mode 100644 index aea43a93..00000000 --- a/ui/packages/platform/public/images/dblab.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/ui/packages/platform/public/images/docker.svg b/ui/packages/platform/public/images/docker.svg deleted file mode 100644 index 2dd944c7..00000000 --- a/ui/packages/platform/public/images/docker.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/ui/packages/platform/public/images/globe.svg b/ui/packages/platform/public/images/globe.svg deleted file mode 100644 index f2f0671c..00000000 --- a/ui/packages/platform/public/images/globe.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/ui/packages/platform/public/images/infosrc.png b/ui/packages/platform/public/images/infosrc.png deleted file mode 100644 index 244c86ab..00000000 Binary files a/ui/packages/platform/public/images/infosrc.png and /dev/null differ diff --git a/ui/packages/platform/public/images/oauth-github-logo.png b/ui/packages/platform/public/images/oauth-github-logo.png deleted file mode 100644 index 1fa19c55..00000000 Binary files a/ui/packages/platform/public/images/oauth-github-logo.png and /dev/null differ diff --git a/ui/packages/platform/public/images/oauth-gitlab-logo.png b/ui/packages/platform/public/images/oauth-gitlab-logo.png deleted file mode 100644 index 7ab02d6a..00000000 Binary files a/ui/packages/platform/public/images/oauth-gitlab-logo.png and /dev/null differ diff --git a/ui/packages/platform/public/images/oauth-google-logo.png b/ui/packages/platform/public/images/oauth-google-logo.png deleted file mode 100644 index 389c1cd5..00000000 Binary files a/ui/packages/platform/public/images/oauth-google-logo.png and /dev/null differ diff --git a/ui/packages/platform/public/images/oauth-linkedin-logo.png b/ui/packages/platform/public/images/oauth-linkedin-logo.png deleted file mode 100644 index 9086cb15..00000000 Binary files a/ui/packages/platform/public/images/oauth-linkedin-logo.png and /dev/null differ diff --git a/ui/packages/platform/public/images/paymentMethods/amex.png b/ui/packages/platform/public/images/paymentMethods/amex.png deleted file mode 100644 index 366f6e2b..00000000 Binary files a/ui/packages/platform/public/images/paymentMethods/amex.png and /dev/null differ diff --git a/ui/packages/platform/public/images/paymentMethods/diners.png b/ui/packages/platform/public/images/paymentMethods/diners.png deleted file mode 100644 index 0134bf0b..00000000 Binary files a/ui/packages/platform/public/images/paymentMethods/diners.png and /dev/null differ diff --git a/ui/packages/platform/public/images/paymentMethods/discover.png b/ui/packages/platform/public/images/paymentMethods/discover.png deleted file mode 100644 index ac653f02..00000000 Binary files a/ui/packages/platform/public/images/paymentMethods/discover.png and /dev/null differ diff --git a/ui/packages/platform/public/images/paymentMethods/maestro.png b/ui/packages/platform/public/images/paymentMethods/maestro.png deleted file mode 100644 index 5aba59ba..00000000 Binary files a/ui/packages/platform/public/images/paymentMethods/maestro.png and /dev/null differ diff --git a/ui/packages/platform/public/images/paymentMethods/mastercard.png b/ui/packages/platform/public/images/paymentMethods/mastercard.png deleted file mode 100644 index b274d7e7..00000000 Binary files a/ui/packages/platform/public/images/paymentMethods/mastercard.png and /dev/null differ diff --git a/ui/packages/platform/public/images/paymentMethods/unionpay.png b/ui/packages/platform/public/images/paymentMethods/unionpay.png deleted file mode 100644 index e9205908..00000000 Binary files a/ui/packages/platform/public/images/paymentMethods/unionpay.png and /dev/null differ diff --git a/ui/packages/platform/public/images/paymentMethods/visa.png b/ui/packages/platform/public/images/paymentMethods/visa.png deleted file mode 100644 index 7d21c22c..00000000 Binary files a/ui/packages/platform/public/images/paymentMethods/visa.png and /dev/null differ diff --git a/ui/packages/platform/public/images/service-providers/aws.png b/ui/packages/platform/public/images/service-providers/aws.png deleted file mode 100644 index 868a91f5..00000000 Binary files a/ui/packages/platform/public/images/service-providers/aws.png and /dev/null differ diff --git a/ui/packages/platform/public/images/service-providers/digitalocean.png b/ui/packages/platform/public/images/service-providers/digitalocean.png deleted file mode 100644 index 3df53ee4..00000000 Binary files a/ui/packages/platform/public/images/service-providers/digitalocean.png and /dev/null differ diff --git a/ui/packages/platform/public/images/service-providers/gcp.png b/ui/packages/platform/public/images/service-providers/gcp.png deleted file mode 100644 index 04abaf7c..00000000 Binary files a/ui/packages/platform/public/images/service-providers/gcp.png and /dev/null differ diff --git a/ui/packages/platform/public/images/service-providers/hetzner.png b/ui/packages/platform/public/images/service-providers/hetzner.png deleted file mode 100644 index e0131d5e..00000000 Binary files a/ui/packages/platform/public/images/service-providers/hetzner.png and /dev/null differ diff --git a/ui/packages/platform/public/images/simple.svg b/ui/packages/platform/public/images/simple.svg deleted file mode 100644 index 624100b7..00000000 --- a/ui/packages/platform/public/images/simple.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - Svg Vector Icons : http://www.onlinewebfonts.com/icon - - \ No newline at end of file diff --git a/ui/packages/platform/public/images/warning.png b/ui/packages/platform/public/images/warning.png deleted file mode 100644 index cd747ffe..00000000 Binary files a/ui/packages/platform/public/images/warning.png and /dev/null differ diff --git a/ui/packages/platform/public/index.html b/ui/packages/platform/public/index.html deleted file mode 100644 index ccbe6cb6..00000000 --- a/ui/packages/platform/public/index.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - Postgres.ai - - - - - - - -
- - - - diff --git a/ui/packages/platform/public/manifest.json b/ui/packages/platform/public/manifest.json deleted file mode 100644 index 56180c0d..00000000 --- a/ui/packages/platform/public/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "short_name": "Database Lab Platform", - "name": "Database Lab Platform", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - } - ], - "start_url": "./index.html", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/ui/packages/platform/src/App.jsx b/ui/packages/platform/src/App.jsx deleted file mode 100644 index a8859920..00000000 --- a/ui/packages/platform/src/App.jsx +++ /dev/null @@ -1,50 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { loadStripe } from '@stripe/stripe-js' -import { Elements } from '@stripe/react-stripe-js' -import { BrowserRouter as Router, Route } from 'react-router-dom' -import { - createGenerateClassName, - StylesProvider, - ThemeProvider, -} from '@material-ui/core/styles' - -import { ROUTES } from 'config/routes' - -import { IndexPageWrapper } from 'components/IndexPage/IndexPageWrapper' -import { theme } from '@postgres.ai/shared/styles/theme' -import settings from 'utils/settings' - -const stripePromise = loadStripe(settings.stripeApiKey, { - locale: 'en', -}) - -class App extends Component { - render() { - const generateClassName = createGenerateClassName({ - productionPrefix: 'p', - }) - - return ( - - - - - - - - - - - - ) - } -} - -export default App diff --git a/ui/packages/platform/src/App.test.js b/ui/packages/platform/src/App.test.js deleted file mode 100644 index 076db353..00000000 --- a/ui/packages/platform/src/App.test.js +++ /dev/null @@ -1,17 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -/* eslint-disable */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); -}); diff --git a/ui/packages/platform/src/actions/actions.js b/ui/packages/platform/src/actions/actions.js deleted file mode 100644 index c9131809..00000000 --- a/ui/packages/platform/src/actions/actions.js +++ /dev/null @@ -1,1537 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import Reflux from 'reflux'; - -import { localStorage } from 'helpers/localStorage'; - -import Api from '../api/api'; -import ExplainDepeszApi from '../api/explain/depesz'; -import ExplainPev2Api from '../api/explain/pev2'; -import settings from '../utils/settings'; -import { visualizeTypes } from '../assets/visualizeTypes'; - -let api; -let explainDepeszApi; -let explainPev2Api; - -settings.init(() => { - api = new Api(settings); - explainDepeszApi = new ExplainDepeszApi(settings); - explainPev2Api = new ExplainPev2Api(settings); -}); - -// Timeout: 30 sec. -const REQUEST_TIMEOUT = 30000; -const ASYNC_ACTION = { - asyncResult: true, - children: ['progressed', 'completed', 'failed'] -}; - -const Actions = Reflux.createActions([{ - auth: {}, - signOut: {}, - ASYNC_ACTION: ASYNC_ACTION, - doAuth: ASYNC_ACTION, - getUserProfile: ASYNC_ACTION, - getAccessTokens: ASYNC_ACTION, - getAccessToken: ASYNC_ACTION, - hideGeneratedAccessToken: {}, - revokeAccessToken: ASYNC_ACTION, - getCheckupReports: ASYNC_ACTION, - getCheckupReportFiles: ASYNC_ACTION, - getCheckupReportFile: ASYNC_ACTION, - getJoeSessions: ASYNC_ACTION, - getJoeSessionCommands: ASYNC_ACTION, - getJoeSessionCommand: ASYNC_ACTION, - getProjects: ASYNC_ACTION, - getOrgs: ASYNC_ACTION, - getOrgUsers: ASYNC_ACTION, - updateOrg: ASYNC_ACTION, - createOrg: ASYNC_ACTION, - inviteUser: ASYNC_ACTION, - useDemoData: ASYNC_ACTION, - setReportsProject: {}, - setSessionsProject: {}, - setDbLabInstancesProject: {}, - refresh: {}, - getDbLabInstances: ASYNC_ACTION, - addDbLabInstance: ASYNC_ACTION, - editDbLabInstance: ASYNC_ACTION, - destroyDbLabInstance: ASYNC_ACTION, - resetNewDbLabInstance: {}, - getDbLabInstanceStatus: ASYNC_ACTION, - checkDbLabInstanceUrl: ASYNC_ACTION, - downloadReportJsonFiles: ASYNC_ACTION, - addOrgDomain: ASYNC_ACTION, - deleteOrgDomain: ASYNC_ACTION, - showNotification: {}, - hideNotification: {}, - setJoeInstancesProject: {}, - getJoeInstances: ASYNC_ACTION, - getJoeInstanceChannels: ASYNC_ACTION, - sendJoeInstanceCommand: ASYNC_ACTION, - initJoeWebSocketConnection: {}, - getJoeInstanceMessages: ASYNC_ACTION, - getJoeMessageArtifacts: ASYNC_ACTION, - resetNewJoeInstance: {}, - checkJoeInstanceUrl: ASYNC_ACTION, - addJoeInstance: ASYNC_ACTION, - destroyJoeInstance: ASYNC_ACTION, - closeJoeWebSocketConnection: {}, - getExternalVisualizationData: ASYNC_ACTION, - closeExternalVisualization: {}, - deleteJoeSessions: ASYNC_ACTION, - deleteJoeCommands: ASYNC_ACTION, - deleteCheckupReports: ASYNC_ACTION, - joeCommandFavorite: ASYNC_ACTION, - clearJoeInstanceChannelMessages: {}, - getSharedUrlData: ASYNC_ACTION, - getSharedUrl: ASYNC_ACTION, - addSharedUrl: ASYNC_ACTION, - removeSharedUrl: ASYNC_ACTION, - showShareUrlDialog: {}, - closeShareUrlDialog: {}, - getBillingDataUsage: ASYNC_ACTION, - subscribeBilling: ASYNC_ACTION, - setSubscriptionError: {}, - getDbLabInstance: ASYNC_ACTION, - getJoeInstance: ASYNC_ACTION, - updateOrgUser: ASYNC_ACTION, - deleteOrgUser: ASYNC_ACTION, - getDbLabSessions: ASYNC_ACTION, - getDbLabSession: ASYNC_ACTION, - getDbLabSessionLogs: ASYNC_ACTION, - getDbLabSessionArtifacts: ASYNC_ACTION, - getDbLabSessionArtifact: ASYNC_ACTION, - getAuditLog: ASYNC_ACTION, - downloadDblabSessionLog: ASYNC_ACTION, - downloadDblabSessionArtifact: ASYNC_ACTION, - sendUserCode: ASYNC_ACTION, - confirmUserEmail: ASYNC_ACTION, - confirmTosAgreement: ASYNC_ACTION -}]); - -function timeoutPromise(ms, promise) { - return new Promise((resolve, reject) => { - const timeoutId = setTimeout(function () { - reject(new Error('timeout')); - }, ms); - - promise - .then( - (res) => { - clearTimeout(timeoutId); - resolve(res); - }, - (err) => { - clearTimeout(timeoutId); - reject(err); - }); - }); -} - -function actionResult(promise, cb, errorCb) { - timeoutPromise(REQUEST_TIMEOUT, promise) - .then(result => { - let count; - try { - let range = result.headers.get('content-range'); - if (range) { - range = range.split('/'); - if (Array.isArray(range) && range.length) { - range = range[range.length - 1]; - count = parseInt(range, 10); - } - } - } catch (e) { - console.log('Range is empty'); - } - - result.json() - .then(json => { - if (!json) { - if (errorCb) { - errorCb(new Error('wrong_reply')); - } else { - this.failed(new Error('wrong_reply')); - } - } - - if (cb) { - cb(json, count); - } else { - this.completed(json, count); - } - }) - .catch(err => { - console.error(err); - - if (errorCb) { - errorCb(new Error('wrong_reply')); - } else { - this.failed(new Error('wrong_reply')); - } - }); - }) - .catch(err => { - console.error(err); - let actionErr = new Error('wrong_reply'); - - if (err && err.message && err.message === 'timeout') { - actionErr = new Error('failed_fetch'); - } - - if (errorCb) { - errorCb(new Error(actionErr)); - } else { - this.failed(actionErr); - } - }); -} - -Actions.doAuth.listen(function (email, password) { - const action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - if (!email && !password) { - // Token should be passed through /auth.html page. - // Example: /auth-gate.html?token=some-token - const token = localStorage.getAuthToken(); - - if (token) { - action.completed({ token: token }); - } else { - action.failed(new Error('empty_request')); - } - - return; - } - - action.progressed(); - - timeoutPromise(REQUEST_TIMEOUT, api.login(email, password)) - .then(result => { - if (result.status !== 200) { - action.failed(new Error('wrong_reply')); - } - - result.json() - .then(json => { - if (json) { - if (typeof json === 'object') { - action.failed(new Error(json.message)); - } else { - action.completed({ token: json }); - } - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getUserProfile.listen(function (token) { - this.progressed(); - - actionResult.bind(this)( - api.getUserProfile(token), - (json) => { - this.completed(json); - } - ); -}); - -Actions.getAccessTokens.listen(function (token, orgId) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(); - - timeoutPromise(REQUEST_TIMEOUT, api.getAccessTokens(token, orgId)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ data: json, orgId: orgId }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getAccessToken.listen(function (token, name, expires, orgId, isPersonal) { - let requestExpires = expires.split('-').join('') + 'T235959-0330'; - - this.progressed(); - actionResult.bind(this)( - api.getAccessToken(token, name, requestExpires, orgId, isPersonal), - (json) => { - this.completed({ orgId: orgId, data: json }); - }); -}); - -Actions.revokeAccessToken.listen(function (token, orgId, id) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(id); - - timeoutPromise(REQUEST_TIMEOUT, api.revokeAccessToken(token, id)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ orgId: orgId, data: json }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getCheckupReports.listen(function (token, orgId, projectId, reportId) { - this.progressed(); - - actionResult.bind(this)( - api.getCheckupReports(token, orgId, projectId, reportId), - (json) => { - this.completed({ - data: json, - orgId: orgId, - projectId: projectId, - reportId: reportId - }); - } - ); -}); - -Actions.getCheckupReportFiles.listen(function (token, reportId, type, orderBy, - orderDirection) { - this.progressed(); - - actionResult.bind(this)( - api.getCheckupReportFiles(token, reportId, type, orderBy, orderDirection), - (json) => { - this.completed({ reportId: reportId, data: json, type: type }); - } - ); -}); - -Actions.getCheckupReportFile.listen(function (token, projectId, reportId, fileId, type) { - this.progressed(); - - actionResult.bind(this)( - api.getCheckupReportFile(token, projectId, reportId, fileId, type), - (json) => { - this.completed(fileId, json); - } - ); -}); - -Actions.getJoeSessions.listen(function (token, orgId, projectId) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(); - - timeoutPromise(REQUEST_TIMEOUT, api.getJoeSessions(token, orgId, projectId)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ data: json, orgId: orgId, projectId: projectId }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getJoeSessionCommands.listen(function (token, params) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(params); - - timeoutPromise(REQUEST_TIMEOUT, api.getJoeSessionCommands(token, params)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed(json, params); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getJoeSessionCommand.listen(function (token, orgId, commandId) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(); - - timeoutPromise(REQUEST_TIMEOUT, api.getJoeSessionCommand(token, orgId, commandId)) - - .then(result => { - result.json() - .then(json => { - if (json) { - const command = json[0]; - action.completed(command); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getProjects.listen(function (token, orgId) { - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - let action = this; - - action.progressed(); - - timeoutPromise(REQUEST_TIMEOUT, api.getProjects(token, orgId)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ data: json, orgId: orgId }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getOrgs.listen(function (token, orgId) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed({ orgId }); - - timeoutPromise(REQUEST_TIMEOUT, api.getOrgs(token, orgId)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ data: json, orgId: orgId }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getOrgUsers.listen(function (token, orgId) { - this.progressed(orgId); - - actionResult.bind(this)( - api.getOrgUsers(token, orgId), - (json) => { - this.completed(orgId, json); - }, - (err) => { - this.failed(orgId, err); - } - ); -}); - -Actions.updateOrg.listen(function (token, orgId, orgData) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed({ orgId } + orgData); - timeoutPromise(REQUEST_TIMEOUT, api.updateOrg(token, orgId, orgData)) - - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed(json); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.createOrg.listen(function (token, orgData) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(orgData); - timeoutPromise(REQUEST_TIMEOUT, api.createOrg(token, orgData)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed(json); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.inviteUser.listen(function (token, orgId, email) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed({ orgId, email }); - timeoutPromise(REQUEST_TIMEOUT, api.inviteUser(token, orgId, email)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed(json); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.useDemoData.listen(function (token) { - this.progressed(); - - actionResult.bind(this)( - api.useDemoData(token), - (json) => { - this.completed(json); - }, - (err) => { - this.failed(err); - } - ); -}); - -Actions.getDbLabInstances.listen(function (token, orgId, projectId) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(); - timeoutPromise(REQUEST_TIMEOUT, api.getDbLabInstances(token, orgId, projectId)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ data: json, orgId: orgId, projectId: projectId }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.addDbLabInstance.listen(function (token, instanceData) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(); - - timeoutPromise(REQUEST_TIMEOUT, api.addDbLabInstance(token, instanceData)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed( - { data: json, orgId: instanceData.orgId, project: instanceData.project }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.editDbLabInstance.listen(function (token, instanceData) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(); - - timeoutPromise(REQUEST_TIMEOUT, api.editDbLabInstance(token, instanceData)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed( - { data: json, orgId: instanceData.orgId, project: instanceData.project }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.destroyDbLabInstance.listen(function (token, instanceId) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed({ instanceId: instanceId }); - - timeoutPromise(REQUEST_TIMEOUT, api.destroyDbLabInstance(token, instanceId)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ data: json, instanceId: instanceId }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getDbLabInstanceStatus.listen(function (token, instanceId) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed({ instanceId: instanceId }); - - timeoutPromise(REQUEST_TIMEOUT, api.getDbLabInstanceStatus(token, instanceId)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ data: json, instanceId: instanceId }); - } else { - action.failed({ instanceId: instanceId }, new Error( - 'wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed({ instanceId: instanceId }, new Error( - 'wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed({ instanceId: instanceId }, new Error( - 'failed_fetch')); - } else { - action.failed({ instanceId: instanceId }, new Error( - 'wrong_reply')); - } - }); -}); - -Actions.checkDbLabInstanceUrl.listen(function (token, url, verifyToken, useTunnel) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed(); - - timeoutPromise(REQUEST_TIMEOUT, api.checkDbLabInstanceUrl(token, url, verifyToken, useTunnel)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed(json); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.downloadBinaryFile = (action, token, fileUrl, storeParams = {}) => { - let xhr = new XMLHttpRequest(); - - action.progressed(storeParams); - xhr.open('GET', fileUrl); - xhr.setRequestHeader('authorization', 'Bearer ' + token); - xhr.setRequestHeader('accept', 'application/octet-stream'); - xhr.responseType = 'arraybuffer'; - xhr.addEventListener('readystatechange', function () { - if (this.readyState === 4) { - let blob; - let filename = ''; - let type = xhr.getResponseHeader('Content-Type'); - let disposition = xhr.getResponseHeader('Content-Disposition'); - let url = window.URL || window.webkitURL; - - if (disposition && disposition.indexOf('attachment') !== -1) { - let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; - let matches = filenameRegex.exec(disposition); - - if (matches !== null && matches[1]) { - filename = matches[1].replace(/['"]/g, ''); - } - } - - if (typeof File === 'function') { - try { - blob = new File([this.response], filename, { type: type }); - } catch (e) { - /* IE 10 or less do not support constructor for File. - In this case we will use Blob on next step. */ - } - } - - if (this.status === 404) { - try { - const jsonBody = JSON.parse(new TextDecoder().decode(this.response)); - action.failed(jsonBody); - } catch (e) { - action.failed({}); - } - return; - } - - if (typeof blob === 'undefined') { - blob = new Blob([this.response], { type: type }); - } - - action.completed(); - - if (typeof window.navigator.msSaveBlob !== 'undefined') { - window.navigator.msSaveBlob(blob, filename); - - return; - } - - let downloadUrl = url.createObjectURL(blob); - - if (filename) { - // use HTML5 a[download] attribute to specify filename. - let a = document.createElement('a'); - // safari doesn't support this yet - if (typeof a.download === 'undefined') { - window.location = downloadUrl; - } else { - a.href = downloadUrl; - a.download = filename; - document.body.appendChild(a); - a.click(); - } - - return; - } - - window.location = downloadUrl; - } - }); - - xhr.send(); -}; - - -Actions.downloadReportJsonFiles.listen(function (token, reportId) { - let url = settings.apiServer + - '/rpc/checkup_report_json_download?checkup_report_id=' + reportId; - Actions.downloadBinaryFile(this, token, url); -}); - -Actions.deleteOrgDomain.listen(function (token, orgId, domainId) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed({ orgId, domainId }); - timeoutPromise(REQUEST_TIMEOUT, api.deleteOrgDomain(token, domainId)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ orgId, domainId }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.addOrgDomain.listen(function (token, orgId, domain) { - let action = this; - - if (!api) { - settings.init(function () { - api = new Api(settings); - }); - } - - action.progressed({ orgId, domain }); - timeoutPromise(REQUEST_TIMEOUT, api.addOrgDomain(token, orgId, domain)) - .then(result => { - result.json() - .then(json => { - if (json) { - action.completed({ orgId, domain }); - } else { - action.failed(new Error('wrong_reply')); - } - }) - .catch(err => { - console.error(err); - action.failed(new Error('wrong_reply')); - }); - }) - .catch(err => { - console.error(err); - if (err && err.message && err.message === 'timeout') { - action.failed(new Error('failed_fetch')); - } else { - action.failed(new Error('wrong_reply')); - } - }); -}); - -Actions.getJoeInstances.listen(function (token, orgId, projectId) { - this.progressed(); - - actionResult.bind(this)( - api.getJoeInstances(token, orgId, projectId), - (json) => { - this.completed(orgId, projectId, json); - }); -}); - -Actions.getJoeInstanceChannels.listen(function (token, instanceId) { - this.progressed(instanceId); - actionResult.bind(this)( - api.getJoeInstanceChannels(token, instanceId), - (json) => { - this.completed(instanceId, json); - }, - (err) => { - this.failed(instanceId, err); - }); -}); - -Actions.sendJoeInstanceCommand.listen(function (token, instanceId, - channelId, command, sessionId) { - this.progressed(instanceId); - actionResult.bind(this)( - api.sendJoeInstanceCommand(token, instanceId, channelId, command, sessionId), - (json) => { - json.message = command; - this.completed(instanceId, json); - }, - (err) => { - this.failed(instanceId, err); - } - ); -}); - -Actions.getJoeInstanceMessages.listen(function (token, instanceId, - channelId, sessionId) { - this.progressed(instanceId); - actionResult.bind(this)( - api.getJoeInstanceMessages(token, channelId, sessionId), - (json) => { - this.completed(instanceId, channelId, json); - }, - (err) => { - this.failed(instanceId, channelId, err); - }); -}); - -Actions.getJoeMessageArtifacts.listen(function (token, instanceId, channelId, messageId) { - this.progressed(instanceId, channelId, messageId); - actionResult.bind(this)( - api.getJoeMessageArtifacts(token, messageId), - (json) => { - this.completed(instanceId, channelId, messageId, json); - }, - (err) => { - this.failed(instanceId, channelId, messageId, err); - } - ); -}); - -Actions.checkJoeInstanceUrl.listen(function (token, instanceData) { - this.progressed(); - - instanceData.dryRun = true; - - actionResult.bind(this)(api.addJoeInstance(token, instanceData)); -}); - -Actions.addJoeInstance.listen(function (token, instanceData) { - this.progressed(); - - instanceData.dryRun = false; - - actionResult.bind(this)( - api.addJoeInstance(token, instanceData), - (json) => { - this.completed(instanceData.orgId, json); - } - ); -}); - -Actions.destroyJoeInstance.listen(function (token, instanceId) { - this.progressed(instanceId); - - actionResult.bind(this)( - api.destroyJoeInstance(token, instanceId), - (json) => { - this.completed(instanceId, json); - } - ); -}); - -Actions.getExternalVisualizationData.listen(function (type, plan, query) { - if (type !== visualizeTypes.depesz && type !== visualizeTypes.pev2) { - console.log('Unsupported visualization type.'); - return; - } - - this.progressed(type, plan, query); - - if (type === visualizeTypes.pev2) { - explainPev2Api.postPlan(plan.json, query) - .then((result) => { - result.json() - .then((response) => { - console.log(response.json); - - if (!response || !response.id) { - this.failed(type); - return; - } - - const url = `${settings.explainPev2Server}#${response.id}`; - this.completed(type, plan, query, url); - }).catch((error) => { - console.error('Error:', error); - this.failed(type); - }); - }).catch((error) => { - console.error('Error:', error); - this.failed(type); - }); - } else { - explainDepeszApi.postPlan(plan.text, query) - .then((response) => { - console.log(response); - - const xFinalUrl = response.headers.get('x-final-url'); - - let url = response.url; - if (response.url === settings.explainDepeszServer && !!xFinalUrl) { - url = xFinalUrl; - } - - this.completed(type, plan, query, url); - }).catch((error) => { - console.error('Error:', error); - this.failed(type); - }); - } -}); - -Actions.deleteJoeSessions.listen(function (token, ids) { - this.progressed(ids); - - actionResult.bind(this)( - api.deleteJoeSessions(token, ids), - (json) => { - this.completed(ids, json); - } - ); -}); - -Actions.deleteJoeCommands.listen(function (token, ids) { - this.progressed(ids); - - actionResult.bind(this)( - api.deleteJoeCommands(token, ids), - (json) => { - this.completed(ids, json); - } - ); -}); - - -Actions.deleteCheckupReports.listen(function (token, ids) { - this.progressed(ids); - - actionResult.bind(this)( - api.deleteCheckupReports(token, ids), - (json) => { - this.completed(ids, json); - } - ); -}); - -Actions.joeCommandFavorite.listen(function (token, commandId, favorite) { - this.progressed(commandId, favorite); - - actionResult.bind(this)( - api.joeCommandFavorite(token, commandId, favorite), - (json) => { - this.completed(json, commandId, favorite); - } - ); -}); - -Actions.getSharedUrlData.listen(function (uuid) { - this.progressed(uuid); - - actionResult.bind(this)( - api.getSharedUrlData(uuid), - (json) => { - this.completed(uuid, json); - } - ); -}); - -Actions.getSharedUrl.listen(function (token, orgId, objectType, objectId) { - this.progressed(orgId, objectType, objectId); - - actionResult.bind(this)( - api.getSharedUrl(token, orgId, objectType, objectId), - (json) => { - this.completed(orgId, objectType, objectId, json); - } - ); -}); - -Actions.removeSharedUrl.listen(function (token, orgId, objectType, objectId, urlId) { - this.progressed(); - - actionResult.bind(this)( - api.removeSharedUrl(token, orgId, urlId), - (json) => { - this.completed(orgId, objectType, objectId, urlId, json); - } - ); -}); - -Actions.addSharedUrl.listen(function (token, params) { - this.progressed(params); - - actionResult.bind(this)( - api.addSharedUrl(token, params), - (json) => { - this.completed(params, json); - } - ); -}); - -Actions.getBillingDataUsage.listen(function (token, orgId) { - this.progressed(orgId); - - actionResult.bind(this)( - api.getBillingDataUsage(token, orgId), - (json) => { - this.completed(orgId, json); - } - ); -}); - -Actions.subscribeBilling.listen(function (token, orgId, paymentMethodId) { - this.progressed(orgId, paymentMethodId); - - actionResult.bind(this)( - api.subscribeBilling(token, orgId, paymentMethodId), - (json) => { - this.completed(orgId, paymentMethodId, json); - } - ); -}); - -Actions.getDbLabInstance.listen(function (token, orgId, projectId, instanceId) { - this.progressed(orgId, projectId, instanceId); - - actionResult.bind(this)( - api.getDbLabInstances(token, orgId, projectId, instanceId), - (json) => { - this.completed(orgId, projectId, instanceId, json); - }, - (err) => { - this.failed(orgId, projectId, instanceId, err); - } - ); -}); - -Actions.getJoeInstance.listen(function (token, orgId, projectId, instanceId) { - this.progressed(orgId, projectId, instanceId); - - actionResult.bind(this)( - api.getJoeInstances(token, orgId, projectId, instanceId), - (json) => { - this.completed(orgId, projectId, instanceId, json); - }, - (err) => { - this.failed(orgId, projectId, instanceId, err); - } - ); -}); - -Actions.getDbLabSessions.listen(function (token, params) { - this.progressed(params); - - actionResult.bind(this)( - api.getDbLabSessions(token, params), - (json, count) => { - this.completed(params, json, count); - }, - (err) => { - this.failed(params, err); - } - ); -}); - -Actions.getDbLabSession.listen(function (token, sessionId) { - this.progressed(sessionId); - - actionResult.bind(this)( - api.getDbLabSession(token, sessionId), - (json) => { - this.completed(sessionId, json); - }, - (err) => { - this.failed(sessionId, err); - } - ); -}); - -Actions.getDbLabSessionLogs.listen(function (token, params) { - this.progressed(params); - - actionResult.bind(this)( - api.getDbLabSessionLogs(token, params), - (json, count) => { - this.completed(params, json, count); - }, - (err) => { - this.failed(params, err); - } - ); -}); - -Actions.getDbLabSessionArtifacts.listen(function (token, sessionId) { - this.progressed(sessionId); - - actionResult.bind(this)( - api.getDbLabSessionArtifacts(token, sessionId), - (json) => { - this.completed(sessionId, json); - }, - (err) => { - this.failed(sessionId, err); - } - ); -}); - -Actions.getDbLabSessionArtifact.listen(function (token, sessionId, artifactType) { - this.progressed(sessionId, artifactType); - - actionResult.bind(this)( - api.getDbLabSessionArtifact(token, sessionId, artifactType), - (json) => { - this.completed(sessionId, artifactType, json); - }, - (err) => { - this.failed(sessionId, artifactType, err); - } - ); -}); - - -Actions.updateOrgUser.listen(function (token, orgId, userId, role) { - this.progressed(orgId, userId, role); - - actionResult.bind(this)( - api.updateOrgUser(token, orgId, userId, role), - (json) => { - this.completed(orgId, userId, role, json); - }, - (err) => { - this.failed(orgId, userId, role, err); - } - ); -}); - -Actions.deleteOrgUser.listen(function (token, orgId, userId) { - this.progressed(orgId, userId); - - actionResult.bind(this)( - api.deleteOrgUser(token, orgId, userId), - (json) => { - this.completed(orgId, userId, json); - }, - (err) => { - this.failed(orgId, userId, err); - } - ); -}); - -Actions.getAuditLog.listen(function (token, params) { - this.progressed(params); - - actionResult.bind(this)( - api.getAuditLog(token, params), - (json, count) => { - this.completed(params, json, count); - }, - (err) => { - this.failed(params, err); - } - ); -}); - -Actions.downloadDblabSessionLog.listen(function (token, sessionId) { - let url = settings.apiServer + - '/rpc/dblab_session_logs_download?dblab_session_id=' + sessionId; - Actions.downloadBinaryFile(this, token, url); -}); - - -Actions.downloadDblabSessionArtifact.listen(function (token, sessionId, artifactType) { - let url = settings.apiServer + - '/rpc/dblab_session_artifacts_download?' + - 'dblab_session_id=' + sessionId + - '&artifact_type=' + artifactType; - Actions.downloadBinaryFile(this, token, url, { artifactType }); -}); - -Actions.sendUserCode.listen(function (token) { - this.progressed(); - - actionResult.bind(this)( - api.sendUserCode(token), - (json, count) => { - this.completed(json, count); - }, - (err) => { - this.failed(err); - } - ); -}); - -Actions.confirmUserEmail.listen(function (token, code) { - this.progressed(); - - actionResult.bind(this)( - api.confirmUserEmail(token, code), - (json) => { - this.completed(json); - }, - (err) => { - this.failed(err); - } - ); -}); - -Actions.confirmTosAgreement.listen(function (token) { - this.progressed(); - - actionResult.bind(this)( - api.confirmTosAgreement(token), - (json) => { - this.completed(json); - }, - (err) => { - this.failed(err); - } - ); -}); - -export default Actions; diff --git a/ui/packages/platform/src/api/api.js b/ui/packages/platform/src/api/api.js deleted file mode 100644 index 62be813b..00000000 --- a/ui/packages/platform/src/api/api.js +++ /dev/null @@ -1,981 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import 'es6-promise/auto'; -import 'whatwg-fetch'; - -function encodeData(data) { - return Object.keys(data).map(function (key) { - return [key, data[key]].map(encodeURIComponent).join('='); - }).join('&'); -} - -class Api { - constructor(setting) { - this.server = setting.server - this.apiServer = setting.apiServer - } - - get(url, query, options) { - let params = ''; - - if (query) { - params = `?${encodeData(query)}`; - } - - if (options) { - options.Prefer = 'count=none'; - } - - let fetchOptions = { - ...options, - method: 'get', - credentials: 'include' - }; - - return fetch(`${url}${params}`, fetchOptions); - } - - post(url, data, options = {}) { - let headers = options.headers || {}; - - let fetchOptions = { - ...options, - method: 'post', - credentials: 'include', - headers: { - ...headers, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }; - - return fetch(url, fetchOptions); - } - - patch(url, data, options = {}) { - let headers = options.headers || {}; - - let fetchOptions = { - ...options, - method: 'PATCH', - credentials: 'include', - headers: { - ...headers, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }; - - return fetch(url, fetchOptions); - } - - delete(url, data, options = {}) { - let headers = options.headers || {}; - - let fetchOptions = { - ...options, - method: 'DELETE', - credentials: 'include', - headers: { - ...headers, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }; - - return fetch(url, fetchOptions); - } - - login(login, password) { - let headers = {}; - - return this.post(`${this.apiServer}/rpc/login`, { - email: login, - password: password - }, { - headers: headers - }); - } - - getUserProfile(token) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.get(`${this.apiServer}/user_get`, {}, { - headers: headers - }); - } - - getAccessTokens(token, orgId) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId !== null && orgId !== 0) { - params.org_id = `eq.${orgId}`; - } - - return this.get(`${this.apiServer}/api_tokens`, params, { - headers: headers - }); - } - - getAccessToken(token, name, expires, orgId, isPersonal) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/api_token_create`, { - name: name, - expires: expires, - org_id: orgId, - is_personal: isPersonal - }, { - headers: headers - }); - } - - revokeAccessToken(token, id) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/api_token_revoke`, { id: id }, { - headers: headers - }); - } - - getCheckupReports(token, orgId, projectId, reportId) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId !== null && orgId !== 0) { - params.org_id = `eq.${orgId}`; - } - - if (projectId !== null && projectId !== 0) { - params.project_id = `eq.${projectId}`; - } - - if (typeof reportId !== 'undefined' && reportId !== 0) { - params.id = `eq.${reportId}`; - } - - return this.get(`${this.apiServer}/checkup_reports`, params, { - headers: headers - }); - } - - getCheckupReportFiles(token, reportId, type, orderBy, orderDirection) { - let headers = { - Authorization: 'Bearer ' + token - }; - let params = { - checkup_report_id: `eq.${reportId}` - }; - - if (type) { - params.type = `eq.${type}`; - } - - if (orderBy && orderDirection) { - params.order = `${orderBy}.${orderDirection}`; - } - - return this.get(`${this.apiServer}/checkup_report_files`, params, { - headers: headers - }); - } - - getCheckupReportFile(token, projectId, reportId, fileId, type) { - let headers = { - Authorization: 'Bearer ' + token - }; - let params = { - project_id: `eq.${projectId}`, - checkup_report_id: `eq.${reportId}`, - type: `eq.${type}` - }; - - if (fileId === parseInt(fileId, 10)) { - params.id = `eq.${fileId}`; - } else { - params.filename = `eq.${fileId}`; - } - - return this.get(`${this.apiServer}/checkup_report_file_data`, params, { - headers: headers - }); - } - - getProjects(token, orgId) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId) { - params.org_id = `eq.${orgId}`; - } - - return this.get(`${this.apiServer}/projects`, params, { - headers: headers - }); - } - - getJoeSessions(token, orgId, projectId) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId !== null && orgId !== 0) { - params.org_id = `eq.${orgId}`; - } - - if (projectId !== null && projectId !== 0) { - params.project_id = `eq.${projectId}`; - } - - return this.get(`${this.apiServer}/joe_sessions`, params, { - headers: headers - }); - } - - getJoeSessionCommands(token, - { orgId, session, fingerprint, command, project, - author, startAt, limit, lastId, search, isFavorite }) { - const params = {}; - const headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId && orgId !== 0) { - params['org_id'] = `eq.${orgId}`; - } - - if (session && session !== 0) { - params['joe_session_id'] = `eq.${session}`; - } - - if (fingerprint) { - params.fingerprint = `ilike.${fingerprint}*`; - } - - if (command) { - params.command = `eq.${command}`; - } - - if (startAt) { - params.created_at = `gt.${startAt}`; - } - - if (limit) { - params.limit = limit; - } - - if (lastId) { - // backend order by id.desc - params.id = `lt.${lastId}`; - } - - if (project) { - // backend order by id.desc - params.project_name = `ilike.${project}*`; - } - - if (author) { - params.or = `(username.ilike.${author}*,` + - `useremail.ilike.${author}*,` + - `slack_username.ilike.${author}*)`; - } - - if (search) { - let searchText = encodeURIComponent(search); - params.tsv = `fts(simple).${searchText}`; - } - - if (isFavorite) { - params.is_favorite = `gt.0`; - } - - - return this.get(`${this.apiServer}/joe_session_commands`, params, { - headers: headers - }); - } - - getJoeSessionCommand(token, orgId, commandId) { - let params = { org_id: `eq.${orgId}` }; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (!!commandId && commandId !== 0) { - params.id = `eq.${commandId}`; - } - - return this.get(`${this.apiServer}/joe_session_commands`, params, { - headers: headers - }); - } - - getOrgs(token, orgId) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId) { - params.id = `eq.${orgId}`; - } - - return this.get(`${this.apiServer}/orgs`, params, { - headers: headers - }); - } - - getOrgUsers(token, orgId) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId) { - params.id = `eq.${orgId}`; - } - - return this.get(`${this.apiServer}/org_users`, params, { - headers: headers - }); - } - - updateOrg(token, orgId, orgData) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token, - prefer: 'return=representation' - }; - - if (orgData.name) { - params.name = orgData.name; - } - - if (orgData.alias) { - params.alias = orgData.alias; - } - - if (typeof orgData.users_autojoin !== 'undefined') { - params.users_autojoin = orgData.users_autojoin; - } - - if (typeof orgData.onboarding_text !== 'undefined') { - params.onboarding_text = orgData.onboarding_text; - } - - if (typeof orgData.oauth_allow_google !== 'undefined') { - params.oauth_allow_google = orgData.oauth_allow_google; - } - - if (typeof orgData.oauth_allow_linkedin !== 'undefined') { - params.oauth_allow_linkedin = orgData.oauth_allow_linkedin; - } - - if (typeof orgData.oauth_allow_github !== 'undefined') { - params.oauth_allow_github = orgData.oauth_allow_github; - } - - if (typeof orgData.oauth_allow_gitlab !== 'undefined') { - params.oauth_allow_gitlab = orgData.oauth_allow_gitlab; - } - - return this.patch(`${this.apiServer}/orgs?id=eq.` + orgId, params, { - headers: headers - }); - } - - createOrg(token, orgData) { - let params = { - name: orgData.name, - alias: orgData.alias - }; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgData.email_domain_autojoin) { - params.org_domain = orgData.email_domain_autojoin; - } - - if (typeof orgData.users_autojoin !== 'undefined') { - params.users_autojoin = orgData.users_autojoin; - } - - return this.post(`${this.apiServer}/rpc/user_create_org`, params, { - headers: headers - }); - } - - addOrgDomain(token, orgId, domain) { - let params = { - org_id: orgId, - domain_name: domain - }; - let headers = { - Authorization: 'Bearer ' + token, - prefer: 'return=representation' - }; - - return this.post(`${this.apiServer}/org_domains`, params, { - headers: headers - }); - } - - deleteOrgDomain(token, domainId) { - let headers = { - Authorization: 'Bearer ' + token, - prefer: 'return=representation' - }; - - return this.delete(`${this.apiServer}/org_domains?id=eq.${domainId}`, {}, { - headers: headers - }); - } - - inviteUser(token, orgId, email) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/user_invite_org_user`, { - org_id: orgId, - email: email - }, { - headers: headers - }); - } - - useDemoData(token) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/use_demo_data`, {}, { - headers: headers - }); - } - - getDbLabInstances(token, orgId, projectId, instanceId) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId !== null && orgId !== 0) { - params.org_id = `eq.${orgId}`; - } - if (projectId !== null && projectId !== 0) { - params.project_id = `eq.${projectId}`; - } - if (typeof instanceId !== 'undefined' && instanceId !== 0) { - params.id = `eq.${instanceId}`; - } - - return this.get(`${this.apiServer}/dblab_instances`, params, { - headers: headers - }); - } - - addDbLabInstance(token, instanceData) { - let headers = { - Authorization: 'Bearer ' + token - }; - let params = { - url: instanceData.url, - org_id: instanceData.orgId, - token: instanceData.instanceToken, - project: instanceData.project, - project_label: instanceData.projectLabel, - use_tunnel: instanceData.useTunnel, - ssh_server_url: instanceData.sshServerUrl - }; - - return this.post(`${this.apiServer}/rpc/dblab_instance_create`, params, { - headers: headers - }); - } - - editDbLabInstance(token, instanceData) { - let headers = { - Authorization: 'Bearer ' + token, - } - let params = { - url: instanceData.url, - instance_id: Number(instanceData.instanceId), - project_name: instanceData.project, - project_label: instanceData.projectLabel, - use_tunnel: instanceData.useTunnel, - ssh_server_url: instanceData.sshServerUrl, - } - - return this.post(`${this.apiServer}/rpc/dblab_instance_edit`, params, { - headers: headers, - }) - } - - destroyDbLabInstance(token, instanceId) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/dblab_instance_destroy`, { - instance_id: instanceId - }, { - headers: headers - }); - } - - getDbLabInstanceStatus(token, instanceId) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/dblab_instance_status_refresh`, { - instance_id: instanceId - }, { - headers: headers - }); - } - - checkDbLabInstanceUrl(token, url, verifyToken, useTunnel) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/dblab_instance_status`, { - url: url, - verify_token: verifyToken, - use_tunnel: useTunnel - }, { - headers: headers - }); - } - - getJoeInstances(token, orgId, projectId, instanceId) { - let params = {}; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (orgId !== null && orgId !== 0) { - params.org_id = `eq.${orgId}`; - } - if (typeof projectId !== 'undefined' && projectId !== 0) { - params.project_id = `eq.${projectId}`; - } - if (typeof instanceId !== 'undefined' && instanceId !== 0) { - params.id = `eq.${instanceId}`; - } - - return this.get(`${this.apiServer}/joe_instances`, params, { - headers: headers - }); - } - - getJoeInstanceChannels(token, instanceId) { - let params = { - instance_id: instanceId - }; - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.get(`${this.apiServer}/rpc/joe_instance_channels_get`, params, { - headers: headers - }); - } - - sendJoeInstanceCommand(token, instanceId, channelId, command, sessionId) { - let params = { - instance_id: instanceId, - channel_id: channelId, - command: command - }; - let headers = { - Authorization: 'Bearer ' + token - }; - - if (sessionId !== null && sessionId !== 0) { - params.session_id = sessionId; - } - - return this.post(`${this.apiServer}/rpc/joe_command_send`, params, { - headers: headers - }); - } - - getJoeInstanceMessages(token, channelId, sessionId) { - let params = { - channel_id: `eq.${channelId}`, - session_id: `eq.${sessionId}` - }; - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.get(`${this.apiServer}/joe_messages`, params, { - headers: headers - }); - } - - getJoeMessageArtifacts(token, messageId) { - let params = { - message_id: `eq.${messageId}` - }; - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.get(`${this.apiServer}/joe_message_artifacts`, params, { - headers: headers - }); - } - - addJoeInstance(token, instanceData) { - let headers = { - Authorization: 'Bearer ' + token - }; - let params = { - url: instanceData.url, - org_id: instanceData.orgId, - token: instanceData.verifyToken, - project: instanceData.project, - use_tunnel: instanceData.useTunnel, - dry_run: instanceData.dryRun - }; - - if (instanceData.useTunnel && instanceData.sshServerUrl) { - params.ssh_server_url = instanceData.sshServerUrl; - } - - return this.post(`${this.apiServer}/rpc/joe_instance_create`, params, { - headers: headers - }); - } - - destroyJoeInstance(token, instanceId) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/joe_instance_destroy`, { - instance_id: instanceId - }, { - headers: headers - }); - } - - deleteJoeSessions(token, ids) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/joe_session_delete`, { - ids: ids - }, { - headers: headers - }); - } - - deleteJoeCommands(token, ids) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/joe_command_delete`, { ids }, - { headers }); - } - - - deleteCheckupReports(token, ids) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/checkup_report_delete`, { - ids: ids - }, { - headers: headers - }); - } - - joeCommandFavorite(token, commandId, favorite) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/joe_command_favorite`, { - command_id: parseInt(commandId, 10), - favorite - }, { headers }); - } - - getSharedUrlData(uuid) { - return this.get(`${this.apiServer}/rpc/shared_url_get_data`, { uuid }, {}); - } - - getSharedUrl(token, org_id, object_type, object_id) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/shared_url_get`, { - org_id, - object_type, - object_id - }, { headers }); - } - - addSharedUrl(token, urlParams) { - let headers = { - Authorization: 'Bearer ' + token - }; - let params = { - org_id: urlParams.orgId, - url: urlParams.url, - object_type: urlParams.objectType, - object_id: urlParams.objectId - }; - - if (urlParams.uuid) { - params['uuid'] = urlParams.uuid; - } - - return this.post(`${this.apiServer}/rpc/shared_url_add`, params, { headers }); - } - - removeSharedUrl(token, org_id, id) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post(`${this.apiServer}/rpc/shared_url_remove`, { org_id, id }, { headers }); - } - - subscribeBilling(token, org_id, payment_method_id) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post( - `${this.apiServer}/rpc/billing_subscribe`, - { org_id, payment_method_id }, - { headers } - ); - } - - getBillingDataUsage(token, orgId) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.get( - `${this.apiServer}/billing_data_usage`, - { org_id: `eq.${orgId}` }, - { headers } - ); - } - - getDbLabSessions(token, { orgId, projectId, instanceId, limit, lastId }) { - let headers = { - Authorization: 'Bearer ' + token, - Prefer: 'count=exact' - }; - - let params = { - org_id: `eq.${orgId}` - }; - - if (typeof projectId !== 'undefined' && projectId) { - params.project_id = `eq.$(projectId)`; - } - - if (typeof instanceId !== 'undefined' && instanceId) { - params.instance_id = `eq.$(instanceId)`; - } - - if (lastId) { - params.id = `lt.${lastId}`; - } - - if (limit) { - params.limit = limit; - } - - return this.get(`${this.apiServer}/dblab_sessions`, params, { - headers: headers - }); - } - - getDbLabSession(token, sessionId) { - let headers = { - Authorization: 'Bearer ' + token - }; - - let params = { - id: `eq.${sessionId}` - }; - - return this.get(`${this.apiServer}/dblab_sessions`, params, { - headers: headers - }); - } - - getDbLabSessionLogs(token, { sessionId, limit, lastId }) { - let headers = { - Authorization: 'Bearer ' + token, - Prefer: 'count=exact' - }; - - let params = { - dblab_session_id: `eq.${sessionId}` - }; - - if (lastId) { - params.id = `lt.${lastId}`; - } - - if (limit) { - params.limit = limit; - } - - return this.get(`${this.apiServer}/dblab_session_logs`, params, { - headers: headers - }); - } - - getDbLabSessionArtifacts(token, sessionId) { - let headers = { - Authorization: 'Bearer ' + token, - Prefer: 'count=exact' - }; - - let params = { - dblab_session_id: `eq.${sessionId}` - }; - - return this.get(`${this.apiServer}/dblab_session_artifacts`, params, { - headers: headers - }); - } - - getDbLabSessionArtifact(token, sessionId, artifactType) { - let headers = { - Authorization: 'Bearer ' + token, - Prefer: 'count=exact' - }; - - let params = { - dblab_session_id: `eq.${sessionId}`, - artifact_type: `eq.${artifactType}` - }; - - return this.get(`${this.apiServer}/dblab_session_artifacts_data`, params, { - headers: headers - }); - } - - updateOrgUser(token, org_id, user_id, role_id) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post( - `${this.apiServer}/rpc/user_update_org_user`, - { org_id, user_id, role_id }, - { headers } - ); - } - - deleteOrgUser(token, org_id, user_id) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post( - `${this.apiServer}/rpc/user_delete_org_user`, - { org_id, user_id }, - { headers } - ); - } - - getAuditLog(token, { orgId, lastId, limit }) { - let headers = { - Authorization: 'Bearer ' + token, - Prefer: 'count=exact' - }; - - let params = { - org_id: `eq.${orgId}` - }; - - if (lastId) { - params.id = `lt.${lastId}`; - } - - if (limit) { - params.limit = limit; - } - - return this.get(`${this.apiServer}/audit_log`, params, { - headers: headers - }); - } - - sendUserCode(token) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post( - `${this.apiServer}/rpc/user_send_code`, - {}, - { headers } - ); - } - - confirmUserEmail(token, verification_code) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post( - `${this.apiServer}/rpc/user_confirm_email`, - { verification_code }, - { headers } - ); - } - - confirmTosAgreement(token) { - let headers = { - Authorization: 'Bearer ' + token - }; - - return this.post( - `${this.apiServer}/rpc/user_confirm_tos_agreement`, - {}, - { headers } - ); - } -} - -export default Api; diff --git a/ui/packages/platform/src/api/billing/getPaymentMethods.ts b/ui/packages/platform/src/api/billing/getPaymentMethods.ts deleted file mode 100644 index 51191867..00000000 --- a/ui/packages/platform/src/api/billing/getPaymentMethods.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { request } from 'helpers/request' - -export const getPaymentMethods = async (orgId: number) => { - const response = await request(`/rpc/billing_payment_methods`, { - headers: { - Accept: 'application/vnd.pgrst.object+json', - }, - method: 'POST', - body: JSON.stringify({ - org_id: orgId, - }), - }) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/billing/getSubscription.ts b/ui/packages/platform/src/api/billing/getSubscription.ts deleted file mode 100644 index 2dc4cbf8..00000000 --- a/ui/packages/platform/src/api/billing/getSubscription.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { request } from 'helpers/request' - -export const getSubscription = async (orgId: number) => { - const response = await request(`/rpc/billing_subscriptions`, { - headers: { - Accept: 'application/vnd.pgrst.object+json', - }, - method: 'POST', - body: JSON.stringify({ - org_id: orgId, - }), - }) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/billing/startBillingSession.ts b/ui/packages/platform/src/api/billing/startBillingSession.ts deleted file mode 100644 index fcfcd81e..00000000 --- a/ui/packages/platform/src/api/billing/startBillingSession.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { request } from 'helpers/request' - -export const startBillingSession = async (orgId: number, returnUrl: string) => { - const response = await request(`/rpc/billing_portal_start_session`, { - headers: { - Accept: 'application/vnd.pgrst.object+json', - }, - method: 'POST', - body: JSON.stringify({ - org_id: orgId, - return_url: returnUrl, - }), - }) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : await response.json(), - } -} diff --git a/ui/packages/platform/src/api/clones/createClone.ts b/ui/packages/platform/src/api/clones/createClone.ts deleted file mode 100644 index 8a8b1f8b..00000000 --- a/ui/packages/platform/src/api/clones/createClone.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { CloneDto, formatCloneDto } from '@postgres.ai/shared/types/api/entities/clone' - -import { request } from 'helpers/request' - -type Req = { - instanceId: string - cloneId: string - snapshotId: string - dbUser: string - dbPassword: string - isProtected: boolean -} - -export const createClone = async (req: Req) => { - const response = await request('/rpc/dblab_clone_create', { - method: 'POST', - body: JSON.stringify({ - instance_id: req.instanceId, - clone_data: { - id: req.cloneId, - snapshot: { - id: req.snapshotId, - }, - db: { - username: req.dbUser, - password: req.dbPassword, - }, - protected: req.isProtected, - }, - }), - }) - - return { - response: response.ok ? formatCloneDto(await response.json() as CloneDto) : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/clones/destroyClone.ts b/ui/packages/platform/src/api/clones/destroyClone.ts deleted file mode 100644 index 96ebae5b..00000000 --- a/ui/packages/platform/src/api/clones/destroyClone.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { DestroyClone } from '@postgres.ai/shared/types/api/endpoints/destroyClone' - -import { request } from 'helpers/request' - -export const destroyClone: DestroyClone = async (req) => { - const response = await request('/rpc/dblab_clone_destroy', { - method: 'POST', - body: JSON.stringify({ - instance_id: req.instanceId, - clone_id: req.cloneId, - }), - }) - - return { - response: response.ok ? true : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/clones/getClone.ts b/ui/packages/platform/src/api/clones/getClone.ts deleted file mode 100644 index 629d9606..00000000 --- a/ui/packages/platform/src/api/clones/getClone.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CloneDto, formatCloneDto } from '@postgres.ai/shared/types/api/entities/clone' - -import { request } from 'helpers/request' - -type Request = { - instanceId: string - cloneId: string -} - -export const getClone = async (req: Request) => { - const response = await request('/rpc/dblab_clone_status', { - method: 'POST', - body: JSON.stringify({ - instance_id: req.instanceId, - clone_id: req.cloneId, - }) - }) - - return { - response: response.ok ? formatCloneDto(await response.json() as CloneDto) : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/clones/resetClone.ts b/ui/packages/platform/src/api/clones/resetClone.ts deleted file mode 100644 index 4feaebbd..00000000 --- a/ui/packages/platform/src/api/clones/resetClone.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { ResetClone } from '@postgres.ai/shared/types/api/endpoints/resetClone' - -import { request } from 'helpers/request' - -export const resetClone: ResetClone = async (req) => { - const response = await request('/rpc/dblab_clone_reset', { - method: 'post', - body: JSON.stringify({ - instance_id: req.instanceId, - clone_id: req.cloneId, - reset_options: { - snapshotID: req.snapshotId, - latest: false, - }, - }), - }) - - return { - response: response.ok ? true : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/clones/updateClone.ts b/ui/packages/platform/src/api/clones/updateClone.ts deleted file mode 100644 index fb61b1ae..00000000 --- a/ui/packages/platform/src/api/clones/updateClone.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { UpdateClone } from '@postgres.ai/shared/types/api/endpoints/updateClone' - -import { request } from 'helpers/request' - -export const updateClone: UpdateClone = async (req) => { - const response = await request('/rpc/dblab_clone_update', { - method: 'POST', - body: JSON.stringify({ - instance_id: req.instanceId, - clone_id: req.cloneId, - clone: { - protected: req.clone.isProtected, - }, - }), - }) - - return { - response: response.ok ? true : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/cloud/getCloudImages.ts b/ui/packages/platform/src/api/cloud/getCloudImages.ts deleted file mode 100644 index c105cfc5..00000000 --- a/ui/packages/platform/src/api/cloud/getCloudImages.ts +++ /dev/null @@ -1,38 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { request } from 'helpers/request' - -export interface CloudImage { - api_name: string - os_name: string - os_version: string - arch: string - cloud_provider: string - region: string - native_os_image: string - release: string -} - -export interface CloudImagesRequest { - os_name: string - os_version: string - arch: string - cloud_provider: string - region: string -} - -export const getCloudImages = async (req: CloudImagesRequest) => { - const response = await request( - `/cloud_os_images?os_name=eq.${req.os_name}&os_version=eq.${req.os_version}&arch=eq.${req.arch}&cloud_provider=eq.${req.cloud_provider}®ion=eq.${req.region}`, - ) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/cloud/getCloudInstances.ts b/ui/packages/platform/src/api/cloud/getCloudInstances.ts deleted file mode 100644 index 7467dbea..00000000 --- a/ui/packages/platform/src/api/cloud/getCloudInstances.ts +++ /dev/null @@ -1,41 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { request } from 'helpers/request' - -export interface CloudInstance { - api_name: string - arch: string - vcpus: number - ram_gib: number - dle_se_price_hourly: number - cloud_provider: string - only_in_regions: boolean | null - native_name: string - native_vcpus: number - native_ram_gib: number - native_reference_price_hourly: number - native_reference_price_currency: string - native_reference_price_region: string - native_reference_price_revision_date: string -} - -export interface CloudInstancesRequest { - provider: string - region: string -} - -export const getCloudInstances = async (req: CloudInstancesRequest) => { - const response = await request( - `/cloud_instances?cloud_provider=eq.${req.provider}&only_in_regions&only_in_regions=ov.{all,${req.region}}&order=vcpus.asc,ram_gib.asc`, - ) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/cloud/getCloudProviders.ts b/ui/packages/platform/src/api/cloud/getCloudProviders.ts deleted file mode 100644 index a46983dd..00000000 --- a/ui/packages/platform/src/api/cloud/getCloudProviders.ts +++ /dev/null @@ -1,22 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { request } from 'helpers/request' - -export interface CloudProvider { - api_name: string - label: string -} - -export const getCloudProviders = async () => { - const response = await request('/cloud_providers') - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/cloud/getCloudRegions.ts b/ui/packages/platform/src/api/cloud/getCloudRegions.ts deleted file mode 100644 index 80b0ccfc..00000000 --- a/ui/packages/platform/src/api/cloud/getCloudRegions.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { request } from 'helpers/request' - -export interface CloudRegion { - api_name: string - cloud_provider: string - label: string - native_code: string - world_part: string -} - -export const getCloudRegions = async (req: string) => { - const response = await request(`/cloud_regions?cloud_provider=eq.${req}`) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/cloud/getCloudVolumes.ts b/ui/packages/platform/src/api/cloud/getCloudVolumes.ts deleted file mode 100644 index 68c2d4c3..00000000 --- a/ui/packages/platform/src/api/cloud/getCloudVolumes.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { request } from 'helpers/request' - -export interface CloudVolumes { - api_name: string - type: string - cloud_provider: string - native_name: string - native_reference_price_per_1000gib_per_hour: number - native_reference_price_currency: string - native_reference_price_region: string - native_reference_price_revision_date: string -} - -export const getCloudVolumes = async (cloud_provider: string) => { - const response = await request( - `/cloud_volumes?cloud_provider=eq.${cloud_provider}`, - ) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/cloud/getOrgKeys.ts b/ui/packages/platform/src/api/cloud/getOrgKeys.ts deleted file mode 100644 index 804befc5..00000000 --- a/ui/packages/platform/src/api/cloud/getOrgKeys.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { request } from 'helpers/request' - -export const getOrgKeys = async (org_id: number) => { - const response = await request(`/org_keys?org_id=eq.${org_id}`) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/configs/getConfig.ts b/ui/packages/platform/src/api/configs/getConfig.ts deleted file mode 100644 index 22ed8fb4..00000000 --- a/ui/packages/platform/src/api/configs/getConfig.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { formatConfig } from '@postgres.ai/shared/types/api/entities/config' -import { request } from 'helpers/request' - -export const getConfig = async () => { - const response = await request('/admin/config') - - return { - response: response.ok ? formatConfig(await response.json()) : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/configs/getFullConfig.ts b/ui/packages/platform/src/api/configs/getFullConfig.ts deleted file mode 100644 index abf0338d..00000000 --- a/ui/packages/platform/src/api/configs/getFullConfig.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { request } from 'helpers/request' -export const getFullConfig = async () => { - const response = await request('/admin/config.yaml') - .then((res) => res.blob()) - .then((blob) => blob.text()) - .then((yamlAsString) => { - return yamlAsString - }) - - return { - response: response ? response : null, - error: response && null, - } -} diff --git a/ui/packages/platform/src/api/configs/getSeImages.ts b/ui/packages/platform/src/api/configs/getSeImages.ts deleted file mode 100644 index 17f2af97..00000000 --- a/ui/packages/platform/src/api/configs/getSeImages.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { request } from 'helpers/request' - -export const getSeImages = async ({ - packageGroup, - platformUrl, -}: { - packageGroup: string - platformUrl?: string -}) => { - const response = await request( - `/dblab_se_images?package_group=eq.${packageGroup} - `, - {}, - platformUrl, - ) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/configs/getTaskState.ts b/ui/packages/platform/src/api/configs/getTaskState.ts deleted file mode 100644 index a614c105..00000000 --- a/ui/packages/platform/src/api/configs/getTaskState.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { simpleInstallRequest } from 'helpers/simpleInstallRequest' - -export const getTaskState = async (req: { taskID: string; userID?: number }) => { - const response = await simpleInstallRequest( - `/state/${req.taskID}`, - { - method: 'GET', - }, - req?.userID, - ) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : await response.json(), - } -} diff --git a/ui/packages/platform/src/api/configs/initStreamLogs.ts b/ui/packages/platform/src/api/configs/initStreamLogs.ts deleted file mode 100644 index 4e010ccb..00000000 --- a/ui/packages/platform/src/api/configs/initStreamLogs.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { SI_API_SERVER } from 'helpers/simpleInstallRequest' - -export const initStreamLogs = (taskId: string, otCode: string): WebSocket => { - let url = new URL( - `${SI_API_SERVER.replace( - 'https', - 'wss', - )}/stream-logs/${taskId}?otCode=${otCode}`, - ) - return new WebSocket(url) -} diff --git a/ui/packages/platform/src/api/configs/launchDeploy.ts b/ui/packages/platform/src/api/configs/launchDeploy.ts deleted file mode 100644 index 6b3b04b4..00000000 --- a/ui/packages/platform/src/api/configs/launchDeploy.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { simpleInstallRequest } from 'helpers/simpleInstallRequest' -import { initialState } from 'components/DbLabInstanceForm/reducer' -import { DEBUG_API_SERVER, sePackageTag } from 'components/DbLabInstanceForm/utils' - -const API_SERVER = process.env.REACT_APP_API_SERVER - -const formatExtraEnvs = (extraEnvs: { [key: string]: string }) => { - return Object.entries(extraEnvs) - .filter(([key, value]) => value) - .map(([key, value]) => { - if (key === 'GCP_SERVICE_ACCOUNT_CONTENTS') { - return `${key}=${value.replace(/\n\s+/g, '')}` - } - return `${key}=${value}` - }) -} - -export const launchDeploy = async ({ - state, - userID, - orgKey, - extraEnvs, - cloudImage, - launchType, -}: { - state: typeof initialState - orgKey: string - userID?: number - extraEnvs: { - [key: string]: string - } - cloudImage: string - launchType: 'cluster' | 'instance' -}) => { - const instanceBody = { - playbook: 'deploy_dle.yml', - provision: state.provider, - server: { - name: state.name, - serverType: state.instanceType.native_name, - image: cloudImage, - location: state.location.native_code, - }, - image: `postgresai/dle-se-ansible:${sePackageTag}`, - extraVars: [ - `provision=${state.provider}`, - `server_name=${state.name}`, - `platform_project_name=${state.name}`, - `server_type=${state.instanceType.native_name}`, - `server_image=${cloudImage}`, - `server_location=${state.location.native_code}`, - `volume_size=${state.storage}`, - `dblab_engine_version=${state.tag}`, - `zpool_datasets_number=${state.snapshots}`, - `dblab_engine_verification_token=${state.verificationToken}`, - `platform_org_key=${orgKey}`, - ...(state.publicKeys - ? // eslint-disable-next-line no-useless-escape - [`ssh_public_keys=\"${state.publicKeys}\"`] - : []), - ...(API_SERVER === DEBUG_API_SERVER - ? [`platform_url=https://v2.postgres.ai/api/general`] - : []), - ], - extraEnvs: formatExtraEnvs(extraEnvs), - } - - const clusterBody = { - playbook: 'deploy_pgcluster.yml', - provision: state.provider, - server: { - name: state.name, - serverType: state.instanceType.native_name, - image: cloudImage, - location: state.location.native_code, - }, - image: 'vitabaks/postgresql_cluster:cloud', - extraVars: [ - `ansible_user=${state.provider === "aws" ? 'ubuntu' : 'root'}`, - `provision=${state.provider}`, - `servers_count=${state.numberOfInstances}`, - `server_type=${state.instanceType.native_name}`, - `server_image=${cloudImage}`, - `server_location=${state.location.native_code}`, - `volume_size=${state.storage}`, - `postgresql_version=${state.version}`, - `database_public_access=${state.database_public_access}`, - `database_public_access=${state.database_public_access}`, - `with_haproxy_load_balancing=${state.with_haproxy_load_balancing}`, - `pgbouncer_install=${state.pgbouncer_install}`, - `synchronous_mode=${state.synchronous_mode}`, - ...(state.synchronous_mode ? [`synchronous_node_count=${state.synchronous_node_count}`] : []), - `netdata_install=${state.netdata_install}`, - `patroni_cluster_name=${state.name}`, - `platform_org_key=${orgKey}`, - ...(state.publicKeys - ? // eslint-disable-next-line no-useless-escape - [`ssh_public_keys=\"${state.publicKeys}\"`] - : []), - ...(API_SERVER === DEBUG_API_SERVER - ? [`platform_url=https://v2.postgres.ai/api/general`] - : []), - ], - extraEnvs: formatExtraEnvs(extraEnvs), - } - - const response = await simpleInstallRequest( - '/launch', - { - method: 'POST', - body: JSON.stringify( - launchType === 'cluster' ? clusterBody : instanceBody, - ), - }, - userID, - ) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : await response.json(), - } -} diff --git a/ui/packages/platform/src/api/configs/regenerateCode.ts b/ui/packages/platform/src/api/configs/regenerateCode.ts deleted file mode 100644 index 0e15b0a2..00000000 --- a/ui/packages/platform/src/api/configs/regenerateCode.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { simpleInstallRequest } from 'helpers/simpleInstallRequest' - -export const regenerateCode = async (req: { - taskID: string - userID?: number -}) => { - const response = await simpleInstallRequest( - '/regenerate-code', - { - method: 'POST', - body: JSON.stringify({ - taskID: req.taskID, - }), - }, - req?.userID, - ) - - return { - response: response.ok ? await response.json() : null, - error: response.ok ? null : await response.json(), - } -} diff --git a/ui/packages/platform/src/api/configs/testDbSource.ts b/ui/packages/platform/src/api/configs/testDbSource.ts deleted file mode 100644 index 07817587..00000000 --- a/ui/packages/platform/src/api/configs/testDbSource.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { dbSource } from '@postgres.ai/shared/types/api/entities/dbSource' -import { request } from 'helpers/request' - -export const testDbSource = async (req: dbSource) => { - const response = await request('/admin/test-db-source', { - method: 'POST', - body: JSON.stringify({ - host: req.host, - port: req.port.toString(), - dbname: req.dbname, - username: req.username, - password: req.password, - db_list: req.db_list - }), - }) - - return { - response: response.ok ? await response.json(): null, - error: response.ok ? null : await response.json() - } -} diff --git a/ui/packages/platform/src/api/configs/updateConfig.ts b/ui/packages/platform/src/api/configs/updateConfig.ts deleted file mode 100644 index 9c40b4f1..00000000 --- a/ui/packages/platform/src/api/configs/updateConfig.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - postUniqueCustomOptions, - postUniqueDatabases, -} from '@postgres.ai/shared/pages/Configuration/utils' -import { Config } from '@postgres.ai/shared/types/api/entities/config' -import { request } from 'helpers/request' - -export const updateConfig = async (req: Config) => { - const response = await request('/admin/config', { - method: 'POST', - body: JSON.stringify({ - global: { - debug: req.debug, - }, - databaseContainer: { - dockerImage: req.dockerPath, - }, - databaseConfigs: { - configs: { - shared_buffers: req.sharedBuffers, - shared_preload_libraries: req.sharedPreloadLibraries, - ...(req.tuningParams as unknown as { [key: string]: string }), - }, - }, - retrieval: { - refresh: { - timetable: req.timetable, - }, - spec: { - logicalDump: { - options: { - databases: postUniqueDatabases(req.databases), - customOptions: postUniqueCustomOptions(req.pgDumpCustomOptions), - parallelJobs: req.dumpParallelJobs, - ignoreErrors: req.dumpIgnoreErrors, - source: { - connection: { - dbname: req.dbname, - host: req.host, - port: req.port, - username: req.username, - password: req.password, - }, - }, - }, - }, - logicalRestore: { - options: { - customOptions: postUniqueCustomOptions( - req.pgRestoreCustomOptions, - ), - parallelJobs: req.restoreParallelJobs, - ignoreErrors: req.restoreIgnoreErrors, - }, - }, - }, - }, - }), - }) - - return { - response: response.ok ? response : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/engine/getEngine.ts b/ui/packages/platform/src/api/engine/getEngine.ts deleted file mode 100644 index 59680981..00000000 --- a/ui/packages/platform/src/api/engine/getEngine.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { request } from 'helpers/request' -import { - EngineDto, - formatEngineDto, -} from '@postgres.ai/shared/types/api/endpoints/getEngine' - -export const getEngine = async () => { - const response = await request('/healthz') - - return { - response: response.ok - ? formatEngineDto((await response.json()) as EngineDto) - : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/engine/getWSToken.ts b/ui/packages/platform/src/api/engine/getWSToken.ts deleted file mode 100644 index 3a7872ce..00000000 --- a/ui/packages/platform/src/api/engine/getWSToken.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { request } from 'helpers/request' -import { formatWSTokenDto, WSTokenDTO } from '@postgres.ai/shared/types/api/entities/wsToken' -import { GetWSToken } from "@postgres.ai/shared/types/api/endpoints/getWSToken"; - -export const getWSToken: GetWSToken = async (req ) => { - const response = await request('/admin/ws-auth') - - return { - response: response.ok - ? formatWSTokenDto((await response.json()) as WSTokenDTO) - : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/engine/initWS.ts b/ui/packages/platform/src/api/engine/initWS.ts deleted file mode 100644 index 74fd0164..00000000 --- a/ui/packages/platform/src/api/engine/initWS.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { InitWS } from "@postgres.ai/shared/types/api/endpoints/initWS"; -import { WS_URL_PREFIX } from 'config/env' - -export const initWS: InitWS = (path: string, token: string): WebSocket => { - let url = new URL(WS_URL_PREFIX + path, window.location.href); - url.protocol = url.protocol.replace('http', 'ws'); - const wsAddr = url.href + '?token=' + token; - - return new WebSocket(wsAddr) -} diff --git a/ui/packages/platform/src/api/explain/depesz.js b/ui/packages/platform/src/api/explain/depesz.js deleted file mode 100644 index 13dde5b0..00000000 --- a/ui/packages/platform/src/api/explain/depesz.js +++ /dev/null @@ -1,36 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import 'es6-promise/auto'; -import 'whatwg-fetch'; - -class ExplainDepeszApi { - constructor(setting) { - this.server = setting.explainDepeszServer; - } - - post(url, data, options = {}) { - let fetchOptions = { - ...options, - method: 'post', - body: data - }; - - return fetch(url, fetchOptions); - } - - postPlan(plan, query) { - const formData = new FormData(); - formData.append('is_public', '0'); - formData.append('plan', plan); - formData.append('query', query); - - return this.post(this.server, formData); - } -} - -export default ExplainDepeszApi; diff --git a/ui/packages/platform/src/api/explain/pev2.js b/ui/packages/platform/src/api/explain/pev2.js deleted file mode 100644 index 92ceb51f..00000000 --- a/ui/packages/platform/src/api/explain/pev2.js +++ /dev/null @@ -1,37 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import 'es6-promise/auto'; -import 'whatwg-fetch'; - -class ExplainPev2Api { - constructor(setting) { - this.server = setting.explainPev2Server; - } - - post(url, data, options = {}) { - let fetchOptions = { - ...options, - method: 'post', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }; - - return fetch(url, fetchOptions); - } - - postPlan(plan, query) { - return this.post(`${this.server}/api/rpc/post_plan`, { - plan: plan, - query: query || '' - }); - } -} - -export default ExplainPev2Api; diff --git a/ui/packages/platform/src/api/getMeta.ts b/ui/packages/platform/src/api/getMeta.ts deleted file mode 100644 index d88573c6..00000000 --- a/ui/packages/platform/src/api/getMeta.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { request } from '@postgres.ai/shared/helpers/request' - -import { MetaDto, formatMetaDto } from 'types/api/entities/meta' - -export const getMeta = async () => { - const response = await request('/meta.json') - - return { - response: response.ok ? formatMetaDto(await response.json() as MetaDto) : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/instances/getInstance.ts b/ui/packages/platform/src/api/instances/getInstance.ts deleted file mode 100644 index fdaf3354..00000000 --- a/ui/packages/platform/src/api/instances/getInstance.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { - formatInstanceDto, - InstanceDto, -} from '@postgres.ai/shared/types/api/entities/instance' -import { GetInstance } from '@postgres.ai/shared/types/api/endpoints/getInstance' - -import { request } from 'helpers/request' - -export const getInstance: GetInstance = async (req) => { - const response = await request('/dblab_instances', { - params: { - id: `eq.${req.instanceId}`, - }, - }) - - return { - response: response.ok - ? ((await response.json()) as InstanceDto[]).map(formatInstanceDto)[0] ?? - null - : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/instances/getWSToken.ts b/ui/packages/platform/src/api/instances/getWSToken.ts deleted file mode 100644 index d5a67a3e..00000000 --- a/ui/packages/platform/src/api/instances/getWSToken.ts +++ /dev/null @@ -1,59 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2022, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { request } from 'helpers/request'; -import { localStorage } from "helpers/localStorage"; - -import { GetWSToken } from "@postgres.ai/shared/types/api/endpoints/getWSToken"; -import { formatWSTokenDto, WSTokenDTO } from "@postgres.ai/shared/types/api/entities/wsToken"; -import { request as requestCore } from "@postgres.ai/shared/helpers/request"; -import { formatInstanceDto, InstanceDto } from "@postgres.ai/shared/types/api/entities/instance"; - -export const getWSToken: GetWSToken = async (req) => { - // TODO: define instance and get a websocket token. - const instanceResponse = await request('/dblab_instances', { - params: { - id: `eq.${req.instanceId}`, - }, - }) - - if (!instanceResponse.ok) { - return { - response: null, - error: instanceResponse, - } - } - - const instance = (await instanceResponse.json() as InstanceDto[]).map(formatInstanceDto)[0] - - const authToken = localStorage.getAuthToken() - - if (instance.useTunnel) { - return { - response: null, - error: new Response(null, { - status: 400, - statusText: `Cannot connect to an instance that is using a tunnel`, - }) - } - } - - const response = await requestCore(instance.url + '/admin/ws-auth', { - headers: { - ...(authToken && {'Verification-Token': authToken}), - }, - }) - - return { - response: response.ok - ? formatWSTokenDto((await response.json()) as WSTokenDTO) - : null, - error: response.ok ? null : response, - } -} - - diff --git a/ui/packages/platform/src/api/instances/refreshInstance.ts b/ui/packages/platform/src/api/instances/refreshInstance.ts deleted file mode 100644 index 92777110..00000000 --- a/ui/packages/platform/src/api/instances/refreshInstance.ts +++ /dev/null @@ -1,24 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { RefreshInstance } from '@postgres.ai/shared/types/api/endpoints/refreshInstance' - -import { request } from 'helpers/request' - -export const refreshInstance: RefreshInstance = async (req) => { - const response = await request('/rpc/dblab_instance_status_refresh', { - method: 'post', - body: JSON.stringify({ - instance_id: req.instanceId, - }), - }) - - return { - response: response.ok ? true : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/api/snapshots/getSnapshots.ts b/ui/packages/platform/src/api/snapshots/getSnapshots.ts deleted file mode 100644 index 35d08eb3..00000000 --- a/ui/packages/platform/src/api/snapshots/getSnapshots.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { - SnapshotDto, - formatSnapshotDto, -} from '@postgres.ai/shared/types/api/entities/snapshot' -import { GetSnapshots } from '@postgres.ai/shared/types/api/endpoints/getSnapshots' - -import { request } from 'helpers/request' - -export const getSnapshots: GetSnapshots = async (req) => { - const response = await request('/rpc/dblab_instance_snapshots', { - method: 'POST', - body: JSON.stringify({ - instance_id: req.instanceId, - }), - }) - - return { - response: response.ok - ? ((await response.json()) as SnapshotDto[]).map(formatSnapshotDto) - : null, - error: response.ok ? null : response, - } -} diff --git a/ui/packages/platform/src/assets/explainSamples.ts b/ui/packages/platform/src/assets/explainSamples.ts deleted file mode 100644 index 87590585..00000000 --- a/ui/packages/platform/src/assets/explainSamples.ts +++ /dev/null @@ -1,600 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -const sampleExplain1 = - `[ - { - "Plan": { - "Node Type": "Limit", - "Startup Cost": 17024.84, - "Total Cost": 17024.87, - "Plan Rows": 10, - "Plan Width": 133, - "Actual Startup Time": 725.773, - "Actual Total Time": 725.775, - "Actual Rows": 10, - "Actual Loops": 1, - "Output": ["c.state", "cat.categoryname", "(sum(o.netamount))", "(sum(o.totalamount))"], - "Shared Hit Blocks": 23, - "Shared Read Blocks": 1392, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Sort", - "Parent Relationship": "Outer", - "Startup Cost": 17024.84, - "Total Cost": 17026.88, - "Plan Rows": 816, - "Plan Width": 133, - "Actual Startup Time": 725.771, - "Actual Total Time": 725.772, - "Actual Rows": 11, - "Actual Loops": 1, - "Output": ["c.state", "cat.categoryname", "(sum(o.netamount))", "(sum(o.totalamount))"], - "Sort Key": ["c.state", "(sum(o.totalamount))"], - "Sort Method": "top-N heapsort", - "Sort Space Used": 25, - "Sort Space Type": "Memory", - "Shared Hit Blocks": 23, - "Shared Read Blocks": 1392, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Aggregate", - "Strategy": "Hashed", - "Parent Relationship": "Outer", - "Startup Cost": 16994.41, - "Total Cost": 17006.65, - "Plan Rows": 816, - "Plan Width": 133, - "Actual Startup Time": 723.877, - "Actual Total Time": 724.417, - "Actual Rows": 832, - "Actual Loops": 1, - "Output": ["c.state", "cat.categoryname", "sum(o.netamount)", "sum(o.totalamount)"], - "Group Key": ["c.state", "cat.categoryname"], - "Shared Hit Blocks": 13, - "Shared Read Blocks": 1392, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Hash Join", - "Parent Relationship": "Outer", - "Join Type": "Inner", - "Startup Cost": 4966.48, - "Total Cost": 13742.65, - "Plan Rows": 325176, - "Plan Width": 133, - "Actual Startup Time": 118.314, - "Actual Total Time": 354.285, - "Actual Rows": 383270, - "Actual Loops": 1, - "Output": ["c.state", "o.netamount", "o.totalamount", "cat.categoryname"], - "Hash Cond": "(o.orderid = ch.orderid)", - "Shared Hit Blocks": 13, - "Shared Read Blocks": 1392, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Hash Join", - "Parent Relationship": "Outer", - "Join Type": "Inner", - "Startup Cost": 834.86, - "Total Cost": 4539.11, - "Plan Rows": 60350, - "Plan Width": 138, - "Actual Startup Time": 22.651, - "Actual Total Time": 133.484, - "Actual Rows": 60350, - "Actual Loops": 1, - "Output": [ - "o.netamount", - "o.totalamount", - "o.orderid", - "ol.orderid", - "cat.categoryname" - ], - "Hash Cond": "(ol.orderid = o.orderid)", - "Shared Hit Blocks": 9, - "Shared Read Blocks": 581, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Hash Join", - "Parent Relationship": "Outer", - "Join Type": "Inner", - "Startup Cost": 464.86, - "Total Cost": 2962.11, - "Plan Rows": 60350, - "Plan Width": 122, - "Actual Startup Time": 12.467, - "Actual Total Time": 85.647, - "Actual Rows": 60350, - "Actual Loops": 1, - "Output": ["ol.orderid", "cat.categoryname"], - "Hash Cond": "(ol.prod_id = p.prod_id)", - "Shared Hit Blocks": 4, - "Shared Read Blocks": 483, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Relation Name": "orderlines", - "Schema": "public", - "Alias": "ol", - "Startup Cost": 0.00, - "Total Cost": 988.50, - "Plan Rows": 60350, - "Plan Width": 8, - "Actual Startup Time": 0.005, - "Actual Total Time": 14.054, - "Actual Rows": 60350, - "Actual Loops": 1, - "Output": [ - "ol.orderlineid", - "ol.orderid", - "ol.prod_id", - "ol.quantity", - "ol.orderdate" - ], - "Shared Hit Blocks": 2, - "Shared Read Blocks": 383, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000 - }, - { - "Node Type": "Hash", - "Parent Relationship": "Inner", - "Startup Cost": 339.86, - "Total Cost": 339.86, - "Plan Rows": 10000, - "Plan Width": 122, - "Actual Startup Time": 12.446, - "Actual Total Time": 12.446, - "Actual Rows": 10000, - "Actual Loops": 1, - "Output": ["p.prod_id", "cat.categoryname"], - "Hash Buckets": 1024, - "Hash Batches": 1, - "Original Hash Batches": 1, - "Peak Memory Usage": 425, - "Shared Hit Blocks": 2, - "Shared Read Blocks": 100, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Hash Join", - "Parent Relationship": "Outer", - "Join Type": "Inner", - "Startup Cost": 1.36, - "Total Cost": 339.86, - "Plan Rows": 10000, - "Plan Width": 122, - "Actual Startup Time": 0.283, - "Actual Total Time": 9.015, - "Actual Rows": 10000, - "Actual Loops": 1, - "Output": ["p.prod_id", "cat.categoryname"], - "Hash Cond": "(p.category = cat.category)", - "Shared Hit Blocks": 2, - "Shared Read Blocks": 100, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Relation Name": "products", - "Schema": "public", - "Alias": "p", - "Startup Cost": 0.00, - "Total Cost": 201.00, - "Plan Rows": 10000, - "Plan Width": 8, - "Actual Startup Time": 0.003, - "Actual Total Time": 4.330, - "Actual Rows": 10000, - "Actual Loops": 1, - "Output": [ - "p.prod_id", - "p.category", - "p.title", - "p.actor", - "p.price", - "p.special", - "p.common_prod_id" - ], - "Shared Hit Blocks": 2, - "Shared Read Blocks": 99, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000 - }, - { - "Node Type": "Hash", - "Parent Relationship": "Inner", - "Startup Cost": 1.16, - "Total Cost": 1.16, - "Plan Rows": 16, - "Plan Width": 122, - "Actual Startup Time": 0.265, - "Actual Total Time": 0.265, - "Actual Rows": 16, - "Actual Loops": 1, - "Output": [ - "cat.categoryname", - "cat.category" - ], - "Hash Buckets": 1024, - "Hash Batches": 1, - "Original Hash Batches": 1, - "Peak Memory Usage": 1, - "Shared Hit Blocks": 0, - "Shared Read Blocks": 1, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Relation Name": "categories", - "Schema": "public", - "Alias": "cat", - "Startup Cost": 0.00, - "Total Cost": 1.16, - "Plan Rows": 16, - "Plan Width": 122, - "Actual Startup Time": 0.250, - "Actual Total Time": 0.252, - "Actual Rows": 16, - "Actual Loops": 1, - "Output": ["cat.categoryname", "cat.category"], - "Shared Hit Blocks": 0, - "Shared Read Blocks": 1, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000 - } - ] - } - ] - } - ] - } - ] - }, - { - "Node Type": "Hash", - "Parent Relationship": "Inner", - "Startup Cost": 220.00, - "Total Cost": 220.00, - "Plan Rows": 12000, - "Plan Width": 16, - "Actual Startup Time": 10.159, - "Actual Total Time": 10.159, - "Actual Rows": 12000, - "Actual Loops": 1, - "Output": ["o.netamount", "o.totalamount", "o.orderid"], - "Hash Buckets": 2048, - "Hash Batches": 1, - "Original Hash Batches": 1, - "Peak Memory Usage": 609, - "Shared Hit Blocks": 2, - "Shared Read Blocks": 98, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Relation Name": "orders", - "Schema": "public", - "Alias": "o", - "Startup Cost": 0.00, - "Total Cost": 220.00, - "Plan Rows": 12000, - "Plan Width": 16, - "Actual Startup Time": 0.008, - "Actual Total Time": 5.548, - "Actual Rows": 12000, - "Actual Loops": 1, - "Output": ["o.netamount", "o.totalamount", "o.orderid"], - "Shared Hit Blocks": 2, - "Shared Read Blocks": 98, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000 - } - ] - } - ] - }, - { - "Node Type": "Hash", - "Parent Relationship": "Inner", - "Startup Cost": 3377.25, - "Total Cost": 3377.25, - "Plan Rows": 60350, - "Plan Width": 7, - "Actual Startup Time": 95.610, - "Actual Total Time": 95.610, - "Actual Rows": 60350, - "Actual Loops": 1, - "Output": ["c.state", "ch.orderid"], - "Hash Buckets": 8192, - "Hash Batches": 1, - "Original Hash Batches": 1, - "Peak Memory Usage": 2239, - "Shared Hit Blocks": 4, - "Shared Read Blocks": 811, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Hash Join", - "Parent Relationship": "Outer", - "Join Type": "Inner", - "Startup Cost": 938.00, - "Total Cost": 3377.25, - "Plan Rows": 60350, - "Plan Width": 7, - "Actual Startup Time": 24.115, - "Actual Total Time": 74.639, - "Actual Rows": 60350, - "Actual Loops": 1, - "Output": ["c.state", "ch.orderid"], - "Hash Cond": "(ch.customerid = c.customerid)", - "Shared Hit Blocks": 4, - "Shared Read Blocks": 811, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Relation Name": "cust_hist", - "Schema": "public", - "Alias": "ch", - "Startup Cost": 0.00, - "Total Cost": 930.50, - "Plan Rows": 60350, - "Plan Width": 8, - "Actual Startup Time": 0.294, - "Actual Total Time": 11.812, - "Actual Rows": 60350, - "Actual Loops": 1, - "Output": ["ch.customerid", "ch.orderid", "ch.prod_id"], - "Shared Hit Blocks": 2, - "Shared Read Blocks": 325, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000 - }, - { - "Node Type": "Hash", - "Parent Relationship": "Inner", - "Startup Cost": 688.00, - "Total Cost": 688.00, - "Plan Rows": 20000, - "Plan Width": 7, - "Actual Startup Time": 23.786, - "Actual Total Time": 23.786, - "Actual Rows": 20000, - "Actual Loops": 1, - "Output": ["c.state", "c.customerid"], - "Hash Buckets": 2048, - "Hash Batches": 1, - "Original Hash Batches": 1, - "Peak Memory Usage": 743, - "Shared Hit Blocks": 2, - "Shared Read Blocks": 486, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000, - "Plans": [ - { - "Node Type": "Seq Scan", - "Parent Relationship": "Outer", - "Relation Name": "customers", - "Schema": "public", - "Alias": "c", - "Startup Cost": 0.00, - "Total Cost": 688.00, - "Plan Rows": 20000, - "Plan Width": 7, - "Actual Startup Time": 0.005, - "Actual Total Time": 16.771, - "Actual Rows": 20000, - "Actual Loops": 1, - "Output": ["c.state", "c.customerid"], - "Shared Hit Blocks": 2, - "Shared Read Blocks": 486, - "Shared Dirtied Blocks": 0, - "Shared Written Blocks": 0, - "Local Hit Blocks": 0, - "Local Read Blocks": 0, - "Local Dirtied Blocks": 0, - "Local Written Blocks": 0, - "Temp Read Blocks": 0, - "Temp Written Blocks": 0, - "I/O Read Time": 0.000, - "I/O Write Time": 0.000 - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - }, - "Planning Time": 26.171, - "Triggers": [ - ], - "Execution Time": 726.800 - } -]`; - -const explainSamples = [{ - value: sampleExplain1, - label: 'Sample 1' -}]; - -export default explainSamples; diff --git a/ui/packages/platform/src/assets/messages.ts b/ui/packages/platform/src/assets/messages.ts deleted file mode 100644 index c136a318..00000000 --- a/ui/packages/platform/src/assets/messages.ts +++ /dev/null @@ -1,11 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -export const messages = { - noPermission: 'You do not have permission to do this.', - noPermissionPage: 'You do not have permission to view this page.', -} diff --git a/ui/packages/platform/src/assets/plans.ts b/ui/packages/platform/src/assets/plans.ts deleted file mode 100644 index 86d0b075..00000000 --- a/ui/packages/platform/src/assets/plans.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { colors } from '@postgres.ai/shared/styles/colors' - -export const plans = { - ce: { - id: 'ce', - name: 'CE', - title: 'Postgres.ai Community Edition', - color: colors.secondary2.main, - limits: { - maxDblabInstances: 1, - maxJoeInstances: 1, - daysJoeHistory: 14, - emailDomainRestricted: true, - }, - }, - ee_gold_monthly: { - color: colors.pgaiOrange, - }, -} diff --git a/ui/packages/platform/src/assets/visualizeTypes.ts b/ui/packages/platform/src/assets/visualizeTypes.ts deleted file mode 100644 index 42ddfc93..00000000 --- a/ui/packages/platform/src/assets/visualizeTypes.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -export const visualizeTypes = { - depesz: 'depesz', - pev2: 'pev2', - flame: 'flame-graph', -} diff --git a/ui/packages/platform/src/components/AccessTokens/AccessTokens.tsx b/ui/packages/platform/src/components/AccessTokens/AccessTokens.tsx deleted file mode 100644 index 734e24bb..00000000 --- a/ui/packages/platform/src/components/AccessTokens/AccessTokens.tsx +++ /dev/null @@ -1,545 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component, MouseEvent } from 'react' -import { - Table, - TableBody, - TableCell, - TableHead, - TableRow, - TextField, - Button, - FormControlLabel, - Checkbox, -} from '@material-ui/core' - -import { HorizontalScrollContainer } from '@postgres.ai/shared/components/HorizontalScrollContainer' -import { styles } from '@postgres.ai/shared/styles/styles' -import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner' -import { - ClassesType, - RefluxTypes, - TokenRequestProps, -} from '@postgres.ai/platform/src/components/types' - -import Store from '../../stores/store' -import Actions from '../../actions/actions' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import ConsolePageTitle from '../ConsolePageTitle' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { DisplayTokenWrapper } from 'components/DisplayToken/DisplayTokenWrapper' -import { AccessTokensProps } from 'components/AccessTokens/AccessTokensWrapper' -import { FilteredTableMessage } from 'components/AccessTokens/FilteredTableMessage/FilteredTableMessage' - -interface AccessTokensWithStylesProps extends AccessTokensProps { - classes: ClassesType -} - -interface UserTokenData { - id: number - name: string - is_personal: boolean - username: string - created_formatted: string - expires_formatted: string - revoking: boolean -} - -interface AccessTokensState { - filterValue: string - data: { - auth: { - token: string - } | null - userTokens: { - orgId: number | null - isProcessing: boolean - isProcessed: boolean - data: UserTokenData[] - error: { - message: boolean - } - } - tokenRequest: TokenRequestProps - } - tokenName: string | null - tokenExpires: string | null - processed: boolean - isPersonal: boolean -} - -class AccessTokens extends Component< - AccessTokensWithStylesProps, - AccessTokensState -> { - state = { - filterValue: '', - data: { - auth: { - token: '', - }, - userTokens: { - orgId: null, - isProcessing: false, - isProcessed: false, - data: [], - error: { - message: false, - }, - }, - tokenRequest: { - isProcessing: false, - isProcessed: false, - data: { - name: '', - is_personal: false, - expires_at: '', - token: '', - }, - errorMessage: '', - error: false, - }, - }, - tokenName: '', - tokenExpires: '', - processed: false, - isPersonal: true, - } - - handleChange = (event: React.ChangeEvent) => { - const name = event.target.name - const value = event.target.value - - if (name === 'tokenName') { - this.setState({ tokenName: value }) - } else if (name === 'tokenExpires') { - this.setState({ tokenExpires: value }) - } else if (name === 'isPersonal') { - this.setState({ isPersonal: event.target.checked }) - } - } - unsubscribe: Function - componentDidMount() { - const that = this - const orgId = this.props.orgId ? this.props.orgId : null - const date = new Date() - const expiresDate = - date.getFullYear() + - 1 + - '-' + - ('0' + (date.getMonth() + 1)).slice(-2) + - '-' + - ('0' + date.getDate()).slice(-2) - - document.getElementsByTagName('html')[0].style.overflow = 'hidden' - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - const auth: AccessTokensState['data']['auth'] = - this.data && this.data.auth ? this.data.auth : null - const userTokens: AccessTokensState['data']['userTokens'] = - this.data && this.data.userTokens ? this.data.userTokens : null - const tokenRequest: TokenRequestProps = - this.data && this.data.tokenRequest ? this.data.tokenRequest : null - - that.setState({ data: this.data }) - - if ( - auth && - auth.token && - (!userTokens.isProcessed || orgId !== userTokens.orgId) && - !userTokens.isProcessing && - !userTokens.error - ) { - Actions.getAccessTokens(auth.token, orgId) - } - - if ( - tokenRequest && - tokenRequest.isProcessed && - !tokenRequest.error && - tokenRequest.data && - tokenRequest.data.name === that.state.tokenName && - tokenRequest.data.expires_at && - tokenRequest.data.token - ) { - that.setState({ - tokenName: '', - tokenExpires: expiresDate, - processed: false, - isPersonal: true, - }) - } - }) - - that.setState({ - tokenName: '', - tokenExpires: expiresDate, - processed: false, - }) - - Actions.refresh() - } - - componentWillUnmount() { - Actions.hideGeneratedAccessToken() - this.unsubscribe() - } - - addToken = () => { - const orgId = this.props.orgId ? this.props.orgId : null - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const tokenRequest = - this.state.data && this.state.data.tokenRequest - ? this.state.data.tokenRequest - : null - - if ( - this.state.tokenName === null || - this.state.tokenName === '' || - this.state.tokenExpires === null || - this.state.tokenExpires === '' - ) { - this.setState({ processed: true }) - return - } - - if (auth && auth.token && !tokenRequest?.isProcessing) { - Actions.getAccessToken( - auth.token, - this.state.tokenName, - this.state.tokenExpires, - orgId, - this.state.isPersonal, - ) - } - } - - getTodayDate() { - const date = new Date() - - return ( - date.getFullYear() + - '-' + - ('0' + (date.getMonth() + 1)).slice(-2) + - '-' + - ('0' + date.getDate()).slice(-2) - ) - } - - revokeToken = ( - _event: MouseEvent, - id: number, - name: string, - ) => { - const orgId = this.props.orgId ? this.props.orgId : null - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - - /* eslint no-alert: 0 */ - if ( - window.confirm( - 'Are you sure you want to revoke token "' + name + '"?', - ) === true - ) { - Actions.revokeAccessToken(auth?.token, orgId, id) - } - } - - filterTokensInputHandler = (event: React.ChangeEvent) => { - this.setState({ filterValue: event.target.value }) - } - - render() { - const { classes, orgPermissions, orgId } = this.props - const data = - this.state && this.state.data ? this.state.data.userTokens : null - const tokenRequest = - this.state && this.state.data && this.state.data.tokenRequest - ? this.state.data.tokenRequest - : null - const filteredTokens = data?.data?.filter( - (token: UserTokenData) => - token.name - ?.toLowerCase() - .indexOf((this.state.filterValue || '')?.toLowerCase()) !== -1, - ) - - const pageTitle = ( - 0 - ? { - filterValue: this.state.filterValue, - filterHandler: this.filterTokensInputHandler, - placeholder: 'Search access tokens by name', - } - : null - } - /> - ) - - let tokenDisplay = null - if ( - tokenRequest && - tokenRequest.isProcessed && - !tokenRequest.error && - tokenRequest.data && - tokenRequest.data.name && - tokenRequest.data.expires_at && - tokenRequest.data.token - ) { - tokenDisplay = ( -
-

- {tokenRequest.data.is_personal - ? 'Your new personal access token' - : 'New administrative access token'} -

- -
- ) - } - - let tokenError = null - if (tokenRequest && tokenRequest.error) { - tokenError = ( -
{tokenRequest.errorMessage}
- ) - } - - const tokenForm = ( -
-

Add token

-
- {tokenError} - - - - - - - } - label="Personal token" - /> - - -
-
- ) - - const breadcrumbs = ( - - ) - - if (this.state && this.state.data && this.state.data.userTokens?.error) { - return ( -
- {breadcrumbs} - - {pageTitle} - -

Access tokens

- -
- ) - } - - if ( - !data || - (data && data.isProcessing) || - (data && data.orgId !== orgId) - ) { - return ( -
- {breadcrumbs} - {pageTitle} - - -
- ) - } - - return ( -
- {breadcrumbs} - - {pageTitle} - - {tokenDisplay} - - {tokenForm} - -
- Users may manage their personal tokens only. Admins may manage their -  personal tokens, as well as administrative (impersonal) tokens -  used to organize infrastructure. Tokens of all types work in -  the context of a particular organization. -
- -
-

Active access tokens

- - {filteredTokens && filteredTokens.length > 0 ? ( - - - - - Name - Type - Creator - Created - Expires - Actions - - - - - {filteredTokens && - filteredTokens.length > 0 && - filteredTokens.map((t: UserTokenData) => { - return ( - - - {t.name} - - - {t.is_personal ? 'Personal' : 'Administrative'} - - - {t.username} - - - {t.created_formatted} - - - {t.expires_formatted} - - - - - - ) - })} - -
-
- ) : ( - - this.setState({ - filterValue: '', - }) - } - /> - )} - -
-
- ) - } -} - -export default AccessTokens diff --git a/ui/packages/platform/src/components/AccessTokens/AccessTokensWrapper.tsx b/ui/packages/platform/src/components/AccessTokens/AccessTokensWrapper.tsx deleted file mode 100644 index 237e8e47..00000000 --- a/ui/packages/platform/src/components/AccessTokens/AccessTokensWrapper.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import AccessTokens from 'components/AccessTokens/AccessTokens' -import { OrgPermissions } from 'components/types' -import { styles } from '@postgres.ai/shared/styles/styles' - -export interface AccessTokensProps { - project: string | undefined - orgId: number - org: string | number - orgPermissions: OrgPermissions -} - -export const AccessTokensWrapper = (props: AccessTokensProps) => { - const useStyles = makeStyles( - (theme) => ({ - root: { - ...(styles.root as Object), - display: 'flex', - flexDirection: 'column', - paddingBottom: '20px', - }, - container: { - display: 'flex', - flexWrap: 'wrap', - }, - textField: { - ...styles.inputField, - maxWidth: 400, - marginBottom: 15, - marginRight: theme.spacing(1), - marginTop: '16px', - }, - nameField: { - ...styles.inputField, - maxWidth: 400, - marginBottom: 15, - width: '400px', - marginRight: theme.spacing(1), - }, - addTokenButton: { - marginTop: 15, - height: '33px', - marginBottom: 10, - maxWidth: 'max-content', - }, - revokeButton: { - paddingRight: 5, - paddingLeft: 5, - paddingTop: 3, - paddingBottom: 3, - }, - errorMessage: { - color: 'red', - width: '100%', - }, - remark: { - width: '100%', - maxWidth: 960, - }, - bottomSpace: { - ...styles.bottomSpace, - }, - }), - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/AccessTokens/FilteredTableMessage/FilteredTableMessage.tsx b/ui/packages/platform/src/components/AccessTokens/FilteredTableMessage/FilteredTableMessage.tsx deleted file mode 100644 index c10af14b..00000000 --- a/ui/packages/platform/src/components/AccessTokens/FilteredTableMessage/FilteredTableMessage.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Button } from '@material-ui/core' -import { AuditLogData } from 'components/Audit/Audit' - -export const FilteredTableMessage = ({ - filterValue, - filteredItems, - clearFilter, - emptyState, -}: { - filterValue: string - filteredItems: string[] | never[] | AuditLogData[] | undefined | null - clearFilter: () => void - emptyState: string | JSX.Element -}) => { - if (filterValue && filteredItems?.length === 0) { - return ( - <> -
- No results found for {filterValue} -
- - - ) - } - - return <>{emptyState} -} diff --git a/ui/packages/platform/src/components/AddDbLabInstanceFormWrapper/AddDbLabInstanceFormWrapper.tsx b/ui/packages/platform/src/components/AddDbLabInstanceFormWrapper/AddDbLabInstanceFormWrapper.tsx deleted file mode 100644 index b38ac7fe..00000000 --- a/ui/packages/platform/src/components/AddDbLabInstanceFormWrapper/AddDbLabInstanceFormWrapper.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { styles } from '@postgres.ai/shared/styles/styles' -import AddDbLabInstanceForm from 'components/AddDbLabInstanceFormWrapper/AddDblabInstanceForm' -import { RouteComponentProps } from 'react-router' - -export interface DbLabInstanceFormProps { - edit?: boolean - orgId: number - project: string | undefined - history: RouteComponentProps['history'] - orgPermissions: { - dblabInstanceCreate?: boolean - } -} - -export const AddDbLabInstanceFormWrapper = (props: DbLabInstanceFormProps) => { - const useStyles = makeStyles( - { - textField: { - ...styles.inputField, - maxWidth: 400, - }, - errorMessage: { - marginTop: 10, - color: 'red', - }, - fieldBlock: { - width: '100%', - }, - urlOkIcon: { - color: 'green', - }, - urlOk: { display: 'flex', gap: 5, alignItems: 'center', color: 'green' }, - urlTextMargin: { - marginTop: 10, - }, - urlFailIcon: { - color: 'red', - }, - urlFail: { - display: 'flex', - gap: 5, - alignItems: 'center', - color: 'red', - }, - warning: { - color: '#801200', - fontSize: '0.9em', - }, - warningIcon: { - color: '#801200', - fontSize: '1.2em', - position: 'relative', - marginBottom: -3, - }, - }, - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/AddDbLabInstanceFormWrapper/AddDblabInstanceForm.tsx b/ui/packages/platform/src/components/AddDbLabInstanceFormWrapper/AddDblabInstanceForm.tsx deleted file mode 100644 index efddd8cb..00000000 --- a/ui/packages/platform/src/components/AddDbLabInstanceFormWrapper/AddDblabInstanceForm.tsx +++ /dev/null @@ -1,623 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { - Checkbox, - Grid, - Button, - TextField, - FormControlLabel, -} from '@material-ui/core' -import CheckCircleOutlineIcon from '@material-ui/icons/CheckCircleOutline' -import BlockIcon from '@material-ui/icons/Block' -import WarningIcon from '@material-ui/icons/Warning' - -import { styles } from '@postgres.ai/shared/styles/styles' -import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner' -import { - ClassesType, - ProjectProps, - RefluxTypes, -} from '@postgres.ai/platform/src/components/types' - -import Actions from '../../actions/actions' -import ConsolePageTitle from './../ConsolePageTitle' -import Store from '../../stores/store' -import Urls from '../../utils/urls' -import { generateToken, isHttps } from '../../utils/utils' -import { WarningWrapper } from 'components/Warning/WarningWrapper' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { DbLabInstanceFormProps } from 'components/DbLabInstanceForm/DbLabInstanceFormWrapper' - -interface DbLabInstanceFormWithStylesProps extends DbLabInstanceFormProps { - classes: ClassesType -} - -interface DbLabInstanceFormState { - data: { - auth: { - token: string | null - } | null - projects: ProjectProps - newDbLabInstance: { - isUpdating: boolean - isChecking: boolean - isChecked: boolean - isCheckProcessed: boolean - errorMessage: string - error: boolean - isProcessed: boolean - data: { - id: string - } - } | null - dbLabInstances: { - isProcessing: boolean - error: boolean - isProcessed: boolean - data: unknown - } | null - } | null - url: string - token: string | null - useTunnel: boolean - instanceID: string - project: string - project_label: string - errorFields: string[] - sshServerUrl: string -} - -class DbLabInstanceForm extends Component< - DbLabInstanceFormWithStylesProps, - DbLabInstanceFormState -> { - state = { - url: 'https://', - token: null, - useTunnel: false, - instanceID: '', - project: this.props.project ? this.props.project : '', - project_label: '', - errorFields: [''], - sshServerUrl: '', - data: { - auth: { - token: null, - }, - projects: { - data: [], - error: false, - isProcessing: false, - isProcessed: false, - }, - newDbLabInstance: { - isUpdating: false, - isChecked: false, - isChecking: false, - isCheckProcessed: false, - isProcessed: false, - error: false, - errorMessage: '', - data: { - id: '', - }, - }, - dbLabInstances: { - isProcessing: false, - error: false, - isProcessed: false, - data: '', - }, - }, - } - - unsubscribe: Function - componentDidMount() { - const that = this - const { orgId } = this.props - const url = window.location.href.split('/') - const instanceID = url[url.length - 1] - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - that.setState({ data: this.data, instanceID: instanceID }) - - const auth = this.data && this.data.auth ? this.data.auth : null - const projects = - this.data && this.data.projects ? this.data.projects : null - const dbLabInstances = - this.data && this.data.dbLabInstances ? this.data.dbLabInstances : null - - if (dbLabInstances.data) { - that.setState({ - project_label: - that.state.project_label || - dbLabInstances.data[instanceID]?.project_label_or_name, - token: dbLabInstances.data[instanceID]?.verify_token, - useTunnel: - that.state.useTunnel || dbLabInstances.data[instanceID]?.use_tunnel, - url: that.state.url || dbLabInstances.data[instanceID]?.url, - sshServerUrl: - that.state.sshServerUrl || - dbLabInstances.data[instanceID]?.ssh_server_url, - }) - } - - if ( - auth && - auth.token && - !projects.isProcessing && - !projects.error && - !projects.isProcessed - ) { - Actions.getProjects(auth.token, orgId) - } - - if ( - auth && - auth.token && - !dbLabInstances?.isProcessing && - !dbLabInstances?.error && - !dbLabInstances?.isProcessed - ) { - Actions.getDbLabInstances(auth.token, orgId, 0) - } - }) - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - Actions.resetNewDbLabInstance() - } - - buttonHandler = () => { - const orgId = this.props.orgId ? this.props.orgId : null - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const data = this.state.data ? this.state.data.newDbLabInstance : null - const errorFields = [] - - if (!this.state.url) { - errorFields.push('url') - } - - if (!this.state.project) { - errorFields.push('project') - } - - if (!this.state.token) { - errorFields.push('token') - } - - if (errorFields.length > 0) { - this.setState({ errorFields: errorFields }) - return - } - - this.setState({ errorFields: [] }) - - if ( - auth && - data && - !data.isUpdating && - this.state.url && - this.state.token && - this.state.project - ) { - Actions[`${this.props.edit ? 'edit' : 'add'}DbLabInstance`](auth.token, { - orgId: orgId, - project: this.state.project, - instanceId: this.props.edit ? this.state.instanceID : null, - projectLabel: this.state.project_label, - url: this.state.url, - instanceToken: this.state.token, - useTunnel: this.state.useTunnel, - sshServerUrl: this.state.sshServerUrl, - }) - } - } - - checkUrlHandler = () => { - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const data = this.state.data ? this.state.data.newDbLabInstance : null - const errorFields = [] - - if (!this.state.url) { - errorFields.push('url') - return - } - - if (auth && data && !data.isChecking && this.state.url) { - Actions.checkDbLabInstanceUrl( - auth.token, - this.state.url, - this.state.token, - this.state.useTunnel, - ) - } - } - - returnHandler = () => { - this.props.history.push(Urls.linkDbLabInstances(this.props)) - } - - processedHandler = () => { - const data = this.state.data ? this.state.data.newDbLabInstance : null - - this.props.history.push( - Urls.linkDbLabInstance(this.props, data?.data?.id as string), - ) - } - - generateTokenHandler = () => { - this.setState({ token: generateToken() }) - } - - render() { - const { classes, orgPermissions } = this.props - const data = - this.state && this.state.data ? this.state.data.newDbLabInstance : null - const projects = - this.state && this.state.data && this.state.data.projects - ? this.state.data.projects - : null - const projectsList = [] - const dbLabInstances = - this.state && this.state.data && this.state.data.dbLabInstances - ? this.state.data.dbLabInstances - : null - - if (data && data.isProcessed && !data.error) { - this.processedHandler() - Actions.resetNewDbLabInstance() - } - - const breadcrumbs = ( - - ) - - const pageTitle = ( - - ) - - const permitted = !orgPermissions || orgPermissions.dblabInstanceCreate - const disabledOnEdit = this.props.edit - const instancesLoaded = dbLabInstances && dbLabInstances.data - - if (!projects || !projects.data || !instancesLoaded) { - return ( -
- {breadcrumbs} - - {pageTitle} - - -
- ) - } - - if (projects.data && projects.data?.length > 0) { - projects.data.map((p: { name: string; id: number }) => { - return projectsList.push({ title: p.name, value: p.id }) - }) - } - - const isDataUpdating = data && (data.isUpdating || data.isChecking) - - return ( -
- {breadcrumbs} - - {pageTitle} - - {!permitted && ( - - You do not have permission to {this.props.edit ? 'edit' : 'add'}{' '} - Database Lab instances. - - )} - - {!disabledOnEdit && ( - - Database Lab provisioning is currently semi-automated. -
- First, you need to prepare a Database Lab instance on a - separate  machine. Once the instance is ready, register it - here. -
- )} - - -
- { - this.setState({ - project: e.target.value, - }) - Actions.resetNewDbLabInstance() - }} - margin="normal" - error={this.state.errorFields.indexOf('project') !== -1} - fullWidth - inputProps={{ - name: 'project', - id: 'project', - shrink: true, - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - project_label: e.target.value, - }) - Actions.resetNewDbLabInstance() - }} - margin="normal" - error={this.state.errorFields.indexOf('project_label') !== -1} - fullWidth - inputProps={{ - name: 'project_label', - id: 'project_label', - shrink: true, - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- - {!disabledOnEdit && ( -
- { - this.setState({ - token: e.target.value, - }) - Actions.resetNewDbLabInstance() - }} - margin="normal" - error={this.state.errorFields.indexOf('token') !== -1} - fullWidth - inputProps={{ - name: 'token', - id: 'token', - shrink: true, - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
-
- )} - -
- { - this.setState({ - url: e.target.value, - }) - Actions.resetNewDbLabInstance() - }} - margin="normal" - helperText={ - this.state.url && - !isHttps(this.state.url) && - !this.state.useTunnel ? ( - - - - The connection to the Database Lab API is not secure. Use - HTTPS. - - - ) : null - } - error={this.state.errorFields.indexOf('url') !== -1} - fullWidth - inputProps={{ - name: 'url', - id: 'url', - shrink: true, - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - useTunnel: e.target.checked, - }) - Actions.resetNewDbLabInstance() - }} - id="useTunnel" - name="useTunnel" - /> - } - label="Use tunnel" - labelPlacement="end" - /> -
- { - this.setState({ - sshServerUrl: e.target.value, - }) - Actions.resetNewDbLabInstance() - }} - margin="normal" - error={this.state.errorFields.indexOf('sshServerUrl') !== -1} - fullWidth - inputProps={{ - name: 'sshServerUrl', - id: 'sshServerUrl', - shrink: true, - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
-
-
- - -
- {data?.isCheckProcessed && - data?.isChecked && - (isHttps(this.state.url) || this.state.useTunnel) ? ( - - {' '} - Verified - - ) : null} - - {data?.isCheckProcessed && - data?.isChecked && - !isHttps(this.state.url) && - !this.state.useTunnel ? ( - - Verified but is - not secure - - ) : null} - - {data?.isCheckProcessed && !data?.isChecked ? ( - - Not available - - ) : null} -
-
- -
- -    - -
-
- {data?.errorMessage ? data.errorMessage : null} -
-
-
- ) - } -} - -export default DbLabInstanceForm diff --git a/ui/packages/platform/src/components/AddMemberForm/AddMemberForm.tsx b/ui/packages/platform/src/components/AddMemberForm/AddMemberForm.tsx deleted file mode 100644 index f01ff018..00000000 --- a/ui/packages/platform/src/components/AddMemberForm/AddMemberForm.tsx +++ /dev/null @@ -1,238 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import Button from '@material-ui/core/Button' -import Grid from '@material-ui/core/Grid' -import TextField from '@material-ui/core/TextField' - -import { styles } from '@postgres.ai/shared/styles/styles' -import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner' -import { ClassesType, RefluxTypes } from '@postgres.ai/platform/src/components/types' - -import Actions from '../../actions/actions' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import ConsolePageTitle from '../ConsolePageTitle' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import Store from '../../stores/store' -import { WarningWrapper } from 'components/Warning/WarningWrapper' -import { messages } from '../../assets/messages' -import { InviteFormProps } from 'components/AddMemberForm/AddMemberFormWrapper' -import { theme } from '@postgres.ai/shared/styles/theme' - -interface InviteFormWithStylesProps extends InviteFormProps { - classes: ClassesType -} - -interface InviteFormState { - email: string - data: { - auth: { - token: string - } | null - inviteUser: { - errorMessage: string - updateErrorFields: string[] - isUpdating: boolean - } | null - orgProfile: { - orgId: number - isProcessing: boolean - isProcessed: boolean - error: boolean - } | null - } -} - -class InviteForm extends Component { - unsubscribe: Function - componentDidMount() { - const that = this - const { org, orgId } = this.props - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - that.setState({ data: this.data }) - - if (this.data.inviteUser.isProcessed && !this.data.inviteUser.error) { - window.location.href = '/' + org + '/members' - } - - const auth: InviteFormState['data']['auth'] = - this.data && this.data.auth ? this.data.auth : null - const orgProfile: InviteFormState['data']['orgProfile'] = - this.data && this.data.orgProfile ? this.data.orgProfile : null - - if ( - auth && - auth.token && - orgProfile && - orgProfile.orgId !== orgId && - !orgProfile.isProcessing && - !orgProfile.error - ) { - Actions.getOrgs(auth.token, orgId) - } - }) - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - handleChange = (event: React.ChangeEvent) => { - const value = event.target.value - - this.setState({ - email: value, - }) - } - - buttonHandler = () => { - const orgId = this.props.orgId ? this.props.orgId : null - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const data = this.state.data ? this.state.data.inviteUser : null - - if (auth && data && !data.isUpdating && this.state.email) { - Actions.inviteUser(auth.token, orgId, this.state.email.trim()) - } - } - - render() { - const { classes, orgPermissions } = this.props - - const breadcrumbs = ( - - ) - - const pageTitle = ( - - ) - - if (orgPermissions && !orgPermissions.settingsMemberAdd) { - return ( -
- {breadcrumbs} - - {pageTitle} - - {messages.noPermissionPage} -
- ) - } - - if (this.state && this.state.data && this.state.data.orgProfile?.error) { - return ( -
- {breadcrumbs} - - {pageTitle} - - -
- ) - } - - if ( - !this.state || - !this.state.data || - !(this.state.data.orgProfile && this.state.data.orgProfile.isProcessed) - ) { - return ( -
- {breadcrumbs} - - {pageTitle} - - -
- ) - } - - const inviteData = this.state.data.inviteUser - - return ( -
- {breadcrumbs} - - {pageTitle} - -
- If the person is not registered yet, ask them to register first. -
- -
- {inviteData && inviteData.errorMessage ? ( -
{inviteData.errorMessage}
- ) : null} -
- - - - - - - - - - - - -
- ) - } -} - -export default InviteForm diff --git a/ui/packages/platform/src/components/AddMemberForm/AddMemberFormWrapper.tsx b/ui/packages/platform/src/components/AddMemberForm/AddMemberFormWrapper.tsx deleted file mode 100644 index ea9f3ba5..00000000 --- a/ui/packages/platform/src/components/AddMemberForm/AddMemberFormWrapper.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { styles } from '@postgres.ai/shared/styles/styles' -import { RouteComponentProps } from 'react-router' -import { OrgPermissions } from 'components/types' -import InviteForm from 'components/AddMemberForm/AddMemberForm' - -export interface InviteFormProps { - org: string | number - orgId: number - history: RouteComponentProps['history'] - project: string | undefined - orgPermissions: OrgPermissions -} - -export const AddMemberFormWrapper = (props: InviteFormProps) => { - const useStyles = makeStyles( - { - container: { - display: 'flex', - flexWrap: 'wrap', - }, - textField: { - ...styles.inputField, - maxWidth: 400, - }, - dense: { - marginTop: 10, - }, - errorMessage: { - color: 'red', - }, - button: { - marginTop: 17, - display: 'inline-block', - marginLeft: 7, - }, - }, - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/AppUpdateBanner/index.tsx b/ui/packages/platform/src/components/AppUpdateBanner/index.tsx deleted file mode 100644 index 05f68684..00000000 --- a/ui/packages/platform/src/components/AppUpdateBanner/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { observer } from 'mobx-react-lite' - -import { icons } from '@postgres.ai/shared/styles/icons' -import { Button } from '@postgres.ai/shared/components/Button' - -import { appStore } from 'stores/app' - -import styles from './styles.module.scss' - -export const AppUpdateBanner = observer(() => { - if (!appStore.isOutdatedVersion) return null - - return ( -
-
- {icons.updateIcon} UI update is available -
- -
- ) -}) diff --git a/ui/packages/platform/src/components/AppUpdateBanner/styles.module.scss b/ui/packages/platform/src/components/AppUpdateBanner/styles.module.scss deleted file mode 100644 index cc51c1d2..00000000 --- a/ui/packages/platform/src/components/AppUpdateBanner/styles.module.scss +++ /dev/null @@ -1,14 +0,0 @@ -.root { - display: flex; - align-items: center; - flex-wrap: wrap; - background-color: #d7eef2; - padding: 4px 14px; - color: #013a44; -} - -.text { - display: flex; - align-items: center; - margin-right: 16px; -} diff --git a/ui/packages/platform/src/components/Audit/Audit.tsx b/ui/packages/platform/src/components/Audit/Audit.tsx deleted file mode 100644 index 6704de25..00000000 --- a/ui/packages/platform/src/components/Audit/Audit.tsx +++ /dev/null @@ -1,420 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Button, - ExpansionPanel, - ExpansionPanelSummary, - ExpansionPanelDetails, - TextField, -} from '@material-ui/core' -import ExpandMoreIcon from '@material-ui/icons/ExpandMore' - -import { HorizontalScrollContainer } from '@postgres.ai/shared/components/HorizontalScrollContainer' -import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner' -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { ClassesType, RefluxTypes } from '@postgres.ai/platform/src/components/types' - -import Actions from '../../actions/actions' -import ConsolePageTitle from '../ConsolePageTitle' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import Store from '../../stores/store' -import { WarningWrapper } from 'components/Warning/WarningWrapper' -import { messages } from '../../assets/messages' -import format from '../../utils/format' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { AuditProps } from 'components/Audit/AuditWrapper' -import { FilteredTableMessage } from 'components/AccessTokens/FilteredTableMessage/FilteredTableMessage' - -const PAGE_SIZE = 20 -const auditTitle = 'Audit log' - -interface AuditWithStylesProps extends AuditProps { - classes: ClassesType -} - -export interface AuditLogData { - id: number - data_before: string - data_after: string - action: string - actor: string - action_data: { - processed_rows_count: number - } - created_at: string - table_name: string -} - -interface AuditState { - filterValue: string - data: { - auth: { - token: string - } | null - auditLog: { - isProcessing: boolean - orgId: number - error: boolean - isComplete: boolean - errorCode: number - errorMessage: string - data: AuditLogData[] - } | null - } -} - -class Audit extends Component { - unsubscribe: Function - componentDidMount() { - const that = this - const orgId = this.props.orgId - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - const auth: AuditState['data']['auth'] = - this.data && this.data.auth ? this.data.auth : null - const auditLog: AuditState['data']['auditLog'] = - this.data && this.data.auditLog ? this.data.auditLog : null - - that.setState({ data: this.data }) - - if ( - auth && - auth.token && - auditLog && - !auditLog.isProcessing && - !auditLog.error && - !that.state - ) { - Actions.getAuditLog(auth.token, { - orgId, - limit: PAGE_SIZE, - }) - } - }) - - const contentContainer = document.getElementById('content-container') - if (contentContainer) { - contentContainer.addEventListener('scroll', () => { - if ( - contentContainer !== null && - contentContainer.scrollTop >= - contentContainer.scrollHeight - contentContainer.offsetHeight - ) { - this.showMore() - } - }) - } - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - showMore() { - const { orgId } = this.props - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const logs = this.state && this.state.data ? this.state.data.auditLog : null - let lastId = null - - if (logs && logs?.data && logs.data?.length) { - lastId = logs.data[logs.data.length - 1].id - } - - if (auth && auth.token && !logs?.isProcessing && lastId) { - Actions.getAuditLog(auth.token, { - orgId, - limit: PAGE_SIZE, - lastId, - }) - } - } - - formatAction = (r: AuditLogData) => { - const { classes } = this.props - let acted = r.action - let actor = r.actor - let actorSrc = '' - let rows = 'row' - - if (!actor) { - actor = 'Unknown' - actorSrc = ' (changed directly in database) ' - } - - if (r.action_data && r.action_data.processed_rows_count) { - rows = - r.action_data.processed_rows_count + - ' ' + - (r.action_data.processed_rows_count > 1 ? 'rows' : 'row') - } - - switch (r.action) { - case 'insert': - acted = ' added ' + rows + ' to' - break - case 'delete': - acted = ' deleted ' + rows + ' from' - break - default: - acted = ' updated ' + rows + ' in' - } - - return ( -
- {actor} - {actorSrc} {acted} table {r.table_name} -
- ) - } - - getDataSectionTitle = (r: AuditLogData, before: boolean) => { - switch (r.action) { - case 'insert': - return '' - case 'delete': - return '' - default: - return before ? 'Before:' : 'After:' - } - } - - getChangesTitle = (r: AuditLogData) => { - const displayedCount = r.data_before - ? r.data_before?.length - : r.data_after?.length - const objCount = - r.action_data && r.action_data.processed_rows_count - ? r.action_data.processed_rows_count - : null - - if (displayedCount && (objCount as number) > displayedCount) { - return ( - 'Changes (displayed ' + - displayedCount + - ' rows out of ' + - objCount + - ')' - ) - } - - return 'Changes' - } - - filterInputHandler = (event: React.ChangeEvent) => { - this.setState({ filterValue: event.target.value }) - } - - render() { - const { classes, orgPermissions, orgId } = this.props - const data = this.state && this.state.data ? this.state.data.auditLog : null - const logsStore = - (this.state && this.state.data && this.state.data.auditLog) || null - const logs = (logsStore && logsStore.data) || [] - - const breadcrumbs = ( - - ) - - const filteredLogs = logs.filter( - (log) => - log.actor - ?.toLowerCase() - .indexOf((this.state.filterValue || '')?.toLowerCase()) !== -1, - ) - - const pageTitle = ( - 0 - ? { - filterValue: this.state.filterValue, - filterHandler: this.filterInputHandler, - placeholder: 'Search audit log', - } - : null - } - /> - ) - - if (orgPermissions && !orgPermissions.auditLogView) { - return ( -
- {breadcrumbs} - {pageTitle} - {messages.noPermissionPage} -
- ) - } - - if ( - !logsStore || - !logsStore.data || - (logsStore && logsStore.orgId !== orgId) - ) { - return ( -
- {breadcrumbs} - {pageTitle} - -
- ) - } - - if (logsStore.error) { - return ( -
- -
- ) - } - - return ( -
- {breadcrumbs} - {pageTitle} - {filteredLogs && filteredLogs.length > 0 ? ( -
- - - - - Action - Time - - - - {logs.map((r) => { - return ( - - - {this.formatAction(r)} - {(r.data_before || r.data_after) && ( -
- - } - aria-controls="panel1b-content" - id="panel1b-header" - className={classes?.expansionPanelSummary} - > - {this.getChangesTitle(r)} - - - {r.data_before && ( -
- {this.getDataSectionTitle(r, true)} - -
- )} - {r.data_after && ( -
- {this.getDataSectionTitle(r, false)} - -
- )} -
-
-
- )} -
- - {format.formatTimestampUtc(r.created_at)} - -
- ) - })} -
-
-
-
- {data && data.isProcessing && ( - - )} - {data && !data.isProcessing && !data.isComplete && ( - - )} -
-
- ) : ( - - this.setState({ - filterValue: '', - }) - } - /> - )} -
-
- ) - } -} - -export default Audit diff --git a/ui/packages/platform/src/components/Audit/AuditWrapper.tsx b/ui/packages/platform/src/components/Audit/AuditWrapper.tsx deleted file mode 100644 index 7387d910..00000000 --- a/ui/packages/platform/src/components/Audit/AuditWrapper.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { styles } from '@postgres.ai/shared/styles/styles' -import { OrgPermissions } from 'components/types' -import Audit from 'components/Audit/Audit' - -export interface AuditProps { - orgId: number - org: string | number - project: string | undefined - orgPermissions: OrgPermissions -} - -export const AuditWrapper = (props: AuditProps) => { - const useStyles = makeStyles( - (theme) => ({ - root: { - ...(styles.root as Object), - display: 'flex', - flexDirection: 'column', - paddingBottom: '20px', - }, - container: { - display: 'flex', - flexWrap: 'wrap', - }, - timeCell: { - verticalAlign: 'top', - minWidth: 200, - }, - expansionPanel: { - boxShadow: 'none', - background: 'transparent', - fontSize: '12px', - marginBottom: '5px', - }, - expansionPanelSummary: { - display: 'inline-block', - padding: '0px', - minHeight: '22px', - '& .MuiExpansionPanelSummary-content': { - margin: '0px', - display: 'inline-block', - }, - '&.Mui-expanded': { - minHeight: '22px', - }, - '& .MuiExpansionPanelSummary-expandIcon': { - display: 'inline-block', - padding: '0px', - }, - }, - expansionPanelDetails: { - padding: '0px', - [theme.breakpoints.down('md')]: { - display: 'block', - }, - }, - actionDescription: { - marginBottom: '5px', - }, - code: { - width: '100%', - 'margin-top': 0, - '& > div': { - paddingTop: 8, - padding: 8, - }, - 'background-color': 'rgb(246, 248, 250)', - '& > div > textarea': { - fontFamily: - '"Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",' + - ' "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace', - color: 'black', - fontSize: '12px', - }, - }, - showMoreContainer: { - marginTop: 20, - textAlign: 'center', - }, - data: { - width: '50%', - [theme.breakpoints.up('md')]: { - width: '50%', - marginRight: '10px', - }, - }, - bottomSpace: { - ...styles.bottomSpace, - }, - }), - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/Billing/Billing.tsx b/ui/packages/platform/src/components/Billing/Billing.tsx deleted file mode 100644 index a6eb24ed..00000000 --- a/ui/packages/platform/src/components/Billing/Billing.tsx +++ /dev/null @@ -1,180 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { loadStripe } from '@stripe/stripe-js' -import { Elements } from '@stripe/react-stripe-js' -import { ClassesType, RefluxTypes } from '@postgres.ai/platform/src/components/types' - -import ConsolePageTitle from '../ConsolePageTitle' -import StripeForm from '../StripeForm' -import settings from '../../utils/settings' -import Store from '../../stores/store' -import Actions from '../../actions/actions' -import Permissions from '../../utils/permissions' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { BillingProps } from 'components/Billing/BillingWrapper' - -interface BillingWithStylesProps extends BillingProps { - classes: ClassesType -} - -interface BillingState { - data: { - auth: { - token: string - } | null - billing: { - orgId: number - error: boolean - isProcessing: boolean - subscriptionError: boolean - subscriptionErrorMessage: string - isSubscriptionProcessing: boolean - primaryPaymentMethod: string - data: { - unit_amount: string - data_usage_estimate: string - data_usage_sum: string - data_usage: { - id: number - instance_id: string - day_date: Date - data_size_gib: number - to_invoice: boolean - }[] - period_start: Date - period_now: Date - } - } - } -} - -const stripePromise = loadStripe(settings.stripeApiKey as string, { - locale: 'en', -}) - -const page = { - title: 'Billing', -} - -class Billing extends Component { - unsubscribe: Function - componentDidMount() { - const that = this - const { orgId } = this.props - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - const auth: BillingState['data']['auth'] = - this.data && this.data.auth ? this.data.auth : null - const billing: BillingState['data']['billing'] = - this.data && this.data.billing ? this.data.billing : null - - that.setState({ data: this.data }) - - if ( - auth && - auth.token && - billing && - !billing.isProcessing && - !billing.error && - !that.state - ) { - Actions.getBillingDataUsage(auth.token, orgId) - } - }) - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - toFixed(value: number) { - if (value && value.toFixed && value !== 0) { - return value.toFixed(4) - } - - return '0.0' - } - - render() { - const { classes, orgId, orgData } = this.props - const auth = - this.state && this.state.data && this.state.data.auth - ? this.state.data.auth - : null - const data = - this.state && this.state.data && this.state.data.billing - ? this.state.data.billing - : null - - const breadcrumbs = ( - - ) - - if (!Permissions.isAdmin(orgData)) { - return ( -
- {breadcrumbs} - - {} - - -
- ) - } - - let mode = 'new' - if (orgData.is_blocked && orgData.stripe_subscription_id) { - mode = 'resume' - } - if (!orgData.is_blocked && orgData.stripe_subscription_id) { - mode = 'update' - } - - return ( -
- {breadcrumbs} - -
-
- {Permissions.isAdmin(orgData) && ( -
- {data && data.subscriptionError && ( -
- {data.subscriptionErrorMessage} -
- )} - - - -
- )} -
-
-
-
- ) - } -} - -export default Billing diff --git a/ui/packages/platform/src/components/Billing/BillingWrapper.tsx b/ui/packages/platform/src/components/Billing/BillingWrapper.tsx deleted file mode 100644 index 6c4497fe..00000000 --- a/ui/packages/platform/src/components/Billing/BillingWrapper.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import Billing from 'components/Billing/Billing' -import { colors } from '@postgres.ai/shared/styles/colors' - -export interface BillingProps { - org: string | number - orgId: number - short: boolean - projectId: number | string | undefined - orgData: { - alias: string - is_priveleged: boolean - stripe_payment_method_primary: string - is_blocked: boolean - new_subscription: boolean - is_blocked_on_creation: boolean - stripe_subscription_id: number - priveleged_until: Date - role: { - id: number - } - } -} - -export const BillingWrapper = (props: BillingProps) => { - const useStyles = makeStyles( - (theme) => ({ - root: { - '& ul': { - '& > li': { - 'list-style-position': 'inside', - }, - padding: 0, - }, - '& h1': { - fontSize: '16px!important', - fontWeight: 'bold', - }, - '& h2': { - fontSize: '14px!important', - fontWeight: 'bold', - }, - width: '100%', - 'min-height': '100%', - 'z-index': 1, - position: 'relative', - [theme.breakpoints.down('sm')]: { - maxWidth: '100vw', - }, - [theme.breakpoints.up('md')]: { - maxWidth: 'calc(100vw - 200px)', - }, - [theme.breakpoints.up('lg')]: { - maxWidth: 'calc(100vw - 200px)', - }, - 'font-size': '14px!important', - 'font-family': '"Roboto", "Helvetica", "Arial", sans-serif', - - display: 'flex', - flexDirection: 'column', - paddingBottom: '20px', - }, - errorMessage: { - color: colors.state.error, - marginBottom: 10, - }, - subscriptionForm: { - marginBottom: 20, - }, - }), - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/CheckupAgentForm/CheckupAgentForm.tsx b/ui/packages/platform/src/components/CheckupAgentForm/CheckupAgentForm.tsx deleted file mode 100644 index 43fcdafd..00000000 --- a/ui/packages/platform/src/components/CheckupAgentForm/CheckupAgentForm.tsx +++ /dev/null @@ -1,1025 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { - Typography, - IconButton, - TextField, - Chip, - Grid, - Tabs, - Tab, - Button, - Radio, - RadioGroup, - FormControlLabel, - FormLabel, - ExpansionPanel, - ExpansionPanelSummary, - ExpansionPanelDetails, -} from '@material-ui/core' -import Box from '@mui/material/Box' -import ExpandMoreIcon from '@material-ui/icons/ExpandMore' - -import { styles } from '@postgres.ai/shared/styles/styles' -import { icons } from '@postgres.ai/shared/styles/icons' -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { - ClassesType, - TabPanelProps, - ProjectProps, - TokenRequestProps, - RefluxTypes, -} from '@postgres.ai/platform/src/components/types' - -import Store from '../../stores/store' -import Actions from '../../actions/actions' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import CfgGen, { DataType } from '../../utils/cfggen' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { CheckupAgentFormProps } from 'components/CheckupAgentForm/CheckupAgentFormWrapper' - -const AUTO_GENERATED_TOKEN_NAME = 'Auto-generated 1-year token' - -interface CheckupAgentFormWithStylesProps extends CheckupAgentFormProps { - classes: ClassesType -} - -interface CheckupAgentFormState extends DataType { - tab: number - data: { - auth: { - token: string - } | null - tokenRequest: TokenRequestProps - reports: { - error: boolean - isProcessed: boolean - isProcessing: boolean - } | null - projects: Omit - } -} - -function TabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props - - return ( - - ) -} - -function a11yProps(index: number) { - return { - id: `simple-tab-${index}`, - 'aria-controls': `simple-tabpanel-${index}`, - } -} - -class CheckupAgentForm extends Component< - CheckupAgentFormWithStylesProps, - CheckupAgentFormState -> { - state = { - data: { - auth: { - token: '', - }, - tokenRequest: { - isProcessing: false, - isProcessed: false, - data: { - name: '', - is_personal: false, - expires_at: '', - token: '', - }, - errorMessage: '', - error: false, - }, - reports: { - error: false, - isProcessed: false, - isProcessing: false, - }, - projects: { - error: false, - isProcessing: false, - isProcessed: false, - }, - }, - hosts: '', - projectName: '', - databaseName: '', - databaseUserName: '', - ssDatabaseName: '', - port: null, - sshPort: null, - pgPort: null, - statementTimeout: null, - pgSocketDir: '', - psqlBinary: '', - collectPeriod: 600, - newHostName: '', - apiToken: '', - sshKeysPath: '', - password: '', - connectionType: '', - tab: 0, - } - - unsubscribe: Function - componentDidMount() { - const that = this - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - const auth: CheckupAgentFormState['data']['auth'] = - this.data && this.data.auth ? this.data.auth : null - const reports: CheckupAgentFormState['data']['reports'] = - this.data && this.data.reports ? this.data.reports : null - const projects: Omit = - this.data && this.data.projects ? this.data.projects : null - const tokenRequest: TokenRequestProps = - this.data && this.data.tokenRequest ? this.data.tokenRequest : null - - that.setState({ data: this.data }) - - if ( - auth && - auth.token && - !reports?.isProcessed && - !reports?.isProcessing && - !reports?.error - ) { - Actions.getCheckupReports(auth.token) - } - - if ( - auth && - auth.token && - !projects?.isProcessed && - !projects?.isProcessing && - !projects?.error - ) { - Actions.getProjects(auth.token) - } - - if ( - tokenRequest && - tokenRequest.isProcessed && - !tokenRequest.error && - tokenRequest.data && - tokenRequest.data.name && - tokenRequest.data.name.startsWith(AUTO_GENERATED_TOKEN_NAME) && - tokenRequest.data.expires_at && - tokenRequest.data.token - ) { - that.setState({ apiToken: tokenRequest.data.token }) - } - }) - - Actions.refresh() - CfgGen.generateRunCheckupSh(this.state) - } - - componentWillUnmount() { - Actions.hideGeneratedAccessToken() - this.unsubscribe() - } - - handleDeleteHost = (_: React.ChangeEvent, host: string) => { - const curHosts = CfgGen.uniqueHosts(this.state.hosts) - const curDividers = this.state.hosts.match(/[;,(\s)(\n)(\r)(\t)(\r\n)]/gm) - const hosts = curHosts.split(';') - let newHosts = '' - - for (const i in hosts) { - if (hosts[i] !== host) { - newHosts = - newHosts + - hosts[i] + - (curDividers !== null && curDividers[i] ? curDividers[i] : '') - } - } - - this.setState({ hosts: newHosts }) - } - - handleChangeTab = (_: React.ChangeEvent<{}>, tabValue: number) => { - this.setState({ tab: tabValue }) - } - - addToken = () => { - const orgId = this.props.orgId ? this.props.orgId : null - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const tokenRequest = - this.state.data && this.state.data.tokenRequest - ? this.state.data.tokenRequest - : null - - if (auth && auth.token && !tokenRequest?.isProcessing) { - const date = new Date() - const expiresAt = - date.getFullYear() + - 1 + - '-' + - ('0' + (date.getMonth() + 1)).slice(-2) + - '-' + - ('0' + date.getDate()).slice(-2) - const nowDateTime = - date.getFullYear() + - '-' + - ('0' + (date.getMonth() + 1)).slice(-2) + - '-' + - ('0' + date.getDate()).slice(-2) + - ' ' + - ('0' + date.getHours()).slice(-2) + - ':' + - ('0' + date.getMinutes()).slice(-2) - const tokenName = AUTO_GENERATED_TOKEN_NAME + ' (' + nowDateTime + ')' - - Actions.getAccessToken(auth.token, tokenName, expiresAt, orgId) - } - } - - copyDockerCfg = () => { - const copyText = document.getElementById( - 'generatedDockerCfg', - ) as HTMLInputElement - - if (copyText) { - copyText.select() - copyText.setSelectionRange(0, 99999) - document.execCommand('copy') - copyText.setSelectionRange(0, 0) - } - } - - copySrcCfg = () => { - const copyText = document.getElementById( - 'generatedSrcCfg', - ) as HTMLInputElement - - if (copyText) { - copyText.select() - copyText.setSelectionRange(0, 99999) - document.execCommand('copy') - copyText.setSelectionRange(0, 0) - } - } - - render() { - const that = this - const { classes } = this.props - const reports = - this.state.data && this.state.data.reports - ? this.state.data.reports - : null - const projects = - this.state.data && this.state.data.projects - ? this.state.data.projects - : null - const tokenRequest = - this.state.data && this.state.data.tokenRequest - ? this.state.data.tokenRequest - : null - let copySrcCfgBtn = null - let copyDockerCfgBtn = null - let token = null - let content = null - - if ( - this.state.projectName !== '' && - this.state.databaseName !== '' && - this.state.databaseUserName !== '' && - this.state.hosts !== '' && - this.state.apiToken !== '' - ) { - copySrcCfgBtn = ( - - {icons.copyIcon} - - ) - copyDockerCfgBtn = ( - - {icons.copyIcon} - - ) - } - - token = ( -
- { - this.setState({ - apiToken: e.target.value, - }) - }} - value={this.state.apiToken} - helperText={ - 'Insert a token or generate a new one. ' + - 'The auto-generated token will expire in 1 year.' - } - inputProps={{ - name: 'apiToken', - id: 'apiToken', - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> - - -
- ) - - if ( - this.state && - this.state.data && - ((this.state.data.reports && this.state.data.reports.error) || - (this.state.data.projects && this.state.data.projects.error)) - ) { - return ( -
- -
- ) - } - - if (reports && reports.isProcessed && projects?.isProcessed) { - content = ( -
- - Use postgres-checkup to check health of your Postgres databases. - This page will help you to generate the configuration file. Provide - settings that you will use inside your private network (local - hostnames, IPs, etc). - -
- - - Do not leave the page in order not to loose the configuration data. - -
- -

1. Configure

- - - - - General options - - - - - -
- { - this.setState({ - projectName: e.target.value, - }) - }} - required - className={classes.textInput} - value={this.state.projectName} - inputProps={{ - name: 'projectName', - id: 'projectName', - shrink: true, - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- - Connection type * - - { - this.setState({ - connectionType: e.target.value, - }) - }} - className={classes.radioButton} - > - } - label="Connect to defined host via SSH" - /> - } - label={ - 'Connect directly to PostgreSQL (some ' + - 'reports won’t be available)' - } - /> - -
- -
-
-
- { - this.setState({ - hosts: e.target.value, - }) - }} - value={this.state.hosts} - multiline - label="Hosts" - fullWidth - required - inputProps={{ - name: 'hosts', - id: 'hosts', - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- {CfgGen.uniqueHosts(that.state.hosts) - .split(';') - .map((h) => { - if (h !== '') { - return ( - - this.handleDeleteHost(event, h) - } - color="primary" - /> - ) - } - - return null - })} -
-
-
- -
- { - this.setState({ - databaseUserName: e.target.value, - }) - }} - required - value={this.state.databaseUserName} - label="Database username" - inputProps={{ - name: 'databaseUserName', - id: 'databaseUserName', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- - Database password * - - { - this.setState({ - password: e.target.value, - }) - }} - className={classes.radioButton} - > - } - label={ - 'No password is required or PGPASSWORD ' + - 'environment variable is predefined' - } - /> - } - label={ - 'I will enter the password manually ' + - '(choose this only for manual testing)' - } - /> - -
- -
- { - this.setState({ - databaseName: e.target.value, - }) - }} - required - value={this.state.databaseName} - inputProps={{ - name: 'databaseName', - id: 'databaseName', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - collectPeriod: e.target.value, - }) - }} - value={this.state.collectPeriod} - helperText={ - 'The delay between collection of two ' + - 'statistics snapshots, sec' - } - type="number" - inputProps={{ - name: 'collectPeriod', - id: 'collectPeriod', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
-
-
-
- - - } - aria-controls="panel1b-content" - id="panel1b-header" - className={classes.advancedExpansionPanelSummary} - > - - Advanced options - - - - - -
- { - this.setState({ - ssDatabaseName: e.target.value, - }) - }} - value={this.state.ssDatabaseName} - helperText={ - 'Database name with enabled "pg_stat_statements"' + - ' extension (for detailed query analysis)' - } - inputProps={{ - name: 'ssDatabaseName', - id: 'ssDatabaseName', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - pgPort: e.target.value, - }) - }} - value={this.state.port} - helperText="PostgreSQL database server port (default: 5432)" - type="number" - inputProps={{ - name: 'pgPort', - id: 'pgPort', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - sshPort: e.target.value, - }) - }} - value={this.state.port} - helperText="SSH server port (default: 22)" - type="number" - inputProps={{ - name: 'sshPort', - id: 'sshPort', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - statementTimeout: e.target.value, - }) - }} - value={this.state.statementTimeout} - helperText={ - 'Statement timeout for all SQL queries ' + - '(default: 30 seconds)' - } - type="number" - inputProps={{ - name: 'statementTimeout', - id: 'statementTimeout', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - pgSocketDir: e.target.value, - }) - }} - value={this.state.pgSocketDir} - label="PostgreSQL domain socket directory" - helperText="PostgreSQL domain socket directory (default: psql's default)" - inputProps={{ - name: 'pgSocketDir', - id: 'pgSocketDir', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - psqlBinary: e.target.value, - }) - }} - value={this.state.psqlBinary} - helperText='Path to "psql" (default: determined by "$PATH")' - inputProps={{ - name: 'psqlBinary', - id: 'psqlBinary', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
- -
- { - this.setState({ - sshKeysPath: e.target.value, - }) - }} - value={this.state.sshKeysPath} - helperText="Path to directory with SSH keys" - inputProps={{ - name: 'sshKeysPath', - id: 'sshKeysPath', - }} - fullWidth - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - /> -
-
-
-
-
-

- 2. Generate token to upload postgres-checkup reports to console -

-
{token}
- -

3. Deploy using Docker or building from source

- - - - - - - - - Requirements: bash, coreutils, jq, golang, awk, - sed, pandoc, wkhtmltopdf (see{' '} - - {' '} - README{' '} - - ). -
- Clone repo: - - git clone https://gitlab.com/postgres-ai/postgres-checkup.git && - cd postgres-checkup - -
- Start script below: -
-
- - {copySrcCfgBtn} -
-
- - - - Requirements: Docker -
- Start script below: -
-
- - {copyDockerCfgBtn} -
-
-
- ) - } else { - content = ( -
- -
- ) - } - - return ( -
- { - - } - - {content} -
- ) - } -} - -export default CheckupAgentForm diff --git a/ui/packages/platform/src/components/CheckupAgentForm/CheckupAgentFormWrapper.tsx b/ui/packages/platform/src/components/CheckupAgentForm/CheckupAgentFormWrapper.tsx deleted file mode 100644 index 87253b7e..00000000 --- a/ui/packages/platform/src/components/CheckupAgentForm/CheckupAgentFormWrapper.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { theme } from '@postgres.ai/shared/styles/theme' -import { styles } from '@postgres.ai/shared/styles/styles' -import CheckupAgentForm from 'components/CheckupAgentForm/CheckupAgentForm' - -export interface CheckupAgentFormProps { - orgId: number -} - -export const CheckupAgentFormWrapper = (props: CheckupAgentFormProps) => { - const useStyles = makeStyles( - (muiTheme) => ({ - root: { - 'min-height': '100%', - 'z-index': 1, - position: 'relative', - [muiTheme.breakpoints.down('sm')]: { - maxWidth: '100vw', - }, - [muiTheme.breakpoints.up('md')]: { - maxWidth: 'calc(100vw - 200px)', - }, - [muiTheme.breakpoints.up('lg')]: { - maxWidth: 'calc(100vw - 200px)', - }, - '& h2': { - ...theme.typography.h2, - }, - '& h3': { - ...theme.typography.h3, - }, - '& h4': { - ...theme.typography.h4, - }, - '& .MuiExpansionPanelSummary-root.Mui-expanded': { - minHeight: 24, - }, - }, - heading: { - ...theme.typography.h3, - }, - fieldValue: { - display: 'inline-block', - width: '100%', - }, - tokenInput: { - ...styles.inputField, - margin: 0, - 'margin-top': 10, - 'margin-bottom': 10, - }, - textInput: { - ...styles.inputField, - margin: 0, - marginTop: 0, - marginBottom: 10, - }, - hostItem: { - marginRight: 10, - marginBottom: 5, - marginTop: 5, - }, - fieldRow: { - marginBottom: 10, - display: 'block', - }, - fieldBlock: { - width: '100%', - 'max-width': 600, - 'margin-bottom': 15, - '& > div.MuiFormControl- > label': { - fontSize: 20, - }, - '& input, & .MuiOutlinedInput-multiline': { - padding: 13, - }, - }, - relativeFieldBlock: { - marginBottom: 10, - marginRight: 20, - position: 'relative', - }, - addTokenButton: { - marginLeft: 10, - marginTop: 10, - }, - code: { - width: '100%', - 'margin-top': 0, - '& > div': { - paddingTop: 12, - }, - 'background-color': 'rgb(246, 248, 250)', - '& > div > textarea': { - fontFamily: - '"Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",' + - ' "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace', - color: 'black', - fontSize: 14, - }, - }, - codeBlock: { - fontFamily: - '"Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",' + - ' "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace', - width: '100%', - padding: 3, - marginTop: 0, - border: 'rgb(204, 204, 204);', - borderRadius: 3, - color: 'black', - backgroundColor: 'rgb(246, 248, 250)', - }, - details: { - display: 'block', - }, - copyButton: { - position: 'absolute', - top: 6, - right: 6, - fontSize: 20, - }, - relativeDiv: { - position: 'relative', - }, - radioButton: { - '& > label > span.MuiFormControlLabel-label': { - fontSize: '0.9rem', - }, - }, - legend: { - fontSize: '10px', - }, - advancedExpansionPanelSummary: { - 'justify-content': 'left', - '& div.MuiExpansionPanelSummary-content': { - 'flex-grow': 0, - }, - }, - }), - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/ConsoleBreadcrumbs/ConsoleBreadcrumbs.tsx b/ui/packages/platform/src/components/ConsoleBreadcrumbs/ConsoleBreadcrumbs.tsx deleted file mode 100644 index be8497c0..00000000 --- a/ui/packages/platform/src/components/ConsoleBreadcrumbs/ConsoleBreadcrumbs.tsx +++ /dev/null @@ -1,156 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { NavLink } from 'react-router-dom' -import { Typography, Paper, Breadcrumbs } from '@material-ui/core' -import clsx from 'clsx' - -import { Head, createTitle as createTitleBase } from 'components/Head' -import { ConsoleBreadcrumbsProps } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { ClassesType, RefluxTypes } from '@postgres.ai/platform/src/components/types' - -import Store from '../../stores/store' -import Actions from '../../actions/actions' -import Urls from '../../utils/urls' - -interface ConsoleBreadcrumbsState { - data: { - userProfile: { - data: { - orgs: { - [org: string]: { - name: string - projects: { - [project: string]: { - name: string - } - } - } - } - } - } - } -} - -interface ConsoleBreadcrumbsWithStylesProps extends ConsoleBreadcrumbsProps { - classes: ClassesType -} - -const createTitle = (parts: string[]) => { - const filteredParts = parts.filter((part) => part !== 'Organizations') - return createTitleBase(filteredParts) -} - -class ConsoleBreadcrumbs extends Component< - ConsoleBreadcrumbsWithStylesProps, - ConsoleBreadcrumbsState -> { - unsubscribe: Function - componentDidMount() { - const that = this - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - that.setState({ data: this.data }) - }) - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - render() { - const { classes, hasDivider = false, breadcrumbs } = this.props - const org = this.props.org ? this.props.org : null - const project = this.props.project ? this.props.project : null - const orgs = - this.state && - this.state.data && - this.state.data.userProfile.data && - this.state.data.userProfile.data.orgs - ? this.state.data.userProfile.data.orgs - : null - const paths = [] - let lastUrl = '' - - if (!breadcrumbs.length || Urls.isSharedUrl()) { - return null - } - - if (org && orgs && orgs[org]) { - if (orgs[org].name) { - paths.push({ name: 'Organizations', url: '/' }) - paths.push({ name: orgs[org].name, url: '/' + org }) - lastUrl = '/' + org - } - - if (project && orgs[org].projects && orgs[org].projects[project]) { - paths.push({ name: orgs[org].projects[project].name, url: null }) - lastUrl = '/' + org + '/' + project - } - } - - for (let i = 0; i < breadcrumbs.length; i++) { - if (breadcrumbs[i].url && breadcrumbs[i].url?.indexOf('/') === -1) { - breadcrumbs[i].url = lastUrl + '/' + breadcrumbs[i].url - lastUrl = breadcrumbs[i].url as string - } - breadcrumbs[i].isLast = i === breadcrumbs.length - 1 - paths.push(breadcrumbs[i]) - } - - return ( - <> - path.name))} /> - - - {paths.map( - ( - b: { name: string; url?: string | null; isLast?: boolean }, - index, - ) => { - return ( - - {b.url ? ( - - {b.name} - - ) : ( - - {b.name} - - )} - - ) - }, - )} - - - - ) - } -} - -export default ConsoleBreadcrumbs diff --git a/ui/packages/platform/src/components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper.tsx b/ui/packages/platform/src/components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper.tsx deleted file mode 100644 index e7414f06..00000000 --- a/ui/packages/platform/src/components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { colors } from '@postgres.ai/shared/styles/colors' -import ConsoleBreadcrumbs from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbs' - -export interface ConsoleBreadcrumbsProps { - hasDivider?: boolean - org?: string | number - project?: string - breadcrumbs: { name: string; url?: string | null; isLast?: boolean }[] -} - -export const ConsoleBreadcrumbsWrapper = (props: ConsoleBreadcrumbsProps) => { - const useStyles = makeStyles( - { - pointerLink: { - cursor: 'pointer', - }, - breadcrumbsLink: { - maxWidth: 150, - textOverflow: 'ellipsis', - overflow: 'hidden', - display: 'block', - cursor: 'pointer', - whiteSpace: 'nowrap', - fontSize: '12px', - lineHeight: '14px', - textDecoration: 'none', - color: colors.consoleFadedFont, - }, - breadcrumbsItem: { - fontSize: '12px', - lineHeight: '14px', - color: colors.consoleFadedFont, - }, - breadcrumbsActiveItem: { - fontSize: '12px', - lineHeight: '14px', - color: '#000000', - }, - breadcrumbPaper: { - '& a, & a:visited': { - color: colors.consoleFadedFont, - }, - 'padding-bottom': '8px', - marginTop: '-10px', - 'font-size': '12px', - borderRadius: 0, - }, - breadcrumbPaperWithDivider: { - borderBottom: `1px solid ${colors.consoleStroke}`, - }, - }, - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/ConsoleButton/ConsoleButton.tsx b/ui/packages/platform/src/components/ConsoleButton/ConsoleButton.tsx deleted file mode 100644 index e1be66f7..00000000 --- a/ui/packages/platform/src/components/ConsoleButton/ConsoleButton.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { Tooltip, Button } from '@material-ui/core' - -import { ClassesType } from '@postgres.ai/platform/src/components/types' -import { ConsoleButtonProps } from 'components/ConsoleButton/ConsoleButtonWrapper' - -interface ConsoleButtonWithStylesProps extends ConsoleButtonProps { - classes: ClassesType -} - -class ConsoleButton extends Component { - render() { - const { classes, title, children, ...other } = this.props - - // We have to use external tooltip component as disable button cannot show tooltip. - // Details: https://material-ui.com/components/tooltips/#disabled-elements. - return ( - - - - - - ) - } -} - -export default ConsoleButton diff --git a/ui/packages/platform/src/components/ConsoleButton/ConsoleButtonWrapper.tsx b/ui/packages/platform/src/components/ConsoleButton/ConsoleButtonWrapper.tsx deleted file mode 100644 index 23d4674b..00000000 --- a/ui/packages/platform/src/components/ConsoleButton/ConsoleButtonWrapper.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import ConsoleButton from 'components/ConsoleButton/ConsoleButton' - -export interface ConsoleButtonProps { - title: string - children: React.ReactNode - className?: string - disabled?: boolean - variant: 'text' | 'outlined' | 'contained' | undefined - color: 'primary' | 'secondary' | undefined - onClick: () => void - id?: string -} - -export const ConsoleButtonWrapper = (props: ConsoleButtonProps) => { - const useStyles = makeStyles( - { - tooltip: { - fontSize: '10px!important', - }, - }, - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/ConsolePageTitle.tsx b/ui/packages/platform/src/components/ConsolePageTitle.tsx deleted file mode 100644 index 4c170526..00000000 --- a/ui/packages/platform/src/components/ConsolePageTitle.tsx +++ /dev/null @@ -1,162 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { - makeStyles, - Tooltip, - TextField, - InputAdornment, -} from '@material-ui/core' -import SearchIcon from '@material-ui/icons/Search' - -import { colors } from '@postgres.ai/shared/styles/colors' -import { icons } from '@postgres.ai/shared/styles/icons' - -interface ConsolePageTitleProps { - title: string - information?: string | JSX.Element - label?: string - actions?: JSX.Element[] | string[] - top?: boolean - filterProps?: { - filterValue: string - filterHandler: (event: React.ChangeEvent) => void - placeholder: string - } | null -} - -const useStyles = makeStyles( - { - pageTitle: { - flex: '0 0 auto', - '& > h1': { - display: 'inline-block', - fontSize: '16px', - lineHeight: '19px', - marginRight: '10px', - }, - 'border-top': '1px solid ' + colors.consoleStroke, - 'border-bottom': '1px solid ' + colors.consoleStroke, - 'padding-top': '8px', - 'padding-bottom': '8px', - display: 'block', - overflow: 'auto', - 'margin-bottom': '20px', - 'max-width': '100%', - }, - pageTitleTop: { - flex: '0 0 auto', - '& > h1': { - display: 'inline-block', - fontSize: '16px', - lineHeight: '19px', - marginRight: '10px', - }, - 'border-bottom': '1px solid ' + colors.consoleStroke, - 'padding-top': '0px', - 'margin-top': '-10px', - 'padding-bottom': '8px', - display: 'block', - overflow: 'auto', - 'margin-bottom': '20px', - }, - pageTitleActions: { - display: 'flex', - alignItems: 'center', - height: '100%', - float: 'right', - }, - pageTitleActionContainer: { - marginLeft: '10px', - display: 'inline-block', - height: "36px", - - "& > span, button": { - height: '100%', - }, - }, - tooltip: { - fontSize: '10px!important', - }, - label: { - backgroundColor: colors.primary.main, - color: colors.primary.contrastText, - display: 'inline-block', - borderRadius: 3, - fontSize: 10, - lineHeight: '12px', - padding: 2, - paddingLeft: 3, - paddingRight: 3, - verticalAlign: 'text-top', - }, - }, - { index: 1 }, -) - -const ConsolePageTitle = ({ - title, - information, - label, - actions, - top, - filterProps, -}: ConsolePageTitleProps) => { - const classes = useStyles() - - if (!title) { - return null - } - - return ( -
-

{title}

- {information ? ( - - {icons.infoIcon} - - ) : null} - {label ? {label} : null} - {(actions && actions?.length > 0) || filterProps ? ( - - {filterProps ? ( - - - - ), - }} - /> - ) : null} - {actions?.map((a, index) => { - return ( - - {a} - - ) - })} - - ) : null} -
- ) -} - -export default ConsolePageTitle diff --git a/ui/packages/platform/src/components/ContentLayout/DemoOrgNotice/index.tsx b/ui/packages/platform/src/components/ContentLayout/DemoOrgNotice/index.tsx deleted file mode 100644 index 22849f65..00000000 --- a/ui/packages/platform/src/components/ContentLayout/DemoOrgNotice/index.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { makeStyles, Button } from '@material-ui/core' -import { useHistory } from 'react-router-dom' - -import { colors } from '@postgres.ai/shared/styles/colors' -import { icons } from '@postgres.ai/shared/styles/icons' - -import { ROUTES } from 'config/routes' - -const useStyles = makeStyles( - { - demoNoticeText: { - marginLeft: '0px', - display: 'inline-block', - position: 'relative', - backgroundColor: colors.blue, - color: colors.secondary2.darkDark, - width: '100%', - fontSize: '12px', - lineHeight: '24px', - fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', - paddingLeft: '10px', - paddingTop: '4px', - paddingBottom: '4px', - '& > svg': { - verticalAlign: 'baseline', - marginBottom: '-1px', - marginLeft: '0px', - marginRight: '4px', - }, - }, - demoOrgNoticeButton: { - padding: '2px', - paddingLeft: '6px', - paddingRight: '6px', - borderRadius: '3px', - marginLeft: '5px', - marginTop: '-2px', - height: '20px', - lineHeight: '20px', - fontSize: '12px', - fontWeight: 'bold', - }, - noWrap: { - whiteSpace: 'nowrap', - }, - }, - { index: 1 }, -) - -export const DemoOrgNotice = () => { - const classes = useStyles() - const history = useHistory() - - const goToOrgForm = () => history.push(ROUTES.CREATE_ORG.path) - - return ( -
- {icons.infoIconBlue} This is a Demo organization, once you’ve - explored Database Lab features: - -
- ) -} diff --git a/ui/packages/platform/src/components/ContentLayout/DeprecatedApiBanner/index.tsx b/ui/packages/platform/src/components/ContentLayout/DeprecatedApiBanner/index.tsx deleted file mode 100644 index 47f00d86..00000000 --- a/ui/packages/platform/src/components/ContentLayout/DeprecatedApiBanner/index.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { makeStyles } from '@material-ui/core' - -import { Status } from '@postgres.ai/shared/components/Status' -import { GatewayLink } from '@postgres.ai/shared/components/GatewayLink' -import { colors } from '@postgres.ai/shared/styles/vars' - -const useStyles = makeStyles( - { - root: { - background: colors.status.warning, - color: colors.white, - fontSize: '12px', - padding: '4px 10px', - lineHeight: '1.5', - }, - status: { - color: 'inherit', - }, - link: { - color: 'inherit', - }, - }, - { index: 1 }, -) - -export const DeprecatedApiBanner = () => { - const classes = useStyles() - - return ( -
- - The version of your DBLab instance is deprecated. - {' '} - Some information about DBLab, disks, clones, and snapshots may be - unavailable. -
- Please upgrade your DBLab to  - - the latest available version - - . -
- ) -} diff --git a/ui/packages/platform/src/components/ContentLayout/Footer/index.tsx b/ui/packages/platform/src/components/ContentLayout/Footer/index.tsx deleted file mode 100644 index 62ed8071..00000000 --- a/ui/packages/platform/src/components/ContentLayout/Footer/index.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { makeStyles } from '@material-ui/core' -import { GatewayLink } from '@postgres.ai/shared/components/GatewayLink' - -import settings from 'utils/settings' - -const useStyles = makeStyles( - (theme) => ({ - footer: { - flex: '0 0 auto', - backgroundColor: 'rgb(68, 79, 96)', - color: '#fff', - display: 'flex', - justifyContent: 'center', - padding: '16px 20px', - [theme.breakpoints.down('sm')]: { - padding: '16px 12px', - flexDirection: 'column', - }, - }, - footerCopyrightItem: { - marginRight: 50, - [theme.breakpoints.down('sm')]: { - marginBottom: 10, - }, - }, - footerItem: { - marginLeft: 10, - marginRight: 10, - color: '#fff', - '& a': { - color: '#fff', - textDecoration: 'none', - }, - '& a:hover': { - textDecoration: 'none', - }, - [theme.breakpoints.down('sm')]: { - marginLeft: 0, - marginBottom: 5, - }, - }, - footerItemSeparator: { - display: 'inline-block', - [theme.breakpoints.down('sm')]: { - display: 'none', - }, - }, - }), - { index: 1 }, -) - -export const Footer = () => { - const classes = useStyles() - - return ( -
-
- {new Date().getFullYear()} © Postgres.ai -
-
- - Documentation - -
-
|
-
- - News - -
-
|
-
- - Terms of Service - -
-
|
-
- - Privacy Policy - -
-
|
-
- - Ask support - -
-
- ) -} diff --git a/ui/packages/platform/src/components/ContentLayout/index.tsx b/ui/packages/platform/src/components/ContentLayout/index.tsx deleted file mode 100644 index 888551f9..00000000 --- a/ui/packages/platform/src/components/ContentLayout/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable react-hooks/rules-of-hooks */ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import React from 'react' -import { useRouteMatch } from 'react-router-dom' -import clsx from 'clsx' -import { observer } from 'mobx-react-lite' - -import { ROUTES } from 'config/routes' -import settings from 'utils/settings' -import { bannersStore } from 'stores/banners' - -import { DemoOrgNotice } from './DemoOrgNotice' -import { DeprecatedApiBanner } from './DeprecatedApiBanner' -import { Footer } from './Footer' - -import styles from './styles.module.scss' - -type Props = { - children: React.ReactNode -} - -export const ContentLayout = React.memo(observer((props: Props) => { - const { children } = props - - const isOrgJoeInstance = Boolean( - useRouteMatch(ROUTES.ORG.JOE_INSTANCES.JOE_INSTANCE.createPath()), - ) - - const isProjectJoeInstance = Boolean( - useRouteMatch(ROUTES.ORG.PROJECT.JOE_INSTANCES.JOE_INSTANCE.createPath()), - ) - - const isDemoOrg = Boolean(useRouteMatch(`/${settings.demoOrgAlias}`)) - - const isHiddenFooter = isOrgJoeInstance || isProjectJoeInstance - - return ( -
- {isDemoOrg && } - { bannersStore.isOpenDeprecatedApi && } - -
-
- {children} -
-
-
-
- ) -})) diff --git a/ui/packages/platform/src/components/ContentLayout/styles.module.scss b/ui/packages/platform/src/components/ContentLayout/styles.module.scss deleted file mode 100644 index 0df13e06..00000000 --- a/ui/packages/platform/src/components/ContentLayout/styles.module.scss +++ /dev/null @@ -1,40 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -@import '@postgres.ai/shared/styles/mixins'; - -.root { - display: flex; - flex-direction: column; - flex: 1 1 100%; - padding-top: 40px; - width: 100%; - // Flexbox bug fix - https://bugzilla.mozilla.org/show_bug.cgi?id=1086218#c4. - min-width: 0; -} - -.wrapper { - flex: 1 1 100%; - overflow: auto; - display: flex; - flex-direction: column; -} - -.content { - flex: 1 1 100%; - display: flex; - flex-direction: column; - padding: 20px; - - &.fullScreen { - flex-shrink: 0; - } - - @include sm { - padding: 20px 12px; - } -} diff --git a/ui/packages/platform/src/components/CreateClusterCards/CreateClusterCards.tsx b/ui/packages/platform/src/components/CreateClusterCards/CreateClusterCards.tsx deleted file mode 100644 index 1cf4a65b..00000000 --- a/ui/packages/platform/src/components/CreateClusterCards/CreateClusterCards.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { StubContainer } from '@postgres.ai/shared/components/StubContainer' -import { icons } from '@postgres.ai/shared/styles/icons' -import { ConsoleButtonWrapper } from 'components/ConsoleButton/ConsoleButtonWrapper' -import { ProductCardWrapper } from 'components/ProductCard/ProductCardWrapper' -import { DashboardProps } from 'components/Dashboard/DashboardWrapper' - -import Urls from '../../utils/urls' -import { messages } from '../../assets/messages' -import { useStyles } from 'components/CreateDbLabCards/CreateDbLabCards' - -export const CreateClusterCards = ({ - props, - dblabPermitted, -}: { - props: DashboardProps - dblabPermitted: boolean | undefined -}) => { - const classes = useStyles() - - const createClusterInstanceButton = (provider: string) => { - props.history.push(Urls.linkClusterInstanceAdd(props, provider)) - } - - const CreateButton = ({ type, title }: { type: string; title: string }) => ( - createClusterInstanceButton(type)} - title={dblabPermitted ? title : messages.noPermission} - > - {type === 'create' ? 'Create' : 'Install'} - - ) - - const productData = [ - { - title: 'Create Postgres Cluster in your cloud', - renderDescription: () => ( - <> -

- Supported cloud platforms include AWS, GCP, Digital Ocean, and - Hetzner Cloud. -

-

All components are installed within your cloud account.

-

Your data remains secure and never leaves your infrastructure.

- - ), - icon: icons.createDLEIcon, - actions: [ - { - id: 'createDblabInstanceButton', - content: ( - - ), - }, - ], - }, - { - title: 'BYOM (Bring Your Own Machines)', - renderDescription: () => ( - <> -

- Install on your existing resources, regardless of the machine or - location. Compatible with both cloud and bare metal infrastructures. - Your data remains secure and never leaves your infrastructure. -

-

Requirements:

-
    -
  • - Three or more servers running a supported Linux distro: Ubuntu - (20.04/22.04), Debian (11/12), CentOS Stream (8/9), Rocky Linux - (8/9), AlmaLinux (8/9), or Red Hat Enterprise Linux (8/9). -
  • -
  • Internet connectivity
  • -
- - ), - icon: icons.installDLEIcon, - actions: [ - { - id: 'createDblabInstanceButton', - content: ( - - ), - }, - ], - }, - ] - - return ( - - {productData.map((product) => ( - -
{product.renderDescription()}
-
- ))} -
- ) -} diff --git a/ui/packages/platform/src/components/CreateDbLabCards/CreateDbLabCards.tsx b/ui/packages/platform/src/components/CreateDbLabCards/CreateDbLabCards.tsx deleted file mode 100644 index 79a46ab7..00000000 --- a/ui/packages/platform/src/components/CreateDbLabCards/CreateDbLabCards.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { StubContainer } from '@postgres.ai/shared/components/StubContainer' -import { icons } from '@postgres.ai/shared/styles/icons' -import { ConsoleButtonWrapper } from 'components/ConsoleButton/ConsoleButtonWrapper' -import { ProductCardWrapper } from 'components/ProductCard/ProductCardWrapper' -import { DashboardProps } from 'components/Dashboard/DashboardWrapper' - -import Urls from '../../utils/urls' -import { messages } from '../../assets/messages' - -export const useStyles = makeStyles((theme) => ({ - stubContainerProjects: { - marginRight: '-20px', - padding: '0 40px', - alignItems: 'initial !important', - - '& > div:nth-child(1), & > div:nth-child(2)': { - minHeight: '350px', - }, - - [theme.breakpoints.down('sm')]: { - flexDirection: 'column', - }, - }, - productCardProjects: { - flex: '1 1 0', - marginRight: '20px', - height: 'maxContent', - gap: 20, - maxHeight: '100%', - '& ul': { - marginBlockStart: '-10px', - paddingInlineStart: '30px', - }, - - '& svg': { - width: '206px', - height: '130px', - }, - - '&:nth-child(1) svg': { - marginBottom: '-5px', - }, - - '&:nth-child(2) svg': { - marginBottom: '-20px', - }, - - '& li': { - listStyleType: 'none', - position: 'relative', - - '&::before': { - content: '"-"', - position: 'absolute', - left: '-10px', - top: '0', - }, - }, - - [theme.breakpoints.down('sm')]: { - flex: '100%', - marginTop: '20px', - minHeight: 'auto !important', - - '&:nth-child(1) svg': { - marginBottom: 0, - }, - - '&:nth-child(2) svg': { - marginBottom: 0, - }, - }, - }, -})) - -export const CreatedDbLabCards = ({ - props, - dblabPermitted, -}: { - props: DashboardProps - dblabPermitted: boolean | undefined -}) => { - const classes = useStyles() - - const createDblabInstanceButtonHandler = (provider: string) => { - props.history.push(Urls.linkDbLabInstanceAdd(props, provider)) - } - - const CreateButton = ({ type, title }: { type: string; title: string }) => ( - createDblabInstanceButtonHandler(type)} - title={dblabPermitted ? title : messages.noPermission} - > - {type === 'create' ? 'Create' : 'Install'} - - ) - - const productData = [ - { - title: 'Create DBLab in your cloud', - renderDescription: () => ( - <> -

- Supported cloud platforms include AWS, GCP, Digital Ocean, and - Hetzner Cloud. -

-

All components are installed within your cloud account.

-

Your data remains secure and never leaves your infrastructure.

- - ), - icon: icons.createDLEIcon, - actions: [ - { - id: 'createDblabInstanceButton', - content: ( - - ), - }, - ], - }, - { - title: 'BYOM (Bring Your Own Machine)', - renderDescription: () => ( - <> -

- Install on your existing resources, regardless of the machine or - location. Compatible with both cloud and bare metal infrastructures. - Your data remains secure and never leaves your infrastructure. -

-

Requirements:

-
    -
  • Ubuntu 20.04 or newer
  • -
  • Internet connectivity
  • -
- - ), - icon: icons.installDLEIcon, - actions: [ - { - id: 'createDblabInstanceButton', - content: ( - - ), - }, - ], - }, - ] - - return ( - - {productData.map((product) => ( - -
{product.renderDescription()}
-
- ))} -
- ) -} diff --git a/ui/packages/platform/src/components/Dashboard/Dashboard.tsx b/ui/packages/platform/src/components/Dashboard/Dashboard.tsx deleted file mode 100644 index b10a35c7..00000000 --- a/ui/packages/platform/src/components/Dashboard/Dashboard.tsx +++ /dev/null @@ -1,591 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component, MouseEvent } from 'react' -import { NavLink } from 'react-router-dom' -import Brightness1Icon from '@material-ui/icons/Brightness1' -import { - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Button, - Grid, -} from '@material-ui/core' -import ReactMarkdown from 'react-markdown' -import rehypeRaw from 'rehype-raw' -import remarkGfm from 'remark-gfm' - -import { HorizontalScrollContainer } from '@postgres.ai/shared/components/HorizontalScrollContainer' -import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner' -import { StubContainer } from '@postgres.ai/shared/components/StubContainer' -import { - ClassesType, - RefluxTypes, -} from '@postgres.ai/platform/src/components/types' - -import { ROUTES } from 'config/routes' - -import Actions from '../../actions/actions' -import ConsolePageTitle from '../ConsolePageTitle' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import { GatewayLink } from '@postgres.ai/shared/components/GatewayLink' -import Store from '../../stores/store' -import Urls from '../../utils/urls' -import settings from '../../utils/settings' -import format from '../../utils/format' - -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { ConsoleButtonWrapper } from 'components/ConsoleButton/ConsoleButtonWrapper' -import { ProductCardWrapper } from 'components/ProductCard/ProductCardWrapper' -import { DashboardProps } from 'components/Dashboard/DashboardWrapper' -import { FilteredTableMessage } from 'components/AccessTokens/FilteredTableMessage/FilteredTableMessage' -import { CreatedDbLabCards } from 'components/CreateDbLabCards/CreateDbLabCards' - -interface DashboardWithStylesProps extends DashboardProps { - classes: ClassesType -} - -interface DashboardState { - filterValue: string - data: { - auth: { - token: string - } | null - projects: { - error: boolean - isProcessing: boolean - orgId: number - data: { - label: string - project_label_or_name: string - id: number - name: string - alias: string - }[] - } | null - orgId: number - userProfile: { - data: { - platform_onboarding_text: string - orgs: { - [org: string]: { - is_blocked: boolean - created_at: string - id: number - alias: string - name: string - onboarding_text: string - projects: Object - } - } - } - isProcessing: boolean - isProcessed: boolean - error: boolean - } | null - useDemoData: { - isProcessing: boolean - isProcessed: boolean - } | null - dashboard: { - profileUpdateInitAfterDemo: boolean - } | null - } -} - -class Dashboard extends Component { - unsubscribe: Function - componentDidMount() { - const that = this - const orgId = this.props.orgId - const onlyProjects = this.props.onlyProjects - - this.unsubscribe = (Store.listen as RefluxTypes['listen'])(function () { - that.setState({ data: this.data }) - const auth: DashboardState['data']['auth'] = - this.data && this.data.auth ? this.data.auth : null - const userProfile: DashboardState['data']['userProfile'] = - this.data && this.data.userProfile ? this.data.userProfile : null - - if (onlyProjects) { - const projects: DashboardState['data']['projects'] = - this.data && this.data.projects ? this.data.projects : null - - if ( - auth && - auth.token && - !projects?.isProcessing && - !projects?.error && - !that.state - ) { - Actions.getProjects(auth.token, orgId) - } - - if ( - auth && - !that.state && - !userProfile?.isProcessing && - !userProfile?.error - ) { - Actions.getUserProfile(auth.token) - } - } else { - const useDemoData = - this.data && this.data.useDemoData ? this.data.useDemoData : null - const profileUpdateInitAfterDemo = - this.data && this.data.dashboard - ? this.data.dashboard.profileUpdateInitAfterDemo - : null - - if ( - auth && - auth.token && - ((!userProfile?.isProcessed && - !userProfile?.isProcessing && - !userProfile?.error) || - (!profileUpdateInitAfterDemo && - useDemoData.isProcessed && - !useDemoData.error)) - ) { - if (useDemoData.isProcessed) { - this.data.dashboard.profileUpdateInitAfterDemo = true - } - - Actions.getUserProfile(auth.token) - } - } - }) - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - handleClick = ( - _: MouseEvent, - alias?: string, - ) => { - this.props.history.push('/' + alias) - } - - useDemoDataButtonHandler = () => { - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - Actions.useDemoData(auth?.token) - } - - addOrgButtonHandler = () => { - this.props.history.push(ROUTES.CREATE_ORG.path) - } - - addCheckupAgentButtonHandler = () => { - this.props.history.push(Urls.linkCheckupAgentAdd(this.props)) - } - - dblabInstancesButtonHandler = (org: string | number, project: string) => { - return () => { - this.props.history.push(Urls.linkDbLabInstances({ org, project })) - } - } - - joeInstancesButtonHandler = (org: string | number, project: string) => { - return () => { - this.props.history.push(Urls.linkJoeInstances({ org, project })) - } - } - - checkupReportsButtonHandler = (org: string | number, project: string) => { - return () => { - this.props.history.push(Urls.linkReports({ org, project })) - } - } - - filterOrgsInputHandler = (event: React.ChangeEvent) => { - this.setState({ filterValue: event.target.value }) - } - - render() { - const renderProjects = this.props.onlyProjects - - if (renderProjects) { - return this.renderProjects() - } - - // TODO(anatoly): Move organization to a separate page component. - return this.renderOrgs() - } - - renderProjects() { - const { classes } = this.props - const org = this.props.org as string | number - const orgId = this.props.orgId - const projectsData = - this.state && this.state.data && this.state.data.projects - ? this.state.data.projects - : null - - const breadcrumbs = ( - - ) - - const pageTitle = ( - - ) - - if (projectsData && projectsData.error) { - return ( - <> - {breadcrumbs} - - - ) - } - - if (!projectsData || !projectsData.data || projectsData.orgId !== orgId) { - return ( - <> - {breadcrumbs} - - - ) - } - - const projects = projectsData.data - - const dblabPermitted = this.props.orgPermissions?.dblabInstanceCreate - - let table = ( - - ) - - if (projects.length > 0) { - table = ( - - - - - Project - Activity - - - - {projects.map((p) => { - return ( - - - {p.project_label_or_name || p.label || p.name} - - - - - - - - ) - })} - -
-
- ) - } - - let onboarding = null - if ( - this.state.data && - this.state.data.userProfile && - this.state.data.userProfile.data && - this.state.data.userProfile.data.orgs && - this.state.data.userProfile.data.orgs[org] && - this.state.data.userProfile.data.orgs[org].projects && - this.state.data.userProfile.data.orgs[org].onboarding_text - ) { - onboarding = ( -
- - -
-

Getting started

- { - const { href, target, children } = props - return ( - - {String(children)} - - ) - }, - }} - /> -
-
-
-
- ) - } - - return ( -
- {breadcrumbs} - - {pageTitle} - - {onboarding} - - {table} -
- ) - } - - renderOrgs() { - const { classes } = this.props - const profile = - this.state && this.state.data ? this.state.data.userProfile : null - const useDemoData = - this.state && this.state.data ? this.state.data.useDemoData : null - const profileUpdateInitAfterDemo = - this.state && this.state.data && this.state.data.dashboard - ? this.state.data.dashboard.profileUpdateInitAfterDemo - : null - - const filteredItems = - profile?.data?.orgs && - Object.keys(profile?.data?.orgs)?.filter( - (org) => - org - ?.toLowerCase() - .indexOf((this.state.filterValue || '')?.toLowerCase()) !== -1, - ) - - // Show organizations. - if (this.state && this.state.data.projects?.error) { - return ( -
- -
- ) - } - - if ( - !profile || - profile.isProcessing || - (profile && !profile.data) || - !useDemoData || - useDemoData.isProcessing || - (useDemoData.isProcessed && !profileUpdateInitAfterDemo) - ) { - return ( - <> - - - ) - } - - const useDemoDataButton = ( - - Join demo organization - - ) - - const createOrgButton = ( - - Create new organization - - ) - - const orgsPlaceholder = ( - - -

- An organization represents a workspace for you and your colleagues. - Organizations allow you to manage users and collaborate across - multiple projects. -

-

- You can create a new organization, join the demo organization or ask - existing members of your organization to invite you. -

-
-
- ) - - const pageActions = [] - if ( - Object.keys(profile?.data?.orgs).length > 0 && - (!profile.data?.orgs || !profile.data?.orgs[settings.demoOrgAlias]) - ) { - pageActions.push(useDemoDataButton) - } - - if (Object.keys(profile?.data?.orgs).length > 0) { - pageActions.push(createOrgButton) - } - - return ( -
- 0 - ? { - filterValue: this.state.filterValue, - filterHandler: this.filterOrgsInputHandler, - placeholder: 'Search organizations by name', - } - : null - } - /> - {profile.data?.orgs && filteredItems && filteredItems.length > 0 ? ( - - - - - - Organization - - Projects count - Status - Created at - - - - {filteredItems.map((index) => { - return ( - - this.handleClick(event, profile.data?.orgs[index].alias) - } - style={{ cursor: 'pointer' }} - data-org-id={profile.data?.orgs[index].id} - data-org-alias={profile.data?.orgs[index].alias} - > - - - {profile.data?.orgs[index].name} - - - - - {profile.data?.orgs[index].projects - ? Object.keys(profile.data?.orgs[index]?.projects) - .length - : '0'} - - - - - - - {format.formatDate( - profile.data?.orgs[index].created_at, - ) || '-'} - - - ) - })} - -
-
- ) : ( - - this.setState({ - filterValue: '', - }) - } - /> - )} -
- ) - } -} - -export default Dashboard diff --git a/ui/packages/platform/src/components/Dashboard/DashboardWrapper.tsx b/ui/packages/platform/src/components/Dashboard/DashboardWrapper.tsx deleted file mode 100644 index b279a66d..00000000 --- a/ui/packages/platform/src/components/Dashboard/DashboardWrapper.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { colors } from '@postgres.ai/shared/styles/colors' -import { RouteComponentProps } from 'react-router' -import Dashboard from 'components/Dashboard/Dashboard' - -export interface DashboardProps { - org?: string | number - orgId?: number - onlyProjects?: boolean - history: RouteComponentProps['history'] - project?: string | undefined - orgPermissions?: { - dblabInstanceCreate?: boolean - checkupReportConfigure?: boolean - } -} - -export const DashboardWrapper = (props: DashboardProps) => { - const useStyles = makeStyles( - (theme) => ({ - orgsHeader: { - position: 'relative', - }, - newOrgBtn: { - position: 'absolute', - top: 0, - right: 10, - }, - nameColumn: { - 'word-wrap': 'break-word', - [theme.breakpoints.down('sm')]: { - maxWidth: 'calc(100vw - 150px)', - }, - [theme.breakpoints.up('md')]: { - maxWidth: 'calc(100vw - 350px)', - }, - [theme.breakpoints.up('lg')]: { - maxWidth: 'calc(100vw - 350px)', - }, - '& > a': { - color: 'black', - textDecoration: 'none', - }, - '& > a:hover': { - color: 'black', - textDecoration: 'none', - }, - }, - cell: { - '& > a': { - color: 'black', - textDecoration: 'none', - }, - '& > a:hover': { - color: 'black', - textDecoration: 'none', - }, - }, - activityButton: { - '&:not(:first-child)': { - marginLeft: '15px', - }, - }, - onboardingCard: { - border: '1px solid ' + colors.consoleStroke, - borderRadius: 3, - padding: 15, - '& h1': { - fontSize: '16px', - margin: '0', - }, - }, - onboarding: { - '& ul': { - paddingInlineStart: '20px', - }, - }, - filterOrgsInput: { - width: '100%', - - '& .MuiOutlinedInput-input': { - width: '200px', - }, - }, - blockedStatus: { - color: colors.state.error, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - validStatus: { - color: colors.state.ok, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - flexContainer: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '100%', - height: '100%', - gap: 40, - marginTop: '20px', - - '& > div': { - maxWidth: '300px', - width: '100%', - height: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - border: '1px solid #e0e0e0', - padding: '20px', - borderRadius: '4px', - cursor: 'pointer', - fontSize: '15px', - transition: 'border 0.3s ease-in-out', - - '&:hover': { - border: '1px solid #FF6212', - }, - }, - }, - }), - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/AnsibleInstance.tsx b/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/AnsibleInstance.tsx deleted file mode 100644 index 8e3b4986..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/AnsibleInstance.tsx +++ /dev/null @@ -1,315 +0,0 @@ -import { Box } from '@mui/material' -import { useEffect, useState } from 'react' -import { makeStyles, Button } from '@material-ui/core' - -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { ErrorStub } from '@postgres.ai/shared/components/ErrorStub' -import { SyntaxHighlight } from '@postgres.ai/shared/components/SyntaxHighlight' - -import { getOrgKeys } from 'api/cloud/getOrgKeys' -import { getCloudImages } from 'api/cloud/getCloudImages' - -import { - getGcpAccountContents, - getNetworkSubnet, - getPlaybookCommandWithoutDocker, -} from 'components/DbLabInstanceForm/utils' -import { - cloneRepositoryCommand, - getAnsibleInstallationCommand, -} from 'components/DbLabInstanceInstallForm/utils' -import { InstanceFormCreation } from 'components/DbLabInstanceForm/DbLabFormSteps/InstanceFormCreation' - -import { initialState } from '../reducer' -import { - cloneClusterRepositoryCommand, - getClusterPlaybookCommandWithoutDocker, -} from 'components/PostgresClusterForm/utils' - -export const formStyles = makeStyles({ - marginTop: { - marginTop: '20px !important', - }, - marginBottom: { - marginBottom: '20px', - display: 'block', - }, - maxContentWidth: { - maxWidth: '800px', - }, - spinner: { - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - height: '100%', - }, - buttonSpinner: { - marginRight: '8px', - color: '#fff', - }, - title: { - fontWeight: 600, - fontSize: '15px', - margin: '10px 0', - }, - mainTitle: { - fontWeight: 600, - fontSize: '20px', - borderBottom: '1px solid #eee', - margin: '0 0 10px 0', - paddingBottom: '10px', - }, - note: { - fontSize: '12px', - margin: '0 0 10px 0', - color: '#777', - }, - code: { - backgroundColor: '#eee', - borderRadius: '3px', - padding: '0 3px', - marginLeft: '0.25em', - }, - ul: { - paddingInlineStart: '30px', - - '& li': { - marginBottom: '5px', - }, - }, - important: { - fontWeight: 600, - margin: 0, - }, - containerMargin: { - margin: '20px 0', - }, - smallMarginTop: { - marginBottom: '10px', - }, -}) - -export const InstanceDocumentation = ({ - firstStep, - firsStepDescription, - documentation, - secondStep, - snippetContent, - classes, -}: { - firstStep: string - firsStepDescription?: React.ReactNode - documentation: string - secondStep: React.ReactNode - snippetContent: string - classes: ReturnType -}) => ( - <> -

1. {firstStep}

- {firsStepDescription &&

{firsStepDescription}

} -

- Documentation:{' '} - - {documentation} - -

-

2. Export {secondStep}

- - -) - -export const AnsibleInstance = ({ - cluster, - state, - orgId, - goBack, - goBackToForm, - formStep, - setFormStep, -}: { - cluster?: boolean - state: typeof initialState - orgId: number - goBack: () => void - goBackToForm: () => void - formStep: string - setFormStep: (step: string) => void -}) => { - const classes = formStyles() - const [orgKey, setOrgKey] = useState('') - const [isLoading, setIsLoading] = useState(false) - const [cloudImages, setCloudImages] = useState([]) - const [orgKeyError, setOrgKeyError] = useState(false) - - useEffect(() => { - setIsLoading(true) - getOrgKeys(orgId).then((data) => { - if (data.error !== null || !Array.isArray(data.response)) { - setIsLoading(false) - setOrgKeyError(true) - } else { - setOrgKeyError(false) - setOrgKey(data.response[0].value) - } - }) - getCloudImages({ - os_name: 'Ubuntu', - os_version: '22.04%20LTS', - arch: state.instanceType.arch, - cloud_provider: state.provider, - region: state.provider === 'aws' ? state.location.native_code : 'all', - }).then((data) => { - setIsLoading(false) - setOrgKeyError(false) - setCloudImages(data.response) - }) - }, [ - orgId, - state.instanceType.arch, - state.location.native_code, - state.provider, - ]) - - const AnsibleInstallation = () => ( - <> -

- 3. Install Ansible on your working machine (which could easily be a - laptop) -

-

example of installing the latest version:

- - - for more instructions on installing ansible, see{' '} - - here - - . - -

- 4. Clone the {cluster ? 'postgresql_cluster' : 'dle-se-ansible'}{' '} - repository -

- - - {!cluster && ( - <> -

5. Install requirements

- - - )} - - ) - - return ( - - {isLoading ? ( - - - - ) : ( - <> - {orgKeyError ? ( - - ) : state.provider === 'digitalocean' ? ( - - DO_API_TOKEN - - } - snippetContent="export DO_API_TOKEN=XXXXXX" - classes={classes} - /> - ) : state.provider === 'hetzner' ? ( - HCLOUD_API_TOKEN - } - snippetContent="export HCLOUD_API_TOKEN=XXXXXX" - classes={classes} - /> - ) : state.provider === 'aws' ? ( - - AWS_ACCESS_KEY_ID and - AWS_SECRET_ACCESS_KEY - - } - snippetContent={`export AWS_ACCESS_KEY_ID=XXXXXX\nexport AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXX`} - classes={classes} - /> - ) : state.provider === 'gcp' ? ( - - GCP_SERVICE_ACCOUNT_CONTENTS - - } - snippetContent={getGcpAccountContents()} - classes={classes} - /> - ) : null} - -

- {cluster - ? '5. Run ansible playbook to deploy Postgres Cluster' - : '6. Run ansible playbook to create server and install DBLab SE'} -

- - {getNetworkSubnet(state.provider, classes)} -

- {cluster - ? '6. After the code snippet runs successfully, follow the directions displayed in the resulting output to start using the database.' - : '7. After the code snippet runs successfully, follow the directions displayed in the resulting output to start using DBLab AUI/API/CLI.'} -

- - - - - - )} -
- ) -} diff --git a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/DockerInstance.tsx b/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/DockerInstance.tsx deleted file mode 100644 index 18eb8585..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/DockerInstance.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { Box } from '@mui/material' -import { useEffect, useState } from 'react' -import { Button } from '@material-ui/core' - -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { ErrorStub } from '@postgres.ai/shared/components/ErrorStub' -import { SyntaxHighlight } from '@postgres.ai/shared/components/SyntaxHighlight' - -import { getOrgKeys } from 'api/cloud/getOrgKeys' -import { getCloudImages } from 'api/cloud/getCloudImages' - -import { - getNetworkSubnet, - getGcpAccountContents, - getPlaybookCommand, -} from 'components/DbLabInstanceForm/utils' -import { - InstanceDocumentation, - formStyles, -} from 'components/DbLabInstanceForm/DbLabFormSteps/AnsibleInstance' -import { InstanceFormCreation } from 'components/DbLabInstanceForm/DbLabFormSteps/InstanceFormCreation' - -import { initialState } from '../reducer' -import { getClusterPlaybookCommand } from 'components/PostgresClusterForm/utils' - -export const DockerInstance = ({ - cluster, - state, - orgId, - goBack, - goBackToForm, - formStep, - setFormStep, -}: { - state: typeof initialState - cluster?: boolean - orgId: number - goBack: () => void - goBackToForm: () => void - formStep: string - setFormStep: (step: string) => void -}) => { - const classes = formStyles() - const [orgKey, setOrgKey] = useState('') - const [isLoading, setIsLoading] = useState(false) - const [cloudImages, setCloudImages] = useState([]) - const [orgKeyError, setOrgKeyError] = useState(false) - - useEffect(() => { - setIsLoading(true) - getOrgKeys(orgId).then((data) => { - if (data.error !== null || !Array.isArray(data.response)) { - setIsLoading(false) - setOrgKeyError(true) - } else { - setOrgKeyError(false) - setOrgKey(data.response[0].value) - } - }) - getCloudImages({ - os_name: 'Ubuntu', - os_version: '22.04%20LTS', - arch: state.instanceType.arch, - cloud_provider: state.provider, - region: state.provider === 'aws' ? state.location.native_code : 'all', - }).then((data) => { - setIsLoading(false) - setOrgKeyError(false) - setCloudImages(data.response) - }) - }, [ - orgId, - state.instanceType.arch, - state.location.native_code, - state.provider, - ]) - - return ( - - {isLoading ? ( - - - - ) : ( - <> - {orgKeyError ? ( - - ) : state.provider === 'digitalocean' ? ( - DO_API_TOKEN} - snippetContent="export DO_API_TOKEN=XXXXXX" - classes={classes} - /> - ) : state.provider === 'hetzner' ? ( - HCLOUD_API_TOKEN - } - snippetContent="export HCLOUD_API_TOKEN=XXXXXX" - classes={classes} - /> - ) : state.provider === 'aws' ? ( - - AWS_ACCESS_KEY_ID and - AWS_SECRET_ACCESS_KEY - - } - snippetContent={`export AWS_ACCESS_KEY_ID=XXXXXX\nexport AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXX`} - classes={classes} - /> - ) : state.provider === 'gcp' ? ( - <> - - Create and save the JSON key for the service account and - point to them using{' '} - - GCP_SERVICE_ACCOUNT_CONTENTS - {' '} - variable. - - } - documentation="https://developers.google.com/identity/protocols/oauth2/service-account#creatinganaccount" - secondStep={ - - GCP_SERVICE_ACCOUNT_CONTENTS - - } - snippetContent={getGcpAccountContents()} - classes={classes} - /> - - ) : null} -

- 3. Run ansible playbook to{' '} - {cluster - ? 'deploy Postgres Cluster' - : 'create server and install DBLab SE'} -

- - {getNetworkSubnet(state.provider, classes)} -

- 4. After the code snippet runs successfully, follow the directions - displayed in the resulting output to start using{' '} - {cluster ? 'the database.' : 'DBLab UI/API/CLI.'} -

- - - - - - )} -
- ) -} diff --git a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/InstanceFormCreation.tsx b/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/InstanceFormCreation.tsx deleted file mode 100644 index 46cf4160..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/InstanceFormCreation.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import classNames from 'classnames' -import { makeStyles } from '@material-ui/core' - -const useStyles = makeStyles((theme) => ({ - snippetContainer: { - width: '100%', - height: '100%', - maxWidth: '800px', - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - gap: 40, - - [theme.breakpoints.down('sm')]: { - flexDirection: 'column', - }, - - '& p:first-child': { - marginTop: '0', - }, - }, - fullWidth: { - width: '100%', - maxWidth: '100%', - - '& .MuiTextField-root': { - maxWidth: '800px', - } - }, - navigation: { - display: 'flex', - flexDirection: 'column', - marginLeft: '-20px', - flex: '0 0 220px', - - [theme.breakpoints.down('sm')]: { - flex: 'auto', - }, - - '& span': { - display: 'flex', - alignItems: 'center', - gap: 10, - cursor: 'pointer', - padding: '8px 14px 8px 20px', - borderBottom: '1px solid #CCD7DA', - transition: 'background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', - - '&:hover': { - backgroundColor: '#F5F8FA', - }, - }, - }, - form: { - flex: '1 1 0', - overflow: 'auto', - - [theme.breakpoints.down('sm')]: { - flex: 'auto', - }, - }, - active: { - backgroundColor: '#F5F8FA', - borderRight: '4px solid #FF6212', - }, -})) - -export const InstanceFormCreation = ({ - formStep, - setFormStep, - children, - install, - fullWidth, -}: { - formStep: string - setFormStep: (step: string) => void - children: React.ReactNode - install?: boolean - fullWidth?: boolean -}) => { - const classes = useStyles() - - return ( -
-
- {!install && ( - setFormStep('simple')} - > - {'simple - Simple setup - - )} - setFormStep('docker')} - > - {'docker - Docker - - setFormStep('ansible')} - > - {'ansible - Ansible - -
-
{children}
-
- ) -} diff --git a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/SimpleInstance.tsx b/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/SimpleInstance.tsx deleted file mode 100644 index b288387a..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/SimpleInstance.tsx +++ /dev/null @@ -1,586 +0,0 @@ -import { Box } from '@mui/material' -import { Button, TextField } from '@material-ui/core' -import { useCallback, useEffect, useState } from 'react' - -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { ErrorStub } from '@postgres.ai/shared/components/ErrorStub' -import { SyntaxHighlight } from '@postgres.ai/shared/components/SyntaxHighlight' -import { formStyles } from 'components/DbLabInstanceForm/DbLabFormSteps/AnsibleInstance' -import { ResponseMessage } from '@postgres.ai/shared/pages/Configuration/ResponseMessage' -import { InstanceFormCreation } from 'components/DbLabInstanceForm/DbLabFormSteps/InstanceFormCreation' - -import { initialState } from '../reducer' -import { cloudProviderName } from '../utils' -import { getOrgKeys } from 'api/cloud/getOrgKeys' -import { establishConnection } from './streamLogs' -import { launchDeploy } from 'api/configs/launchDeploy' -import { getCloudImages } from 'api/cloud/getCloudImages' -import { regenerateCode } from 'api/configs/regenerateCode' -import { useWsScroll } from '@postgres.ai/shared/pages/Logs/hooks/useWsScroll' -import { getTaskState } from 'api/configs/getTaskState' - -const SimpleInstanceDocumentation = ({ - state, - isLoading, - secondStep, - documentation, - deployingState, - handleDeploy, -}: { - isLoading: boolean - documentation: string - secondStep: JSX.Element - state: typeof initialState - handleDeploy: (e: React.FormEvent) => void - deployingState: { - status: string - error: string - } -}) => { - const classes = formStyles() - - useEffect(() => { - const textFields = document.querySelectorAll('input[type="text"]') - textFields?.forEach((textField) => { - textField.addEventListener('blur', () => { - textField.setAttribute('type', 'password') - }) - textField.addEventListener('focus', () => { - textField.setAttribute('type', 'text') - }) - }) - }, []) - - return ( -
-

{cloudProviderName(state.provider)}

-

- {state.provider === 'aws' ? ( - <> - {`Create a ${cloudProviderName(state.provider)} access key per`}{' '} - - the official documentation. - {' '} - These secrets will be used securely in a - - ) : state.provider === 'gcp' ? ( - <> - {`Create a ${cloudProviderName( - state.provider, - )} service account per`}{' '} - - the official documentation. - {' '} - The service account content will be used securely in a - - ) : ( - <> - {`Generate a ${cloudProviderName(state.provider)} API token per`}{' '} - - the official documentation. - {' '} - This token will be used securely in a - - )}{' '} - - Postgres.ai - {' '} - temporary container and will not be stored. -

- {secondStep} -
- -
- - {deployingState.error && ( - - )} - - ) -} - -export const SimpleInstance = ({ - cluster, - state, - orgId, - userID, - goBackToForm, - formStep, - setFormStep, -}: { - cluster?: boolean - state: typeof initialState - orgId: number - userID?: number - goBackToForm: () => void - formStep: string - setFormStep: (step: string) => void -}) => { - const classes = formStyles() - const hasTaskID = - new URLSearchParams(window.location.search).get('taskID') === state.taskID - const logElement = document.getElementById('logs-container') - const [orgKey, setOrgKey] = useState('') - const [isLoading, setIsLoading] = useState(false) - const [taskStatus, setTaskStatus] = useState('') - const [isConnected, setIsConnected] = useState(false) - const [deployingState, setDeployingState] = useState({ - status: 'stale', - error: '', - }) - useWsScroll(deployingState.status === 'loading', true) - const [cloudImages, setCloudImages] = useState([ - { - native_os_image: '', - }, - ]) - const [orgKeyError, setOrgKeyError] = useState(false) - - const [extraEnvs, setExtraEnvs] = useState({ - DO_API_TOKEN: '', - HCLOUD_API_TOKEN: '', - AWS_ACCESS_KEY_ID: '', - AWS_SECRET_ACCESS_KEY: '', - GCP_SERVICE_ACCOUNT_CONTENTS: '', - }) - - useEffect(() => { - if ( - state.provider && - state.location.native_code && - state.instanceType?.arch - ) { - setIsLoading(true) - getOrgKeys(orgId).then((data) => { - if (data.error !== null || !Array.isArray(data.response)) { - setIsLoading(false) - setOrgKeyError(true) - } else { - setOrgKeyError(false) - setOrgKey(data.response[0].value) - } - }) - getCloudImages({ - os_name: 'Ubuntu', - os_version: '22.04%20LTS', - arch: state.instanceType.arch, - cloud_provider: state.provider, - region: state.provider === 'aws' ? state.location.native_code : 'all', - }).then((data) => { - setIsLoading(false) - setOrgKeyError(false) - setCloudImages(data.response) - }) - } - }, [ - orgId, - state.instanceType?.arch, - state.location.native_code, - state.provider, - ]) - - useEffect(() => { - const handleHeightChange = () => { - if (logElement) { - logElement.scrollIntoView({ - behavior: 'smooth', - block: 'end', - }) - } - } - - const observer = new ResizeObserver(handleHeightChange) - if (logElement) { - observer.observe(logElement) - } - - return () => { - if (logElement) { - observer.unobserve(logElement) - } - } - }, [logElement]) - - const establishWebsocketConnection = useCallback( - ({ taskId, otCode }: { taskId: string; otCode: string }) => { - establishConnection({ - taskId: taskId, - otCode: otCode, - userID, - isConnected, - setIsConnected, - }).then(() => { - getTaskState({ taskID: taskId, userID }).then((status) => { - if (status.response) { - const responseStatus = - status.response?.state === 'error' || - status.response?.state === 'finished' - ? 'finished' - : 'loading' - setTaskStatus(responseStatus) - setDeployingState({ - status: 'finished', - error: '', - }) - } else if (status.error) { - setDeployingState({ - status: 'finished', - error: status.error?.Error, - }) - } - }) - }) - }, - [isConnected, userID], - ) - - useEffect(() => { - if ( - hasTaskID && - userID && - Object.values(extraEnvs).every((x) => x === null || x === '') && - taskStatus !== 'error' && - taskStatus !== 'finished' - ) { - setDeployingState({ - status: 'loading', - error: '', - }) - getTaskState({ taskID: state.taskID, userID }).then((data) => { - if (data.response?.state) { - regenerateCode({ taskID: state.taskID, userID }).then((res) => { - if (res.response) { - establishWebsocketConnection({ - taskId: state.taskID, - otCode: res.response.otCode, - }) - } else if (res.error) { - setDeployingState({ - status: 'finished', - error: res.error?.Error, - }) - } - }) - } else if (data.error) { - setDeployingState({ - status: 'finished', - error: data.error?.Error, - }) - } - }) - } - }, [ - hasTaskID, - state.taskID, - userID, - isConnected, - extraEnvs, - taskStatus, - establishWebsocketConnection, - ]) - - const handleDeploy = useCallback( - async (e: React.FormEvent) => { - e.preventDefault() - if (logElement) { - logElement.innerHTML = '' - } - - setDeployingState({ - status: 'loading', - error: '', - }) - await launchDeploy({ - launchType: cluster ? 'cluster' : 'instance', - state: state, - userID: userID, - extraEnvs: extraEnvs, - orgKey: orgKey, - cloudImage: cloudImages[0]?.native_os_image, - }) - .then(async (data) => { - if (data.response) { - window.history.pushState( - {}, - '', - `${window.location.pathname}?taskID=${data.response.taskID}&provider=${state.provider}`, - ) - establishWebsocketConnection({ - taskId: data.response.taskID, - otCode: data.response.otCode, - }) - setDeployingState({ - status: 'finished', - error: '', - }) - } else if (data.error) { - const error = - data.error.Error || - data.error.Errors[0] || - data.error.FieldErrors.playbook - - setDeployingState({ - status: 'stale', - error: error, - }) - if (logElement) { - logElement.innerHTML = error - } - } - }) - .catch(() => { - setDeployingState({ - ...deployingState, - status: 'stale', - }) - }) - }, - [ - state, - extraEnvs, - orgKey, - cloudImages, - userID, - logElement, - deployingState, - establishWebsocketConnection, - ], - ) - - const isFormDisabled = - deployingState.status === 'loading' || - deployingState.status === 'finished' || - isConnected || - (hasTaskID && Object.values(extraEnvs).every((x) => x === null || x === '')) - - return ( - - {isLoading ? ( - - - - ) : ( - <> - {orgKeyError ? ( - - ) : state.provider === 'digitalocean' ? ( - - setExtraEnvs({ - ...extraEnvs, - DO_API_TOKEN: e.target.value, - }) - } - /> - } - /> - ) : state.provider === 'hetzner' ? ( - - setExtraEnvs({ - ...extraEnvs, - HCLOUD_API_TOKEN: e.target.value, - }) - } - /> - } - /> - ) : state.provider === 'aws' ? ( - - - setExtraEnvs({ - ...extraEnvs, - AWS_ACCESS_KEY_ID: e.target.value, - }) - } - /> - - setExtraEnvs({ - ...extraEnvs, - AWS_SECRET_ACCESS_KEY: e.target.value, - }) - } - /> - - } - /> - ) : state.provider === 'gcp' ? ( - { - e.target.style.color = 'transparent' - e.target.style.textShadow = '0 0 8px rgba(0,0,0,0.5)' - }} - onFocus={(e) => { - e.target.style.color = 'black' - e.target.style.textShadow = 'none' - }} - multiline - value={ - isFormDisabled - ? '****************' - : extraEnvs.GCP_SERVICE_ACCOUNT_CONTENTS - } - className={classes.marginTop} - InputLabelProps={{ - shrink: true, - }} - onChange={(e) => - setExtraEnvs({ - ...extraEnvs, - GCP_SERVICE_ACCOUNT_CONTENTS: e.target.value, - }) - } - /> - } - /> - ) : null} - {deployingState.status === 'loading' || - deployingState.status === 'finished' ? ( - - ) : null} - - - - - )} - - ) -} diff --git a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/streamLogs.ts b/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/streamLogs.ts deleted file mode 100644 index 5cffaaff..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabFormSteps/streamLogs.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { initStreamLogs } from '@postgres.ai/platform/src/api/configs/initStreamLogs' -import { getTaskState } from 'api/configs/getTaskState' -import { regenerateCode } from 'api/configs/regenerateCode' - -export const establishConnection = async ({ - taskId, - otCode, - userID, - isConnected, - setIsConnected, -}: { - taskId: string - otCode: string - userID?: number - isConnected: boolean - setIsConnected: (isConnected: boolean) => void -}) => { - const logElement = document.getElementById('logs-container') - - if (logElement === null) { - return - } - - const appendLogElement = (logEntry: string) => { - const codeTag = logElement.querySelector('code') - if (codeTag) { - codeTag.appendChild(document.createTextNode(logEntry + '\n')) - logElement.appendChild(codeTag) - } - } - - const socket = initStreamLogs(taskId, otCode) - - socket.onclose = () => { - setIsConnected(false) - } - - socket.onerror = () => { - if (!isConnected) { - return - } - - setTimeout(() => { - getTaskState({ taskID: taskId, userID }).then((res) => { - if ( - res.response?.state && - res.response.state !== 'finished' && - res.response.state !== 'error' - ) { - while (logElement.firstChild) { - logElement.removeChild(logElement.firstChild) - } - regenerateCode({ taskID: taskId, userID }).then((res) => { - if (res.response) { - establishConnection({ - taskId, - otCode: res.response?.otCode, - userID, - isConnected, - setIsConnected, - }) - } - }) - } - }) - }, 5000) - } - - socket.onmessage = function (event) { - const logEntry = decodeURIComponent(atob(event.data)) - appendLogElement(logEntry) - setIsConnected(true) - } -} diff --git a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabInstanceForm.tsx b/ui/packages/platform/src/components/DbLabInstanceForm/DbLabInstanceForm.tsx deleted file mode 100644 index a7b5a73d..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceForm/DbLabInstanceForm.tsx +++ /dev/null @@ -1,717 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import cn from 'classnames' -import { useEffect, useReducer } from 'react' -import { Box } from '@mui/material' -import { - Tab, - Tabs, - TextField, - Button, - MenuItem, - InputAdornment, -} from '@material-ui/core' - -import ConsolePageTitle from './../ConsolePageTitle' -import { TabPanel } from 'pages/JoeSessionCommand/TabPanel' -import { WarningWrapper } from 'components/Warning/WarningWrapper' -import { ClassesType } from '@postgres.ai/platform/src/components/types' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { DbLabInstanceFormProps } from 'components/DbLabInstanceForm/DbLabInstanceFormWrapper' -import { StorageSlider } from 'components/DbLabInstanceForm/DbLabInstanceFormSlider' -import { CloudProvider, getCloudProviders } from 'api/cloud/getCloudProviders' -import { CloudVolumes, getCloudVolumes } from 'api/cloud/getCloudVolumes' -import { initialState, reducer } from 'components/DbLabInstanceForm/reducer' -import { DbLabInstanceFormSidebar } from 'components/DbLabInstanceForm/DbLabInstanceFormSidebar' -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { StubSpinner } from '@postgres.ai/shared/components/StubSpinnerFlex' -import { Select } from '@postgres.ai/shared/components/Select' - -import { generateToken, validateDLEName } from 'utils/utils' -import urls from 'utils/urls' - -import { AnsibleInstance } from 'components/DbLabInstanceForm/DbLabFormSteps/AnsibleInstance' -import { CloudRegion, getCloudRegions } from 'api/cloud/getCloudRegions' -import { CloudInstance, getCloudInstances } from 'api/cloud/getCloudInstances' -import { DockerInstance } from './DbLabFormSteps/DockerInstance' -import { availableTags } from 'components/DbLabInstanceForm/utils' -import { SimpleInstance } from './DbLabFormSteps/SimpleInstance' - -interface DbLabInstanceFormWithStylesProps extends DbLabInstanceFormProps { - classes: ClassesType - auth?: { - userId: number - } -} - -const DbLabInstanceForm = (props: DbLabInstanceFormWithStylesProps) => { - const { classes, orgPermissions } = props - const [state, dispatch] = useReducer(reducer, initialState) - - const permitted = !orgPermissions || orgPermissions.dblabInstanceCreate - const urlParams = new URLSearchParams(window.location.search) - const urlTaskID = urlParams.get('taskID') - const urlProvider = urlParams.get('provider') - - useEffect(() => { - if (urlTaskID && urlProvider) { - dispatch({ - type: 'set_form_step', - formStep: 'simple', - taskID: urlTaskID, - provider: urlProvider, - }) - } else { - dispatch({ - type: 'set_form_step', - formStep: initialState.formStep, - provider: initialState.provider, - }) - } - }, [urlTaskID, urlProvider]) - - useEffect(() => { - const fetchCloudDetails = async () => { - dispatch({ type: 'set_is_loading', isLoading: true }) - try { - const cloudRegions = await getCloudRegions(initialState.provider) - const cloudVolumes = await getCloudVolumes(initialState.provider) - const serviceProviders = await getCloudProviders() - const ssdCloudVolumes = cloudVolumes.response.filter( - (volume: CloudVolumes) => volume.api_name === initialState?.api_name, - )[0] - - dispatch({ - type: 'set_initial_state', - cloudRegions: cloudRegions.response, - volumes: cloudVolumes.response, - volumeType: `${ssdCloudVolumes.api_name} (${ssdCloudVolumes.cloud_provider}: ${ssdCloudVolumes.native_name})`, - volumeCurrency: ssdCloudVolumes.native_reference_price_currency, - volumePricePerHour: - ssdCloudVolumes.native_reference_price_per_1000gib_per_hour, - volumePrice: - (initialState.storage * - ssdCloudVolumes.native_reference_price_per_1000gib_per_hour) / - 1000, - serviceProviders: serviceProviders.response, - isLoading: false, - }) - } catch (error) { - console.log(error) - } - } - fetchCloudDetails() - }, []) - - useEffect(() => { - const fetchUpdatedDetails = async () => { - try { - const cloudRegions = await getCloudRegions(state.provider) - const cloudVolumes = await getCloudVolumes(state.provider) - const ssdCloudVolumes = cloudVolumes.response.filter( - (volume: CloudVolumes) => volume.api_name === initialState?.api_name, - )[0] - dispatch({ - type: 'update_initial_state', - volumes: cloudVolumes.response, - volumeType: `${ssdCloudVolumes.api_name} (${ssdCloudVolumes.cloud_provider}: ${ssdCloudVolumes.native_name})`, - volumeCurrency: ssdCloudVolumes.native_reference_price_currency, - volumePricePerHour: - ssdCloudVolumes.native_reference_price_per_1000gib_per_hour, - volumePrice: - (initialState.storage * - ssdCloudVolumes.native_reference_price_per_1000gib_per_hour) / - 1000, - cloudRegions: cloudRegions.response, - }) - } catch (error) { - console.log(error) - } - } - fetchUpdatedDetails() - }, [state.api_name, state.provider]) - - useEffect(() => { - if (state.location.native_code && state.provider) { - const fetchUpdatedDetails = async () => { - dispatch({ type: 'set_is_reloading', isReloading: true }) - try { - const cloudInstances = await getCloudInstances({ - provider: state.provider, - region: state.location.native_code, - }) - - dispatch({ - type: 'update_instance_type', - cloudInstances: cloudInstances.response, - instanceType: cloudInstances.response[0], - isReloading: false, - }) - } catch (error) { - console.log(error) - } - } - fetchUpdatedDetails() - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.location.native_code, state.provider]) - - const uniqueRegionsByProvider = state.cloudRegions - .map((region: CloudRegion) => region.world_part) - .filter( - (value: string, index: number, self: string) => - self.indexOf(value) === index, - ) - - const filteredRegions = state.cloudRegions.filter( - (region: CloudRegion) => region.world_part === state.region, - ) - - const pageTitle = - const breadcrumbs = ( - - ) - - const handleGenerateToken = () => { - dispatch({ - type: 'change_verification_token', - verificationToken: generateToken(), - }) - } - - const handleChangeVolume = ( - event: React.ChangeEvent, - ) => { - const volumeApiName = event.target.value.split(' ')[0] - const selectedVolume = state.volumes.filter( - (volume: CloudVolumes) => volume.api_name === volumeApiName, - )[0] - - dispatch({ - type: 'change_volume_type', - volumeType: event.target.value, - volumePricePerHour: - selectedVolume.native_reference_price_per_1000gib_per_hour, - volumePrice: - (state.storage * - selectedVolume.native_reference_price_per_1000gib_per_hour) / - 1000, - }) - } - - const handleSetFormStep = (step: string) => { - dispatch({ type: 'set_form_step', formStep: step }) - } - - const handleReturnToList = () => { - props.history.push(urls.linkDbLabInstances(props)) - } - - const handleReturnToForm = () => { - dispatch({ type: 'set_form_step', formStep: initialState.formStep }) - } - - const requirePublicKeys = - !state.publicKeys && (state.provider === 'aws' || state.provider === 'gcp') - - const calculateVolumePrice = (databaseSize: number, snapshots: number) => { - let storage = databaseSize * snapshots - if (storage > 2000) storage = 2000 - - return (storage * state.volumePricePerHour) / 1000 - } - - if (state.isLoading) return - - return ( -
- {breadcrumbs} - - {pageTitle} - - {!permitted && ( - - You do not have permission to add Database Lab instances. - - )} - -
- {state.formStep === initialState.formStep && permitted ? ( - <> - {state.isReloading && ( - - )} -
-

- 1. Select your cloud provider -

-
- {state.serviceProviders.map( - (provider: CloudProvider, index: number) => ( -
- dispatch({ - type: 'change_provider', - provider: provider.api_name, - }) - } - > - {provider.label} -
- ), - )} -
-

- 2. Select your cloud region -

-
- | null, value: string) => - dispatch({ - type: 'change_region', - region: value, - location: state.cloudRegions.find( - (region: CloudRegion) => - region.world_part === value && - region.cloud_provider === state.provider, - ), - }) - } - > - {uniqueRegionsByProvider.map( - (region: string, index: number) => ( - - ), - )} - -
- - {filteredRegions.map((region: CloudRegion, index: number) => ( -
- dispatch({ - type: 'change_location', - location: region, - }) - } - > -

{region.api_name}

-

🏴 {region.label}

-
- ))} -
- {state.instanceType ? ( - <> -

- 3. Choose instance type -

-

- A larger instance can accommodate more dev/test activities. - For example, a team of 5 engineers requiring 5-10 clones - during peak times should consider a minimum instance size of - 8 vCPUs and 32 GiB. -

- - {state.cloudInstances.map( - (instance: CloudInstance, index: number) => ( -
- dispatch({ - type: 'change_instance_type', - instanceType: instance, - }) - } - > -

- {instance.api_name} ( - {state.instanceType.cloud_provider}:{' '} - {instance.native_name}) -

-
- 🔳 {instance.native_vcpus} CPU - 🧠 {instance.native_ram_gib} GiB RAM -
-
- ), - )} -
-

4. Database volume

- - - - - {(state.volumes as CloudVolumes[]).map((p, id) => { - const volumeName = `${p.api_name} (${p.cloud_provider}: ${p.native_name})` - return ( - - {volumeName} - - ) - })} - - - - - GiB - - ), - }} - value={Number(state.databaseSize)?.toFixed(2)} - className={classes.filterSelect} - onChange={( - event: React.ChangeEvent< - HTMLTextAreaElement | HTMLInputElement - >, - ) => { - dispatch({ - type: 'change_volume_price', - storage: Math.min( - Number(event.target.value) * state.snapshots, - 2000, - ), - databaseSize: event.target.value, - volumePrice: calculateVolumePrice( - Number(event.target.value), - state.snapshots, - ), - }) - }} - /> - × - - {Number(state.snapshots) === 1 - ? 'snapshot' - : 'snapshots'} - - ), - }} - value={state.snapshots} - className={classes.filterSelect} - onChange={( - event: React.ChangeEvent< - HTMLTextAreaElement | HTMLInputElement - >, - ) => { - dispatch({ - type: 'change_snapshots', - snapshots: Number(event.target.value), - storage: Math.min( - Number(event.target.value) * state.databaseSize, - 2000, - ), - volumePrice: calculateVolumePrice( - state.databaseSize, - Number(event.target.value), - ), - }) - }} - /> - - - , value: unknown) => { - dispatch({ - type: 'change_volume_price', - storage: value, - databaseSize: Number(value) / state.snapshots, - volumePrice: - (Number(value) * state.volumePricePerHour) / 1000, - }) - }} - /> - -

5. Provide DBLab name

- , - ) => - dispatch({ - type: 'change_name', - name: event.target.value, - }) - } - /> -

- 6. Define DBLab verification token (keep it secret!) -

-
- , - ) => - dispatch({ - type: 'change_verification_token', - verificationToken: event.target.value, - }) - } - /> - -
-

- 7. Choose DBLab version -

- { - const defaultTag = availableTags[0] - - return { - value: tag, - children: defaultTag === tag ? `${tag} (default)` : tag, - } - }) ?? [] - } - value={state.tag} - onChange={( - e: React.ChangeEvent, - ) => - dispatch({ - type: 'set_tag', - tag: e.target.value, - }) - } - /> -

- 4. Provide SSH public keys (one per line) -

{' '} -

- The specified ssh public keys will be added to authorized_keys - on the DBLab server. Add your public key here to have access to - the server after deployment. -

- , - ) => - dispatch({ - type: 'change_public_keys', - publicKeys: event.target.value, - }) - } - /> -
- - !validateDLEName(state.name) && handleSetFormStep('docker') - } - /> - - ) : state.formStep === 'ansible' && permitted ? ( - - ) : state.formStep === 'docker' && permitted ? ( - - ) : null} -
-
- ) -} - -export default DbLabInstanceInstallForm diff --git a/ui/packages/platform/src/components/DbLabInstanceInstallForm/DbLabInstanceInstallFormSidebar.tsx b/ui/packages/platform/src/components/DbLabInstanceInstallForm/DbLabInstanceInstallFormSidebar.tsx deleted file mode 100644 index 2917990e..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceInstallForm/DbLabInstanceInstallFormSidebar.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Button, makeStyles } from '@material-ui/core' - -import { initialState } from 'components/DbLabInstanceForm/reducer' - -const useStyles = makeStyles({ - boxShadow: { - padding: '24px', - boxShadow: '0 8px 16px #3a3a441f, 0 16px 32px #5a5b6a1f', - }, - aside: { - width: '100%', - height: 'fit-content', - borderRadius: '4px', - display: 'flex', - flexDirection: 'column', - justifyContent: 'flex-start', - flex: '1 1 0', - position: 'sticky', - top: 10, - - '& h2': { - fontSize: '14px', - fontWeight: 500, - margin: '0 0 10px 0', - height: 'fit-content', - }, - - '& span': { - fontSize: '13px', - }, - - '& button': { - padding: '10px 20px', - marginTop: '20px', - }, - - '@media (max-width: 1200px)': { - position: 'relative', - boxShadow: 'none', - borderRadius: '0', - padding: '0', - flex: 'auto', - marginBottom: '30px', - - '& button': { - width: 'max-content', - }, - }, - }, - asideSection: { - padding: '12px 0', - borderBottom: '1px solid #e0e0e0', - - '& span': { - color: '#808080', - }, - - '& p': { - margin: '5px 0 0 0', - fontSize: '13px', - }, - }, -}) - -export const DbLabInstanceFormInstallSidebar = ({ - state, - handleCreate, - disabled, -}: { - state: typeof initialState - handleCreate: () => void - disabled: boolean -}) => { - const classes = useStyles() - - return ( -
-
- {state.name && ( -
- Name -

{state.name}

-
- )} - {state.tag && ( -
- Tag -

{state.tag}

-
- )} - -
-
- ) -} diff --git a/ui/packages/platform/src/components/DbLabInstanceInstallForm/DbLabInstanceInstallFormWrapper.tsx b/ui/packages/platform/src/components/DbLabInstanceInstallForm/DbLabInstanceInstallFormWrapper.tsx deleted file mode 100644 index c294d604..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceInstallForm/DbLabInstanceInstallFormWrapper.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { RouteComponentProps } from 'react-router' - -import DbLabInstanceInstallForm from 'components/DbLabInstanceInstallForm/DbLabInstanceInstallForm' - -import { useInstanceFormStyles } from 'components/DbLabInstanceForm/DbLabInstanceFormWrapper' - -export interface DbLabInstanceFormProps { - edit?: boolean - orgId: number - project: string | undefined - history: RouteComponentProps['history'] - orgPermissions: { - dblabInstanceCreate?: boolean - } -} - -export const DbLabInstanceFormInstallWrapper = ( - props: DbLabInstanceFormProps, -) => { - - const classes = useInstanceFormStyles() - - return -} diff --git a/ui/packages/platform/src/components/DbLabInstanceInstallForm/reducer/index.tsx b/ui/packages/platform/src/components/DbLabInstanceInstallForm/reducer/index.tsx deleted file mode 100644 index 83a3fe76..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceInstallForm/reducer/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ -import { ReducerAction } from 'react' - -import { availableTags } from 'components/DbLabInstanceForm/utils' - -export const initialState = { - isLoading: false, - formStep: 'create', - api_name: 'ssd', - name: '', - publicKeys: '', - tag: availableTags[0], - verificationToken: '', -} - -export const reducer = ( - state: typeof initialState, - // @ts-ignore - action: ReducerAction, -) => { - switch (action.type) { - case 'change_name': { - return { - ...state, - name: action.name, - } - } - case 'change_verification_token': { - return { - ...state, - verificationToken: action.verificationToken, - } - } - case 'change_public_keys': { - return { - ...state, - publicKeys: action.publicKeys, - } - } - case 'set_form_step': { - return { - ...state, - formStep: action.formStep, - } - } - case 'set_tag': { - return { - ...state, - tag: action.tag, - } - } - default: - throw new Error() - } -} diff --git a/ui/packages/platform/src/components/DbLabInstanceInstallForm/utils/index.ts b/ui/packages/platform/src/components/DbLabInstanceInstallForm/utils/index.ts deleted file mode 100644 index 8c1f81ff..00000000 --- a/ui/packages/platform/src/components/DbLabInstanceInstallForm/utils/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { initialState } from '../reducer' -import { sePackageTag, DEBUG_API_SERVER } from 'components/DbLabInstanceForm/utils' - -const API_SERVER = process.env.REACT_APP_API_SERVER - -export const getPlaybookCommand = ( - state: typeof initialState, - orgKey: string, -) => - `docker run --rm -it \\\r - --volume $HOME/.ssh:/root/.ssh:ro \\\r - --env ANSIBLE_SSH_ARGS="-F none" \\\r - postgresai/dle-se-ansible:${sePackageTag} \\\r - ansible-playbook deploy_dle.yml --extra-vars \\\r - "dblab_engine_host='user@server-ip-address' \\\r - platform_project_name='${state.name}' \\\r - dblab_engine_version='${state.tag}' \\\r - ${ orgKey ? `platform_org_key='${orgKey}' \\\r` : `` } - ${ API_SERVER === DEBUG_API_SERVER ? `platform_url='${DEBUG_API_SERVER}' \\\r` : `` } - ${ state.publicKeys ? `ssh_public_keys='${state.publicKeys}' \\\r` : `` } - dblab_engine_verification_token='${state.verificationToken}'" -` - -export const getAnsiblePlaybookCommand = ( - state: typeof initialState, - orgKey: string, -) => - `ansible-playbook deploy_dle.yml --extra-vars \\\r - "dblab_engine_host='user@server-ip-address' \\\r - platform_project_name='${state.name}' \\\r - dblab_engine_version='${state.tag}' \\\r - ${orgKey ? `platform_org_key='${orgKey}' \\\r` : ``} - ${ - API_SERVER === DEBUG_API_SERVER - ? `platform_url='${DEBUG_API_SERVER}' \\\r` - : `` - } - ${state.publicKeys ? `ssh_public_keys='${state.publicKeys}' \\\r` : ``} - dblab_engine_verification_token='${state.verificationToken}'" -` - -export const getAnsibleInstallationCommand = () => - `sudo apt update -sudo apt install -y python3-pip -pip3 install ansible` - -export const cloneRepositoryCommand = () => - `git clone --depth 1 --branch ${sePackageTag} \\ - https://gitlab.com/postgres-ai/dle-se-ansible.git \\ - && cd dle-se-ansible/` diff --git a/ui/packages/platform/src/components/DbLabInstances/DbLabInstances.tsx b/ui/packages/platform/src/components/DbLabInstances/DbLabInstances.tsx deleted file mode 100644 index 4b75080d..00000000 --- a/ui/packages/platform/src/components/DbLabInstances/DbLabInstances.tsx +++ /dev/null @@ -1,625 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component, MouseEvent } from 'react' -import { formatDistanceToNowStrict } from 'date-fns' -import { - Table, - TableBody, - TableCell, - TableHead, - TableRow, - TextField, - IconButton, - Menu, - MenuItem, - Tooltip, -} from '@material-ui/core' -import MoreVertIcon from '@material-ui/icons/MoreVert' -import WarningIcon from '@material-ui/icons/Warning' - -import { HorizontalScrollContainer } from '@postgres.ai/shared/components/HorizontalScrollContainer' -import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner' -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { Modal } from '@postgres.ai/shared/components/Modal' -import { styles } from '@postgres.ai/shared/styles/styles' -import { - ClassesType, - ProjectProps, - RefluxTypes, -} from '@postgres.ai/platform/src/components/types' - -import Actions from '../../actions/actions' -import ConsolePageTitle from './../ConsolePageTitle' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import { messages } from '../../assets/messages' -import Store from '../../stores/store' -import Urls from '../../utils/urls' -import format from '../../utils/format' -import { isHttps } from '../../utils/utils' -import { WarningWrapper } from 'components/Warning/WarningWrapper' -import { ProjectDataType, getProjectAliasById } from 'utils/aliases' -import { InstanceStateDto } from '@postgres.ai/shared/types/api/entities/instanceState' -import { InstanceDto } from '@postgres.ai/shared/types/api/entities/instance' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { DbLabStatusWrapper } from 'components/DbLabStatus/DbLabStatusWrapper' -import { DbLabInstancesProps } from 'components/DbLabInstances/DbLabInstancesWrapper' -import { CreatedDbLabCards } from 'components/CreateDbLabCards/CreateDbLabCards' -import { ConsoleButtonWrapper } from 'components/ConsoleButton/ConsoleButtonWrapper' - -interface DbLabInstancesWithStylesProps extends DbLabInstancesProps { - classes: ClassesType -} - -interface DbLabInstancesState { - modalOpen: boolean - data: { - auth: { - token: string - } | null - userProfile: { - data: { - orgs: ProjectDataType - } - } - dbLabInstances: { - orgId: number - data: { - [org: string]: { - created_at: string - project_label_or_name: string - plan: string - project_name: string - project_label: string - url: string - use_tunnel: boolean - isProcessing: boolean - id: string - project_alias: string - state: InstanceStateDto - dto: InstanceDto - } - } - isProcessing: boolean - projectId: string | number | undefined - error: boolean - } | null - dbLabInstanceStatus: { - instanceId: string - isProcessing: boolean - } - projects: Omit - } - anchorEl: (EventTarget & HTMLButtonElement) | null -} - -class DbLabInstances extends Component< - DbLabInstancesWithStylesProps, - DbLabInstancesState -> { - componentDidMount() { - const that = this - const orgId = this.props.orgId ? this.props.orgId : null - let projectId = this.props.projectId ? this.props.projectId : null - - if (!projectId) { - projectId = - this.props.match && - this.props.match.params && - this.props.match.params.projectId - ? this.props.match.params.projectId - : null - } - - if (projectId) { - Actions.setDbLabInstancesProject(orgId, projectId) - } else { - Actions.setDbLabInstancesProject(orgId, 0) - } - - this.unsubscribe = (Store.listen as RefluxTypes['listen'])(function () { - const auth: DbLabInstancesState['data']['auth'] = - this.data && this.data.auth ? this.data.auth : null - const dbLabInstances: DbLabInstancesState['data']['dbLabInstances'] = - this.data && this.data.dbLabInstances ? this.data.dbLabInstances : null - const projects: Omit = - this.data && this.data.projects ? this.data.projects : null - - if ( - auth && - auth.token && - !dbLabInstances?.isProcessing && - !dbLabInstances?.error && - !that.state - ) { - Actions.getDbLabInstances(auth.token, orgId, projectId) - } - - if ( - auth && - auth.token && - !projects.isProcessing && - !projects.error && - !that.state - ) { - Actions.getProjects(auth.token, orgId) - } - - that.setState({ data: this.data }) - }) - - Actions.refresh() - } - - unsubscribe: Function - componentWillUnmount() { - this.unsubscribe() - } - - handleClick = ( - _: MouseEvent, - id: string, - ) => { - const url = Urls.linkDbLabInstance(this.props, id) - - if (url) { - this.props.history.push(url) - } - } - - handleChangeProject = ( - event: React.ChangeEvent, - ) => { - const org = this.props.org ? this.props.org : null - const orgId = this.props.orgId ? this.props.orgId : null - const projectId = event.target.value - const project = this.state.data - ? getProjectAliasById(this.state.data?.userProfile?.data?.orgs, projectId) - : '' - const props = { org, orgId, projectId, project } - - Actions.setDbLabInstancesProject(orgId, event.target.value) - this.props.history.push(Urls.linkDbLabInstances(props)) - } - - registerButtonHandler = (provider: string) => { - this.props.history.push(Urls.linkDbLabInstanceAdd(this.props, provider)) - } - - openMenu = (event: MouseEvent) => { - event.stopPropagation() - this.setState({ anchorEl: event.currentTarget }) - } - - closeMenu = () => { - this.setState({ anchorEl: null }) - } - - menuHandler = (_: MouseEvent, action: string) => { - const anchorEl = this.state.anchorEl - - this.closeMenu() - - setTimeout(() => { - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const data = - this.state.data && this.state.data.dbLabInstances - ? this.state.data.dbLabInstances - : null - - if (anchorEl) { - const instanceId = anchorEl.getAttribute('aria-label') - if (!instanceId) { - return - } - - let project = '' - if (data?.data) { - for (const i in data.data) { - if (parseInt(data.data[i].id, 10) === parseInt(instanceId, 10)) { - project = data.data[i].project_alias - } - } - } - - switch (action) { - case 'addclone': - this.props.history.push( - Urls.linkDbLabCloneAdd( - { org: this.props.org, project: project }, - instanceId, - ), - ) - - break - - case 'destroy': - /* eslint no-alert: 0 */ - if ( - window.confirm( - 'Are you sure you want to remove this Database Lab instance?', - ) === true - ) { - Actions.destroyDbLabInstance(auth?.token, instanceId) - } - - break - - case 'refresh': - Actions.getDbLabInstanceStatus(auth?.token, instanceId) - - break - - case 'editProject': - this.props.history.push( - Urls.linkDbLabInstanceEditProject( - { org: this.props.org, project: project }, - instanceId, - ), - ) - - break - - default: - break - } - } - }, 100) - } - - render() { - const { classes, orgPermissions, orgId } = this.props - const data = - this.state && this.state.data && this.state.data.dbLabInstances - ? this.state.data.dbLabInstances - : null - const projects = - this.state && this.state.data && this.state.data?.projects - ? this.state.data.projects - : null - const projectId = this.props.projectId ? this.props.projectId : null - const menuOpen = Boolean(this.state && this.state.anchorEl) - const title = 'Database Lab Instances' - const addPermitted = !orgPermissions || orgPermissions.dblabInstanceCreate - const deletePermitted = - !orgPermissions || orgPermissions.dblabInstanceDelete - - const getVersionDigits = (str: string) => { - if (!str) { - return 'N/A' - } - - const digits = str.match(/\d+/g) - - if (digits && digits.length > 0) { - return `${digits[0]}.${digits[1]}.${digits[2]}` - } - return '' - } - - const addInstanceButton = ( - this.setState({ modalOpen: true })} - title={addPermitted ? 'Create new DBLab' : messages.noPermission} - > - New DBLab - - ) - const pageTitle = ( - 0 - ? [addInstanceButton] - : [] - } - /> - ) - - let projectFilter = null - if (projects && projects.data && data) { - projectFilter = ( -
- this.handleChangeProject(event)} - select - label="Project" - inputProps={{ - name: 'project', - id: 'project-filter', - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - variant="outlined" - className={classes.filterSelect} - > - All - - {projects.data.map((p) => { - return ( - - {p?.project_label_or_name || p.name} - - ) - })} - -
- ) - } - - const breadcrumbs = ( - - ) - - if (orgPermissions && !orgPermissions.dblabInstanceList) { - return ( -
- {breadcrumbs} - - {pageTitle} - - {messages.noPermissionPage} -
- ) - } - - if (this.state?.data && this.state.data?.dbLabInstances?.error) { - return ( -
- {breadcrumbs} - - - - -
- ) - } - - if ( - !data || - (data && data.isProcessing) || - data.orgId !== orgId || - data.projectId !== (projectId ? projectId : 0) - ) { - return ( -
- {breadcrumbs} - - - - -
- ) - } - - const CardsModal = () => ( - this.setState({ modalOpen: false })} - aria-labelledby="simple-modal-title" - aria-describedby="simple-modal-description" - > - - - ) - - let table = ( - - ) - - let menu = null - if (data.data && Object.keys(data.data).length > 0) { - table = ( - - - - - Project - Instance ID - URL - Clones - Plan - Version - State - Created at -   - - - - - {Object.keys(data.data).map((index) => { - return ( - - this.handleClick(event, data.data[index].id) - } - style={{ cursor: 'pointer' }} - > - - {data.data[index].project_label_or_name || - data.data[index].project_name} - - - - {data.data[index].id} - - - {data.data[index].state && data.data[index].url - ? data.data[index].url - : 'N/A'} - {!isHttps(data.data[index].url) && - data.data[index].url && - !data.data[index].use_tunnel ? ( - - - - ) : null} - - - {data.data[index]?.state?.cloning?.numClones ?? - data.data[index]?.state?.clones?.length ?? - 'N/A'} - - - {data.data[index] && - (data.data[index]?.plan === 'EE' - ? 'Enterprise' - : data.data[index]?.plan === 'SE' - ? 'Standard' - : data.data[index]?.plan)} - - - {getVersionDigits( - data.data[index] && - (data.data[index].state?.engine?.version as string), - )} - - - {data.data[index].state && data.data[index].url ? ( - - ) : ( - 'N/A' - )} - - - - - {format.formatTimestampUtc( - data.data[index].created_at, - ) ?? ''} - - - - - {data.data[index].isProcessing || - (this.state.data?.dbLabInstanceStatus.instanceId === - index && - this.state.data.dbLabInstanceStatus.isProcessing) ? ( - - ) : null} - - - - - - ) - })} - -
-
- ) - - const selectedInstance = Object.values(data.data).filter((item) => { - const anchorElLabel = this.state.anchorEl?.getAttribute('aria-label') - // eslint-disable-next-line eqeqeq - return anchorElLabel && item.id == anchorElLabel - })[0] - - menu = ( - - this.menuHandler(event, 'editProject')} - disabled={!addPermitted || selectedInstance?.plan === 'SE'} - > - Edit - - this.menuHandler(event, 'addclone')} - disabled={selectedInstance?.plan === 'SE'} - > - Create clone - - this.menuHandler(event, 'refresh')} - disabled={selectedInstance?.plan === 'SE'} - > - Refresh - - this.menuHandler(event, 'destroy')} - > - Remove from List - - - ) - } - - return ( -
- {breadcrumbs} - - {pageTitle} - - {projectFilter} - - {table} - - {menu} - - -
- ) - } -} - -export default DbLabInstances diff --git a/ui/packages/platform/src/components/DbLabInstances/DbLabInstancesWrapper.tsx b/ui/packages/platform/src/components/DbLabInstances/DbLabInstancesWrapper.tsx deleted file mode 100644 index 5433d2c8..00000000 --- a/ui/packages/platform/src/components/DbLabInstances/DbLabInstancesWrapper.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { styles } from '@postgres.ai/shared/styles/styles' -import DbLabInstances from 'components/DbLabInstances/DbLabInstances' -import { RouteComponentProps } from 'react-router' -import { colors } from '@postgres.ai/shared/styles/colors' - -export interface DbLabInstancesProps { - orgId: number - org: string | number - project: string | undefined - projectId: string | number | undefined - history: RouteComponentProps['history'] - match: { - params: { - project?: string - projectId?: string | number | undefined - org?: string - } - } - orgPermissions: { - dblabInstanceCreate?: boolean - dblabInstanceDelete?: boolean - dblabInstanceList?: boolean - } -} - -export const DbLabInstancesWrapper = (props: DbLabInstancesProps) => { - const useStyles = makeStyles( - { - root: { - ...(styles.root as Object), - display: 'flex', - flexDirection: 'column', - }, - filterSelect: { - ...styles.inputField, - width: 150, - }, - cell: { - '& > a': { - color: 'black', - textDecoration: 'none', - }, - '& > a:hover': { - color: 'black', - textDecoration: 'none', - }, - }, - inTableProgress: { - width: '30px!important', - height: '30px!important', - }, - warningIcon: { - color: colors.state.warning, - fontSize: '1.2em', - position: 'absolute', - marginLeft: 5, - }, - tooltip: { - fontSize: '10px!important', - }, - timeLabel: { - lineHeight: '16px', - fontSize: 12, - cursor: 'pointer', - }, - buttonContainer: { - display: 'flex', - gap: 10, - }, - flexContainer: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: '100%', - height: '100%', - gap: 40, - marginTop: '20px', - - '& > div': { - maxWidth: '300px', - width: '100%', - height: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - border: '1px solid #e0e0e0', - padding: '20px', - borderRadius: '4px', - cursor: 'pointer', - fontSize: '15px', - transition: 'border 0.3s ease-in-out', - - '&:hover': { - border: '1px solid #FF6212', - }, - }, - }, - }, - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/DbLabSession/DbLabSession.tsx b/ui/packages/platform/src/components/DbLabSession/DbLabSession.tsx deleted file mode 100644 index e41e63f7..00000000 --- a/ui/packages/platform/src/components/DbLabSession/DbLabSession.tsx +++ /dev/null @@ -1,1018 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { NavLink } from 'react-router-dom' -import { - Button, - Table, - TableBody, - TableCell, - TableRow, - TextField, - ExpansionPanelSummary, - ExpansionPanelDetails, - ExpansionPanel, - Typography, -} from '@material-ui/core' -import ExpandMoreIcon from '@material-ui/icons/ExpandMore' -import { formatDistanceToNowStrict } from 'date-fns' - -import { HorizontalScrollContainer } from '@postgres.ai/shared/components/HorizontalScrollContainer' -import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner' -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { icons } from '@postgres.ai/shared/styles/icons' -import { ClassesType, RefluxTypes } from '@postgres.ai/platform/src/components/types' - -import Store from '../../stores/store' -import Actions from '../../actions/actions' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import ConsolePageTitle from './../ConsolePageTitle' -import { WarningWrapper } from 'components/Warning/WarningWrapper' -import { messages } from '../../assets/messages' -import format from '../../utils/format' -import urls, { PropsType } from '../../utils/urls' -import { GatewayLink } from '@postgres.ai/shared/components/GatewayLink' -import dblabutils from '../../utils/dblabutils' -import { MomentInput } from 'moment' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { DbLabStatusWrapper } from 'components/DbLabStatus/DbLabStatusWrapper' -import { DbLabSessionProps } from 'components/DbLabSession/DbLabSessionWrapper' - -interface DbLabSessionWithStylesProps extends DbLabSessionProps { - classes: ClassesType -} - -interface Session { - id: number - project_name: string - internal_instance_id: string | undefined - duration: number - started_at: string - config: { - [config: string | number]: number - } - tags: { - request_link: string - username_full: string - username_link: string - launched_by: string - dle_version: string - data_state_at: string - branch: string - branch_link: string - revision: string - revision_link: string - } - result: { - status: string - intervals: { - warning: boolean - started_at: MomentInput - duration: number - }[] - summary: { - elapsed: boolean - total_intervals: number - total_duration: number - checklist: { - [x: string]: { author_id: string } - } - } - } -} - -interface DbLabSessionState { - logsExpanded: boolean - artifactsExpanded: { [x: string]: boolean } - configurationExpanded: boolean - intervalsExpanded: boolean - data: { - dbLabInstanceStatus: { - error: boolean - } - auth: { - token: string | null - } | null - dbLabSession: { - artifactsData: Record - isLogsComplete: boolean - isArtifactsProcessed: boolean - isArtifactsProcessing: boolean - isLogDownloading: boolean - downloadingArtifactType: string - isArtifactDownloading: boolean - artifacts: - | { - artifact_size: number - artifact_type: string - dblab_session_id: string - }[] - | null - artifactData: { - [data: string]: { - isProcessing: boolean - isProcessed: boolean - data: string - } - } | null - logs: { - process_id: string - connection_from: string - session_id: string - session_line_num: string - command_tag: string - session_start_time: string - virtual_transaction_id: string - transaction_id: string - error_severity: string - sql_state_code: string - message: string - detail: string - hint: string - internal_query: string - internal_query_pos: string - context: string - query: string - query_pos: string - location: string - application_name: string - backend_type: string - database_name: string - user_name: string - log_time: string - id: number - }[] - error: boolean - errorMessage: string - errorCode: number - isLogsProcessing: boolean - isProcessing: boolean - data: Session - } | null - } | null -} - -const PAGE_SIZE = 20 - -class DbLabSession extends Component< - DbLabSessionWithStylesProps, - DbLabSessionState -> { - unsubscribe: Function - componentDidMount() { - const sessionId = this.props.match.params.sessionId - const that = this - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - const auth = this.data && this.data.auth ? this.data.auth : null - const dbLabSession = - this.data && this.data.dbLabSession ? this.data.dbLabSession : null - - if (auth && auth.token && !dbLabSession?.isProcessing && !that.state) { - Actions.getDbLabSession(auth.token, sessionId) - } - - if ( - auth && - auth.token && - !dbLabSession?.isLogsProcessing && - !that.state - ) { - Actions.getDbLabSessionLogs(auth.token, { sessionId, limit: PAGE_SIZE }) - } - - if ( - auth && - auth.token && - !dbLabSession?.isArtifactsProcessing && - !that.state - ) { - Actions.getDbLabSessionArtifacts(auth.token, sessionId) - } - - that.setState({ data: this.data }) - - const contentContainer = document.getElementById('logs-container') - if (contentContainer && !contentContainer.getAttribute('onscroll')) { - contentContainer.addEventListener('scroll', () => { - if ( - contentContainer.scrollTop >= - contentContainer.scrollHeight - contentContainer.offsetHeight - ) { - this.showMore() - } - }) - contentContainer.setAttribute('onscroll', '1') - } - }) - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - showMore() { - const sessionId = this.props.match.params.sessionId - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const session = - this.state.data && this.state.data.dbLabSession - ? this.state.data.dbLabSession - : null - let lastId = null - - if (session && session.logs && session.logs.length) { - lastId = session.logs[session.logs.length - 1].id - } - - if (auth && auth.token && !session?.isLogsProcessing && lastId) { - Actions.getDbLabSessionLogs(auth.token, { - sessionId, - limit: PAGE_SIZE, - lastId, - }) - } - } - - getCheckDetails(session: Session, check: string) { - let intervals = null - let maxIntervals = null - let totalDuration = null - let maxDuration = null - switch (check) { - case 'no_long_dangerous_locks': - if ( - session && - session.result && - session.result.summary && - session.result.summary.total_intervals - ) { - intervals = session.result.summary.total_intervals - } - if (session && session.config && session.config.observation_interval) { - maxIntervals = session.config.observation_interval - } - if (intervals && maxIntervals) { - return ( - '(' + - intervals + - ' ' + - (intervals > 1 ? 'intervals' : 'interval') + - ' with locks of ' + - maxIntervals + - ' allowed)' - ) - } - break - case 'session_duration_acceptable': - if ( - session && - session.result && - session.result.summary && - session.result.summary.total_duration - ) { - totalDuration = session.result.summary.total_duration - } - if (session && session.config && session.config.max_duration) { - maxDuration = session.config.max_duration - } - if (totalDuration && maxDuration) { - return ( - '(spent ' + - format.formatSeconds(totalDuration, 0, '') + - ' of the allowed ' + - format.formatSeconds(maxDuration, 0, '') + - ')' - ) - } - break - - default: - } - - return '' - } - - downloadLog = () => { - const auth = - this.state && this.state.data && this.state.data.auth - ? this.state.data.auth - : null - const sessionId = this.props.match.params.sessionId - - Actions.downloadDblabSessionLog(auth?.token, sessionId) - } - - downloadArtifact = (artifactType: string) => { - const auth = - this.state && this.state.data && this.state.data.auth - ? this.state.data.auth - : null - const sessionId = this.props.match.params.sessionId - - Actions.downloadDblabSessionArtifact(auth?.token, sessionId, artifactType) - } - - getArtifact = (artifactType: string) => { - const auth = - this.state && this.state.data && this.state.data.auth - ? this.state.data.auth - : null - const sessionId = this.props.match.params.sessionId - - Actions.getDbLabSessionArtifact(auth?.token, sessionId, artifactType) - } - - render() { - const that = this - const { classes, orgPermissions } = this.props - const sessionId = this.props.match.params.sessionId - const data = - this.state && this.state.data && this.state.data.dbLabSession - ? this.state.data.dbLabSession - : null - const title = 'Database Lab observed session #' + sessionId - - const pageTitle = - const breadcrumbs = ( - - ) - - if (orgPermissions && !orgPermissions.dblabSessionView) { - return ( -
- {breadcrumbs} - - {pageTitle} - - {messages.noPermissionPage} -
- ) - } - - let errorWidget = null - if (this.state && this.state.data?.dbLabSession?.error) { - errorWidget = ( - - ) - } - - if ( - this.state && - (this.state.data?.dbLabSession?.error || - this.state.data?.dbLabInstanceStatus?.error) - ) { - return ( -
- {breadcrumbs} - - {pageTitle} - - {errorWidget} -
- ) - } - - if ( - !data || - (this.state && - this.state.data && - this.state.data?.dbLabSession?.isProcessing) - ) { - return ( -
- {breadcrumbs} - - {pageTitle} - - -
- ) - } - - const session = data && data.data ? data.data : null - const logs = data && data.logs ? data.logs : null - const artifacts = data && data.artifacts ? data.artifacts : null - const artifactData = data && data.artifactData ? data.artifactData : null - - return ( -
- {breadcrumbs} - - {pageTitle} - -
-
Summary
- - - Status: - - - -
- - - Session: - {session ? '#' + session.id : '-'} - - - - Project: - {session?.project_name ? session.project_name : '-'} - - - - DBLab instance: - {session?.internal_instance_id ? ( - - {'#' + session.internal_instance_id} - - ) : ( - '' - )} - - - - DBLab version: - {session && session.tags && session.tags.dle_version - ? session.tags.dle_version - : '-'} - - -
- - - Data state at: - {session && session.tags && session.tags.data_state_at - ? session.tags.data_state_at - : '-'} - - -
- - - Duration: - {session && - session.result && - session.result.summary && - session.result.summary.elapsed - ? session.result.summary.elapsed - : null} - {!( - session && - session.result && - session.result.summary && - session.result.summary.elapsed - ) && - session && - session.duration > 0 - ? format.formatSeconds(session.duration, 0, '') - : null} - - - - Created: - {session && - formatDistanceToNowStrict(new Date(session.started_at), { - addSuffix: true, - })} - - -
- - - Branch: - {session && - session.tags && - session.tags.branch && - session.tags.branch_link ? ( - - {session.tags.branch} - - ) : ( - - {session && session.tags && session.tags.branch - ? session.tags.branch - : '-'} - - )} - - - - Commit: - {session && - session.tags && - session.tags.revision && - session.tags.revision_link ? ( - - {session.tags.revision} - - ) : ( - - {session && session.tags && session.tags.revision - ? session.tags.revision - : '-'} - - )} - - - - Triggered by: - {session && - session.tags && - session.tags.launched_by && - session.tags.username_link ? ( - - {session.tags.launched_by} - {session.tags.username_full - ? ' (' + session.tags.username_full + ')' - : ''} - - ) : ( - - {session && session.tags && session.tags.launched_by - ? session.tags.launched_by - : '-'} - - )} - - - - PR/MR: - {session && session.tags && session.tags.request_link ? ( - - {session.tags.request_link} - - ) : ( - '-' - )} - - - - Changes: - {session && session.tags && session.tags.request_link ? ( - - {session.tags.request_link} - - ) : ( - '-' - )} - - - {false && ( - - Check documentation for the details about observed sessions: - - Database Lab – CI Observer - - - )} -
- -
- -
-
Checklist
- - {session?.result && - session.result.summary && - session.result.summary.checklist ? ( -
- {Object.keys(session.result.summary.checklist).map(function ( - key, - ) { - const details = that.getCheckDetails(session, key) - return ( - -
- {session.result.summary?.checklist && - session.result.summary.checklist[key] ? ( - - ) : ( - - )} -
-
- {format.formatDbLabSessionCheck(key)} -
{details}
-
-
- ) - })} -
- ) : ( - icons.processingLargeIcon - )} -
- -
- -
-
- Observed intervals and details -
- { - that.setState({ intervalsExpanded: expanded }) - }} - > - } - aria-controls="panel1b-content" - id="panel1b-header" - className={classes.expansionPanelSummary} - > - {that.state.intervalsExpanded - ? 'Hide intervals' - : 'Show intervals'} - - - {session?.result && - session.result?.intervals && - session.result.intervals.length > 0 ? ( -
-
-
-
Started at
-
Duration
-
- {session.result.intervals.map((i) => { - return ( -
-
-
- {i.warning - ? icons.intervalWarning - : icons.intervalOk} -
-
- {format.formatTimestampUtc(i.started_at)} -
-
- {format.formatSeconds(i.duration, 0, '')} -
-
- {i.warning && ( -
-
- -
-
- )} -
- ) - })} -
- ) : ( - - Not specified - - )} - - -
- -
- -
-
Configuration
- - { - that.setState({ configurationExpanded: expanded }) - }} - > - } - aria-controls="panel1b-content" - id="panel1b-header" - className={classes.expansionPanelSummary} - > - {that.state.configurationExpanded - ? 'Hide configuration' - : 'Show configuration'} - - - {session?.config ? ( -
- {Object.keys(session.config).map(function (key) { - return ( - - {key}: - {session.config && session.config[key]} - - ) - })} -
- ) : ( - - Not specified - - )} -
-
-
- -
- -
- -
-
- Artifacts -
- - {Array.isArray(artifacts) && artifacts.length ? ( - - - - {artifacts.map((a) => { - return ( - { - if ( - orgPermissions && - !orgPermissions.dblabSessionArtifactsView - ) { - return - } - const artifactsExpanded = - that.state.artifactsExpanded || {} - artifactsExpanded[a.artifact_type] = - !artifactsExpanded[a.artifact_type] - that.setState({ - artifactsExpanded: artifactsExpanded, - }) - if ( - artifactsExpanded[a.artifact_type] && - a.artifact_type !== 'log' && - (!data.artifactsData || - (data.artifactsData && - !data.artifactsData[a.artifact_type])) - ) { - this.getArtifact(a.artifact_type) - } - }} - > - -
-
- {a.artifact_type}  - {orgPermissions && - orgPermissions.dblabSessionArtifactsView && ( - - {icons.detailsArrow} - - )} -
-
- {dblabutils.getArtifactDescription( - a.artifact_type, - )} -
-
- {format.formatBytes(a.artifact_size, 0, true)} -
- {orgPermissions && - orgPermissions.dblabSessionArtifactsView && ( -
- -
- )} -
-
- - } - aria-controls="panel1b-content" - id="panel1b-header" - className={ - classes.artifactExpansionPanelSummary - } - > - {that.state.logsExpanded - ? 'Hide log' - : 'Show log'} - - - {a.artifact_type === 'log' ? ( -
- {Array.isArray(logs) && logs.length ? ( -
- {logs.map((r) => { - return ( -
- {r.log_time},{r.user_name}, - {r.database_name},{r.process_id}, - {r.connection_from},{r.session_id} - ,{r.session_line_num}, - {r.command_tag}, - {r.session_start_time}, - {r.virtual_transaction_id}, - {r.transaction_id}, - {r.error_severity}, - {r.sql_state_code},{r.message}, - {r.detail},{r.hint}, - {r.internal_query}, - {r.internal_query_pos},{r.context} - ,{r.query},{r.query_pos}, - {r.location},{r.application_name}, - {r.backend_type} -
- ) - })} -
- {data && data.isLogsProcessing && ( - - )} - {data && - !data.isLogsProcessing && - !data.isLogsComplete && ( - - )} -
-
- ) : ( - 'No log uploaded yet.' - )} -
- ) : ( -
- {artifactData && - artifactData[a.artifact_type] && - artifactData[a.artifact_type] - .isProcessing ? ( - - ) : null} - {artifactData && - artifactData[a.artifact_type] && - artifactData[a.artifact_type].isProcessed && - artifactData[a.artifact_type].data ? ( -
- {artifactData[a.artifact_type].data} -
- ) : null} -
- )} -
-
-
-
-
- ) - })} -
-
-
- ) : ( - - {data.isArtifactsProcessed ? 'Artifacts not found' : ''} - {data.isArtifactsProcessing ? ( - - ) : null} - - )} -
- -
-
- ) - } -} - -export default DbLabSession diff --git a/ui/packages/platform/src/components/DbLabSession/DbLabSessionWrapper.tsx b/ui/packages/platform/src/components/DbLabSession/DbLabSessionWrapper.tsx deleted file mode 100644 index 965d977e..00000000 --- a/ui/packages/platform/src/components/DbLabSession/DbLabSessionWrapper.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { colors } from '@postgres.ai/shared/styles/colors' -import { styles } from '@postgres.ai/shared/styles/styles' -import DbLabSession from 'components/DbLabSession/DbLabSession' -import { RouteComponentProps } from 'react-router' - -interface MatchParams { - sessionId: string -} - -export interface DbLabSessionProps extends RouteComponentProps { - orgPermissions: { - dblabSessionView?: boolean - dblabSessionArtifactsView?: boolean - } -} - -export interface ErrorProps { - code?: number - message?: string -} - -export const DbLabSessionWrapper = (props: DbLabSessionProps) => { - const useStyles = makeStyles( - (theme) => ({ - root: { - ...(styles.root as Object), - flex: '1 1 100%', - display: 'flex', - flexDirection: 'column', - }, - summary: { - marginTop: 20, - marginBottom: 20, - }, - paramTitle: { - display: 'inline-block', - width: 200, - }, - sectionHeader: { - fontWeight: 600, - display: 'block', - paddingBottom: 10, - marginBottom: 10, - borderBottom: '1px solid ' + colors.consoleStroke, - }, - logContainer: { - backgroundColor: 'black', - color: 'white', - fontFamily: - '"Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",' + - ' "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace', - fontSize: '13px', - maxHeight: 'calc(100vh - 120px)', - overflowY: 'auto', - width: '100%', - '& > div': { - overflowWrap: 'anywhere', - }, - }, - artifactContainer: { - backgroundColor: 'black', - color: 'white', - fontFamily: - '"Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",' + - ' "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace', - fontSize: '13px', - maxHeight: 'calc(100vh - 300px)', - width: '100%', - whiteSpace: 'break-spaces', - overflowWrap: 'anywhere', - overflow: 'auto', - }, - showMoreContainer: { - marginTop: 20, - textAlign: 'center', - }, - link: { - color: colors.secondary2.main, - '&:visited': { - color: colors.secondary2.main, - }, - '&:hover': { - color: colors.secondary2.main, - }, - '&:active': { - color: colors.secondary2.main, - }, - }, - checkStatusColumn: { - display: 'block', - width: 80, - marginTop: 10, - height: 30, - float: 'left', - }, - checkDescriptionColumn: { - display: 'inline-block', - }, - checkDetails: { - clear: 'both', - display: 'block', - color: colors.pgaiDarkGray, - }, - checkListItem: { - marginBottom: 10, - minHeight: 30, - }, - cfgListItem: { - marginBottom: 5, - }, - expansionPanel: { - marginTop: '5px!important', - borderRadius: '0px!important', - }, - expansionPanelSummary: { - display: 'inline-block', - padding: '5px', - paddingLeft: '12px', - minHeight: '30px', - lineHeight: '30px', - width: '100%', - '& .MuiExpansionPanelSummary-content': { - margin: '0px', - display: 'inline-block', - }, - '&.Mui-expanded': { - minHeight: '22px', - }, - '& .MuiExpansionPanelSummary-expandIcon': { - display: 'inline-block', - padding: '0px', - marginTop: '-1px', - }, - }, - expansionPanelDetails: { - padding: '12px', - paddingTop: '0px', - [theme.breakpoints.down('md')]: { - display: 'block', - }, - }, - intervalsRow: { - borderBottom: '1px solid ' + colors.consoleStroke, - width: '100%', - lineHeight: '22px', - '&:last-child': { - borderBottom: 'none', - }, - }, - intervalIcon: { - display: 'inline-block', - width: 25, - }, - intervalStarted: { - display: 'inline-block', - width: 200, - }, - intervalDuration: { - display: 'inline-block', - }, - intervalWarning: { - display: 'inline-block', - width: '100%', - }, - code: { - width: '100%', - 'margin-top': 0, - '& > div': { - paddingTop: 8, - padding: 8, - }, - 'background-color': 'rgb(246, 248, 250)', - '& > div > textarea': { - fontFamily: - '"Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",' + - ' "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace', - color: 'black', - fontSize: '12px', - }, - }, - button: { - marginTop: 5, - marginBottom: 10, - }, - bottomSpace: { - ...styles.bottomSpace, - }, - artifactRow: { - padding: '5px', - cursor: 'pointer', - [theme.breakpoints.down('sm')]: { - paddingLeft: '0px', - paddingRight: '0px', - paddingTop: '10px', - }, - }, - artifactName: { - display: 'inline-block', - width: '20%', - [theme.breakpoints.down('sm')]: { - display: 'block', - width: '100%', - marginBottom: '10px', - }, - '& svg': { - verticalAlign: 'middle', - }, - }, - artifactDescription: { - display: 'inline-block', - width: '40%', - [theme.breakpoints.down('sm')]: { - display: 'block', - width: '100%', - marginBottom: '10px', - }, - }, - artifactSize: { - display: 'inline-block', - width: '20%', - [theme.breakpoints.down('sm')]: { - display: 'block', - width: '100%', - marginBottom: '10px', - }, - }, - artifactAction: { - display: 'inline-block', - width: '20%', - textAlign: 'right', - '& button': { - marginBottom: '5px', - }, - [theme.breakpoints.down('sm')]: { - display: 'block', - width: '100%', - }, - }, - artifactExpansionPanel: { - padding: '0px!important', - boxShadow: 'none', - }, - artifactExpansionPanelSummary: { - display: 'none', - minHeight: '0px!important', - }, - artifactsExpansionPanelDetails: { - padding: '0px!important', - }, - summaryDivider: { - minHeight: '10px', - }, - rotate180Icon: { - '& svg': { - transform: 'rotate(180deg)', - }, - }, - rotate0Icon: { - '& svg': { - transform: 'rotate(0deg)', - }, - }, - }), - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/DbLabSessions/DbLabSessions.tsx b/ui/packages/platform/src/components/DbLabSessions/DbLabSessions.tsx deleted file mode 100644 index fdbbc1a6..00000000 --- a/ui/packages/platform/src/components/DbLabSessions/DbLabSessions.tsx +++ /dev/null @@ -1,404 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component, MouseEvent } from 'react' -import { RouteComponentProps } from 'react-router' -import { - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Button, -} from '@material-ui/core' -import { formatDistanceToNowStrict } from 'date-fns' - -import { HorizontalScrollContainer } from '@postgres.ai/shared/components/HorizontalScrollContainer' -import { StubContainer } from '@postgres.ai/shared/components/StubContainer' -import { PageSpinner } from '@postgres.ai/shared/components/PageSpinner' -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { icons } from '@postgres.ai/shared/styles/icons' -import { ClassesType, RefluxTypes } from '@postgres.ai/platform/src/components/types' - -import Store from '../../stores/store' -import Actions from '../../actions/actions' -import { ErrorWrapper } from 'components/Error/ErrorWrapper' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' -import { ProductCardWrapper } from 'components/ProductCard/ProductCardWrapper' -import { DbLabStatusWrapper } from 'components/DbLabStatus/DbLabStatusWrapper' - -import ConsolePageTitle from './../ConsolePageTitle' -import format from '../../utils/format' - -interface DbLabSessionsProps { - classes: ClassesType - org: string | number - orgId: number - history: RouteComponentProps['history'] -} - -interface DbLabSessionsState { - data: { - auth: { - token: string - } | null - dbLabSessions: { - isProcessing: boolean - isProcessed: boolean - isComplete: boolean - error: boolean - data: { - id: number - duration: number - started_at: string - tags: { - instance_id: string - branch: string - revision: string - launched_by: string - project_id: number - } - result: { - status: string - summary: { - checklist: { [list: string]: { string: string | boolean } } - elapsed: string - } - } - }[] - } | null - } | null -} - -const PAGE_SIZE = 20 - -class DbLabSessions extends Component { - unsubscribe: Function - componentDidMount() { - const that = this - const { orgId } = this.props - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - const auth = this.data && this.data.auth ? this.data.auth : null - const sessions = - this.data && this.data.dbLabSessions ? this.data.dbLabSessions : null - - if ( - auth && - auth.token && - !sessions?.isProcessing && - !sessions?.error && - !that.state - ) { - Actions.getDbLabSessions(auth.token, { orgId, limit: PAGE_SIZE }) - } - - that.setState({ data: this.data }) - }) - - const contentContainer = document.getElementById('content-container') - if (contentContainer) { - contentContainer.addEventListener('scroll', () => { - if ( - contentContainer.scrollTop >= - contentContainer.scrollHeight - contentContainer.offsetHeight - ) { - this.showMore() - } - }) - } - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - onSessionClick( - _: MouseEvent, - sessionId: string | number, - ) { - const { org } = this.props - - this.props.history.push('/' + org + '/observed-sessions/' + sessionId) - } - - formatStatus(status: string) { - const { classes } = this.props - let icon = null - let className = null - let label = status - if (status.length) { - label = status.charAt(0).toUpperCase() + status.slice(1) - } - - switch (status) { - case 'passed': - icon = icons.okIcon - className = classes.passedStatus - break - case 'failed': - icon = icons.failedIcon - className = classes.failedStatus - break - default: - icon = icons.processingIcon - className = classes.processingStatus - } - - return ( -
- - {icon} {label} - -
- ) - } - - showMore() { - const { orgId } = this.props - const auth = - this.state.data && this.state.data.auth ? this.state.data.auth : null - const sessions = - this.state.data && this.state.data.dbLabSessions - ? this.state.data.dbLabSessions - : null - let lastId = null - - if (sessions && sessions.data && sessions.data.length) { - lastId = sessions.data[sessions.data.length - 1].id - } - - if (auth && auth.token && !sessions?.isProcessing && lastId) { - Actions.getDbLabSessions(auth.token, { - orgId, - limit: PAGE_SIZE, - lastId, - }) - } - } - - render() { - const { classes, org } = this.props - - const breadcrumbs = ( - - ) - - const pageTitle = ( - - ) - - if (!this.state || !this.state.data) { - return ( -
- {breadcrumbs} - {pageTitle} - - -
- ) - } - - const sessionsStore = - (this.state.data && this.state.data.dbLabSessions) || null - const sessions = (sessionsStore && sessionsStore.data) || [] - - if (sessionsStore && sessionsStore.error) { - return ( -
- {breadcrumbs} - - {pageTitle} - - -
- ) - } - - if (!sessionsStore || !sessionsStore.data) { - return ( -
- {breadcrumbs} - - {pageTitle} - - -
- ) - } - - return ( -
- {breadcrumbs} - {pageTitle} - - {sessions && sessions.length > 0 ? ( -
- - - - Status - Session - - Project/Instance - - Commit - Checklist -   - - - - {sessions.map((s) => { - if (s) { - return ( - { - this.onSessionClick(event, s.id) - return false - }} - style={{ cursor: 'pointer' }} - > - - - - - #{s.id} - - - {s.tags && s.tags.project_id - ? s.tags.project_id - : '-'} - / - {s.tags && s.tags.instance_id - ? s.tags.instance_id - : '-'} - - - {icons.branch}  - {s.tags && s.tags.branch && s.tags.revision - ? s.tags.branch + '/' + s.tags.revision - : '-'} - - - {s.result && - s.result.summary && - s.result.summary.checklist ? ( -
- {Object.keys(s.result.summary.checklist).map( - function (key) { - return ( - - {s.result?.summary?.checklist && - s.result.summary.checklist[key] - ? icons.okLargeIcon - : icons.failedLargeIcon} -   - - ) - }, - )} -
- ) : ( - icons.processingLargeIcon - )} -
- -
- {s.duration > 0 || - (s.result && - s.result.summary && - s.result.summary.elapsed) ? ( - - {icons.timer}  - {s.result && - s.result.summary && - s.result.summary.elapsed - ? s.result.summary.elapsed - : format.formatSeconds(s.duration, 0, '')} - - ) : ( - '-' - )} -
-
- {icons.calendar} created  - {formatDistanceToNowStrict( - new Date(s.started_at), - { addSuffix: true }, - )} - {s.tags && s.tags.launched_by ? ( - by {s.tags.launched_by} - ) : ( - '' - )} -
-
-
- ) - } - - return null - })} -
-
-
-
- {sessionsStore && sessionsStore.isProcessing && ( - - )} - {sessionsStore && - !sessionsStore.isProcessing && - !sessionsStore.isComplete && ( - - )} -
-
- ) : ( - <> - {sessions && sessions.length === 0 && sessionsStore.isProcessed && ( - - - - )} - - )} -
- ) - } -} - -export default DbLabSessions diff --git a/ui/packages/platform/src/components/DbLabSessions/DbLabSessionsWrapper.tsx b/ui/packages/platform/src/components/DbLabSessions/DbLabSessionsWrapper.tsx deleted file mode 100644 index 9eae282a..00000000 --- a/ui/packages/platform/src/components/DbLabSessions/DbLabSessionsWrapper.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import { styles } from '@postgres.ai/shared/styles/styles' -import { RouteComponentProps } from 'react-router' -import DbLabSessions from 'components/DbLabSessions/DbLabSessions' - -interface DbLabSessionsProps { - org: string | number - orgId: number - history: RouteComponentProps['history'] -} - -export const DbLabSessionsWrapper = (props: DbLabSessionsProps) => { - const useStyles = makeStyles( - { - root: { - ...(styles.root as Object), - paddingBottom: '20px', - display: 'flex', - flexDirection: 'column', - }, - tableHead: { - ...(styles.tableHead as Object), - textAlign: 'left', - }, - tableCell: { - textAlign: 'left', - }, - showMoreContainer: { - marginTop: 20, - textAlign: 'center', - }, - }, - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/DbLabStatus/DbLabStatus.tsx b/ui/packages/platform/src/components/DbLabStatus/DbLabStatus.tsx deleted file mode 100644 index ac1d51e9..00000000 --- a/ui/packages/platform/src/components/DbLabStatus/DbLabStatus.tsx +++ /dev/null @@ -1,216 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import Brightness1Icon from '@material-ui/icons/Brightness1' -import Tooltip from '@material-ui/core/Tooltip' - -import { icons } from '@postgres.ai/shared/styles/icons' -import { ClassesType } from '@postgres.ai/platform/src/components/types' - -import Format from '../../utils/format' -import { Clone } from '@postgres.ai/shared/types/api/entities/clone' -import { DbLabStatusProps } from 'components/DbLabStatus/DbLabStatusWrapper' - -export interface DbLabStatusInstance { - state: { - status: { - code: string - message: string - } - } -} -interface DbLabStatusWithStylesProps extends DbLabStatusProps { - classes: ClassesType -} - -class DbLabStatus extends Component { - getCloneStatus = ( - clone: Clone, - onlyText: boolean, - showDescription: boolean, - ) => { - const { classes } = this.props - let className = classes?.cloneReadyStatus - - if (!clone.status) { - return null - } - - switch (clone.status.code) { - case 'OK': - className = classes?.cloneReadyStatus - break - case 'CREATING': - className = classes?.cloneCreatingStatus - break - case 'DELETING': - className = classes?.cloneDeletingStatus - break - case 'RESETTING': - className = classes?.cloneResettingStatus - break - case 'FATAL': - className = classes?.cloneFatalStatus - break - default: - break - } - - if (onlyText && showDescription) { - return ( - - - -   - {Format.formatStatus(clone.status.code)} - - - {clone.status.message && clone.status.message.length > 100 ? ( - - {Format.limitStr(clone.status.message, 100)} - - ) : ( - clone.status.message - )} - - - ) - } - - if (onlyText && !showDescription) { - return ( - - - - -   - {Format.formatStatus(clone.status.code)} - - ) - } - - return ( - - - - ) - } - - getInstanceStatus = (instance: DbLabStatusInstance, onlyText: boolean) => { - const { classes } = this.props - let className = classes?.instanceReadyStatus - - if (!instance.state) { - return null - } - - if (!instance.state.status) { - return null - } - switch (instance.state.status.code) { - case 'OK': - className = classes?.instanceReadyStatus - break - case 'WARNING': - className = classes?.instanceWarningStatus - break - case 'NO_RESPONSE': - className = classes?.instanceNoResponseStatus - break - default: - break - } - - if (onlyText) { - return ( - - - - -   - {Format.formatStatus(instance.state.status.code)} - - ) - } - - return ( - - - - ) - } - - getSessionStatus = (session: { status: string }) => { - const { classes } = this.props - let icon = null - let className = null - let label = session.status - if (session.status.length) { - label = session.status.charAt(0).toUpperCase() + session.status.slice(1) - } - - switch (session.status) { - case 'passed': - icon = icons.okIconWhite - className = classes?.sessionPassedStatus - break - case 'failed': - icon = icons.failedIconWhite - className = classes?.sessionFailedStatus - break - default: - icon = icons.processingIconWhite - className = classes?.sessionProcessingStatus - } - - return ( -
- - {icon} - {label} - -
- ) - } - - render() { - const { onlyText, showDescription, instance, clone, session } = this.props - - if (clone) { - return this.getCloneStatus( - clone, - onlyText as boolean, - showDescription as boolean, - ) - } - - if (instance) { - return this.getInstanceStatus(instance, onlyText as boolean) - } - - if (session) { - return this.getSessionStatus(session) - } - - return null - } -} - -export default DbLabStatus diff --git a/ui/packages/platform/src/components/DbLabStatus/DbLabStatusWrapper.tsx b/ui/packages/platform/src/components/DbLabStatus/DbLabStatusWrapper.tsx deleted file mode 100644 index b4c20e89..00000000 --- a/ui/packages/platform/src/components/DbLabStatus/DbLabStatusWrapper.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import DbLabStatus, { - DbLabStatusInstance, -} from 'components/DbLabStatus/DbLabStatus' -import { colors } from '@postgres.ai/shared/styles/colors' -import { Clone } from '@postgres.ai/shared/types/api/entities/clone' - -export interface DbLabStatusProps { - session?: { status: string } - onlyText?: boolean - showDescription?: boolean - instance?: DbLabStatusInstance - clone?: Clone -} - -export const DbLabStatusWrapper = (props: DbLabStatusProps) => { - const useStyles = makeStyles( - { - cloneReadyStatus: { - color: colors.state.ok, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - cloneCreatingStatus: { - color: colors.state.processing, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - cloneResettingStatus: { - color: colors.state.processing, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - cloneDeletingStatus: { - color: colors.state.warning, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - cloneFatalStatus: { - color: colors.state.error, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - instanceReadyStatus: { - color: colors.state.ok, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - instanceWarningStatus: { - color: colors.state.warning, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - instanceNoResponseStatus: { - color: colors.state.error, - fontSize: '1.1em', - verticalAlign: 'middle', - '& svg': { - marginTop: '-3px', - }, - }, - toolTip: { - fontSize: '10px!important', - }, - sessionPassedStatus: { - display: 'inline-block', - border: '1px solid ' + colors.state.ok, - fontSize: '12px', - color: '#FFFFFF', - backgroundColor: colors.state.ok, - padding: '3px', - paddingLeft: '5px', - paddingRight: '5px', - borderRadius: 3, - lineHeight: '14px', - '& svg': { - width: 10, - height: 10, - marginBottom: '-1px', - marginRight: '5px', - }, - }, - sessionFailedStatus: { - display: 'inline-block', - border: '1px solid ' + colors.state.error, - fontSize: '12px', - color: '#FFFFFF', - backgroundColor: colors.state.error, - padding: '3px', - paddingLeft: '5px', - paddingRight: '5px', - borderRadius: 3, - lineHeight: '14px', - '& svg': { - width: 10, - height: 10, - marginBottom: '-1px', - marginRight: '5px', - }, - }, - sessionProcessingStatus: { - display: 'inline-block', - border: '1px solid ' + colors.state.processing, - fontSize: '12px', - color: '#FFFFFF', - backgroundColor: colors.state.processing, - padding: '3px', - paddingLeft: '5px', - paddingRight: '5px', - borderRadius: 3, - lineHeight: '14px', - '& svg': { - width: 10, - height: 10, - marginBottom: '-1px', - marginRight: '5px', - }, - }, - }, - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/DisplayToken/DisplayToken.tsx b/ui/packages/platform/src/components/DisplayToken/DisplayToken.tsx deleted file mode 100644 index 8c08e09f..00000000 --- a/ui/packages/platform/src/components/DisplayToken/DisplayToken.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { InputAdornment } from '@material-ui/core' -import { IconButton, TextField } from '@material-ui/core' - -import { ClassesType, RefluxTypes } from '@postgres.ai/platform/src/components/types' -import { styles } from '@postgres.ai/shared/styles/styles' -import { icons } from '@postgres.ai/shared/styles/icons' - -import Store from '../../stores/store' -import Actions from '../../actions/actions' - -interface DisplayTokenProps { - classes: ClassesType -} - -interface DisplayTokenState { - data: { - tokenRequest: { - isProcessed: boolean - error: boolean - data: { - name: string - expires_at: string - token: string - } - } | null - } | null -} - -class DisplayToken extends Component { - unsubscribe: Function - componentDidMount() { - const that = this - - document.getElementsByTagName('html')[0].style.overflow = 'hidden' - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - that.setState({ data: this.data }) - }) - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - copyToken = () => { - const copyText = document.getElementById( - 'generatedToken', - ) as HTMLInputElement - - if (copyText) { - copyText.select() - copyText.setSelectionRange(0, 99999) - document.execCommand('copy') - } - } - - render() { - const { classes } = this.props - const tokenRequest = - this.state && this.state.data && this.state.data.tokenRequest - ? this.state.data.tokenRequest - : null - let tokenDisplay = null - - if ( - tokenRequest && - tokenRequest.isProcessed && - !tokenRequest.error && - tokenRequest.data && - tokenRequest.data.name && - tokenRequest.data.expires_at && - tokenRequest.data.token - ) { - tokenDisplay = ( - - - {icons.copyIcon} - - - ), - }} - InputLabelProps={{ - shrink: true, - style: styles.inputFieldLabel, - }} - FormHelperTextProps={{ - style: styles.inputFieldHelper, - }} - helperText="Make sure you have saved token - you will not be able to access it again" - /> - ) - } - - return
{tokenDisplay}
- } -} - -export default DisplayToken diff --git a/ui/packages/platform/src/components/DisplayToken/DisplayTokenWrapper.tsx b/ui/packages/platform/src/components/DisplayToken/DisplayTokenWrapper.tsx deleted file mode 100644 index 87328243..00000000 --- a/ui/packages/platform/src/components/DisplayToken/DisplayTokenWrapper.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import DisplayToken from 'components/DisplayToken/DisplayToken' -import { styles } from '@postgres.ai/shared/styles/styles' - -export const DisplayTokenWrapper = () => { - const useStyles = makeStyles( - { - textField: { - ...styles.inputField, - marginTop: 0, - }, - input: { - '&.MuiOutlinedInput-adornedEnd': { - padding: 0, - }, - }, - inputElement: { - marginRight: '-8px', - }, - inputAdornment: { - margin: 0, - }, - inputButton: { - padding: '9px 10px', - }, - }, - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/Error/Error.tsx b/ui/packages/platform/src/components/Error/Error.tsx deleted file mode 100644 index 23815d41..00000000 --- a/ui/packages/platform/src/components/Error/Error.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { Component } from 'react' -import { Paper, Typography } from '@material-ui/core' - -import { ClassesType } from '@postgres.ai/platform/src/components/types' -import { ErrorProps } from 'components/Error/ErrorWrapper' - -interface ErrorWithStylesProps extends ErrorProps { - classes: ClassesType -} - -class Error extends Component { - render() { - const { classes } = this.props - - return ( -
- - - ERROR {this.props.code ? this.props.code : null} - - -
- - - {this.props.message - ? this.props.message - : 'Unknown error occurred. Please try again later.'} - -
-
- ) - } -} - -export default Error diff --git a/ui/packages/platform/src/components/Error/ErrorWrapper.tsx b/ui/packages/platform/src/components/Error/ErrorWrapper.tsx deleted file mode 100644 index 6e840000..00000000 --- a/ui/packages/platform/src/components/Error/ErrorWrapper.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { makeStyles } from '@material-ui/core' -import Error from 'components/Error/Error' - -export interface ErrorProps { - code?: number - message?: string -} - -export const ErrorWrapper = (props: ErrorProps) => { - const useStyles = makeStyles( - (theme) => ({ - paper: theme.mixins.gutters({ - paddingTop: 16, - paddingBottom: 16, - marginTop: 0, - }), - errorHead: { - color: '#c00111', - fontWeight: 'bold', - fontSize: '16px', - }, - errorText: { - color: '#c00111', - }, - }), - { index: 1 }, - ) - - const classes = useStyles() - - return -} diff --git a/ui/packages/platform/src/components/ExplainVisualization/ExplainVisualization.tsx b/ui/packages/platform/src/components/ExplainVisualization/ExplainVisualization.tsx deleted file mode 100644 index b001c3a5..00000000 --- a/ui/packages/platform/src/components/ExplainVisualization/ExplainVisualization.tsx +++ /dev/null @@ -1,295 +0,0 @@ -/*-------------------------------------------------------------------------- - * Copyright (c) 2019-2021, Postgres.ai, Nikolay Samokhvalov nik@postgres.ai - * All Rights Reserved. Proprietary and confidential. - * Unauthorized copying of this file, via any medium is strictly prohibited - *-------------------------------------------------------------------------- - */ - -import { - AppBar, - Dialog, - Button, - IconButton, - TextField, - Toolbar, - Typography, -} from '@material-ui/core' -import CloseIcon from '@material-ui/icons/Close' -import React, { Component } from 'react' -import Store from '../../stores/store' - -import { styles } from '@postgres.ai/shared/styles/styles' -import { Spinner } from '@postgres.ai/shared/components/Spinner' -import { ClassesType, RefluxTypes } from '@postgres.ai/platform/src/components/types' - -import Actions from '../../actions/actions' -import { ConsoleBreadcrumbsWrapper } from 'components/ConsoleBreadcrumbs/ConsoleBreadcrumbsWrapper' - -import ConsolePageTitle from '../ConsolePageTitle' -import FlameGraph from '../FlameGraph' -import explainSamples from '../../assets/explainSamples' -import { visualizeTypes } from '../../assets/visualizeTypes' - -interface ExplainVisualizationProps { - classes: ClassesType -} - -interface ExplainVisualizationState { - plan: string | null - showFlameGraph: boolean - data: { - externalVisualization: { - url: string - type: string - isProcessing: boolean - } - } | null -} - -class ExplainVisualization extends Component< - ExplainVisualizationProps, - ExplainVisualizationState -> { - unsubscribe: Function - componentDidMount() { - const that = this - - this.unsubscribe = (Store.listen as RefluxTypes["listen"]) (function () { - that.setState({ data: this.data }) - }) - - Actions.refresh() - } - - componentWillUnmount() { - this.unsubscribe() - } - - handleChange = (event: React.ChangeEvent) => { - this.setState({ - plan: event.target.value, - }) - } - - insertSample = () => { - this.setState({ plan: explainSamples[0].value }) - } - - getExternalVisualization = () => { - return this.state && - this.state.data && - this.state.data.externalVisualization - ? this.state.data.externalVisualization - : null - } - - showExternalVisualization = (type: string) => { - const { plan } = this.state - - if (!plan) { - return - } - - Actions.getExternalVisualizationData(type, plan, '') - } - - closeExternalVisualization = () => { - Actions.closeExternalVisualization() - this.setState({ - showFlameGraph: false, - }) - } - - handleExternalVisualizationClick = (type: string) => { - return () => { - this.showExternalVisualization(type) - } - } - - showFlameGraphVisualization = () => { - this.setState({ - showFlameGraph: true, - }) - } - - render() { - const { classes } = this.props - - const breadcrumbs = ( - - ) - - const pageTitle = ( - -

- Visualize explain plans gathered manually. Plans gathered with Joe - will be automatically saved in Joe history and can be visualized - in command page without copy-pasting of a plan. -

-

Currently only JSON format is supported.

-

- For better results, use: explain (analyze, costs, verbose, - buffers, format json). -

- - } - /> - ) - - if (!this.state || !this.state.data) { - return ( -
- {breadcrumbs} - - {pageTitle} - - -
- ) - } - - const { plan, showFlameGraph } = this.state - - const externalVisualization = this.getExternalVisualization() - - const disableVizButtons = - !plan || showFlameGraph || externalVisualization?.isProcessing - const openVizDialog = - showFlameGraph || - (externalVisualization?.url - ? externalVisualization.url?.length > 0 - : false) - - return ( -
- {breadcrumbs} - - {pageTitle} - -
- -
- - - -
- - - - - -
- -
- - - - - Visualization - - - - - - - - {showFlameGraph && ( -
-

Flame Graph (buffers):

- - -

Flame Graph (timing):

- -
- )} - - {externalVisualization?.url && ( -