mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 11:29:02 +00:00
Resolve merge conflicts
This commit is contained in:
@@ -25,7 +25,7 @@
|
|||||||
<img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" />
|
<img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://cloudsmith.io/~infisical/repos/">
|
<a href="https://cloudsmith.io/~infisical/repos/">
|
||||||
<img src="https://img.shields.io/badge/Downloads-45.7k-orange" alt="Cloudsmith downloads" />
|
<img src="https://img.shields.io/badge/Downloads-55.7k-orange" alt="Cloudsmith downloads" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g">
|
<a href="https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g">
|
||||||
<img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" />
|
<img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" />
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
collectCoverageFrom: ['src/*.{js,ts}', '!**/node_modules/**'],
|
||||||
|
modulePaths: ['<rootDir>/src'],
|
||||||
|
testMatch: ['<rootDir>/tests/**/*.test.ts'],
|
||||||
|
setupFiles: ['<rootDir>/test-resources/env-vars.js'],
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/tests/setupTests.ts']
|
||||||
|
};
|
||||||
Generated
+84
-27
@@ -12,7 +12,7 @@
|
|||||||
"@aws-sdk/client-secrets-manager": "^3.267.0",
|
"@aws-sdk/client-secrets-manager": "^3.267.0",
|
||||||
"@godaddy/terminus": "^4.11.2",
|
"@godaddy/terminus": "^4.11.2",
|
||||||
"@octokit/rest": "^19.0.5",
|
"@octokit/rest": "^19.0.5",
|
||||||
"@sentry/node": "^7.14.0",
|
"@sentry/node": "^7.39.0",
|
||||||
"@sentry/tracing": "^7.19.0",
|
"@sentry/tracing": "^7.19.0",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/libsodium-wrappers": "^0.7.10",
|
"@types/libsodium-wrappers": "^0.7.10",
|
||||||
@@ -40,13 +40,13 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mongoose": "^6.7.2",
|
"mongoose": "^6.7.2",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
"posthog-node": "^2.2.2",
|
"posthog-node": "^2.5.4",
|
||||||
"query-string": "^7.1.3",
|
"query-string": "^7.1.3",
|
||||||
"request-ip": "^3.3.0",
|
"request-ip": "^3.3.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"stripe": "^10.7.0",
|
"stripe": "^10.7.0",
|
||||||
"swagger-autogen": "^2.22.0",
|
"swagger-autogen": "^2.22.0",
|
||||||
"swagger-ui-express": "^4.6.0",
|
"swagger-ui-express": "^4.6.2",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
"tweetnacl-util": "^0.15.1",
|
"tweetnacl-util": "^0.15.1",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3",
|
||||||
@@ -2800,13 +2800,13 @@
|
|||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/node": {
|
"node_modules/@sentry/node": {
|
||||||
"version": "7.38.0",
|
"version": "7.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.39.0.tgz",
|
||||||
"integrity": "sha512-jNIN6NZvgzn/oms8RQzffjX8Z0LQDTN6N28nnhzqGCvnfmS1QtTt0FlU+pTuFXZNNSjfGy4XMXMYvLlbvhm2bg==",
|
"integrity": "sha512-oe1OBxgs6t/FizjxkSPtuvJv5wJMO+mLENZkiE0PpBD56JyZrWK48kYIt2ccWAfk6Vh235/oIpmqET150xB4lQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "7.38.0",
|
"@sentry/core": "7.39.0",
|
||||||
"@sentry/types": "7.38.0",
|
"@sentry/types": "7.39.0",
|
||||||
"@sentry/utils": "7.38.0",
|
"@sentry/utils": "7.39.0",
|
||||||
"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",
|
||||||
@@ -2816,6 +2816,39 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sentry/node/node_modules/@sentry/core": {
|
||||||
|
"version": "7.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.39.0.tgz",
|
||||||
|
"integrity": "sha512-45WJIcWWCQnZ8zhHtcrkJjQ4YydmzMWY4pmRuBG7Qp+zrCT6ISoyODcjY+SCHFdvXkiYFi8+bFZa1qG3YQnnYw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.39.0",
|
||||||
|
"@sentry/utils": "7.39.0",
|
||||||
|
"tslib": "^1.9.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/node/node_modules/@sentry/types": {
|
||||||
|
"version": "7.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.39.0.tgz",
|
||||||
|
"integrity": "sha512-5Y83Y8O3dT5zT2jTKEIPMcpn5lUm05KRMaCXuw0sRsv4r9TbBUKeqiSU1LjowT8rB/XNy8m7DHav8+NmogPaJw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sentry/node/node_modules/@sentry/utils": {
|
||||||
|
"version": "7.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.39.0.tgz",
|
||||||
|
"integrity": "sha512-/ZxlPgm1mGgmuMckCTc9iyqDuFTEYNEoMB53IjVFz8ann+37OiWB7Py/QV1rEEsv3xKrGbA8thhRhV9E1sjTlQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/types": "7.39.0",
|
||||||
|
"tslib": "^1.9.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sentry/node/node_modules/tslib": {
|
"node_modules/@sentry/node/node_modules/tslib": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
@@ -10500,9 +10533,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/posthog-node": {
|
"node_modules/posthog-node": {
|
||||||
"version": "2.5.3",
|
"version": "2.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.5.4.tgz",
|
||||||
"integrity": "sha512-kDmBjQHguPrh/rUTKmB0+Hj7C3fq2t+/fcfQkDBGz0f0fEF2WxV5yyxRmd2IF/hFmHxMrGLDkEVjKr78B+judg==",
|
"integrity": "sha512-CdywlVh0CZU05/3MrBc0qY/zsLdU2X9XSz/yL1qMRhbyZhD8lrnuGlI69G2cpzZtli6S/nu64wcmULz/mFFA5w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.27.0"
|
"axios": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -11483,9 +11516,9 @@
|
|||||||
"integrity": "sha512-4J4XekQG0ol4/TyUzMfksrWsMTbw/7JYlT+SFaX7H0xamd1OeuVlUSb/Cbq4qdDx1lc+uLZQW7u2mlImcE8c+w=="
|
"integrity": "sha512-4J4XekQG0ol4/TyUzMfksrWsMTbw/7JYlT+SFaX7H0xamd1OeuVlUSb/Cbq4qdDx1lc+uLZQW7u2mlImcE8c+w=="
|
||||||
},
|
},
|
||||||
"node_modules/swagger-ui-express": {
|
"node_modules/swagger-ui-express": {
|
||||||
"version": "4.6.1",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.2.tgz",
|
||||||
"integrity": "sha512-Pss7YNFKNdq66XKNjRe4IRXKKYNx/LvOSml9TdrZ8/78UpxUHIp9JoXpXWA5Z4L+SCmX63DZ9IPlQ8nnRuncvA==",
|
"integrity": "sha512-MHIOaq9JrTTB3ygUJD+08PbjM5Tt/q7x80yz9VTFIatw8j5uIWKcr90S0h5NLMzFEDC6+eVprtoeA5MDZXCUKQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"swagger-ui-dist": ">=4.11.0"
|
"swagger-ui-dist": ">=4.11.0"
|
||||||
},
|
},
|
||||||
@@ -14361,19 +14394,43 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sentry/node": {
|
"@sentry/node": {
|
||||||
"version": "7.38.0",
|
"version": "7.39.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.39.0.tgz",
|
||||||
"integrity": "sha512-jNIN6NZvgzn/oms8RQzffjX8Z0LQDTN6N28nnhzqGCvnfmS1QtTt0FlU+pTuFXZNNSjfGy4XMXMYvLlbvhm2bg==",
|
"integrity": "sha512-oe1OBxgs6t/FizjxkSPtuvJv5wJMO+mLENZkiE0PpBD56JyZrWK48kYIt2ccWAfk6Vh235/oIpmqET150xB4lQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sentry/core": "7.38.0",
|
"@sentry/core": "7.39.0",
|
||||||
"@sentry/types": "7.38.0",
|
"@sentry/types": "7.39.0",
|
||||||
"@sentry/utils": "7.38.0",
|
"@sentry/utils": "7.39.0",
|
||||||
"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": {
|
"dependencies": {
|
||||||
|
"@sentry/core": {
|
||||||
|
"version": "7.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.39.0.tgz",
|
||||||
|
"integrity": "sha512-45WJIcWWCQnZ8zhHtcrkJjQ4YydmzMWY4pmRuBG7Qp+zrCT6ISoyODcjY+SCHFdvXkiYFi8+bFZa1qG3YQnnYw==",
|
||||||
|
"requires": {
|
||||||
|
"@sentry/types": "7.39.0",
|
||||||
|
"@sentry/utils": "7.39.0",
|
||||||
|
"tslib": "^1.9.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@sentry/types": {
|
||||||
|
"version": "7.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.39.0.tgz",
|
||||||
|
"integrity": "sha512-5Y83Y8O3dT5zT2jTKEIPMcpn5lUm05KRMaCXuw0sRsv4r9TbBUKeqiSU1LjowT8rB/XNy8m7DHav8+NmogPaJw=="
|
||||||
|
},
|
||||||
|
"@sentry/utils": {
|
||||||
|
"version": "7.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.39.0.tgz",
|
||||||
|
"integrity": "sha512-/ZxlPgm1mGgmuMckCTc9iyqDuFTEYNEoMB53IjVFz8ann+37OiWB7Py/QV1rEEsv3xKrGbA8thhRhV9E1sjTlQ==",
|
||||||
|
"requires": {
|
||||||
|
"@sentry/types": "7.39.0",
|
||||||
|
"tslib": "^1.9.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
@@ -20049,9 +20106,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"posthog-node": {
|
"posthog-node": {
|
||||||
"version": "2.5.3",
|
"version": "2.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-2.5.4.tgz",
|
||||||
"integrity": "sha512-kDmBjQHguPrh/rUTKmB0+Hj7C3fq2t+/fcfQkDBGz0f0fEF2WxV5yyxRmd2IF/hFmHxMrGLDkEVjKr78B+judg==",
|
"integrity": "sha512-CdywlVh0CZU05/3MrBc0qY/zsLdU2X9XSz/yL1qMRhbyZhD8lrnuGlI69G2cpzZtli6S/nu64wcmULz/mFFA5w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"axios": "^0.27.0"
|
"axios": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -20779,9 +20836,9 @@
|
|||||||
"integrity": "sha512-4J4XekQG0ol4/TyUzMfksrWsMTbw/7JYlT+SFaX7H0xamd1OeuVlUSb/Cbq4qdDx1lc+uLZQW7u2mlImcE8c+w=="
|
"integrity": "sha512-4J4XekQG0ol4/TyUzMfksrWsMTbw/7JYlT+SFaX7H0xamd1OeuVlUSb/Cbq4qdDx1lc+uLZQW7u2mlImcE8c+w=="
|
||||||
},
|
},
|
||||||
"swagger-ui-express": {
|
"swagger-ui-express": {
|
||||||
"version": "4.6.1",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.6.2.tgz",
|
||||||
"integrity": "sha512-Pss7YNFKNdq66XKNjRe4IRXKKYNx/LvOSml9TdrZ8/78UpxUHIp9JoXpXWA5Z4L+SCmX63DZ9IPlQ8nnRuncvA==",
|
"integrity": "sha512-MHIOaq9JrTTB3ygUJD+08PbjM5Tt/q7x80yz9VTFIatw8j5uIWKcr90S0h5NLMzFEDC6+eVprtoeA5MDZXCUKQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"swagger-ui-dist": ">=4.11.0"
|
"swagger-ui-dist": ">=4.11.0"
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-14
@@ -3,7 +3,7 @@
|
|||||||
"@aws-sdk/client-secrets-manager": "^3.267.0",
|
"@aws-sdk/client-secrets-manager": "^3.267.0",
|
||||||
"@godaddy/terminus": "^4.11.2",
|
"@godaddy/terminus": "^4.11.2",
|
||||||
"@octokit/rest": "^19.0.5",
|
"@octokit/rest": "^19.0.5",
|
||||||
"@sentry/node": "^7.14.0",
|
"@sentry/node": "^7.39.0",
|
||||||
"@sentry/tracing": "^7.19.0",
|
"@sentry/tracing": "^7.19.0",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/libsodium-wrappers": "^0.7.10",
|
"@types/libsodium-wrappers": "^0.7.10",
|
||||||
@@ -31,13 +31,13 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mongoose": "^6.7.2",
|
"mongoose": "^6.7.2",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
"posthog-node": "^2.2.2",
|
"posthog-node": "^2.5.4",
|
||||||
"query-string": "^7.1.3",
|
"query-string": "^7.1.3",
|
||||||
"request-ip": "^3.3.0",
|
"request-ip": "^3.3.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"stripe": "^10.7.0",
|
"stripe": "^10.7.0",
|
||||||
"swagger-autogen": "^2.22.0",
|
"swagger-autogen": "^2.22.0",
|
||||||
"swagger-ui-express": "^4.6.0",
|
"swagger-ui-express": "^4.6.2",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
"tweetnacl-util": "^0.15.1",
|
"tweetnacl-util": "^0.15.1",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3",
|
||||||
@@ -101,17 +101,6 @@
|
|||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"ts-node": "^10.9.1"
|
"ts-node": "^10.9.1"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"preset": "ts-jest",
|
|
||||||
"testEnvironment": "node",
|
|
||||||
"collectCoverageFrom": [
|
|
||||||
"src/*.{js,ts}",
|
|
||||||
"!**/node_modules/**"
|
|
||||||
],
|
|
||||||
"setupFiles": [
|
|
||||||
"<rootDir>/test-resources/env-vars.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"jest-junit": {
|
"jest-junit": {
|
||||||
"outputDirectory": "reports",
|
"outputDirectory": "reports",
|
||||||
"outputName": "jest-junit.xml",
|
"outputName": "jest-junit.xml",
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ export const getEncryptionKey = () => infisical.get('ENCRYPTION_KEY')!;
|
|||||||
export const getSaltRounds = () => parseInt(infisical.get('SALT_ROUNDS')!) || 10;
|
export const getSaltRounds = () => parseInt(infisical.get('SALT_ROUNDS')!) || 10;
|
||||||
export const getJwtAuthLifetime = () => infisical.get('JWT_AUTH_LIFETIME')! || '10d';
|
export const getJwtAuthLifetime = () => infisical.get('JWT_AUTH_LIFETIME')! || '10d';
|
||||||
export const getJwtAuthSecret = () => infisical.get('JWT_AUTH_SECRET')!;
|
export const getJwtAuthSecret = () => infisical.get('JWT_AUTH_SECRET')!;
|
||||||
export const getJwtMfaLifetime = () => infisical.get('JWT_MFA_LIFETIME')!;
|
export const getJwtMfaLifetime = () => infisical.get('JWT_MFA_LIFETIME')! || '5m';
|
||||||
export const getJwtMfaSecret = () => infisical.get('JWT_MFA_LIFETIME')! || '5m';
|
export const getJwtMfaSecret = () => infisical.get('JWT_MFA_LIFETIME')! || '5m';
|
||||||
export const getJwtRefreshLifetime = () => infisical.get('JWT_REFRESH_LIFETIME')! || '90d';
|
export const getJwtRefreshLifetime = () => infisical.get('JWT_REFRESH_LIFETIME')! || '90d';
|
||||||
export const getJwtRefreshSecret = () => infisical.get('JWT_REFRESH_SECRET')!;
|
export const getJwtRefreshSecret = () => infisical.get('JWT_REFRESH_SECRET')!;
|
||||||
export const getJwtServiceSecret = () => infisical.get('JWT_SERVICE_SECRET')!;
|
export const getJwtServiceSecret = () => infisical.get('JWT_SERVICE_SECRET')!;
|
||||||
export const getJwtSignupLifetime = () => infisical.get('JWT_SIGNUP_LIFETIME')!;
|
export const getJwtSignupLifetime = () => infisical.get('JWT_SIGNUP_LIFETIME')! || '15m';
|
||||||
export const getJwtSignupSecret = () => infisical.get('JWT_SIGNUP_SECRET')!;
|
export const getJwtSignupSecret = () => infisical.get('JWT_SIGNUP_SECRET')!;
|
||||||
export const getMongoURL = () => infisical.get('MONGO_URL')!;
|
export const getMongoURL = () => infisical.get('MONGO_URL')!;
|
||||||
export const getNodeEnv = () => infisical.get('NODE_ENV')!;
|
export const getNodeEnv = () => infisical.get('NODE_ENV')!;
|
||||||
@@ -48,3 +48,4 @@ export const getStripeSecretKey = () => infisical.get('STRIPE_SECRET_KEY')!;
|
|||||||
export const getStripeWebhookSecret = () => infisical.get('STRIPE_WEBHOOK_SECRET')!;
|
export const getStripeWebhookSecret = () => infisical.get('STRIPE_WEBHOOK_SECRET')!;
|
||||||
export const getTelemetryEnabled = () => infisical.get('TELEMETRY_ENABLED')! !== 'false' && true;
|
export const getTelemetryEnabled = () => infisical.get('TELEMETRY_ENABLED')! !== 'false' && true;
|
||||||
export const getLoopsApiKey = () => infisical.get('LOOPS_API_KEY')!;
|
export const getLoopsApiKey = () => infisical.get('LOOPS_API_KEY')!;
|
||||||
|
export const getSmtpConfigured = () => infisical.get('SMTP_HOST') == '' || infisical.get('SMTP_HOST') == undefined ? false : true
|
||||||
@@ -7,7 +7,7 @@ import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
|
|||||||
import { sendMail } from '../../helpers/nodemailer';
|
import { sendMail } from '../../helpers/nodemailer';
|
||||||
import { TokenService } from '../../services';
|
import { TokenService } from '../../services';
|
||||||
import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED, TOKEN_EMAIL_ORG_INVITATION } from '../../variables';
|
import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED, TOKEN_EMAIL_ORG_INVITATION } from '../../variables';
|
||||||
import { getSiteURL, getJwtSignupLifetime, getJwtSignupSecret } from '../../config';
|
import { getSiteURL, getJwtSignupLifetime, getJwtSignupSecret, getSmtpConfigured } from '../../config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete organization membership with id [membershipOrgId] from organization
|
* Delete organization membership with id [membershipOrgId] from organization
|
||||||
@@ -99,9 +99,11 @@ export const changeMembershipOrgRole = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||||
let invitee, inviteeMembershipOrg;
|
let invitee, inviteeMembershipOrg, completeInviteLink;
|
||||||
try {
|
try {
|
||||||
const { organizationId, inviteeEmail } = req.body;
|
const { organizationId, inviteeEmail } = req.body;
|
||||||
|
const host = req.headers.host;
|
||||||
|
const siteUrl = `${req.protocol}://${host}`;
|
||||||
|
|
||||||
// validate membership
|
// validate membership
|
||||||
const membershipOrg = await MembershipOrg.findOne({
|
const membershipOrg = await MembershipOrg.findOne({
|
||||||
@@ -181,6 +183,10 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
callback_url: getSiteURL() + '/signupinvite'
|
callback_url: getSiteURL() + '/signupinvite'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!getSmtpConfigured()) {
|
||||||
|
completeInviteLink = `${siteUrl + '/signupinvite'}?token=${token}&to=${inviteeEmail}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateSubscriptionOrgQuantity({ organizationId });
|
await updateSubscriptionOrgQuantity({ organizationId });
|
||||||
@@ -193,7 +199,8 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: `Sent an invite link to ${req.body.inviteeEmail}`
|
message: `Sent an invite link to ${req.body.inviteeEmail}`,
|
||||||
|
completeInviteLink
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from '../../helpers/signup';
|
} from '../../helpers/signup';
|
||||||
import { createToken } from '../../helpers/auth';
|
import { createToken } from '../../helpers/auth';
|
||||||
import { BadRequestError } from '../../utils/errors';
|
import { BadRequestError } from '../../utils/errors';
|
||||||
import { getInviteOnlySignup, getJwtSignupLifetime, getJwtSignupSecret } from '../../config';
|
import { getInviteOnlySignup, getJwtSignupLifetime, getJwtSignupSecret, getSmtpConfigured } from '../../config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signup step 1: Initialize account for user under email [email] and send a verification code
|
* Signup step 1: Initialize account for user under email [email] and send a verification code
|
||||||
@@ -21,7 +21,7 @@ export const beginEmailSignup = async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
email = req.body.email;
|
email = req.body.email;
|
||||||
|
|
||||||
if (getInviteOnlySignup() || false) {
|
if (getInviteOnlySignup()) {
|
||||||
// Only one user can create an account without being invited. The rest need to be invited in order to make an account
|
// Only one user can create an account without being invited. The rest need to be invited in order to make an account
|
||||||
const userCount = await User.countDocuments({})
|
const userCount = await User.countDocuments({})
|
||||||
if (userCount != 0) {
|
if (userCount != 0) {
|
||||||
@@ -66,7 +66,7 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
|
|||||||
const { email, code } = req.body;
|
const { email, code } = req.body;
|
||||||
|
|
||||||
// initialize user account
|
// initialize user account
|
||||||
user = await User.findOne({ email });
|
user = await User.findOne({ email }).select('+publicKey');
|
||||||
if (user && user?.publicKey) {
|
if (user && user?.publicKey) {
|
||||||
// case: user has already completed account
|
// case: user has already completed account
|
||||||
return res.status(403).send({
|
return res.status(403).send({
|
||||||
@@ -75,10 +75,12 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verify email
|
// verify email
|
||||||
|
if (getSmtpConfigured()) {
|
||||||
await checkEmailVerification({
|
await checkEmailVerification({
|
||||||
email,
|
email,
|
||||||
code
|
code
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = await new User({
|
user = await new User({
|
||||||
|
|||||||
@@ -317,6 +317,18 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
tag: string;
|
tag: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface VerifyMfaTokenRes {
|
||||||
|
encryptionVersion: number;
|
||||||
|
protectedKey?: string;
|
||||||
|
protectedKeyIV?: string;
|
||||||
|
protectedKeyTag?: string;
|
||||||
|
token: string;
|
||||||
|
publicKey: string;
|
||||||
|
encryptedPrivateKey: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
}
|
||||||
|
|
||||||
const resObj: VerifyMfaTokenRes = {
|
const resObj: VerifyMfaTokenRes = {
|
||||||
encryptionVersion: user.encryptionVersion,
|
encryptionVersion: user.encryptionVersion,
|
||||||
token: tokens.token,
|
token: tokens.token,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import handlebars from 'handlebars';
|
import handlebars from 'handlebars';
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
import { getSmtpFromName, getSmtpFromAddress } from '../config';
|
import { getSmtpFromName, getSmtpFromAddress, getSmtpConfigured } from '../config';
|
||||||
|
|
||||||
let smtpTransporter: nodemailer.Transporter;
|
let smtpTransporter: nodemailer.Transporter;
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ const sendMail = async ({
|
|||||||
recipients: string[];
|
recipients: string[];
|
||||||
substitutions: any;
|
substitutions: any;
|
||||||
}) => {
|
}) => {
|
||||||
|
if (getSmtpConfigured()) {
|
||||||
try {
|
try {
|
||||||
const html = fs.readFileSync(
|
const html = fs.readFileSync(
|
||||||
path.resolve(__dirname, '../templates/' + template),
|
path.resolve(__dirname, '../templates/' + template),
|
||||||
@@ -43,6 +44,7 @@ const sendMail = async ({
|
|||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setTransporter = (transporter: nodemailer.Transporter) => {
|
const setTransporter = (transporter: nodemailer.Transporter) => {
|
||||||
|
|||||||
@@ -74,9 +74,11 @@ import {
|
|||||||
} from './config';
|
} from './config';
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
|
if (process.env.INFISICAL_TOKEN != "" || process.env.INFISICAL_TOKEN != undefined) {
|
||||||
await infisical.connect({
|
await infisical.connect({
|
||||||
token: process.env.INFISICAL_TOKEN!
|
token: process.env.INFISICAL_TOKEN!
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
logTelemetryMessage();
|
logTelemetryMessage();
|
||||||
setTransporter(initSmtp());
|
setTransporter(initSmtp());
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import express, { Request, Response } from 'express';
|
import express, { Request, Response } from 'express';
|
||||||
|
import { getSmtpConfigured } from '../../config';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -8,6 +9,7 @@ router.get(
|
|||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
message: 'Ok',
|
message: 'Ok',
|
||||||
|
emailConfigured: getSmtpConfigured()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { it, expect } from '@jest/globals';
|
||||||
|
|
||||||
|
it('should return true', () => {
|
||||||
|
expect(true).toBeTruthy();
|
||||||
|
});
|
||||||
+3
-1
@@ -4,7 +4,10 @@ go 1.19
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/keyring v1.2.2
|
github.com/99designs/keyring v1.2.2
|
||||||
|
github.com/mattn/go-isatty v0.0.14
|
||||||
|
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a
|
||||||
github.com/muesli/mango-cobra v1.2.0
|
github.com/muesli/mango-cobra v1.2.0
|
||||||
|
github.com/muesli/reflow v0.3.0
|
||||||
github.com/muesli/roff v0.1.0
|
github.com/muesli/roff v0.1.0
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||||
@@ -22,7 +25,6 @@ require (
|
|||||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.3.3 // indirect
|
github.com/mitchellh/mapstructure v1.3.3 // indirect
|
||||||
github.com/mtibben/percent v0.2.1 // indirect
|
github.com/mtibben/percent v0.2.1 // indirect
|
||||||
|
|||||||
+6
-1
@@ -56,6 +56,7 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||||
@@ -63,12 +64,16 @@ github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
|||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
|
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
|
||||||
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
|
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
|
||||||
|
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4=
|
||||||
|
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||||
github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI=
|
github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI=
|
||||||
github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4=
|
github.com/muesli/mango v0.1.0/go.mod h1:5XFpbC8jY5UUv89YQciiXNlbi+iJgt29VDC5xbzrLL4=
|
||||||
github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg=
|
github.com/muesli/mango-cobra v1.2.0 h1:DQvjzAM0PMZr85Iv9LIMaYISpTOliMEg+uMFtNbYvWg=
|
||||||
github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA=
|
github.com/muesli/mango-cobra v1.2.0/go.mod h1:vMJL54QytZAJhCT13LPVDfkvCUJ5/4jNUKF/8NC2UjA=
|
||||||
github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg=
|
github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe7Sg=
|
||||||
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
|
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
|
||||||
|
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||||
|
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||||
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
||||||
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
@@ -78,6 +83,7 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
|
|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
@@ -106,7 +112,6 @@ go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAV
|
|||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
|||||||
@@ -263,10 +263,10 @@ var secretsSetCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Print secret operations
|
// Print secret operations
|
||||||
headers := []string{"SECRET NAME", "SECRET VALUE", "STATUS"}
|
headers := [...]string{"SECRET NAME", "SECRET VALUE", "STATUS"}
|
||||||
rows := [][]string{}
|
rows := [][3]string{}
|
||||||
for _, secretOperation := range secretOperations {
|
for _, secretOperation := range secretOperations {
|
||||||
rows = append(rows, []string{secretOperation.SecretKey, secretOperation.SecretValue, secretOperation.SecretOperation})
|
rows = append(rows, [...]string{secretOperation.SecretKey, secretOperation.SecretValue, secretOperation.SecretOperation})
|
||||||
}
|
}
|
||||||
|
|
||||||
visualize.Table(headers, rows)
|
visualize.Table(headers, rows)
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package visualize
|
|||||||
import "github.com/Infisical/infisical-merge/packages/models"
|
import "github.com/Infisical/infisical-merge/packages/models"
|
||||||
|
|
||||||
func PrintAllSecretDetails(secrets []models.SingleEnvironmentVariable) {
|
func PrintAllSecretDetails(secrets []models.SingleEnvironmentVariable) {
|
||||||
rows := [][]string{}
|
rows := [][3]string{}
|
||||||
for _, secret := range secrets {
|
for _, secret := range secrets {
|
||||||
rows = append(rows, []string{secret.Key, secret.Value, secret.Type})
|
rows = append(rows, [...]string{secret.Key, secret.Value, secret.Type})
|
||||||
}
|
}
|
||||||
|
|
||||||
headers := []string{"SECRET NAME", "SECRET VALUE", "SECRET TYPE"}
|
headers := [...]string{"SECRET NAME", "SECRET VALUE", "SECRET TYPE"}
|
||||||
|
|
||||||
Table(headers, rows)
|
Table(headers, rows)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,14 @@ package visualize
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/jedib0t/go-pretty/table"
|
"github.com/jedib0t/go-pretty/table"
|
||||||
|
"github.com/mattn/go-isatty"
|
||||||
|
"github.com/muesli/ansi"
|
||||||
|
"github.com/muesli/reflow/truncate"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TableOptions struct {
|
type TableOptions struct {
|
||||||
@@ -16,8 +22,35 @@ type TableOptions struct {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
const (
|
||||||
|
// combined width of the table borders and padding
|
||||||
|
borderWidths = 10
|
||||||
|
// char to indicate that a string has been truncated
|
||||||
|
ellipsis = "…"
|
||||||
|
)
|
||||||
|
|
||||||
// Given headers and rows, this function will print out a table
|
// Given headers and rows, this function will print out a table
|
||||||
func Table(headers []string, rows [][]string) {
|
func Table(headers [3]string, rows [][3]string) {
|
||||||
|
// if we're not in a terminal or cygwin terminal, don't truncate the secret value
|
||||||
|
shouldTruncate := isatty.IsTerminal(os.Stdout.Fd())
|
||||||
|
|
||||||
|
// This will return an error if we're not in a terminal or
|
||||||
|
// if the terminal is a cygwin terminal like Git Bash.
|
||||||
|
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
if shouldTruncate {
|
||||||
|
log.Errorf("error getting terminal size: %s", err)
|
||||||
|
} else {
|
||||||
|
log.Debug(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
longestSecretName, longestSecretType := getLongestValues(append(rows, headers))
|
||||||
|
availableWidth := width - longestSecretName - longestSecretType - borderWidths
|
||||||
|
if availableWidth < 0 {
|
||||||
|
availableWidth = 0
|
||||||
|
}
|
||||||
|
|
||||||
t := table.NewWriter()
|
t := table.NewWriter()
|
||||||
t.SetOutputMirror(os.Stdout)
|
t.SetOutputMirror(os.Stdout)
|
||||||
t.SetStyle(table.StyleLight)
|
t.SetStyle(table.StyleLight)
|
||||||
@@ -35,7 +68,11 @@ func Table(headers []string, rows [][]string) {
|
|||||||
t.AppendHeader(tableHeaders)
|
t.AppendHeader(tableHeaders)
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
tableRow := table.Row{}
|
tableRow := table.Row{}
|
||||||
for _, val := range row {
|
for i, val := range row {
|
||||||
|
// only truncate the first column (secret value)
|
||||||
|
if i == 1 && stringWidth(val) > availableWidth && shouldTruncate {
|
||||||
|
val = truncate.StringWithTail(val, uint(availableWidth), ellipsis)
|
||||||
|
}
|
||||||
tableRow = append(tableRow, val)
|
tableRow = append(tableRow, val)
|
||||||
}
|
}
|
||||||
t.AppendRow(tableRow)
|
t.AppendRow(tableRow)
|
||||||
@@ -43,3 +80,28 @@ func Table(headers []string, rows [][]string) {
|
|||||||
|
|
||||||
t.Render()
|
t.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getLongestValues returns the length of the longest secret name and type from all rows (including the header).
|
||||||
|
func getLongestValues(rows [][3]string) (longestSecretName, longestSecretType int) {
|
||||||
|
for _, row := range rows {
|
||||||
|
if len(row[0]) > longestSecretName {
|
||||||
|
longestSecretName = stringWidth(row[0])
|
||||||
|
}
|
||||||
|
if len(row[2]) > longestSecretType {
|
||||||
|
longestSecretType = stringWidth(row[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringWidth returns the width of a string.
|
||||||
|
// ANSI escape sequences are ignored and double-width characters are handled correctly.
|
||||||
|
func stringWidth(str string) (width int) {
|
||||||
|
for _, l := range strings.Split(str, "\n") {
|
||||||
|
w := ansi.PrintableRuneWidth(l)
|
||||||
|
if w > width {
|
||||||
|
width = w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
AWSTemplateFormatVersion: 2010-09-09
|
||||||
|
Description: >-
|
||||||
|
CloudFormation template to deploy Infisical on a EC2 instance with a
|
||||||
|
DocumentDB instance
|
||||||
|
Parameters:
|
||||||
|
KeyPairName:
|
||||||
|
Description: The name of the EC2 Key Pair to enable SSH access to the instance
|
||||||
|
Type: "AWS::EC2::KeyPair::KeyName"
|
||||||
|
VpcId:
|
||||||
|
Description: The ID of the VPC in which to launch the instance
|
||||||
|
Type: "AWS::EC2::VPC::Id"
|
||||||
|
DocumentDBUsername:
|
||||||
|
Description: The username for the DocumentDB instance
|
||||||
|
Type: String
|
||||||
|
MinLength: 5
|
||||||
|
DocumentDBPassword:
|
||||||
|
Description: The password for the DocumentDB instance (minimum 8 characters)
|
||||||
|
Type: String
|
||||||
|
MinLength: 8
|
||||||
|
NoEcho: true
|
||||||
|
Resources:
|
||||||
|
DocumentDBCluster:
|
||||||
|
Type: "AWS::DocDB::DBCluster"
|
||||||
|
Properties:
|
||||||
|
EngineVersion: 4.0.0
|
||||||
|
StorageEncrypted: true
|
||||||
|
MasterUsername: !Ref DocumentDBUsername
|
||||||
|
MasterUserPassword: !Ref DocumentDBPassword
|
||||||
|
VpcSecurityGroupIds:
|
||||||
|
- !Ref DocumentDBClusterSecurityGroup
|
||||||
|
DBClusterParameterGroupName: !Ref DBClusterParameterGroup
|
||||||
|
Metadata:
|
||||||
|
"AWS::CloudFormation::Designer":
|
||||||
|
id: 73b974cf-eed3-4f7d-8657-6a6746bac169
|
||||||
|
DependsOn:
|
||||||
|
- DBClusterParameterGroup
|
||||||
|
DBClusterParameterGroup:
|
||||||
|
Type: "AWS::DocDB::DBClusterParameterGroup"
|
||||||
|
Properties:
|
||||||
|
Description: "description"
|
||||||
|
Family: "docdb4.0"
|
||||||
|
Parameters:
|
||||||
|
tls: "disabled"
|
||||||
|
ttl_monitor: "disabled"
|
||||||
|
Tags:
|
||||||
|
- Key: "String"
|
||||||
|
Value: "String"
|
||||||
|
DocumentDBInstance:
|
||||||
|
Type: "AWS::DocDB::DBInstance"
|
||||||
|
Properties:
|
||||||
|
DBInstanceClass: db.t4g.medium
|
||||||
|
DBClusterIdentifier: !Ref DocumentDBCluster
|
||||||
|
Metadata:
|
||||||
|
"AWS::CloudFormation::Designer":
|
||||||
|
id: f04cee38-175e-4432-9ad7-62ca28bbf935
|
||||||
|
DocumentDBClusterSecurityGroup:
|
||||||
|
Type: AWS::EC2::SecurityGroup
|
||||||
|
Properties:
|
||||||
|
GroupDescription: Allow inbound traffic for DocumentDB cluster
|
||||||
|
VpcId: !Ref VpcId
|
||||||
|
SecurityGroupIngress:
|
||||||
|
- IpProtocol: tcp
|
||||||
|
FromPort: 27017
|
||||||
|
ToPort: 27017
|
||||||
|
SourceSecurityGroupId: !Ref InstanceSecurityGroup
|
||||||
|
EC2Instance:
|
||||||
|
Type: "AWS::EC2::Instance"
|
||||||
|
Properties:
|
||||||
|
ImageId: ami-0557a15b87f6559cf
|
||||||
|
InstanceType: t2.medium
|
||||||
|
KeyName: !Ref KeyPairName
|
||||||
|
UserData:
|
||||||
|
Fn::Base64: !Sub |
|
||||||
|
#!/bin/bash
|
||||||
|
cd /home/ubuntu
|
||||||
|
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||||
|
sh get-docker.sh
|
||||||
|
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
|
sudo chmod +x /usr/local/bin/docker-compose
|
||||||
|
git clone https://github.com/Infisical/infisical.git
|
||||||
|
cd infisical
|
||||||
|
|
||||||
|
DOCUMENT_DB_CONNECTION_URL="mongodb://${DocumentDBUsername}:${DocumentDBPassword}@${DocumentDBCluster.Endpoint}:${DocumentDBCluster.Port}/infisical?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false"
|
||||||
|
|
||||||
|
ENCRYPTION_KEY=$(openssl rand -hex 16)
|
||||||
|
JWT_SIGNUP_SECRET=$(openssl rand -hex 16)
|
||||||
|
JWT_REFRESH_SECRET=$(openssl rand -hex 16)
|
||||||
|
JWT_AUTH_SECRET=$(openssl rand -hex 16)
|
||||||
|
JWT_SERVICE_SECRET=$(openssl rand -hex 16)
|
||||||
|
|
||||||
|
touch .env
|
||||||
|
|
||||||
|
echo "ENCRYPTION_KEY=${!ENCRYPTION_KEY}" >> .env
|
||||||
|
echo "JWT_SIGNUP_SECRET=${!JWT_SIGNUP_SECRET}" >> .env
|
||||||
|
echo "JWT_REFRESH_SECRET=${!JWT_REFRESH_SECRET}" >> .env
|
||||||
|
echo "JWT_AUTH_SECRET=${!JWT_AUTH_SECRET}" >> .env
|
||||||
|
echo "JWT_SERVICE_SECRET=${!JWT_SERVICE_SECRET}" >> .env
|
||||||
|
echo "MONGO_URL=${!DOCUMENT_DB_CONNECTION_URL}" >> .env
|
||||||
|
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
SecurityGroupIds:
|
||||||
|
- !Ref InstanceSecurityGroup
|
||||||
|
Tags:
|
||||||
|
- Key: Name
|
||||||
|
Value: infisical
|
||||||
|
Metadata:
|
||||||
|
"AWS::CloudFormation::Designer":
|
||||||
|
id: 2c0a771c-5002-4785-9848-0377e33cd0e9
|
||||||
|
DependsOn:
|
||||||
|
- DocumentDBInstance
|
||||||
|
|
||||||
|
InstanceSecurityGroup:
|
||||||
|
Type: "AWS::EC2::SecurityGroup"
|
||||||
|
Properties:
|
||||||
|
GroupDescription: Allow SSH and HTTP traffic
|
||||||
|
SecurityGroupIngress:
|
||||||
|
- IpProtocol: tcp
|
||||||
|
FromPort: 22
|
||||||
|
ToPort: 22
|
||||||
|
CidrIp: 0.0.0.0/0
|
||||||
|
- IpProtocol: tcp
|
||||||
|
FromPort: 80
|
||||||
|
ToPort: 80
|
||||||
|
CidrIp: 0.0.0.0/0
|
||||||
|
VpcId: !Ref VpcId
|
||||||
|
Metadata:
|
||||||
|
"AWS::CloudFormation::Designer":
|
||||||
|
id: 1fd6856a-11e5-4369-84fa-d18d4011b3de
|
||||||
|
|
||||||
|
Outputs:
|
||||||
|
InstanceIP:
|
||||||
|
Value: !GetAtt EC2Instance.PublicIp
|
||||||
|
Metadata:
|
||||||
|
"AWS::CloudFormation::Designer":
|
||||||
|
1fd6856a-11e5-4369-84fa-d18d4011b3de:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 60
|
||||||
|
"y": 90
|
||||||
|
z: 1
|
||||||
|
embeds: []
|
||||||
|
2c0a771c-5002-4785-9848-0377e33cd0e9:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 180
|
||||||
|
"y": 90
|
||||||
|
z: 1
|
||||||
|
embeds: []
|
||||||
|
isassociatedwith:
|
||||||
|
- 1fd6856a-11e5-4369-84fa-d18d4011b3de
|
||||||
|
dependson:
|
||||||
|
- 2cabaada-fbdb-4945-bf95-a0406704dd5a
|
||||||
|
- f04cee38-175e-4432-9ad7-62ca28bbf935
|
||||||
|
73b974cf-eed3-4f7d-8657-6a6746bac169:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 390
|
||||||
|
"y": 210
|
||||||
|
z: 1
|
||||||
|
embeds: []
|
||||||
|
f04cee38-175e-4432-9ad7-62ca28bbf935:
|
||||||
|
size:
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
position:
|
||||||
|
x: 270
|
||||||
|
"y": 90
|
||||||
|
z: 1
|
||||||
|
embeds: []
|
||||||
+1
-1
@@ -4,7 +4,7 @@ description: "Frequently Asked Questions about Infisical"
|
|||||||
---
|
---
|
||||||
|
|
||||||
Frequently asked questions about the CLI can be found on this page.
|
Frequently asked questions about the CLI can be found on this page.
|
||||||
If you can't find the answer you're looking for, please create an issue on our GitHub repository or join our Slack channel for additional support.
|
If you can't find the answer you are looking for, please create an issue on our GitHub repository or join our Slack channel for additional support.
|
||||||
|
|
||||||
<Accordion title="I'm getting a Keyring related error message when trying to login" defaultOpen="true">
|
<Accordion title="I'm getting a Keyring related error message when trying to login" defaultOpen="true">
|
||||||
By default, the CLI will choose the most suitable store available on your system.
|
By default, the CLI will choose the most suitable store available on your system.
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ Start syncing environment variables with [Infisical Cloud](https://app.infisical
|
|||||||
>
|
>
|
||||||
Learn how to configure and deploy Infisical.
|
Learn how to configure and deploy Infisical.
|
||||||
</Card>
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
href="/integrations/overview"
|
href="/integrations/overview"
|
||||||
title="Integrations"
|
title="Integrations"
|
||||||
@@ -38,16 +40,3 @@ Start syncing environment variables with [Infisical Cloud](https://app.infisical
|
|||||||
>
|
>
|
||||||
Explore integrations for Docker, AWS, Heroku, etc.
|
Explore integrations for Docker, AWS, Heroku, etc.
|
||||||
</Card>
|
</Card>
|
||||||
</CardGroup>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
title="Set up a 1x1 with an Infisical Engineer"
|
|
||||||
iconType="duotone"
|
|
||||||
color="#ca8b04"
|
|
||||||
href="https://calendly.com/maidull/30min"
|
|
||||||
>
|
|
||||||
Our team is happy to help you get started with Infisical. If you have any
|
|
||||||
questions or want to learn how you can leverage Infisical within your
|
|
||||||
infrastructure, **[set up a 1-on-1 with an Infisical
|
|
||||||
engineer](https://cal.com/maidul/15min)**.
|
|
||||||
</Card>
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -39,10 +39,10 @@ Prerequisites:
|
|||||||
Starting your service with the Infisical CLI pulls your secrets from Infisical and injects them into your service.
|
Starting your service with the Infisical CLI pulls your secrets from Infisical and injects them into your service.
|
||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
CMD ["infisical", "run", "---", "[your service 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 && ..."]
|
||||||
@@ -55,7 +55,7 @@ Head to your project settings in Infisical Cloud to generate an [Infisical Token
|
|||||||
## Feed Docker your Infisical Token
|
## Feed Docker your Infisical Token
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --env INFISICAL_TOKEN=[token]...
|
docker run --env INFISICAL_TOKEN=[token] [DOCKER-IMAGE]...
|
||||||
```
|
```
|
||||||
|
|
||||||
<Info>
|
<Info>
|
||||||
|
|||||||
+8
-18
@@ -44,11 +44,6 @@
|
|||||||
"icon": "shield-halved",
|
"icon": "shield-halved",
|
||||||
"url": "security"
|
"url": "security"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Self-hosting",
|
|
||||||
"icon": "server",
|
|
||||||
"url": "self-hosting"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "SDKs",
|
"name": "SDKs",
|
||||||
"icon": "puzzle-piece",
|
"icon": "puzzle-piece",
|
||||||
@@ -101,6 +96,14 @@
|
|||||||
"getting-started/dashboard/token"
|
"getting-started/dashboard/token"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"group": "Self-hosting",
|
||||||
|
"pages": [
|
||||||
|
"self-hosting/overview",
|
||||||
|
"self-hosting/configuration/envars",
|
||||||
|
"self-hosting/configuration/email"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"group": "Command line",
|
"group": "Command line",
|
||||||
"pages": [
|
"pages": [
|
||||||
@@ -171,12 +174,6 @@
|
|||||||
"integrations/platforms/pm2"
|
"integrations/platforms/pm2"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"group": "Self-hosting",
|
|
||||||
"pages": [
|
|
||||||
"self-hosting/overview"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"group": "Deployment options",
|
"group": "Deployment options",
|
||||||
"pages": [
|
"pages": [
|
||||||
@@ -184,13 +181,6 @@
|
|||||||
"self-hosting/deployments/kubernetes"
|
"self-hosting/deployments/kubernetes"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"group": "Configuration",
|
|
||||||
"pages": [
|
|
||||||
"self-hosting/configuration/envars",
|
|
||||||
"self-hosting/configuration/email"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"group": "Overview",
|
"group": "Overview",
|
||||||
"pages": [
|
"pages": [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Email"
|
title: "Configure email service"
|
||||||
description: "How to configure your email when self-hosting Infisical."
|
description: "How to configure your email when self-hosting Infisical."
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +1,151 @@
|
|||||||
---
|
---
|
||||||
title: "Environment Variables"
|
title: "All environment variables"
|
||||||
description: "How to configure your environment variables when self-hosting Infisical."
|
description: "Configure your environment variables when self-hosting Infisical."
|
||||||
---
|
---
|
||||||
|
|
||||||
Configuring Infisical requires setting some environment variables. There is a file called [`.env.example`](https://github.com/Infisical/infisical/blob/main/.env.example) at the root directory of our main repo that you can use to create a `.env` file before you start the server.
|
## Backend environment variables
|
||||||
|
|
||||||
| Variable | Description | Default Value |
|
Depending on your choosen self hosted deployment method, you may need to configured at least the required environment variable listed below.
|
||||||
| ----------------------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
|
Other environment variables are listed below to increase the functionality of your self hosted instance based on your use case.
|
||||||
| `ENCRYPTION_KEY` | ❗️ Strong hex encryption key | `None` |
|
|
||||||
| `JWT_SIGNUP_SECRET` | ❗️ JWT token secret | `None` |
|
<Tabs>
|
||||||
| `JWT_REFRESH_SECRET` | ❗️ JWT token secret | `None` |
|
<Tab title="Required">
|
||||||
| `JWT_AUTH_SECRET` | ❗️ JWT token secret | `None` |
|
<ParamField query="ENCRYPTION_KEY" type="string" default="none" required>
|
||||||
| `JWT_MFA_SECRET` | ❗️ JWT token secret | `None` |
|
Must be a random 32 character length hex string
|
||||||
| `JWT_SERVICE_SECRET` | ❗️ JWT token secret | `None` |
|
</ParamField>
|
||||||
| `JWT_SIGNUP_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `15m` |
|
|
||||||
| `JWT_REFRESH_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `90d` |
|
<ParamField query="JWT_SIGNUP_SECRET" type="string" default="none" required>
|
||||||
| `JWT_AUTH_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `10d` |
|
Must be a random 32 character length hex string
|
||||||
| `JWT_MFA_LIFETIME` | JWT token lifetime expressed in seconds or a string describing a time span (e.g. 60, "2 days", "10h", "7d") | `5m` |
|
</ParamField>
|
||||||
| `MONGO_URL` | ❗️ MongoDB instance connection string either to container instance or MongoDB Cloud | `None` |
|
|
||||||
| `MONGO_USERNAME` | MongoDB username if using container | `None` |
|
<ParamField query="JWT_REFRESH_SECRET" type="string" default="none" required>
|
||||||
| `MONGO_PASSWORD` | MongoDB password if using container | `None` |
|
Must be a random 32 character length hex string
|
||||||
| `SITE_URL` | ❗️ Site URL - should be an absolute URL including the protocol (e.g. `https://app.infisical.com`) | `None` |
|
</ParamField>
|
||||||
| `SMTP_HOST` | ❗️ Hostname to connect to for establishing SMTP connections | `None` |
|
|
||||||
| `SMTP_USERNAME` | ❗️ Credential to connect to host (e.g. `[email protected]`) | `None` |
|
<ParamField query="JWT_AUTH_SECRET" type="string" default="none" required>
|
||||||
| `SMTP_PASSWORD` | ❗️ Credential to connect to host | `None` |
|
Must be a random 32 character length hex string
|
||||||
| `SMTP_PORT` | Port to connect to for establishing SMTP connections | `587` |
|
</ParamField>
|
||||||
| `SMTP_SECURE` | If true, use TLS when connecting to host. If false, TLS will be used if STARTTLS is supported | `false` |
|
|
||||||
| `SMTP_FROM_ADDRESS` | ❗️ Email address to be used for sending emails (e.g. `[email protected]`) | `None` |
|
<ParamField query="JWT_MFA_SECRET" type="string" default="none" required>
|
||||||
| `SMTP_FROM_NAME` | Name label to be used in From field (e.g. `Team`) | `Infisical` |
|
Must be a random 32 character length hex string
|
||||||
| `TELEMETRY_ENABLED` | `true` or `false`. [More](../overview). | `true` |
|
</ParamField>
|
||||||
| `LICENSE_KEY` | License key if using Infisical Enterprise Edition | `true` |
|
|
||||||
| `CLIENT_ID_HEROKU` | OAuth2 client ID for Heroku integration | `None` |
|
<ParamField query="JWT_SERVICE_SECRET" type="string" default="none" required>
|
||||||
| `CLIENT_ID_VERCEL` | OAuth2 client ID for Vercel integration | `None` |
|
Must be a random 32 character length hex string
|
||||||
| `CLIENT_ID_NETLIFY` | OAuth2 client ID for Netlify integration | `None` |
|
</ParamField>
|
||||||
| `CLIENT_ID_GITHUB` | OAuth2 client ID for GitHub integration | `None` |
|
|
||||||
| `CLIENT_SECRET_HEROKU` | OAuth2 client secret for Heroku integration | `None` |
|
<ParamField query="MONGO_URL" type="string" default="none" required>
|
||||||
| `CLIENT_SECRET_VERCEL` | OAuth2 client secret for Vercel integration | `None` |
|
*TLS based connection string is not yet supported
|
||||||
| `CLIENT_SECRET_NETLIFY` | OAuth2 client secret for Netlify integration | `None` |
|
</ParamField>
|
||||||
| `CLIENT_SECRET_GITHUB` | OAuth2 client secret for GitHub integration | `None` |
|
</Tab>
|
||||||
| `CLIENT_SLUG_VERCEL` | OAuth2 slug for Netlify integration | `None` |
|
<Tab title="Email service">
|
||||||
| `SENTRY_DSN` | DSN for error-monitoring with Sentry | `None` |
|
<Info>When email service is not configured, Infisical will have limited functionality</Info>
|
||||||
| `INVITE_ONLY_SIGNUP` | If true, users can only sign up if they are invited | `false` |
|
|
||||||
|
<ParamField query="SMTP_HOST" type="string" default="none" optional>
|
||||||
|
Hostname to connect to for establishing SMTP connections
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="SMTP_USERNAME" type="string" default="none" optional>
|
||||||
|
Credential to connect to host (e.g. [email protected])
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="SMTP_PASSWORD" type="string" default="587" optional>
|
||||||
|
Credential to connect to host
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="SMTP_PORT" type="string" default="587" optional>
|
||||||
|
Port to connect to for establishing SMTP connections
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="SMTP_SECURE" type="string" default="none" optional>
|
||||||
|
If true, use TLS when connecting to host. If false, TLS will be used if STARTTLS is supported
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="SMTP_FROM_ADDRESS" type="string" default="none" optional>
|
||||||
|
Email address to be used for sending emails
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="SMTP_FROM_NAME" type="string" default="none" optional>
|
||||||
|
Name label to be used in From field (e.g. Team)
|
||||||
|
</ParamField>
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Integrations">
|
||||||
|
To sync secret to third party services, provide value for the related services
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_ID_HEROKU" type="string" default="none" optional>
|
||||||
|
OAuth2 client ID for Heroku integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_SECRET_HEROKU" type="string" default="none" optional>
|
||||||
|
OAuth2 client secret for Heroku integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_ID_VERCEL" type="string" default="none" optional>
|
||||||
|
OAuth2 client ID for Vercel integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_SECRET_VERCEL" type="string" default="none" optional>
|
||||||
|
OAuth2 client secret for Vercel integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_ID_NETLIFY" type="string" default="none" optional>
|
||||||
|
OAuth2 client ID for Netlify integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_SECRET_NETLIFY" type="string" default="none" optional>
|
||||||
|
OAuth2 client secret for Netlify integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_ID_GITHUB" type="string" default="none" optional>
|
||||||
|
OAuth2 client ID for GitHub integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_SECRET_GITHUB" type="string" default="none" optional>
|
||||||
|
OAuth2 client secret for GitHub integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_SLUG_VERCEL" type="string" default="none" optional>
|
||||||
|
OAuth2 slug for Netlify integration
|
||||||
|
</ParamField>
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Others">
|
||||||
|
#### JWT
|
||||||
|
<ParamField query="JWT_SIGNUP_LIFETIME" type="string" default="15m" optional>
|
||||||
|
JWT token lifetime expressed in seconds or a string describing a time span
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="JWT_REFRESH_LIFETIME" type="string" default="90d" optional>
|
||||||
|
JWT token lifetime expressed in seconds or a string describing a time span
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="JWT_AUTH_LIFETIME" type="string" default="10d" optional>
|
||||||
|
JWT token lifetime expressed in seconds or a string describing a time span
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="JWT_MFA_LIFETIME" type="string" default="5m" optional>
|
||||||
|
JWT token lifetime expressed in seconds or a string describing a time span
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="MONGO_USERNAME" type="string" default="none" optional></ParamField>
|
||||||
|
|
||||||
|
<ParamField query="MONGO_PASSWORD" type="string" default="none" optional></ParamField>
|
||||||
|
|
||||||
|
#### Error logging
|
||||||
|
Infisical uses Sentry to report error logs
|
||||||
|
<ParamField query="SENTRY_DSN" type="string" default="none" optional></ParamField>
|
||||||
|
|
||||||
|
#### Settings
|
||||||
|
<ParamField query="INVITE_ONLY_SIGNUP" type="string" default="false" optional>
|
||||||
|
Only allow users who are invited to sign up
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="SITE_URL" type="string" default="none" optional>
|
||||||
|
Site URL - should be an absolute URL including the protocol (e.g. https://app.infisical.com)
|
||||||
|
</ParamField>
|
||||||
|
<ParamField query="TELEMETRY_ENABLED" type="string" default="true" optional></ParamField>
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
|
||||||
|
## Frontend environment variables
|
||||||
|
<ParamField query="TELEMETRY_ENABLED" type="string" default="true" optional></ParamField>
|
||||||
|
|||||||
+184
-22
@@ -1,36 +1,198 @@
|
|||||||
---
|
---
|
||||||
title: "Overview"
|
title: "Deployment options"
|
||||||
description: "Infisical is an open-source end-to-end encrypted secrets manager that developers can set up within 15 minutes."
|
description: "Explore deployment options for self hosting Infisical"
|
||||||
---
|
---
|
||||||
|
|
||||||
<Info>
|
To meet various compliance requirements, you may want to self-host Infisical instead of using [Infisical Cloud](https://app.infisical.com/).
|
||||||
Self-host vs. Infisical Cloud
|
Self-hosted Infisical allows you to maintain your sensitive information within your own infrastructure and network, ensuring complete control over your data.
|
||||||
|
|
||||||
Self-hosting Infisical means managing the service yourself, taking care of upgrades, scaling, security, etc.
|
<Tabs>
|
||||||
|
<Tab title="Quick deploy AWS">
|
||||||
|
<iframe width="560" height="315" src="https://www.youtube.com/embed/jR-gM7vIY2c" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||||
|
This deployment option will use AWS Cloudformation to auto deploy an instance of Infisical on a single EC2 via Docker Compose.
|
||||||
|
|
||||||
If you're less technical and looking for a hands-free experience with minimal overhead then we recommend Infisical Cloud.
|
**Resources that will be provisioned**
|
||||||
|
- 1 EC2 instance
|
||||||
|
- 1 DocumentDB cluster
|
||||||
|
- 1 DocumentDB instance
|
||||||
|
- Security groups
|
||||||
|
|
||||||
Infisical Cloud also comes with some extra features unavailable in the self-hosted edition. You can find more information about Infisical Cloud's offering on the pricing page.
|
<a href="https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review?templateURL=https://ec2-instance-cloudformation.s3.amazonaws.com/cloudformation.template&stackName=infisical">
|
||||||
|
<img width="200" src="../images/deploy-aws-button.png" />
|
||||||
|
</a>
|
||||||
|
|
||||||
</Info>
|
</Tab>
|
||||||
|
<Tab title="Quick deploy Digital Ocean">
|
||||||
|
<Note>This deployment option is highly available</Note>
|
||||||
|
Coming soon
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Helm Kubernetes">
|
||||||
|
<Note>This deployment option is highly available</Note>
|
||||||
|
|
||||||
## Deployment options
|
**Prerequisites**
|
||||||
|
- You have understanding of [Kubernetes](https://kubernetes.io/)
|
||||||
|
- You have understanding of [Helm package manager](https://helm.sh/)
|
||||||
|
- You have [kubectl](https://kubernetes.io/docs/reference/kubectl/kubectl/) installed and connected to your kubernetes cluster
|
||||||
|
|
||||||
Infisical can be deployed on a Linux VM with docker-compose and Kubernetes. We're rolling out more specific deployment options for DigitalOcean, AWS, GCP, and Azure soon.
|
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
#### 1. Fill our environment variables
|
||||||
<Card title="Any Linux" icon="square-1" color="#ea5a0c" href="/self-hosting/deployments/linux">
|
|
||||||
Deploy to any Linux with Docker
|
|
||||||
</Card>
|
|
||||||
<Card title="Kubernetes" icon="square-2" color="#0285c7" href="/self-hosting/deployments/kubernetes">
|
|
||||||
Deploy to your Kubernetes cluster
|
|
||||||
</Card>
|
|
||||||
</CardGroup>
|
|
||||||
|
|
||||||
## Telemetry
|
Before you can deploy the Helm chart, you must fill out the required environment variables. To do so, please copy the below file to a `.yaml` file.
|
||||||
|
Refer to the available [environment variables](/self-hosting/configuration/envars) to learn more
|
||||||
|
|
||||||
Infisical collects telemetry data about general usage.
|
<Accordion title="values.yaml">
|
||||||
|
[View all available Helm chart values parameters](https://github.com/Infisical/infisical/tree/main/helm-charts/infisical)
|
||||||
|
```yaml
|
||||||
|
frontend:
|
||||||
|
enabled: true
|
||||||
|
name: frontend
|
||||||
|
podAnnotations: {}
|
||||||
|
deploymentAnnotations: {}
|
||||||
|
replicaCount: 2
|
||||||
|
image:
|
||||||
|
repository: infisical/frontend
|
||||||
|
tag: "latest"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
kubeSecretRef: ""
|
||||||
|
service:
|
||||||
|
annotations: {}
|
||||||
|
type: ClusterIP
|
||||||
|
nodePort: ""
|
||||||
|
|
||||||
The data helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth for investors as we support Infisical as open-source software.
|
frontendEnvironmentVariables:
|
||||||
|
SITE_URL: infisical.local
|
||||||
|
|
||||||
To opt out of telemetry, you can set `TELEMETRY_ENABLED=false` within the [environment variables](./configuration/envars).
|
backend:
|
||||||
|
enabled: true
|
||||||
|
name: backend
|
||||||
|
podAnnotations: {}
|
||||||
|
deploymentAnnotations: {}
|
||||||
|
replicaCount: 2
|
||||||
|
image:
|
||||||
|
repository: infisical/backend
|
||||||
|
tag: "latest"
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
kubeSecretRef: ""
|
||||||
|
service:
|
||||||
|
annotations: {}
|
||||||
|
type: ClusterIP
|
||||||
|
nodePort: ""
|
||||||
|
|
||||||
|
backendEnvironmentVariables:
|
||||||
|
ENCRYPTION_KEY: MUST_REPLACE
|
||||||
|
JWT_SIGNUP_SECRET: MUST_REPLACE
|
||||||
|
JWT_REFRESH_SECRET: MUST_REPLACE
|
||||||
|
JWT_AUTH_SECRET: MUST_REPLACE
|
||||||
|
JWT_SERVICE_SECRET: MUST_REPLACE
|
||||||
|
SMTP_HOST: MUST_REPLACE
|
||||||
|
SMTP_PORT: 587
|
||||||
|
SMTP_SECURE: false
|
||||||
|
SMTP_FROM_NAME: Infisical
|
||||||
|
SMTP_FROM_ADDRESS: MUST_REPLACE
|
||||||
|
SMTP_USERNAME: MUST_REPLACE
|
||||||
|
SMTP_PASSWORD: MUST_REPLACE
|
||||||
|
SITE_URL: infisical.local
|
||||||
|
|
||||||
|
## Mongo DB persistence
|
||||||
|
mongodb:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
## By default the backend will be connected to a Mongo instance within the cluster
|
||||||
|
## However, it is recommended to add a managed document DB connection string for production-use (DBaaS)
|
||||||
|
## Learn about connection string type here https://www.mongodb.com/docs/manual/reference/connection-string/
|
||||||
|
## e.g. "mongodb://<user>:<pass>@<host>:<port>/<database-name>"
|
||||||
|
mongodbConnection:
|
||||||
|
externalMongoDBConnectionString: ""
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: "nginx"
|
||||||
|
# cert-manager.io/issuer: letsencrypt-nginx
|
||||||
|
hostName: infisical.local ## <- Replace with your own domain
|
||||||
|
frontend:
|
||||||
|
path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
path: /api
|
||||||
|
pathType: Prefix
|
||||||
|
tls: []
|
||||||
|
# - secretName: letsencrypt-nginx
|
||||||
|
# hosts:
|
||||||
|
# - infisical.local
|
||||||
|
|
||||||
|
mailhog:
|
||||||
|
enabled: false
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
Once you have a local copy of the values file, fill our the required environment variables and save the file.
|
||||||
|
|
||||||
|
|
||||||
|
#### 2. Install Infisical Helm repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
|
||||||
|
|
||||||
|
helm repo update
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Install the Helm chart
|
||||||
|
|
||||||
|
By default, the helm chart will be installed on your default namespace. If you wish to install the Chart on a different namespace, you may specify
|
||||||
|
that by adding the `--namespace <namespace-to-install-to>` to your `helm install` command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
## Installs to default namespace
|
||||||
|
helm install infisical-helm-charts/infisical --generate-name --values <path to the values.yaml you downloaded/created in step 2>
|
||||||
|
```
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
If you have not filled out all of the required environment variables, you will see an error message prompting you to
|
||||||
|
do so.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
#### 4. Your Infisical installation is complete and should be running on the host name you specified in Ingress in `values.yaml`.
|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Bare Docker Compose">
|
||||||
|
1. Install Docker on your VM
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example in ubuntu
|
||||||
|
apt-get update
|
||||||
|
apt-get upgrade
|
||||||
|
apt install docker-compose
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Download the required files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download env file template
|
||||||
|
wget -O .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example
|
||||||
|
|
||||||
|
# Download docker compose template
|
||||||
|
wget -O docker-compose.yml https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.yml
|
||||||
|
|
||||||
|
# Download nginx config
|
||||||
|
mkdir nginx && wget -O ./nginx/default.conf https://raw.githubusercontent.com/Infisical/infisical/main/nginx/default.dev.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Tweak the `.env` according to your preferences. Refer to the available [environment variables](/self-hosting/configuration/envars)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# update environment variables like mongo login
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Get the service up and running.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start up services in detached mode
|
||||||
|
docker-compose -f docker-compose.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Your Infisical installation is complete and should be running on [http://localhost:80](http://localhost:80). Please note that the containers are not exposed to the internet and only bind to the localhost. It's up to you to configure a firewall, SSL certificates, and implement any additional security measures.
|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ import React, { useState } from 'react';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
|
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||||
|
import { usePopUp } from '@app/hooks/usePopUp';
|
||||||
import addUserToOrg from '@app/pages/api/organization/addUserToOrg';
|
import addUserToOrg from '@app/pages/api/organization/addUserToOrg';
|
||||||
import getWorkspaces from '@app/pages/api/workspace/getWorkspaces';
|
import getWorkspaces from '@app/pages/api/workspace/getWorkspaces';
|
||||||
|
|
||||||
import Button from '../basic/buttons/Button';
|
import Button from '../basic/buttons/Button';
|
||||||
|
import { EmailServiceSetupModal } from '../v2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the last step of the signup flow. People can optionally invite their teammates here.
|
* This is the last step of the signup flow. People can optionally invite their teammates here.
|
||||||
@@ -14,6 +17,10 @@ export default function TeamInviteStep(): JSX.Element {
|
|||||||
const [emails, setEmails] = useState('');
|
const [emails, setEmails] = useState('');
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const {data: serverDetails } = useFetchServerStatus()
|
||||||
|
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([
|
||||||
|
'setUpEmail'
|
||||||
|
] as const);
|
||||||
|
|
||||||
// Redirect user to the getting started page
|
// Redirect user to the getting started page
|
||||||
const redirectToHome = async () => {
|
const redirectToHome = async () => {
|
||||||
@@ -62,10 +69,20 @@ export default function TeamInviteStep(): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
text={t('signup:step5-send-invites') ?? ''}
|
text={t('signup:step5-send-invites') ?? ''}
|
||||||
onButtonPressed={() => inviteUsers({ emails })}
|
onButtonPressed={() => {
|
||||||
|
if(serverDetails?.emailConfigured){
|
||||||
|
inviteUsers({ emails })
|
||||||
|
}else{
|
||||||
|
handlePopUpOpen('setUpEmail');
|
||||||
|
}
|
||||||
|
}}
|
||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<EmailServiceSetupModal
|
||||||
|
isOpen={popUp.setUpEmail?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ 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 = "https://app.infisical.com";
|
|
||||||
|
|
||||||
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,7 +1,5 @@
|
|||||||
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;
|
||||||
@@ -19,7 +17,7 @@ const yyyy = today.getFullYear();
|
|||||||
const todayFormatted = `${mm}/${dd}/${yyyy}`;
|
const todayFormatted = `${mm}/${dd}/${yyyy}`;
|
||||||
|
|
||||||
|
|
||||||
function createPdfHeader(doc: jsPDF, personalName : string) {
|
function createPdfHeader(doc: jsPDF, personalName: string) {
|
||||||
doc.setFillColor(255, 255, 255);
|
doc.setFillColor(255, 255, 255);
|
||||||
doc.rect(0, 0, 600, 900, 'F');
|
doc.rect(0, 0, 600, 900, 'F');
|
||||||
doc.setTextColor(23, 23, 23);
|
doc.setTextColor(23, 23, 23);
|
||||||
@@ -30,6 +28,10 @@ function createPdfHeader(doc: jsPDF, personalName : string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createPdfContent(doc: jsPDF, personalEmail: string, generatedKey: string) {
|
function createPdfContent(doc: jsPDF, personalEmail: string, generatedKey: string) {
|
||||||
|
const { protocol, hostname, port } = window.location;
|
||||||
|
const portSuffix = port && port !== '80' ? `:${port}` : '';
|
||||||
|
const siteURL = `${protocol}//${hostname}${portSuffix}`;
|
||||||
|
|
||||||
doc.setFontSize(14);
|
doc.setFontSize(14);
|
||||||
doc.text(
|
doc.text(
|
||||||
'In case you get locked out of you Infisical account, you`ll need these account details',
|
'In case you get locked out of you Infisical account, you`ll need these account details',
|
||||||
@@ -73,7 +75,7 @@ function createPdfContent(doc: jsPDF, personalEmail: string, generatedKey: strin
|
|||||||
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(`${SITE_URL}/login`, 180, 420);
|
doc.text(`${siteURL}/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);
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Button } from '../Button';
|
||||||
|
import { Modal, ModalContent } from '../Modal';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isOpen?: boolean;
|
||||||
|
onOpenChange?: (isOpen: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EmailServiceSetupModal = ({ isOpen, onOpenChange }: Props): JSX.Element => (
|
||||||
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||||
|
<ModalContent title="Email service not configured">
|
||||||
|
<p className="mb-4 text-bunker-300">
|
||||||
|
The administrators of this Infisical instance have not yet set up an email service provider required to perform this action
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a href="https://infisical.com/docs/self-hosting/configuration/email">
|
||||||
|
<Button className="mr-4">Learn more</Button>
|
||||||
|
</a>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { EmailServiceSetupModal } from './EmailServiceSetupModal';
|
||||||
@@ -3,6 +3,7 @@ export * from './Card';
|
|||||||
export * from './Checkbox';
|
export * from './Checkbox';
|
||||||
export * from './DeleteActionModal';
|
export * from './DeleteActionModal';
|
||||||
export * from './Dropdown';
|
export * from './Dropdown';
|
||||||
|
export * from './EmailServiceSetupModal'
|
||||||
export * from './EmptyState';
|
export * from './EmptyState';
|
||||||
export * from './FormControl';
|
export * from './FormControl';
|
||||||
export * from './IconButton';
|
export * from './IconButton';
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { useFetchServerStatus } from './queries'
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import {useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { apiRequest } from '@app/config/request';
|
||||||
|
|
||||||
|
import { ServerStatus } from './types';
|
||||||
|
|
||||||
|
// cache key
|
||||||
|
const serverStatusKeys = {
|
||||||
|
serverStatus: ['serverStatus'] as const
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchServerStatus = async () => {
|
||||||
|
const {data} = await apiRequest.get<ServerStatus>('/api/status');
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchServerStatus= () => {
|
||||||
|
return useQuery({ queryKey: serverStatusKeys.serverStatus, queryFn: fetchServerStatus });
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export type ServerStatus = {
|
||||||
|
date: string;
|
||||||
|
message: string;
|
||||||
|
emailConfigured: boolean;
|
||||||
|
};
|
||||||
@@ -83,8 +83,14 @@ export const useAddUserToWs = () => {
|
|||||||
|
|
||||||
export const useAddUserToOrg = () => {
|
export const useAddUserToOrg = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
type Response = {
|
||||||
|
data: {
|
||||||
|
message: string,
|
||||||
|
completeInviteLink: string | undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return useMutation<{}, {}, AddUserToOrgDTO>({
|
return useMutation<Response, {}, AddUserToOrgDTO>({
|
||||||
mutationFn: (dto) => apiRequest.post(`/api/v1/invite-org/signup`, dto),
|
mutationFn: (dto) => apiRequest.post(`/api/v1/invite-org/signup`, dto),
|
||||||
onSuccess: (_, { organizationId }) => {
|
onSuccess: (_, { organizationId }) => {
|
||||||
queryClient.invalidateQueries(userKeys.getOrgUsers(organizationId));
|
queryClient.invalidateQueries(userKeys.getOrgUsers(organizationId));
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import TeamInviteStep from '@app/components/signup/TeamInviteStep';
|
|||||||
import UserInfoStep from '@app/components/signup/UserInfoStep';
|
import UserInfoStep from '@app/components/signup/UserInfoStep';
|
||||||
import SecurityClient from '@app/components/utilities/SecurityClient';
|
import SecurityClient from '@app/components/utilities/SecurityClient';
|
||||||
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
||||||
|
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||||
|
|
||||||
import checkEmailVerificationCode from './api/auth/CheckEmailVerificationCode';
|
import checkEmailVerificationCode from './api/auth/CheckEmailVerificationCode';
|
||||||
import getWorkspaces from './api/workspace/getWorkspaces';
|
import getWorkspaces from './api/workspace/getWorkspaces';
|
||||||
@@ -25,10 +26,12 @@ export default function SignUp() {
|
|||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [firstName, setFirstName] = useState('');
|
const [firstName, setFirstName] = useState('');
|
||||||
const [lastName, setLastName] = useState('');
|
const [lastName, setLastName] = useState('');
|
||||||
const [code, setCode] = useState('');
|
const [code, setCode] = useState('123456');
|
||||||
const [codeError, setCodeError] = useState(false);
|
const [codeError, setCodeError] = useState(false);
|
||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const {data: serverDetails } = useFetchServerStatus()
|
||||||
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -68,6 +71,19 @@ export default function SignUp() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// when email service is not configured, skip step 2 and 5
|
||||||
|
useEffect(() => {
|
||||||
|
if (!serverDetails?.emailConfigured && step === 2){
|
||||||
|
incrementStep()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!serverDetails?.emailConfigured && step === 5){
|
||||||
|
getWorkspaces().then((userWorkspaces)=>{
|
||||||
|
router.push(`/dashboard/${userWorkspaces[0]._id}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [step]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bunker-800 h-screen flex flex-col items-center justify-center">
|
<div className="bg-bunker-800 h-screen flex flex-col items-center justify-center">
|
||||||
<Head>
|
<Head>
|
||||||
@@ -111,9 +127,7 @@ export default function SignUp() {
|
|||||||
password={password}
|
password={password}
|
||||||
name={`${firstName} ${lastName}`}
|
name={`${firstName} ${lastName}`}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (serverDetails?.emailConfigured ? <TeamInviteStep /> : "")}
|
||||||
<TeamInviteStep />
|
|
||||||
)}
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,12 +6,19 @@ import Link from 'next/link';
|
|||||||
import Button from '@app/components/basic/buttons/Button';
|
import Button from '@app/components/basic/buttons/Button';
|
||||||
import InputField from '@app/components/basic/InputField';
|
import InputField from '@app/components/basic/InputField';
|
||||||
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
||||||
|
import { EmailServiceSetupModal } from '@app/components/v2';
|
||||||
|
import { usePopUp } from '@app/hooks';
|
||||||
|
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||||
|
|
||||||
import SendEmailOnPasswordReset from './api/auth/SendEmailOnPasswordReset';
|
import SendEmailOnPasswordReset from './api/auth/SendEmailOnPasswordReset';
|
||||||
|
|
||||||
export default function VerifyEmail() {
|
export default function VerifyEmail() {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
|
const {data: serverDetails } = useFetchServerStatus()
|
||||||
|
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([
|
||||||
|
'setUpEmail'
|
||||||
|
] as const);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function sends the verification email and forwards a user to the next step.
|
* This function sends the verification email and forwards a user to the next step.
|
||||||
@@ -63,7 +70,13 @@ export default function VerifyEmail() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center justify-center w-full md:p-2 max-h-20 max-w-md mt-4 mx-auto text-sm">
|
<div className="flex flex-col items-center justify-center w-full md:p-2 max-h-20 max-w-md mt-4 mx-auto text-sm">
|
||||||
<div className="text-l mt-6 m-8 px-8 py-3 text-lg">
|
<div className="text-l mt-6 m-8 px-8 py-3 text-lg">
|
||||||
<Button text="Continue" onButtonPressed={sendVerificationEmail} size="lg" />
|
<Button text="Continue" onButtonPressed={()=>{
|
||||||
|
if (serverDetails?.emailConfigured){
|
||||||
|
sendVerificationEmail()
|
||||||
|
} else {
|
||||||
|
handlePopUpOpen('setUpEmail');
|
||||||
|
}
|
||||||
|
}} size="lg" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,6 +93,11 @@ export default function VerifyEmail() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<EmailServiceSetupModal
|
||||||
|
isOpen={popUp.setUpEmail?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { plans } from 'public/data/frequentConstants';
|
import { plans } from 'public/data/frequentConstants';
|
||||||
|
|
||||||
@@ -55,6 +56,8 @@ export const OrgSettingsPage = () => {
|
|||||||
const addIncidentContact = useAddIncidentContact();
|
const addIncidentContact = useAddIncidentContact();
|
||||||
const removeIncidentContact = useDeleteIncidentContact();
|
const removeIncidentContact = useDeleteIncidentContact();
|
||||||
|
|
||||||
|
const [completeInviteLink, setcompleteInviteLink] = useState<string|undefined>("")
|
||||||
|
|
||||||
const isMoreUsersNotAllowed =
|
const isMoreUsersNotAllowed =
|
||||||
(orgUsers || []).length >= 5 &&
|
(orgUsers || []).length >= 5 &&
|
||||||
subscriptionPlan === plans.starter &&
|
subscriptionPlan === plans.starter &&
|
||||||
@@ -100,11 +103,16 @@ export const OrgSettingsPage = () => {
|
|||||||
if (!currentOrg?._id) return;
|
if (!currentOrg?._id) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await addUserToOrg.mutateAsync({ organizationId: currentOrg?._id, inviteeEmail: email });
|
const {data} = await addUserToOrg.mutateAsync({ organizationId: currentOrg?._id, inviteeEmail: email });
|
||||||
|
setcompleteInviteLink(data?.completeInviteLink)
|
||||||
|
|
||||||
|
// only show this notification when email is configured. A [completeInviteLink] will not be sent if smtp is configured
|
||||||
|
if (!data.completeInviteLink){
|
||||||
createNotification({
|
createNotification({
|
||||||
text: 'Successfully invited user to the organization.',
|
text: 'Successfully invited user to the organization.',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -249,6 +257,8 @@ export const OrgSettingsPage = () => {
|
|||||||
onRemoveMember={onRemoveUserOrgMembership}
|
onRemoveMember={onRemoveUserOrgMembership}
|
||||||
onRoleChange={onUpdateOrgUserRole}
|
onRoleChange={onUpdateOrgUserRole}
|
||||||
onGrantAccess={onGrantUserAccess}
|
onGrantAccess={onGrantUserAccess}
|
||||||
|
completeInviteLink={completeInviteLink}
|
||||||
|
setCompleteInviteLink={setcompleteInviteLink}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-6 mt-2 w-full rounded-md bg-white/5 p-6">
|
<div className="mb-6 mt-2 w-full rounded-md bg-white/5 p-6">
|
||||||
|
|||||||
+16
-2
@@ -13,6 +13,7 @@ import * as yup from 'yup';
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DeleteActionModal,
|
DeleteActionModal,
|
||||||
|
EmailServiceSetupModal,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
FormControl,
|
FormControl,
|
||||||
IconButton,
|
IconButton,
|
||||||
@@ -29,6 +30,7 @@ import {
|
|||||||
Tr
|
Tr
|
||||||
} from '@app/components/v2';
|
} from '@app/components/v2';
|
||||||
import { usePopUp } from '@app/hooks';
|
import { usePopUp } from '@app/hooks';
|
||||||
|
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||||
import { IncidentContact } from '@app/hooks/api/types';
|
import { IncidentContact } from '@app/hooks/api/types';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -51,9 +53,11 @@ export const OrgIncidentContactsTable = ({
|
|||||||
isLoading
|
isLoading
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [searchContact, setSearchContact] = useState('');
|
const [searchContact, setSearchContact] = useState('');
|
||||||
|
const {data: serverDetails } = useFetchServerStatus()
|
||||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
'addContact',
|
'addContact',
|
||||||
'removeContact'
|
'removeContact',
|
||||||
|
'setUpEmail'
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -93,7 +97,13 @@ export const OrgIncidentContactsTable = ({
|
|||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
onClick={() => handlePopUpOpen('addContact')}
|
onClick={() => {
|
||||||
|
if (serverDetails?.emailConfigured){
|
||||||
|
handlePopUpOpen('addContact');
|
||||||
|
} else {
|
||||||
|
handlePopUpOpen('setUpEmail');
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Add Contact
|
Add Contact
|
||||||
</Button>
|
</Button>
|
||||||
@@ -181,6 +191,10 @@ export const OrgIncidentContactsTable = ({
|
|||||||
onChange={(isOpen) => handlePopUpToggle('removeContact', isOpen)}
|
onChange={(isOpen) => handlePopUpToggle('removeContact', isOpen)}
|
||||||
onDeleteApproved={onRemoveIncidentContact}
|
onDeleteApproved={onRemoveIncidentContact}
|
||||||
/>
|
/>
|
||||||
|
<EmailServiceSetupModal
|
||||||
|
isOpen={popUp.setUpEmail?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+48
-11
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
|
||||||
import { Controller, useForm } from 'react-hook-form';
|
import { Controller, useForm } from 'react-hook-form';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { faMagnifyingGlass, faPlus, faTrash, faUsers } from '@fortawesome/free-solid-svg-icons';
|
import { faCheck, faCopy, faMagnifyingGlass, faPlus, faTrash, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
@@ -9,7 +9,7 @@ import * as yup from 'yup';
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
DeleteActionModal,
|
DeleteActionModal,
|
||||||
EmptyState,
|
EmailServiceSetupModal, EmptyState,
|
||||||
FormControl,
|
FormControl,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
Tr,
|
Tr,
|
||||||
UpgradePlanModal} from '@app/components/v2';
|
UpgradePlanModal} from '@app/components/v2';
|
||||||
import { usePopUp } from '@app/hooks';
|
import { usePopUp } from '@app/hooks';
|
||||||
|
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||||
import { OrgUser, Workspace } from '@app/hooks/api/types';
|
import { OrgUser, Workspace } from '@app/hooks/api/types';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -42,6 +43,8 @@ type Props = {
|
|||||||
onGrantAccess: (userId: string, publicKey: string) => Promise<void>;
|
onGrantAccess: (userId: string, publicKey: string) => Promise<void>;
|
||||||
// the current user id to block remove org button
|
// the current user id to block remove org button
|
||||||
userId: string;
|
userId: string;
|
||||||
|
completeInviteLink: string | undefined,
|
||||||
|
setCompleteInviteLink: Dispatch<SetStateAction<string | undefined>>
|
||||||
};
|
};
|
||||||
|
|
||||||
const addMemberFormSchema = yup.object({
|
const addMemberFormSchema = yup.object({
|
||||||
@@ -60,14 +63,18 @@ export const OrgMembersTable = ({
|
|||||||
onGrantAccess,
|
onGrantAccess,
|
||||||
onRoleChange,
|
onRoleChange,
|
||||||
userId,
|
userId,
|
||||||
isLoading
|
isLoading,
|
||||||
|
completeInviteLink,
|
||||||
|
setCompleteInviteLink
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [searchMemberFilter, setSearchMemberFilter] = useState('');
|
const [searchMemberFilter, setSearchMemberFilter] = useState('');
|
||||||
|
const {data: serverDetails } = useFetchServerStatus()
|
||||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
'addMember',
|
'addMember',
|
||||||
'removeMember',
|
'removeMember',
|
||||||
'upgradePlan'
|
'upgradePlan',
|
||||||
|
'setUpEmail'
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -79,7 +86,10 @@ export const OrgMembersTable = ({
|
|||||||
|
|
||||||
const onAddMember = async ({ email }: TAddMemberForm) => {
|
const onAddMember = async ({ email }: TAddMemberForm) => {
|
||||||
await onInviteMember(email);
|
await onInviteMember(email);
|
||||||
|
if (serverDetails?.emailConfigured){
|
||||||
handlePopUpClose('addMember');
|
handlePopUpClose('addMember');
|
||||||
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,6 +116,11 @@ export const OrgMembersTable = ({
|
|||||||
[members, searchMemberFilter]
|
[members, searchMemberFilter]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const copyTokenToClipboard = () => {
|
||||||
|
navigator.clipboard.writeText(completeInviteLink as string);
|
||||||
|
// setIsTokenCopied.on();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="mb-4 flex">
|
<div className="mb-4 flex">
|
||||||
@@ -171,7 +186,7 @@ export const OrgMembersTable = ({
|
|||||||
<SelectItem value="member">member</SelectItem>
|
<SelectItem value="member">member</SelectItem>
|
||||||
</Select>
|
</Select>
|
||||||
)}
|
)}
|
||||||
{(status === 'invited' || status === 'verified') && (
|
{((status === 'invited' || status === 'verified') && serverDetails?.emailConfigured) && (
|
||||||
<Button className='w-40' colorSchema="secondary" onClick={() => onInviteMember(email)}>
|
<Button className='w-40' colorSchema="secondary" onClick={() => onInviteMember(email)}>
|
||||||
Resend Invite
|
Resend Invite
|
||||||
</Button>
|
</Button>
|
||||||
@@ -232,20 +247,23 @@ export const OrgMembersTable = ({
|
|||||||
isOpen={popUp?.addMember?.isOpen}
|
isOpen={popUp?.addMember?.isOpen}
|
||||||
onOpenChange={(isOpen) => {
|
onOpenChange={(isOpen) => {
|
||||||
handlePopUpToggle('addMember', isOpen);
|
handlePopUpToggle('addMember', isOpen);
|
||||||
reset();
|
setCompleteInviteLink(undefined)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title={`Invite others to ${orgName}`}
|
title={`Invite others to ${orgName}`}
|
||||||
subTitle={
|
subTitle={
|
||||||
<>
|
<div>
|
||||||
|
{!completeInviteLink && <div>
|
||||||
An invite is specific to an email address and expires after 1 day.
|
An invite is specific to an email address and expires after 1 day.
|
||||||
<br />
|
<br />
|
||||||
For security reasons, you will need to separately add members to projects.
|
For security reasons, you will need to separately add members to projects.
|
||||||
</>
|
</div>}
|
||||||
|
{completeInviteLink && "This Infisical instance does not have a email provider setup. Please share this invite link with the invitee manually"}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<form onSubmit={handleSubmit(onAddMember)}>
|
{!completeInviteLink && <form onSubmit={handleSubmit(onAddMember)} >
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
@@ -274,7 +292,22 @@ export const OrgMembersTable = ({
|
|||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>}
|
||||||
|
{
|
||||||
|
completeInviteLink &&
|
||||||
|
<div className="mt-2 mb-3 mr-2 flex items-center justify-end rounded-md bg-white/[0.07] p-2 text-base text-gray-400">
|
||||||
|
<p className="mr-4 break-all">{completeInviteLink}</p>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="copy icon"
|
||||||
|
colorSchema="secondary"
|
||||||
|
className="group relative"
|
||||||
|
onClick={copyTokenToClipboard}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={false ? faCheck : faCopy} />
|
||||||
|
<span className="absolute -left-8 -top-20 hidden w-28 translate-y-full rounded-md bg-bunker-800 py-2 pl-3 text-center text-sm text-gray-400 group-hover:flex group-hover:animate-fadeIn">click to copy</span>
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
@@ -289,6 +322,10 @@ export const OrgMembersTable = ({
|
|||||||
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
|
||||||
text="You can add custom environments if you switch to Infisical's Team plan."
|
text="You can add custom environments if you switch to Infisical's Team plan."
|
||||||
/>
|
/>
|
||||||
|
<EmailServiceSetupModal
|
||||||
|
isOpen={popUp.setUpEmail?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+18
-1
@@ -1,7 +1,9 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||||
import { Checkbox } from '@app/components/v2';
|
import { Checkbox, EmailServiceSetupModal } from '@app/components/v2';
|
||||||
|
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
|
||||||
|
import { usePopUp } from '@app/hooks/usePopUp';
|
||||||
|
|
||||||
import { useGetUser } from '../../../../hooks/api';
|
import { useGetUser } from '../../../../hooks/api';
|
||||||
import { User } from '../../../../hooks/api/types';
|
import { User } from '../../../../hooks/api/types';
|
||||||
@@ -11,6 +13,11 @@ export const SecuritySection = () => {
|
|||||||
const [isMfaEnabled, setIsMfaEnabled] = useState(false);
|
const [isMfaEnabled, setIsMfaEnabled] = useState(false);
|
||||||
const { data: user } = useGetUser();
|
const { data: user } = useGetUser();
|
||||||
const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
|
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([
|
||||||
|
'setUpEmail'
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const {data: serverDetails } = useFetchServerStatus()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user && typeof user.isMfaEnabled !== 'undefined') {
|
if (user && typeof user.isMfaEnabled !== 'undefined') {
|
||||||
@@ -42,6 +49,7 @@ export const SecuritySection = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<form>
|
<form>
|
||||||
<div className="mb-6 mt-2 flex w-full flex-col items-start rounded-md bg-white/5 px-6 pb-6 pt-2">
|
<div className="mb-6 mt-2 flex w-full flex-col items-start rounded-md bg-white/5 px-6 pb-6 pt-2">
|
||||||
<p className="mb-4 mt-2 text-xl font-semibold">
|
<p className="mb-4 mt-2 text-xl font-semibold">
|
||||||
@@ -52,12 +60,21 @@ export const SecuritySection = () => {
|
|||||||
id="isTwoFAEnabled"
|
id="isTwoFAEnabled"
|
||||||
isChecked={isMfaEnabled}
|
isChecked={isMfaEnabled}
|
||||||
onCheckedChange={(state) => {
|
onCheckedChange={(state) => {
|
||||||
|
if (serverDetails?.emailConfigured){
|
||||||
toggleMfa(state as boolean);
|
toggleMfa(state as boolean);
|
||||||
|
} else {
|
||||||
|
handlePopUpOpen('setUpEmail');
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Enable 2-factor authentication via your personal email.
|
Enable 2-factor authentication via your personal email.
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<EmailServiceSetupModal
|
||||||
|
isOpen={popUp.setUpEmail?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle('setUpEmail', isOpen)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user