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: with:
fetch-depth: 0 fetch-depth: 0
- run: git fetch --force --tags - run: git fetch --force --tags
- run: echo "Ref name ${{github.ref_name}}"
- uses: actions/setup-go@v3 - uses: actions/setup-go@v3
with: with:
go-version: '>=1.19.3' go-version: '>=1.19.3'
@@ -33,11 +34,11 @@ jobs:
run: | run: |
mkdir ../../osxcross mkdir ../../osxcross
git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target
- uses: goreleaser/goreleaser-action@v2 - uses: goreleaser/goreleaser-action@v4
with: with:
distribution: goreleaser distribution: goreleaser
version: latest version: latest
args: release --rm-dist args: release --clean
env: env:
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }} FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
+20 -1
View File
@@ -14,6 +14,9 @@ before:
builds: builds:
- id: darwin-build - id: darwin-build
binary: infisical binary: infisical
ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
flags:
- -trimpath
env: env:
- CGO_ENABLED=1 - CGO_ENABLED=1
- CC=/home/runner/work/osxcross/target/bin/o64-clang - CC=/home/runner/work/osxcross/target/bin/o64-clang
@@ -24,10 +27,14 @@ builds:
- goos: darwin - goos: darwin
goarch: "386" goarch: "386"
dir: ./cli dir: ./cli
- id: all-other-builds - id: all-other-builds
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
binary: infisical binary: infisical
ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
flags:
- -trimpath
goos: goos:
- freebsd - freebsd
- linux - linux
@@ -65,8 +72,10 @@ release:
checksum: checksum:
name_template: 'checksums.txt' name_template: 'checksums.txt'
snapshot: snapshot:
name_template: "{{ incpatch .Version }}" name_template: "{{ incpatch .Version }}-devel"
changelog: changelog:
sort: asc sort: asc
filters: filters:
@@ -80,6 +89,7 @@ changelog:
# - infisical # - infisical
# dir: "{{ dir .ArtifactPath }}" # dir: "{{ dir .ArtifactPath }}"
# cmd: curl -F package=@{{ .ArtifactName }} https://{{ .Env.FURY_TOKEN }}@push.fury.io/infisical/ # cmd: curl -F package=@{{ .ArtifactName }} https://{{ .Env.FURY_TOKEN }}@push.fury.io/infisical/
brews: brews:
- name: infisical - name: infisical
tap: tap:
@@ -91,6 +101,13 @@ brews:
folder: Formula folder: Formula
homepage: "https://infisical.com" homepage: "https://infisical.com"
description: "The official Infisical CLI" 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: nfpms:
- id: infisical - id: infisical
package_name: infisical package_name: infisical
@@ -116,6 +133,7 @@ nfpms:
dst: /usr/share/zsh/site-functions/_infisical dst: /usr/share/zsh/site-functions/_infisical
- src: ./manpages/infisical.1.gz - src: ./manpages/infisical.1.gz
dst: /usr/share/man/man1/infisical.1.gz dst: /usr/share/man/man1/infisical.1.gz
scoop: scoop:
bucket: bucket:
owner: Infisical owner: Infisical
@@ -126,6 +144,7 @@ scoop:
homepage: "https://infisical.com" homepage: "https://infisical.com"
description: "The official Infisical CLI" description: "The official Infisical CLI"
license: MIT license: MIT
aurs: aurs:
- -
name: infisical-bin name: infisical-bin
+21 -12
View File
File diff suppressed because one or more lines are too long
+117 -114
View File
@@ -10,13 +10,13 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@godaddy/terminus": "^4.11.2", "@godaddy/terminus": "^4.11.2",
"@sentry/node": "^7.21.1",
"@octokit/rest": "^19.0.5", "@octokit/rest": "^19.0.5",
"@sentry/node": "^7.14.0", "@sentry/tracing": "^7.21.1",
"@sentry/tracing": "^7.19.0",
"@types/crypto-js": "^4.1.1", "@types/crypto-js": "^4.1.1",
"axios": "^1.2.0",
"@types/libsodium-wrappers": "^0.7.10", "@types/libsodium-wrappers": "^0.7.10",
"await-to-js": "^3.0.0", "await-to-js": "^3.0.0",
"axios": "^1.1.3",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"bigint-conversion": "^2.2.2", "bigint-conversion": "^2.2.2",
"builder-pattern": "^2.2.0", "builder-pattern": "^2.2.0",
@@ -32,9 +32,9 @@
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4", "jsrp": "^0.2.4",
"mongoose": "^6.7.3",
"libsodium-wrappers": "^0.7.10", "libsodium-wrappers": "^0.7.10",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mongoose": "^6.7.2",
"nodemailer": "^6.8.0", "nodemailer": "^6.8.0",
"posthog-node": "^2.2.2", "posthog-node": "^2.2.2",
"query-string": "^7.1.3", "query-string": "^7.1.3",
@@ -2838,6 +2838,19 @@
"@maxmind/geoip2-node": "^3.4.0" "@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": { "node_modules/@protobufjs/aspromise": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "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", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz",
"integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==", "integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==",
"dependencies": { "dependencies": {
"@sentry/types": "7.19.0", "@sentry/types": "7.21.1",
"@sentry/utils": "7.19.0", "@sentry/utils": "7.21.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@sentry/node/node_modules/@sentry/types": { "node_modules/@sentry/node": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.21.1.tgz",
"integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==", "integrity": "sha512-B+p1nQHaFWdCCRVmvqlr/+vdQCI3mGLObucNfK2YC22IQZg7+3u6tEbxJ7umITIjeSSKgf7ZoZwCxL9VfkrNXg==",
"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==",
"dependencies": { "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" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@@ -2943,46 +2953,53 @@
} }
}, },
"node_modules/@sentry/tracing": { "node_modules/@sentry/tracing": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.21.1.tgz",
"integrity": "sha512-SWY17M3TsgBePaGowUcSqBwaT0TJQzuNexVnLojuU0k6F57L9hubvP9zaoosoCfARXQ/3NypAFWnlJyf570rFQ==", "integrity": "sha512-b1BTPsRaNQpohzegoz59KGuBl+To651vEq0vMS4tCzSyIdxkYso3JCrjDdEqW/2MliQYANNVrUai2bmwmU9h1g==",
"dependencies": { "dependencies": {
"@sentry/core": "7.19.0", "@sentry/core": "7.21.1",
"@sentry/types": "7.19.0", "@sentry/types": "7.21.1",
"@sentry/utils": "7.19.0", "@sentry/utils": "7.21.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@sentry/tracing/node_modules/@sentry/core": { "node_modules/@sentry/types": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz",
"integrity": "sha512-YF9cTBcAnO4R44092BJi5Wa2/EO02xn2ziCtmNgAVTN2LD31a/YVGxGBt/FDr4Y6yeuVehaqijVVvtpSmXrGJw==", "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": { "dependencies": {
"@sentry/types": "7.19.0", "@sentry/types": "7.21.1",
"@sentry/utils": "7.19.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@sentry/tracing/node_modules/@sentry/types": { "node_modules/@sentry/types": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz",
"integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==", "integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@sentry/tracing/node_modules/@sentry/utils": { "node_modules/@sentry/utils": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz",
"integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==", "integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==",
"dependencies": { "dependencies": {
"@sentry/types": "7.19.0", "@sentry/types": "7.21.1",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@@ -3767,9 +3784,9 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.1.3", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.0.tgz",
"integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==", "integrity": "sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@@ -4448,9 +4465,9 @@
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
}, },
"node_modules/cookiejar": { "node_modules/cookiejar": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
"integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
"dev": true "dev": true
}, },
"node_modules/core-util-is": { "node_modules/core-util-is": {
@@ -7165,9 +7182,9 @@
} }
}, },
"node_modules/mongoose": { "node_modules/mongoose": {
"version": "6.7.2", "version": "6.7.3",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.2.tgz", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.3.tgz",
"integrity": "sha512-lrP2V5U1qhaf+z33fiIn7aYAZZ1fVDly+TkFRjTujNBF/FIHESATj2RbgAOSlWqv32fsZXkXejXzeVfjbv35Ow==", "integrity": "sha512-bLC2Pt6Vpoov+1kBYvQgJXG/2DWXbfIvfK4Gh68kCdYGh6CVO31YxYuIGz70hyGwX2g4DmSzbs5IA8Px2neMCQ==",
"dependencies": { "dependencies": {
"bson": "^4.7.0", "bson": "^4.7.0",
"kareem": "2.4.1", "kareem": "2.4.1",
@@ -14289,6 +14306,16 @@
"@maxmind/geoip2-node": "^3.4.0" "@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": { "@protobufjs/aspromise": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -14344,80 +14371,56 @@
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
}, },
"@sentry/node": { "@sentry/node": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.21.1.tgz",
"integrity": "sha512-yG7Tx32WqOkEHVotFLrumCcT9qlaSDTkFNZ+yLSvZXx74ifsE781DzBA9W7K7bBdYO3op+p2YdsOKzf3nPpAyQ==", "integrity": "sha512-B+p1nQHaFWdCCRVmvqlr/+vdQCI3mGLObucNfK2YC22IQZg7+3u6tEbxJ7umITIjeSSKgf7ZoZwCxL9VfkrNXg==",
"requires": { "requires": {
"@sentry/core": "7.19.0", "@sentry/core": "7.21.1",
"@sentry/types": "7.19.0", "@sentry/types": "7.21.1",
"@sentry/utils": "7.19.0", "@sentry/utils": "7.21.1",
"cookie": "^0.4.1", "cookie": "^0.4.1",
"https-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0",
"lru_map": "^0.3.3", "lru_map": "^0.3.3",
"tslib": "^1.9.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": { "@sentry/tracing": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.21.1.tgz",
"integrity": "sha512-SWY17M3TsgBePaGowUcSqBwaT0TJQzuNexVnLojuU0k6F57L9hubvP9zaoosoCfARXQ/3NypAFWnlJyf570rFQ==", "integrity": "sha512-b1BTPsRaNQpohzegoz59KGuBl+To651vEq0vMS4tCzSyIdxkYso3JCrjDdEqW/2MliQYANNVrUai2bmwmU9h1g==",
"requires": { "requires": {
"@sentry/core": "7.19.0", "@sentry/core": "7.21.1",
"@sentry/types": "7.19.0", "@sentry/types": "7.21.1",
"@sentry/utils": "7.19.0", "@sentry/utils": "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" "tslib": "^1.9.3"
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.21.1.tgz",
"integrity": "sha512-oGRAT6lfzoKrxO1mvxiSj0XHxWPd6Gd1wpPGuu6iJo03xgWDS+MIlD1h2unqL4N5fAzLjzmbC2D2lUw50Kn2pA==" "integrity": "sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "7.19.0", "version": "7.21.1",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.19.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.21.1.tgz",
"integrity": "sha512-2L6lq+c9Ol2uiRxQDdcgoapmHJp24MhMN0gIkn2alSfMJ+ls6bGXzQHx6JAIdoOiwFQXRZHKL9ecfAc8O+vItA==", "integrity": "sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==",
"requires": { "requires": {
"@sentry/types": "7.19.0", "@sentry/types": "7.21.1",
"tslib": "^1.9.3" "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"
} }
}, },
"@sinclair/typebox": { "@sinclair/typebox": {
@@ -15051,9 +15054,9 @@
"integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==" "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="
}, },
"axios": { "axios": {
"version": "1.1.3", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.0.tgz",
"integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==", "integrity": "sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==",
"requires": { "requires": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@@ -15561,9 +15564,9 @@
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
}, },
"cookiejar": { "cookiejar": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
"integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
"dev": true "dev": true
}, },
"core-util-is": { "core-util-is": {
@@ -17626,9 +17629,9 @@
} }
}, },
"mongoose": { "mongoose": {
"version": "6.7.2", "version": "6.7.3",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.2.tgz", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.7.3.tgz",
"integrity": "sha512-lrP2V5U1qhaf+z33fiIn7aYAZZ1fVDly+TkFRjTujNBF/FIHESATj2RbgAOSlWqv32fsZXkXejXzeVfjbv35Ow==", "integrity": "sha512-bLC2Pt6Vpoov+1kBYvQgJXG/2DWXbfIvfK4Gh68kCdYGh6CVO31YxYuIGz70hyGwX2g4DmSzbs5IA8Px2neMCQ==",
"requires": { "requires": {
"bson": "^4.7.0", "bson": "^4.7.0",
"kareem": "2.4.1", "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", "name": "infisical-api",
"version": "1.0.0", "version": "1.0.0",
"main": "src/index.js", "main": "src/index.js",
+32 -2
View File
@@ -4,16 +4,21 @@ import jwt from 'jsonwebtoken';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import * as bigintConversion from 'bigint-conversion'; import * as bigintConversion from 'bigint-conversion';
const jsrp = require('jsrp'); const jsrp = require('jsrp');
import { User } from '../../models'; import { User, LoginSRPDetail } from '../../models';
import { createToken, issueTokens, clearTokens } from '../../helpers/auth'; import { createToken, issueTokens, clearTokens } from '../../helpers/auth';
import {
ACTION_LOGIN,
ACTION_LOGOUT
} from '../../variables';
import { import {
NODE_ENV, NODE_ENV,
JWT_AUTH_LIFETIME, JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET, JWT_AUTH_SECRET,
JWT_REFRESH_SECRET JWT_REFRESH_SECRET
} from '../../config'; } from '../../config';
import LoginSRPDetail from '../../models/LoginSRPDetail';
import { BadRequestError } from '../../utils/errors'; import { BadRequestError } from '../../utils/errors';
import { EELogService } from '../../ee/services';
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
declare module 'jsonwebtoken' { declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -116,6 +121,18 @@ export const login2 = async (req: Request, res: Response) => {
secure: NODE_ENV === 'production' ? true : false 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 (access) token in response
return res.status(200).send({ return res.status(200).send({
token: tokens.token, token: tokens.token,
@@ -159,6 +176,19 @@ export const logout = async (req: Request, res: Response) => {
sameSite: 'strict', sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false 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) { } catch (err) {
Sentry.setUser({ email: req.user.email }); Sentry.setUser({ email: req.user.email });
Sentry.captureException(err); Sentry.captureException(err);
@@ -77,8 +77,6 @@ export const changeMembershipOrgRole = async (req: Request, res: Response) => {
// change role for (target) organization membership with id // change role for (target) organization membership with id
// [membershipOrgId] // [membershipOrgId]
// TODO
let membershipToChangeRole; let membershipToChangeRole;
// try { // try {
// } catch (err) { // } catch (err) {
@@ -4,12 +4,11 @@ import crypto from 'crypto';
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const jsrp = require('jsrp'); const jsrp = require('jsrp');
import * as bigintConversion from 'bigint-conversion'; 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 { checkEmailVerification } from '../../helpers/signup';
import { createToken } from '../../helpers/auth'; import { createToken } from '../../helpers/auth';
import { sendMail } from '../../helpers/nodemailer'; import { sendMail } from '../../helpers/nodemailer';
import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config'; import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config';
import LoginSRPDetail from '../../models/LoginSRPDetail';
import { BadRequestError } from '../../utils/errors'; import { BadRequestError } from '../../utils/errors';
/** /**
@@ -11,7 +11,7 @@ import {
import { SecretVersion } from '../../ee/models'; import { SecretVersion } from '../../ee/models';
import { BadRequestError } from '../../utils/errors'; import { BadRequestError } from '../../utils/errors';
import _ from 'lodash'; 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 * Create new workspace environment named [environmentName] under workspace with id
@@ -236,7 +236,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
throw BadRequestError() throw BadRequestError()
} }
const accessibleEnvironments: { name: string; slug: string; }[] = [] const accessibleEnvironments: any = []
const deniedPermission = workspacesUserIsMemberOf.deniedPermissions const deniedPermission = workspacesUserIsMemberOf.deniedPermissions
const relatedWorkspace = await Workspace.findById(workspaceId) const relatedWorkspace = await Workspace.findById(workspaceId)
@@ -245,11 +245,15 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
} }
relatedWorkspace.environments.forEach(environment => { relatedWorkspace.environments.forEach(environment => {
const isReadBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_READ }) 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) { if (isReadBlocked) {
return return
} else { } 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 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) const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_WRITE)
if (!hasAccess) { 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, name: ACTION_ADD_SECRETS,
userId: req.user._id.toString(), userId: req.user._id,
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
secretIds: newSecrets.map((n) => n._id) secretIds: newSecrets.map((n) => n._id)
}); });
// (EE) create (audit) log // (EE) create (audit) log
addAction && await EELogService.createLog({ addAction && await EELogService.createLog({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
actions: [addAction], actions: [addAction],
channel, channel,
ipAddress: req.ip ipAddress: req.ip
@@ -300,16 +300,16 @@ export const getSecrets = async (req: Request, res: Response) => {
const channel = getChannelFromUserAgent(req.headers['user-agent']) const channel = getChannelFromUserAgent(req.headers['user-agent'])
const readAction = await EELogService.createActionSecret({ const readAction = await EELogService.createAction({
name: ACTION_READ_SECRETS, name: ACTION_READ_SECRETS,
userId: userId, userId: new Types.ObjectId(userId),
workspaceId: workspaceId as string, workspaceId: new Types.ObjectId(workspaceId as string),
secretIds: secrets.map((n: any) => n._id) secretIds: secrets.map((n: any) => n._id)
}); });
readAction && await EELogService.createLog({ readAction && await EELogService.createLog({
userId: userId, userId: new Types.ObjectId(userId),
workspaceId: workspaceId as string, workspaceId: new Types.ObjectId(workspaceId as string),
actions: [readAction], actions: [readAction],
channel, channel,
ipAddress: req.ip ipAddress: req.ip
@@ -505,17 +505,17 @@ export const updateSecrets = async (req: Request, res: Response) => {
}); });
}, 10000); }, 10000);
const updateAction = await EELogService.createActionSecret({ const updateAction = await EELogService.createAction({
name: ACTION_UPDATE_SECRETS, name: ACTION_UPDATE_SECRETS,
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: key, workspaceId: new Types.ObjectId(key),
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
}); });
// (EE) create (audit) log // (EE) create (audit) log
updateAction && await EELogService.createLog({ updateAction && await EELogService.createLog({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: key, workspaceId: new Types.ObjectId(key),
actions: [updateAction], actions: [updateAction],
channel, channel,
ipAddress: req.ip ipAddress: req.ip
@@ -631,17 +631,17 @@ export const deleteSecrets = async (req: Request, res: Response) => {
workspaceId: key workspaceId: key
}) })
}); });
const deleteAction = await EELogService.createActionSecret({ const deleteAction = await EELogService.createAction({
name: ACTION_DELETE_SECRETS, name: ACTION_DELETE_SECRETS,
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: key, workspaceId: new Types.ObjectId(key),
secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id)
}); });
// (EE) create (audit) log // (EE) create (audit) log
deleteAction && await EELogService.createLog({ deleteAction && await EELogService.createLog({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: key, workspaceId: new Types.ObjectId(key),
actions: [deleteAction], actions: [deleteAction],
channel, channel,
ipAddress: req.ip ipAddress: req.ip
@@ -8,6 +8,8 @@ import {
import { import {
SALT_ROUNDS SALT_ROUNDS
} from '../../config'; } from '../../config';
import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions';
import { ABILITY_READ } from '../../variables/organization';
/** /**
* Return service token data associated with service token on request * Return service token data associated with service token on request
@@ -37,6 +39,11 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
expiresIn expiresIn
} = req.body; } = 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 secret = crypto.randomBytes(16).toString('hex');
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS); const secretHash = await bcrypt.hash(secret, SALT_ROUNDS);
@@ -101,3 +108,7 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => {
serviceTokenData serviceTokenData
}); });
} }
function UnauthorizedRequestError(arg0: { message: string; }) {
throw new Error('Function not implemented.');
}
+154 -25
View File
@@ -1,39 +1,40 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { SecretVersion, Action } from '../models'; import { Action } from '../models';
import { import {
getLatestSecretVersionIds, getLatestSecretVersionIds,
getLatestNSecretSecretVersionIds getLatestNSecretSecretVersionIds
} from '../helpers/secretVersion'; } 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 * Create an (audit) action for updating secrets
* add, delete, update, and read actions.
* @param {Object} obj * @param {Object} obj
* @param {String} obj.name - name of action * @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 * @returns {Action} action - new action
*/ */
const createActionSecretHelper = async ({ const createActionUpdateSecret = async ({
name, name,
userId, userId,
workspaceId, workspaceId,
secretIds secretIds
}: { }: {
name: string; name: string;
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId: Types.ObjectId;
secretIds: Types.ObjectId[]; secretIds: Types.ObjectId[];
}) => { }) => {
let action; let action;
let latestSecretVersions;
try { try {
if (name === ACTION_UPDATE_SECRETS) { const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
// case: action is updating secrets
// -> add old and new secret versions
latestSecretVersions = (await getLatestNSecretSecretVersionIds({
secretIds, secretIds,
n: 2 n: 2
})) }))
@@ -41,16 +42,6 @@ const createActionSecretHelper = async ({
oldSecretVersion: s.versions[0]._id, oldSecretVersion: s.versions[0]._id,
newSecretVersion: s.versions[1]._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({ action = await new Action({
name, name,
@@ -61,6 +52,142 @@ const createActionSecretHelper = async ({
} }
}).save(); }).save();
} 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) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
@@ -70,4 +197,6 @@ const createActionSecretHelper = async ({
return action; return action;
} }
export { createActionSecretHelper }; export {
createActionHelper
};
+14 -4
View File
@@ -1,9 +1,19 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import { import {
Log, Log,
IAction IAction
} from '../models'; } 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 ({ const createLogHelper = async ({
userId, userId,
workspaceId, workspaceId,
@@ -11,8 +21,8 @@ const createLogHelper = async ({
channel, channel,
ipAddress ipAddress
}: { }: {
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId?: Types.ObjectId;
actions: IAction[]; actions: IAction[];
channel: string; channel: string;
ipAddress: string; ipAddress: string;
@@ -21,7 +31,7 @@ const createLogHelper = async ({
try { try {
log = await new Log({ log = await new Log({
user: userId, user: userId,
workspace: workspaceId, workspace: workspaceId ?? undefined,
actionNames: actions.map((a) => a.name), actionNames: actions.map((a) => a.name),
actions, actions,
channel, channel,
+18 -2
View File
@@ -1,10 +1,18 @@
import { Schema, model, Types } from 'mongoose'; 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 { export interface IAction {
name: string; name: string;
user?: Types.ObjectId, user?: Types.ObjectId,
workspace?: Types.ObjectId, workspace?: Types.ObjectId,
payload: { payload?: {
secretVersions?: Types.ObjectId[] secretVersions?: Types.ObjectId[]
} }
} }
@@ -13,7 +21,15 @@ const actionSchema = new Schema<IAction>(
{ {
name: { name: {
type: String, type: String,
required: true required: true,
enum: [
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
]
}, },
user: { user: {
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,
+4
View File
@@ -1,5 +1,7 @@
import { Schema, model, Types } from 'mongoose'; import { Schema, model, Types } from 'mongoose';
import { import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS, ACTION_READ_SECRETS,
@@ -29,6 +31,8 @@ const logSchema = new Schema<ILog>(
actionNames: { actionNames: {
type: [String], type: [String],
enum: [ enum: [
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS, ACTION_READ_SECRETS,
+12 -14
View File
@@ -1,14 +1,12 @@
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { import {
Log,
Action,
IAction IAction
} from '../models'; } from '../models';
import { import {
createLogHelper createLogHelper
} from '../helpers/log'; } from '../helpers/log';
import { import {
createActionSecretHelper createActionHelper
} from '../helpers/action'; } from '../helpers/action';
import EELicenseService from './EELicenseService'; import EELicenseService from './EELicenseService';
@@ -33,8 +31,8 @@ class EELogService {
channel, channel,
ipAddress ipAddress
}: { }: {
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId?: Types.ObjectId;
actions: IAction[]; actions: IAction[];
channel: string; channel: string;
ipAddress: string; ipAddress: string;
@@ -50,26 +48,26 @@ class EELogService {
} }
/** /**
* Create an (audit) action for secrets including * Create an (audit) action
* add, delete, update, and read actions.
* @param {Object} obj * @param {Object} obj
* @param {String} obj.name - name of action * @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 * @returns {Action} action - new action
*/ */
static async createActionSecret({ static async createAction({
name, name,
userId, userId,
workspaceId, workspaceId,
secretIds secretIds
}: { }: {
name: string; name: string;
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId?: Types.ObjectId;
secretIds: Types.ObjectId[]; secretIds?: Types.ObjectId[];
}) { }) {
if (!EELicenseService.isLicenseValid) return null; return await createActionHelper({
return await createActionSecretHelper({
name, name,
userId, userId,
workspaceId, workspaceId,
+16 -16
View File
@@ -406,10 +406,10 @@ const v2PushSecrets = async ({
secretIds: toDelete secretIds: toDelete
}); });
const deleteAction = await EELogService.createActionSecret({ const deleteAction = await EELogService.createAction({
name: ACTION_DELETE_SECRETS, name: ACTION_DELETE_SECRETS,
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(userId),
secretIds: toDelete secretIds: toDelete
}); });
@@ -499,10 +499,10 @@ const v2PushSecrets = async ({
}) })
}); });
const updateAction = await EELogService.createActionSecret({ const updateAction = await EELogService.createAction({
name: ACTION_UPDATE_SECRETS, name: ACTION_UPDATE_SECRETS,
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
secretIds: toUpdate.map((u) => u._id) 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, name: ACTION_ADD_SECRETS,
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
secretIds: newSecrets.map((n) => n._id) secretIds: newSecrets.map((n) => n._id)
}); });
addAction && actions.push(addAction); addAction && actions.push(addAction);
@@ -553,8 +553,8 @@ const v2PushSecrets = async ({
// (EE) create (audit) log // (EE) create (audit) log
if (actions.length > 0) { if (actions.length > 0) {
await EELogService.createLog({ await EELogService.createLog({
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
actions, actions,
channel, channel,
ipAddress ipAddress
@@ -645,16 +645,16 @@ const pullSecrets = async ({
environment environment
}) })
const readAction = await EELogService.createActionSecret({ const readAction = await EELogService.createAction({
name: ACTION_READ_SECRETS, name: ACTION_READ_SECRETS,
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
secretIds: secrets.map((n: any) => n._id) secretIds: secrets.map((n: any) => n._id)
}); });
readAction && await EELogService.createLog({ readAction && await EELogService.createLog({
userId, userId: new Types.ObjectId(userId),
workspaceId, workspaceId: new Types.ObjectId(workspaceId),
actions: [readAction], actions: [readAction],
channel, channel,
ipAddress 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 Workspace, { IWorkspace } from './workspace';
import ServiceTokenData, { IServiceTokenData } from './serviceTokenData'; import ServiceTokenData, { IServiceTokenData } from './serviceTokenData';
import APIKeyData, { IAPIKeyData } from './apiKeyData'; import APIKeyData, { IAPIKeyData } from './apiKeyData';
import LoginSRPDetail, { ILoginSRPDetail } from './loginSRPDetail';
export { export {
BackupPrivateKey, BackupPrivateKey,
@@ -53,5 +54,7 @@ export {
ServiceTokenData, ServiceTokenData,
IServiceTokenData, IServiceTokenData,
APIKeyData, 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; salt?: string;
verifier?: string; verifier?: string;
refreshVersion?: number; refreshVersion?: number;
seenIps: [string];
} }
const userSchema = new Schema<IUser>( const userSchema = new Schema<IUser>(
@@ -54,7 +55,8 @@ const userSchema = new Schema<IUser>(
type: Number, type: Number,
default: 0, default: 0,
select: false select: false
} },
seenIps: [String]
}, },
{ {
timestamps: true timestamps: true
+1 -1
View File
@@ -30,7 +30,7 @@ router.patch(
'/:organizationId/memberships/:membershipId', '/:organizationId/memberships/:membershipId',
param('organizationId').exists().trim(), param('organizationId').exists().trim(),
param('membershipId').exists().trim(), param('membershipId').exists().trim(),
body('role').exists().isString().trim().isIn([ADMIN, MEMBER]), body('role').exists().isString().trim().isIn([OWNER, ADMIN, MEMBER]),
validateRequest, validateRequest,
requireAuth({ requireAuth({
acceptedAuthModes: ['jwt', 'apiKey'] 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_ADD_SECRETS = 'addSecrets';
const ACTION_DELETE_SECRETS = 'deleteSecrets'; const ACTION_DELETE_SECRETS = 'deleteSecrets';
const ACTION_UPDATE_SECRETS = 'updateSecrets'; const ACTION_UPDATE_SECRETS = 'updateSecrets';
const ACTION_READ_SECRETS = 'readSecrets'; const ACTION_READ_SECRETS = 'readSecrets';
export { export {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_DELETE_SECRETS, ACTION_DELETE_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
+4
View File
@@ -35,6 +35,8 @@ import {
import { SECRET_SHARED, SECRET_PERSONAL } from './secret'; import { SECRET_SHARED, SECRET_PERSONAL } from './secret';
import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event'; import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event';
import { import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_DELETE_SECRETS, ACTION_DELETE_SECRETS,
@@ -75,6 +77,8 @@ export {
INTEGRATION_FLYIO_API_URL, INTEGRATION_FLYIO_API_URL,
EVENT_PUSH_SECRETS, EVENT_PUSH_SECRETS,
EVENT_PULL_SECRETS, EVENT_PULL_SECRETS,
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS, ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS, ACTION_UPDATE_SECRETS,
ACTION_DELETE_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 package main
+22
View File
@@ -5,6 +5,7 @@ import (
"github.com/Infisical/infisical-merge/packages/config" "github.com/Infisical/infisical-merge/packages/config"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
) )
const USER_AGENT = "cli" const USER_AGENT = "cli"
@@ -144,3 +145,24 @@ func CallGetAllWorkSpacesUserBelongsTo(httpClient *resty.Client) (GetWorkSpacesR
return workSpacesResponse, nil 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 package cmd
@@ -56,7 +56,12 @@ var exportCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag") 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 { if err != nil {
util.HandleError(err, "Unable to fetch secrets") 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().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().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().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 // 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 package cmd
+6 -3
View File
@@ -1,5 +1,5 @@
/* /*
Copyright © 2022 NAME HERE <EMAIL ADDRESS> Copyright (c) 2023 Infisical Inc.
*/ */
package cmd package cmd
@@ -33,13 +33,13 @@ var loginCmd = &cobra.Command{
PreRun: toggleDebug, PreRun: toggleDebug,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() 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) log.Debug(err)
} else if err != nil { } else if err != nil {
util.HandleError(err) 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) shouldOverride, err := shouldOverrideLoginPrompt(currentLoggedInUserDetails.UserCredentials.Email)
if err != nil { if err != nil {
util.HandleError(err) 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") 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) 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 package cmd
+1 -1
View File
@@ -1,5 +1,5 @@
/* /*
Copyright © 2022 NAME HERE <EMAIL ADDRESS> Copyright (c) 2023 Infisical Inc.
*/ */
package cmd package cmd
+12 -4
View File
@@ -1,5 +1,5 @@
/* /*
Copyright © 2022 NAME HERE <EMAIL ADDRESS> Copyright (c) 2023 Infisical Inc.
*/ */
package cmd package cmd
@@ -12,6 +12,7 @@ import (
"strings" "strings"
"syscall" "syscall"
"github.com/Infisical/infisical-merge/packages/models"
"github.com/Infisical/infisical-merge/packages/util" "github.com/Infisical/infisical-merge/packages/util"
"github.com/fatih/color" "github.com/fatih/color"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -58,10 +59,15 @@ var runCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
} }
if !util.IsSecretEnvironmentValid(envName) { infisicalToken, err := cmd.Flags().GetString("token")
util.PrintMessageAndExit("Invalid environment name passed. Environment names can only be prod, dev, test or staging") 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") secretOverriding, err := cmd.Flags().GetBool("secret-overriding")
if err != nil { if err != nil {
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
@@ -72,7 +78,8 @@ var runCmd = &cobra.Command{
util.HandleError(err, "Unable to parse flag") 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 { 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") 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() { func init() {
rootCmd.AddCommand(runCmd) 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().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("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") 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 package cmd
@@ -34,12 +34,17 @@ var secretsCmd = &cobra.Command{
util.HandleError(err) 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") shouldExpandSecrets, err := cmd.Flags().GetBool("expand")
if err != nil { if err != nil {
util.HandleError(err) util.HandleError(err)
} }
secrets, err := util.GetAllEnvironmentVariables(environmentName) secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken})
if err != nil { if err != nil {
util.HandleError(err) util.HandleError(err)
} }
@@ -111,7 +116,7 @@ var secretsSetCmd = &cobra.Command{
plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey) plainTextEncryptionKey := crypto.DecryptAsymmetric(encryptedWorkspaceKey, encryptedWorkspaceKeyNonce, encryptedWorkspaceKeySenderPublicKey, currentUsersPrivateKey)
// pull current secrets // pull current secrets
secrets, err := util.GetAllEnvironmentVariables(environmentName) secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName})
if err != nil { if err != nil {
util.HandleError(err, "unable to retrieve secrets") util.HandleError(err, "unable to retrieve secrets")
} }
@@ -267,7 +272,7 @@ var secretsDeleteCmd = &cobra.Command{
util.HandleError(err, "Unable to get local project details") 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 { if err != nil {
util.HandleError(err, "Unable to fetch secrets") 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) { func getSecretsByNames(cmd *cobra.Command, args []string) {
environmentName, err := cmd.Flags().GetString("env") environmentName, err := cmd.Flags().GetString("env")
if err != nil { if err != nil {
@@ -344,7 +325,12 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
util.HandleError(err, "Unable to parse flag") 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 { if err != nil {
util.HandleError(err, "To fetch all secrets") util.HandleError(err, "To fetch all secrets")
} }
@@ -380,3 +366,25 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod
return secretMapByName 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 package cmd
+8 -3
View File
@@ -34,7 +34,12 @@ type WorkspaceConfigFile struct {
} }
type SymmetricEncryptionResult struct { type SymmetricEncryptionResult struct {
CipherText []byte CipherText []byte `json:"CipherText"`
Nonce []byte Nonce []byte `json:"Nonce"`
AuthTag []byte AuthTag []byte `json:"AuthTag"`
}
type GetAllSecretsParameters struct {
Environment string
InfisicalToken string
} }
+2 -2
View File
@@ -2,10 +2,10 @@ package util
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"errors"
) )
func CheckForUpdate() { func CheckForUpdate() {
@@ -42,5 +42,5 @@ func getLatestTag(repoOwner string, repoName string) (string, error) {
json.Unmarshal(body, &tags) 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 ( import (
"fmt" "fmt"
"net/http"
"os" "os"
) )
@@ -19,3 +20,11 @@ func WriteToFile(fileName string, dataToWrite []byte, filePerm os.FileMode) erro
return nil 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" KEYRING_SERVICE_NAME = "infisical"
PERSONAL_SECRET_TYPE_NAME = "personal" PERSONAL_SECRET_TYPE_NAME = "personal"
SHARED_SECRET_TYPE_NAME = "shared" SHARED_SECRET_TYPE_NAME = "shared"
CLI_VERSION = "v0.2.8" )
var (
CLI_VERSION = "devel"
) )
+4 -11
View File
@@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"github.com/99designs/keyring" "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/Infisical/infisical-merge/packages/models"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
@@ -87,17 +87,10 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
SetAuthToken(userCreds.JTWToken). SetAuthToken(userCreds.JTWToken).
SetHeader("Accept", "application/json") SetHeader("Accept", "application/json")
response, err := httpClient. isAuthenticated := api.CallIsAuthenticated(httpClient)
R(). if !isAuthenticated {
Post(fmt.Sprintf("%v/v1/auth/checkAuth", config.INFISICAL_URL))
if err != nil {
return LoggedInUserDetails{}, err
}
if response.StatusCode() > 299 {
return LoggedInUserDetails{ return LoggedInUserDetails{
IsUserLoggedIn: true, IsUserLoggedIn: true, // was logged in
LoginExpired: true, LoginExpired: true,
UserCredentials: userCreds, UserCredentials: userCreds,
}, nil }, nil
+135 -6
View File
@@ -2,6 +2,8 @@ package util
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json"
"errors"
"fmt" "fmt"
"os" "os"
"regexp" "regexp"
@@ -97,13 +99,26 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work
return plainTextSecrets, nil return plainTextSecrets, nil
} }
func GetAllEnvironmentVariables(envName string) ([]models.SingleEnvironmentVariable, error) { func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models.SingleEnvironmentVariable, error) {
infisicalToken := os.Getenv(INFISICAL_TOKEN_NAME) 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 == "" { if infisicalToken == "" {
if isConnected {
log.Debug("GetAllEnvironmentVariables: Connected to internet, checking logged in creds")
RequireLocalWorkspaceFile() RequireLocalWorkspaceFile()
RequireLogin() RequireLogin()
log.Debug("Trying to fetch secrets using logged in details") }
log.Debug("GetAllEnvironmentVariables: Trying to fetch secrets using logged in details")
loggedInUserDetails, err := GetCurrentLoggedInUserDetails() loggedInUserDetails, err := GetCurrentLoggedInUserDetails()
if err != nil { if err != nil {
@@ -115,13 +130,30 @@ func GetAllEnvironmentVariables(envName string) ([]models.SingleEnvironmentVaria
return nil, err return nil, err
} }
secrets, err := GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, envName) secretsToReturn, errorToReturn = GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, params.Environment)
return secrets, err 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 { } else {
log.Debug("Trying to fetch secrets using service token") 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 { 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 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 { if password, ok := os.LookupEnv("INFISICAL_VAULT_FILE_PASSPHRASE"); ok {
return password, nil return password, nil
} else { } 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) fmt.Fprintf(os.Stderr, "%s:", prompt)
@@ -65,6 +65,7 @@ func fileKeyringPassphrasePrompt(prompt string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
fmt.Println("") fmt.Println("")
return string(b), nil return string(b), nil
} }
+5
View File
@@ -14,3 +14,8 @@ If you are still experiencing trouble, please seek support.
[Learn more about vault command](./commands/vault) [Learn more about vault command](./commands/vault)
</Accordion> </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](/integrations/platforms/docker) | Platform | Available |
| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available | | [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available | | [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available |
| [PM2](/integrations/platforms/pm2) | Platform | Available |
| [Heroku](/integrations/cloud/heroku) | Cloud | Available | | [Heroku](/integrations/cloud/heroku) | Cloud | Available |
| [Vercel](/integrations/cloud/vercel) | Cloud | Available | | [Vercel](/integrations/cloud/vercel) | Cloud | Available |
| [Netlify](/integrations/cloud/netlify) | Cloud | Available | | [Netlify](/integrations/cloud/netlify) | Cloud | Available |
+12 -19
View File
@@ -1,45 +1,38 @@
--- ---
title: "Docker Compose" 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. ## Generate Infisical Tokens
To learn how, visit [Infisical Token](../../getting-started/dashboard/token). Once you have generated the token, keep it handy.
<Info> Generate a unique [Infisical Token](https://infisical.com/docs/getting-started/dashboard/token) for each service.
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>
## 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. For each service you want to inject secrets into, set an environment variable called `INFISICAL_TOKEN` equal to a unique identifier variable.
This will ensure that you can set Infisical Tokens for multiple services.
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 ```yaml
# Example Docker Compose file # Example Docker Compose file
services: services:
web: web:
build: . build: .
image: auledge-frontend image: example-service-1
container_name: auledge-frontend
environment: environment:
- INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_WEB} - INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_WEB}
api: api:
build: . build: .
image: auledge-backend image: example-service-2
container_name: auledge-backend
environment: environment:
- INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_API} - 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." 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> <Tabs>
<Tab title="Alpine"> <Tab title="Alpine">
@@ -32,32 +34,32 @@ Infisical can be used in a Dockerfile to inject environment variables into a Doc
</Tab> </Tab>
</Tabs> </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. Starting your service with the Infisical CLI pulls your secrets from Infisical and injects them into your service.
This will automatically pull the necessary secrets and make them available to your application as if they were natively exposed within the container.
```dockerfile ```dockerfile
CMD ["infisical", "run", "---", "[your application start command]"] CMD ["infisical", "run", "---", "[your service start command]"]
# example with single single command # example with single single command
CMD ["infisical", "run", "---", "npm run start"] CMD ["infisical", "run", "---", "npm run start"]
# example with multiple commands # example with multiple commands
CMD ["infisical", "run", "--command", "npm run start && ..."] 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 ## 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 ```bash
docker run --env INFISICAL_TOKEN=[token]... 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": [ "pages": [
"integrations/platforms/docker", "integrations/platforms/docker",
"integrations/platforms/docker-compose", "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/free-solid-svg-icons": "^6.1.2",
"@fortawesome/react-fontawesome": "^0.1.19", "@fortawesome/react-fontawesome": "^0.1.19",
"@headlessui/react": "^1.6.6", "@headlessui/react": "^1.6.6",
"@hookform/resolvers": "^2.9.10",
"@radix-ui/react-accordion": "^1.1.0", "@radix-ui/react-accordion": "^1.1.0",
"@radix-ui/react-alert-dialog": "^1.0.2", "@radix-ui/react-alert-dialog": "^1.0.2",
"@radix-ui/react-checkbox": "^1.0.1", "@radix-ui/react-checkbox": "^1.0.1",
@@ -51,6 +52,7 @@
"react-code-input": "^3.10.1", "react-code-input": "^3.10.1",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-grid-layout": "^1.3.4", "react-grid-layout": "^1.3.4",
"react-hook-form": "^7.43.0",
"react-i18next": "^12.1.1", "react-i18next": "^12.1.1",
"react-mailchimp-subscribe": "^2.1.3", "react-mailchimp-subscribe": "^2.1.3",
"react-markdown": "^8.0.3", "react-markdown": "^8.0.3",
@@ -64,7 +66,8 @@
"tweetnacl-util": "^0.15.1", "tweetnacl-util": "^0.15.1",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"uuidv4": "^6.2.13", "uuidv4": "^6.2.13",
"yaml": "^2.2.0" "yaml": "^2.2.0",
"yup": "^0.32.11"
}, },
"devDependencies": { "devDependencies": {
"@storybook/addon-essentials": "^7.0.0-beta.30", "@storybook/addon-essentials": "^7.0.0-beta.30",
@@ -108,7 +111,6 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
"dev": true,
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.9"
@@ -121,7 +123,6 @@
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
"dev": true,
"dependencies": { "dependencies": {
"@jridgewell/set-array": "^1.0.0", "@jridgewell/set-array": "^1.0.0",
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
@@ -157,7 +158,6 @@
"version": "7.20.5", "version": "7.20.5",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
"integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
"dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@@ -166,7 +166,6 @@
"version": "7.20.5", "version": "7.20.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
"integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
"dev": true,
"dependencies": { "dependencies": {
"@ampproject/remapping": "^2.1.0", "@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
@@ -196,7 +195,6 @@
"version": "4.3.4", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "2.1.2"
}, },
@@ -212,14 +210,12 @@
"node_modules/@babel/core/node_modules/ms": { "node_modules/@babel/core/node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"node_modules/@babel/core/node_modules/semver": { "node_modules/@babel/core/node_modules/semver": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
@@ -265,7 +261,6 @@
"version": "7.20.7", "version": "7.20.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
"integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
"dev": true,
"dependencies": { "dependencies": {
"@babel/compat-data": "^7.20.5", "@babel/compat-data": "^7.20.5",
"@babel/helper-validator-option": "^7.18.6", "@babel/helper-validator-option": "^7.18.6",
@@ -284,7 +279,6 @@
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"dependencies": { "dependencies": {
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
@@ -293,7 +287,6 @@
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
@@ -301,8 +294,7 @@
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": { "node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
"dev": true
}, },
"node_modules/@babel/helper-create-class-features-plugin": { "node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.20.12", "version": "7.20.12",
@@ -461,7 +453,6 @@
"version": "7.20.11", "version": "7.20.11",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz",
"integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==",
"dev": true,
"dependencies": { "dependencies": {
"@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-environment-visitor": "^7.18.9",
"@babel/helper-module-imports": "^7.18.6", "@babel/helper-module-imports": "^7.18.6",
@@ -535,7 +526,6 @@
"version": "7.20.2", "version": "7.20.2",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
"integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
"dev": true,
"dependencies": { "dependencies": {
"@babel/types": "^7.20.2" "@babel/types": "^7.20.2"
}, },
@@ -586,7 +576,6 @@
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
"integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
"dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@@ -610,7 +599,6 @@
"version": "7.20.6", "version": "7.20.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
"integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
"dev": true,
"dependencies": { "dependencies": {
"@babel/template": "^7.18.10", "@babel/template": "^7.18.10",
"@babel/traverse": "^7.20.5", "@babel/traverse": "^7.20.5",
@@ -2897,6 +2885,14 @@
"react-dom": "^16 || ^17 || ^18" "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": { "node_modules/@humanwhocodes/config-array": {
"version": "0.11.8", "version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
@@ -7005,8 +7001,7 @@
"node_modules/@types/lodash": { "node_modules/@types/lodash": {
"version": "4.14.191", "version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
"dev": true
}, },
"node_modules/@types/mdast": { "node_modules/@types/mdast": {
"version": "3.0.10", "version": "3.0.10",
@@ -8997,7 +8992,6 @@
"version": "4.21.4", "version": "4.21.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
"integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -10680,8 +10674,7 @@
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.284", "version": "1.4.284",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
"dev": true
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "9.2.2", "version": "9.2.2",
@@ -10977,7 +10970,6 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@@ -12818,7 +12810,6 @@
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@@ -14991,7 +14982,6 @@
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"bin": { "bin": {
"json5": "lib/cli.js" "json5": "lib/cli.js"
}, },
@@ -15210,6 +15200,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "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": { "node_modules/lodash.castarray": {
"version": "4.4.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
@@ -16183,6 +16178,11 @@
"object-assign": "^4.1.0" "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": { "node_modules/nanoid": {
"version": "3.3.4", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
@@ -16464,8 +16464,7 @@
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg=="
"dev": true
}, },
"node_modules/normalize-package-data": { "node_modules/normalize-package-data": {
"version": "2.5.0", "version": "2.5.0",
@@ -17625,6 +17624,11 @@
"react-is": "^16.13.1" "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": { "node_modules/property-information": {
"version": "6.1.1", "version": "6.1.1",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz",
@@ -18142,6 +18146,21 @@
"react-dom": ">= 16.3.0" "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": { "node_modules/react-i18next": {
"version": "12.1.1", "version": "12.1.1",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz",
@@ -21168,6 +21187,11 @@
"node": ">=0.6" "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": { "node_modules/tr46": {
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -21404,7 +21428,7 @@
"version": "4.9.3", "version": "4.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"dev": true, "devOptional": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -21730,7 +21754,6 @@
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -22480,6 +22503,23 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "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": { "dependencies": {
@@ -22487,7 +22527,6 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
"integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
"dev": true,
"requires": { "requires": {
"@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.9"
@@ -22497,7 +22536,6 @@
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
"integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
"dev": true,
"requires": { "requires": {
"@jridgewell/set-array": "^1.0.0", "@jridgewell/set-array": "^1.0.0",
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
@@ -22525,14 +22563,12 @@
"@babel/compat-data": { "@babel/compat-data": {
"version": "7.20.5", "version": "7.20.5",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
"integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g=="
"dev": true
}, },
"@babel/core": { "@babel/core": {
"version": "7.20.5", "version": "7.20.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
"integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
"dev": true,
"requires": { "requires": {
"@ampproject/remapping": "^2.1.0", "@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6", "@babel/code-frame": "^7.18.6",
@@ -22555,7 +22591,6 @@
"version": "4.3.4", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"requires": { "requires": {
"ms": "2.1.2" "ms": "2.1.2"
} }
@@ -22563,14 +22598,12 @@
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
"dev": true
} }
} }
}, },
@@ -22606,7 +22639,6 @@
"version": "7.20.7", "version": "7.20.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
"integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
"dev": true,
"requires": { "requires": {
"@babel/compat-data": "^7.20.5", "@babel/compat-data": "^7.20.5",
"@babel/helper-validator-option": "^7.18.6", "@babel/helper-validator-option": "^7.18.6",
@@ -22619,7 +22651,6 @@
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"requires": { "requires": {
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
@@ -22627,14 +22658,12 @@
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
"dev": true
}, },
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
"dev": true
} }
} }
}, },
@@ -22753,7 +22782,6 @@
"version": "7.20.11", "version": "7.20.11",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz",
"integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==", "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==",
"dev": true,
"requires": { "requires": {
"@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-environment-visitor": "^7.18.9",
"@babel/helper-module-imports": "^7.18.6", "@babel/helper-module-imports": "^7.18.6",
@@ -22809,7 +22837,6 @@
"version": "7.20.2", "version": "7.20.2",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
"integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
"dev": true,
"requires": { "requires": {
"@babel/types": "^7.20.2" "@babel/types": "^7.20.2"
} }
@@ -22844,8 +22871,7 @@
"@babel/helper-validator-option": { "@babel/helper-validator-option": {
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
"integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw=="
"dev": true
}, },
"@babel/helper-wrap-function": { "@babel/helper-wrap-function": {
"version": "7.20.5", "version": "7.20.5",
@@ -22863,7 +22889,6 @@
"version": "7.20.6", "version": "7.20.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
"integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
"dev": true,
"requires": { "requires": {
"@babel/template": "^7.18.10", "@babel/template": "^7.18.10",
"@babel/traverse": "^7.20.5", "@babel/traverse": "^7.20.5",
@@ -24061,7 +24086,8 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", "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==", "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==",
"dev": true "dev": true,
"requires": {}
}, },
"@emotion/utils": { "@emotion/utils": {
"version": "1.2.0", "version": "1.2.0",
@@ -24366,7 +24392,14 @@
"@headlessui/react": { "@headlessui/react": {
"version": "1.6.6", "version": "1.6.6",
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.6.6.tgz", "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": { "@humanwhocodes/config-array": {
"version": "0.11.8", "version": "0.11.8",
@@ -27340,8 +27373,7 @@
"@types/lodash": { "@types/lodash": {
"version": "4.14.191", "version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
"dev": true
}, },
"@types/mdast": { "@types/mdast": {
"version": "3.0.10", "version": "3.0.10",
@@ -28016,7 +28048,8 @@
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true "dev": true,
"requires": {}
}, },
"acorn-node": { "acorn-node": {
"version": "1.8.2", "version": "1.8.2",
@@ -28150,7 +28183,8 @@
"version": "3.5.2", "version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true "dev": true,
"requires": {}
}, },
"ansi-align": { "ansi-align": {
"version": "3.0.1", "version": "3.0.1",
@@ -28444,7 +28478,8 @@
"axios-auth-refresh": { "axios-auth-refresh": {
"version": "3.3.3", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/axios-auth-refresh/-/axios-auth-refresh-3.3.3.tgz", "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": { "axobject-query": {
"version": "3.1.1", "version": "3.1.1",
@@ -28459,7 +28494,8 @@
"version": "7.0.0-bridge.0", "version": "7.0.0-bridge.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
"integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
"dev": true "dev": true,
"requires": {}
}, },
"babel-loader": { "babel-loader": {
"version": "9.1.2", "version": "9.1.2",
@@ -28838,7 +28874,6 @@
"version": "4.21.4", "version": "4.21.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
"integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
"dev": true,
"requires": { "requires": {
"caniuse-lite": "^1.0.30001400", "caniuse-lite": "^1.0.30001400",
"electron-to-chromium": "^1.4.251", "electron-to-chromium": "^1.4.251",
@@ -29668,7 +29703,8 @@
"cva": { "cva": {
"version": "npm:[email protected]", "version": "npm:[email protected]",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.4.0.tgz", "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": { "damerau-levenshtein": {
"version": "1.0.8", "version": "1.0.8",
@@ -30121,8 +30157,7 @@
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.4.284", "version": "1.4.284",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA=="
"dev": true
}, },
"emoji-regex": { "emoji-regex": {
"version": "9.2.2", "version": "9.2.2",
@@ -30368,8 +30403,7 @@
"escalade": { "escalade": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
"dev": true
}, },
"escape-html": { "escape-html": {
"version": "1.0.3", "version": "1.0.3",
@@ -30632,7 +30666,8 @@
"version": "8.6.0", "version": "8.6.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz",
"integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==", "integrity": "sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==",
"dev": true "dev": true,
"requires": {}
}, },
"eslint-import-resolver-node": { "eslint-import-resolver-node": {
"version": "0.3.7", "version": "0.3.7",
@@ -30890,13 +30925,15 @@
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
"dev": true "dev": true,
"requires": {}
}, },
"eslint-plugin-simple-import-sort": { "eslint-plugin-simple-import-sort": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz", "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==", "integrity": "sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw==",
"dev": true "dev": true,
"requires": {}
}, },
"eslint-plugin-storybook": { "eslint-plugin-storybook": {
"version": "0.6.10", "version": "0.6.10",
@@ -31760,8 +31797,7 @@
"gensync": { "gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="
"dev": true
}, },
"get-caller-file": { "get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
@@ -32325,7 +32361,8 @@
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
"dev": true "dev": true,
"requires": {}
}, },
"ieee754": { "ieee754": {
"version": "1.2.1", "version": "1.2.1",
@@ -33339,8 +33376,7 @@
"json5": { "json5": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
"dev": true
}, },
"jsonfile": { "jsonfile": {
"version": "6.1.0", "version": "6.1.0",
@@ -33508,6 +33544,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "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": { "lodash.castarray": {
"version": "4.4.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
@@ -33638,7 +33679,8 @@
"version": "7.1.8", "version": "7.1.8",
"resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.8.tgz", "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.8.tgz",
"integrity": "sha512-rRSa1aFmFnpDRFAhv5vIkWM4nPaoB9vnzIjuIKa1wGupfn2hdCNeaQHKpu4/muoc8n8J7yowjTP2oncA4/Rbgg==", "integrity": "sha512-rRSa1aFmFnpDRFAhv5vIkWM4nPaoB9vnzIjuIKa1wGupfn2hdCNeaQHKpu4/muoc8n8J7yowjTP2oncA4/Rbgg==",
"dev": true "dev": true,
"requires": {}
}, },
"md5.js": { "md5.js": {
"version": "1.3.5", "version": "1.3.5",
@@ -34155,6 +34197,11 @@
"object-assign": "^4.1.0" "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": { "nanoid": {
"version": "3.3.4", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
@@ -34344,8 +34391,7 @@
"node-releases": { "node-releases": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
"integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg=="
"dev": true
}, },
"normalize-package-data": { "normalize-package-data": {
"version": "2.5.0", "version": "2.5.0",
@@ -34935,7 +34981,8 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
"dev": true "dev": true,
"requires": {}
}, },
"postcss-modules-local-by-default": { "postcss-modules-local-by-default": {
"version": "4.0.0", "version": "4.0.0",
@@ -35044,7 +35091,8 @@
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.2.tgz", "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.2.tgz",
"integrity": "sha512-5RjUbWRe305pUpc48MosoIp6uxZvZxrM6GyOgsbGLTce+ehePKNm7ziW2dLG2air9aXbGuXlHVSQQw4Lbosq3w==", "integrity": "sha512-5RjUbWRe305pUpc48MosoIp6uxZvZxrM6GyOgsbGLTce+ehePKNm7ziW2dLG2air9aXbGuXlHVSQQw4Lbosq3w==",
"dev": true "dev": true,
"requires": {}
}, },
"pretty-error": { "pretty-error": {
"version": "4.0.0", "version": "4.0.0",
@@ -35132,6 +35180,11 @@
"react-is": "^16.13.1" "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": { "property-information": {
"version": "6.1.1", "version": "6.1.1",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.1.1.tgz",
@@ -35437,7 +35490,8 @@
"version": "5.6.1", "version": "5.6.1",
"resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
"integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
"dev": true "dev": true,
"requires": {}
}, },
"react-docgen": { "react-docgen": {
"version": "5.4.3", "version": "5.4.3",
@@ -35469,7 +35523,8 @@
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz",
"integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==",
"dev": true "dev": true,
"requires": {}
}, },
"react-dom": { "react-dom": {
"version": "17.0.2", "version": "17.0.2",
@@ -35521,6 +35576,12 @@
"react-resizable": "^3.0.4" "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": { "react-i18next": {
"version": "12.1.1", "version": "12.1.1",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz",
@@ -35534,7 +35595,8 @@
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.1.tgz", "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.1.tgz",
"integrity": "sha512-cxKSeFTf7jpSSVddm66sKdolG90qURAX3g1roTeaN6x0YEbtWc8JpmFN9+yIqLNH2uEkYerWLtJZIXRIFuBKrg==", "integrity": "sha512-cxKSeFTf7jpSSVddm66sKdolG90qURAX3g1roTeaN6x0YEbtWc8JpmFN9+yIqLNH2uEkYerWLtJZIXRIFuBKrg==",
"dev": true "dev": true,
"requires": {}
}, },
"react-is": { "react-is": {
"version": "16.13.1", "version": "16.13.1",
@@ -35649,7 +35711,8 @@
"react-table": { "react-table": {
"version": "7.8.0", "version": "7.8.0",
"resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz",
"integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==" "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==",
"requires": {}
}, },
"read-cache": { "read-cache": {
"version": "1.0.0", "version": "1.0.0",
@@ -35805,7 +35868,8 @@
"redux-thunk": { "redux-thunk": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", "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": { "regenerate": {
"version": "1.4.2", "version": "1.4.2",
@@ -37258,7 +37322,8 @@
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz",
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
"dev": true "dev": true,
"requires": {}
}, },
"style-to-object": { "style-to-object": {
"version": "0.3.0", "version": "0.3.0",
@@ -37288,7 +37353,8 @@
"styled-jsx": { "styled-jsx": {
"version": "5.0.7", "version": "5.0.7",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz",
"integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==" "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==",
"requires": {}
}, },
"stylis": { "stylis": {
"version": "4.0.13", "version": "4.0.13",
@@ -37750,6 +37816,11 @@
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"dev": true "dev": true
}, },
"toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
},
"tr46": { "tr46": {
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -37928,7 +37999,7 @@
"version": "4.9.3", "version": "4.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz",
"integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==",
"dev": true "devOptional": true
}, },
"uc.micro": { "uc.micro": {
"version": "1.0.6", "version": "1.0.6",
@@ -38164,7 +38235,6 @@
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
"dev": true,
"requires": { "requires": {
"escalade": "^3.1.1", "escalade": "^3.1.1",
"picocolors": "^1.0.0" "picocolors": "^1.0.0"
@@ -38202,12 +38272,14 @@
"use-isomorphic-layout-effect": { "use-isomorphic-layout-effect": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", "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": { "use-memo-one": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", "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": { "use-sidecar": {
"version": "1.1.2", "version": "1.1.2",
@@ -38221,7 +38293,8 @@
"use-sync-external-store": { "use-sync-external-store": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", "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": { "util": {
"version": "0.12.5", "version": "0.12.5",
@@ -38412,7 +38485,8 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
"dev": true "dev": true,
"requires": {}
}, },
"eslint-scope": { "eslint-scope": {
"version": "5.1.1", "version": "5.1.1",
@@ -38638,7 +38712,8 @@
"version": "8.12.0", "version": "8.12.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz",
"integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==", "integrity": "sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==",
"dev": true "dev": true,
"requires": {}
}, },
"xtend": { "xtend": {
"version": "4.0.2", "version": "4.0.2",
@@ -38698,6 +38773,20 @@
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true "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/free-solid-svg-icons": "^6.1.2",
"@fortawesome/react-fontawesome": "^0.1.19", "@fortawesome/react-fontawesome": "^0.1.19",
"@headlessui/react": "^1.6.6", "@headlessui/react": "^1.6.6",
"@hookform/resolvers": "^2.9.10",
"@radix-ui/react-accordion": "^1.1.0", "@radix-ui/react-accordion": "^1.1.0",
"@radix-ui/react-alert-dialog": "^1.0.2", "@radix-ui/react-alert-dialog": "^1.0.2",
"@radix-ui/react-checkbox": "^1.0.1", "@radix-ui/react-checkbox": "^1.0.1",
@@ -58,6 +59,7 @@
"react-code-input": "^3.10.1", "react-code-input": "^3.10.1",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-grid-layout": "^1.3.4", "react-grid-layout": "^1.3.4",
"react-hook-form": "^7.43.0",
"react-i18next": "^12.1.1", "react-i18next": "^12.1.1",
"react-mailchimp-subscribe": "^2.1.3", "react-mailchimp-subscribe": "^2.1.3",
"react-markdown": "^8.0.3", "react-markdown": "^8.0.3",
@@ -71,7 +73,8 @@
"tweetnacl-util": "^0.15.1", "tweetnacl-util": "^0.15.1",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"uuidv4": "^6.2.13", "uuidv4": "^6.2.13",
"yaml": "^2.2.0" "yaml": "^2.2.0",
"yup": "^0.32.11"
}, },
"devDependencies": { "devDependencies": {
"@storybook/addon-essentials": "^7.0.0-beta.30", "@storybook/addon-essentials": "^7.0.0-beta.30",
+1 -1
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.", "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. ", "no-mobile": " To use Infisical, please log in through a device with larger dimensions. ",
"email": "Email", "email": "Email",
@@ -4,7 +4,7 @@
"add-new": "Add New Token", "add-new": "Add New Token",
"add-dialog": { "add-dialog": {
"title": "Add a service token for {{target}}", "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", "name": "Service Token Name",
"add": "Add Service Token", "add": "Add Service Token",
"copy-service-token": "Copy your service token", "copy-service-token": "Copy your service token",
@@ -1,4 +1,4 @@
{ {
"title": "Project Members", "title": "Project Members",
"description": "This page shows the members of the selected project." "description": "This page shows the members of the selected project, and allows you to modify their permissions."
} }
+1 -1
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à.", "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. ", "no-mobile": " Pour utiliser Infisical, veuillez vous connecter avec un appareil avec des dimensions plus grandes. ",
"email": "Email", "email": "Email",
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"head-title": "{{title}} | Infiscal", "head-title": "{{title}} | Infisical",
"error_project-already-exists": "동일한 이름을 가진 프로젝트가 이미 존재해요.", "error_project-already-exists": "동일한 이름을 가진 프로젝트가 이미 존재해요.",
"no-mobile": " Infisical을 사용하려면, 큰 화면을 가진 디바이스로 로그인하여 주세요.", "no-mobile": " Infisical을 사용하려면, 큰 화면을 가진 디바이스로 로그인하여 주세요.",
"email": "메일", "email": "메일",
+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.", "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.", "no-mobile": "Para usar o Infisical, faça o login através de um dispositivo com dimensões maiores.",
"email": "Email", "email": "Email",
+26 -27
View File
@@ -187,10 +187,10 @@ const Layout = ({ children }: LayoutProps) => {
}); });
const userWorkspaces = orgUserProjects; const userWorkspaces = orgUserProjects;
if ( if (
userWorkspaces.length === 0 && (userWorkspaces.length === 0 &&
router.asPath !== '/noprojects' && router.asPath !== '/noprojects' &&
!router.asPath.includes('home') && !router.asPath.includes('home') &&
!router.asPath.includes('settings') || !router.asPath.includes('settings')) ||
router.asPath === '/dashboard/undefined' router.asPath === '/dashboard/undefined'
) { ) {
router.push('/noprojects'); router.push('/noprojects');
@@ -246,16 +246,15 @@ const Layout = ({ children }: LayoutProps) => {
return ( return (
<> <>
<div className="fixed w-full md:block flex flex-col h-screen"> <div className="flex h-screen w-full flex-col overflow-x-hidden">
<script src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.2.2/cdn.js" defer />
<NavBarDashboard /> <NavBarDashboard />
<div className="flex flex-col md:flex-row flex-1"> <div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
<aside className="bg-bunker-600 border-r border-mineshaft-500 w-full md:w-60 h-screen"> <aside className="w-full border-r border-mineshaft-500 bg-bunker-600 md:w-60">
<nav className="flex flex-col justify-between items-between h-full"> <nav className="items-between flex h-full flex-col justify-between">
{/* <div className="py-6"></div> */} {/* <div className="py-6"></div> */}
<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="mt-6 mb-6 flex h-20 w-full flex-col items-center justify-center bg-bunker-600 px-4">
<div className="text-gray-400 self-start ml-1 mb-1 text-xs font-semibold tracking-wide"> <div className="ml-1 mb-1 self-start text-xs font-semibold tracking-wide text-gray-400">
{t('nav:menu.project')} {t('nav:menu.project')}
</div> </div>
{Object.keys(workspaceMapping).length > 0 ? ( {Object.keys(workspaceMapping).length > 0 ? (
@@ -279,29 +278,29 @@ const Layout = ({ children }: LayoutProps) => {
<ul> <ul>
{Object.keys(workspaceMapping).length > 0 && {Object.keys(workspaceMapping).length > 0 &&
menuItems.map(({ href, title, emoji }) => ( 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] && {router.asPath.split('/')[1] === href.split('/')[1] &&
(['project', 'billing', 'org', 'personal'].includes( (['project', 'billing', 'org', 'personal'].includes(
router.asPath.split('/')[2] router.asPath.split('/')[2]
) )
? router.asPath.split('/')[2] === href.split('/')[2] ? router.asPath.split('/')[2] === href.split('/')[2]
: true) ? ( : true) ? (
<div className="flex relative px-0.5 py-2.5 text-white text-sm rounded cursor-pointer bg-primary-50/10"> <div className="relative flex cursor-pointer rounded bg-primary-50/10 px-0.5 py-2.5 text-sm text-white">
<div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1" /> <div className="absolute inset-0 top-0 my-1 ml-1 mr-1 w-1 rounded-xl bg-primary" />
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg"> <p className="ml-4 mr-2 flex w-6 items-center justify-center text-lg">
{emoji} {emoji}
</p> </p>
{title} {title}
</div> </div>
) : router.asPath === '/noprojects' ? ( ) : router.asPath === '/noprojects' ? (
<div className="flex p-2.5 text-white text-sm rounded"> <div className="flex rounded p-2.5 text-sm text-white">
<p className="w-10 flex items-center justify-center text-lg">{emoji}</p> <p className="flex w-10 items-center justify-center text-lg">{emoji}</p>
{title} {title}
</div> </div>
) : ( ) : (
<Link href={href}> <Link href={href}>
<div className="flex p-2.5 text-white text-sm rounded cursor-pointer hover:bg-primary-50/5"> <div className="flex cursor-pointer rounded p-2.5 text-sm text-white hover:bg-primary-50/5">
<p className="w-10 flex items-center justify-center text-lg"> <p className="flex w-10 items-center justify-center text-lg">
{emoji} {emoji}
</p> </p>
{title} {title}
@@ -312,11 +311,11 @@ const Layout = ({ children }: LayoutProps) => {
))} ))}
</ul> </ul>
</div> </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' ? ( {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="relative flex cursor-pointer rounded bg-primary-50/10 px-0.5 py-2.5 text-sm text-white">
<div className="absolute top-0 my-1 ml-1 inset-0 bg-primary w-1 rounded-xl mr-1" /> <div className="absolute inset-0 top-0 my-1 ml-1 mr-1 w-1 rounded-xl bg-primary" />
<p className="w-6 ml-4 mr-2 flex items-center justify-center text-lg"> <p className="ml-4 mr-2 flex w-6 items-center justify-center text-lg">
<FontAwesomeIcon icon={faBookOpen} /> <FontAwesomeIcon icon={faBookOpen} />
</p> </p>
Infisical Guide Infisical Guide
@@ -336,8 +335,8 @@ const Layout = ({ children }: LayoutProps) => {
</div> </div>
) : ( ) : (
<Link href={`/home/${workspaceMapping[workspaceSelected as any]}`}> <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"> <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="w-10 flex items-center justify-center text-lg"> <p className="flex w-10 items-center justify-center text-lg">
<FontAwesomeIcon icon={faBookOpen} /> <FontAwesomeIcon icon={faBookOpen} />
</p> </p>
Infisical Guide Infisical Guide
@@ -369,12 +368,12 @@ const Layout = ({ children }: LayoutProps) => {
error={error} error={error}
loading={loading} 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> </div>
<div className="md:hidden bg-bunker-800 w-screen h-screen flex flex-col justify-center items-center"> <div className="flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
<FontAwesomeIcon icon={faMobile} className="text-gray-300 text-7xl mb-8" /> <FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
<p className="text-gray-200 px-6 text-center text-lg max-w-sm"> <p className="max-w-sm px-6 text-center text-lg text-gray-200">
{` ${t('common:no-mobile')} `} {` ${t('common:no-mobile')} `}
</p> </p>
</div> </div>
@@ -42,15 +42,15 @@ const UpgradePlanModal = ({
leaveFrom='opacity-100 scale-100' leaveFrom='opacity-100 scale-100'
leaveTo='opacity-0 scale-95' 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 <Dialog.Title
as='h3' 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 Unleash Infisical&apos;s Full Power
</Dialog.Title> </Dialog.Title>
<div className='mt-2'> <div className='mt-2'>
<p className='text-sm text-bunker-300 mb-0.5'> <p className='text-sm text-bunker-300 mb-1'>
{text} {text}
</p> </p>
<p className='text-sm text-bunker-300'> <p className='text-sm text-bunker-300'>
@@ -60,7 +60,7 @@ const UpgradePlanModal = ({
<div className='mt-4'> <div className='mt-4'>
<button <button
type='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")}`)} onClick={() => router.push(`/settings/billing/${localStorage.getItem("projectData.id")}`)}
> >
Upgrade Now Upgrade Now
@@ -41,7 +41,6 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const onEnvCreateCB = async (env: Env) => { const onEnvCreateCB = async (env: Env) => {
try { try {
await onCreateEnv(env); await onCreateEnv(env);
@@ -71,14 +70,14 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
return ( return (
<> <>
<div className="flex flex-row justify-between w-full"> <div className="flex w-full flex-row justify-between">
<div className="flex flex-col w-full"> <div className="flex w-full flex-col">
<p className="text-xl font-semibold mb-3">Project Environments</p> <p className="mb-3 text-xl font-semibold">Project Environments</p>
<p className="text-base text-gray-400 mb-4"> <p className="mb-4 text-base text-gray-400">
Choose which environments will show up in your dashboard like development, staging, Choose which environments will show up in your dashboard like development, staging,
production production
</p> </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. Note: the text in slugs shows how these environmant should be accessed in CLI.
</p> </p>
</div> </div>
@@ -86,10 +85,10 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
<Button <Button
text="Add New Env" text="Add New Env"
onButtonPressed={() => { onButtonPressed={() => {
if (plan !== plans.starter || host !== "https://app.infisical.com") { if (plan !== plans.starter || host !== 'https://app.infisical.com') {
handlePopUpOpen('createUpdateEnv') handlePopUpOpen('createUpdateEnv');
} else { } else {
handlePopUpOpen('upgradePlan') handlePopUpOpen('upgradePlan');
} }
}} }}
color="mineshaft" color="mineshaft"
@@ -98,32 +97,30 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
/> />
</div> </div>
</div> </div>
<div className="table-container w-full bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1"> <div className="table-container relative mb-6 mt-1 w-full rounded-md border border-mineshaft-700 bg-bunker">
<div className="absolute rounded-t-md w-full h-12 bg-white/5" /> <div className="absolute h-12 w-full rounded-t-md bg-white/5" />
<table className="w-full my-1"> <table className="my-1 w-full">
<thead className="text-bunker-300"> <thead className="text-bunker-300">
<tr> <tr>
<th className="text-left pl-6 pt-2.5 pb-2">Name</th> <th className="pl-6 pt-2.5 pb-2 text-left">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">Slug</th>
<th aria-label="buttons" /> <th aria-label="buttons" />
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{data?.length > 0 ? ( {data?.length > 0 ? (
data.map(({ name, slug }) => ( data.map(({ name, slug }) => (
<tr key={name} className="bg-bunker-800 hover:bg-bunker-800/5 duration-100"> <tr key={name} className="bg-bunker-800 duration-100 hover:bg-bunker-800/5">
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300"> <td className="border-t border-mineshaft-700 py-2 pl-6 text-gray-300">{name}</td>
{name} <td className="border-t border-mineshaft-700 py-2 pl-6 text-gray-300">{slug}</td>
</td> <td className="flex justify-end border-t border-mineshaft-700 py-2">
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">{slug}</td> <div className="mr-2 flex items-center duration-200 hover:opacity-100">
<td className="py-2 border-mineshaft-700 border-t flex justify-end">
<div className="hover:opacity-100 duration-200 flex items-center mr-2">
<Button <Button
onButtonPressed={() => { onButtonPressed={() => {
if (plan !== plans.starter || host !== "https://app.infisical.com") { if (plan !== plans.starter || host !== 'https://app.infisical.com') {
handlePopUpOpen('createUpdateEnv', { name, slug }) handlePopUpOpen('createUpdateEnv', { name, slug });
} else { } else {
handlePopUpOpen('upgradePlan') handlePopUpOpen('upgradePlan');
} }
}} }}
color="mineshaft" color="mineshaft"
@@ -131,13 +128,13 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
icon={faPencil} icon={faPencil}
/> />
</div> </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 <Button
onButtonPressed={() => { onButtonPressed={() => {
if (plan !== plans.starter || host !== "https://app.infisical.com") { if (plan !== plans.starter || host !== 'https://app.infisical.com') {
handlePopUpOpen('deleteEnv', { name, slug }) handlePopUpOpen('deleteEnv', { name, slug });
} else { } else {
handlePopUpOpen('upgradePlan') handlePopUpOpen('upgradePlan');
} }
}} }}
color="red" color="red"
@@ -150,8 +147,8 @@ const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }:
)) ))
) : ( ) : (
<tr> <tr>
<td colSpan={4} className="text-center pt-7 pb-4 text-bunker-400"> <td colSpan={4} className="pt-7 pb-4 text-center text-bunker-400">
No environmants found No environments found
</td> </td>
</tr> </tr>
)} )}
@@ -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 { useRouter } from 'next/router';
import { faX } from '@fortawesome/free-solid-svg-icons'; 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 deleteUserFromOrganization from '@app/pages/api/organization/deleteUserFromOrganization';
import changeUserRoleInWorkspace from '@app/pages/api/workspace/changeUserRoleInWorkspace';
import deleteUserFromWorkspace from '@app/pages/api/workspace/deleteUserFromWorkspace'; import deleteUserFromWorkspace from '@app/pages/api/workspace/deleteUserFromWorkspace';
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey'; import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
import uploadKeys from '@app/pages/api/workspace/uploadKeys'; 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) => { const handleRoleUpdate = (index: number, e: string) => {
changeUserRoleInWorkspace(userData[index].membershipId, e); changeUserRoleInOrganization(String(localStorage.getItem("orgData.id")), userData[index].membershipId, e);
changeData([ changeData([
...userData.slice(0, index), ...userData.slice(0, index),
...[ ...[
@@ -110,14 +110,14 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
}; };
return ( 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" /> <div className="absolute rounded-t-md w-full h-[3.25rem] bg-white/5" />
<table className="w-full my-0.5"> <table className="w-full my-0.5">
<thead className="text-gray-400 text-sm font-light"> <thead className="text-gray-400 text-sm font-light">
<tr> <tr>
<th className="text-left pl-6 py-3.5">FIRST NAME</th> <th className="text-left pl-4 py-3.5">NAME</th>
<th className="text-left pl-6 py-3.5">LAST NAME</th> <th className="text-left pl-4 py-3.5">EMAIL</th>
<th className="text-left pl-6 py-3.5">EMAIL</th> <th className="text-left pl-6 pr-10 py-3.5">ROLE</th>
<th aria-label="buttons" /> <th aria-label="buttons" />
</tr> </tr>
</thead> </thead>
@@ -136,35 +136,30 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
user.email?.toLowerCase().includes(filter) user.email?.toLowerCase().includes(filter)
) )
.map((row, index) => ( .map((row, index) => (
<tr key={guidGenerator()} className="bg-bunker-800 hover:bg-bunker-800/5"> <tr key={guidGenerator()} className="bg-bunker-800 hover:bg-bunker-700">
<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.firstName} {row.firstName} {row.lastName}
</td> </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.lastName}
</td>
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
{row.email} {row.email}
</td> </td>
<td className="flex flex-row justify-end pr-8 py-2 border-t border-0.5 border-mineshaft-700"> <td className="pl-6 pr-10 py-2 border-mineshaft-700 border-t text-gray-300">
<div className="justify-end mr-6 mx-2 w-full h-full flex flex-row items-center"> <div className="justify-start h-full flex flex-row items-center">
{row.status === 'granted' && {row.status === 'accepted' &&
((myRole === 'admin' && row.role !== 'owner') || myRole === 'owner') && ((myRole === 'admin' && row.role !== 'owner') || myRole === 'owner') &&
myUser !== row.email ? ( (myUser !== row.email) ? (
<Listbox <Listbox
isSelected={row.role} isSelected={row.role}
onChange={(e) => handleRoleUpdate(index, e)} onChange={(e) => handleRoleUpdate(index, e)}
data={ data={
myRole === 'owner' ? ['owner', 'admin', 'member'] : ['admin', 'member'] myRole === 'owner' ? ['owner', 'admin', 'member'] : ['admin', 'member']
} }
text="Role: "
/> />
) : ( ) : (
row.status !== 'invited' && row.status !== 'invited' &&
row.status !== 'verified' && ( row.status !== 'verified' && (
<Listbox <Listbox
isSelected={row.role} isSelected={row.role}
text="Role: "
onChange={() => { onChange={() => {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}} }}
@@ -173,7 +168,7 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
) )
)} )}
{(row.status === 'invited' || row.status === 'verified') && ( {(row.status === 'invited' || row.status === 'verified') && (
<div className="w-full pl-9"> <div className="w-full pr-20">
<Button <Button
onButtonPressed={() => deleteMembershipAndResendInvite(row.email)} onButtonPressed={() => deleteMembershipAndResendInvite(row.email)}
color="mineshaft" color="mineshaft"
@@ -193,10 +188,12 @@ const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }
</div> </div>
)} )}
</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 && {myUser !== row.email &&
// row.role !== "admin" && // row.role !== "admin" &&
myRole !== 'member' ? ( 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 <Button
onButtonPressed={() => handleDelete(row.membershipId, index)} onButtonPressed={() => handleDelete(row.membershipId, index)}
color="red" 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;
+31 -4
View File
@@ -6,10 +6,12 @@ import { faX } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import SecretVersionList from '@app/ee/components/SecretVersionList'; import SecretVersionList from '@app/ee/components/SecretVersionList';
import { WorkspaceEnv } from '@app/hooks/api/types';
import Button from '../basic/buttons/Button'; import Button from '../basic/buttons/Button';
import Toggle from '../basic/Toggle'; import Toggle from '../basic/Toggle';
import CommentField from './CommentField'; import CommentField from './CommentField';
import CompareSecretsModal from './CompareSecretsModal';
import DashboardInputField from './DashboardInputField'; import DashboardInputField from './DashboardInputField';
import { DeleteActionButton } from './DeleteActionButton'; import { DeleteActionButton } from './DeleteActionButton';
import GenerateSecretMenu from './GenerateSecretMenu'; import GenerateSecretMenu from './GenerateSecretMenu';
@@ -40,6 +42,9 @@ interface SideBarProps {
sharedToHide: string[]; sharedToHide: string[];
setSharedToHide: (values: string[]) => void; setSharedToHide: (values: string[]) => void;
deleteRow: (props: DeleteRowFunctionProps) => void; deleteRow: (props: DeleteRowFunctionProps) => void;
workspaceEnvs: WorkspaceEnv[];
selectedEnv: WorkspaceEnv;
workspaceId: string;
} }
/** /**
@@ -63,15 +68,19 @@ const SideBar = ({
modifyComment, modifyComment,
buttonReady, buttonReady,
savePush, savePush,
deleteRow deleteRow,
workspaceEnvs,
selectedEnv,
workspaceId
}: SideBarProps) => { }: SideBarProps) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [isLoading, setIsLoading] = useState(false); 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(); const { t } = useTranslation();
return ( 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 ? ( {isLoading ? (
<div className="flex items-center justify-center h-full"> <div className="flex items-center justify-center h-full">
<Image <Image
@@ -171,7 +180,24 @@ const SideBar = ({
/> />
</div> </div>
)} )}
<div className="flex justify-start max-w-sm mt-4 px-4 mt-full mb-[4.7rem]"> <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 <Button
text={String(t('common:save-changes'))} text={String(t('common:save-changes'))}
onButtonPressed={savePush} onButtonPressed={savePush}
@@ -187,6 +213,7 @@ const SideBar = ({
/> />
</div> </div>
</div> </div>
</div>
); );
}; };
@@ -93,7 +93,7 @@ export default function Navbar() {
}; };
return ( 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="m-auto flex justify-start items-center mx-4">
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<div className="flex justify-center py-4"> <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 { faAngleRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useWorkspace } from '@app/context';
import getOrganization from '@app/pages/api/organization/GetOrg'; 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. * This is the component at the top of almost every page.
@@ -23,8 +23,9 @@ export default function NavHeader({
isProjectRelated?: boolean; isProjectRelated?: boolean;
}): JSX.Element { }): JSX.Element {
const [orgName, setOrgName] = useState(''); const [orgName, setOrgName] = useState('');
const [workspaceName, setWorkspaceName] = useState('');
const router = useRouter(); const router = useRouter();
const projectId = String(router.query.id);
const { currentWorkspace } = useWorkspace();
useEffect(() => { useEffect(() => {
(async () => { (async () => {
@@ -33,29 +34,24 @@ export default function NavHeader({
orgId: orgId || '' orgId: orgId || ''
}); });
setOrgName(org.name); setOrgName(org.name);
const workspace = await getProjectInfo({
projectId: String(router.query.id)
});
setWorkspaceName(workspace.name);
})(); })();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, [projectId]);
return ( return (
<div className="pt-20 ml-6 flex flex-row items-center"> <div className="ml-6 flex flex-row items-center pt-8">
<div className="bg-primary-900 h-6 w-6 rounded-md flex items-center justify-center text-mineshaft-100 mr-2"> <div className="mr-2 flex h-6 w-6 items-center justify-center rounded-md bg-primary-900 text-mineshaft-100">
{orgName?.charAt(0)} {orgName?.charAt(0)}
</div> </div>
<div className="text-primary text-sm font-semibold">{orgName}</div> <div className="text-sm font-semibold text-primary">{orgName}</div>
{isProjectRelated && ( {isProjectRelated && (
<> <>
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" /> <FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
<div className="font-semibold text-primary text-sm">{workspaceName}</div> <div className="text-sm font-semibold text-primary">{currentWorkspace?.name}</div>
</> </>
)} )}
<FontAwesomeIcon icon={faAngleRight} className="ml-3 text-sm text-gray-400 mr-3" /> <FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
<div className="text-gray-400 text-sm">{pageName}</div> <div className="text-sm text-gray-400">{pageName}</div>
</div> </div>
); );
} }
@@ -4,11 +4,12 @@ const POSTHOG_HOST =
process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com"; process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com";
const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!; const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!;
const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!; const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!;
const {SITE_URL} = process.env
export { export {
ENV, ENV,
POSTHOG_API_KEY, POSTHOG_API_KEY,
POSTHOG_HOST, POSTHOG_HOST,
SITE_URL,
STRIPE_PRODUCT_PRO, STRIPE_PRODUCT_PRO,
STRIPE_PRODUCT_STARTER STRIPE_PRODUCT_STARTER};
};
@@ -1,5 +1,7 @@
import { jsPDF } from 'jspdf'; import { jsPDF } from 'jspdf';
import { SITE_URL } from './config';
interface PDFProps { interface PDFProps {
personalName: string; personalName: string;
personalEmail: string; personalEmail: string;
@@ -73,7 +75,7 @@ function generateBackupPDF({ personalName, personalEmail, generatedKey }: PDFPro
doc.roundedRect(170, 488, 375, 35, 5, 5, 'F'); doc.roundedRect(170, 488, 375, 35, 5, 5, 'F');
doc.setTextColor(23, 23, 23); doc.setTextColor(23, 23, 23);
doc.setFontSize(14); 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(personalEmail, 180, 465);
doc.text(generatedKey, 180, 510); doc.text(generatedKey, 180, 510);
doc.text('Need help? Contact us at [email protected]', 32, 575); doc.text('Need help? Contact us at [email protected]', 32, 575);
@@ -29,8 +29,8 @@ interface SecretProps {
interface FunctionProps { interface FunctionProps {
env: string; env: string;
setIsKeyAvailable: any; setIsKeyAvailable?: any;
setData: any; setData?: any;
workspaceId: string; workspaceId: string;
} }
@@ -58,7 +58,9 @@ const getSecretsForProject = async ({
const latestKey = await getLatestFileKey({ workspaceId }); 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 // This is called isKeyAvailable but what it really means is if a person is able to create new key pairs
if (typeof setIsKeyAvailable === 'function') {
setIsKeyAvailable(!latestKey ? encryptedSecrets.length === 0 : true); setIsKeyAvailable(!latestKey ? encryptedSecrets.length === 0 : true);
}
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string; const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
@@ -131,7 +133,10 @@ const getSecretsForProject = async ({
)[0]?.comment )[0]?.comment
})); }));
if (typeof setData === 'function') {
setData(result); setData(result);
}
return result; return result;
} catch (error) { } catch (error) {
console.log('Something went wrong during accessing or decripting secrets.'); 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', 'font-inter font-medium',
'cursor-pointer', 'cursor-pointer',
'inline-flex items-center justify-center', 'inline-flex items-center justify-center',
'relative' 'relative',
'whitespace-nowrap'
], ],
{ {
variants: { variants: {
colorSchema: { colorSchema: {
primary: ['bg-primary', 'text-black', 'border-primary hover:bg-opacity-80'], primary: ['bg-primary', 'text-black', 'border-primary hover:bg-opacity-80'],
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft 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: { variant: {
solid: '', solid: '',
@@ -33,7 +34,7 @@ const buttonVariants = cva(
plain: '' plain: ''
}, },
isDisabled: { isDisabled: {
true: 'bg-opacity-70 cursor-not-allowed', true: 'bg-mineshaft opacity-40 cursor-not-allowed',
false: '' false: ''
}, },
isFullWidth: { isFullWidth: {
@@ -45,9 +46,9 @@ const buttonVariants = cva(
false: '' false: ''
}, },
size: { size: {
xs: ['text-xs', 'py-1', 'px-2'], xs: ['text-xs', 'py-1', 'px-1'],
sm: ['text-sm', 'py-2', 'px-4'], sm: ['text-sm', 'py-2', 'px-2'],
md: ['text-md', 'py-2', 'px-6'], md: ['text-md', 'py-2', 'px-4'],
lg: ['text-lg', 'py-2', 'px-8'] lg: ['text-lg', 'py-2', 'px-8']
} }
}, },
@@ -75,7 +76,7 @@ const buttonVariants = cva(
{ {
colorSchema: 'secondary', colorSchema: 'secondary',
variant: 'plain', variant: 'plain',
className: 'text-mineshaft' className: 'text-mineshaft-300'
}, },
{ {
colorSchema: 'danger', colorSchema: 'danger',
@@ -101,7 +102,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
children, children,
isDisabled = false, isDisabled = false,
className = '', className = '',
size = 'md', size = 'sm',
variant = 'solid', variant = 'solid',
isFullWidth, isFullWidth,
isRounded = true, isRounded = true,
@@ -144,9 +145,10 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
)} )}
<span <span
className={twMerge( className={twMerge(
'transition-all shrink-0 cursor-pointer', 'shrink-0 cursor-pointer transition-all',
loadingToggleClass, loadingToggleClass,
size === 'xs' ? 'mr-1' : 'mr-2' leftIcon && 'ml-2',
size === 'xs' ? 'mr-1' : 'mr-3'
)} )}
> >
{leftIcon} {leftIcon}
@@ -154,7 +156,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
<span className={twMerge('transition-all', loadingToggleClass)}>{children}</span> <span className={twMerge('transition-all', loadingToggleClass)}>{children}</span>
<span <span
className={twMerge( className={twMerge(
'transition-all shrink-0 cursor-pointer', 'shrink-0 cursor-pointer transition-all',
loadingToggleClass, loadingToggleClass,
size === 'xs' ? 'ml-1' : 'ml-2' size === 'xs' ? 'ml-1' : 'ml-2'
)} )}
+3 -3
View File
@@ -8,7 +8,7 @@ export type CardTitleProps = {
}; };
export const CardTitle = ({ children, className, subTitle }: 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} {children}
{subTitle && <p className="py-1 text-sm font-normal text-gray-400">{subTitle}</p>} {subTitle && <p className="py-1 text-sm font-normal text-gray-400">{subTitle}</p>}
</div> </div>
@@ -29,7 +29,7 @@ export type CardBodyProps = {
}; };
export const CardBody = ({ children, className }: 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 = { export type CardProps = {
@@ -47,7 +47,7 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(
<div <div
ref={ref} ref={ref}
className={twMerge( 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', isFullHeight && 'h-full',
isRounded && 'rounded-md', isRounded && 'rounded-md',
isPlain && 'shadow-none', 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) => ( 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} {label}
{isRequired && <span className="ml-1 text-red">*</span>} {isRequired && <span className="ml-1 text-red">*</span>}
</Label.Root> </Label.Root>
@@ -25,7 +25,7 @@ export type FormHelperTextProps = {
export const FormHelperText = ({ isError, text }: FormHelperTextProps) => ( export const FormHelperText = ({ isError, text }: FormHelperTextProps) => (
<div <div
className={twMerge( 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' isError && 'text-red-600'
)} )}
> >
@@ -46,6 +46,7 @@ export type FormControlProps = {
helperText?: ReactNode; helperText?: ReactNode;
errorText?: ReactNode; errorText?: ReactNode;
children: JSX.Element; children: JSX.Element;
className?: string;
}; };
export const FormControl = ({ export const FormControl = ({
@@ -55,10 +56,11 @@ export const FormControl = ({
helperText, helperText,
errorText, errorText,
id, id,
isError isError,
className
}: FormControlProps): JSX.Element => { }: FormControlProps): JSX.Element => {
return ( return (
<div> <div className={twMerge('mb-4', className)}>
{typeof label === 'string' ? ( {typeof label === 'string' ? (
<FormLabel label={label} isRequired={isRequired} id={id} /> <FormLabel label={label} isRequired={isRequired} id={id} />
) : ( ) : (
@@ -14,7 +14,7 @@ const iconButtonVariants = cva(
[ [
'button', 'button',
'transition-all', 'transition-all',
'font-inter font-medium', 'font-inter font-medium user-select-none',
'cursor-pointer', 'cursor-pointer',
'inline-flex items-center justify-center', 'inline-flex items-center justify-center',
'relative' 'relative'
@@ -22,9 +22,9 @@ const iconButtonVariants = cva(
{ {
variants: { variants: {
colorSchema: { colorSchema: {
primary: ['bg-primary', 'text-black', 'border-primary hover:bg-opacity-80'], primary: ['bg-primary', 'text-black', 'border-primary hover:opacity-80'],
secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-opacity-80'], secondary: ['bg-mineshaft', 'text-gray-300', 'border-mineshaft hover:bg-bunker-400'],
danger: ['bg-red', 'text-white', 'border-red'] danger: ['bg-[#973939] hover:bg-red', 'text-white', 'border-red']
}, },
variant: { variant: {
solid: '', solid: '',
@@ -40,10 +40,10 @@ const iconButtonVariants = cva(
false: '' false: ''
}, },
size: { size: {
xs: ['text-xs', 'py-1', 'px-2'], xs: ['text-xs', 'py-2', 'px-2'],
sm: ['text-sm', 'py-2', 'px-3'], sm: ['text-sm', 'py-3', 'px-3'],
md: ['text-md', 'py-3', 'px-4'], md: ['text-md', 'py-4', 'px-4'],
lg: ['text-lg', 'py-3', 'px-6'] lg: ['text-lg', 'py-6', 'px-6']
} }
}, },
compoundVariants: [ compoundVariants: [
@@ -97,7 +97,7 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
ariaLabel, ariaLabel,
isDisabled = false, isDisabled = false,
className, className,
size = 'md', size = 'sm',
variant = 'solid', variant = 'solid',
isRounded = true, isRounded = true,
colorSchema = 'primary', colorSchema = 'primary',
+7 -4
View File
@@ -8,10 +8,11 @@ type Props = {
isRequired?: boolean; isRequired?: boolean;
leftIcon?: ReactNode; leftIcon?: ReactNode;
rightIcon?: ReactNode; rightIcon?: ReactNode;
isDisabled?: boolean;
}; };
const inputVariants = cva( 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: { variants: {
size: { size: {
@@ -46,7 +47,7 @@ const inputParentContainerVariants = cva('inline-flex font-inter items-center bo
}, },
isError: { isError: {
true: 'border-red', true: 'border-red',
false: 'border-mineshaft-400' false: 'border-mineshaft-500'
}, },
isFullWidth: { isFullWidth: {
true: 'w-full', true: 'w-full',
@@ -70,6 +71,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
className, className,
isRounded = true, isRounded = true,
isFullWidth = true, isFullWidth = true,
isDisabled,
isError = false, isError = false,
isRequired, isRequired,
leftIcon, leftIcon,
@@ -87,9 +89,10 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
{...props} {...props}
required={isRequired} required={isRequired}
ref={ref} ref={ref}
disabled={isDisabled}
className={twMerge( className={twMerge(
leftIcon ? 'pl-9' : 'pl-4', leftIcon ? 'pl-9' : 'pl-2.5',
rightIcon ? 'pr-9' : 'pr-4', rightIcon ? 'pr-9' : 'pr-2.5',
inputVariants({ className, isError, size, isRounded, variant }) inputVariants({ className, isError, size, isRounded, variant })
)} )}
/> />
+11 -7
View File
@@ -11,33 +11,34 @@ export type ModalContentProps = DialogPrimitive.DialogContentProps & {
title?: ReactNode; title?: ReactNode;
subTitle?: string; subTitle?: string;
footerContent?: ReactNode; footerContent?: ReactNode;
onClose?: () => void;
}; };
export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>( export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
({ children, title, subTitle, className, footerContent, ...props }, forwardedRef) => ( ({ children, title, subTitle, className, footerContent, onClose, ...props }, forwardedRef) => (
<DialogPrimitive.Portal> <DialogPrimitive.Portal>
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
className="fixed inset-0 w-full h-full animate-fadeIn" className="fixed inset-0 h-full w-full animate-fadeIn"
style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} style={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
/> />
<DialogPrimitive.Content {...props} ref={forwardedRef}> <DialogPrimitive.Content {...props} ref={forwardedRef}>
<Card <Card
isRounded isRounded
className={twMerge( 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 className
)} )}
> >
{title && <CardTitle subTitle={subTitle}>{title}</CardTitle>} {title && <CardTitle subTitle={subTitle}>{title}</CardTitle>}
<CardBody>{children}</CardBody> <CardBody>{children}</CardBody>
{footerContent && <CardFooter>{footerContent}</CardFooter>} {footerContent && <CardFooter>{footerContent}</CardFooter>}
<DialogPrimitive.Close aria-label="Close" asChild> <DialogPrimitive.Close aria-label="Close" asChild onClick={onClose}>
<IconButton <IconButton
variant="plain" variant="plain"
ariaLabel="close" 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> </IconButton>
</DialogPrimitive.Close> </DialogPrimitive.Close>
</Card> </Card>
@@ -55,3 +56,6 @@ export const Modal = ({ isOpen, ...props }: ModalProps) => (
export const ModalTrigger = DialogPrimitive.Trigger; export const ModalTrigger = DialogPrimitive.Trigger;
export type ModalTriggerProps = DialogPrimitive.DialogTriggerProps; 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 type { ModalCloseProps, ModalContentProps, ModalProps, ModalTriggerProps } from './Modal';
export { Modal, ModalContent, ModalTrigger } from './Modal'; export { Modal, ModalClose, ModalContent, ModalTrigger } from './Modal';
+20 -14
View File
@@ -10,39 +10,45 @@ type Props = {
children: ReactNode; children: ReactNode;
placeholder?: string; placeholder?: string;
className?: string; className?: string;
dropdownContainerClassName?: string;
isLoading?: boolean; isLoading?: boolean;
}; };
export type SelectProps = SelectPrimitive.SelectProps & Props; export type SelectProps = SelectPrimitive.SelectProps & Props;
export const Select = forwardRef<HTMLButtonElement, SelectProps>( export const Select = forwardRef<HTMLButtonElement, SelectProps>(
({ children, placeholder, className, isLoading, ...props }, ref): JSX.Element => { (
{ children, placeholder, className, isLoading, dropdownContainerClassName, ...props },
ref
): JSX.Element => {
return ( return (
<SelectPrimitive.Root {...props}> <SelectPrimitive.Root {...props}>
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
ref={ref} ref={ref}
className={twMerge( className={twMerge(
`inline-flex items-center justify-between data-[placeholder]:text-gray-500 `inline-flex items-center justify-between rounded-md
px-4 py-2.5 font-inter text-sm text-white rounded-md bg-mineshaft-800`, bg-bunker-800 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-gray-500`,
className className
)} )}
> >
<SelectPrimitive.Value placeholder={placeholder} /> <SelectPrimitive.Value placeholder={placeholder} />
{!props.disabled && (
<SelectPrimitive.Icon className="ml-3"> <SelectPrimitive.Icon className="ml-3">
<FontAwesomeIcon icon={faChevronDown} size="sm" /> <FontAwesomeIcon icon={faChevronDown} size="sm" />
</SelectPrimitive.Icon> </SelectPrimitive.Icon>
)}
</SelectPrimitive.Trigger> </SelectPrimitive.Trigger>
<SelectPrimitive.Portal> <SelectPrimitive.Portal>
<SelectPrimitive.Content <SelectPrimitive.Content
position="popper" className={twMerge(
sideOffset={5} 'relative left-4 top-1 overflow-hidden rounded-md bg-bunker-800 font-inter text-bunker-100 shadow-md',
className="overflow-hidden text-white rounded-md shadow-md font-inter bg-mineshaft-800" dropdownContainerClassName
style={{ width: 'var(--radix-select-trigger-width)' }} )}
> >
<SelectPrimitive.ScrollUpButton> <SelectPrimitive.ScrollUpButton>
<FontAwesomeIcon icon={faChevronUp} size="sm" /> <FontAwesomeIcon icon={faChevronUp} size="sm" />
</SelectPrimitive.ScrollUpButton> </SelectPrimitive.ScrollUpButton>
<SelectPrimitive.Viewport className="p-2"> <SelectPrimitive.Viewport className="p-1.5">
{isLoading ? ( {isLoading ? (
<div className="flex items-center justify-center"> <div className="flex items-center justify-center">
<Spinner size="xs" /> <Spinner size="xs" />
@@ -75,16 +81,16 @@ export const SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(
<SelectPrimitive.Item <SelectPrimitive.Item
{...props} {...props}
className={twMerge( className={twMerge(
`text-sm rounded-sm transition-all hover:text-primary `relative flex cursor-pointer
hover:bg-mineshaft-700 flex items-center pl-10 pr-4 py-2 cursor-pointer select-none items-center rounded-md py-2 pl-10 pr-4 text-sm
select-none outline-none relative`, outline-none transition-all hover:bg-mineshaft-500`,
isSelected && 'text-primary', isSelected && 'bg-primary',
isDisabled && 'text-gray-600 hover:bg-transparent cursor-not-allowed hover:text-gray-600', isDisabled && 'cursor-not-allowed text-gray-600 hover:bg-transparent hover:text-gray-600',
className className
)} )}
ref={forwardedRef} ref={forwardedRef}
> >
<SelectPrimitive.ItemIndicator className="absolute left-2"> <SelectPrimitive.ItemIndicator className="absolute left-3.5">
<FontAwesomeIcon icon={faCheck} size="sm" /> <FontAwesomeIcon icon={faCheck} size="sm" />
</SelectPrimitive.ItemIndicator> </SelectPrimitive.ItemIndicator>
<SelectPrimitive.ItemText className="">{children}</SelectPrimitive.ItemText> <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'; import { twMerge } from 'tailwind-merge';
export type TableContainerProps = { export type TableContainerProps = {
@@ -14,7 +14,7 @@ export const TableContainer = ({
}: TableContainerProps): JSX.Element => ( }: TableContainerProps): JSX.Element => (
<div <div
className={twMerge( 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', isRounded && 'rounded-md',
className className
)} )}
@@ -32,7 +32,7 @@ export type TableProps = {
export const Table = ({ children, className }: TableProps): JSX.Element => ( export const Table = ({ children, className }: TableProps): JSX.Element => (
<table <table
className={twMerge( 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 className
)} )}
> >
@@ -47,7 +47,7 @@ export type THeadProps = {
}; };
export const THead = ({ children, className }: THeadProps): JSX.Element => ( 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} {children}
</thead> </thead>
); );
@@ -56,20 +56,22 @@ export const THead = ({ children, className }: THeadProps): JSX.Element => (
export type TrProps = { export type TrProps = {
children: ReactNode; children: ReactNode;
className?: string; className?: string;
}; } & HTMLAttributes<HTMLTableRowElement>;
export const Tr = ({ children, className }: TrProps): JSX.Element => ( export const Tr = ({ children, className, ...props }: TrProps): JSX.Element => (
<tr className={twMerge('border border-solid border-mineshaft-700', className)}>{children}</tr> <tr className={twMerge('border border-solid border-mineshaft-700 hover:bg-bunker-700', className)} {...props}>
{children}
</tr>
); );
// table head columns // table head columns
export type ThProps = { export type ThProps = {
children: ReactNode; children?: ReactNode;
className?: string; className?: string;
}; };
export const Th = ({ children, className }: ThProps): JSX.Element => ( 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 // table body
@@ -84,10 +86,12 @@ export const TBody = ({ children, className }: TBodyProps): JSX.Element => (
// table body columns // table body columns
export type TdProps = { export type TdProps = {
children: ReactNode; children?: ReactNode;
className?: string; className?: string;
}; } & TdHTMLAttributes<HTMLTableCellElement>;
export const Td = ({ children, className }: TdProps): JSX.Element => ( export const Td = ({ children, className, ...props }: TdProps): JSX.Element => (
<td className={twMerge('text-left px-6 py-3', className)}>{children}</td> <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 './Button';
export * from './Card'; export * from './Card';
export * from './Checkbox'; export * from './Checkbox';
export * from './DeleteActionModal';
export * from './Dropdown'; export * from './Dropdown';
export * from './FormControl'; export * from './FormControl';
export * from './IconButton'; export * from './IconButton';
@@ -12,3 +13,4 @@ export * from './Spinner';
export * from './Switch'; export * from './Switch';
export * from './Table'; export * from './Table';
export * from './TextArea'; export * from './TextArea';
export * from './UpgradePlanModal';
+25 -3
View File
@@ -15,10 +15,32 @@ export const publicPaths = [
`/privacy`, `/privacy`,
`/terms`, `/terms`,
`/subprocessors`, `/subprocessors`,
`/verify-email`,
`/password-reset`
]; ];
export const languageMap = { export const languageMap = {
en: "English", en: 'English',
ko: "한국어", ko: '한국어',
fr: "Français", 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