mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge remote-tracking branch 'origin/main' into auto-capitalization-toggle
This commit is contained in:
@@ -19,6 +19,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: git fetch --force --tags
|
||||
- run: echo "Ref name ${{github.ref_name}}"
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '>=1.19.3'
|
||||
@@ -33,11 +34,11 @@ jobs:
|
||||
run: |
|
||||
mkdir ../../osxcross
|
||||
git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target
|
||||
- uses: goreleaser/goreleaser-action@v2
|
||||
- uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
|
||||
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
|
||||
|
||||
+20
-1
@@ -14,6 +14,9 @@ before:
|
||||
builds:
|
||||
- id: darwin-build
|
||||
binary: infisical
|
||||
ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
|
||||
flags:
|
||||
- -trimpath
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=/home/runner/work/osxcross/target/bin/o64-clang
|
||||
@@ -24,10 +27,14 @@ builds:
|
||||
- goos: darwin
|
||||
goarch: "386"
|
||||
dir: ./cli
|
||||
|
||||
- id: all-other-builds
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
binary: infisical
|
||||
ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
|
||||
flags:
|
||||
- -trimpath
|
||||
goos:
|
||||
- freebsd
|
||||
- linux
|
||||
@@ -65,8 +72,10 @@ release:
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}"
|
||||
name_template: "{{ incpatch .Version }}-devel"
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
@@ -80,6 +89,7 @@ changelog:
|
||||
# - infisical
|
||||
# dir: "{{ dir .ArtifactPath }}"
|
||||
# cmd: curl -F package=@{{ .ArtifactName }} https://{{ .Env.FURY_TOKEN }}@push.fury.io/infisical/
|
||||
|
||||
brews:
|
||||
- name: infisical
|
||||
tap:
|
||||
@@ -91,6 +101,13 @@ brews:
|
||||
folder: Formula
|
||||
homepage: "https://infisical.com"
|
||||
description: "The official Infisical CLI"
|
||||
install: |-
|
||||
bin.install "infisical"
|
||||
bash_completion.install "completions/infisical.bash" => "infisical"
|
||||
zsh_completion.install "completions/infisical.zsh" => "_infisical"
|
||||
fish_completion.install "completions/infisical.fish"
|
||||
man1.install "manpages/infisical.1.gz"
|
||||
|
||||
nfpms:
|
||||
- id: infisical
|
||||
package_name: infisical
|
||||
@@ -116,6 +133,7 @@ nfpms:
|
||||
dst: /usr/share/zsh/site-functions/_infisical
|
||||
- src: ./manpages/infisical.1.gz
|
||||
dst: /usr/share/man/man1/infisical.1.gz
|
||||
|
||||
scoop:
|
||||
bucket:
|
||||
owner: Infisical
|
||||
@@ -126,6 +144,7 @@ scoop:
|
||||
homepage: "https://infisical.com"
|
||||
description: "The official Infisical CLI"
|
||||
license: MIT
|
||||
|
||||
aurs:
|
||||
-
|
||||
name: infisical-bin
|
||||
|
||||
Generated
+125
-122
@@ -10,13 +10,13 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@godaddy/terminus": "^4.11.2",
|
||||
"@sentry/node": "^7.21.1",
|
||||
"@octokit/rest": "^19.0.5",
|
||||
"@sentry/node": "^7.14.0",
|
||||
"@sentry/tracing": "^7.19.0",
|
||||
"@sentry/tracing": "^7.21.1",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"axios": "^1.2.0",
|
||||
"@types/libsodium-wrappers": "^0.7.10",
|
||||
"await-to-js": "^3.0.0",
|
||||
"axios": "^1.1.3",
|
||||
"bcrypt": "^5.1.0",
|
||||
"bigint-conversion": "^2.2.2",
|
||||
"builder-pattern": "^2.2.0",
|
||||
@@ -32,9 +32,9 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"jsrp": "^0.2.4",
|
||||
"mongoose": "^6.7.3",
|
||||
"libsodium-wrappers": "^0.7.10",
|
||||
"lodash": "^4.17.21",
|
||||
"mongoose": "^6.7.2",
|
||||
"nodemailer": "^6.8.0",
|
||||
"posthog-node": "^2.2.2",
|
||||
"query-string": "^7.1.3",
|
||||
@@ -2838,6 +2838,19 @@
|
||||
"@maxmind/geoip2-node": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz",
|
||||
"integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.21.1",
|
||||
"@sentry/utils": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
@@ -2914,28 +2927,25 @@
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz",
|
||||
"integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/utils": "7.19.0",
|
||||
"@sentry/types": "7.21.1",
|
||||
"@sentry/utils": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/node/node_modules/@sentry/types": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz",
|
||||
"integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/node/node_modules/@sentry/utils": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz",
|
||||
"integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==",
|
||||
"node_modules/@sentry/node": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.21.1.tgz",
|
||||
"integrity": "sha512-B+p1nQHaFWdCCRVmvqlr/+vdQCI3mGLObucNfK2YC22IQZg7+3u6tEbxJ7umITIjeSSKgf7ZoZwCxL9VfkrNXg==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/core": "7.21.1",
|
||||
"@sentry/types": "7.21.1",
|
||||
"@sentry/utils": "7.21.1",
|
||||
"cookie": "^0.4.1",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"lru_map": "^0.3.3",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2943,46 +2953,53 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/tracing": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.19.0.tgz",
|
||||
"integrity": "sha512-SWY17M3TsgBePaGowUcSqBwaT0TJQzuNexVnLojuU0k6F57L9hubvP9zaoosoCfARXQ/3NypAFWnlJyf570rFQ==",
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.21.1.tgz",
|
||||
"integrity": "sha512-b1BTPsRaNQpohzegoz59KGuBl+To651vEq0vMS4tCzSyIdxkYso3JCrjDdEqW/2MliQYANNVrUai2bmwmU9h1g==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "7.19.0",
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/utils": "7.19.0",
|
||||
"@sentry/core": "7.21.1",
|
||||
"@sentry/types": "7.21.1",
|
||||
"@sentry/utils": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/tracing/node_modules/@sentry/core": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz",
|
||||
"integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==",
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz",
|
||||
"integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz",
|
||||
"integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/utils": "7.19.0",
|
||||
"@sentry/types": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/tracing/node_modules/@sentry/types": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz",
|
||||
"integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==",
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz",
|
||||
"integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/tracing/node_modules/@sentry/utils": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz",
|
||||
"integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==",
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz",
|
||||
"integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/types": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3767,9 +3784,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz",
|
||||
"integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.0.tgz",
|
||||
"integrity": "sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -4448,9 +4465,9 @@
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/cookiejar": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
|
||||
"integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
|
||||
"integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
@@ -7165,9 +7182,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.2.tgz",
|
||||
"integrity": "sha512-lrP2V5U1qhaf+z33fiIn7aYAZZ1fVDly+TkFRjTujNBF/FIHESATj2RbgAOSlWqv32fsZXkXejXzeVfjbv35Ow==",
|
||||
"version": "6.7.3",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.3.tgz",
|
||||
"integrity": "sha512-bLC2Pt6Vpoov+1kBYvQgJXG/2DWXbfIvfK4Gh68kCdYGh6CVO31YxYuIGz70hyGwX2g4DmSzbs5IA8Px2neMCQ==",
|
||||
"dependencies": {
|
||||
"bson": "^4.7.0",
|
||||
"kareem": "2.4.1",
|
||||
@@ -14289,6 +14306,16 @@
|
||||
"@maxmind/geoip2-node": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"@sentry/core": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.21.1.tgz",
|
||||
"integrity": "sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.21.1",
|
||||
"@sentry/utils": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
@@ -14344,80 +14371,56 @@
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
||||
},
|
||||
"@sentry/node": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz",
|
||||
"integrity": "sha512-yG7Tx32WqOkEHVotFLrumCcT9qlaSDTkFNZ+yLSvZXx74ifsE781DzBA9W7K7bBdYO3op+p2YdsOKzf3nPpAyQ==",
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.21.1.tgz",
|
||||
"integrity": "sha512-B+p1nQHaFWdCCRVmvqlr/+vdQCI3mGLObucNfK2YC22IQZg7+3u6tEbxJ7umITIjeSSKgf7ZoZwCxL9VfkrNXg==",
|
||||
"requires": {
|
||||
"@sentry/core": "7.19.0",
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/utils": "7.19.0",
|
||||
"@sentry/core": "7.21.1",
|
||||
"@sentry/types": "7.21.1",
|
||||
"@sentry/utils": "7.21.1",
|
||||
"cookie": "^0.4.1",
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"lru_map": "^0.3.3",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/core": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz",
|
||||
"integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/utils": "7.19.0",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz",
|
||||
"integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz",
|
||||
"integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.19.0",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sentry/tracing": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.19.0.tgz",
|
||||
"integrity": "sha512-SWY17M3TsgBePaGowUcSqBwaT0TJQzuNexVnLojuU0k6F57L9hubvP9zaoosoCfARXQ/3NypAFWnlJyf570rFQ==",
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.21.1.tgz",
|
||||
"integrity": "sha512-b1BTPsRaNQpohzegoz59KGuBl+To651vEq0vMS4tCzSyIdxkYso3JCrjDdEqW/2MliQYANNVrUai2bmwmU9h1g==",
|
||||
"requires": {
|
||||
"@sentry/core": "7.19.0",
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/utils": "7.19.0",
|
||||
"@sentry/core": "7.21.1",
|
||||
"@sentry/types": "7.21.1",
|
||||
"@sentry/utils": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz",
|
||||
"integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz",
|
||||
"integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz",
|
||||
"integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "7.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz",
|
||||
"integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.21.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/core": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz",
|
||||
"integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.19.0",
|
||||
"@sentry/utils": "7.19.0",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz",
|
||||
"integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz",
|
||||
"integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==",
|
||||
"requires": {
|
||||
"@sentry/types": "7.19.0",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@sinclair/typebox": {
|
||||
@@ -15051,9 +15054,9 @@
|
||||
"integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz",
|
||||
"integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.2.0.tgz",
|
||||
"integrity": "sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -15561,9 +15564,9 @@
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"cookiejar": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
|
||||
"integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
|
||||
"integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
@@ -17626,9 +17629,9 @@
|
||||
}
|
||||
},
|
||||
"mongoose": {
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.2.tgz",
|
||||
"integrity": "sha512-lrP2V5U1qhaf+z33fiIn7aYAZZ1fVDly+TkFRjTujNBF/FIHESATj2RbgAOSlWqv32fsZXkXejXzeVfjbv35Ow==",
|
||||
"version": "6.7.3",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.3.tgz",
|
||||
"integrity": "sha512-bLC2Pt6Vpoov+1kBYvQgJXG/2DWXbfIvfK4Gh68kCdYGh6CVO31YxYuIGz70hyGwX2g4DmSzbs5IA8Px2neMCQ==",
|
||||
"requires": {
|
||||
"bson": "^4.7.0",
|
||||
"kareem": "2.4.1",
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@godaddy/terminus": "^4.11.2",
|
||||
"@sentry/node": "^7.21.1",
|
||||
"@sentry/tracing": "^7.21.1",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"axios": "^1.2.0",
|
||||
"bigint-conversion": "^2.2.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.18.1",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"express-validator": "^6.14.2",
|
||||
"handlebars": "^4.7.7",
|
||||
"helmet": "^5.1.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsrp": "^0.2.4",
|
||||
"mongoose": "^6.7.3",
|
||||
"nodemailer": "^6.8.0",
|
||||
"posthog-node": "^2.1.0",
|
||||
"query-string": "^7.1.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"stripe": "^10.7.0",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"tweetnacl-util": "^0.15.1",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"name": "infisical-api",
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.js",
|
||||
|
||||
@@ -4,16 +4,21 @@ import jwt from 'jsonwebtoken';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import * as bigintConversion from 'bigint-conversion';
|
||||
const jsrp = require('jsrp');
|
||||
import { User } from '../../models';
|
||||
import { User, LoginSRPDetail } from '../../models';
|
||||
import { createToken, issueTokens, clearTokens } from '../../helpers/auth';
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT
|
||||
} from '../../variables';
|
||||
import {
|
||||
NODE_ENV,
|
||||
JWT_AUTH_LIFETIME,
|
||||
JWT_AUTH_SECRET,
|
||||
JWT_REFRESH_SECRET
|
||||
} from '../../config';
|
||||
import LoginSRPDetail from '../../models/LoginSRPDetail';
|
||||
import { BadRequestError } from '../../utils/errors';
|
||||
import { EELogService } from '../../ee/services';
|
||||
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
|
||||
|
||||
declare module 'jsonwebtoken' {
|
||||
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
||||
@@ -116,6 +121,18 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
secure: NODE_ENV === 'production' ? true : false
|
||||
});
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
});
|
||||
|
||||
loginAction && await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
||||
ipAddress: req.ip
|
||||
});
|
||||
|
||||
// return (access) token in response
|
||||
return res.status(200).send({
|
||||
token: tokens.token,
|
||||
@@ -159,6 +176,19 @@ export const logout = async (req: Request, res: Response) => {
|
||||
sameSite: 'strict',
|
||||
secure: NODE_ENV === 'production' ? true : false
|
||||
});
|
||||
|
||||
const logoutAction = await EELogService.createAction({
|
||||
name: ACTION_LOGOUT,
|
||||
userId: req.user._id
|
||||
});
|
||||
|
||||
logoutAction && await EELogService.createLog({
|
||||
userId: req.user._id,
|
||||
actions: [logoutAction],
|
||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
||||
ipAddress: req.ip
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
Sentry.setUser({ email: req.user.email });
|
||||
Sentry.captureException(err);
|
||||
|
||||
@@ -77,8 +77,6 @@ export const changeMembershipOrgRole = async (req: Request, res: Response) => {
|
||||
// change role for (target) organization membership with id
|
||||
// [membershipOrgId]
|
||||
|
||||
// TODO
|
||||
|
||||
let membershipToChangeRole;
|
||||
// try {
|
||||
// } catch (err) {
|
||||
|
||||
@@ -4,12 +4,11 @@ import crypto from 'crypto';
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const jsrp = require('jsrp');
|
||||
import * as bigintConversion from 'bigint-conversion';
|
||||
import { User, Token, BackupPrivateKey } from '../../models';
|
||||
import { User, Token, BackupPrivateKey, LoginSRPDetail } from '../../models';
|
||||
import { checkEmailVerification } from '../../helpers/signup';
|
||||
import { createToken } from '../../helpers/auth';
|
||||
import { sendMail } from '../../helpers/nodemailer';
|
||||
import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config';
|
||||
import LoginSRPDetail from '../../models/LoginSRPDetail';
|
||||
import { BadRequestError } from '../../utils/errors';
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import { SecretVersion } from '../../ee/models';
|
||||
import { BadRequestError } from '../../utils/errors';
|
||||
import _ from 'lodash';
|
||||
import { ABILITY_READ } from '../../variables/organization';
|
||||
import { ABILITY_READ, ABILITY_WRITE } from '../../variables/organization';
|
||||
|
||||
/**
|
||||
* Create new workspace environment named [environmentName] under workspace with id
|
||||
@@ -236,7 +236,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
|
||||
throw BadRequestError()
|
||||
}
|
||||
|
||||
const accessibleEnvironments: { name: string; slug: string; }[] = []
|
||||
const accessibleEnvironments: any = []
|
||||
const deniedPermission = workspacesUserIsMemberOf.deniedPermissions
|
||||
|
||||
const relatedWorkspace = await Workspace.findById(workspaceId)
|
||||
@@ -245,11 +245,15 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
|
||||
}
|
||||
relatedWorkspace.environments.forEach(environment => {
|
||||
const isReadBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_READ })
|
||||
// const isWriteBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_WRITE })
|
||||
const isWriteBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_WRITE })
|
||||
if (isReadBlocked) {
|
||||
return
|
||||
} else {
|
||||
accessibleEnvironments.push(environment)
|
||||
accessibleEnvironments.push({
|
||||
name: environment.name,
|
||||
slug: environment.slug,
|
||||
isWriteDenied: isWriteBlocked
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
||||
*/
|
||||
|
||||
const channel = getChannelFromUserAgent(req.headers['user-agent'])
|
||||
const { workspaceId, environment } = req.body;
|
||||
const { workspaceId, environment }: { workspaceId: string, environment: string } = req.body;
|
||||
|
||||
const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_WRITE)
|
||||
if (!hasAccess) {
|
||||
@@ -175,17 +175,17 @@ export const createSecrets = async (req: Request, res: Response) => {
|
||||
}))
|
||||
});
|
||||
|
||||
const addAction = await EELogService.createActionSecret({
|
||||
const addAction = await EELogService.createAction({
|
||||
name: ACTION_ADD_SECRETS,
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId,
|
||||
userId: req.user._id,
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: newSecrets.map((n) => n._id)
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
addAction && await EELogService.createLog({
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId,
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
actions: [addAction],
|
||||
channel,
|
||||
ipAddress: req.ip
|
||||
@@ -300,16 +300,16 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
|
||||
const channel = getChannelFromUserAgent(req.headers['user-agent'])
|
||||
|
||||
const readAction = await EELogService.createActionSecret({
|
||||
const readAction = await EELogService.createAction({
|
||||
name: ACTION_READ_SECRETS,
|
||||
userId: userId,
|
||||
workspaceId: workspaceId as string,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId as string),
|
||||
secretIds: secrets.map((n: any) => n._id)
|
||||
});
|
||||
|
||||
readAction && await EELogService.createLog({
|
||||
userId: userId,
|
||||
workspaceId: workspaceId as string,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId as string),
|
||||
actions: [readAction],
|
||||
channel,
|
||||
ipAddress: req.ip
|
||||
@@ -505,17 +505,17 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
||||
});
|
||||
}, 10000);
|
||||
|
||||
const updateAction = await EELogService.createActionSecret({
|
||||
const updateAction = await EELogService.createAction({
|
||||
name: ACTION_UPDATE_SECRETS,
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId: key,
|
||||
userId: req.user._id,
|
||||
workspaceId: new Types.ObjectId(key),
|
||||
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
updateAction && await EELogService.createLog({
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId: key,
|
||||
workspaceId: new Types.ObjectId(key),
|
||||
actions: [updateAction],
|
||||
channel,
|
||||
ipAddress: req.ip
|
||||
@@ -631,17 +631,17 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
||||
workspaceId: key
|
||||
})
|
||||
});
|
||||
const deleteAction = await EELogService.createActionSecret({
|
||||
const deleteAction = await EELogService.createAction({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId: key,
|
||||
userId: req.user._id,
|
||||
workspaceId: new Types.ObjectId(key),
|
||||
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
|
||||
});
|
||||
|
||||
// (EE) create (audit) log
|
||||
deleteAction && await EELogService.createLog({
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId: key,
|
||||
workspaceId: new Types.ObjectId(key),
|
||||
actions: [deleteAction],
|
||||
channel,
|
||||
ipAddress: req.ip
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
import {
|
||||
SALT_ROUNDS
|
||||
} from '../../config';
|
||||
import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions';
|
||||
import { ABILITY_READ } from '../../variables/organization';
|
||||
|
||||
/**
|
||||
* Return service token data associated with service token on request
|
||||
@@ -37,6 +39,11 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
||||
expiresIn
|
||||
} = req.body;
|
||||
|
||||
const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_READ)
|
||||
if (!hasAccess) {
|
||||
throw UnauthorizedRequestError({ message: "You do not have the necessary permission(s) perform this action" })
|
||||
}
|
||||
|
||||
const secret = crypto.randomBytes(16).toString('hex');
|
||||
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS);
|
||||
|
||||
@@ -100,4 +107,8 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
serviceTokenData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function UnauthorizedRequestError(arg0: { message: string; }) {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
|
||||
@@ -1,39 +1,40 @@
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { Types } from 'mongoose';
|
||||
import { SecretVersion, Action } from '../models';
|
||||
import { Action } from '../models';
|
||||
import {
|
||||
getLatestSecretVersionIds,
|
||||
getLatestNSecretSecretVersionIds
|
||||
} from '../helpers/secretVersion';
|
||||
import { ACTION_UPDATE_SECRETS } from '../../variables';
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
} from '../../variables';
|
||||
|
||||
/**
|
||||
* Create an (audit) action for secrets including
|
||||
* add, delete, update, and read actions.
|
||||
* Create an (audit) action for updating secrets
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of action
|
||||
* @param {ObjectId[]} obj.secretIds - ids of relevant secrets
|
||||
* @param {Types.ObjectId} obj.secretIds - ids of relevant secrets
|
||||
* @returns {Action} action - new action
|
||||
*/
|
||||
const createActionSecretHelper = async ({
|
||||
const createActionUpdateSecret = async ({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
}: {
|
||||
name: string;
|
||||
userId: string;
|
||||
workspaceId: string;
|
||||
userId: Types.ObjectId;
|
||||
workspaceId: Types.ObjectId;
|
||||
secretIds: Types.ObjectId[];
|
||||
}) => {
|
||||
|
||||
let action;
|
||||
let latestSecretVersions;
|
||||
try {
|
||||
if (name === ACTION_UPDATE_SECRETS) {
|
||||
// case: action is updating secrets
|
||||
// -> add old and new secret versions
|
||||
latestSecretVersions = (await getLatestNSecretSecretVersionIds({
|
||||
const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
|
||||
secretIds,
|
||||
n: 2
|
||||
}))
|
||||
@@ -41,17 +42,7 @@ const createActionSecretHelper = async ({
|
||||
oldSecretVersion: s.versions[0]._id,
|
||||
newSecretVersion: s.versions[1]._id
|
||||
}));
|
||||
} else {
|
||||
// case: action is adding, deleting, or reading secrets
|
||||
// -> add new secret versions
|
||||
latestSecretVersions = (await getLatestSecretVersionIds({
|
||||
secretIds
|
||||
}))
|
||||
.map((s) => ({
|
||||
newSecretVersion: s.versionId
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
action = await new Action({
|
||||
name,
|
||||
user: userId,
|
||||
@@ -64,10 +55,148 @@ const createActionSecretHelper = async ({
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
throw new Error('Failed to create update secret action');
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an (audit) action for creating, reading, and deleting
|
||||
* secrets
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of action
|
||||
* @param {Types.ObjectId} obj.secretIds - ids of relevant secrets
|
||||
* @returns {Action} action - new action
|
||||
*/
|
||||
const createActionSecret = async ({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
}: {
|
||||
name: string;
|
||||
userId: Types.ObjectId;
|
||||
workspaceId: Types.ObjectId;
|
||||
secretIds: Types.ObjectId[];
|
||||
}) => {
|
||||
let action;
|
||||
try {
|
||||
// case: action is adding, deleting, or reading secrets
|
||||
// -> add new secret versions
|
||||
const latestSecretVersions = (await getLatestSecretVersionIds({
|
||||
secretIds
|
||||
}))
|
||||
.map((s) => ({
|
||||
newSecretVersion: s.versionId
|
||||
}));
|
||||
|
||||
action = await new Action({
|
||||
name,
|
||||
user: userId,
|
||||
workspace: workspaceId,
|
||||
payload: {
|
||||
secretVersions: latestSecretVersions
|
||||
}
|
||||
}).save();
|
||||
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
throw new Error('Failed to create action create/read/delete secret action');
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an (audit) action for user with id [userId]
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of action
|
||||
* @param {String} obj.userId - id of user associated with action
|
||||
* @returns
|
||||
*/
|
||||
const createActionUser = ({
|
||||
name,
|
||||
userId
|
||||
}: {
|
||||
name: string;
|
||||
userId: Types.ObjectId;
|
||||
}) => {
|
||||
let action;
|
||||
try {
|
||||
action = new Action({
|
||||
name,
|
||||
user: userId
|
||||
}).save();
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
throw new Error('Failed to create user action');
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an (audit) action.
|
||||
* @param {Object} obj
|
||||
* @param {Object} obj.name - name of action
|
||||
* @param {Types.ObjectId} obj.userId - id of user associated with action
|
||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with action
|
||||
* @param {Types.ObjectId[]} obj.secretIds - ids of secrets associated with action
|
||||
*/
|
||||
const createActionHelper = async ({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds,
|
||||
}: {
|
||||
name: string;
|
||||
userId: Types.ObjectId;
|
||||
workspaceId?: Types.ObjectId;
|
||||
secretIds?: Types.ObjectId[];
|
||||
}) => {
|
||||
let action;
|
||||
try {
|
||||
switch (name) {
|
||||
case ACTION_LOGIN:
|
||||
case ACTION_LOGOUT:
|
||||
action = await createActionUser({
|
||||
name,
|
||||
userId
|
||||
});
|
||||
break;
|
||||
case ACTION_ADD_SECRETS:
|
||||
case ACTION_READ_SECRETS:
|
||||
case ACTION_DELETE_SECRETS:
|
||||
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
|
||||
action = await createActionSecret({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
});
|
||||
break;
|
||||
case ACTION_UPDATE_SECRETS:
|
||||
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
|
||||
action = await createActionUpdateSecret({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
});
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
throw new Error('Failed to create action');
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
export { createActionSecretHelper };
|
||||
export {
|
||||
createActionHelper
|
||||
};
|
||||
@@ -1,9 +1,19 @@
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { Types } from 'mongoose';
|
||||
import {
|
||||
Log,
|
||||
IAction
|
||||
} from '../models';
|
||||
|
||||
/**
|
||||
* Create an (audit) log
|
||||
* @param {Object} obj
|
||||
* @param {Types.ObjectId} obj.userId - id of user associated with the log
|
||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the log
|
||||
* @param {IAction[]} obj.actions - actions to include in log
|
||||
* @param {String} obj.channel - channel (web/cli/auto) associated with the log
|
||||
* @param {String} obj.ipAddress - ip address associated with the log
|
||||
* @returns {Log} log - new audit log
|
||||
*/
|
||||
const createLogHelper = async ({
|
||||
userId,
|
||||
workspaceId,
|
||||
@@ -11,8 +21,8 @@ const createLogHelper = async ({
|
||||
channel,
|
||||
ipAddress
|
||||
}: {
|
||||
userId: string;
|
||||
workspaceId: string;
|
||||
userId: Types.ObjectId;
|
||||
workspaceId?: Types.ObjectId;
|
||||
actions: IAction[];
|
||||
channel: string;
|
||||
ipAddress: string;
|
||||
@@ -21,7 +31,7 @@ const createLogHelper = async ({
|
||||
try {
|
||||
log = await new Log({
|
||||
user: userId,
|
||||
workspace: workspaceId,
|
||||
workspace: workspaceId ?? undefined,
|
||||
actionNames: actions.map((a) => a.name),
|
||||
actions,
|
||||
channel,
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { Schema, model, Types } from 'mongoose';
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS
|
||||
} from '../../variables';
|
||||
|
||||
export interface IAction {
|
||||
name: string;
|
||||
user?: Types.ObjectId,
|
||||
workspace?: Types.ObjectId,
|
||||
payload: {
|
||||
payload?: {
|
||||
secretVersions?: Types.ObjectId[]
|
||||
}
|
||||
}
|
||||
@@ -13,7 +21,15 @@ const actionSchema = new Schema<IAction>(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
enum: [
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS
|
||||
]
|
||||
},
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Schema, model, Types } from 'mongoose';
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
@@ -29,6 +31,8 @@ const logSchema = new Schema<ILog>(
|
||||
actionNames: {
|
||||
type: [String],
|
||||
enum: [
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { Types } from 'mongoose';
|
||||
import {
|
||||
Log,
|
||||
Action,
|
||||
IAction
|
||||
} from '../models';
|
||||
import {
|
||||
createLogHelper
|
||||
} from '../helpers/log';
|
||||
import {
|
||||
createActionSecretHelper
|
||||
createActionHelper
|
||||
} from '../helpers/action';
|
||||
import EELicenseService from './EELicenseService';
|
||||
|
||||
@@ -33,8 +31,8 @@ class EELogService {
|
||||
channel,
|
||||
ipAddress
|
||||
}: {
|
||||
userId: string;
|
||||
workspaceId: string;
|
||||
userId: Types.ObjectId;
|
||||
workspaceId?: Types.ObjectId;
|
||||
actions: IAction[];
|
||||
channel: string;
|
||||
ipAddress: string;
|
||||
@@ -50,26 +48,26 @@ class EELogService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an (audit) action for secrets including
|
||||
* add, delete, update, and read actions.
|
||||
* Create an (audit) action
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of action
|
||||
* @param {ObjectId[]} obj.secretIds - secret ids
|
||||
* @param {Types.ObjectId} obj.userId - id of user associated with the action
|
||||
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the action
|
||||
* @param {ObjectId[]} obj.secretIds - ids of secrets associated with the action
|
||||
* @returns {Action} action - new action
|
||||
*/
|
||||
static async createActionSecret({
|
||||
static async createAction({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
}: {
|
||||
name: string;
|
||||
userId: string;
|
||||
workspaceId: string;
|
||||
secretIds: Types.ObjectId[];
|
||||
userId: Types.ObjectId;
|
||||
workspaceId?: Types.ObjectId;
|
||||
secretIds?: Types.ObjectId[];
|
||||
}) {
|
||||
if (!EELicenseService.isLicenseValid) return null;
|
||||
return await createActionSecretHelper({
|
||||
return await createActionHelper({
|
||||
name,
|
||||
userId,
|
||||
workspaceId,
|
||||
|
||||
@@ -406,10 +406,10 @@ const v2PushSecrets = async ({
|
||||
secretIds: toDelete
|
||||
});
|
||||
|
||||
const deleteAction = await EELogService.createActionSecret({
|
||||
const deleteAction = await EELogService.createAction({
|
||||
name: ACTION_DELETE_SECRETS,
|
||||
userId,
|
||||
workspaceId,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(userId),
|
||||
secretIds: toDelete
|
||||
});
|
||||
|
||||
@@ -499,10 +499,10 @@ const v2PushSecrets = async ({
|
||||
})
|
||||
});
|
||||
|
||||
const updateAction = await EELogService.createActionSecret({
|
||||
const updateAction = await EELogService.createAction({
|
||||
name: ACTION_UPDATE_SECRETS,
|
||||
userId,
|
||||
workspaceId,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: toUpdate.map((u) => u._id)
|
||||
});
|
||||
|
||||
@@ -536,10 +536,10 @@ const v2PushSecrets = async ({
|
||||
})
|
||||
});
|
||||
|
||||
const addAction = await EELogService.createActionSecret({
|
||||
const addAction = await EELogService.createAction({
|
||||
name: ACTION_ADD_SECRETS,
|
||||
userId,
|
||||
workspaceId,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: newSecrets.map((n) => n._id)
|
||||
});
|
||||
addAction && actions.push(addAction);
|
||||
@@ -553,8 +553,8 @@ const v2PushSecrets = async ({
|
||||
// (EE) create (audit) log
|
||||
if (actions.length > 0) {
|
||||
await EELogService.createLog({
|
||||
userId,
|
||||
workspaceId,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
actions,
|
||||
channel,
|
||||
ipAddress
|
||||
@@ -645,16 +645,16 @@ const pullSecrets = async ({
|
||||
environment
|
||||
})
|
||||
|
||||
const readAction = await EELogService.createActionSecret({
|
||||
const readAction = await EELogService.createAction({
|
||||
name: ACTION_READ_SECRETS,
|
||||
userId,
|
||||
workspaceId,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
secretIds: secrets.map((n: any) => n._id)
|
||||
});
|
||||
|
||||
readAction && await EELogService.createLog({
|
||||
userId,
|
||||
workspaceId,
|
||||
userId: new Types.ObjectId(userId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
actions: [readAction],
|
||||
channel,
|
||||
ipAddress
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import mongoose, { Schema, model } from 'mongoose';
|
||||
|
||||
const LoginSRPDetailSchema = new Schema(
|
||||
{
|
||||
clientPublicKey: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
serverBInt: { type: mongoose.Schema.Types.Buffer },
|
||||
expireAt: { type: Date }
|
||||
}
|
||||
);
|
||||
|
||||
const LoginSRPDetail = model('LoginSRPDetail', LoginSRPDetailSchema);
|
||||
|
||||
// LoginSRPDetailSchema.index({ "expireAt": 1 }, { expireAfterSeconds: 0 });
|
||||
|
||||
export default LoginSRPDetail;
|
||||
@@ -16,6 +16,7 @@ import UserAction, { IUserAction } from './userAction';
|
||||
import Workspace, { IWorkspace } from './workspace';
|
||||
import ServiceTokenData, { IServiceTokenData } from './serviceTokenData';
|
||||
import APIKeyData, { IAPIKeyData } from './apiKeyData';
|
||||
import LoginSRPDetail, { ILoginSRPDetail } from './loginSRPDetail';
|
||||
|
||||
export {
|
||||
BackupPrivateKey,
|
||||
@@ -53,5 +54,7 @@ export {
|
||||
ServiceTokenData,
|
||||
IServiceTokenData,
|
||||
APIKeyData,
|
||||
IAPIKeyData
|
||||
IAPIKeyData,
|
||||
LoginSRPDetail,
|
||||
ILoginSRPDetail
|
||||
};
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import mongoose, { Schema, model, Types } from 'mongoose';
|
||||
|
||||
export interface ILoginSRPDetail {
|
||||
_id: Types.ObjectId;
|
||||
clientPublicKey: string;
|
||||
email: string;
|
||||
serverBInt: mongoose.Schema.Types.Buffer;
|
||||
expireAt: Date;
|
||||
}
|
||||
|
||||
const loginSRPDetailSchema = new Schema<ILoginSRPDetail>(
|
||||
{
|
||||
clientPublicKey: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true
|
||||
},
|
||||
serverBInt: { type: mongoose.Schema.Types.Buffer },
|
||||
expireAt: { type: Date }
|
||||
}
|
||||
);
|
||||
|
||||
const LoginSRPDetail = model('LoginSRPDetail', loginSRPDetailSchema);
|
||||
|
||||
export default LoginSRPDetail;
|
||||
@@ -12,6 +12,7 @@ export interface IUser {
|
||||
salt?: string;
|
||||
verifier?: string;
|
||||
refreshVersion?: number;
|
||||
seenIps: [string];
|
||||
}
|
||||
|
||||
const userSchema = new Schema<IUser>(
|
||||
@@ -54,7 +55,8 @@ const userSchema = new Schema<IUser>(
|
||||
type: Number,
|
||||
default: 0,
|
||||
select: false
|
||||
}
|
||||
},
|
||||
seenIps: [String]
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
|
||||
@@ -30,7 +30,7 @@ router.patch(
|
||||
'/:organizationId/memberships/:membershipId',
|
||||
param('organizationId').exists().trim(),
|
||||
param('membershipId').exists().trim(),
|
||||
body('role').exists().isString().trim().isIn([ADMIN, MEMBER]),
|
||||
body('role').exists().isString().trim().isIn([OWNER, ADMIN, MEMBER]),
|
||||
validateRequest,
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
const ACTION_LOGIN = 'login';
|
||||
const ACTION_LOGOUT = 'logout';
|
||||
const ACTION_ADD_SECRETS = 'addSecrets';
|
||||
const ACTION_DELETE_SECRETS = 'deleteSecrets';
|
||||
const ACTION_UPDATE_SECRETS = 'updateSecrets';
|
||||
const ACTION_READ_SECRETS = 'readSecrets';
|
||||
|
||||
export {
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
|
||||
@@ -35,6 +35,8 @@ import {
|
||||
import { SECRET_SHARED, SECRET_PERSONAL } from './secret';
|
||||
import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event';
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
@@ -75,6 +77,8 @@ export {
|
||||
INTEGRATION_FLYIO_API_URL,
|
||||
EVENT_PUSH_SECRETS,
|
||||
EVENT_PULL_SECRETS,
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package main
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/config"
|
||||
"github.com/go-resty/resty/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const USER_AGENT = "cli"
|
||||
@@ -144,3 +145,24 @@ func CallGetAllWorkSpacesUserBelongsTo(httpClient *resty.Client) (GetWorkSpacesR
|
||||
|
||||
return workSpacesResponse, nil
|
||||
}
|
||||
|
||||
func CallIsAuthenticated(httpClient *resty.Client) bool {
|
||||
var workSpacesResponse GetWorkSpacesResponse
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&workSpacesResponse).
|
||||
SetHeader("User-Agent", USER_AGENT).
|
||||
Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL))
|
||||
|
||||
log.Debugln(fmt.Errorf("CallIsAuthenticated: Unsuccessful response: [response=%v]", response))
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if response.IsError() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
@@ -56,7 +56,12 @@ var exportCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(envName)
|
||||
infisicalToken, err := cmd.Flags().GetString("token")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: envName, InfisicalToken: infisicalToken})
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to fetch secrets")
|
||||
}
|
||||
@@ -91,6 +96,7 @@ func init() {
|
||||
exportCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
exportCmd.Flags().StringP("format", "f", "dotenv", "Set the format of the output file (dotenv, json, csv)")
|
||||
exportCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
|
||||
exportCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
}
|
||||
|
||||
// Format according to the format flag
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
@@ -33,13 +33,13 @@ var loginCmd = &cobra.Command{
|
||||
PreRun: toggleDebug,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
|
||||
if err != nil && strings.Contains(err.Error(), "The specified item could not be found in the keyring") { // if the key can't be found allow them to override
|
||||
if err != nil && (strings.Contains(err.Error(), "The specified item could not be found in the keyring") || strings.Contains(err.Error(), "unable to get key from Keyring")) { // if the key can't be found allow them to override
|
||||
log.Debug(err)
|
||||
} else if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
if currentLoggedInUserDetails.IsUserLoggedIn {
|
||||
if currentLoggedInUserDetails.IsUserLoggedIn && !currentLoggedInUserDetails.LoginExpired { // if you are logged in but not expired
|
||||
shouldOverride, err := shouldOverrideLoginPrompt(currentLoggedInUserDetails.UserCredentials.Email)
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
@@ -101,6 +101,9 @@ var loginCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to write write to Infisical Config file. Please try again")
|
||||
}
|
||||
|
||||
// clear backed up secrets from prev account
|
||||
util.DeleteBackupSecrets()
|
||||
|
||||
color.Green("Nice! You are logged in as: %v", email)
|
||||
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
|
||||
+12
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/Infisical/infisical-merge/packages/util"
|
||||
"github.com/fatih/color"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -58,10 +59,15 @@ var runCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
if !util.IsSecretEnvironmentValid(envName) {
|
||||
util.PrintMessageAndExit("Invalid environment name passed. Environment names can only be prod, dev, test or staging")
|
||||
infisicalToken, err := cmd.Flags().GetString("token")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
// if !util.IsSecretEnvironmentValid(envName) {
|
||||
// util.PrintMessageAndExit("Invalid environment name passed. Environment names can only be prod, dev, test or staging")
|
||||
// }
|
||||
|
||||
secretOverriding, err := cmd.Flags().GetBool("secret-overriding")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
@@ -72,7 +78,8 @@ var runCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(envName)
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: envName, InfisicalToken: infisicalToken})
|
||||
|
||||
if err != nil {
|
||||
util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid")
|
||||
}
|
||||
@@ -140,6 +147,7 @@ var runCmd = &cobra.Command{
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(runCmd)
|
||||
runCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
|
||||
runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
runCmd.Flags().Bool("secret-overriding", true, "Prioritizes personal secrets, if any, with the same name over shared secrets")
|
||||
|
||||
+37
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
@@ -34,12 +34,17 @@ var secretsCmd = &cobra.Command{
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
infisicalToken, err := cmd.Flags().GetString("token")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
shouldExpandSecrets, err := cmd.Flags().GetBool("expand")
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(environmentName)
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken})
|
||||
if err != nil {
|
||||
util.HandleError(err)
|
||||
}
|
||||
@@ -111,7 +116,7 @@ var secretsSetCmd = &cobra.Command{
|
||||
plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
|
||||
|
||||
// pull current secrets
|
||||
secrets, err := util.GetAllEnvironmentVariables(environmentName)
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName})
|
||||
if err != nil {
|
||||
util.HandleError(err, "unable to retrieve secrets")
|
||||
}
|
||||
@@ -267,7 +272,7 @@ var secretsDeleteCmd = &cobra.Command{
|
||||
util.HandleError(err, "Unable to get local project details")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(environmentName)
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName})
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to fetch secrets")
|
||||
}
|
||||
@@ -309,30 +314,6 @@ var secretsDeleteCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
secretsCmd.AddCommand(secretsGetCmd)
|
||||
secretsGetCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
}
|
||||
|
||||
secretsCmd.AddCommand(secretsSetCmd)
|
||||
secretsSetCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
}
|
||||
|
||||
secretsCmd.AddCommand(secretsDeleteCmd)
|
||||
secretsDeleteCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
}
|
||||
|
||||
secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on")
|
||||
secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
rootCmd.AddCommand(secretsCmd)
|
||||
}
|
||||
|
||||
func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
environmentName, err := cmd.Flags().GetString("env")
|
||||
if err != nil {
|
||||
@@ -344,7 +325,12 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(environmentName)
|
||||
infisicalToken, err := cmd.Flags().GetString("token")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken})
|
||||
if err != nil {
|
||||
util.HandleError(err, "To fetch all secrets")
|
||||
}
|
||||
@@ -380,3 +366,25 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod
|
||||
|
||||
return secretMapByName
|
||||
}
|
||||
|
||||
func init() {
|
||||
secretsGetCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
secretsCmd.AddCommand(secretsGetCmd)
|
||||
|
||||
secretsCmd.AddCommand(secretsSetCmd)
|
||||
secretsSetCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
}
|
||||
|
||||
secretsCmd.AddCommand(secretsDeleteCmd)
|
||||
secretsDeleteCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
|
||||
util.RequireLogin()
|
||||
util.RequireLocalWorkspaceFile()
|
||||
}
|
||||
|
||||
secretsCmd.Flags().String("token", "", "Fetch secrets using the Infisical Token")
|
||||
secretsCmd.PersistentFlags().String("env", "dev", "Used to select the environment name on which actions should be taken on")
|
||||
secretsCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
|
||||
rootCmd.AddCommand(secretsCmd)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright (c) 2023 Infisical Inc.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
|
||||
@@ -34,7 +34,12 @@ type WorkspaceConfigFile struct {
|
||||
}
|
||||
|
||||
type SymmetricEncryptionResult struct {
|
||||
CipherText []byte
|
||||
Nonce []byte
|
||||
AuthTag []byte
|
||||
CipherText []byte `json:"CipherText"`
|
||||
Nonce []byte `json:"Nonce"`
|
||||
AuthTag []byte `json:"AuthTag"`
|
||||
}
|
||||
|
||||
type GetAllSecretsParameters struct {
|
||||
Environment string
|
||||
InfisicalToken string
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func CheckForUpdate() {
|
||||
@@ -42,5 +42,5 @@ func getLatestTag(repoOwner string, repoName string) (string, error) {
|
||||
|
||||
json.Unmarshal(body, &tags)
|
||||
|
||||
return tags[0].Name, nil
|
||||
return tags[0].Name[1:], nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -19,3 +20,11 @@ func WriteToFile(fileName string, dataToWrite []byte, filePerm os.FileMode) erro
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckIsConnectedToInternet() (ok bool) {
|
||||
_, err := http.Get("http://clients3.google.com/generate_204")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -11,5 +11,8 @@ const (
|
||||
KEYRING_SERVICE_NAME = "infisical"
|
||||
PERSONAL_SECRET_TYPE_NAME = "personal"
|
||||
SHARED_SECRET_TYPE_NAME = "shared"
|
||||
CLI_VERSION = "v0.2.8"
|
||||
)
|
||||
|
||||
var (
|
||||
CLI_VERSION = "devel"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/99designs/keyring"
|
||||
"github.com/Infisical/infisical-merge/packages/config"
|
||||
"github.com/Infisical/infisical-merge/packages/api"
|
||||
"github.com/Infisical/infisical-merge/packages/models"
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
@@ -87,17 +87,10 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
|
||||
SetAuthToken(userCreds.JTWToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
response, err := httpClient.
|
||||
R().
|
||||
Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
return LoggedInUserDetails{}, err
|
||||
}
|
||||
|
||||
if response.StatusCode() > 299 {
|
||||
isAuthenticated := api.CallIsAuthenticated(httpClient)
|
||||
if !isAuthenticated {
|
||||
return LoggedInUserDetails{
|
||||
IsUserLoggedIn: true,
|
||||
IsUserLoggedIn: true, // was logged in
|
||||
LoginExpired: true,
|
||||
UserCredentials: userCreds,
|
||||
}, nil
|
||||
|
||||
@@ -2,6 +2,8 @@ package util
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -97,13 +99,26 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work
|
||||
return plainTextSecrets, nil
|
||||
}
|
||||
|
||||
func GetAllEnvironmentVariables(envName string) ([]models.SingleEnvironmentVariable, error) {
|
||||
infisicalToken := os.Getenv(INFISICAL_TOKEN_NAME)
|
||||
func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models.SingleEnvironmentVariable, error) {
|
||||
var infisicalToken string
|
||||
if params.InfisicalToken == "" {
|
||||
infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME)
|
||||
} else {
|
||||
infisicalToken = params.InfisicalToken
|
||||
}
|
||||
|
||||
isConnected := CheckIsConnectedToInternet()
|
||||
var secretsToReturn []models.SingleEnvironmentVariable
|
||||
var errorToReturn error
|
||||
|
||||
if infisicalToken == "" {
|
||||
RequireLocalWorkspaceFile()
|
||||
RequireLogin()
|
||||
log.Debug("Trying to fetch secrets using logged in details")
|
||||
if isConnected {
|
||||
log.Debug("GetAllEnvironmentVariables: Connected to internet, checking logged in creds")
|
||||
RequireLocalWorkspaceFile()
|
||||
RequireLogin()
|
||||
}
|
||||
|
||||
log.Debug("GetAllEnvironmentVariables: Trying to fetch secrets using logged in details")
|
||||
|
||||
loggedInUserDetails, err := GetCurrentLoggedInUserDetails()
|
||||
if err != nil {
|
||||
@@ -115,13 +130,30 @@ func GetAllEnvironmentVariables(envName string) ([]models.SingleEnvironmentVaria
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secrets, err := GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, envName)
|
||||
return secrets, err
|
||||
secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, params.Environment)
|
||||
log.Debugf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", errorToReturn)
|
||||
|
||||
backupSecretsEncryptionKey := []byte(loggedInUserDetails.UserCredentials.PrivateKey)[0:32]
|
||||
if errorToReturn == nil {
|
||||
WriteBackupSecrets(workspaceFile.WorkspaceId, params.Environment, backupSecretsEncryptionKey, secretsToReturn)
|
||||
}
|
||||
|
||||
// only attempt to serve cached secrets if no internet connection and if at least one secret cached
|
||||
if !isConnected {
|
||||
backedSecrets, err := ReadBackupSecrets(workspaceFile.WorkspaceId, params.Environment, backupSecretsEncryptionKey)
|
||||
if len(backedSecrets) > 0 {
|
||||
PrintWarning("Unable to fetch latest secret(s) due to connection error, serving secrets from last successful fetch. For more info, run with --debug")
|
||||
secretsToReturn = backedSecrets
|
||||
errorToReturn = err
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Debug("Trying to fetch secrets using service token")
|
||||
return GetPlainTextSecretsViaServiceToken(infisicalToken)
|
||||
secretsToReturn, errorToReturn = GetPlainTextSecretsViaServiceToken(infisicalToken)
|
||||
}
|
||||
|
||||
return secretsToReturn, errorToReturn
|
||||
}
|
||||
|
||||
func getExpandedEnvVariable(secrets []models.SingleEnvironmentVariable, variableWeAreLookingFor string, hashMapOfCompleteVariables map[string]string, hashMapOfSelfRefs map[string]string) string {
|
||||
@@ -295,3 +327,100 @@ func GetPlainTextSecrets(key []byte, encryptedSecrets api.GetEncryptedSecretsV2R
|
||||
|
||||
return plainTextSecrets, nil
|
||||
}
|
||||
|
||||
func WriteBackupSecrets(workspace string, environment string, encryptionKey []byte, secrets []models.SingleEnvironmentVariable) error {
|
||||
fileName := fmt.Sprintf("secrets_%s_%s", workspace, environment)
|
||||
secrets_backup_folder_name := "secrets-backup"
|
||||
|
||||
_, fullConfigFileDirPath, err := GetFullConfigFilePath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("WriteBackupSecrets: unable to get full config folder path [err=%s]", err)
|
||||
}
|
||||
|
||||
// create secrets backup directory
|
||||
fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name)
|
||||
if _, err := os.Stat(fullPathToSecretsBackupFolder); errors.Is(err, os.ErrNotExist) {
|
||||
err := os.Mkdir(fullPathToSecretsBackupFolder, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var encryptedSecrets []models.SymmetricEncryptionResult
|
||||
for _, secret := range secrets {
|
||||
marshaledSecrets, _ := json.Marshal(secret)
|
||||
result, err := crypto.EncryptSymmetric(marshaledSecrets, encryptionKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encryptedSecrets = append(encryptedSecrets, result)
|
||||
}
|
||||
|
||||
listOfSecretsMarshalled, _ := json.Marshal(encryptedSecrets)
|
||||
err = os.WriteFile(fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName), listOfSecretsMarshalled, os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("WriteBackupSecrets: Unable to write backup secrets to file [err=%s]", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadBackupSecrets(workspace string, environment string, encryptionKey []byte) ([]models.SingleEnvironmentVariable, error) {
|
||||
fileName := fmt.Sprintf("secrets_%s_%s", workspace, environment)
|
||||
secrets_backup_folder_name := "secrets-backup"
|
||||
|
||||
_, fullConfigFileDirPath, err := GetFullConfigFilePath()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ReadBackupSecrets: unable to write config file because an error occurred when getting config file path [err=%s]", err)
|
||||
}
|
||||
|
||||
fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name)
|
||||
if _, err := os.Stat(fullPathToSecretsBackupFolder); errors.Is(err, os.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
encryptedBackupSecretsFilePath := fmt.Sprintf("%s/%s", fullPathToSecretsBackupFolder, fileName)
|
||||
|
||||
encryptedBackupSecretsAsBytes, err := os.ReadFile(encryptedBackupSecretsFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var listOfEncryptedBackupSecrets []models.SymmetricEncryptionResult
|
||||
|
||||
_ = json.Unmarshal(encryptedBackupSecretsAsBytes, &listOfEncryptedBackupSecrets)
|
||||
|
||||
var plainTextSecrets []models.SingleEnvironmentVariable
|
||||
for _, encryptedSecret := range listOfEncryptedBackupSecrets {
|
||||
result, err := crypto.DecryptSymmetric(encryptionKey, encryptedSecret.CipherText, encryptedSecret.AuthTag, encryptedSecret.Nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var plainTextSecret models.SingleEnvironmentVariable
|
||||
|
||||
err = json.Unmarshal(result, &plainTextSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
plainTextSecrets = append(plainTextSecrets, plainTextSecret)
|
||||
}
|
||||
|
||||
return plainTextSecrets, nil
|
||||
|
||||
}
|
||||
|
||||
func DeleteBackupSecrets() error {
|
||||
secrets_backup_folder_name := "secrets-backup"
|
||||
|
||||
_, fullConfigFileDirPath, err := GetFullConfigFilePath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReadBackupSecrets: unable to write config file because an error occurred when getting config file path [err=%s]", err)
|
||||
}
|
||||
|
||||
fullPathToSecretsBackupFolder := fmt.Sprintf("%s/%s", fullConfigFileDirPath, secrets_backup_folder_name)
|
||||
|
||||
return os.RemoveAll(fullPathToSecretsBackupFolder)
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func fileKeyringPassphrasePrompt(prompt string) (string, error) {
|
||||
if password, ok := os.LookupEnv("INFISICAL_VAULT_FILE_PASSPHRASE"); ok {
|
||||
return password, nil
|
||||
} else {
|
||||
fmt.Println("You may set the `INFISICAL_VAULT_FILE_PASSPHRASE` environment variable to avoid typing password")
|
||||
fmt.Println("You may set the environment variable `INFISICAL_VAULT_FILE_PASSPHRASE` with your password to avoid typing it")
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "%s:", prompt)
|
||||
@@ -65,6 +65,7 @@ func fileKeyringPassphrasePrompt(prompt string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
@@ -13,4 +13,9 @@ If none of the available stores work for you, you can try using the `file` store
|
||||
If you are still experiencing trouble, please seek support.
|
||||
|
||||
[Learn more about vault command](./commands/vault)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I fetch secrets with Infisical if I am offline?">
|
||||
Yes. If you have previously retrieved secrets for a specific project and environment (such as dev, staging, or prod), the `run`/`secret` command will utilize the saved secrets, even when offline, on subsequent fetch attempts.
|
||||
|
||||
</Accordion>
|
||||
@@ -12,6 +12,7 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
|
||||
| [Docker](/integrations/platforms/docker) | Platform | Available |
|
||||
| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
|
||||
| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available |
|
||||
| [PM2](/integrations/platforms/pm2) | Platform | Available |
|
||||
| [Heroku](/integrations/cloud/heroku) | Cloud | Available |
|
||||
| [Vercel](/integrations/cloud/vercel) | Cloud | Available |
|
||||
| [Netlify](/integrations/cloud/netlify) | Cloud | Available |
|
||||
|
||||
@@ -1,45 +1,38 @@
|
||||
---
|
||||
title: "Docker Compose"
|
||||
description: "How to use Infisical to inject environment variables into container defined in your Docker Compose file."
|
||||
description: "How to use Infisical to inject environment variables into services defined in your Docker Compose file."
|
||||
---
|
||||
|
||||
The Docker Compose integration enables you to inject environment variables from Infisical into the containers defined in your compose file.
|
||||
Prerequisites:
|
||||
|
||||
## Add the CLI to your Dockerfile(s) start command
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
Follow the [guide to configure Infisical CLI](./docker) in your your Dockerfile first.
|
||||
## Configure the Infisical CLI for each service
|
||||
|
||||
## Generate Infisical Token
|
||||
Follow this [guide](./docker) to configure the Infisical CLI for each service that you wish to inject environment variables into; you'll have to update the Dockerfile of each service.
|
||||
|
||||
In order for Infisical CLI to authenticate and retrieve your project's secrets without exposing your login credentials, you must generate a Infisical Token.
|
||||
To learn how, visit [Infisical Token](../../getting-started/dashboard/token). Once you have generated the token, keep it handy.
|
||||
## Generate Infisical Tokens
|
||||
|
||||
<Info>
|
||||
If you have multiple services and they do not use the same secrets, you will
|
||||
have to generate a Infisical Token for each service.
|
||||
</Info>
|
||||
Generate a unique [Infisical Token](https://infisical.com/docs/getting-started/dashboard/token) for each service.
|
||||
|
||||
## Tell Docker Compose your Infisical Token
|
||||
## Add Infisical Tokens to your Docker Compose file
|
||||
|
||||
For each service you want to inject secrets into, set an environment variable called `INFISICAL_TOKEN` equal to a helpful identifier variable.
|
||||
This will ensure that you can set Infisical Tokens for multiple services.
|
||||
For each service you want to inject secrets into, set an environment variable called `INFISICAL_TOKEN` equal to a unique identifier variable.
|
||||
|
||||
For the example below, we have set `INFISICAL_TOKEN_FOR_WEB` and `INFISICAL_TOKEN_FOR_API` as the `INFISICAL_TOKEN` for the corresponding service.
|
||||
In the example below, we set `INFISICAL_TOKEN_FOR_WEB` and `INFISICAL_TOKEN_FOR_API` as the `INFISICAL_TOKEN` for the services.
|
||||
|
||||
```yaml
|
||||
# Example Docker Compose file
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
image: auledge-frontend
|
||||
container_name: auledge-frontend
|
||||
image: example-service-1
|
||||
environment:
|
||||
- INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_WEB}
|
||||
|
||||
api:
|
||||
build: .
|
||||
image: auledge-backend
|
||||
container_name: auledge-backend
|
||||
image: example-service-2
|
||||
environment:
|
||||
- INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_API}
|
||||
```
|
||||
|
||||
@@ -3,9 +3,11 @@ title: "Docker"
|
||||
description: "How to use Infisical to inject environment variables into a Docker container."
|
||||
---
|
||||
|
||||
Infisical can be used in a Dockerfile to inject environment variables into a Docker container.
|
||||
Prerequisites:
|
||||
|
||||
## Add the CLI to your Dockerfile
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
|
||||
## Add the Infisical CLI to your Dockerfile
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Alpine">
|
||||
@@ -32,32 +34,32 @@ Infisical can be used in a Dockerfile to inject environment variables into a Doc
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Modify your Dockerfile start command
|
||||
## Modify the start command in your Dockerfile
|
||||
|
||||
To make your Docker container consume Infisical secrets, you can start your application with Infisical.
|
||||
This will automatically pull the necessary secrets and make them available to your application as if they were natively exposed within the container.
|
||||
Starting your service with the Infisical CLI pulls your secrets from Infisical and injects them into your service.
|
||||
|
||||
```dockerfile
|
||||
CMD ["infisical", "run", "---", "[your application start command]"]
|
||||
```dockerfile
|
||||
CMD ["infisical", "run", "---", "[your service start command]"]
|
||||
|
||||
# example with single single command
|
||||
CMD ["infisical", "run", "---", "npm run start"]
|
||||
# example with single single command
|
||||
CMD ["infisical", "run", "---", "npm run start"]
|
||||
|
||||
# example with multiple commands
|
||||
CMD ["infisical", "run", "--command", "npm run start && ..."]
|
||||
```
|
||||
# example with multiple commands
|
||||
CMD ["infisical", "run", "--command", "npm run start && ..."]
|
||||
```
|
||||
|
||||
View more options for the `run` command [here](../../cli/commands/run)
|
||||
## Generate an Infisical Token
|
||||
|
||||
Head to your project settings in Infisical Cloud to generate an [Infisical Token](https://infisical.com/docs/getting-started/dashboard/token).
|
||||
|
||||
## Feed Docker your Infisical Token
|
||||
|
||||
The CLI looks out for an environment variable called `INFISICAL_TOKEN`. If the token is detected, the CLI will authenticate, retrieve, and inject the environment variables which the token is authorized for.
|
||||
|
||||
```bash
|
||||
docker run --env INFISICAL_TOKEN=[token]...
|
||||
```
|
||||
|
||||
## Generate an Infisical Token
|
||||
<Info>
|
||||
|
||||
[Generate an Infisical Token](../../getting-started/dashboard/token) and keep it handy.
|
||||
The Infisical CLI uses the detected `INFISICAL_TOKEN` environment variable to authenticate, retrieve, and inject the environment variables which the token is authorized for.
|
||||
|
||||
</Info>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "PM2"
|
||||
description: "How to use Infisical to inject environment variables and secrets with PM2 into a Node.js app"
|
||||
---
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||
- [Install the CLI](/cli/overview)
|
||||
|
||||
## Initialize Infisical for your Node.js app
|
||||
|
||||
```bash
|
||||
# navigate to the root of your of your project
|
||||
cd /path/to/project
|
||||
|
||||
# then initialize infisical
|
||||
infisical init
|
||||
```
|
||||
|
||||
## Create a bash or js script
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash infisical-run.sh
|
||||
infisical run -- npm start
|
||||
```
|
||||
|
||||
```js infisical-run.js
|
||||
const spawn = require("child_process").spawn;
|
||||
|
||||
const infisical = spawn("infisical", ["run", "--", "npm", "start"]);
|
||||
|
||||
infisical.stdout.on("data", (data) => console.log(`${data}`));
|
||||
infisical.stderr.on("data", (data) => console.error(`${data}`));
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Start your application as usual but with the script
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash infisical-run.sh
|
||||
pm2 start infisical-run.sh
|
||||
```
|
||||
|
||||
```bash infisical-run.js
|
||||
pm2 start infisical-run.js
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
+2
-1
@@ -209,7 +209,8 @@
|
||||
"pages": [
|
||||
"integrations/platforms/docker",
|
||||
"integrations/platforms/docker-compose",
|
||||
"integrations/platforms/kubernetes"
|
||||
"integrations/platforms/kubernetes",
|
||||
"integrations/platforms/pm2"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
Generated
+181
-92
@@ -14,6 +14,7 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
||||
"@fortawesome/react-fontawesome": "^0.1.19",
|
||||
"@headlessui/react": "^1.6.6",
|
||||
"@hookform/resolvers": "^2.9.10",
|
||||
"@radix-ui/react-accordion": "^1.1.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
||||
"@radix-ui/react-checkbox": "^1.0.1",
|
||||
@@ -51,6 +52,7 @@
|
||||
"react-code-input": "^3.10.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
"react-hook-form": "^7.43.0",
|
||||
"react-i18next": "^12.1.1",
|
||||
"react-mailchimp-subscribe": "^2.1.3",
|
||||
"react-markdown": "^8.0.3",
|
||||
@@ -64,7 +66,8 @@
|
||||
"tweetnacl-util": "^0.15.1",
|
||||
"uuid": "^8.3.2",
|
||||
"uuidv4": "^6.2.13",
|
||||
"yaml": "^2.2.0"
|
||||
"yaml": "^2.2.0",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-essentials": "^7.0.0-beta.30",
|
||||
@@ -108,7 +111,6 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
|
||||
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.1.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
@@ -121,7 +123,6 @@
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
@@ -157,7 +158,6 @@
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
|
||||
"integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -166,7 +166,6 @@
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
|
||||
"integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.1.0",
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
@@ -196,7 +195,6 @@
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
@@ -212,14 +210,12 @@
|
||||
"node_modules/@babel/core/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/@babel/core/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -265,7 +261,6 @@
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
|
||||
"integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.20.5",
|
||||
"@babel/helper-validator-option": "^7.18.6",
|
||||
@@ -284,7 +279,6 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
@@ -293,7 +287,6 @@
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -301,8 +294,7 @@
|
||||
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.20.12",
|
||||
@@ -461,7 +453,6 @@
|
||||
"version": "7.20.11",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz",
|
||||
"integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-environment-visitor": "^7.18.9",
|
||||
"@babel/helper-module-imports": "^7.18.6",
|
||||
@@ -535,7 +526,6 @@
|
||||
"version": "7.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
|
||||
"integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.20.2"
|
||||
},
|
||||
@@ -586,7 +576,6 @@
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
|
||||
"integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -610,7 +599,6 @@
|
||||
"version": "7.20.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
|
||||
"integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.18.10",
|
||||
"@babel/traverse": "^7.20.5",
|
||||
@@ -2897,6 +2885,14 @@
|
||||
"react-dom": "^16 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/@hookform/resolvers": {
|
||||
"version": "2.9.10",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.10.tgz",
|
||||
"integrity": "sha512-JIL1DgJIlH9yuxcNGtyhsWX/PgNltz+5Gr6+8SX9fhXc/hPbEIk6wPI82nhgvp3uUb6ZfAM5mqg/x7KR7NAb+A==",
|
||||
"peerDependencies": {
|
||||
"react-hook-form": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
|
||||
@@ -7005,8 +7001,7 @@
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.191",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
|
||||
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "3.0.10",
|
||||
@@ -8997,7 +8992,6 @@
|
||||
"version": "4.21.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
|
||||
"integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -10680,8 +10674,7 @@
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.284",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
|
||||
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
@@ -10977,7 +10970,6 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -12818,7 +12810,6 @@
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
@@ -14991,7 +14982,6 @@
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
@@ -15210,6 +15200,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"node_modules/lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
@@ -16183,6 +16178,11 @@
|
||||
"object-assign": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoclone": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
|
||||
"integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
@@ -16464,8 +16464,7 @@
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
|
||||
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg=="
|
||||
},
|
||||
"node_modules/normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
@@ -17625,6 +17624,11 @@
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/property-expr": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz",
|
||||
"integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA=="
|
||||
},
|
||||
"node_modules/property-information": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz",
|
||||
@@ -18142,6 +18146,21 @@
|
||||
"react-dom": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hook-form": {
|
||||
"version": "7.43.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.0.tgz",
|
||||
"integrity": "sha512-/rVEz7T0gLdSFwPqutJ1kn2e0sQNyb9ci/hmwEYr2YG0KF/LSuRLvNrf9QWJM+gj88CjDpDW5Bh/1AD7B2+z9Q==",
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/react-hook-form"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "12.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz",
|
||||
@@ -21168,6 +21187,11 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/toposort": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
|
||||
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
@@ -21404,7 +21428,7 @@
|
||||
"version": "4.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
||||
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -21730,7 +21754,6 @@
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
|
||||
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -22480,6 +22503,23 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/yup": {
|
||||
"version": "0.32.11",
|
||||
"resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz",
|
||||
"integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"@types/lodash": "^4.14.175",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoclone": "^0.2.1",
|
||||
"property-expr": "^2.0.4",
|
||||
"toposort": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -22487,7 +22527,6 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
|
||||
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/gen-mapping": "^0.1.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
@@ -22497,7 +22536,6 @@
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/set-array": "^1.0.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
@@ -22525,14 +22563,12 @@
|
||||
"@babel/compat-data": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
|
||||
"integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g=="
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
|
||||
"integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@ampproject/remapping": "^2.1.0",
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
@@ -22555,7 +22591,6 @@
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
@@ -22563,14 +22598,12 @@
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -22606,7 +22639,6 @@
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
|
||||
"integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/compat-data": "^7.20.5",
|
||||
"@babel/helper-validator-option": "^7.18.6",
|
||||
@@ -22619,7 +22651,6 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
@@ -22627,14 +22658,12 @@
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -22753,7 +22782,6 @@
|
||||
"version": "7.20.11",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz",
|
||||
"integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-environment-visitor": "^7.18.9",
|
||||
"@babel/helper-module-imports": "^7.18.6",
|
||||
@@ -22809,7 +22837,6 @@
|
||||
"version": "7.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
|
||||
"integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.20.2"
|
||||
}
|
||||
@@ -22844,8 +22871,7 @@
|
||||
"@babel/helper-validator-option": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
|
||||
"integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw=="
|
||||
},
|
||||
"@babel/helper-wrap-function": {
|
||||
"version": "7.20.5",
|
||||
@@ -22863,7 +22889,6 @@
|
||||
"version": "7.20.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
|
||||
"integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/template": "^7.18.10",
|
||||
"@babel/traverse": "^7.20.5",
|
||||
@@ -24061,7 +24086,8 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz",
|
||||
"integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"@emotion/utils": {
|
||||
"version": "1.2.0",
|
||||
@@ -24366,7 +24392,14 @@
|
||||
"@headlessui/react": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.6.6.tgz",
|
||||
"integrity": "sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q=="
|
||||
"integrity": "sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==",
|
||||
"requires": {}
|
||||
},
|
||||
"@hookform/resolvers": {
|
||||
"version": "2.9.10",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.10.tgz",
|
||||
"integrity": "sha512-JIL1DgJIlH9yuxcNGtyhsWX/PgNltz+5Gr6+8SX9fhXc/hPbEIk6wPI82nhgvp3uUb6ZfAM5mqg/x7KR7NAb+A==",
|
||||
"requires": {}
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.11.8",
|
||||
@@ -27340,8 +27373,7 @@
|
||||
"@types/lodash": {
|
||||
"version": "4.14.191",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
|
||||
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
|
||||
},
|
||||
"@types/mdast": {
|
||||
"version": "3.0.10",
|
||||
@@ -28016,7 +28048,8 @@
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"acorn-node": {
|
||||
"version": "1.8.2",
|
||||
@@ -28150,7 +28183,8 @@
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"ansi-align": {
|
||||
"version": "3.0.1",
|
||||
@@ -28444,7 +28478,8 @@
|
||||
"axios-auth-refresh": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.3.tgz",
|
||||
"integrity": "sha512-2IbDhJ/h6ddNBBnnzn1VFK/qx17pE9aVqiafB8rx5LVHsJ1HtFpUGkbXY7PzTG+8P9HJWcyA3fNZl9BikSuilg=="
|
||||
"integrity": "sha512-2IbDhJ/h6ddNBBnnzn1VFK/qx17pE9aVqiafB8rx5LVHsJ1HtFpUGkbXY7PzTG+8P9HJWcyA3fNZl9BikSuilg==",
|
||||
"requires": {}
|
||||
},
|
||||
"axobject-query": {
|
||||
"version": "3.1.1",
|
||||
@@ -28459,7 +28494,8 @@
|
||||
"version": "7.0.0-bridge.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
|
||||
"integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"babel-loader": {
|
||||
"version": "9.1.2",
|
||||
@@ -28838,7 +28874,6 @@
|
||||
"version": "4.21.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
|
||||
"integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001400",
|
||||
"electron-to-chromium": "^1.4.251",
|
||||
@@ -29668,7 +29703,8 @@
|
||||
"cva": {
|
||||
"version": "npm:[email protected]",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.4.0.tgz",
|
||||
"integrity": "sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ=="
|
||||
"integrity": "sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
@@ -30121,8 +30157,7 @@
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.284",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
|
||||
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
@@ -30368,8 +30403,7 @@
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3",
|
||||
@@ -30632,7 +30666,8 @@
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz",
|
||||
"integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"eslint-import-resolver-node": {
|
||||
"version": "0.3.7",
|
||||
@@ -30890,13 +30925,15 @@
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
|
||||
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"eslint-plugin-simple-import-sort": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz",
|
||||
"integrity": "sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"eslint-plugin-storybook": {
|
||||
"version": "0.6.10",
|
||||
@@ -31760,8 +31797,7 @@
|
||||
"gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
@@ -32325,7 +32361,8 @@
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
|
||||
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
@@ -33339,8 +33376,7 @@
|
||||
"json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.1.0",
|
||||
@@ -33508,6 +33544,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
@@ -33638,7 +33679,8 @@
|
||||
"version": "7.1.8",
|
||||
"resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.8.tgz",
|
||||
"integrity": "sha512-rRSa1aFmFnpDRFAhv5vIkWM4nPaoB9vnzIjuIKa1wGupfn2hdCNeaQHKpu4/muoc8n8J7yowjTP2oncA4/Rbgg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
@@ -34155,6 +34197,11 @@
|
||||
"object-assign": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"nanoclone": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
|
||||
"integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
@@ -34344,8 +34391,7 @@
|
||||
"node-releases": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
|
||||
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg=="
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
@@ -34935,7 +34981,8 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
|
||||
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"postcss-modules-local-by-default": {
|
||||
"version": "4.0.0",
|
||||
@@ -35044,7 +35091,8 @@
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.2.tgz",
|
||||
"integrity": "sha512-5RjUbWRe305pUpc48MosoIp6uxZvZxrM6GyOgsbGLTce+ehePKNm7ziW2dLG2air9aXbGuXlHVSQQw4Lbosq3w==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"pretty-error": {
|
||||
"version": "4.0.0",
|
||||
@@ -35132,6 +35180,11 @@
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"property-expr": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz",
|
||||
"integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA=="
|
||||
},
|
||||
"property-information": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz",
|
||||
@@ -35437,7 +35490,8 @@
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
|
||||
"integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"react-docgen": {
|
||||
"version": "5.4.3",
|
||||
@@ -35469,7 +35523,8 @@
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz",
|
||||
"integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "17.0.2",
|
||||
@@ -35521,6 +35576,12 @@
|
||||
"react-resizable": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"react-hook-form": {
|
||||
"version": "7.43.0",
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.0.tgz",
|
||||
"integrity": "sha512-/rVEz7T0gLdSFwPqutJ1kn2e0sQNyb9ci/hmwEYr2YG0KF/LSuRLvNrf9QWJM+gj88CjDpDW5Bh/1AD7B2+z9Q==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-i18next": {
|
||||
"version": "12.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz",
|
||||
@@ -35534,7 +35595,8 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.1.tgz",
|
||||
"integrity": "sha512-cxKSeFTf7jpSSVddm66sKdolG90qURAX3g1roTeaN6x0YEbtWc8JpmFN9+yIqLNH2uEkYerWLtJZIXRIFuBKrg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
@@ -35649,7 +35711,8 @@
|
||||
"react-table": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz",
|
||||
"integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA=="
|
||||
"integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==",
|
||||
"requires": {}
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
@@ -35805,7 +35868,8 @@
|
||||
"redux-thunk": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
|
||||
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q=="
|
||||
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
|
||||
"requires": {}
|
||||
},
|
||||
"regenerate": {
|
||||
"version": "1.4.2",
|
||||
@@ -37258,7 +37322,8 @@
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
|
||||
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"style-to-object": {
|
||||
"version": "0.3.0",
|
||||
@@ -37288,7 +37353,8 @@
|
||||
"styled-jsx": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz",
|
||||
"integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA=="
|
||||
"integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==",
|
||||
"requires": {}
|
||||
},
|
||||
"stylis": {
|
||||
"version": "4.0.13",
|
||||
@@ -37750,6 +37816,11 @@
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"dev": true
|
||||
},
|
||||
"toposort": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
|
||||
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
@@ -37928,7 +37999,7 @@
|
||||
"version": "4.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
|
||||
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"uc.micro": {
|
||||
"version": "1.0.6",
|
||||
@@ -38164,7 +38235,6 @@
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
|
||||
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escalade": "^3.1.1",
|
||||
"picocolors": "^1.0.0"
|
||||
@@ -38202,12 +38272,14 @@
|
||||
"use-isomorphic-layout-effect": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
|
||||
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA=="
|
||||
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
|
||||
"requires": {}
|
||||
},
|
||||
"use-memo-one": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
|
||||
"integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ=="
|
||||
"integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"use-sidecar": {
|
||||
"version": "1.1.2",
|
||||
@@ -38221,7 +38293,8 @@
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA=="
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"requires": {}
|
||||
},
|
||||
"util": {
|
||||
"version": "0.12.5",
|
||||
@@ -38412,7 +38485,8 @@
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
|
||||
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
@@ -38638,7 +38712,8 @@
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
|
||||
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
@@ -38698,6 +38773,20 @@
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"yup": {
|
||||
"version": "0.32.11",
|
||||
"resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz",
|
||||
"integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"@types/lodash": "^4.14.175",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoclone": "^0.2.1",
|
||||
"property-expr": "^2.0.4",
|
||||
"toposort": "^2.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
||||
"@fortawesome/react-fontawesome": "^0.1.19",
|
||||
"@headlessui/react": "^1.6.6",
|
||||
"@hookform/resolvers": "^2.9.10",
|
||||
"@radix-ui/react-accordion": "^1.1.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
||||
"@radix-ui/react-checkbox": "^1.0.1",
|
||||
@@ -58,6 +59,7 @@
|
||||
"react-code-input": "^3.10.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
"react-hook-form": "^7.43.0",
|
||||
"react-i18next": "^12.1.1",
|
||||
"react-mailchimp-subscribe": "^2.1.3",
|
||||
"react-markdown": "^8.0.3",
|
||||
@@ -71,7 +73,8 @@
|
||||
"tweetnacl-util": "^0.15.1",
|
||||
"uuid": "^8.3.2",
|
||||
"uuidv4": "^6.2.13",
|
||||
"yaml": "^2.2.0"
|
||||
"yaml": "^2.2.0",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-essentials": "^7.0.0-beta.30",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"head-title": "{{title}} | Infiscal",
|
||||
"head-title": "{{title}} | Infisical",
|
||||
"error_project-already-exists": "A project with this name already exists.",
|
||||
"no-mobile": " To use Infisical, please log in through a device with larger dimensions. ",
|
||||
"email": "Email",
|
||||
@@ -28,7 +28,7 @@
|
||||
"select-event": "Select an event",
|
||||
"event": "Event",
|
||||
"user": "User",
|
||||
"source": "Source",
|
||||
"time": "Time",
|
||||
"source": "Source",
|
||||
"time": "Time",
|
||||
"timestamp": "Timestamp"
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"add-new": "Add New Token",
|
||||
"add-dialog": {
|
||||
"title": "Add a service token for {{target}}",
|
||||
"description": "Specify the name, environment, and expiry period. When a token is generated, you will only be able to see it once before it disappears. Make sure to save it somewhere.",
|
||||
"description": "When a token is generated, you will only be able to see it once before it disappears. Make sure to save it somewhere.",
|
||||
"name": "Service Token Name",
|
||||
"add": "Add Service Token",
|
||||
"copy-service-token": "Copy your service token",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"title": "Project Members",
|
||||
"description": "This page shows the members of the selected project."
|
||||
"description": "This page shows the members of the selected project, and allows you to modify their permissions."
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"head-title": "{{title}} | Infiscal",
|
||||
"head-title": "{{title}} | Infisical",
|
||||
"error_project-already-exists": "Un projet avec ce nom existe déjà.",
|
||||
"no-mobile": " Pour utiliser Infisical, veuillez vous connecter avec un appareil avec des dimensions plus grandes. ",
|
||||
"email": "Email",
|
||||
@@ -28,7 +28,7 @@
|
||||
"select-event": "Sélectionnez un événement",
|
||||
"event": "Événement",
|
||||
"user": "Utilisateur",
|
||||
"source": "Source",
|
||||
"time": "Heure",
|
||||
"source": "Source",
|
||||
"time": "Heure",
|
||||
"timestamp": "Horodatage"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"head-title": "{{title}} | Infiscal",
|
||||
"head-title": "{{title}} | Infisical",
|
||||
"error_project-already-exists": "동일한 이름을 가진 프로젝트가 이미 존재해요.",
|
||||
"no-mobile": " Infisical을 사용하려면, 큰 화면을 가진 디바이스로 로그인하여 주세요.",
|
||||
"email": "메일",
|
||||
@@ -23,4 +23,4 @@
|
||||
"language": "언어",
|
||||
"search": "검색하기...",
|
||||
"note": "Note"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"head-title": "{{title}} | Infiscal",
|
||||
"head-title": "{{title}} | Infisical",
|
||||
"error_project-already-exists": "Já exite um projeto com este nome.",
|
||||
"no-mobile": "Para usar o Infisical, faça o login através de um dispositivo com dimensões maiores.",
|
||||
"email": "Email",
|
||||
|
||||
@@ -187,10 +187,10 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
});
|
||||
const userWorkspaces = orgUserProjects;
|
||||
if (
|
||||
userWorkspaces.length === 0 &&
|
||||
router.asPath !== '/noprojects' &&
|
||||
!router.asPath.includes('home') &&
|
||||
!router.asPath.includes('settings') ||
|
||||
(userWorkspaces.length === 0 &&
|
||||
router.asPath !== '/noprojects' &&
|
||||
!router.asPath.includes('home') &&
|
||||
!router.asPath.includes('settings')) ||
|
||||
router.asPath === '/dashboard/undefined'
|
||||
) {
|
||||
router.push('/noprojects');
|
||||
@@ -246,16 +246,15 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="fixed w-full md:block flex flex-col h-screen">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.2.2/cdn.js" defer />
|
||||
<div className="flex h-screen w-full flex-col overflow-x-hidden">
|
||||
<NavBarDashboard />
|
||||
<div className="flex flex-col md:flex-row flex-1">
|
||||
<aside className="bg-bunker-600 border-r border-mineshaft-500 w-full md:w-60 h-screen">
|
||||
<nav className="flex flex-col justify-between items-between h-full">
|
||||
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
||||
<aside className="w-full border-r border-mineshaft-500 bg-bunker-600 md:w-60">
|
||||
<nav className="items-between flex h-full flex-col justify-between">
|
||||
{/* <div className="py-6"></div> */}
|
||||
<div>
|
||||
<div className="flex justify-center w-full mt-[4.5rem] mb-6 bg-bunker-600 h-20 flex-col items-center px-4">
|
||||
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide">
|
||||
<div className="mt-6 mb-6 flex h-20 w-full flex-col items-center justify-center bg-bunker-600 px-4">
|
||||
<div className="ml-1 mb-1 self-start text-xs font-semibold tracking-wide text-gray-400">
|
||||
{t('nav:menu.project')}
|
||||
</div>
|
||||
{Object.keys(workspaceMapping).length > 0 ? (
|
||||
@@ -279,29 +278,29 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
<ul>
|
||||
{Object.keys(workspaceMapping).length > 0 &&
|
||||
menuItems.map(({ href, title, emoji }) => (
|
||||
<li className="mt-0.5 mx-2" key={title}>
|
||||
<li className="mx-2 mt-0.5" key={title}>
|
||||
{router.asPath.split('/')[1] === href.split('/')[1] &&
|
||||
(['project', 'billing', 'org', 'personal'].includes(
|
||||
router.asPath.split('/')[2]
|
||||
)
|
||||
? router.asPath.split('/')[2] === href.split('/')[2]
|
||||
: true) ? (
|
||||
<div className="flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10">
|
||||
<div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1" />
|
||||
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">
|
||||
<div className="relative flex cursor-pointer rounded bg-primary-50/10 px-0.5 py-2.5 text-sm text-white">
|
||||
<div className="absolute inset-0 top-0 my-1 ml-1 mr-1 w-1 rounded-xl bg-primary" />
|
||||
<p className="ml-4 mr-2 flex w-6 items-center justify-center text-lg">
|
||||
{emoji}
|
||||
</p>
|
||||
{title}
|
||||
</div>
|
||||
) : router.asPath === '/noprojects' ? (
|
||||
<div className="flex p-2.5 text-white text-sm rounded">
|
||||
<p className="w-10 flex items-center justify-center text-lg">{emoji}</p>
|
||||
<div className="flex rounded p-2.5 text-sm text-white">
|
||||
<p className="flex w-10 items-center justify-center text-lg">{emoji}</p>
|
||||
{title}
|
||||
</div>
|
||||
) : (
|
||||
<Link href={href}>
|
||||
<div className="flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5">
|
||||
<p className="w-10 flex items-center justify-center text-lg">
|
||||
<div className="flex cursor-pointer rounded p-2.5 text-sm text-white hover:bg-primary-50/5">
|
||||
<p className="flex w-10 items-center justify-center text-lg">
|
||||
{emoji}
|
||||
</p>
|
||||
{title}
|
||||
@@ -312,11 +311,11 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-full mt-40 mb-4 px-2">
|
||||
<div className="mt-40 mb-4 w-full px-2">
|
||||
{router.asPath.split('/')[1] === 'home' ? (
|
||||
<div className="flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10">
|
||||
<div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1" />
|
||||
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg">
|
||||
<div className="relative flex cursor-pointer rounded bg-primary-50/10 px-0.5 py-2.5 text-sm text-white">
|
||||
<div className="absolute inset-0 top-0 my-1 ml-1 mr-1 w-1 rounded-xl bg-primary" />
|
||||
<p className="ml-4 mr-2 flex w-6 items-center justify-center text-lg">
|
||||
<FontAwesomeIcon icon={faBookOpen} />
|
||||
</p>
|
||||
Infisical Guide
|
||||
@@ -336,8 +335,8 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
</div>
|
||||
) : (
|
||||
<Link href={`/home/${workspaceMapping[workspaceSelected as any]}`}>
|
||||
<div className="relative flex p-2.5 overflow-visible text-white h-10 text-sm rounded cursor-pointer bg-white/10 hover:bg-primary-50/[0.15] mt-max">
|
||||
<p className="w-10 flex items-center justify-center text-lg">
|
||||
<div className="mt-max relative flex h-10 cursor-pointer overflow-visible rounded bg-white/10 p-2.5 text-sm text-white hover:bg-primary-50/[0.15]">
|
||||
<p className="flex w-10 items-center justify-center text-lg">
|
||||
<FontAwesomeIcon icon={faBookOpen} />
|
||||
</p>
|
||||
Infisical Guide
|
||||
@@ -369,12 +368,12 @@ const Layout = ({ children }: LayoutProps) => {
|
||||
error={error}
|
||||
loading={loading}
|
||||
/>
|
||||
<main className="flex-1 bg-bunker-800">{children}</main>
|
||||
<main className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800">{children}</main>
|
||||
</div>
|
||||
</div>
|
||||
<div className="md:hidden bg-bunker-800 w-screen h-screen flex flex-col justify-center items-center">
|
||||
<FontAwesomeIcon icon={faMobile} className="text-gray-300 text-7xl mb-8" />
|
||||
<p className="text-gray-200 px-6 text-center text-lg max-w-sm">
|
||||
<div className="flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
|
||||
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
|
||||
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
|
||||
{` ${t('common:no-mobile')} `}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -42,15 +42,15 @@ const UpgradePlanModal = ({
|
||||
leaveFrom='opacity-100 scale-100'
|
||||
leaveTo='opacity-0 scale-95'
|
||||
>
|
||||
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-md bg-bunker border border-bunker-400 p-6 pt-5 text-left align-middle shadow-xl transition-all'>
|
||||
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-md bg-bunker border border-mineshaft-500 p-6 pt-5 text-left align-middle shadow-xl transition-all'>
|
||||
<Dialog.Title
|
||||
as='h3'
|
||||
className='text-lg font-medium leading-6 text-bunker-200'
|
||||
className='text-xl font-medium leading-6 text-primary'
|
||||
>
|
||||
Unleash Infisical's Full Power
|
||||
</Dialog.Title>
|
||||
<div className='mt-2'>
|
||||
<p className='text-sm text-bunker-300 mb-0.5'>
|
||||
<p className='text-sm text-bunker-300 mb-1'>
|
||||
{text}
|
||||
</p>
|
||||
<p className='text-sm text-bunker-300'>
|
||||
@@ -60,7 +60,7 @@ const UpgradePlanModal = ({
|
||||
<div className='mt-4'>
|
||||
<button
|
||||
type='button'
|
||||
className='inline-flex justify-center rounded-md border border-transparent bg-primary opacity-90 hover:opacity-100 px-4 py-2 text-sm font-medium text-black hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
||||
className='inline-flex justify-center rounded-md border border-transparent bg-primary opacity-80 hover:opacity-100 px-4 py-2 text-sm font-medium text-black hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
||||
onClick={() => router.push(`/settings/billing/${localStorage.getItem("projectData.id")}`)}
|
||||
>
|
||||
Upgrade Now
|
||||
|
||||
@@ -41,7 +41,6 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
|
||||
const onEnvCreateCB = async (env: Env) => {
|
||||
try {
|
||||
await onCreateEnv(env);
|
||||
@@ -71,14 +70,14 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row justify-between w-full">
|
||||
<div className="flex flex-col w-full">
|
||||
<p className="text-xl font-semibold mb-3">Project Environments</p>
|
||||
<p className="text-base text-gray-400 mb-4">
|
||||
<div className="flex w-full flex-row justify-between">
|
||||
<div className="flex w-full flex-col">
|
||||
<p className="mb-3 text-xl font-semibold">Project Environments</p>
|
||||
<p className="mb-4 text-base text-gray-400">
|
||||
Choose which environments will show up in your dashboard like development, staging,
|
||||
production
|
||||
</p>
|
||||
<p className="text-sm mr-1 text-gray-500 self-start">
|
||||
<p className="mr-1 self-start text-sm text-gray-500">
|
||||
Note: the text in slugs shows how these environmant should be accessed in CLI.
|
||||
</p>
|
||||
</div>
|
||||
@@ -86,10 +85,10 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
<Button
|
||||
text="Add New Env"
|
||||
onButtonPressed={() => {
|
||||
if (plan !== plans.starter || host !== "https://app.infisical.com") {
|
||||
handlePopUpOpen('createUpdateEnv')
|
||||
if (plan !== plans.starter || host !== 'https://app.infisical.com') {
|
||||
handlePopUpOpen('createUpdateEnv');
|
||||
} else {
|
||||
handlePopUpOpen('upgradePlan')
|
||||
handlePopUpOpen('upgradePlan');
|
||||
}
|
||||
}}
|
||||
color="mineshaft"
|
||||
@@ -98,32 +97,30 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="table-container w-full bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
|
||||
<div className="absolute rounded-t-md w-full h-12 bg-white/5" />
|
||||
<table className="w-full my-1">
|
||||
<div className="table-container relative mb-6 mt-1 w-full rounded-md border border-mineshaft-700 bg-bunker">
|
||||
<div className="absolute h-12 w-full rounded-t-md bg-white/5" />
|
||||
<table className="my-1 w-full">
|
||||
<thead className="text-bunker-300">
|
||||
<tr>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">Name</th>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">Slug</th>
|
||||
<th className="pl-6 pt-2.5 pb-2 text-left">Name</th>
|
||||
<th className="pl-6 pt-2.5 pb-2 text-left">Slug</th>
|
||||
<th aria-label="buttons" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.length > 0 ? (
|
||||
data.map(({ name, slug }) => (
|
||||
<tr key={name} className="bg-bunker-800 hover:bg-bunker-800/5 duration-100">
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{name}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">{slug}</td>
|
||||
<td className="py-2 border-mineshaft-700 border-t flex justify-end">
|
||||
<div className="hover:opacity-100 duration-200 flex items-center mr-2">
|
||||
<tr key={name} className="bg-bunker-800 duration-100 hover:bg-bunker-800/5">
|
||||
<td className="border-t border-mineshaft-700 py-2 pl-6 text-gray-300">{name}</td>
|
||||
<td className="border-t border-mineshaft-700 py-2 pl-6 text-gray-300">{slug}</td>
|
||||
<td className="flex justify-end border-t border-mineshaft-700 py-2">
|
||||
<div className="mr-2 flex items-center duration-200 hover:opacity-100">
|
||||
<Button
|
||||
onButtonPressed={() => {
|
||||
if (plan !== plans.starter || host !== "https://app.infisical.com") {
|
||||
handlePopUpOpen('createUpdateEnv', { name, slug })
|
||||
if (plan !== plans.starter || host !== 'https://app.infisical.com') {
|
||||
handlePopUpOpen('createUpdateEnv', { name, slug });
|
||||
} else {
|
||||
handlePopUpOpen('upgradePlan')
|
||||
handlePopUpOpen('upgradePlan');
|
||||
}
|
||||
}}
|
||||
color="mineshaft"
|
||||
@@ -131,13 +128,13 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
icon={faPencil}
|
||||
/>
|
||||
</div>
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center mr-6">
|
||||
<div className="mr-6 flex items-center opacity-50 duration-200 hover:opacity-100">
|
||||
<Button
|
||||
onButtonPressed={() => {
|
||||
if (plan !== plans.starter || host !== "https://app.infisical.com") {
|
||||
handlePopUpOpen('deleteEnv', { name, slug })
|
||||
if (plan !== plans.starter || host !== 'https://app.infisical.com') {
|
||||
handlePopUpOpen('deleteEnv', { name, slug });
|
||||
} else {
|
||||
handlePopUpOpen('upgradePlan')
|
||||
handlePopUpOpen('upgradePlan');
|
||||
}
|
||||
}}
|
||||
color="red"
|
||||
@@ -150,8 +147,8 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={4} className="text-center pt-7 pb-4 text-bunker-400">
|
||||
No environmants found
|
||||
<td colSpan={4} className="pt-7 pb-4 text-center text-bunker-400">
|
||||
No environments found
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
@@ -174,7 +171,7 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
|
||||
onCreateSubmit={onEnvCreateCB}
|
||||
onEditSubmit={onEnvUpdateCB}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onClose={() => handlePopUpClose('upgradePlan')}
|
||||
text="You can add custom environments if you switch to Infisical's Team plan."
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||
import { plans } from 'public/data/frequentConstants';
|
||||
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import { Select, SelectItem } from '@app/components/v2';
|
||||
import updateUserProjectPermission from '@app/ee/api/memberships/UpdateUserProjectPermission';
|
||||
import getOrganizationSubscriptions from '@app/pages/api/organization/GetOrgSubscription';
|
||||
import changeUserRoleInWorkspace from '@app/pages/api/workspace/changeUserRoleInWorkspace';
|
||||
import deleteUserFromWorkspace from '@app/pages/api/workspace/deleteUserFromWorkspace';
|
||||
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
|
||||
import getProjectInfo from '@app/pages/api/workspace/getProjectInfo';
|
||||
import uploadKeys from '@app/pages/api/workspace/uploadKeys';
|
||||
|
||||
import { decryptAssymmetric, encryptAssymmetric } from '../../utilities/cryptography/crypto';
|
||||
import guidGenerator from '../../utilities/randomId';
|
||||
import Button from '../buttons/Button';
|
||||
import UpgradePlanModal from '../dialog/UpgradePlan';
|
||||
|
||||
// const roles = ['admin', 'user'];
|
||||
// TODO: Set type for this
|
||||
type Props = {
|
||||
userData: any[];
|
||||
changeData: (users: any[]) => void;
|
||||
myUser: string;
|
||||
filter: string;
|
||||
};
|
||||
|
||||
type EnvironmentProps = {
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the component that shows the users of a certin project
|
||||
* #TODO: add the possibility of choosing and doing operations on multiple users.
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
const ProjectUsersTable = ({ userData, changeData, myUser, filter }: Props) => {
|
||||
const [roleSelected, setRoleSelected] = useState(
|
||||
Array(userData?.length).fill(userData.map((user) => user.role))
|
||||
);
|
||||
const host = window.location.origin;
|
||||
const router = useRouter();
|
||||
const [myRole, setMyRole] = useState('member');
|
||||
const [currentPlan, setCurrentPlan] = useState('');
|
||||
const [workspaceEnvs, setWorkspaceEnvs] = useState<EnvironmentProps[]>([]);
|
||||
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
|
||||
const { createNotification } = useNotificationContext();
|
||||
|
||||
const workspaceId = router.query.id as string;
|
||||
// Delete the row in the table (e.g. a user)
|
||||
// #TODO: Add a pop-up that warns you that the user is going to be deleted.
|
||||
const handleDelete = (membershipId: string, index: number) => {
|
||||
// setUserIdToBeDeleted(userId);
|
||||
// onClick();
|
||||
deleteUserFromWorkspace(membershipId);
|
||||
changeData(userData.filter((v, i) => i !== index));
|
||||
setRoleSelected([
|
||||
...roleSelected.slice(0, index),
|
||||
...roleSelected.slice(index + 1, userData?.length)
|
||||
]);
|
||||
};
|
||||
|
||||
// Update the rold of a certain user
|
||||
const handleRoleUpdate = (index: number, e: string) => {
|
||||
changeUserRoleInWorkspace(userData[index].membershipId, e.toLowerCase());
|
||||
changeData([
|
||||
...userData.slice(0, index),
|
||||
...[
|
||||
{
|
||||
key: userData[index].key,
|
||||
firstName: userData[index].firstName,
|
||||
lastName: userData[index].lastName,
|
||||
email: userData[index].email,
|
||||
role: e.toLocaleLowerCase(),
|
||||
status: userData[index].status,
|
||||
userId: userData[index].userId,
|
||||
membershipId: userData[index].membershipId,
|
||||
publicKey: userData[index].publicKey,
|
||||
deniedPermissions: userData[index].deniedPermissions
|
||||
}
|
||||
],
|
||||
...userData.slice(index + 1, userData?.length)
|
||||
]);
|
||||
createNotification({
|
||||
text: `Successfully changed user role.`,
|
||||
type: 'success'
|
||||
});
|
||||
};
|
||||
|
||||
const handlePermissionUpdate = (index: number, val: string, membershipId: string, slug: string ) => {
|
||||
let denials: { ability: string; environmentSlug: string; }[];
|
||||
if (val === "Read Only") {
|
||||
denials = [{
|
||||
ability: "write",
|
||||
environmentSlug: slug
|
||||
}];
|
||||
} else if (val === "No Access") {
|
||||
denials = [{
|
||||
ability: "write",
|
||||
environmentSlug: slug
|
||||
}, {
|
||||
ability: "read",
|
||||
environmentSlug: slug
|
||||
}];
|
||||
} else {
|
||||
denials = [];
|
||||
}
|
||||
|
||||
if (currentPlan !== plans.professional && host === 'https://app.infisical.com') {
|
||||
setIsUpgradeModalOpen(true);
|
||||
} else {
|
||||
const allDenials = userData[index].deniedPermissions.filter((perm: { ability: string; environmentSlug: string; }) => perm.environmentSlug !== slug).concat(denials);
|
||||
updateUserProjectPermission({ membershipId, denials: allDenials});
|
||||
changeData([
|
||||
...userData.slice(0, index),
|
||||
...[
|
||||
{
|
||||
key: userData[index].key,
|
||||
firstName: userData[index].firstName,
|
||||
lastName: userData[index].lastName,
|
||||
email: userData[index].email,
|
||||
role: userData[index].role,
|
||||
status: userData[index].status,
|
||||
userId: userData[index].userId,
|
||||
membershipId: userData[index].membershipId,
|
||||
publicKey: userData[index].publicKey,
|
||||
deniedPermissions: allDenials
|
||||
}
|
||||
],
|
||||
...userData.slice(index + 1, userData?.length)
|
||||
]);
|
||||
createNotification({
|
||||
text: `Successfully changed user permissions.`,
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setMyRole(userData.filter((user) => user.email === myUser)[0]?.role);
|
||||
(async () => {
|
||||
const result = await getProjectInfo({ projectId: workspaceId });
|
||||
setWorkspaceEnvs(result.environments);
|
||||
|
||||
const orgId = localStorage.getItem('orgData.id') as string;
|
||||
const subscriptions = await getOrganizationSubscriptions({
|
||||
orgId
|
||||
});
|
||||
if (subscriptions) {
|
||||
setCurrentPlan(subscriptions.data[0].plan.product)
|
||||
}
|
||||
})();
|
||||
}, [userData, myUser]);
|
||||
|
||||
const grantAccess = async (id: string, publicKey: string) => {
|
||||
const result = await getLatestFileKey({ workspaceId });
|
||||
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||
|
||||
// assymmetrically decrypt symmetric key with local private key
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: result.latestKey.encryptedKey,
|
||||
nonce: result.latestKey.nonce,
|
||||
publicKey: result.latestKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
const { ciphertext, nonce } = encryptAssymmetric({
|
||||
plaintext: key,
|
||||
publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
uploadKeys(workspaceId, id, ciphertext, nonce);
|
||||
router.reload();
|
||||
};
|
||||
|
||||
const closeUpgradeModal = () => {
|
||||
setIsUpgradeModalOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="table-container bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1 min-w-max">
|
||||
<div className="absolute rounded-t-md w-full h-[3.25rem] bg-white/5" />
|
||||
<UpgradePlanModal
|
||||
isOpen={isUpgradeModalOpen}
|
||||
onClose={closeUpgradeModal}
|
||||
text="You can change user permissions if you switch to Infisical's Professional plan."
|
||||
/>
|
||||
<table className="w-full my-0.5">
|
||||
<thead className="text-gray-400 text-sm font-light">
|
||||
<tr>
|
||||
<th className="text-left pl-4 py-3.5">NAME</th>
|
||||
<th className="text-left pl-4 py-3.5">EMAIL</th>
|
||||
<th className="text-left pl-6 pr-10 py-3.5">ROLE</th>
|
||||
{workspaceEnvs.map(env => (
|
||||
<th key={guidGenerator()} className="text-left pl-8 py-1 max-w-min break-normal">
|
||||
<span>{env.name.toUpperCase()}<br/></span>
|
||||
{/* <span>PERMISSION</span> */}
|
||||
</th>
|
||||
))}
|
||||
<th aria-label="buttons" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{userData?.filter(
|
||||
(user) =>
|
||||
user.firstName?.toLowerCase().includes(filter) ||
|
||||
user.lastName?.toLowerCase().includes(filter) ||
|
||||
user.email?.toLowerCase().includes(filter)
|
||||
).length > 0 &&
|
||||
userData
|
||||
?.filter(
|
||||
(user) =>
|
||||
user.firstName?.toLowerCase().includes(filter) ||
|
||||
user.lastName?.toLowerCase().includes(filter) ||
|
||||
user.email?.toLowerCase().includes(filter)
|
||||
)
|
||||
.map((row, index) => (
|
||||
<tr key={guidGenerator()} className="bg-bunker-800 hover:bg-bunker-700">
|
||||
<td className="pl-4 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.firstName} {row.lastName}
|
||||
</td>
|
||||
<td className="pl-4 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.email}
|
||||
</td>
|
||||
<td className="pl-6 pr-10 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
<div className="justify-start h-full flex flex-row items-center">
|
||||
<Select
|
||||
className="w-36"
|
||||
// open={isOpen}
|
||||
onValueChange={(e) => handleRoleUpdate(index, e)}
|
||||
value={row.role}
|
||||
disabled={myRole !== 'admin' || myUser === row.email}
|
||||
// onOpenChange={(open) => setIsOpen(open)}
|
||||
>
|
||||
<SelectItem value="admin">Admin</SelectItem>
|
||||
<SelectItem value="member">Member</SelectItem>
|
||||
</Select>
|
||||
{row.status === 'completed' && myUser !== row.email && (
|
||||
<div className="border border-mineshaft-700 rounded-md bg-white/5 hover:bg-primary text-white hover:text-black duration-200">
|
||||
<Button
|
||||
onButtonPressed={() => grantAccess(row.userId, row.publicKey)}
|
||||
color="mineshaft"
|
||||
text="Grant Access"
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
{workspaceEnvs.map((env) => <td key={guidGenerator()} className="pl-8 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
<Select
|
||||
className="w-36"
|
||||
// open={isOpen}
|
||||
onValueChange={(val) => handlePermissionUpdate(index, val, row.membershipId, env.slug)}
|
||||
value={
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
(row.deniedPermissions.filter((perm: any) => perm.environmentSlug === env.slug).map((perm: {ability: string}) => perm.ability).includes("write") && row.deniedPermissions.filter((perm: any) => perm.environmentSlug === env.slug).map((perm: {ability: string}) => perm.ability).includes("read"))
|
||||
? "No Access"
|
||||
: (row.deniedPermissions.filter((perm: any) => perm.environmentSlug === env.slug).map((perm: {ability: string}) => perm.ability).includes("write") ? "Read Only" : "Read & Write")
|
||||
}
|
||||
disabled={myRole !== 'admin'}
|
||||
// onOpenChange={(open) => setIsOpen(open)}
|
||||
>
|
||||
<SelectItem value="No Access">No Access</SelectItem>
|
||||
<SelectItem value="Read Only">Read Only</SelectItem>
|
||||
<SelectItem value="Read & Write">Read & Write</SelectItem>
|
||||
</Select>
|
||||
</td>)}
|
||||
<td className="flex flex-row justify-end pl-8 pr-8 py-2 border-t border-0.5 border-mineshaft-700">
|
||||
{myUser !== row.email &&
|
||||
// row.role !== "admin" &&
|
||||
myRole !== 'member' ? (
|
||||
<div className="opacity-50 hover:opacity-100 flex items-center mt-0.5">
|
||||
<Button
|
||||
onButtonPressed={() => handleDelete(row.membershipId, index)}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-9 h-9" />
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectUsersTable;
|
||||
@@ -2,8 +2,8 @@ import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import changeUserRoleInOrganization from '@app/pages/api/organization/changeUserRoleInOrganization';
|
||||
import deleteUserFromOrganization from '@app/pages/api/organization/deleteUserFromOrganization';
|
||||
import changeUserRoleInWorkspace from '@app/pages/api/workspace/changeUserRoleInWorkspace';
|
||||
import deleteUserFromWorkspace from '@app/pages/api/workspace/deleteUserFromWorkspace';
|
||||
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
|
||||
import uploadKeys from '@app/pages/api/workspace/uploadKeys';
|
||||
@@ -55,9 +55,9 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
|
||||
]);
|
||||
};
|
||||
|
||||
// Update the rold of a certain user
|
||||
// Update the role of a certain user
|
||||
const handleRoleUpdate = (index: number, e: string) => {
|
||||
changeUserRoleInWorkspace(userData[index].membershipId, e);
|
||||
changeUserRoleInOrganization(String(localStorage.getItem("orgData.id")), userData[index].membershipId, e);
|
||||
changeData([
|
||||
...userData.slice(0, index),
|
||||
...[
|
||||
@@ -110,14 +110,14 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="table-container bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
|
||||
<div className="table-container bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1 min-w-max">
|
||||
<div className="absolute rounded-t-md w-full h-[3.25rem] bg-white/5" />
|
||||
<table className="w-full my-0.5">
|
||||
<thead className="text-gray-400 text-sm font-light">
|
||||
<tr>
|
||||
<th className="text-left pl-6 py-3.5">FIRST NAME</th>
|
||||
<th className="text-left pl-6 py-3.5">LAST NAME</th>
|
||||
<th className="text-left pl-6 py-3.5">EMAIL</th>
|
||||
<th className="text-left pl-4 py-3.5">NAME</th>
|
||||
<th className="text-left pl-4 py-3.5">EMAIL</th>
|
||||
<th className="text-left pl-6 pr-10 py-3.5">ROLE</th>
|
||||
<th aria-label="buttons" />
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -136,35 +136,30 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
|
||||
user.email?.toLowerCase().includes(filter)
|
||||
)
|
||||
.map((row, index) => (
|
||||
<tr key={guidGenerator()} className="bg-bunker-800 hover:bg-bunker-800/5">
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.firstName}
|
||||
<tr key={guidGenerator()} className="bg-bunker-800 hover:bg-bunker-700">
|
||||
<td className="pl-4 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.firstName} {row.lastName}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.lastName}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
<td className="pl-4 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.email}
|
||||
</td>
|
||||
<td className="flex flex-row justify-end pr-8 py-2 border-t border-0.5 border-mineshaft-700">
|
||||
<div className="justify-end mr-6 mx-2 w-full h-full flex flex-row items-center">
|
||||
{row.status === 'granted' &&
|
||||
<td className="pl-6 pr-10 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
<div className="justify-start h-full flex flex-row items-center">
|
||||
{row.status === 'accepted' &&
|
||||
((myRole === 'admin' && row.role !== 'owner') || myRole === 'owner') &&
|
||||
myUser !== row.email ? (
|
||||
(myUser !== row.email) ? (
|
||||
<Listbox
|
||||
isSelected={row.role}
|
||||
onChange={(e) => handleRoleUpdate(index, e)}
|
||||
data={
|
||||
myRole === 'owner' ? ['owner', 'admin', 'member'] : ['admin', 'member']
|
||||
}
|
||||
text="Role: "
|
||||
/>
|
||||
) : (
|
||||
row.status !== 'invited' &&
|
||||
row.status !== 'verified' && (
|
||||
<Listbox
|
||||
isSelected={row.role}
|
||||
text="Role: "
|
||||
onChange={() => {
|
||||
throw new Error('Function not implemented.');
|
||||
}}
|
||||
@@ -173,7 +168,7 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
|
||||
)
|
||||
)}
|
||||
{(row.status === 'invited' || row.status === 'verified') && (
|
||||
<div className="w-full pl-9">
|
||||
<div className="w-full pr-20">
|
||||
<Button
|
||||
onButtonPressed={() => deleteMembershipAndResendInvite(row.email)}
|
||||
color="mineshaft"
|
||||
@@ -193,10 +188,12 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="flex flex-row justify-end pl-8 pr-8 py-2 border-t border-0.5 border-mineshaft-700">
|
||||
{myUser !== row.email &&
|
||||
// row.role !== "admin" &&
|
||||
myRole !== 'member' ? (
|
||||
<div className="opacity-50 hover:opacity-100 flex items-center">
|
||||
<div className="opacity-50 hover:opacity-100 flex items-center mt-0.5">
|
||||
<Button
|
||||
onButtonPressed={() => handleDelete(row.membershipId, index)}
|
||||
color="red"
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { SetStateAction, useEffect, useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { WorkspaceEnv } from '@app/hooks/api/types';
|
||||
|
||||
import getSecretsForProject from '../utilities/secrets/getSecretsForProject';
|
||||
import { Modal, ModalContent } from '../v2';
|
||||
|
||||
interface Secrets {
|
||||
label: string;
|
||||
secret: string;
|
||||
}
|
||||
|
||||
interface CompareSecretsModalProps {
|
||||
compareModal: boolean;
|
||||
setCompareModal: React.Dispatch<SetStateAction<boolean>>;
|
||||
selectedEnv: WorkspaceEnv;
|
||||
workspaceEnvs: WorkspaceEnv[];
|
||||
workspaceId: string;
|
||||
currentSecret: {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
const CompareSecretsModal = ({
|
||||
compareModal,
|
||||
setCompareModal,
|
||||
selectedEnv,
|
||||
workspaceEnvs,
|
||||
workspaceId,
|
||||
currentSecret
|
||||
}: CompareSecretsModalProps) => {
|
||||
const [secrets, setSecrets] = useState<Secrets[]>([]);
|
||||
|
||||
const getEnvSecrets = async () => {
|
||||
const workspaceEnvironments = workspaceEnvs?.filter((env) => env !== selectedEnv);
|
||||
const newSecrets = await Promise.all(
|
||||
workspaceEnvironments.map(async (env) => {
|
||||
// #TODO: optimize this query somehow...
|
||||
const allSecrets = await getSecretsForProject({ env: env.slug, workspaceId });
|
||||
const secret =
|
||||
allSecrets.find((item) => item.key === currentSecret.key)?.value ?? 'Not found';
|
||||
return { label: env.name, secret };
|
||||
})
|
||||
);
|
||||
setSecrets([{ label: selectedEnv.name, secret: currentSecret.value }, ...newSecrets]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (compareModal) {
|
||||
(async () => {
|
||||
await getEnvSecrets();
|
||||
})();
|
||||
}
|
||||
}, [compareModal]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={compareModal} onOpenChange={setCompareModal}>
|
||||
<ModalContent title={currentSecret?.key} onOpenAutoFocus={(e) => e.preventDefault()}>
|
||||
<div className="space-y-4">
|
||||
{secrets.length === 0 ? (
|
||||
<div className="flex items-center bg-bunker-900 justify-center h-full py-4 rounded-md">
|
||||
<Image
|
||||
src="/images/loading/loading.gif"
|
||||
height={60}
|
||||
width={100}
|
||||
alt="infisical loading indicator"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
secrets.map((item) => (
|
||||
<div key={`${currentSecret.key}${item.label}`} className="space-y-0.5">
|
||||
<p className="text-sm text-bunker-300">{item.label}</p>
|
||||
<input
|
||||
defaultValue={item.secret}
|
||||
className="h-no-capture border border-mineshaft-500 text-md min-w-16 no-scrollbar::-webkit-scrollbar peer z-10 w-full rounded-md bg-bunker-800 px-2 py-1.5 font-mono text-gray-400 caret-white outline-none duration-200 no-scrollbar focus:ring-2 focus:ring-primary/50 "
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
export default CompareSecretsModal;
|
||||
@@ -6,10 +6,12 @@ import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import SecretVersionList from '@app/ee/components/SecretVersionList';
|
||||
import { WorkspaceEnv } from '@app/hooks/api/types';
|
||||
|
||||
import Button from '../basic/buttons/Button';
|
||||
import Toggle from '../basic/Toggle';
|
||||
import CommentField from './CommentField';
|
||||
import CompareSecretsModal from './CompareSecretsModal';
|
||||
import DashboardInputField from './DashboardInputField';
|
||||
import { DeleteActionButton } from './DeleteActionButton';
|
||||
import GenerateSecretMenu from './GenerateSecretMenu';
|
||||
@@ -40,6 +42,9 @@ interface SideBarProps {
|
||||
sharedToHide: string[];
|
||||
setSharedToHide: (values: string[]) => void;
|
||||
deleteRow: (props: DeleteRowFunctionProps) => void;
|
||||
workspaceEnvs: WorkspaceEnv[];
|
||||
selectedEnv: WorkspaceEnv;
|
||||
workspaceId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,15 +68,19 @@ const SideBar = ({
|
||||
modifyComment,
|
||||
buttonReady,
|
||||
savePush,
|
||||
deleteRow
|
||||
deleteRow,
|
||||
workspaceEnvs,
|
||||
selectedEnv,
|
||||
workspaceId
|
||||
}: SideBarProps) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [overrideEnabled, setOverrideEnabled] = useState(data[0].valueOverride !== undefined);
|
||||
const [overrideEnabled, setOverrideEnabled] = useState(data[0]?.valueOverride !== undefined);
|
||||
const [compareModal, setCompareModal] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="absolute border-l border-mineshaft-500 bg-bunker h-full w-96 top-14 right-0 z-40 shadow-xl flex flex-col justify-between">
|
||||
<div className="absolute border-l border-mineshaft-500 bg-bunker h-full w-96 right-0 z-40 shadow-xl flex flex-col justify-between">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Image
|
||||
@@ -171,20 +180,38 @@ const SideBar = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-start max-w-sm mt-4 px-4 mt-full mb-[4.7rem]">
|
||||
<Button
|
||||
text={String(t('common:save-changes'))}
|
||||
onButtonPressed={savePush}
|
||||
color="primary"
|
||||
size="md"
|
||||
active={buttonReady}
|
||||
textDisabled="Saved"
|
||||
/>
|
||||
<DeleteActionButton
|
||||
onSubmit={() =>
|
||||
deleteRow({ ids: data.map((secret) => secret.id), secretName: data[0]?.key })
|
||||
}
|
||||
/>
|
||||
<div className="mt-full mt-4 mb-4 flex max-w-sm flex-col justify-start space-y-2 px-4">
|
||||
<div>
|
||||
<Button
|
||||
text="Compare secret across environments"
|
||||
color="mineshaft"
|
||||
size="md"
|
||||
onButtonPressed={() => setCompareModal(true)}
|
||||
/>
|
||||
<CompareSecretsModal
|
||||
compareModal={compareModal}
|
||||
setCompareModal={setCompareModal}
|
||||
currentSecret={{ key: data[0]?.key, value: data[0]?.value }}
|
||||
workspaceEnvs={workspaceEnvs}
|
||||
selectedEnv={selectedEnv}
|
||||
workspaceId={workspaceId}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
text={String(t('common:save-changes'))}
|
||||
onButtonPressed={savePush}
|
||||
color="primary"
|
||||
size="md"
|
||||
active={buttonReady}
|
||||
textDisabled="Saved"
|
||||
/>
|
||||
<DeleteActionButton
|
||||
onSubmit={() =>
|
||||
deleteRow({ ids: data.map((secret) => secret.id), secretName: data[0]?.key })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -93,7 +93,7 @@ export default function Navbar() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute flex flex-row justify-between w-full bg-bunker text-white border-b border-mineshaft-500 z-50">
|
||||
<div className="flex flex-row justify-between w-full bg-bunker text-white border-b border-mineshaft-500 z-50">
|
||||
<div className="m-auto flex justify-start items-center mx-4">
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex justify-center py-4">
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useRouter } from 'next/router';
|
||||
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import { useWorkspace } from '@app/context';
|
||||
import getOrganization from '@app/pages/api/organization/GetOrg';
|
||||
import getProjectInfo from '@app/pages/api/workspace/getProjectInfo';
|
||||
|
||||
/**
|
||||
* This is the component at the top of almost every page.
|
||||
@@ -23,8 +23,9 @@ export default function NavHeader({
|
||||
isProjectRelated?: boolean;
|
||||
}): JSX.Element {
|
||||
const [orgName, setOrgName] = useState('');
|
||||
const [workspaceName, setWorkspaceName] = useState('');
|
||||
const router = useRouter();
|
||||
const projectId = String(router.query.id);
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -33,29 +34,24 @@ export default function NavHeader({
|
||||
orgId: orgId || ''
|
||||
});
|
||||
setOrgName(org.name);
|
||||
|
||||
const workspace = await getProjectInfo({
|
||||
projectId: String(router.query.id)
|
||||
});
|
||||
setWorkspaceName(workspace.name);
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
}, [projectId]);
|
||||
|
||||
return (
|
||||
<div className="pt-20 ml-6 flex flex-row items-center">
|
||||
<div className="bg-primary-900 h-6 w-6 rounded-md flex items-center justify-center text-mineshaft-100 mr-2">
|
||||
<div className="ml-6 flex flex-row items-center pt-8">
|
||||
<div className="mr-2 flex h-6 w-6 items-center justify-center rounded-md bg-primary-900 text-mineshaft-100">
|
||||
{orgName?.charAt(0)}
|
||||
</div>
|
||||
<div className="text-primary text-sm font-semibold">{orgName}</div>
|
||||
<div className="text-sm font-semibold text-primary">{orgName}</div>
|
||||
{isProjectRelated && (
|
||||
<>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" />
|
||||
<div className="font-semibold text-primary text-sm">{workspaceName}</div>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
|
||||
<div className="text-sm font-semibold text-primary">{currentWorkspace?.name}</div>
|
||||
</>
|
||||
)}
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" />
|
||||
<div className="text-gray-400 text-sm">{pageName}</div>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
|
||||
<div className="text-sm text-gray-400">{pageName}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ const POSTHOG_HOST =
|
||||
process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com";
|
||||
const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!;
|
||||
const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!;
|
||||
const {SITE_URL} = process.env
|
||||
|
||||
export {
|
||||
ENV,
|
||||
POSTHOG_API_KEY,
|
||||
POSTHOG_HOST,
|
||||
SITE_URL,
|
||||
STRIPE_PRODUCT_PRO,
|
||||
STRIPE_PRODUCT_STARTER
|
||||
};
|
||||
STRIPE_PRODUCT_STARTER};
|
||||
@@ -1,5 +1,7 @@
|
||||
import { jsPDF } from 'jspdf';
|
||||
|
||||
import { SITE_URL } from './config';
|
||||
|
||||
interface PDFProps {
|
||||
personalName: string;
|
||||
personalEmail: string;
|
||||
@@ -73,7 +75,7 @@ function generateBackupPDF({ personalName, personalEmail, generatedKey }: PDFPro
|
||||
doc.roundedRect(170, 488, 375, 35, 5, 5, 'F');
|
||||
doc.setTextColor(23, 23, 23);
|
||||
doc.setFontSize(14);
|
||||
doc.text('https://app.infisical.com/login', 180, 420);
|
||||
doc.text(`${SITE_URL}/login`, 180, 420);
|
||||
doc.text(personalEmail, 180, 465);
|
||||
doc.text(generatedKey, 180, 510);
|
||||
doc.text('Need help? Contact us at [email protected]', 32, 575);
|
||||
|
||||
@@ -29,8 +29,8 @@ interface SecretProps {
|
||||
|
||||
interface FunctionProps {
|
||||
env: string;
|
||||
setIsKeyAvailable: any;
|
||||
setData: any;
|
||||
setIsKeyAvailable?: any;
|
||||
setData?: any;
|
||||
workspaceId: string;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,9 @@ const getSecretsForProject = async ({
|
||||
|
||||
const latestKey = await getLatestFileKey({ workspaceId });
|
||||
// This is called isKeyAvailable but what it really means is if a person is able to create new key pairs
|
||||
setIsKeyAvailable(!latestKey ? encryptedSecrets.length === 0 : true);
|
||||
if (typeof setIsKeyAvailable === 'function') {
|
||||
setIsKeyAvailable(!latestKey ? encryptedSecrets.length === 0 : true);
|
||||
}
|
||||
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||
|
||||
@@ -131,7 +133,10 @@ const getSecretsForProject = async ({
|
||||
)[0]?.comment
|
||||
}));
|
||||
|
||||
setData(result);
|
||||
if (typeof setData === 'function') {
|
||||
setData(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.log('Something went wrong during accessing or decripting secrets.');
|
||||
|
||||
@@ -18,14 +18,15 @@ const buttonVariants = cva(
|
||||
'font-inter font-medium',
|
||||
'cursor-pointer',
|
||||
'inline-flex items-center justify-center',
|
||||
'relative'
|
||||
'relative',
|
||||
'whitespace-nowrap'
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
colorSchema: {
|
||||
primary: ['bg-primary', 'text-black', 'border-primary hover:bg-opacity-80'],
|
||||
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-opacity-80'],
|
||||
danger: ['bg-red', 'text-white', 'border-red']
|
||||
danger: ['bg-red', 'text-white', 'border-red hover:bg-opacity-90']
|
||||
},
|
||||
variant: {
|
||||
solid: '',
|
||||
@@ -33,7 +34,7 @@ const buttonVariants = cva(
|
||||
plain: ''
|
||||
},
|
||||
isDisabled: {
|
||||
true: 'bg-opacity-70 cursor-not-allowed',
|
||||
true: 'bg-mineshaft opacity-40 cursor-not-allowed',
|
||||
false: ''
|
||||
},
|
||||
isFullWidth: {
|
||||
@@ -45,9 +46,9 @@ const buttonVariants = cva(
|
||||
false: ''
|
||||
},
|
||||
size: {
|
||||
xs: ['text-xs', 'py-1', 'px-2'],
|
||||
sm: ['text-sm', 'py-2', 'px-4'],
|
||||
md: ['text-md', 'py-2', 'px-6'],
|
||||
xs: ['text-xs', 'py-1', 'px-1'],
|
||||
sm: ['text-sm', 'py-2', 'px-2'],
|
||||
md: ['text-md', 'py-2', 'px-4'],
|
||||
lg: ['text-lg', 'py-2', 'px-8']
|
||||
}
|
||||
},
|
||||
@@ -75,7 +76,7 @@ const buttonVariants = cva(
|
||||
{
|
||||
colorSchema: 'secondary',
|
||||
variant: 'plain',
|
||||
className: 'text-mineshaft'
|
||||
className: 'text-mineshaft-300'
|
||||
},
|
||||
{
|
||||
colorSchema: 'danger',
|
||||
@@ -101,7 +102,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
children,
|
||||
isDisabled = false,
|
||||
className = '',
|
||||
size = 'md',
|
||||
size = 'sm',
|
||||
variant = 'solid',
|
||||
isFullWidth,
|
||||
isRounded = true,
|
||||
@@ -144,9 +145,10 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
)}
|
||||
<span
|
||||
className={twMerge(
|
||||
'transition-all shrink-0 cursor-pointer',
|
||||
'shrink-0 cursor-pointer transition-all',
|
||||
loadingToggleClass,
|
||||
size === 'xs' ? 'mr-1' : 'mr-2'
|
||||
leftIcon && 'ml-2',
|
||||
size === 'xs' ? 'mr-1' : 'mr-3'
|
||||
)}
|
||||
>
|
||||
{leftIcon}
|
||||
@@ -154,7 +156,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
<span className={twMerge('transition-all', loadingToggleClass)}>{children}</span>
|
||||
<span
|
||||
className={twMerge(
|
||||
'transition-all shrink-0 cursor-pointer',
|
||||
'shrink-0 cursor-pointer transition-all',
|
||||
loadingToggleClass,
|
||||
size === 'xs' ? 'ml-1' : 'ml-2'
|
||||
)}
|
||||
|
||||
@@ -8,7 +8,7 @@ export type CardTitleProps = {
|
||||
};
|
||||
|
||||
export const CardTitle = ({ children, className, subTitle }: CardTitleProps) => (
|
||||
<div className={twMerge('p-6 pb-4 font-sans text-xl font-medium', className)}>
|
||||
<div className={twMerge('px-6 py-4 font-sans text-xl font-medium', className)}>
|
||||
{children}
|
||||
{subTitle && <p className="py-1 text-sm font-normal text-gray-400">{subTitle}</p>}
|
||||
</div>
|
||||
@@ -29,7 +29,7 @@ export type CardBodyProps = {
|
||||
};
|
||||
|
||||
export const CardBody = ({ children, className }: CardBodyProps) => (
|
||||
<div className={twMerge('p-6 pt-0', className)}>{children}</div>
|
||||
<div className={twMerge('px-6 pb-6 pt-0', className)}>{children}</div>
|
||||
);
|
||||
|
||||
export type CardProps = {
|
||||
@@ -47,7 +47,7 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(
|
||||
<div
|
||||
ref={ref}
|
||||
className={twMerge(
|
||||
'flex flex-col w-full font-inter text-gray-200 bg-mineshaft-700 shadow-md',
|
||||
'flex w-full flex-col bg-mineshaft-800 font-inter text-gray-200 shadow-md',
|
||||
isFullHeight && 'h-full',
|
||||
isRounded && 'rounded-md',
|
||||
isPlain && 'shadow-none',
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useToggle } from '@app/hooks';
|
||||
|
||||
import { Button } from '../Button';
|
||||
import { FormControl } from '../FormControl';
|
||||
import { Input } from '../Input';
|
||||
import { Modal, ModalContent } from '../Modal';
|
||||
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onClose?: () => void;
|
||||
onChange?: (isOpen: boolean) => void;
|
||||
deleteKey: string;
|
||||
title: string;
|
||||
onDeleteApproved: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const DeleteActionModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onChange,
|
||||
deleteKey,
|
||||
onDeleteApproved,
|
||||
title
|
||||
}: Props): JSX.Element => {
|
||||
const [inputData, setInputData] = useState('');
|
||||
const [isLoading, setIsLoading] = useToggle();
|
||||
|
||||
useEffect(() => {
|
||||
setInputData('');
|
||||
}, [isOpen]);
|
||||
|
||||
const onDelete = async () => {
|
||||
setIsLoading.on();
|
||||
try {
|
||||
await onDeleteApproved();
|
||||
} catch {
|
||||
setIsLoading.off();
|
||||
} finally {
|
||||
setIsLoading.off();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={(isOpenState) => {
|
||||
setInputData('');
|
||||
if (onChange) onChange(isOpenState);
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
title={title}
|
||||
subTitle="This action is irreversible!!!"
|
||||
footerContent={
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
colorSchema="danger"
|
||||
isDisabled={!(deleteKey === inputData) || isLoading}
|
||||
onClick={onDelete}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<Button variant="plain" colorSchema="secondary" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
onClose={onClose}
|
||||
>
|
||||
<form>
|
||||
<FormControl
|
||||
label={
|
||||
<div className="pb-2 text-sm">
|
||||
Type <span className="font-bold">{deleteKey}</span> to delete the resource
|
||||
</div>
|
||||
}
|
||||
className="mb-4"
|
||||
>
|
||||
<Input value={inputData} onChange={(e) => setInputData(e.target.value)} />
|
||||
</FormControl>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { DeleteActionModal } from './DeleteActionModal';
|
||||
@@ -11,7 +11,7 @@ export type FormLabelProps = {
|
||||
};
|
||||
|
||||
export const FormLabel = ({ id, label, isRequired }: FormLabelProps) => (
|
||||
<Label.Root className="text-mineshaft-300 text-sm font-medium block mb-1 ml-0.5" htmlFor={id}>
|
||||
<Label.Root className="mb-1 ml-0.5 block text-sm font-medium text-mineshaft-300" htmlFor={id}>
|
||||
{label}
|
||||
{isRequired && <span className="ml-1 text-red">*</span>}
|
||||
</Label.Root>
|
||||
@@ -25,7 +25,7 @@ export type FormHelperTextProps = {
|
||||
export const FormHelperText = ({ isError, text }: FormHelperTextProps) => (
|
||||
<div
|
||||
className={twMerge(
|
||||
'text-xs font-inter flex items-center opacity-90 text-mineshaft-300 mt-2',
|
||||
'mt-2 flex items-center font-inter text-xs text-mineshaft-300 opacity-90',
|
||||
isError && 'text-red-600'
|
||||
)}
|
||||
>
|
||||
@@ -46,6 +46,7 @@ export type FormControlProps = {
|
||||
helperText?: ReactNode;
|
||||
errorText?: ReactNode;
|
||||
children: JSX.Element;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const FormControl = ({
|
||||
@@ -55,10 +56,11 @@ export const FormControl = ({
|
||||
helperText,
|
||||
errorText,
|
||||
id,
|
||||
isError
|
||||
isError,
|
||||
className
|
||||
}: FormControlProps): JSX.Element => {
|
||||
return (
|
||||
<div>
|
||||
<div className={twMerge('mb-4', className)}>
|
||||
{typeof label === 'string' ? (
|
||||
<FormLabel label={label} isRequired={isRequired} id={id} />
|
||||
) : (
|
||||
|
||||
@@ -14,7 +14,7 @@ const iconButtonVariants = cva(
|
||||
[
|
||||
'button',
|
||||
'transition-all',
|
||||
'font-inter font-medium',
|
||||
'font-inter font-medium user-select-none',
|
||||
'cursor-pointer',
|
||||
'inline-flex items-center justify-center',
|
||||
'relative'
|
||||
@@ -22,9 +22,9 @@ const iconButtonVariants = cva(
|
||||
{
|
||||
variants: {
|
||||
colorSchema: {
|
||||
primary: ['bg-primary', 'text-black', 'border-primary hover:bg-opacity-80'],
|
||||
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-opacity-80'],
|
||||
danger: ['bg-red', 'text-white', 'border-red']
|
||||
primary: ['bg-primary', 'text-black', 'border-primary hover:opacity-80'],
|
||||
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-bunker-400'],
|
||||
danger: ['bg-[#973939] hover:bg-red', 'text-white', 'border-red']
|
||||
},
|
||||
variant: {
|
||||
solid: '',
|
||||
@@ -40,10 +40,10 @@ const iconButtonVariants = cva(
|
||||
false: ''
|
||||
},
|
||||
size: {
|
||||
xs: ['text-xs', 'py-1', 'px-2'],
|
||||
sm: ['text-sm', 'py-2', 'px-3'],
|
||||
md: ['text-md', 'py-3', 'px-4'],
|
||||
lg: ['text-lg', 'py-3', 'px-6']
|
||||
xs: ['text-xs', 'py-2', 'px-2'],
|
||||
sm: ['text-sm', 'py-3', 'px-3'],
|
||||
md: ['text-md', 'py-4', 'px-4'],
|
||||
lg: ['text-lg', 'py-6', 'px-6']
|
||||
}
|
||||
},
|
||||
compoundVariants: [
|
||||
@@ -97,7 +97,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
||||
ariaLabel,
|
||||
isDisabled = false,
|
||||
className,
|
||||
size = 'md',
|
||||
size = 'sm',
|
||||
variant = 'solid',
|
||||
isRounded = true,
|
||||
colorSchema = 'primary',
|
||||
|
||||
@@ -8,10 +8,11 @@ type Props = {
|
||||
isRequired?: boolean;
|
||||
leftIcon?: ReactNode;
|
||||
rightIcon?: ReactNode;
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
const inputVariants = cva(
|
||||
'input w-full py-2 text-gray-400 placeholder-gray-500 placeholder-opacity-50',
|
||||
'input w-full py-2 text-gray-400 placeholder-gray-500 placeholder-opacity-50 outline-none focus:ring-2',
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
@@ -46,7 +47,7 @@ const inputParentContainerVariants = cva('inline-flex font-inter items-center bo
|
||||
},
|
||||
isError: {
|
||||
true: 'border-red',
|
||||
false: 'border-mineshaft-400'
|
||||
false: 'border-mineshaft-500'
|
||||
},
|
||||
isFullWidth: {
|
||||
true: 'w-full',
|
||||
@@ -70,6 +71,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
className,
|
||||
isRounded = true,
|
||||
isFullWidth = true,
|
||||
isDisabled,
|
||||
isError = false,
|
||||
isRequired,
|
||||
leftIcon,
|
||||
@@ -87,9 +89,10 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
{...props}
|
||||
required={isRequired}
|
||||
ref={ref}
|
||||
disabled={isDisabled}
|
||||
className={twMerge(
|
||||
leftIcon ? 'pl-9' : 'pl-4',
|
||||
rightIcon ? 'pr-9' : 'pr-4',
|
||||
leftIcon ? 'pl-9' : 'pl-2.5',
|
||||
rightIcon ? 'pr-9' : 'pr-2.5',
|
||||
inputVariants({ className, isError, size, isRounded, variant })
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -11,33 +11,34 @@ export type ModalContentProps = DialogPrimitive.DialogContentProps & {
|
||||
title?: ReactNode;
|
||||
subTitle?: string;
|
||||
footerContent?: ReactNode;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
||||
({ children, title, subTitle, className, footerContent, ...props }, forwardedRef) => (
|
||||
({ children, title, subTitle, className, footerContent, onClose, ...props }, forwardedRef) => (
|
||||
<DialogPrimitive.Portal>
|
||||
<DialogPrimitive.Overlay
|
||||
className="fixed inset-0 w-full h-full animate-fadeIn"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }}
|
||||
className="fixed inset-0 h-full w-full animate-fadeIn"
|
||||
style={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
|
||||
/>
|
||||
<DialogPrimitive.Content {...props} ref={forwardedRef}>
|
||||
<Card
|
||||
isRounded
|
||||
className={twMerge(
|
||||
'fixed max-w-md animate-popIn top-1/2 left-1/2 -translate-y-2/4 -translate-x-2/4',
|
||||
'fixed top-1/2 left-1/2 max-w-lg -translate-y-2/4 -translate-x-2/4 animate-popIn drop-shadow-2xl z-50',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{title && <CardTitle subTitle={subTitle}>{title}</CardTitle>}
|
||||
<CardBody>{children}</CardBody>
|
||||
{footerContent && <CardFooter>{footerContent}</CardFooter>}
|
||||
<DialogPrimitive.Close aria-label="Close" asChild>
|
||||
<DialogPrimitive.Close aria-label="Close" asChild onClick={onClose}>
|
||||
<IconButton
|
||||
variant="plain"
|
||||
ariaLabel="close"
|
||||
className="absolute top-2.5 right-2.5 text-white hover:bg-gray-600 rounded"
|
||||
className="absolute top-3 right-3 rounded text-white hover:bg-gray-600"
|
||||
>
|
||||
<FontAwesomeIcon icon={faTimes} size="sm" className="cursor-pointer" />
|
||||
<FontAwesomeIcon icon={faTimes} size="lg" className="cursor-pointer" />
|
||||
</IconButton>
|
||||
</DialogPrimitive.Close>
|
||||
</Card>
|
||||
@@ -55,3 +56,6 @@ export const Modal = ({ isOpen, ...props }: ModalProps) => (
|
||||
|
||||
export const ModalTrigger = DialogPrimitive.Trigger;
|
||||
export type ModalTriggerProps = DialogPrimitive.DialogTriggerProps;
|
||||
|
||||
export const ModalClose = DialogPrimitive.Close;
|
||||
export type ModalCloseProps = DialogPrimitive.DialogCloseProps;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export type { ModalContentProps, ModalProps, ModalTriggerProps } from './Modal';
|
||||
export { Modal, ModalContent, ModalTrigger } from './Modal';
|
||||
export type { ModalCloseProps, ModalContentProps, ModalProps, ModalTriggerProps } from './Modal';
|
||||
export { Modal, ModalClose, ModalContent, ModalTrigger } from './Modal';
|
||||
|
||||
@@ -10,39 +10,45 @@ type Props = {
|
||||
children: ReactNode;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
dropdownContainerClassName?: string;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
export type SelectProps = SelectPrimitive.SelectProps & Props;
|
||||
|
||||
export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
({ children, placeholder, className, isLoading, ...props }, ref): JSX.Element => {
|
||||
(
|
||||
{ children, placeholder, className, isLoading, dropdownContainerClassName, ...props },
|
||||
ref
|
||||
): JSX.Element => {
|
||||
return (
|
||||
<SelectPrimitive.Root {...props}>
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={twMerge(
|
||||
`inline-flex items-center justify-between data-[placeholder]:text-gray-500
|
||||
px-4 py-2.5 font-inter text-sm text-white rounded-md bg-mineshaft-800`,
|
||||
`inline-flex items-center justify-between rounded-md
|
||||
bg-bunker-800 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-gray-500`,
|
||||
className
|
||||
)}
|
||||
>
|
||||
<SelectPrimitive.Value placeholder={placeholder} />
|
||||
<SelectPrimitive.Icon className="ml-3">
|
||||
<FontAwesomeIcon icon={faChevronDown} size="sm" />
|
||||
</SelectPrimitive.Icon>
|
||||
{!props.disabled && (
|
||||
<SelectPrimitive.Icon className="ml-3">
|
||||
<FontAwesomeIcon icon={faChevronDown} size="sm" />
|
||||
</SelectPrimitive.Icon>
|
||||
)}
|
||||
</SelectPrimitive.Trigger>
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
position="popper"
|
||||
sideOffset={5}
|
||||
className="overflow-hidden text-white rounded-md shadow-md font-inter bg-mineshaft-800"
|
||||
style={{ width: 'var(--radix-select-trigger-width)' }}
|
||||
className={twMerge(
|
||||
'relative left-4 top-1 overflow-hidden rounded-md bg-bunker-800 font-inter text-bunker-100 shadow-md',
|
||||
dropdownContainerClassName
|
||||
)}
|
||||
>
|
||||
<SelectPrimitive.ScrollUpButton>
|
||||
<FontAwesomeIcon icon={faChevronUp} size="sm" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
<SelectPrimitive.Viewport className="p-2">
|
||||
<SelectPrimitive.Viewport className="p-1.5">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center">
|
||||
<Spinner size="xs" />
|
||||
@@ -75,16 +81,16 @@ export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
|
||||
<SelectPrimitive.Item
|
||||
{...props}
|
||||
className={twMerge(
|
||||
`text-sm rounded-sm transition-all hover:text-primary
|
||||
hover:bg-mineshaft-700 flex items-center pl-10 pr-4 py-2 cursor-pointer
|
||||
select-none outline-none relative`,
|
||||
isSelected && 'text-primary',
|
||||
isDisabled && 'text-gray-600 hover:bg-transparent cursor-not-allowed hover:text-gray-600',
|
||||
`relative flex cursor-pointer
|
||||
select-none items-center rounded-md py-2 pl-10 pr-4 text-sm
|
||||
outline-none transition-all hover:bg-mineshaft-500`,
|
||||
isSelected && 'bg-primary',
|
||||
isDisabled && 'cursor-not-allowed text-gray-600 hover:bg-transparent hover:text-gray-600',
|
||||
className
|
||||
)}
|
||||
ref={forwardedRef}
|
||||
>
|
||||
<SelectPrimitive.ItemIndicator className="absolute left-2">
|
||||
<SelectPrimitive.ItemIndicator className="absolute left-3.5">
|
||||
<FontAwesomeIcon icon={faCheck} size="sm" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
<SelectPrimitive.ItemText className="">{children}</SelectPrimitive.ItemText>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { HTMLAttributes, ReactNode, TdHTMLAttributes } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export type TableContainerProps = {
|
||||
@@ -14,7 +14,7 @@ export const TableContainer = ({
|
||||
}: TableContainerProps): JSX.Element => (
|
||||
<div
|
||||
className={twMerge(
|
||||
'overflow-x-auto font-inter shadow-md relative border border-solid border-mineshaft-700',
|
||||
'relative w-full overflow-x-auto border border-solid border-mineshaft-700 font-inter shadow-md',
|
||||
isRounded && 'rounded-md',
|
||||
className
|
||||
)}
|
||||
@@ -32,7 +32,7 @@ export type TableProps = {
|
||||
export const Table = ({ children, className }: TableProps): JSX.Element => (
|
||||
<table
|
||||
className={twMerge(
|
||||
'bg-bunker-800 p-2 roun text-gray-300 w-full text-sm text-left rounded-md',
|
||||
'w-full rounded rounded-md bg-bunker-800 p-2 text-left text-sm text-gray-300',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -47,7 +47,7 @@ export type THeadProps = {
|
||||
};
|
||||
|
||||
export const THead = ({ children, className }: THeadProps): JSX.Element => (
|
||||
<thead className={twMerge('text-xs bg-bunker text-bunker-300 uppercase', className)}>
|
||||
<thead className={twMerge('bg-bunker text-xs uppercase text-bunker-300', className)}>
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
@@ -56,20 +56,22 @@ export const THead = ({ children, className }: THeadProps): JSX.Element => (
|
||||
export type TrProps = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
} & HTMLAttributes<HTMLTableRowElement>;
|
||||
|
||||
export const Tr = ({ children, className }: TrProps): JSX.Element => (
|
||||
<tr className={twMerge('border border-solid border-mineshaft-700', className)}>{children}</tr>
|
||||
export const Tr = ({ children, className, ...props }: TrProps): JSX.Element => (
|
||||
<tr className={twMerge('border border-solid border-mineshaft-700 hover:bg-bunker-700', className)} {...props}>
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
|
||||
// table head columns
|
||||
export type ThProps = {
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const Th = ({ children, className }: ThProps): JSX.Element => (
|
||||
<th className={twMerge('px-6 py-3 font-medium', className)}>{children}</th>
|
||||
<th className={twMerge('px-5 pt-4 pb-3.5 font-medium font-semibold bg-bunker-500', className)}>{children}</th>
|
||||
);
|
||||
|
||||
// table body
|
||||
@@ -84,10 +86,12 @@ export const TBody = ({ children, className }: TBodyProps): JSX.Element => (
|
||||
|
||||
// table body columns
|
||||
export type TdProps = {
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
} & TdHTMLAttributes<HTMLTableCellElement>;
|
||||
|
||||
export const Td = ({ children, className }: TdProps): JSX.Element => (
|
||||
<td className={twMerge('text-left px-6 py-3', className)}>{children}</td>
|
||||
export const Td = ({ children, className, ...props }: TdProps): JSX.Element => (
|
||||
<td className={twMerge('px-5 py-2 text-left', className)} {...props}>
|
||||
{children}
|
||||
</td>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Button } from '../Button';
|
||||
import { Modal, ModalClose, ModalContent } from '../Modal';
|
||||
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const UpgradePlanModal = ({ text, isOpen, onOpenChange }: Props): JSX.Element => (
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent
|
||||
title="Unleash Infisical's Full Power"
|
||||
footerContent={[
|
||||
<Link
|
||||
href={`/settings/billing/${localStorage.getItem('projectData.id') as string}`}
|
||||
key="upgrade-plan"
|
||||
>
|
||||
<Button className="mr-4">Upgrade Plan</Button>
|
||||
</Link>,
|
||||
<ModalClose asChild key="upgrade-plan-cancel">
|
||||
<Button colorSchema="secondary" variant="plain">
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
]}
|
||||
>
|
||||
<p className="mb-4 text-bunker-300">{text}</p>
|
||||
<p className="font-medium text-bunker-300">
|
||||
Upgrade and get access to this, as well as to other powerful enhancements.
|
||||
</p>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
export { UpgradePlanModal } from './UpgradePlanModal';
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './Button';
|
||||
export * from './Card';
|
||||
export * from './Checkbox';
|
||||
export * from './DeleteActionModal';
|
||||
export * from './Dropdown';
|
||||
export * from './FormControl';
|
||||
export * from './IconButton';
|
||||
@@ -12,3 +13,4 @@ export * from './Spinner';
|
||||
export * from './Switch';
|
||||
export * from './Table';
|
||||
export * from './TextArea';
|
||||
export * from './UpgradePlanModal';
|
||||
|
||||
+25
-3
@@ -15,10 +15,32 @@ export const publicPaths = [
|
||||
`/privacy`,
|
||||
`/terms`,
|
||||
`/subprocessors`,
|
||||
`/verify-email`,
|
||||
`/password-reset`
|
||||
];
|
||||
|
||||
export const languageMap = {
|
||||
en: "English",
|
||||
ko: "한국어",
|
||||
fr: "Français",
|
||||
en: 'English',
|
||||
ko: '한국어',
|
||||
fr: 'Français'
|
||||
};
|
||||
|
||||
interface Mapping {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
const plansDev: Mapping = {
|
||||
starter: 'prod_Mb4ATFT5QAHoPM',
|
||||
team: 'prod_NEpD2WMXUS2eDn',
|
||||
professional: 'prod_Mb4CetZ2jE7jdl',
|
||||
enterprise: 'licence_key_required'
|
||||
};
|
||||
|
||||
const plansProd: Mapping = {
|
||||
starter: 'prod_Mb8oR5XNwyFTul',
|
||||
team: 'prod_NEp7fAB3UJWK6A',
|
||||
professional: 'prod_Mb8pUIpA0OUi5N',
|
||||
enterprise: 'licence_key_required'
|
||||
};
|
||||
|
||||
export const plans = plansProd || plansDev;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { createContext, ReactNode, useContext, useMemo } from 'react';
|
||||
|
||||
import { useGetOrgSubscription } from '@app/hooks/api';
|
||||
import { GetSubscriptionPlan } from '@app/hooks/api/types';
|
||||
|
||||
import { useWorkspace } from '../WorkspaceContext';
|
||||
// import { Subscription } from '@app/hooks/api/workspace/types';
|
||||
|
||||
type TSubscriptionContext = {
|
||||
subscription?: GetSubscriptionPlan;
|
||||
subscriptionPlan: string;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const SubscriptionContext = createContext<TSubscriptionContext | null>(null);
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const SubscriptionProvider = ({ children }: Props): JSX.Element => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { data, isLoading } = useGetOrgSubscription({
|
||||
orgID: currentWorkspace?.organization || ''
|
||||
});
|
||||
|
||||
// memorize the workspace details for the context
|
||||
const value = useMemo<TSubscriptionContext>(
|
||||
() => ({
|
||||
subscription: data,
|
||||
subscriptionPlan: data?.data?.[0]?.plan?.product || '',
|
||||
isLoading
|
||||
}),
|
||||
[data, isLoading]
|
||||
);
|
||||
|
||||
return <SubscriptionContext.Provider value={value}>{children}</SubscriptionContext.Provider>;
|
||||
};
|
||||
|
||||
export const useSubscription = () => {
|
||||
const ctx = useContext(SubscriptionContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useSubscription has to be used within <SubscriptionContext.Provider>');
|
||||
}
|
||||
|
||||
return ctx;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { SubscriptionProvider, useSubscription } from './SubscriptionContext';
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createContext, ReactNode, useContext, useMemo } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import { useGetUserWorkspaces } from '@app/hooks/api';
|
||||
import { Workspace } from '@app/hooks/api/workspace/types';
|
||||
|
||||
type TWorkspaceContext = {
|
||||
workspaces: Workspace[];
|
||||
currentWorkspace?: Workspace;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const WorkspaceContext = createContext<TWorkspaceContext | null>(null);
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const WorkspaceProvider = ({ children }: Props): JSX.Element => {
|
||||
const { data: ws, isLoading } = useGetUserWorkspaces();
|
||||
const router = useRouter();
|
||||
const workspaceId = router.query.id;
|
||||
|
||||
// memorize the workspace details for the context
|
||||
const value = useMemo<TWorkspaceContext>(() => {
|
||||
return {
|
||||
workspaces: ws || [],
|
||||
currentWorkspace: (ws || []).find(({ _id: id }) => id === workspaceId),
|
||||
isLoading
|
||||
};
|
||||
}, [ws, workspaceId, isLoading]);
|
||||
|
||||
return <WorkspaceContext.Provider value={value}>{children}</WorkspaceContext.Provider>;
|
||||
};
|
||||
|
||||
export const useWorkspace = () => {
|
||||
const ctx = useContext(WorkspaceContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useWorkspace has to be used within <WorkspaceContext.Provider>');
|
||||
}
|
||||
|
||||
return ctx;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { useWorkspace, WorkspaceProvider } from './WorkspaceContext';
|
||||
@@ -0,0 +1,2 @@
|
||||
export { SubscriptionProvider, useSubscription } from './SubscriptionContext';
|
||||
export { useWorkspace, WorkspaceProvider } from './WorkspaceContext';
|
||||
@@ -0,0 +1,39 @@
|
||||
import SecurityClient from '@app/components/utilities/SecurityClient';
|
||||
|
||||
/**
|
||||
* This function updates user permissions for a certain environment in a project
|
||||
* @param {object} obj
|
||||
* @param {string} obj.membershipId - membershipId of a certain user in a project
|
||||
* @param {*[]} obj.denials - permissions that we are prohibitting users to do
|
||||
* @returns
|
||||
*/
|
||||
const updateUserProjectPermission = async ({
|
||||
membershipId,
|
||||
denials
|
||||
}: {
|
||||
membershipId: string;
|
||||
denials: {
|
||||
ability: string;
|
||||
environmentSlug: string;
|
||||
}[]
|
||||
}) =>
|
||||
SecurityClient.fetchCall(`/api/v1/membership/${membershipId}/deny-permissions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
permissions: denials
|
||||
})
|
||||
}).then(async (res) => {
|
||||
console.log({
|
||||
permissions: denials
|
||||
}, res)
|
||||
if (res && res.status === 200) {
|
||||
return res.json();
|
||||
}
|
||||
console.log('Failed to update user permissions for a certain environment in a project');
|
||||
return undefined;
|
||||
});
|
||||
|
||||
export default updateUserProjectPermission;
|
||||
@@ -1 +1,5 @@
|
||||
export { useGetAuthToken } from './auth';
|
||||
export * from './auth';
|
||||
export * from './keys';
|
||||
export * from './serviceTokens';
|
||||
export * from './subscriptions';
|
||||
export * from './workspace';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { useGetUserWsKey } from './queries';
|
||||
@@ -0,0 +1,24 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { apiRequest } from '@app/config/request';
|
||||
|
||||
import { UserWsKeyPair } from './types';
|
||||
|
||||
const encKeyKeys = {
|
||||
getUserWorkspaceKey: (workspaceID: string) => ['worksapce-key-pair', { workspaceID }] as const
|
||||
};
|
||||
|
||||
const fetchUserWsKey = async (workspaceID: string) => {
|
||||
const { data } = await apiRequest.get<{ latestKey: UserWsKeyPair }>(
|
||||
`/api/v1/key/${workspaceID}/latest`
|
||||
);
|
||||
|
||||
return data.latestKey;
|
||||
};
|
||||
|
||||
export const useGetUserWsKey = (workspaceID: string) =>
|
||||
useQuery({
|
||||
queryKey: encKeyKeys.getUserWorkspaceKey(workspaceID),
|
||||
queryFn: () => fetchUserWsKey(workspaceID),
|
||||
enabled: Boolean(workspaceID)
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
export type UserWsKeyPair = {
|
||||
_id: string;
|
||||
encryptedKey: string;
|
||||
nonce: string;
|
||||
sender: Sender;
|
||||
receiver: string;
|
||||
workspace: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
__v: number;
|
||||
};
|
||||
|
||||
export type Sender = {
|
||||
_id: string;
|
||||
email: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
__v: number;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
publicKey: string;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { useCreateServiceToken, useDeleteServiceToken, useGetUserWsServiceTokens } from './queries';
|
||||
@@ -0,0 +1,60 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { apiRequest } from '@app/config/request';
|
||||
|
||||
import {
|
||||
CreateServiceTokenDTO,
|
||||
CreateServiceTokenRes,
|
||||
DeleteServiceTokenRes,
|
||||
ServiceToken
|
||||
} from './types';
|
||||
|
||||
const serviceTokenKeys = {
|
||||
getAllWorkspaceServiceToken: (workspaceID: string) => [{ workspaceID }, 'service-tokens'] as const
|
||||
};
|
||||
|
||||
const fetchWorkspaceServiceTokens = async (workspaceID: string) => {
|
||||
const { data } = await apiRequest.get<{ serviceTokenData: ServiceToken[] }>(
|
||||
`/api/v2/workspace/${workspaceID}/service-token-data`
|
||||
);
|
||||
|
||||
return data.serviceTokenData;
|
||||
};
|
||||
|
||||
type UseGetWorkspaceServiceTokensProps = { workspaceID: string };
|
||||
|
||||
export const useGetUserWsServiceTokens = ({ workspaceID }: UseGetWorkspaceServiceTokensProps) =>
|
||||
useQuery({
|
||||
queryKey: serviceTokenKeys.getAllWorkspaceServiceToken(workspaceID),
|
||||
queryFn: () => fetchWorkspaceServiceTokens(workspaceID),
|
||||
enabled: Boolean(workspaceID)
|
||||
});
|
||||
|
||||
// mutation
|
||||
export const useCreateServiceToken = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<CreateServiceTokenRes, {}, CreateServiceTokenDTO>({
|
||||
mutationFn: async (body) => {
|
||||
const { data } = await apiRequest.post('/api/v2/service-token/', body);
|
||||
return data;
|
||||
},
|
||||
onSuccess: ({ serviceTokenData: { workspace } }) => {
|
||||
queryClient.invalidateQueries(serviceTokenKeys.getAllWorkspaceServiceToken(workspace));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteServiceToken = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<DeleteServiceTokenRes, {}, string>({
|
||||
mutationFn: async (serviceTokenId) => {
|
||||
const { data } = await apiRequest.delete(`/api/v2/service-token/${serviceTokenId}`);
|
||||
return data;
|
||||
},
|
||||
onSuccess: ({ serviceTokenData: { workspace } }) => {
|
||||
queryClient.invalidateQueries(serviceTokenKeys.getAllWorkspaceServiceToken(workspace));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
export type ServiceToken = {
|
||||
_id: string;
|
||||
name: string;
|
||||
workspace: string;
|
||||
environment: string;
|
||||
user: string;
|
||||
expiresAt: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
__v: number;
|
||||
};
|
||||
|
||||
export type CreateServiceTokenDTO = {
|
||||
name: string;
|
||||
workspaceId: string;
|
||||
environment: string;
|
||||
expiresIn: number;
|
||||
encryptedKey: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
};
|
||||
|
||||
export type CreateServiceTokenRes = {
|
||||
serviceToken: string;
|
||||
serviceTokenData: ServiceToken;
|
||||
};
|
||||
|
||||
export type DeleteServiceTokenRes = { serviceTokenData: ServiceToken };
|
||||
@@ -0,0 +1 @@
|
||||
export { useGetOrgSubscription } from './queries';
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { apiRequest } from '@app/config/request';
|
||||
|
||||
import { GetSubscriptionPlan } from './types';
|
||||
|
||||
// import { Workspace } from './types';
|
||||
|
||||
const subscriptionKeys = {
|
||||
getOrgSubsription: (orgID: string) => ['subscription', { orgID }] as const
|
||||
};
|
||||
|
||||
const fetchOrgSubscription = async (orgID: string) => {
|
||||
const { data } = await apiRequest.get<{ subscriptions: GetSubscriptionPlan }>(
|
||||
`/api/v1/organization/${orgID}/subscriptions`
|
||||
);
|
||||
|
||||
return data.subscriptions;
|
||||
};
|
||||
|
||||
type UseGetOrgSubscriptionProps = {
|
||||
orgID: string;
|
||||
};
|
||||
|
||||
export const useGetOrgSubscription = ({ orgID }: UseGetOrgSubscriptionProps) =>
|
||||
useQuery({
|
||||
queryKey: subscriptionKeys.getOrgSubsription(orgID),
|
||||
queryFn: () => fetchOrgSubscription(orgID),
|
||||
enabled: Boolean(orgID)
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
export type GetSubscriptionPlan = {
|
||||
data: { plan: SubscriptionPlan }[];
|
||||
};
|
||||
|
||||
export type SubscriptionPlan = {
|
||||
id: string;
|
||||
object: string;
|
||||
active: boolean;
|
||||
aggregate_usage: unknown;
|
||||
amount: 1400;
|
||||
amount_decimal: 1400;
|
||||
billing_scheme: string;
|
||||
created: 1674833546;
|
||||
currency: string;
|
||||
interval: string;
|
||||
interval_count: 1;
|
||||
livemode: false;
|
||||
metadata: {};
|
||||
nickname: null;
|
||||
product: string;
|
||||
tiers_mode: unknown;
|
||||
transform_usage: unknown;
|
||||
trial_period_days: unknown;
|
||||
usage_type: string;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
export type { GetAuthTokenAPI } from './auth/types';
|
||||
export type { UserWsKeyPair } from './keys/types';
|
||||
export type { CreateServiceTokenDTO, ServiceToken } from './serviceTokens/types';
|
||||
export type { GetSubscriptionPlan, SubscriptionPlan } from './subscriptions/types';
|
||||
export type {
|
||||
CreateEnvironmentDTO,
|
||||
DeleteEnvironmentDTO,
|
||||
DeleteWorkspaceDTO,
|
||||
RenameWorkspaceDTO,
|
||||
UpdateEnvironmentDTO,
|
||||
Workspace,
|
||||
WorkspaceEnv
|
||||
} from './workspace/types';
|
||||
@@ -0,0 +1,8 @@
|
||||
export {
|
||||
useCreateWsEnvironment,
|
||||
useDeleteWorkspace,
|
||||
useDeleteWsEnvironment,
|
||||
useGetUserWorkspaces,
|
||||
useRenameWorkspace,
|
||||
useUpdateWsEnvironment
|
||||
} from './queries';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user