Compare commits
1 Commits
2.18.3
...
fix/dev-bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96a626324c |
@@ -1,44 +0,0 @@
|
||||
version: "2"
|
||||
checks:
|
||||
argument-count:
|
||||
enabled: false
|
||||
complex-logic:
|
||||
enabled: false
|
||||
file-lines:
|
||||
enabled: false
|
||||
method-complexity:
|
||||
enabled: false
|
||||
method-count:
|
||||
enabled: false
|
||||
method-lines:
|
||||
enabled: false
|
||||
nested-control-flow:
|
||||
enabled: false
|
||||
return-statements:
|
||||
enabled: false
|
||||
similar-code:
|
||||
enabled: false
|
||||
identical-code:
|
||||
enabled: false
|
||||
plugins:
|
||||
gofmt:
|
||||
enabled: true
|
||||
eslint:
|
||||
enabled: true
|
||||
channel: "eslint-5"
|
||||
config:
|
||||
config: .eslintrc.yml
|
||||
exclude_patterns:
|
||||
- assets/
|
||||
- build/
|
||||
- dist/
|
||||
- distribution/
|
||||
- node_modules
|
||||
- test/
|
||||
- webpack/
|
||||
- gruntfile.js
|
||||
- webpack.config.js
|
||||
- api/
|
||||
- "!app/kubernetes/**"
|
||||
- .github/
|
||||
- .tmp/
|
||||
43
.github/workflows/nightly-security-scan.yml
vendored
43
.github/workflows/nightly-security-scan.yml
vendored
@@ -1,16 +1,16 @@
|
||||
name: Nightly Code Security Scan
|
||||
|
||||
on:
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
client-dependencies:
|
||||
name: Client dependency check
|
||||
runs-on: ubuntu-latest
|
||||
if: >- # only run for develop branch
|
||||
github.ref == 'refs/heads/develop'
|
||||
github.ref == 'refs/heads/develop'
|
||||
outputs:
|
||||
js: ${{ steps.set-matrix.outputs.js_result }}
|
||||
steps:
|
||||
@@ -24,14 +24,14 @@ jobs:
|
||||
with:
|
||||
json: true
|
||||
|
||||
- name: Upload js security scan result as artifact
|
||||
- name: Upload js security scan result as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: js-security-scan-develop-result
|
||||
path: snyk.json
|
||||
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 summary -report-type=snyk -path="/data/snyk.json" -output-type=table -export -export-filename="/data/js-result")
|
||||
|
||||
- name: Upload js result html file
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
- name: Analyse the js result
|
||||
id: set-matrix
|
||||
run: |
|
||||
run: |
|
||||
result=$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 summary -report-type=snyk -path="/data/snyk.json" -output-type=matrix)
|
||||
echo "::set-output name=js_result::${result}"
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
name: Server dependency check
|
||||
runs-on: ubuntu-latest
|
||||
if: >- # only run for develop branch
|
||||
github.ref == 'refs/heads/develop'
|
||||
github.ref == 'refs/heads/develop'
|
||||
outputs:
|
||||
go: ${{ steps.set-matrix.outputs.go_result }}
|
||||
steps:
|
||||
@@ -77,8 +77,8 @@ jobs:
|
||||
name: go-security-scan-develop-result
|
||||
path: snyk.json
|
||||
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 summary -report-type=snyk -path="/data/snyk.json" -output-type=table -export -export-filename="/data/go-result")
|
||||
|
||||
- name: Upload go result html file
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
|
||||
- name: Analyse the go result
|
||||
id: set-matrix
|
||||
run: |
|
||||
run: |
|
||||
result=$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 summary -report-type=snyk -path="/data/snyk.json" -output-type=matrix)
|
||||
echo "::set-output name=go_result::${result}"
|
||||
|
||||
@@ -114,8 +114,11 @@ jobs:
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Install packages and build
|
||||
run: yarn install && yarn build
|
||||
- name: Install packages
|
||||
run: yarn --frozen-lockfile
|
||||
|
||||
- name: build
|
||||
run: make build
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
@@ -134,9 +137,9 @@ jobs:
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: docker://docker.io/aquasec/trivy:latest
|
||||
continue-on-error: true
|
||||
continue-on-error: true
|
||||
with:
|
||||
args: image --ignore-unfixed=true --vuln-type="os,library" --exit-code=1 --format="json" --output="image-trivy.json" --no-progress trivy-portainer:${{ github.sha }}
|
||||
args: image --ignore-unfixed=true --vuln-type="os,library" --exit-code=1 --format="json" --output="image-trivy.json" --no-progress trivy-portainer:${{ github.sha }}
|
||||
|
||||
- name: Upload image security scan result as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -144,8 +147,8 @@ jobs:
|
||||
name: image-security-scan-develop-result
|
||||
path: image-trivy.json
|
||||
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 summary -report-type=trivy -path="/data/image-trivy.json" -output-type=table -export -export-filename="/data/image-result")
|
||||
|
||||
- name: Upload go result html file
|
||||
@@ -156,7 +159,7 @@ jobs:
|
||||
|
||||
- name: Analyse the trivy result
|
||||
id: set-matrix
|
||||
run: |
|
||||
run: |
|
||||
result=$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 summary -report-type=trivy -path="/data/image-trivy.json" -output-type=matrix)
|
||||
echo "::set-output name=image_result::${result}"
|
||||
|
||||
@@ -167,7 +170,7 @@ jobs:
|
||||
if: >-
|
||||
github.ref == 'refs/heads/develop'
|
||||
strategy:
|
||||
matrix:
|
||||
matrix:
|
||||
js: ${{fromJson(needs.client-dependencies.outputs.js)}}
|
||||
go: ${{fromJson(needs.server-dependencies.outputs.go)}}
|
||||
image: ${{fromJson(needs.image-vulnerability.outputs.image)}}
|
||||
@@ -182,7 +185,7 @@ jobs:
|
||||
echo ${{ matrix.image.summary }}
|
||||
|
||||
- name: Send Slack message
|
||||
if: >-
|
||||
if: >-
|
||||
matrix.js.status == 'failure' ||
|
||||
matrix.go.status == 'failure' ||
|
||||
matrix.image.status == 'failure'
|
||||
|
||||
36
.github/workflows/pr-security.yml
vendored
36
.github/workflows/pr-security.yml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
- 'build/linux/Dockerfile'
|
||||
- 'build/linux/alpine.Dockerfile'
|
||||
- 'build/windows/Dockerfile'
|
||||
|
||||
|
||||
jobs:
|
||||
client-dependencies:
|
||||
name: Client dependency check
|
||||
@@ -51,8 +51,8 @@ jobs:
|
||||
echo "null" > ./js-snyk-develop.json
|
||||
fi
|
||||
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 diff -report-type=snyk -path="/data/js-snyk-feature.json" -compare-to="/data/js-snyk-develop.json" -output-type=table -export -export-filename="/data/js-result")
|
||||
|
||||
- name: Upload js result html file
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- name: Analyse the js diff result
|
||||
id: set-diff-matrix
|
||||
run: |
|
||||
run: |
|
||||
result=$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 diff -report-type=snyk -path="/data/js-snyk-feature.json" -compare-to="./data/js-snyk-develop.json" -output-type=matrix)
|
||||
echo "::set-output name=js_diff_result::${result}"
|
||||
|
||||
@@ -111,8 +111,8 @@ jobs:
|
||||
echo "null" > ./go-snyk-develop.json
|
||||
fi
|
||||
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 diff -report-type=snyk -path="/data/go-snyk-feature.json" -compare-to="/data/go-snyk-develop.json" -output-type=table -export -export-filename="/data/go-result")
|
||||
|
||||
- name: Upload go result html file
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
|
||||
- name: Analyse the go diff result
|
||||
id: set-diff-matrix
|
||||
run: |
|
||||
run: |
|
||||
result=$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 diff -report-type=snyk -path="/data/go-snyk-feature.json" -compare-to="/data/go-snyk-develop.json" -output-type=matrix)
|
||||
echo "::set-output name=go_diff_result::${result}"
|
||||
|
||||
@@ -149,8 +149,11 @@ jobs:
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Install packages and build
|
||||
run: yarn install && yarn build
|
||||
- name: Install packages
|
||||
run: yarn --frozen-lockfile
|
||||
|
||||
- name: build
|
||||
run: make build
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
@@ -169,9 +172,9 @@ jobs:
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: docker://docker.io/aquasec/trivy:latest
|
||||
continue-on-error: true
|
||||
continue-on-error: true
|
||||
with:
|
||||
args: image --ignore-unfixed=true --vuln-type="os,library" --exit-code=1 --format="json" --output="image-trivy.json" --no-progress trivy-portainer:${{ github.sha }}
|
||||
args: image --ignore-unfixed=true --vuln-type="os,library" --exit-code=1 --format="json" --output="image-trivy.json" --no-progress trivy-portainer:${{ github.sha }}
|
||||
|
||||
- name: Upload image security scan result as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -191,8 +194,8 @@ jobs:
|
||||
echo "null" > ./image-trivy-develop.json
|
||||
fi
|
||||
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
- name: Export scan result to html file
|
||||
run: |
|
||||
$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 diff -report-type=trivy -path="/data/image-trivy-feature.json" -compare-to="/data/image-trivy-develop.json" -output-type=table -export -export-filename="/data/image-result")
|
||||
|
||||
- name: Upload image result html file
|
||||
@@ -203,7 +206,7 @@ jobs:
|
||||
|
||||
- name: Analyse the image diff result
|
||||
id: set-diff-matrix
|
||||
run: |
|
||||
run: |
|
||||
result=$(docker run --rm -v ${{ github.workspace }}:/data oscarzhou/scan-report:0.1.8 diff -report-type=trivy -path="/data/image-trivy-feature.json" -compare-to="./data/image-trivy-develop.json" -output-type=matrix)
|
||||
echo "::set-output name=image_diff_result::${result}"
|
||||
|
||||
@@ -215,17 +218,16 @@ jobs:
|
||||
github.event.pull_request &&
|
||||
github.event.review.body == '/scan'
|
||||
strategy:
|
||||
matrix:
|
||||
matrix:
|
||||
jsdiff: ${{fromJson(needs.client-dependencies.outputs.jsdiff)}}
|
||||
godiff: ${{fromJson(needs.server-dependencies.outputs.godiff)}}
|
||||
imagediff: ${{fromJson(needs.image-vulnerability.outputs.imagediff)}}
|
||||
steps:
|
||||
|
||||
- name: Check job status of diff result
|
||||
if: >-
|
||||
matrix.jsdiff.status == 'failure' ||
|
||||
matrix.godiff.status == 'failure' ||
|
||||
matrix.imagediff.status == 'failure'
|
||||
matrix.imagediff.status == 'failure'
|
||||
run: |
|
||||
echo ${{ matrix.jsdiff.status }}
|
||||
echo ${{ matrix.godiff.status }}
|
||||
|
||||
4
.github/workflows/test.yaml
vendored
4
.github/workflows/test.yaml
vendored
@@ -8,12 +8,12 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: '18'
|
||||
cache: 'yarn'
|
||||
- run: yarn --frozen-lockfile
|
||||
|
||||
- name: Run tests
|
||||
run: yarn test:client
|
||||
run: yarn jest --maxWorkers=2
|
||||
# test-server:
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
|
||||
29
.github/workflows/validate-openapi-spec.yaml
vendored
Normal file
29
.github/workflows/validate-openapi-spec.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Validate OpenAPI specs
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- 'release/*'
|
||||
|
||||
jobs:
|
||||
openapi-spec:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.18'
|
||||
|
||||
- name: Download golang modules
|
||||
run: cd ./api && go get -t -v -d ./...
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
cache: 'yarn'
|
||||
- run: yarn --frozen-lockfile
|
||||
|
||||
- name: Validate OpenAPI Spec
|
||||
run: make docs-validate
|
||||
53
.github/workflows/validate-openapi-spec.yml
vendored
53
.github/workflows/validate-openapi-spec.yml
vendored
@@ -1,53 +0,0 @@
|
||||
name: Validate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- 'release/*'
|
||||
|
||||
jobs:
|
||||
openapi-spec:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Node v14
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
# https://github.com/actions/cache/blob/main/examples.md#node---yarn
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Setup Go v1.17.3
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17.3'
|
||||
|
||||
- name: Prebuild docs
|
||||
run: yarn prebuild:docs
|
||||
|
||||
- name: Build OpenAPI 2.0 Spec
|
||||
run: yarn build:docs
|
||||
|
||||
# Install dependencies globally to bypass installing all frontend deps
|
||||
- name: Install swagger2openapi and swagger-cli
|
||||
run: yarn global add swagger2openapi @apidevtools/swagger-cli
|
||||
|
||||
# OpenAPI2.0 does not support multiple body params (which we utilise in some of our handlers).
|
||||
# OAS3.0 however does support multiple body params - hence its best to convert the generated OAS 2.0
|
||||
# to OAS 3.0 and validate the output of generated OAS 3.0 instead.
|
||||
- name: Convert OpenAPI 2.0 to OpenAPI 3.0 and validate spec
|
||||
run: yarn validate:docs
|
||||
122
Makefile
Normal file
122
Makefile
Normal file
@@ -0,0 +1,122 @@
|
||||
# See: https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63
|
||||
# For a list of valid GOOS and GOARCH values
|
||||
# Note: these can be overriden on the command line e.g. `make PLATFORM=<platform> ARCH=<arch>`
|
||||
PLATFORM=$(shell go env GOOS)
|
||||
ARCH=$(shell go env GOARCH)
|
||||
|
||||
TAG=latest
|
||||
SWAG_VERSION=v1.8.11
|
||||
|
||||
# build target, can be one of "production", "testing", "development"
|
||||
ENV=development
|
||||
WEBPACK_CONFIG=webpack/webpack.$(ENV).js
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
.PHONY: help build-storybook build-client devops download-binaries tidy clean client-deps
|
||||
|
||||
##@ Building
|
||||
|
||||
init-dist:
|
||||
@mkdir -p dist
|
||||
|
||||
build-storybook:
|
||||
yarn storybook:build
|
||||
|
||||
build: build-server build-client ## Build the server and client
|
||||
|
||||
build-client: init-dist client-deps ## Build the client
|
||||
export NODE_ENV=$(ENV) && yarn build --config $(WEBPACK_CONFIG)
|
||||
|
||||
build-server: init-dist ## Build the server binary
|
||||
./build/build_binary.sh "$(PLATFORM)" "$(ARCH)"
|
||||
|
||||
build-image: build ## Build the Portainer image
|
||||
docker buildx build --load -t portainerci/portainer:$(TAG) -f build/linux/Dockerfile .
|
||||
|
||||
devops: clean init-dist download-binaries build-client ## Build the server binary for CI
|
||||
echo "Building the devops binary..."
|
||||
@./build/build_binary_azuredevops.sh "$(PLATFORM)" "$(ARCH)"
|
||||
|
||||
##@ Dependencies
|
||||
|
||||
download-binaries: ## Download dependant binaries
|
||||
@./build/download_binaries.sh $(PLATFORM) $(ARCH)
|
||||
|
||||
tidy: ## Tidy up the go.mod file
|
||||
cd api && go mod tidy
|
||||
|
||||
client-deps: ## Install client dependencies
|
||||
yarn
|
||||
|
||||
##@ Cleanup
|
||||
|
||||
clean: ## Remove all build and download artifacts
|
||||
@echo "Clearing the dist directory..."
|
||||
@rm -rf dist/*
|
||||
|
||||
##@ Testing
|
||||
|
||||
test-client: ## Run client tests
|
||||
yarn test
|
||||
|
||||
test-server: ## Run server tests
|
||||
cd api && go test -v ./...
|
||||
|
||||
test: test-client test-server ## Run all tests
|
||||
|
||||
##@ Dev
|
||||
|
||||
dev-client: ## Run the client in development mode
|
||||
yarn dev
|
||||
|
||||
dev-server: build-image ## Run the server in development mode
|
||||
@./dev/run_container.sh
|
||||
|
||||
|
||||
##@ Format
|
||||
|
||||
format-client: ## Format client code
|
||||
yarn format
|
||||
|
||||
format-server: ## Format server code
|
||||
cd api && go fmt ./...
|
||||
|
||||
format: format-client format-server ## Format all code
|
||||
|
||||
##@ Lint
|
||||
|
||||
lint: lint-client lint-server ## Lint all code
|
||||
|
||||
lint-client: ## Lint client code
|
||||
yarn lint
|
||||
|
||||
lint-server: ## Lint server code
|
||||
cd api && go vet ./...
|
||||
|
||||
##@ Extension
|
||||
|
||||
dev-extension: build-server build-client ## Run the extension in development mode
|
||||
make local -f build/docker-extension/Makefile
|
||||
|
||||
##@ Docs
|
||||
|
||||
docs-deps: ## Install docs dependencies
|
||||
go install github.com/swaggo/swag/cmd/swag@$(SWAG_VERSION)
|
||||
|
||||
docs-build: docs-deps ## Build docs
|
||||
cd api && swag init -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 --markdownFiles ./
|
||||
|
||||
docs-validate: docs-build ## Validate docs
|
||||
yarn swagger2openapi --warnOnly api/docs/swagger.yaml -o api/docs/openapi.yaml
|
||||
yarn swagger-cli validate api/docs/openapi.yaml
|
||||
|
||||
docs-clean: ## Clean docs
|
||||
rm -rf api/docs
|
||||
|
||||
docs-validate-clean: docs-validate docs-clean ## Validate and clean docs
|
||||
|
||||
##@ Helpers
|
||||
|
||||
help: ## Display this help
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
@@ -20,8 +20,6 @@ func CreateTLSConfiguration() *tls.Config {
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,6 +945,6 @@
|
||||
}
|
||||
],
|
||||
"version": {
|
||||
"VERSION": "{\"SchemaVersion\":\"2.18.3\",\"MigratorCount\":0,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
|
||||
"VERSION": "{\"SchemaVersion\":\"2.18.2\",\"MigratorCount\":0,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ type Handler struct {
|
||||
}
|
||||
|
||||
// @title PortainerCE API
|
||||
// @version 2.18.3
|
||||
// @version 2.18.2
|
||||
// @description.markdown api-description.md
|
||||
// @termsOfService
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func (payload *teamCreatePayload) Validate(r *http.Request) error {
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 409 "Team already exists"
|
||||
// @failure 500 "Server error"
|
||||
// @router /teams [post]
|
||||
// @router /team [post]
|
||||
func (handler *Handler) teamCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
var payload teamCreatePayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
|
||||
@@ -35,7 +35,7 @@ func (payload *teamUpdatePayload) Validate(r *http.Request) error {
|
||||
// @failure 403 "Permission denied"
|
||||
// @failure 404 "Team not found"
|
||||
// @failure 500 "Server error"
|
||||
// @router /teams/{id} [put]
|
||||
// @router /team/{id} [put]
|
||||
func (handler *Handler) teamUpdate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
teamID, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||
if err != nil {
|
||||
|
||||
@@ -1514,7 +1514,7 @@ type (
|
||||
|
||||
const (
|
||||
// APIVersion is the version number of the Portainer API
|
||||
APIVersion = "2.18.3"
|
||||
APIVersion = "2.18.2"
|
||||
// Edition is what this edition of Portainer is called
|
||||
Edition = PortainerCE
|
||||
// ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="auto" height="auto" viewBox="0 0 8 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 14.3223V3.49792C1 2.11836 2.11832 1 3.49792 1C4.87753 1 5.99585 2.11836 5.99585 3.49792C5.99585 4.87749 4.87753 5.99585 3.49792 5.99585L3.91425 5.99609C5.52374 5.99609 6.82849 7.30084 6.82849 8.91034C6.82849 10.5198 5.52374 11.8246 3.91425 11.8246C2.30475 11.8246 1 10.5198 1 8.91034" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 493 B |
@@ -15,7 +15,6 @@ import { HealthStatus } from '@/react/docker/containers/ItemView/HealthStatus';
|
||||
import { GpusList } from '@/react/docker/host/SetupView/GpusList';
|
||||
import { GpusInsights } from '@/react/docker/host/SetupView/GpusInsights';
|
||||
import { InsightsBox } from '@/react/components/InsightsBox';
|
||||
import { BetaAlert } from '@/react/portainer/environments/update-schedules/common/BetaAlert';
|
||||
|
||||
export const componentsModule = angular
|
||||
.module('portainer.docker.react.components', [])
|
||||
@@ -66,5 +65,4 @@ export const componentsModule = angular
|
||||
'className',
|
||||
])
|
||||
)
|
||||
.component('betaAlert', r2a(BetaAlert, ['className', 'message', 'isHtml']))
|
||||
.component('gpusInsights', r2a(GpusInsights, [])).name;
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
<code-editor
|
||||
identifier="image-build-editor"
|
||||
placeholder="Define or paste the content of your Dockerfile here"
|
||||
docker-file="true"
|
||||
yml="false"
|
||||
on-change="(editorUpdate)"
|
||||
></code-editor>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<page-header title="'Helm'" breadcrumbs="['Charts']" reload="true"></page-header>
|
||||
|
||||
<information-panel title-text="Information" ng-if="!$ctrl.state.chart">
|
||||
<beta-alert
|
||||
is-html="true"
|
||||
message="'Beta feature - initial version of Helm charts functionality, for more information see this <a href=\'https://www.portainer.io/blog/portainer-now-with-helm-support\' target=\'_blank\' class=\'hyperlink\'>blog post</a>.'"
|
||||
></beta-alert>
|
||||
<span class="small text-muted">
|
||||
<p class="inline-flex flex-row items-center">
|
||||
<pr-icon icon="'info'" class="vertical-center mr-1" mode="'primary'"></pr-icon>
|
||||
This is a first version for Helm charts, for more information see this <a
|
||||
class="hyperlink"
|
||||
href="https://www.portainer.io/blog/portainer-now-with-helm-support"
|
||||
target="_blank"
|
||||
>blog post</a
|
||||
>.</p
|
||||
>
|
||||
<p ng-if="$ctrl.state.globalRepository === ''" class="inline-flex items-center">
|
||||
<pr-icon icon="'info'"></pr-icon>
|
||||
<span>The Global Helm Repository is not configured.</span>
|
||||
@@ -18,7 +23,7 @@
|
||||
<!-- helmchart-form -->
|
||||
<div class="col-sm-12" ng-if="$ctrl.state.chart">
|
||||
<rd-widget>
|
||||
<div class="toolBarTitle vertical-center px-5 pt-5 text-[16px] font-medium">
|
||||
<div class="toolBarTitle vertical-center text-muted px-5 pt-5">
|
||||
<fallback-image src="$ctrl.state.chart.icon" fallback-icon="'svg-helm'" class-name="'h-8 w-8'" size="'lg'"></fallback-image>
|
||||
{{ $ctrl.state.chart.name }}
|
||||
</div>
|
||||
|
||||
@@ -20,11 +20,10 @@
|
||||
Release
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolBarTitle vertical-center !gap-0 px-5">
|
||||
<beta-alert
|
||||
is-html="true"
|
||||
message="'Beta feature - initial version of Helm charts functionality, for more information see this <a href=\'https://www.portainer.io/blog/portainer-now-with-helm-support\' target=\'_blank\' class=\'hyperlink\'>blog post</a>.'"
|
||||
></beta-alert>
|
||||
<div class="toolBarTitle text-muted small vertical-center !gap-0 px-5">
|
||||
<pr-icon icon="'info'" mode="'primary'" class-name="'!mr-1'" class="vertical-center"></pr-icon>
|
||||
This is a first version for Helm charts, for more information see this
|
||||
<a href="https://www.portainer.io/blog/portainer-now-with-helm-support" target="_blank" class="hyperlink">blog post</a>.
|
||||
</div>
|
||||
<rd-widget-body>
|
||||
<table class="table">
|
||||
|
||||
@@ -80,8 +80,6 @@
|
||||
ng-if="ctrl.state.BuildMethod === ctrl.BuildMethods.GIT"
|
||||
value="ctrl.formValues"
|
||||
on-change="(ctrl.onChangeFormValues)"
|
||||
environment-type="KUBERNETES"
|
||||
is-force-pull-visible="false"
|
||||
is-additional-files-field-visible="true"
|
||||
is-auth-explanation-visible="true"
|
||||
deploy-method="{{ ctrl.state.DeployType === ctrl.ManifestDeployTypes.COMPOSE ? 'compose' : 'manifest' }}"
|
||||
@@ -184,7 +182,7 @@
|
||||
<form class="form-horizontal mt-3">
|
||||
<div class="form-group" ng-if="ctrl.state.activeTab === 1">
|
||||
<div class="col-sm-12">
|
||||
<code-editor identifier="kubernetes-deploy-logs" yml="true" read-only="true" value="ctrl.errorLog"></code-editor>
|
||||
<code-editor identifier="kubernetes-deploy-logs" read-only="true" yml="false" value="ctrl.errorLog"></code-editor>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
8
app/portainer/components/beta-panel/betaPanel.html
Normal file
8
app/portainer/components/beta-panel/betaPanel.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<information-panel title-text="Information">
|
||||
<span class="small">
|
||||
<p class="text-muted">
|
||||
<pr-icon icon="'flask-conical'" mode="'warning'"></pr-icon>
|
||||
This is a beta feature.
|
||||
</p>
|
||||
</span>
|
||||
</information-panel>
|
||||
3
app/portainer/components/beta-panel/betaPanel.js
Normal file
3
app/portainer/components/beta-panel/betaPanel.js
Normal file
@@ -0,0 +1,3 @@
|
||||
angular.module('portainer.app').component('betaPanel', {
|
||||
templateUrl: './betaPanel.html',
|
||||
});
|
||||
@@ -2,7 +2,6 @@
|
||||
id="$ctrl.identifier"
|
||||
placeholder="$ctrl.placeholder"
|
||||
yaml="$ctrl.yml"
|
||||
docker-file="$ctrl.dockerFile"
|
||||
readonly="$ctrl.readOnly"
|
||||
on-change="($ctrl.handleChange)"
|
||||
value="$ctrl.value"
|
||||
|
||||
@@ -7,7 +7,6 @@ angular.module('portainer.app').component('codeEditor', {
|
||||
identifier: '@',
|
||||
placeholder: '@',
|
||||
yml: '<',
|
||||
dockerFile: '<',
|
||||
readOnly: '<',
|
||||
onChange: '<',
|
||||
value: '<',
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
Switch to simple mode to define variables line by line, or load from .env file
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<code-editor identifier="environment-variables-editor" placeholder="e.g. key=value" value="$ctrl.editorText" on-change="($ctrl.editorUpdate)"></code-editor>
|
||||
<code-editor identifier="environment-variables-editor" placeholder="e.g. key=value" value="$ctrl.editorText" yml="false" on-change="($ctrl.editorUpdate)"></code-editor>
|
||||
</div>
|
||||
<div class="col-sm-12 small text-muted" ng-if="$ctrl.showHelpMessage">
|
||||
<pr-icon icon="'alert-circle'" mode="'primary'"></pr-icon>
|
||||
|
||||
@@ -8,7 +8,6 @@ export const gitForm: IComponentOptions = {
|
||||
<react-git-form
|
||||
value="$ctrl.value"
|
||||
on-change="$ctrl.handleChange"
|
||||
environment-type="$ctrl.environmentType"
|
||||
is-docker-standalone="$ctrl.isDockerStandalone"
|
||||
deploy-method="$ctrl.deployMethod"
|
||||
is-additional-files-field-visible="$ctrl.isAdditionalFilesFieldVisible"
|
||||
@@ -23,7 +22,6 @@ export const gitForm: IComponentOptions = {
|
||||
bindings: {
|
||||
value: '<',
|
||||
onChange: '<',
|
||||
environmentType: '@',
|
||||
isDockerStandalone: '<',
|
||||
deployMethod: '@',
|
||||
baseWebhookUrl: '@',
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
value="$ctrl.formValues.AutoUpdate"
|
||||
on-change="($ctrl.onChangeAutoUpdate)"
|
||||
environment-type="KUBERNETES"
|
||||
is-force-pull-visible="false"
|
||||
base-webhook-url="{{ $ctrl.state.baseWebhookUrl }}"
|
||||
webhook-id="{{ $ctrl.state.webhookId }}"
|
||||
webhooks-docs="https://docs.portainer.io/user/kubernetes/applications/webhooks"
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal">
|
||||
<box-selector radio-name="'theme'" value="$ctrl.state.themeColor" options="$ctrl.state.availableThemes" on-change="($ctrl.setThemeColor)"></box-selector>
|
||||
|
||||
<p class="vertical-center mt-2">
|
||||
<pr-icon icon="'alert-circle'" class-name="'icon-primary'"></pr-icon>
|
||||
<span class="small">Dark and High-contrast theme are experimental. Some UI components might not display properly.</span>
|
||||
</p>
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
|
||||
@@ -18,7 +18,6 @@ export const gitFormModule = angular
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(GitForm))), [
|
||||
'value',
|
||||
'onChange',
|
||||
'environmentType',
|
||||
'isDockerStandalone',
|
||||
'deployMethod',
|
||||
'isAdditionalFilesFieldVisible',
|
||||
|
||||
@@ -189,7 +189,6 @@ export const componentsModule = angular
|
||||
'id',
|
||||
'placeholder',
|
||||
'yaml',
|
||||
'dockerFile',
|
||||
'readonly',
|
||||
'onChange',
|
||||
'value',
|
||||
|
||||
@@ -27,8 +27,6 @@ axios.interceptors.request.use(async (config) => {
|
||||
return newConfig;
|
||||
});
|
||||
|
||||
export const agentTargetHeader = 'X-PortainerAgent-Target';
|
||||
|
||||
export function agentInterceptor(config: AxiosRequestConfig) {
|
||||
if (!config.url || !config.url.includes('/docker/')) {
|
||||
return config;
|
||||
@@ -37,7 +35,7 @@ export function agentInterceptor(config: AxiosRequestConfig) {
|
||||
const newConfig = { headers: config.headers || {}, ...config };
|
||||
const target = portainerAgentTargetHeader();
|
||||
if (target) {
|
||||
newConfig.headers[agentTargetHeader] = target;
|
||||
newConfig.headers['X-PortainerAgent-Target'] = target;
|
||||
}
|
||||
|
||||
if (portainerAgentManagerOperation()) {
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
--text-cm-string-color: var(--red-3);
|
||||
--text-cm-number-color: var(--green-1);
|
||||
--text-cm-keyword-color: var(--ui-blue-dark-9);
|
||||
--text-cm-comment-color: var(--ui-orange-6);
|
||||
--text-cm-variable-name-color: var(--ui-green-8);
|
||||
--text-codemirror-color: var(--black-color);
|
||||
--bg-codemirror-color: var(--white-color);
|
||||
--bg-codemirror-gutters-color: var(--grey-17);
|
||||
@@ -62,50 +60,9 @@
|
||||
}
|
||||
|
||||
.root :global(.cm-button) {
|
||||
@apply bg-blue-8;
|
||||
color: var(--text-codemirror-color);
|
||||
background-image: none;
|
||||
border-radius: 4px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.root :global(.cm-button[name='next']),
|
||||
.root :global(.cm-button[name='replace']) {
|
||||
@apply border-blue-8 bg-blue-8 text-white;
|
||||
@apply hover:border-blue-9 hover:bg-blue-9 hover:text-white;
|
||||
@apply th-dark:hover:border-blue-7 th-dark:hover:bg-blue-7;
|
||||
}
|
||||
|
||||
.root :global(.cm-button[name='prev']),
|
||||
.root :global(.cm-button[name='replaceAll']) {
|
||||
@apply border border-solid;
|
||||
|
||||
@apply border-blue-8 bg-blue-2 text-blue-9;
|
||||
@apply hover:bg-blue-3;
|
||||
|
||||
@apply th-dark:border-blue-7 th-dark:bg-gray-10 th-dark:text-blue-3;
|
||||
@apply th-dark:hover:bg-blue-11;
|
||||
}
|
||||
|
||||
.root :global(.cm-button[name='select']) {
|
||||
@apply border-gray-5 bg-white text-gray-9;
|
||||
@apply hover:border-gray-5 hover:bg-gray-3 hover:text-gray-10;
|
||||
|
||||
/* dark mode */
|
||||
@apply th-dark:border-gray-warm-7 th-dark:bg-gray-iron-10 th-dark:text-gray-warm-4;
|
||||
@apply th-dark:hover:border-gray-6 th-dark:hover:bg-gray-iron-9 th-dark:hover:text-gray-warm-4;
|
||||
|
||||
@apply th-highcontrast:border-gray-2 th-highcontrast:bg-black th-highcontrast:text-white;
|
||||
@apply th-highcontrast:hover:border-gray-6 th-highcontrast:hover:bg-gray-9 th-highcontrast:hover:text-gray-warm-4;
|
||||
}
|
||||
|
||||
.root :global(.cm-search) label {
|
||||
font-weight: 400;
|
||||
@apply text-gray-7;
|
||||
@apply th-dark:text-gray-warm-3;
|
||||
@apply th-highcontrast:text-white;
|
||||
}
|
||||
|
||||
.root :global(.cm-search) input {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.root :global(.cm-textfield) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import { StreamLanguage, LanguageSupport } from '@codemirror/language';
|
||||
import { yaml } from '@codemirror/legacy-modes/mode/yaml';
|
||||
import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile';
|
||||
import { useMemo } from 'react';
|
||||
import { createTheme } from '@uiw/codemirror-themes';
|
||||
import { tags as highlightTags } from '@lezer/highlight';
|
||||
@@ -13,7 +12,6 @@ interface Props {
|
||||
id: string;
|
||||
placeholder?: string;
|
||||
yaml?: boolean;
|
||||
dockerFile?: boolean;
|
||||
readonly?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
value: string;
|
||||
@@ -39,18 +37,10 @@ const theme = createTheme({
|
||||
},
|
||||
{ tag: highlightTags.number, color: 'var(--text-cm-number-color)' },
|
||||
{ tag: highlightTags.keyword, color: 'var(--text-cm-keyword-color)' },
|
||||
{ tag: highlightTags.comment, color: 'var(--text-cm-comment-color)' },
|
||||
{
|
||||
tag: highlightTags.variableName,
|
||||
color: 'var(--text-cm-variable-name-color)',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const yamlLanguage = new LanguageSupport(StreamLanguage.define(yaml));
|
||||
const dockerFileLanguage = new LanguageSupport(
|
||||
StreamLanguage.define(dockerFile)
|
||||
);
|
||||
|
||||
export function CodeEditor({
|
||||
id,
|
||||
@@ -60,18 +50,8 @@ export function CodeEditor({
|
||||
value,
|
||||
height = '500px',
|
||||
yaml: isYaml,
|
||||
dockerFile: isDockerFile,
|
||||
}: Props) {
|
||||
const extensions = useMemo(() => {
|
||||
const extensions = [];
|
||||
if (isYaml) {
|
||||
extensions.push(yamlLanguage);
|
||||
}
|
||||
if (isDockerFile) {
|
||||
extensions.push(dockerFileLanguage);
|
||||
}
|
||||
return extensions;
|
||||
}, [isYaml, isDockerFile]);
|
||||
const extensions = useMemo(() => (isYaml ? [yamlLanguage] : []), [isYaml]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -85,10 +65,6 @@ export function CodeEditor({
|
||||
id={id}
|
||||
extensions={extensions}
|
||||
height={height}
|
||||
basicSetup={{
|
||||
highlightSelectionMatches: false,
|
||||
autocompletion: false,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -36,7 +36,6 @@ import nomadicon from '@/assets/ico/vendor/nomad-icon.svg?c';
|
||||
import openldap from '@/assets/ico/vendor/openldap.svg?c';
|
||||
import proget from '@/assets/ico/vendor/proget.svg?c';
|
||||
import quay from '@/assets/ico/vendor/quay.svg?c';
|
||||
import beta from '@/assets/ico/beta.svg?c';
|
||||
|
||||
const placeholder = Placeholder;
|
||||
|
||||
@@ -77,7 +76,6 @@ export const SvgIcons = {
|
||||
proget,
|
||||
quay,
|
||||
kube,
|
||||
beta,
|
||||
};
|
||||
|
||||
interface SvgProps {
|
||||
|
||||
@@ -10,7 +10,6 @@ export interface Props {
|
||||
icon?: React.ReactNode;
|
||||
color?: Color;
|
||||
className?: string;
|
||||
childrenWrapperClassName?: string;
|
||||
}
|
||||
|
||||
export function TextTip({
|
||||
@@ -18,13 +17,11 @@ export function TextTip({
|
||||
icon = AlertCircle,
|
||||
className,
|
||||
children,
|
||||
childrenWrapperClassName = 'text-muted',
|
||||
}: PropsWithChildren<Props>) {
|
||||
return (
|
||||
<div className={clsx('small inline-flex gap-1', className)}>
|
||||
<Icon icon={icon} mode={getMode(color)} className="!mt-[2px]" />
|
||||
|
||||
<span className={childrenWrapperClassName}>{children}</span>
|
||||
<div className={clsx('small inline-flex items-center gap-1', className)}>
|
||||
<Icon icon={icon} mode={getMode(color)} className="shrink-0" />
|
||||
<span className="text-muted">{children}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,12 +64,9 @@ function OptionItem({
|
||||
color="light"
|
||||
as="label"
|
||||
disabled={disabled || readOnly}
|
||||
className={clsx(
|
||||
{
|
||||
active: selected,
|
||||
},
|
||||
'!static !z-auto'
|
||||
)}
|
||||
className={clsx({
|
||||
active: selected,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
<input
|
||||
|
||||
@@ -52,9 +52,12 @@ export function ContainersDatatable({
|
||||
|
||||
const [search, setSearch] = useSearchBarState(storageKey);
|
||||
|
||||
const containersQuery = useContainers(environment.Id, {
|
||||
autoRefreshRate: settings.autoRefreshRate * 1000,
|
||||
});
|
||||
const containersQuery = useContainers(
|
||||
environment.Id,
|
||||
true,
|
||||
undefined,
|
||||
settings.autoRefreshRate * 1000
|
||||
);
|
||||
|
||||
return (
|
||||
<RowProvider context={{ environment }}>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import axios, {
|
||||
agentTargetHeader,
|
||||
parseAxiosError,
|
||||
} from '@/portainer/services/axios';
|
||||
import { withGlobalError } from '@/react-tools/react-query';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
|
||||
import { urlBuilder } from '../containers.service';
|
||||
import { DockerContainerResponse } from '../types/response';
|
||||
@@ -14,27 +10,20 @@ import { parseViewModel } from '../utils';
|
||||
import { Filters } from './types';
|
||||
import { queryKeys } from './query-keys';
|
||||
|
||||
interface UseContainers {
|
||||
all?: boolean;
|
||||
filters?: Filters;
|
||||
nodeName?: string;
|
||||
}
|
||||
|
||||
export function useContainers(
|
||||
environmentId: EnvironmentId,
|
||||
{
|
||||
autoRefreshRate,
|
||||
|
||||
...params
|
||||
}: UseContainers & {
|
||||
autoRefreshRate?: number;
|
||||
} = {}
|
||||
all = true,
|
||||
filters?: Filters,
|
||||
autoRefreshRate?: number
|
||||
) {
|
||||
return useQuery(
|
||||
queryKeys.filters(environmentId, params),
|
||||
() => getContainers(environmentId, params),
|
||||
queryKeys.filters(environmentId, all, filters),
|
||||
() => getContainers(environmentId, all, filters),
|
||||
{
|
||||
...withGlobalError('Unable to retrieve containers'),
|
||||
meta: {
|
||||
title: 'Failure',
|
||||
message: 'Unable to retrieve containers',
|
||||
},
|
||||
refetchInterval() {
|
||||
return autoRefreshRate ?? false;
|
||||
},
|
||||
@@ -44,18 +33,14 @@ export function useContainers(
|
||||
|
||||
async function getContainers(
|
||||
environmentId: EnvironmentId,
|
||||
{ all = true, filters, nodeName }: UseContainers = {}
|
||||
all = true,
|
||||
filters?: Filters
|
||||
) {
|
||||
try {
|
||||
const { data } = await axios.get<DockerContainerResponse[]>(
|
||||
urlBuilder(environmentId, undefined, 'json'),
|
||||
{
|
||||
params: { all, filters: filters && JSON.stringify(filters) },
|
||||
headers: nodeName
|
||||
? {
|
||||
[agentTargetHeader]: nodeName,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
);
|
||||
return data.map((c) => parseViewModel(c));
|
||||
|
||||
@@ -8,10 +8,8 @@ export const queryKeys = {
|
||||
list: (environmentId: EnvironmentId) =>
|
||||
[dockerQueryKeys.root(environmentId), 'containers'] as const,
|
||||
|
||||
filters: (
|
||||
environmentId: EnvironmentId,
|
||||
params: { all?: boolean; filters?: Filters; nodeName?: string } = {}
|
||||
) => [...queryKeys.list(environmentId), params] as const,
|
||||
filters: (environmentId: EnvironmentId, all?: boolean, filters?: Filters) =>
|
||||
[...queryKeys.list(environmentId), { all, filters }] as const,
|
||||
|
||||
container: (environmentId: EnvironmentId, id: string) =>
|
||||
[...queryKeys.list(environmentId), id] as const,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useRouter, useCurrentStateAndParams } from '@uirouter/react';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
import { AccessControlPanel } from '@/react/portainer/access-control/AccessControlPanel/AccessControlPanel';
|
||||
@@ -13,7 +15,7 @@ import { PageHeader } from '@@/PageHeader';
|
||||
|
||||
import { useNetwork, useDeleteNetwork } from '../queries';
|
||||
import { isSystemNetwork } from '../network.helper';
|
||||
import { NetworkResponseContainers } from '../types';
|
||||
import { DockerNetwork, NetworkContainer } from '../types';
|
||||
|
||||
import { NetworkDetailsTable } from './NetworkDetailsTable';
|
||||
import { NetworkOptionsTable } from './NetworkOptionsTable';
|
||||
@@ -23,18 +25,28 @@ export function ItemView() {
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [networkContainers, setNetworkContainers] = useState<
|
||||
NetworkContainer[]
|
||||
>([]);
|
||||
const {
|
||||
params: { id: networkId, nodeName },
|
||||
} = useCurrentStateAndParams();
|
||||
const environmentId = useEnvironmentId();
|
||||
const networkQuery = useNetwork(environmentId, networkId, { nodeName });
|
||||
|
||||
const networkQuery = useNetwork(environmentId, networkId);
|
||||
const deleteNetworkMutation = useDeleteNetwork();
|
||||
const containersQuery = useContainers(environmentId, {
|
||||
filters: {
|
||||
network: [networkId],
|
||||
},
|
||||
nodeName,
|
||||
});
|
||||
const filters = {
|
||||
network: [networkId],
|
||||
};
|
||||
const containersQuery = useContainers(environmentId, true, filters);
|
||||
|
||||
useEffect(() => {
|
||||
if (networkQuery.data && containersQuery.data) {
|
||||
setNetworkContainers(
|
||||
filterContainersInNetwork(networkQuery.data, containersQuery.data)
|
||||
);
|
||||
}
|
||||
}, [networkQuery.data, containersQuery.data]);
|
||||
|
||||
if (!networkQuery.data) {
|
||||
return null;
|
||||
@@ -42,10 +54,6 @@ export function ItemView() {
|
||||
|
||||
const network = networkQuery.data;
|
||||
|
||||
const networkContainers = filterContainersInNetwork(
|
||||
network.Containers,
|
||||
containersQuery.data
|
||||
);
|
||||
const resourceControl = network.Portainer?.ResourceControl
|
||||
? new ResourceControlViewModel(network.Portainer.ResourceControl)
|
||||
: undefined;
|
||||
@@ -108,20 +116,24 @@ export function ItemView() {
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function filterContainersInNetwork(
|
||||
networkContainers?: NetworkResponseContainers,
|
||||
containers: DockerContainer[] = []
|
||||
) {
|
||||
if (!networkContainers) {
|
||||
return [];
|
||||
function filterContainersInNetwork(
|
||||
network: DockerNetwork,
|
||||
containers: DockerContainer[]
|
||||
) {
|
||||
const containersInNetwork = _.compact(
|
||||
containers.map((container) => {
|
||||
const containerInNetworkResponse = network.Containers[container.Id];
|
||||
if (containerInNetworkResponse) {
|
||||
const containerInNetwork: NetworkContainer = {
|
||||
...containerInNetworkResponse,
|
||||
Id: container.Id,
|
||||
};
|
||||
return containerInNetwork;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
);
|
||||
return containersInNetwork;
|
||||
}
|
||||
|
||||
return containers
|
||||
.filter((container) => networkContainers[container.Id])
|
||||
.map((container) => ({
|
||||
...networkContainers[container.Id],
|
||||
Id: container.Id,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Authorized } from '@/react/hooks/useUser';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
|
||||
import { TableContainer, TableTitle } from '@@/datatables';
|
||||
import { Table, TableContainer, TableTitle } from '@@/datatables';
|
||||
import { DetailsTable } from '@@/DetailsTable';
|
||||
import { Button } from '@@/buttons';
|
||||
import { Link } from '@@/Link';
|
||||
@@ -42,51 +42,53 @@ export function NetworkContainersTable({
|
||||
return (
|
||||
<TableContainer>
|
||||
<TableTitle label="Containers in network" icon={Server} />
|
||||
<DetailsTable
|
||||
headers={tableHeaders}
|
||||
dataCy="networkDetails-networkContainers"
|
||||
>
|
||||
{networkContainers.map((container) => (
|
||||
<tr key={container.Id}>
|
||||
<td>
|
||||
<Link
|
||||
to="docker.containers.container"
|
||||
params={{
|
||||
id: container.Id,
|
||||
nodeName,
|
||||
}}
|
||||
title={container.Name}
|
||||
>
|
||||
{container.Name}
|
||||
</Link>
|
||||
</td>
|
||||
<td>{container.IPv4Address || '-'}</td>
|
||||
<td>{container.IPv6Address || '-'}</td>
|
||||
<td>{container.MacAddress || '-'}</td>
|
||||
<td>
|
||||
<Authorized authorizations="DockerNetworkDisconnect">
|
||||
<Button
|
||||
data-cy={`networkDetails-disconnect${container.Name}`}
|
||||
size="xsmall"
|
||||
color="dangerlight"
|
||||
onClick={() => {
|
||||
if (container.Id) {
|
||||
disconnectContainer.mutate({
|
||||
containerId: container.Id,
|
||||
environmentId,
|
||||
networkId,
|
||||
});
|
||||
}
|
||||
<Table className="nopadding">
|
||||
<DetailsTable
|
||||
headers={tableHeaders}
|
||||
dataCy="networkDetails-networkContainers"
|
||||
>
|
||||
{networkContainers.map((container) => (
|
||||
<tr key={container.Id}>
|
||||
<td>
|
||||
<Link
|
||||
to="docker.containers.container"
|
||||
params={{
|
||||
id: container.Id,
|
||||
nodeName,
|
||||
}}
|
||||
title={container.Name}
|
||||
>
|
||||
<Icon icon={Trash2} class-name="icon-secondary icon-md" />
|
||||
Leave Network
|
||||
</Button>
|
||||
</Authorized>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</DetailsTable>
|
||||
{container.Name}
|
||||
</Link>
|
||||
</td>
|
||||
<td>{container.IPv4Address || '-'}</td>
|
||||
<td>{container.IPv6Address || '-'}</td>
|
||||
<td>{container.MacAddress || '-'}</td>
|
||||
<td>
|
||||
<Authorized authorizations="DockerNetworkDisconnect">
|
||||
<Button
|
||||
data-cy={`networkDetails-disconnect${container.Name}`}
|
||||
size="xsmall"
|
||||
color="dangerlight"
|
||||
onClick={() => {
|
||||
if (container.Id) {
|
||||
disconnectContainer.mutate({
|
||||
containerId: container.Id,
|
||||
environmentId,
|
||||
networkId,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon icon={Trash2} class-name="icon-secondary icon-md" />
|
||||
Leave Network
|
||||
</Button>
|
||||
</Authorized>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</DetailsTable>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Share2, Trash2 } from 'lucide-react';
|
||||
import DockerNetworkHelper from '@/docker/helpers/networkHelper';
|
||||
import { Authorized } from '@/react/hooks/useUser';
|
||||
|
||||
import { TableContainer, TableTitle } from '@@/datatables';
|
||||
import { Table, TableContainer, TableTitle } from '@@/datatables';
|
||||
import { DetailsTable } from '@@/DetailsTable';
|
||||
import { Button } from '@@/buttons';
|
||||
import { Icon } from '@@/Icon';
|
||||
@@ -32,74 +32,76 @@ export function NetworkDetailsTable({
|
||||
return (
|
||||
<TableContainer>
|
||||
<TableTitle label="Network details" icon={Share2} />
|
||||
<DetailsTable dataCy="networkDetails-detailsTable">
|
||||
{/* networkRowContent */}
|
||||
<DetailsTable.Row label="Name">{network.Name}</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Id">
|
||||
{network.Id}
|
||||
{allowRemoveNetwork && (
|
||||
<Authorized authorizations="DockerNetworkDelete">
|
||||
<Button
|
||||
data-cy="networkDetails-deleteNetwork"
|
||||
size="xsmall"
|
||||
color="danger"
|
||||
onClick={() => onRemoveNetworkClicked()}
|
||||
<Table className="nopadding">
|
||||
<DetailsTable dataCy="networkDetails-detailsTable">
|
||||
{/* networkRowContent */}
|
||||
<DetailsTable.Row label="Name">{network.Name}</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Id">
|
||||
{network.Id}
|
||||
{allowRemoveNetwork && (
|
||||
<Authorized authorizations="DockerNetworkDelete">
|
||||
<Button
|
||||
data-cy="networkDetails-deleteNetwork"
|
||||
size="xsmall"
|
||||
color="danger"
|
||||
onClick={() => onRemoveNetworkClicked()}
|
||||
>
|
||||
<Icon
|
||||
icon={Trash2}
|
||||
className="space-right"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Delete this network
|
||||
</Button>
|
||||
</Authorized>
|
||||
)}
|
||||
</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Driver">{network.Driver}</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Scope">{network.Scope}</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Attachable">
|
||||
{String(network.Attachable)}
|
||||
</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Internal">
|
||||
{String(network.Internal)}
|
||||
</DetailsTable.Row>
|
||||
|
||||
{/* IPV4 ConfigRowContent */}
|
||||
{ipv4Configs.map((config) => (
|
||||
<Fragment key={config.Subnet}>
|
||||
<DetailsTable.Row
|
||||
label={`IPV4 Subnet${getConfigDetails(config.Subnet)}`}
|
||||
>
|
||||
<Icon
|
||||
icon={Trash2}
|
||||
className="space-right"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Delete this network
|
||||
</Button>
|
||||
</Authorized>
|
||||
)}
|
||||
</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Driver">{network.Driver}</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Scope">{network.Scope}</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Attachable">
|
||||
{String(network.Attachable)}
|
||||
</DetailsTable.Row>
|
||||
<DetailsTable.Row label="Internal">
|
||||
{String(network.Internal)}
|
||||
</DetailsTable.Row>
|
||||
{`IPV4 Gateway${getConfigDetails(config.Gateway)}`}
|
||||
</DetailsTable.Row>
|
||||
<DetailsTable.Row
|
||||
label={`IPV4 IP Range${getConfigDetails(config.IPRange)}`}
|
||||
>
|
||||
{`IPV4 Excluded IPs${getAuxiliaryAddresses(
|
||||
config.AuxiliaryAddresses
|
||||
)}`}
|
||||
</DetailsTable.Row>
|
||||
</Fragment>
|
||||
))}
|
||||
|
||||
{/* IPV4 ConfigRowContent */}
|
||||
{ipv4Configs.map((config) => (
|
||||
<Fragment key={config.Subnet}>
|
||||
<DetailsTable.Row
|
||||
label={`IPV4 Subnet${getConfigDetails(config.Subnet)}`}
|
||||
>
|
||||
{`IPV4 Gateway${getConfigDetails(config.Gateway)}`}
|
||||
</DetailsTable.Row>
|
||||
<DetailsTable.Row
|
||||
label={`IPV4 IP Range${getConfigDetails(config.IPRange)}`}
|
||||
>
|
||||
{`IPV4 Excluded IPs${getAuxiliaryAddresses(
|
||||
config.AuxiliaryAddresses
|
||||
)}`}
|
||||
</DetailsTable.Row>
|
||||
</Fragment>
|
||||
))}
|
||||
|
||||
{/* IPV6 ConfigRowContent */}
|
||||
{ipv6Configs.map((config) => (
|
||||
<Fragment key={config.Subnet}>
|
||||
<DetailsTable.Row
|
||||
label={`IPV6 Subnet${getConfigDetails(config.Subnet)}`}
|
||||
>
|
||||
{`IPV6 Gateway${getConfigDetails(config.Gateway)}`}
|
||||
</DetailsTable.Row>
|
||||
<DetailsTable.Row
|
||||
label={`IPV6 IP Range${getConfigDetails(config.IPRange)}`}
|
||||
>
|
||||
{`IPV6 Excluded IPs${getAuxiliaryAddresses(
|
||||
config.AuxiliaryAddresses
|
||||
)}`}
|
||||
</DetailsTable.Row>
|
||||
</Fragment>
|
||||
))}
|
||||
</DetailsTable>
|
||||
{/* IPV6 ConfigRowContent */}
|
||||
{ipv6Configs.map((config) => (
|
||||
<Fragment key={config.Subnet}>
|
||||
<DetailsTable.Row
|
||||
label={`IPV6 Subnet${getConfigDetails(config.Subnet)}`}
|
||||
>
|
||||
{`IPV6 Gateway${getConfigDetails(config.Gateway)}`}
|
||||
</DetailsTable.Row>
|
||||
<DetailsTable.Row
|
||||
label={`IPV6 IP Range${getConfigDetails(config.IPRange)}`}
|
||||
>
|
||||
{`IPV6 Excluded IPs${getAuxiliaryAddresses(
|
||||
config.AuxiliaryAddresses
|
||||
)}`}
|
||||
</DetailsTable.Row>
|
||||
</Fragment>
|
||||
))}
|
||||
</DetailsTable>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Share2 } from 'lucide-react';
|
||||
|
||||
import { TableContainer, TableTitle } from '@@/datatables';
|
||||
import { Table, TableContainer, TableTitle } from '@@/datatables';
|
||||
import { DetailsTable } from '@@/DetailsTable';
|
||||
|
||||
import { NetworkOptions } from '../types';
|
||||
@@ -19,13 +19,15 @@ export function NetworkOptionsTable({ options }: Props) {
|
||||
return (
|
||||
<TableContainer>
|
||||
<TableTitle label="Network options" icon={Share2} />
|
||||
<DetailsTable dataCy="networkDetails-networkOptionsTable">
|
||||
{networkEntries.map(([key, value]) => (
|
||||
<DetailsTable.Row key={key} label={key}>
|
||||
{value}
|
||||
</DetailsTable.Row>
|
||||
))}
|
||||
</DetailsTable>
|
||||
<Table className="nopadding">
|
||||
<DetailsTable dataCy="networkDetails-networkOptionsTable">
|
||||
{networkEntries.map(([key, value]) => (
|
||||
<DetailsTable.Row key={key} label={key}>
|
||||
{value}
|
||||
</DetailsTable.Row>
|
||||
))}
|
||||
</DetailsTable>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { ContainerId } from '@/react/docker/containers/types';
|
||||
import axios, {
|
||||
agentTargetHeader,
|
||||
parseAxiosError,
|
||||
} from '@/portainer/services/axios';
|
||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { NetworkId, DockerNetwork } from './types';
|
||||
@@ -11,19 +8,11 @@ type NetworkAction = 'connect' | 'disconnect' | 'create';
|
||||
|
||||
export async function getNetwork(
|
||||
environmentId: EnvironmentId,
|
||||
networkId: NetworkId,
|
||||
{ nodeName }: { nodeName?: string } = {}
|
||||
networkId: NetworkId
|
||||
) {
|
||||
try {
|
||||
const { data: network } = await axios.get<DockerNetwork>(
|
||||
buildUrl(environmentId, networkId),
|
||||
nodeName
|
||||
? {
|
||||
headers: {
|
||||
[agentTargetHeader]: nodeName,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
buildUrl(environmentId, networkId)
|
||||
);
|
||||
return network;
|
||||
} catch (e) {
|
||||
|
||||
@@ -14,21 +14,10 @@ import {
|
||||
} from './network.service';
|
||||
import { NetworkId } from './types';
|
||||
|
||||
export function useNetwork(
|
||||
environmentId: EnvironmentId,
|
||||
networkId: NetworkId,
|
||||
{ nodeName }: { nodeName?: string } = {}
|
||||
) {
|
||||
export function useNetwork(environmentId: EnvironmentId, networkId: NetworkId) {
|
||||
return useQuery(
|
||||
[
|
||||
'environments',
|
||||
environmentId,
|
||||
'docker',
|
||||
'networks',
|
||||
networkId,
|
||||
{ nodeName },
|
||||
],
|
||||
() => getNetwork(environmentId, networkId, { nodeName }),
|
||||
['environments', environmentId, 'docker', 'networks', networkId],
|
||||
() => getNetwork(environmentId, networkId),
|
||||
{
|
||||
onError: (err) => {
|
||||
notifyError('Failure', err as Error, 'Unable to get network');
|
||||
|
||||
@@ -49,12 +49,14 @@ export function StackContainersDatatable({ environment, stackName }: Props) {
|
||||
columns.filter((col) => col.canHide).map((col) => col.id)
|
||||
);
|
||||
|
||||
const containersQuery = useContainers(environment.Id, {
|
||||
filters: {
|
||||
const containersQuery = useContainers(
|
||||
environment.Id,
|
||||
true,
|
||||
{
|
||||
label: [`com.docker.compose.project=${stackName}`],
|
||||
},
|
||||
autoRefreshRate: settings.autoRefreshRate * 1000,
|
||||
});
|
||||
settings.autoRefreshRate * 1000
|
||||
);
|
||||
|
||||
return (
|
||||
<RowProvider context={{ environment }}>
|
||||
|
||||
@@ -55,10 +55,7 @@ function CreateView() {
|
||||
breadcrumbs="Edge agent update and rollback"
|
||||
/>
|
||||
|
||||
<BetaAlert
|
||||
className="ml-[15px] mb-2"
|
||||
message="Beta feature - currently limited to standalone Linux and Nomad edge devices."
|
||||
/>
|
||||
<BetaAlert />
|
||||
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
|
||||
@@ -77,10 +77,7 @@ function ItemView() {
|
||||
]}
|
||||
/>
|
||||
|
||||
<BetaAlert
|
||||
className="ml-[15px] mb-2"
|
||||
message="Beta feature - currently limited to standalone Linux and Nomad edge devices."
|
||||
/>
|
||||
<BetaAlert />
|
||||
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
|
||||
@@ -42,10 +42,7 @@ export function ListView() {
|
||||
breadcrumbs="Update and rollback"
|
||||
/>
|
||||
|
||||
<BetaAlert
|
||||
className="ml-[15px] mb-2"
|
||||
message="Beta feature - currently limited to standalone Linux and Nomad edge devices."
|
||||
/>
|
||||
<BetaAlert />
|
||||
|
||||
<Datatable
|
||||
dataset={listQuery.data}
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
import { InformationPanel } from '@@/InformationPanel';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
|
||||
interface Props {
|
||||
message: string;
|
||||
className?: string;
|
||||
isHtml?: boolean;
|
||||
}
|
||||
|
||||
export function BetaAlert({ message, className, isHtml }: Props) {
|
||||
export function BetaAlert() {
|
||||
return (
|
||||
<TextTip
|
||||
icon="svg-beta"
|
||||
className={className}
|
||||
childrenWrapperClassName="text-warning"
|
||||
>
|
||||
{!isHtml ? (
|
||||
message
|
||||
) : (
|
||||
// eslint-disable-next-line react/no-danger
|
||||
<span dangerouslySetInnerHTML={{ __html: message }} />
|
||||
)}
|
||||
</TextTip>
|
||||
<InformationPanel title="Limited Feature">
|
||||
<TextTip>
|
||||
This feature is currently in beta and is limited to standalone linux
|
||||
edge devices.
|
||||
</TextTip>
|
||||
</InformationPanel>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import { refFieldValidation } from './RefField/RefField';
|
||||
interface Props {
|
||||
value: GitFormModel;
|
||||
onChange: (value: Partial<GitFormModel>) => void;
|
||||
environmentType?: 'DOCKER' | 'KUBERNETES' | undefined;
|
||||
deployMethod?: 'compose' | 'nomad' | 'manifest';
|
||||
isDockerStandalone?: boolean;
|
||||
isAdditionalFilesFieldVisible?: boolean;
|
||||
@@ -37,7 +36,6 @@ interface Props {
|
||||
export function GitForm({
|
||||
value,
|
||||
onChange,
|
||||
environmentType = 'DOCKER',
|
||||
deployMethod = 'compose',
|
||||
isDockerStandalone = false,
|
||||
isAdditionalFilesFieldVisible,
|
||||
@@ -96,7 +94,6 @@ export function GitForm({
|
||||
|
||||
{value.AutoUpdate && (
|
||||
<AutoUpdateFieldset
|
||||
environmentType={environmentType}
|
||||
webhookId={webhookId}
|
||||
baseWebhookUrl={baseWebhookUrl}
|
||||
value={value.AutoUpdate}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Formik, Field, Form } from 'formik';
|
||||
import { FlaskConical, Laptop } from 'lucide-react';
|
||||
import { Laptop } from 'lucide-react';
|
||||
|
||||
import { FDOConfiguration } from '@/portainer/hostmanagement/fdo/model';
|
||||
import {
|
||||
@@ -38,7 +38,7 @@ export function SettingsFDO({ settings, onSubmit }: Props) {
|
||||
return (
|
||||
<Widget>
|
||||
<Widget.Body>
|
||||
<TextTip color="blue" icon={FlaskConical}>
|
||||
<TextTip color="blue">
|
||||
Since FDO is still an experimental feature that requires additional
|
||||
infrastructure, it has been temporarily hidden in the UI.
|
||||
</TextTip>
|
||||
|
||||
6
binary-version.json
Normal file
6
binary-version.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"docker": "v20.10.21",
|
||||
"dockerCompose": "v2.17.2",
|
||||
"helm": "v3.11.0",
|
||||
"kubectl": "v1.24.1"
|
||||
}
|
||||
48
build.sh
48
build.sh
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ARCHIVE_BUILD_FOLDER="/tmp/portainer-builds"
|
||||
|
||||
# parameter: "platform-architecture"
|
||||
function build_and_push_images() {
|
||||
docker build -t "portainer/portainer:$1-${VERSION}" -f build/linux/Dockerfile .
|
||||
docker tag "portainer/portainer:$1-${VERSION}" "portainer/portainer:$1"
|
||||
docker push "portainer/portainer:$1-${VERSION}"
|
||||
docker push "portainer/portainer:$1"
|
||||
}
|
||||
|
||||
# parameter: "platform-architecture"
|
||||
function build_archive() {
|
||||
BUILD_FOLDER="${ARCHIVE_BUILD_FOLDER}/$1"
|
||||
rm -rf ${BUILD_FOLDER} && mkdir -pv ${BUILD_FOLDER}/portainer
|
||||
cp -r dist/* ${BUILD_FOLDER}/portainer/
|
||||
cd ${BUILD_FOLDER}
|
||||
tar cvpfz "portainer-${VERSION}-$1.tar.gz" portainer
|
||||
mv "portainer-${VERSION}-$1.tar.gz" ${ARCHIVE_BUILD_FOLDER}/
|
||||
cd -
|
||||
}
|
||||
|
||||
function build_all() {
|
||||
mkdir -pv "${ARCHIVE_BUILD_FOLDER}"
|
||||
for tag in $@; do
|
||||
yarn grunt "release:`echo "$tag" | tr '-' ':'`"
|
||||
name="portainer"; if [ "$(echo "$tag" | cut -c1)" = "w" ]; then name="${name}.exe"; fi
|
||||
mv dist/portainer-$tag* dist/$name
|
||||
if [ `echo $tag | cut -d \- -f 1` == 'linux' ]; then build_and_push_images "$tag"; fi
|
||||
build_archive "$tag"
|
||||
done
|
||||
docker rmi $(docker images -q -f dangling=true)
|
||||
}
|
||||
|
||||
if [[ $# -ne 1 ]] ; then
|
||||
echo "Usage: $(basename $0) <VERSION>"
|
||||
echo " $(basename $0) \"echo 'Custom' && <BASH COMMANDS>\""
|
||||
exit 1
|
||||
else
|
||||
VERSION="$1"
|
||||
if [ `echo "$@" | cut -c1-4` == 'echo' ]; then
|
||||
bash -c "$@";
|
||||
else
|
||||
build_all 'linux-amd64 linux-arm linux-arm64 linux-ppc64le linux-s390x darwin-amd64 windows-amd64'
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
@@ -1,21 +0,0 @@
|
||||
param (
|
||||
[string]$platform,
|
||||
[string]$arch
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop";
|
||||
|
||||
$binary = "portainer.exe"
|
||||
$project_path = $((Get-Location).Path)
|
||||
|
||||
New-Item -Name dist -Path "$project_path" -ItemType Directory | Out-Null
|
||||
Set-Location -Path "$project_path\api\cmd\portainer"
|
||||
|
||||
# copy templates
|
||||
Copy-Item -Path "./mustache-templates" -Destination "./dist" -Recurse
|
||||
|
||||
|
||||
C:\go\bin\go.exe get -t -d -v ./...
|
||||
C:\go\bin\go.exe build -v
|
||||
|
||||
Move-Item -Path "$($binary)" -Destination "..\..\..\dist"
|
||||
@@ -1,8 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
mkdir -p dist
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# populate tool versions
|
||||
BUILDNUMBER="N/A"
|
||||
@@ -12,24 +9,38 @@ YARN_VERSION="0"
|
||||
WEBPACK_VERSION="0"
|
||||
GO_VERSION="0"
|
||||
|
||||
ldflags="-s -X 'github.com/portainer/liblicense.LicenseServerBaseURL=https://api.portainer.io' \
|
||||
-X 'github.com/portainer/portainer-ee/api/build.BuildNumber=${BUILDNUMBER}' \
|
||||
-X 'github.com/portainer/portainer-ee/api/build.ImageTag=${CONTAINER_IMAGE_TAG}' \
|
||||
-X 'github.com/portainer/portainer-ee/api/build.NodejsVersion=${NODE_VERSION}' \
|
||||
-X 'github.com/portainer/portainer-ee/api/build.YarnVersion=${YARN_VERSION}' \
|
||||
-X 'github.com/portainer/portainer-ee/api/build.WebpackVersion=${WEBPACK_VERSION}' \
|
||||
-X 'github.com/portainer/portainer-ee/api/build.GoVersion=${GO_VERSION}'"
|
||||
|
||||
echo "LDFLAGS=${ldflags}"
|
||||
|
||||
# create output folder
|
||||
mkdir -p dist
|
||||
|
||||
# copy templates
|
||||
cp -r "./mustache-templates" "./dist"
|
||||
|
||||
cd api || exit 1
|
||||
|
||||
cd api
|
||||
# the go get adds 8 seconds
|
||||
go get -t -d -v ./...
|
||||
|
||||
|
||||
# the build takes 2 seconds
|
||||
GOOS=$1 GOARCH=$2 CGO_ENABLED=0 go build \
|
||||
-trimpath \
|
||||
--installsuffix cgo \
|
||||
--ldflags "-s \
|
||||
-trimpath \
|
||||
--installsuffix cgo \
|
||||
--ldflags "-s \
|
||||
--X 'github.com/portainer/portainer/api/build.BuildNumber=${BUILDNUMBER}' \
|
||||
--X 'github.com/portainer/portainer/api/build.ImageTag=${CONTAINER_IMAGE_TAG}' \
|
||||
--X 'github.com/portainer/portainer/api/build.NodejsVersion=${NODE_VERSION}' \
|
||||
--X 'github.com/portainer/portainer/api/build.YarnVersion=${YARN_VERSION}' \
|
||||
--X 'github.com/portainer/portainer/api/build.WebpackVersion=${WEBPACK_VERSION}' \
|
||||
--X 'github.com/portainer/portainer/api/build.GoVersion=${GO_VERSION}'" \
|
||||
-o "../dist/portainer" \
|
||||
./cmd/portainer/
|
||||
--X 'github.com/portainer/portainer/api/build.GoVersion=${GO_VERSION}'" \
|
||||
-o "../dist/portainer" \
|
||||
./cmd/portainer/
|
||||
|
||||
23
build/download_binaries.sh
Executable file
23
build/download_binaries.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
PLATFORM=${1:-"linux"}
|
||||
ARCH=${2:-"amd64"}
|
||||
|
||||
BINARY_VERSION_FILE="./binary-version.json"
|
||||
|
||||
dockerVersion=$(jq -r '.docker' < "${BINARY_VERSION_FILE}")
|
||||
dockerComposeVersion=$(jq -r '.dockerCompose' < "${BINARY_VERSION_FILE}")
|
||||
helmVersion=$(jq -r '.helm' < "${BINARY_VERSION_FILE}")
|
||||
kubectlVersion=$(jq -r '.kubectl' < "${BINARY_VERSION_FILE}")
|
||||
|
||||
mkdir -p dist
|
||||
|
||||
echo "Downloading binaries for docker ${dockerVersion}, docker-compose ${dockerComposeVersion}, helm ${helmVersion}, kubectl ${kubectlVersion}"
|
||||
|
||||
./build/download_docker_binary.sh "$PLATFORM" "$ARCH" "$dockerVersion" &
|
||||
./build/download_docker_compose_binary.sh "$PLATFORM" "$ARCH" "$dockerComposeVersion" &
|
||||
./build/download_helm_binary.sh "$PLATFORM" "$ARCH" "$helmVersion" &
|
||||
./build/download_kubectl_binary.sh "$PLATFORM" "$ARCH" "$kubectlVersion" &
|
||||
wait
|
||||
21
dev/run_container.sh
Executable file
21
dev/run_container.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
PORTAINER_DATA=${PORTAINER_DATA:-/tmp/portainer}
|
||||
PORTAINER_PROJECT=${PORTAINER_PROJECT:-$(pwd)}
|
||||
PORTAINER_FLAGS=${PORTAINER_FLAGS:-}
|
||||
|
||||
docker rm -f portainer > /dev/null
|
||||
|
||||
docker run -d \
|
||||
-p 8000:8000 \
|
||||
-p 9000:9000 \
|
||||
-p 9443:9443 \
|
||||
-v "$PORTAINER_PROJECT/dist:/app" \
|
||||
-v "$PORTAINER_DATA:/data" \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:z \
|
||||
-v /var/run/docker.sock:/var/run/alternative.sock:z \
|
||||
-v /tmp:/tmp \
|
||||
--name portainer \
|
||||
portainer/base \
|
||||
/app/portainer ${PORTAINER_FLAGS}
|
||||
2
go.work.sum
Normal file
2
go.work.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
238
gruntfile.js
238
gruntfile.js
@@ -1,238 +0,0 @@
|
||||
const os = require('os');
|
||||
const loadGruntTasks = require('load-grunt-tasks');
|
||||
const webpackDevConfig = require('./webpack/webpack.develop');
|
||||
const webpackProdConfig = require('./webpack/webpack.production');
|
||||
const webpackTestingConfig = require('./webpack/webpack.testing');
|
||||
|
||||
let arch = os.arch();
|
||||
if (arch === 'x64') {
|
||||
arch = 'amd64';
|
||||
}
|
||||
let platform = os.platform();
|
||||
switch (platform) {
|
||||
case 'windows':
|
||||
case 'darwin':
|
||||
break;
|
||||
default:
|
||||
platform = 'linux';
|
||||
}
|
||||
|
||||
module.exports = function (grunt) {
|
||||
loadGruntTasks(grunt, {
|
||||
pattern: ['grunt-*', 'gruntify-*'],
|
||||
});
|
||||
|
||||
grunt.initConfig({
|
||||
root: 'dist',
|
||||
distdir: 'dist/public',
|
||||
binaries: {
|
||||
dockerVersion: 'v20.10.21',
|
||||
dockerComposePluginVersion: 'v2.17.2',
|
||||
helmVersion: 'v3.11.0',
|
||||
kubectlVersion: 'v1.24.1',
|
||||
},
|
||||
env: gruntConfig.env,
|
||||
clean: gruntConfig.clean,
|
||||
shell: gruntConfig.shell,
|
||||
webpack: gruntConfig.webpack,
|
||||
});
|
||||
|
||||
grunt.registerTask('lint', ['eslint']);
|
||||
|
||||
grunt.task.registerTask('build:server', 'build:server:<platform>:<arch>', function (p = platform, a = arch) {
|
||||
grunt.task.run([`shell:build_binary:${p}:${a}`, `download_binaries:${p}:${a}`]);
|
||||
});
|
||||
|
||||
grunt.registerTask('build:client', ['webpack:dev']);
|
||||
|
||||
grunt.registerTask('build', ['build:server', 'build:client']);
|
||||
|
||||
grunt.registerTask('start:server', ['build:server:linux', 'shell:run_container']);
|
||||
|
||||
grunt.registerTask('start:localserver', [`shell:build_binary:${platform}:${arch}`, 'shell:run_localserver']);
|
||||
|
||||
grunt.registerTask('start:client', ['shell:install_yarndeps', 'webpack:devWatch']);
|
||||
|
||||
grunt.registerTask('start', ['start:server', 'start:client']);
|
||||
|
||||
grunt.registerTask('start:toolkit', ['start:localserver', 'start:client']);
|
||||
|
||||
grunt.task.registerTask('release', 'release:<platform>:<arch>', function (platform = 'linux', a = arch) {
|
||||
grunt.task.run(['env:prod', 'clean:all', `shell:build_binary:${platform}:${a}`, `download_binaries:${platform}:${a}`, 'webpack:prod']);
|
||||
});
|
||||
|
||||
grunt.task.registerTask('devopsbuild', 'devopsbuild:<platform>:<arch>:<env>', function (platform, a = arch, env = 'prod') {
|
||||
grunt.task.run([
|
||||
`env:${env}`,
|
||||
'clean:all',
|
||||
`shell:build_binary_azuredevops:${platform}:${a}`,
|
||||
`download_binaries:${platform}:${a}`,
|
||||
`webpack:${env}`,
|
||||
`shell:storybook:${env}`,
|
||||
]);
|
||||
});
|
||||
|
||||
grunt.task.registerTask('download_binaries', 'download_binaries:<platform>:<arch>', function (platform = 'linux', a = arch) {
|
||||
grunt.task.run([
|
||||
`shell:download_docker_binary:${platform}:${a}`,
|
||||
`shell:download_docker_compose_binary:${platform}:${a}`,
|
||||
`shell:download_helm_binary:${platform}:${a}`,
|
||||
`shell:download_kubectl_binary:${platform}:${a}`,
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
/***/
|
||||
const gruntConfig = {};
|
||||
|
||||
gruntConfig.env = {
|
||||
dev: {
|
||||
NODE_ENV: 'development',
|
||||
},
|
||||
prod: {
|
||||
NODE_ENV: 'production',
|
||||
},
|
||||
testing: {
|
||||
NODE_ENV: 'testing',
|
||||
},
|
||||
};
|
||||
|
||||
gruntConfig.webpack = {
|
||||
dev: webpackDevConfig,
|
||||
devWatch: Object.assign({ watch: true }, webpackDevConfig),
|
||||
prod: webpackProdConfig,
|
||||
testing: webpackTestingConfig,
|
||||
};
|
||||
|
||||
gruntConfig.clean = {
|
||||
server: ['<%= root %>/portainer'],
|
||||
client: ['<%= distdir %>/*'],
|
||||
all: ['<%= root %>/*'],
|
||||
};
|
||||
|
||||
gruntConfig.shell = {
|
||||
build_binary: { command: shell_build_binary },
|
||||
build_binary_azuredevops: { command: shell_build_binary_azuredevops },
|
||||
download_docker_binary: { command: shell_download_docker_binary },
|
||||
download_helm_binary: { command: shell_download_helm_binary },
|
||||
download_kubectl_binary: { command: shell_download_kubectl_binary },
|
||||
download_docker_compose_binary: { command: shell_download_docker_compose_binary },
|
||||
run_container: { command: shell_run_container },
|
||||
run_localserver: { command: shell_run_localserver, options: { async: true } },
|
||||
install_yarndeps: { command: shell_install_yarndeps },
|
||||
storybook: { command: shell_storybook },
|
||||
};
|
||||
|
||||
function shell_storybook(env) {
|
||||
if (env === 'prod') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `
|
||||
yarn build-storybook
|
||||
`;
|
||||
}
|
||||
|
||||
function shell_build_binary(platform, arch) {
|
||||
const binfile = 'dist/portainer';
|
||||
if (platform === 'linux' || platform === 'darwin') {
|
||||
return `
|
||||
if [ -f ${binfile} ]; then
|
||||
echo "Portainer binary exists";
|
||||
else
|
||||
build/build_binary.sh ${platform} ${arch};
|
||||
fi
|
||||
`;
|
||||
}
|
||||
|
||||
// windows
|
||||
return `
|
||||
powershell -Command "& {if (Get-Item -Path ${binfile}.exe -ErrorAction:SilentlyContinue) {
|
||||
Write-Host "Portainer binary exists"
|
||||
} else {
|
||||
& ".\\build\\build_binary.ps1" -platform ${platform} -arch ${arch}
|
||||
}}"
|
||||
`;
|
||||
}
|
||||
|
||||
function shell_build_binary_azuredevops(platform, arch) {
|
||||
return `build/build_binary_azuredevops.sh ${platform} ${arch};`;
|
||||
}
|
||||
|
||||
function shell_run_container() {
|
||||
const portainerData = '${PORTAINER_DATA:-/tmp/portainer}';
|
||||
const portainerRoot = process.env.PORTAINER_PROJECT ? process.env.PORTAINER_PROJECT : process.env.PWD;
|
||||
const portainerFlags = '${PORTAINER_FLAGS:-}';
|
||||
|
||||
return `
|
||||
docker rm -f portainer
|
||||
docker run -d \
|
||||
-p 8000:8000 \
|
||||
-p 9000:9000 \
|
||||
-p 9443:9443 \
|
||||
-v ${portainerRoot}/dist:/app \
|
||||
-v ${portainerData}:/data \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:z \
|
||||
-v /var/run/docker.sock:/var/run/alternative.sock:z \
|
||||
-v /tmp:/tmp \
|
||||
--name portainer \
|
||||
portainer/base \
|
||||
/app/portainer ${portainerFlags}
|
||||
`;
|
||||
}
|
||||
|
||||
function shell_run_localserver() {
|
||||
return './dist/portainer';
|
||||
}
|
||||
|
||||
function shell_install_yarndeps() {
|
||||
return 'yarn';
|
||||
}
|
||||
|
||||
function shell_download_docker_binary(platform, arch) {
|
||||
const binaryVersion = '<%= binaries.dockerVersion %>';
|
||||
|
||||
return `
|
||||
if [ -f dist/docker ] || [ -f dist/docker.exe ]; then
|
||||
echo "docker binary exists";
|
||||
else
|
||||
build/download_docker_binary.sh ${platform} ${arch} ${binaryVersion};
|
||||
fi
|
||||
`;
|
||||
}
|
||||
|
||||
function shell_download_docker_compose_binary(platform, arch) {
|
||||
var binaryVersion = '<%= binaries.dockerComposePluginVersion %>';
|
||||
|
||||
return `
|
||||
if [ -f dist/docker-compose ] || [ -f dist/docker-compose.exe ]; then
|
||||
echo "docker compose binary exists";
|
||||
else
|
||||
build/download_docker_compose_binary.sh ${platform} ${arch} ${binaryVersion};
|
||||
fi
|
||||
`;
|
||||
}
|
||||
|
||||
function shell_download_helm_binary(platform, arch) {
|
||||
var binaryVersion = '<%= binaries.helmVersion %>';
|
||||
|
||||
return `
|
||||
if [ -f dist/helm ] || [ -f dist/helm.exe ]; then
|
||||
echo "helm binary exists";
|
||||
else
|
||||
build/download_helm_binary.sh ${platform} ${arch} ${binaryVersion};
|
||||
fi
|
||||
`;
|
||||
}
|
||||
|
||||
function shell_download_kubectl_binary(platform, arch) {
|
||||
var binaryVersion = '<%= binaries.kubectlVersion %>';
|
||||
|
||||
return `
|
||||
if [ -f dist/kubectl ] || [ -f dist/kubectl.exe ]; then
|
||||
echo "kubectl binary exists";
|
||||
else
|
||||
build/download_kubectl_binary.sh ${platform} ${arch} ${binaryVersion};
|
||||
fi
|
||||
`;
|
||||
}
|
||||
63
package.json
63
package.json
@@ -2,7 +2,7 @@
|
||||
"author": "Portainer.io",
|
||||
"name": "portainer",
|
||||
"homepage": "http://portainer.io",
|
||||
"version": "2.18.3",
|
||||
"version": "2.18.2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:portainer/portainer.git"
|
||||
@@ -17,52 +17,19 @@
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"build": "grunt clean:all && grunt build",
|
||||
"build:server": "grunt clean:server && grunt build:server",
|
||||
"build:client": "grunt clean:client && grunt build:client",
|
||||
"prebuild:docs": "go install github.com/swaggo/swag/cmd/swag@v1.8.11",
|
||||
"build:docs": "cd api && swag init -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 --markdownFiles ./",
|
||||
"validate:docs": "swagger2openapi --warnOnly api/docs/swagger.yaml -o api/docs/openapi.yaml && swagger-cli validate api/docs/openapi.yaml",
|
||||
"clean": "grunt clean:all",
|
||||
"start": "grunt start",
|
||||
"start:clean": "grunt clean:all && grunt start",
|
||||
"start:localserver": "grunt start:localserver",
|
||||
"start:server": "grunt clean:server && grunt start:server",
|
||||
"start:client": "grunt clean:client && grunt start:client",
|
||||
"dev:client": "grunt clean:client && webpack-dev-server --config=./webpack/webpack.develop.js",
|
||||
"dev:client:prod": "grunt clean:client && webpack-dev-server --config=./webpack/webpack.production.js",
|
||||
"dev:nodl": "grunt clean:server && grunt clean:client && grunt build:server && grunt start:client",
|
||||
"dev:extension": "grunt build && make local -f build/docker-extension/Makefile",
|
||||
"start:toolkit": "grunt start:toolkit",
|
||||
"build:server:offline": "cd ./api/cmd/portainer && CGO_ENABLED=0 go build --installsuffix cgo --ldflags '-s' && mv -f portainer ../../../dist/portainer",
|
||||
"clean:all": "grunt clean:all",
|
||||
"dev": "webpack-dev-server",
|
||||
"start": "webpack -w",
|
||||
"build": "webpack",
|
||||
"format": "prettier --loglevel warn --write \"**/*.{js,css,html,jsx,tsx,ts,json}\"",
|
||||
"lint": "yarn lint:client; yarn lint:server",
|
||||
"lint:server": "cd api && golangci-lint run -E exportloopref",
|
||||
"lint:client": "eslint --cache --fix './**/*.{js,jsx,ts,tsx}'",
|
||||
"lint:pr": "make lint-pr",
|
||||
"test": "yarn test:client; yarn test:server",
|
||||
"test:server": "cd api && go test ./...",
|
||||
"test:client": "jest --silent",
|
||||
"lint": "eslint --cache --fix './**/*.{js,jsx,ts,tsx}'",
|
||||
"test": "jest --silent",
|
||||
"sb": "yarn storybook",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook -o ./dist/storybook",
|
||||
"storybook:build": "build-storybook -o ./dist/storybook",
|
||||
"analyze-webpack": "webpack --config ./webpack/webpack.analyze.js"
|
||||
},
|
||||
"scriptsComments": {
|
||||
"build": "Build the entire app (backend/frontend) in development mode",
|
||||
"build:server": "Build the backend",
|
||||
"build:client": "Build the frontend (development mode)",
|
||||
"build:docs": "builds API docs",
|
||||
"clean": "Clean the entire dist folder",
|
||||
"start": "Build the entire app (backend/frontend) in development mode, run backend inside a container and start a watch process locally for the frontend files",
|
||||
"start:server": "Build the backend and run it inside a container",
|
||||
"start:toolkit": "Build the entire app (backend/frontend) in development mode, run backend locally and start a watch process for the frontend files",
|
||||
"clean:all": "Deprecated. Use the clean script instead",
|
||||
"build:server:offline": "Deprecated. Use the build:server script instead",
|
||||
"format": "Should be removed before pr is merged"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.4"
|
||||
"node": ">= 14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-js": "^2.0.0",
|
||||
@@ -218,24 +185,12 @@
|
||||
"eslint-plugin-regex": "^1.9.0",
|
||||
"eslint-plugin-storybook": "^0.6.3",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"grunt": "^1.4.1",
|
||||
"grunt-cli": "^1.4.3",
|
||||
"grunt-contrib-clean": "^2.0.0",
|
||||
"grunt-contrib-copy": "^1.0.0",
|
||||
"grunt-env": "^0.4.4",
|
||||
"grunt-filerev": "^2.3.1",
|
||||
"grunt-replace": "^1.0.1",
|
||||
"grunt-shell-spawn": "^0.4.0",
|
||||
"grunt-usemin": "^3.1.1",
|
||||
"grunt-webpack": "^5.0.0",
|
||||
"gruntify-eslint": "^3.1.0",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"husky": "4.2.5",
|
||||
"jest": "^27.4.3",
|
||||
"kubernetes-types": "^1.26.0",
|
||||
"lint-staged": ">=10",
|
||||
"load-grunt-tasks": "^3.5.2",
|
||||
"lodash-webpack-plugin": "^0.11.6",
|
||||
"mini-css-extract-plugin": "^2.5.3",
|
||||
"msw-storybook-addon": "^1.6.3",
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"description": "This file contains current tool versions",
|
||||
"go_version": "v1.17.6",
|
||||
"node_version": "12.x",
|
||||
"yarn_version": "1.x"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
module.exports = require('./webpack/webpack.develop');
|
||||
module.exports = require('./webpack/webpack.development');
|
||||
|
||||
Reference in New Issue
Block a user