Merge remote-tracking branch 'origin/main' into auto-capitalization-toggle

This commit is contained in:
Hasan Mansoor
2023-02-05 03:19:06 +03:00
129 changed files with 3586 additions and 1104 deletions
+3 -2
View File
@@ -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
View File
@@ -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
+21 -12
View File
File diff suppressed because one or more lines are too long
+125 -122
View File
@@ -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",
+28
View File
@@ -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",
+32 -2
View File
@@ -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
})
}
})
+18 -18
View File
@@ -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.');
}
+155 -26
View File
@@ -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
};
+14 -4
View File
@@ -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,
+18 -2
View File
@@ -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,
+4
View File
@@ -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,
+12 -14
View File
@@ -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,
+16 -16
View File
@@ -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
-23
View File
@@ -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;
+4 -1
View File
@@ -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
};
+29
View File
@@ -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;
+3 -1
View File
@@ -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
+1 -1
View File
@@ -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']
+4
View File
@@ -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,
+4
View File
@@ -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
View File
@@ -1,5 +1,5 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
Copyright (c) 2023 Infisical Inc.
*/
package main
+22
View File
@@ -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
}
+8 -2
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
Copyright (c) 2023 Infisical Inc.
*/
package cmd
+6 -3
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
Copyright (c) 2023 Infisical Inc.
*/
package cmd
+1 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
Copyright (c) 2023 Infisical Inc.
*/
package cmd
+12 -4
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
Copyright (c) 2023 Infisical Inc.
*/
package cmd
+8 -3
View File
@@ -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 -2
View File
@@ -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
}
+9
View File
@@ -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
}
+4 -1
View File
@@ -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"
)
+4 -11
View File
@@ -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
+137 -8
View File
@@ -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)
}
+2 -1
View File
@@ -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
}
+5
View File
@@ -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>
+1
View File
@@ -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 |
+12 -19
View File
@@ -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}
```
+19 -17
View File
@@ -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>
+52
View File
@@ -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
View File
@@ -209,7 +209,8 @@
"pages": [
"integrations/platforms/docker",
"integrations/platforms/docker-compose",
"integrations/platforms/kubernetes"
"integrations/platforms/kubernetes",
"integrations/platforms/pm2"
]
},
{
+181 -92
View File
@@ -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"
}
}
}
}
+4 -1
View File
@@ -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",
+4 -4
View File
@@ -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."
}
+4 -4
View File
@@ -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"
}
}
+2 -2
View File
@@ -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 -1
View File
@@ -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",
+28 -29
View File
@@ -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&apos;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;
+44 -17
View File
@@ -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.');
+13 -11
View File
@@ -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'
)}
+3 -3
View File
@@ -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',
+7 -4
View File
@@ -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 -7
View File
@@ -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;
+2 -2
View File
@@ -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';
+23 -17
View File
@@ -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>
+17 -13
View File
@@ -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';
+2
View File
@@ -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
View File
@@ -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';
+2
View File
@@ -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;
+5 -1
View File
@@ -1 +1,5 @@
export { useGetAuthToken } from './auth';
export * from './auth';
export * from './keys';
export * from './serviceTokens';
export * from './subscriptions';
export * from './workspace';
+1
View File
@@ -0,0 +1 @@
export { useGetUserWsKey } from './queries';
+24
View File
@@ -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)
});
+22
View File
@@ -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;
};
+13
View File
@@ -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