diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..45816f5cd --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,22 @@ +# Description 📣 + +*Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.* + +## Type ✨ + +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation + +# Tests 🛠️ + +*Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. You may want to add screenshots when relevant and possible* + +```sh +# Here's some code block to paste some code snippets +``` + +--- + +- [ ] I have read the [contributing guide](https://infisical.com/docs/contributing/overview), agreed and acknowledged the [code of conduct](https://infisical.com/docs/contributing/code-of-conduct). 📝 \ No newline at end of file diff --git a/README.md b/README.md index 791a2f2b2..d2951c023 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ git commit activity - Cloudsmith downloads + Cloudsmith downloads Slack community channel @@ -39,28 +39,31 @@ **Read this in other languages**: [English language](i18n/README.en.md) [Spanish language](i18n/README.es.md) +[German language](i18n/README.de.md) [Korean language](i18n/README.ko.md) [Turkish language](i18n/README.tr.md) +[Bahasa Indonesia language](i18n/README.id.md) +[Portuguese - Brazil](i18n/README.pt-br.md) -**[Infisical](https://infisical.com)** is an open source, end-to-end encrypted secret manager which you can use to centralize your API keys and configs. From Infisical, you can then distribute these secrets across your whole developemnt lifecycle - from development to production . It's designed to be simple and take minutes to get going. +**[Infisical](https://infisical.com)** is an open source, end-to-end encrypted secret manager which you can use to centralize your API keys and configs. From Infisical, you can then distribute these secrets across your whole development lifecycle - from development to production . It's designed to be simple and take minutes to get going. -- **[User-Friendly Dashboard](https://infisical.com/docs/getting-started/dashboard/project)** to manage your team's secrets and configs within projects -- **[Language-Agnostic CLI](https://infisical.com/docs/cli/overview)** that pulls and injects esecrets and configs into your local workflow -- **[Complete control over your data](https://infisical.com/docs/self-hosting/overview)** - host it yourself on any infrastructure -- **Navigate Multiple Environments** per project (e.g. development, staging, production, etc.) -- **Personal overrides** for secrets and configs -- **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure -- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** - manage secrets via HTTPS requests to the platform -- **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** to view the change history for any secret -- **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** to record every action taken in a project -- **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** for rolling back to any snapshot of your secrets -- **Role-based Access Controls** per environment -- **2FA** (more options coming soon) -- 🔜 **1-Click Deploy** to AWS -- 🔜 **Automatic Secret Rotation** -- 🔜 **Smart Security Alerts** -- 🔜 **Secrets Rotation** -- 🔜 **Slack & MS Teams** integrations +- **[User-Friendly Dashboard](https://infisical.com/docs/getting-started/dashboard/project)** to manage your team's secrets and configs within projects +- **[Language-Agnostic CLI](https://infisical.com/docs/cli/overview)** that pulls and injects esecrets and configs into your local workflow +- **[Complete control over your data](https://infisical.com/docs/self-hosting/overview)** - host it yourself on any infrastructure +- **Navigate Multiple Environments** per project (e.g. development, staging, production, etc.) +- **Personal overrides** for secrets and configs +- **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure +- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** - manage secrets via HTTPS requests to the platform +- **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** to view the change history for any secret +- **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** to record every action taken in a project +- **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** for rolling back to any snapshot of your secrets +- **Role-based Access Controls** per environment +- **2FA** (more options coming soon) +- 🔜 **1-Click Deploy** to AWS +- 🔜 **Automatic Secret Rotation** +- 🔜 **Smart Security Alerts** +- 🔜 **Secrets Rotation** +- 🔜 **Slack & MS Teams** integrations And more. @@ -92,7 +95,7 @@ We're currently setting the foundation and building [integrations](https://infis Platforms Frameworks - + @@ -150,7 +153,7 @@ We're currently setting the foundation and building [integrations](https://infis
- ✔️ GitLab CI/CD + ✔️ GitLab CI/CD @@ -315,19 +318,19 @@ We're currently setting the foundation and building [integrations](https://infis
- + ## 💚 Community & Support -- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (For live discussion with the community and the Infisical team) -- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (For help with building and deeper conversations about features) -- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (For any bugs and errors you encounter using Infisical) -- [Twitter](https://twitter.com/infisical) (Get news fast) +- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (For live discussion with the community and the Infisical team) +- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (For help with building and deeper conversations about features) +- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (For any bugs and errors you encounter using Infisical) +- [Twitter](https://twitter.com/infisical) (Get news fast) ## 🏘 Open-source vs. paid -This repo is entirely MIT licensed, with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license in the future. We're currently focused on developing non-enterprise offerings first that should suit most use-cases. +This repo is entirely MIT licensed, with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license in the future. We're currently focused on developing non-enterprise offerings first that should suit most use cases. ## 🛡 Security @@ -345,8 +348,8 @@ Whether it's big or small, we love contributions ❤️ Check out our guide to s Not sure where to get started? You can: -- [Book a free, non-pressure pairing sessions with one of our teammates](mailto:tony@infisical.com?subject=Pairing%20session&body=I'd%20like%20to%20do%20a%20pairing%20session!)! -- Join our
Slack, and ask us any questions there. +- [Book a free, non-pressure pairing sessions with one of our teammates](mailto:tony@infisical.com?subject=Pairing%20session&body=I'd%20like%20to%20do%20a%20pairing%20session!)! +- Join our Slack, and ask us any questions there. ## 🦸 Contributors @@ -356,7 +359,7 @@ Not sure where to get started? You can: - + ## 🌎 Translations diff --git a/backend/package-lock.json b/backend/package-lock.json index 6514ebb17..3741f7665 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -19,6 +19,7 @@ "await-to-js": "^3.0.0", "aws-sdk": "^2.1311.0", "axios": "^1.1.3", + "axios-retry": "^3.4.0", "bcrypt": "^5.1.0", "bigint-conversion": "^2.2.2", "builder-pattern": "^2.2.0", @@ -3473,6 +3474,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -5317,6 +5329,15 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-retry": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.4.0.tgz", + "integrity": "sha512-VdgaP+gHH4iQYCCNUWF2pcqeciVOdGrBBAYUfTY+wPcO5Ltvp/37MLFNCmJKo7Gj3SHvCSdL8ouI1qLYJN3liA==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "is-retry-allowed": "^2.2.0" + } + }, "node_modules/babel-jest": { "version": "29.3.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", @@ -7604,6 +7625,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -12179,6 +12211,11 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -16584,6 +16621,14 @@ "@babel/helper-plugin-utils": "^7.19.0" } }, + "@babel/runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, "@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -18075,6 +18120,15 @@ "proxy-from-env": "^1.1.0" } }, + "axios-retry": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.4.0.tgz", + "integrity": "sha512-VdgaP+gHH4iQYCCNUWF2pcqeciVOdGrBBAYUfTY+wPcO5Ltvp/37MLFNCmJKo7Gj3SHvCSdL8ouI1qLYJN3liA==", + "requires": { + "@babel/runtime": "^7.15.4", + "is-retry-allowed": "^2.2.0" + } + }, "babel-jest": { "version": "29.3.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", @@ -19771,6 +19825,11 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" }, + "is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -23088,6 +23147,11 @@ "picomatch": "^2.2.1" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", diff --git a/backend/package.json b/backend/package.json index d3ce5a393..89bd52b07 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,6 +10,7 @@ "await-to-js": "^3.0.0", "aws-sdk": "^2.1311.0", "axios": "^1.1.3", + "axios-retry": "^3.4.0", "bcrypt": "^5.1.0", "bigint-conversion": "^2.2.2", "builder-pattern": "^2.2.0", diff --git a/backend/src/config/request.ts b/backend/src/config/request.ts new file mode 100644 index 000000000..afadd1c15 --- /dev/null +++ b/backend/src/config/request.ts @@ -0,0 +1,16 @@ +import axios from 'axios'; +import axiosRetry from 'axios-retry'; + +const axiosInstance = axios.create(); + +// add retry functionality to the axios instance +axiosRetry(axiosInstance, { + retries: 3, + retryDelay: (retryCount) => retryCount * 1000, // delay between retries (in milliseconds) + retryCondition: (error) => { + // only retry if the error is a network error or a 5xx server error + return axiosRetry.isNetworkError(error) || axiosRetry.isRetryableError(error); + }, +}); + +export default axiosInstance; \ No newline at end of file diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts index 61d345c38..70b60dd9f 100644 --- a/backend/src/integrations/sync.ts +++ b/backend/src/integrations/sync.ts @@ -32,6 +32,7 @@ import { INTEGRATION_CIRCLECI_API_URL, INTEGRATION_TRAVISCI_API_URL, } from "../variables"; +import axiosWithRetry from '../config/request'; /** * Sync/push [secrets] to [app] in integration named [integration] @@ -555,7 +556,7 @@ const syncSecretsVercel = async ({ value: string; target: string[]; } - + try { // Get all (decrypted) secrets back from Vercel in // decrypted format @@ -568,40 +569,84 @@ const syncSecretsVercel = async ({ : {}), }; - const res = ( - await Promise.all( - ( - await axios.get( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, + // const res = ( + // await Promise.all( + // ( + // await axios.get( + // `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, + // { + // params, + // headers: { + // Authorization: `Bearer ${accessToken}`, + // 'Accept-Encoding': 'application/json' + // } + // } + // )) + // .data + // .envs + // .filter((secret: VercelSecret) => secret.target.includes(integration.targetEnvironment)) + // .map(async (secret: VercelSecret) => { + // if (secret.type === 'encrypted') { + // // case: secret is encrypted -> need to decrypt + // const decryptedSecret = (await axios.get( + // `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, + // { + // params, + // headers: { + // Authorization: `Bearer ${accessToken}`, + // 'Accept-Encoding': 'application/json' + // } + // } + // )).data; + + // return decryptedSecret; + // } + + // return secret; + // }))).reduce((obj: any, secret: any) => ({ + // ...obj, + // [secret.key]: secret + // }), {}); + + const vercelSecrets: VercelSecret[] = (await axiosWithRetry.get( + `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + 'Accept-Encoding': 'application/json' + } + } + )) + .data + .envs + .filter((secret: VercelSecret) => secret.target.includes(integration.targetEnvironment)); + + const res: { [key: string]: VercelSecret } = {}; + + for await (const vercelSecret of vercelSecrets) { + if (vercelSecret.type === 'encrypted') { + // case: secret is encrypted -> need to decrypt + const decryptedSecret = (await axiosWithRetry.get( + `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${vercelSecret.id}`, { params, headers: { Authorization: `Bearer ${accessToken}`, 'Accept-Encoding': 'application/json' } - } - )) - .data - .envs - .filter((secret: VercelSecret) => secret.target.includes(integration.targetEnvironment)) - .map(async (secret: VercelSecret) => (await axios.get( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - 'Accept-Encoding': 'application/json' - } - } - )).data) - )).reduce((obj: any, secret: any) => ({ - ...obj, - [secret.key]: secret - }), {}); + } + )).data; - const updateSecrets: VercelSecret[] = []; - const deleteSecrets: VercelSecret[] = []; - const newSecrets: VercelSecret[] = []; + res[vercelSecret.key] = decryptedSecret; + } else { + res[vercelSecret.key] = vercelSecret; + } + } + + const updateSecrets: VercelSecret[] = []; + const deleteSecrets: VercelSecret[] = []; + const newSecrets: VercelSecret[] = []; // Identify secrets to create Object.keys(secrets).map((key) => { @@ -625,8 +670,10 @@ const syncSecretsVercel = async ({ id: res[key].id, key: key, value: secrets[key], - type: "encrypted", - target: [integration.targetEnvironment], + type: res[key].type, + target: res[key].target.includes(integration.targetEnvironment) + ? [...res[key].target] + : [...res[key].target, integration.targetEnvironment] }); } } else { @@ -635,7 +682,7 @@ const syncSecretsVercel = async ({ id: res[key].id, key: key, value: res[key].value, - type: "encrypted", + type: "encrypted", // value doesn't matter target: [integration.targetEnvironment], }); } @@ -643,7 +690,7 @@ const syncSecretsVercel = async ({ // Sync/push new secrets if (newSecrets.length > 0) { - await axios.post( + await axiosWithRetry.post( `${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`, newSecrets, { @@ -655,12 +702,11 @@ const syncSecretsVercel = async ({ } ); } - - // Sync/push updated secrets - if (updateSecrets.length > 0) { - updateSecrets.forEach(async (secret: VercelSecret) => { + + for await (const secret of updateSecrets) { + if (secret.type !== 'sensitive') { const { id, ...updatedSecret } = secret; - await axios.patch( + await axiosWithRetry.patch( `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, updatedSecret, { @@ -671,23 +717,20 @@ const syncSecretsVercel = async ({ }, } ); - }); + } } - // Delete secrets - if (deleteSecrets.length > 0) { - deleteSecrets.forEach(async (secret: VercelSecret) => { - await axios.delete( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}`, - 'Accept-Encoding': 'application/json' - }, - } - ); - }); + for await (const secret of deleteSecrets) { + await axiosWithRetry.delete( + `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}`, + 'Accept-Encoding': 'application/json' + }, + } + ); } } catch (err) { Sentry.setUser(null); diff --git a/backend/src/routes/v2/workspace.ts b/backend/src/routes/v2/workspace.ts index 9a753badd..6183c9e66 100644 --- a/backend/src/routes/v2/workspace.ts +++ b/backend/src/routes/v2/workspace.ts @@ -118,7 +118,6 @@ router.delete( // TODO - rewire dashboard to this route workspaceController.deleteWorkspaceMembership ); - router.patch( '/:workspaceId/auto-capitalization', requireAuth({ diff --git a/backend/src/services/EventService.ts b/backend/src/services/EventService.ts index fcbac9ad0..9f982bf75 100644 --- a/backend/src/services/EventService.ts +++ b/backend/src/services/EventService.ts @@ -1,5 +1,3 @@ -import { Bot, IBot } from '../models'; -import * as Sentry from '@sentry/node'; import { handleEventHelper } from '../helpers/event'; interface Event { diff --git a/backend/src/services/smtp.ts b/backend/src/services/smtp.ts index f960f6bae..8debfdb6f 100644 --- a/backend/src/services/smtp.ts +++ b/backend/src/services/smtp.ts @@ -1,6 +1,10 @@ import nodemailer from 'nodemailer'; import { SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_SECURE } from '../config'; -import { SMTP_HOST_SENDGRID, SMTP_HOST_MAILGUN } from '../variables'; +import { + SMTP_HOST_SENDGRID, + SMTP_HOST_MAILGUN, + SMTP_HOST_SOCKETLABS +} from '../variables'; import SMTPConnection from 'nodemailer/lib/smtp-connection'; import * as Sentry from '@sentry/node'; @@ -27,6 +31,12 @@ if (SMTP_SECURE) { ciphers: 'TLSv1.2' } break; + case SMTP_HOST_SOCKETLABS: + mailOpts.requireTLS = true; + mailOpts.tls = { + ciphers: 'TLSv1.2' + } + break; default: if (SMTP_HOST.includes('amazonaws.com')) { mailOpts.tls = { diff --git a/backend/src/variables/index.ts b/backend/src/variables/index.ts index 9d16b7f43..2be394971 100644 --- a/backend/src/variables/index.ts +++ b/backend/src/variables/index.ts @@ -44,7 +44,11 @@ import { ACTION_DELETE_SECRETS, ACTION_READ_SECRETS } from './action'; -import { SMTP_HOST_SENDGRID, SMTP_HOST_MAILGUN } from './smtp'; +import { + SMTP_HOST_SENDGRID, + SMTP_HOST_MAILGUN, + SMTP_HOST_SOCKETLABS +} from './smtp'; import { PLAN_STARTER, PLAN_PRO } from './stripe'; import { MFA_METHOD_EMAIL @@ -105,6 +109,7 @@ export { INTEGRATION_OPTIONS, SMTP_HOST_SENDGRID, SMTP_HOST_MAILGUN, + SMTP_HOST_SOCKETLABS, PLAN_STARTER, PLAN_PRO, MFA_METHOD_EMAIL, diff --git a/backend/src/variables/smtp.ts b/backend/src/variables/smtp.ts index 4db7c9f12..c807ddf05 100644 --- a/backend/src/variables/smtp.ts +++ b/backend/src/variables/smtp.ts @@ -1,7 +1,9 @@ const SMTP_HOST_SENDGRID = 'smtp.sendgrid.net'; const SMTP_HOST_MAILGUN = 'smtp.mailgun.org'; +const SMTP_HOST_SOCKETLABS = 'smtp.socketlabs.com'; export { SMTP_HOST_SENDGRID, - SMTP_HOST_MAILGUN + SMTP_HOST_MAILGUN, + SMTP_HOST_SOCKETLABS } \ No newline at end of file diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index d1e72ff2d..649de41f2 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -36,9 +36,12 @@ var exportCmd = &cobra.Command{ // util.RequireLocalWorkspaceFile() }, Run: func(cmd *cobra.Command, args []string) { - envName, err := cmd.Flags().GetString("env") - if err != nil { - util.HandleError(err) + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } } shouldExpandSecrets, err := cmd.Flags().GetBool("expand") @@ -66,7 +69,7 @@ var exportCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } - secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: envName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs}) + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs}) if err != nil { util.HandleError(err, "Unable to fetch secrets") } diff --git a/cli/packages/cmd/login.go b/cli/packages/cmd/login.go index e09253407..092bcdfa1 100644 --- a/cli/packages/cmd/login.go +++ b/cli/packages/cmd/login.go @@ -87,7 +87,7 @@ var loginCmd = &cobra.Command{ break } else if mfaErrorResponse != nil { if mfaErrorResponse.Context.Code == "mfa_invalid" { - msg := fmt.Sprintf("Incorrect, MFA code. You have %v attempts left", 5-i) + msg := fmt.Sprintf("Incorrect, verification code. You have %v attempts left", 5-i) fmt.Println(msg) if i == 5 { util.PrintErrorMessageAndExit("No tries left, please try again in a bit") @@ -96,7 +96,7 @@ var loginCmd = &cobra.Command{ } if mfaErrorResponse.Context.Code == "mfa_expired" { - util.PrintErrorMessageAndExit("Your MFA code has expired, please try logging in again") + util.PrintErrorMessageAndExit("Your 2FA verification code has expired, please try logging in again") break } i++ @@ -120,6 +120,7 @@ var loginCmd = &cobra.Command{ var decryptedPrivateKey []byte if loginTwoResponse.EncryptionVersion == 1 { + log.Debug("Login version 1") encryptedPrivateKey, _ := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey) tag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag) if err != nil { @@ -134,11 +135,15 @@ var loginCmd = &cobra.Command{ paddedPassword := fmt.Sprintf("%032s", password) key := []byte(paddedPassword) - decryptedPrivateKey, err := crypto.DecryptSymmetric(key, encryptedPrivateKey, tag, IV) - if err != nil || len(decryptedPrivateKey) == 0 { + computedDecryptedPrivateKey, err := crypto.DecryptSymmetric(key, encryptedPrivateKey, tag, IV) + if err != nil || len(computedDecryptedPrivateKey) == 0 { util.HandleError(err) } + + decryptedPrivateKey = computedDecryptedPrivateKey + } else if loginTwoResponse.EncryptionVersion == 2 { + log.Debug("Login version 2") protectedKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKey) if err != nil { util.HandleError(err) @@ -191,14 +196,21 @@ var loginCmd = &cobra.Command{ util.HandleError(err) } - decryptedPrivateKey, err = crypto.DecryptSymmetric(decryptedProtectedKeyInHex, encryptedPrivateKey, nonProtectedTag, nonProtectedIv) + computedDecryptedPrivateKey, err := crypto.DecryptSymmetric(decryptedProtectedKeyInHex, encryptedPrivateKey, nonProtectedTag, nonProtectedIv) if err != nil { util.HandleError(err) } + + decryptedPrivateKey = computedDecryptedPrivateKey } else { util.PrintErrorMessageAndExit("Insufficient details to decrypt private key") } + if string(decryptedPrivateKey) == "" || email == "" || loginTwoResponse.Token == "" { + log.Debugf("[decryptedPrivateKey=%s] [email=%s] [loginTwoResponse.Token=%s]", string(decryptedPrivateKey), email, loginTwoResponse.Token) + util.PrintErrorMessageAndExit("We were unable to fetch required details to complete your login. Run with -d to see more info") + } + userCredentialsToBeStored := &models.UserCredentials{ Email: email, PrivateKey: string(decryptedPrivateKey), @@ -340,7 +352,7 @@ func generateFromPassword(password string, salt []byte, p *params) (hash []byte, func askForMFACode() string { mfaCodePromptUI := promptui.Prompt{ - Label: "MFA verification code", + Label: "Enter the 2FA verification code sent to your email", } mfaVerifyCode, err := mfaCodePromptUI.Run() diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go index 70c6e627d..f70d29726 100644 --- a/cli/packages/cmd/run.go +++ b/cli/packages/cmd/run.go @@ -54,9 +54,12 @@ var runCmd = &cobra.Command{ return nil }, Run: func(cmd *cobra.Command, args []string) { - envName, err := cmd.Flags().GetString("env") - if err != nil { - util.HandleError(err, "Unable to parse flag") + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } } infisicalToken, err := cmd.Flags().GetString("token") @@ -79,7 +82,7 @@ var runCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } - secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: envName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs}) + secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs}) if err != nil { util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid") diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index 4bb8d0663..625152aa9 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -19,7 +19,6 @@ import ( "github.com/Infisical/infisical-merge/packages/util" "github.com/Infisical/infisical-merge/packages/visualize" "github.com/go-resty/resty/v2" - log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -31,9 +30,12 @@ var secretsCmd = &cobra.Command{ PreRun: toggleDebug, Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - environmentName, err := cmd.Flags().GetString("env") - if err != nil { - util.HandleError(err) + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } } infisicalToken, err := cmd.Flags().GetString("token") @@ -94,9 +96,12 @@ var secretsSetCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { util.RequireLocalWorkspaceFile() - environmentName, err := cmd.Flags().GetString("env") - if err != nil { - util.HandleError(err, "Unable to parse flag") + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } } workspaceFile, err := util.GetWorkSpaceFromFile() @@ -270,11 +275,12 @@ var secretsDeleteCmd = &cobra.Command{ PreRun: toggleDebug, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - environmentName, err := cmd.Flags().GetString("env") - if err != nil { - log.Errorln("Unable to parse the environment name flag") - log.Debugln(err) - return + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } } loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails() @@ -330,9 +336,12 @@ var secretsDeleteCmd = &cobra.Command{ } func getSecretsByNames(cmd *cobra.Command, args []string) { - environmentName, err := cmd.Flags().GetString("env") - if err != nil { - util.HandleError(err, "Unable to parse flag") + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } } infisicalToken, err := cmd.Flags().GetString("token") @@ -373,9 +382,12 @@ func getSecretsByNames(cmd *cobra.Command, args []string) { } func generateExampleEnv(cmd *cobra.Command, args []string) { - environmentName, err := cmd.Flags().GetString("env") - if err != nil { - util.HandleError(err, "Unable to parse flag") + environmentName, _ := cmd.Flags().GetString("env") + if !cmd.Flags().Changed("env") { + environmentFromWorkspace := util.GetEnvelopmentBasedOnGitBranch() + if environmentFromWorkspace != "" { + environmentName = environmentFromWorkspace + } } infisicalToken, err := cmd.Flags().GetString("token") diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go index fca9ae0f2..c2cee9463 100644 --- a/cli/packages/models/cli.go +++ b/cli/packages/models/cli.go @@ -39,8 +39,9 @@ type Workspace struct { } type WorkspaceConfigFile struct { - WorkspaceId string `json:"workspaceId"` - DefaultEnvironment string `json:"defaultEnvironment"` + WorkspaceId string `json:"workspaceId"` + DefaultEnvironment string `json:"defaultEnvironment"` + GitBranchToEnvironmentMapping map[string]string `json:"gitBranchToEnvironmentMapping"` } type SymmetricEncryptionResult struct { @@ -50,7 +51,8 @@ type SymmetricEncryptionResult struct { } type GetAllSecretsParameters struct { - Environment string - InfisicalToken string - TagSlugs string + Environment string + EnvironmentPassedViaFlag bool + InfisicalToken string + TagSlugs string } diff --git a/cli/packages/util/helper.go b/cli/packages/util/helper.go index 2a7ac7317..8d70e9e0a 100644 --- a/cli/packages/util/helper.go +++ b/cli/packages/util/helper.go @@ -1,10 +1,14 @@ package util import ( + "bytes" "crypto/sha256" "encoding/base64" "fmt" "os" + "os/exec" + "path" + "strings" ) type DecodedSymmetricEncryptionDetails = struct { @@ -110,3 +114,14 @@ func GetHashFromStringList(list []string) string { sum := sha256.Sum256(hash.Sum(nil)) return fmt.Sprintf("%x", sum) } + +func getCurrentBranch() (string, error) { + cmd := exec.Command("git", "symbolic-ref", "--short", "HEAD") + var out bytes.Buffer + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + return "", err + } + return path.Base(strings.TrimSpace(out.String())), nil +} diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index 8caa07de4..95065de66 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -131,10 +131,6 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models return nil, err } - if workspaceFile.DefaultEnvironment != "" { - params.Environment = workspaceFile.DefaultEnvironment - } - // Verify environment err = ValidateEnvironmentName(params.Environment, workspaceFile.WorkspaceId, loggedInUserDetails.UserCredentials) if err != nil { @@ -485,3 +481,27 @@ func DeleteBackupSecrets() error { return os.RemoveAll(fullPathToSecretsBackupFolder) } + +func GetEnvelopmentBasedOnGitBranch() string { + branch, err := getCurrentBranch() + if err != nil { + log.Debugf("getEnvelopmentBasedOnGitBranch: [err=%s]", err) + } + + workspaceFile, err := GetWorkSpaceFromFile() + if err != nil { + log.Debugf("getEnvelopmentBasedOnGitBranch: [err=%s]", err) + return "" + } + + envBasedOnGitBranch, ok := workspaceFile.GitBranchToEnvironmentMapping[branch] + + log.Debugf("GetEnvelopmentBasedOnGitBranch: [envBasedOnGitBranch=%s] [ok=%s]", envBasedOnGitBranch, ok) + + if err == nil && ok { + return envBasedOnGitBranch + } else { + log.Debugf("getEnvelopmentBasedOnGitBranch: [err=%s]", err) + return "" + } +} diff --git a/docs/cli/faq.mdx b/docs/cli/faq.mdx index 7eb73258e..daeeb6458 100644 --- a/docs/cli/faq.mdx +++ b/docs/cli/faq.mdx @@ -18,4 +18,8 @@ If you are still experiencing trouble, please seek support. Yes. If you have previously retrieved secrets for a specific project and environment (such as dev, staging, or prod), the `run`/`secret` command will utilize the saved secrets, even when offline, on subsequent fetch attempts. + + + +Yes. This is simply a configuration file and contains no sensitive data. \ No newline at end of file diff --git a/docs/cli/overview.mdx b/docs/cli/overview.mdx index 18f2f1396..47de44e01 100644 --- a/docs/cli/overview.mdx +++ b/docs/cli/overview.mdx @@ -20,7 +20,7 @@ The Infisical CLI provides a way to inject environment variables from the platfo ### Updates ```bash - brew upgrade infisical + brew update && brew upgrade infisical ``` diff --git a/docs/cli/project-config.mdx b/docs/cli/project-config.mdx new file mode 100644 index 000000000..26ccc743d --- /dev/null +++ b/docs/cli/project-config.mdx @@ -0,0 +1,26 @@ +--- +title: "Project config file" +description: "Project config file & customization options" +--- + +To link your local project on your machine with an Infisical project, we suggest using the infisical init CLI command. This will generate a `.infisical.json` file in the root directory of your project. + +The `.infisical.json` file specifies various parameters, such as the Infisical project to retrieve secrets from, along with other configuration options. Furthermore, you can define additional properties in the file to further tailor your local development experience. + +## Set Infisical environment based on GitHub branch +When fetching your secrets from Infisical, you can switch between environments by using the `--env` flag. However, in certain cases, you may prefer the environment to be automatically mapped based on the current GitHub branch you are working on. +To achieve this, simply add the `gitBranchToEnvironmentMapping` property to your configuration file, as shown below. + +```json .infisical.json +{ + "workspaceId": "63ee5410a45f7a1ed39ba118", + "gitBranchToEnvironmentMapping": { + "branchName": "dev", + "anotherBranchName": "staging" + } +} +``` + +### How it works +After configuring this property, every time you use the CLI with the specified configuration file, it will automatically verify if there is a corresponding environment mapping for the current Github branch you are on. +If it exists, the CLI will use that environment to retrieve secrets. You can override this behavior by explicitly using the `--env` flag while interacting with the CLI. \ No newline at end of file diff --git a/docs/images/email-socketlabs-credentials.png b/docs/images/email-socketlabs-credentials.png new file mode 100644 index 000000000..2db862868 Binary files /dev/null and b/docs/images/email-socketlabs-credentials.png differ diff --git a/docs/images/email-socketlabs-dashboard.png b/docs/images/email-socketlabs-dashboard.png new file mode 100644 index 000000000..5a9e4e865 Binary files /dev/null and b/docs/images/email-socketlabs-dashboard.png differ diff --git a/docs/images/email-socketlabs-domains.png b/docs/images/email-socketlabs-domains.png new file mode 100644 index 000000000..33e467c99 Binary files /dev/null and b/docs/images/email-socketlabs-domains.png differ diff --git a/docs/integrations/cloud/vercel.mdx b/docs/integrations/cloud/vercel.mdx index 65526ac22..af20c60cf 100644 --- a/docs/integrations/cloud/vercel.mdx +++ b/docs/integrations/cloud/vercel.mdx @@ -30,3 +30,20 @@ Select which Infisical environment secrets you want to sync to which Vercel app ![integrations vercel](../../images/integrations-vercel-create.png) ![integrations vercel](../../images/integrations-vercel.png) + + + Infisical syncs every envar to Vercel with type `encrypted` unless an existing + envar with the same name in Vercel exists with a different type. Note that + Infisical will not be able to update Vercel envars with type `sensitive` since + they can only be decrypted and modified by Vercel's deployment systems. + + + + The following environment variable names are reserved by Vercel and cannot be + synced: `AWS_SECRET_KEY`, `AWS_EXECUTION_ENV`, `AWS_LAMBDA_LOG_GROUP_NAME`, + `AWS_LAMBDA_LOG_STREAM_NAME`, `AWS_LAMBDA_FUNCTION_NAME`, + `AWS_LAMBDA_FUNCTION_MEMORY_SIZE`, `AWS_LAMBDA_FUNCTION_VERSION`, + `NOW_REGION`, `TZ`, `LAMBDA_TASK_ROOT`, `LAMBDA_RUNTIME_DIR`, + `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`, + `AWS_REGION`, and `AWS_DEFAULT_REGION`. + diff --git a/docs/mint.json b/docs/mint.json index 59be8b375..ebedbd5b4 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -119,6 +119,7 @@ "cli/commands/reset" ] }, + "cli/project-config", "cli/faq" ] }, diff --git a/docs/self-hosting/configuration/email.mdx b/docs/self-hosting/configuration/email.mdx index 8cb6525d3..85c3325ef 100644 --- a/docs/self-hosting/configuration/email.mdx +++ b/docs/self-hosting/configuration/email.mdx @@ -25,7 +25,8 @@ By default, you need to configure the following SMTP [environment variables](htt Below you will find details on how to configure common email providers (not in any particular order). -## Twilio SendGrid + + 1. Create an account and configure [SendGrid](https://sendgrid.com) to send emails. 2. Create a SendGrid API Key under Settings > [API Keys](https://app.sendgrid.com/settings/api_keys) @@ -47,11 +48,12 @@ SMTP_FROM_ADDRESS=hey@example.com # your email address being used to send out em SMTP_FROM_NAME=Infisical ``` - - Remember that you will need to restart Infisical for this to work properly. - + + Remember that you will need to restart Infisical for this to work properly. + + -## Mailgun + 1. Create an account and configure [Mailgun](https://www.mailgun.com) to send emails. 2. Obtain your Mailgun credentials in Sending > Overview > SMTP @@ -70,7 +72,9 @@ SMTP_FROM_ADDRESS=hey@example.com # your email address being used to send out em SMTP_FROM_NAME=Infisical ``` -## AWS SES + + + 1. Create an account and [configure AWS SES](https://aws.amazon.com/premiumsupport/knowledge-center/ses-set-up-connect-smtp/) to send emails in the Amazon SES console. 2. Create an IAM user for SMTP authentication and obtain SMTP credentials in SMTP settings > Create SMTP credentials @@ -82,7 +86,6 @@ SMTP_FROM_NAME=Infisical 3. With your AWS SES SMTP credentials, you can now set up your SMTP environment variables: ``` -SMTP_HOST=smtp.mailgun.org # obtained from credentials page SMTP_HOST=email-smtp.ap-northeast-1.amazonaws.com # SMTP endpoint obtained from SMTP settings SMTP_USERNAME=xxx # your SMTP username SMTP_PASSWORD=xxx # your SMTP password @@ -95,3 +98,40 @@ SMTP_FROM_NAME=Infisical Remember that you will need to restart Infisical for this to work properly. + + + + +1. Create an account and configure [SocketLabs](https://www.socketlabs.com/) to send emails. +2. From the dashboard, navigate to SMTP Credentials > SMTP & APIs > SMTP Credentials to obtain your SocketLabs SMTP credentials. + +![opening SocketLabs dashboard](../../images/email-socketlabs-dashboard.png) + +![obtaining SocketLabs credentials](../../images/email-socketlabs-credentials.png) + +3. With your SocketLabs SMTP credentials, you can now set up your SMTP environment variables: + +``` +SMTP_HOST=smtp.socketlabs.com +SMTP_USERNAME=username # obtained from your credentials +SMTP_PASSWORD=password # obtained from your credentials +SMTP_PORT=587 +SMTP_SECURE=true +SMTP_FROM_ADDRESS=hey@example.com # your email address being used to send out emails +SMTP_FROM_NAME=Infisical +``` + + + The `SMTP_FROM_ADDRESS` environment variable should be an email for an + authenticated domain under Configuration > Domain Management in SocketLabs. + For example, if you're using SocketLabs in sandbox mode, then you may use an + email like `team@sandbox.socketlabs.dev`. + + +![SocketLabs domain management](../../images/email-socketlabs-domains.png) + + + Remember that you will need to restart Infisical for this to work properly. + + + diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js index 94133eee3..1fef7cf80 100644 --- a/frontend/.storybook/preview.js +++ b/frontend/.storybook/preview.js @@ -10,6 +10,6 @@ export const parameters = { } }, darkMode: { - dark: { ...themes.dark, appContentBg: '#0e1014', appBg: '#0e1014' } + dark: { ...themes.dark, appContentBg: 'rgb(14,16,20)', appBg: 'rgb(14,16,20)' } } }; diff --git a/frontend/src/components/v2/EmptyState/EmptyState.stories.tsx b/frontend/src/components/v2/EmptyState/EmptyState.stories.tsx new file mode 100644 index 000000000..d3fb1bd4a --- /dev/null +++ b/frontend/src/components/v2/EmptyState/EmptyState.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { EmptyState } from './EmptyState'; + +const meta: Meta = { + title: 'Components/EmptyState', + component: EmptyState, + tags: ['v2'], + argTypes: {}, + args: { + title: 'No members found' + } +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => +}; diff --git a/frontend/src/components/v2/EmptyState/EmptyState.tsx b/frontend/src/components/v2/EmptyState/EmptyState.tsx new file mode 100644 index 000000000..be2333723 --- /dev/null +++ b/frontend/src/components/v2/EmptyState/EmptyState.tsx @@ -0,0 +1,21 @@ +import { ReactNode } from 'react'; +import { faCubesStacked, IconDefinition } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { twMerge } from 'tailwind-merge'; + +type Props = { + title: ReactNode; + className?: string; + children?: ReactNode; + icon?: IconDefinition; +}; + +export const EmptyState = ({ title, className, children, icon = faCubesStacked }: Props) => ( +
+ +
+
{title}
+
{children}
+
+
+); diff --git a/frontend/src/components/v2/EmptyState/index.tsx b/frontend/src/components/v2/EmptyState/index.tsx new file mode 100644 index 000000000..89798dbd6 --- /dev/null +++ b/frontend/src/components/v2/EmptyState/index.tsx @@ -0,0 +1 @@ +export { EmptyState } from './EmptyState'; diff --git a/frontend/src/components/v2/Skeleton/Skeleton.stories.tsx b/frontend/src/components/v2/Skeleton/Skeleton.stories.tsx new file mode 100644 index 000000000..5380f8f7f --- /dev/null +++ b/frontend/src/components/v2/Skeleton/Skeleton.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Skeleton } from './Skeleton'; + +const meta: Meta = { + title: 'Components/Skeleton', + component: Skeleton, + tags: ['v2'], + argTypes: {} +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => +}; diff --git a/frontend/src/components/v2/Skeleton/Skeleton.tsx b/frontend/src/components/v2/Skeleton/Skeleton.tsx new file mode 100644 index 000000000..4fe25ad64 --- /dev/null +++ b/frontend/src/components/v2/Skeleton/Skeleton.tsx @@ -0,0 +1,12 @@ +import { twMerge } from 'tailwind-merge'; + +export type Props = { + className?: string; +}; + +// To show something is coming up +// Can be used with cards +// Tables etc +export const Skeleton = ({ className }: Props) => ( +
+); diff --git a/frontend/src/components/v2/Skeleton/index.tsx b/frontend/src/components/v2/Skeleton/index.tsx new file mode 100644 index 000000000..3f34073b3 --- /dev/null +++ b/frontend/src/components/v2/Skeleton/index.tsx @@ -0,0 +1 @@ +export { Skeleton } from './Skeleton'; diff --git a/frontend/src/components/v2/Table/Table.stories.tsx b/frontend/src/components/v2/Table/Table.stories.tsx index 666d43ed4..19be5216b 100644 --- a/frontend/src/components/v2/Table/Table.stories.tsx +++ b/frontend/src/components/v2/Table/Table.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { Table, TableContainer, TBody, Td, Th, THead, Tr } from './Table'; +import { Table, TableContainer, TableSkeleton, TBody, Td, Th, THead, Tr } from './Table'; const meta: Meta = { title: 'Components/Table', @@ -39,3 +39,22 @@ export const Basic: Story = { ) }; + +export const Loading: Story = { + render: (args) => ( + + + + + + + + + + + + +
Head#1Head#2Head#3
+
+ ) +}; diff --git a/frontend/src/components/v2/Table/Table.tsx b/frontend/src/components/v2/Table/Table.tsx index d44ba7755..dd4bb4e7b 100644 --- a/frontend/src/components/v2/Table/Table.tsx +++ b/frontend/src/components/v2/Table/Table.tsx @@ -1,6 +1,8 @@ import { HTMLAttributes, ReactNode, TdHTMLAttributes } from 'react'; import { twMerge } from 'tailwind-merge'; +import { Skeleton } from '../Skeleton'; + export type TableContainerProps = { children: ReactNode; isRounded?: boolean; @@ -14,7 +16,7 @@ export const TableContainer = ({ }: TableContainerProps): JSX.Element => (
( @@ -59,7 +61,10 @@ export type TrProps = { } & HTMLAttributes; export const Tr = ({ children, className, ...props }: TrProps): JSX.Element => ( - + {children} ); @@ -71,7 +76,7 @@ export type ThProps = { }; export const Th = ({ children, className }: ThProps): JSX.Element => ( - + ); // table body @@ -95,3 +100,25 @@ export const Td = ({ children, className, ...props }: TdProps): JSX.Element => ( {children} ); + +export type TBodyLoader = { + rows?: number; + columns: number; + className?: string; + // unique key for mapping + key: string; +}; + +export const TableSkeleton = ({ rows = 3, columns, key, className }: TBodyLoader): JSX.Element => ( + <> + {Array.apply(0, Array(rows)).map((_x, i) => ( + + {Array.apply(0, Array(columns)).map((_y, j) => ( + + ))} + + ))} + +); diff --git a/frontend/src/components/v2/Table/index.tsx b/frontend/src/components/v2/Table/index.tsx index ccf14665f..41ab02207 100644 --- a/frontend/src/components/v2/Table/index.tsx +++ b/frontend/src/components/v2/Table/index.tsx @@ -7,4 +7,4 @@ export type { ThProps, TrProps } from './Table'; -export { Table, TableContainer, TBody, Td, Th, THead, Tr } from './Table'; +export { Table, TableContainer, TableSkeleton,TBody, Td, Th, THead, Tr } from './Table'; diff --git a/frontend/src/components/v2/index.tsx b/frontend/src/components/v2/index.tsx index d69b0bfb4..52c8fc4f4 100644 --- a/frontend/src/components/v2/index.tsx +++ b/frontend/src/components/v2/index.tsx @@ -3,12 +3,14 @@ export * from './Card'; export * from './Checkbox'; export * from './DeleteActionModal'; export * from './Dropdown'; +export * from './EmptyState'; export * from './FormControl'; export * from './IconButton'; export * from './Input'; export * from './Menu'; export * from './Modal'; export * from './Select'; +export * from './Skeleton'; export * from './Spinner'; export * from './Switch'; export * from './Table'; diff --git a/frontend/src/views/Settings/OrgSettingsPage/OrgSettingsPage.tsx b/frontend/src/views/Settings/OrgSettingsPage/OrgSettingsPage.tsx index 26c06a9d2..0f4973609 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/OrgSettingsPage.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/OrgSettingsPage.tsx @@ -36,10 +36,12 @@ export const OrgSettingsPage = () => { const { createNotification } = useNotificationContext(); const orgId = currentOrg?._id || ''; - const { data: orgUsers } = useGetOrgUsers(orgId); - const { data: workspaceMemberships } = useGetUserWorkspaceMemberships(orgId); + const { data: orgUsers, isLoading: isOrgUserLoading } = useGetOrgUsers(orgId); + const { data: workspaceMemberships, isLoading: IsWsMembershipLoading } = + useGetUserWorkspaceMemberships(orgId); const { data: wsKey } = useGetUserWsKey(currentWorkspace?._id || ''); - const { data: incidentContact } = useGetOrgIncidentContact(orgId); + const { data: incidentContact, isLoading: IsIncidentContactLoading } = + useGetOrgIncidentContact(orgId); const renameOrg = useRenameOrg(); const removeUserOrgMembership = useDeleteOrgMembership(); @@ -84,7 +86,7 @@ export const OrgSettingsPage = () => { } catch (error) { console.error(error); createNotification({ - text: 'Failed to remove user from org', + text: 'Failed to remove user from the organization', type: 'error' }); } @@ -95,7 +97,7 @@ export const OrgSettingsPage = () => { try { await addUserToOrg.mutateAsync({ organizationId: currentOrg?._id, inviteeEmail: email }); createNotification({ - text: 'Successfully invited user to org', + text: 'Successfully invited user to the organization.', type: 'success' }); } catch (error) { @@ -197,9 +199,9 @@ export const OrgSettingsPage = () => { /** * This function deleted a workspace. - * It first checks if there is more than one workspace aviable. Otherwise, it doesn't delete + * It first checks if there is more than one workspace available. Otherwise, it doesn't delete * It then checks if the name of the workspace to be deleted is correct. Otherwise, it doesn't delete. - * It then deletes the workspace and forwards the user to another aviable workspace. + * It then deletes the workspace and forwards the user to another available workspace. */ // const executeDeletingWorkspace = async () => { // const userWorkspaces = await getWorkspaces(); @@ -230,13 +232,11 @@ export const OrgSettingsPage = () => {
-

+

{t('section-members:org-members')}

-

- {t('section-members:org-members-description')} -

{
Promise; onAddContact: (email: string) => Promise; @@ -39,7 +46,8 @@ type TAddContactForm = yup.InferType; export const OrgIncidentContactsTable = ({ contacts = [], onAddContact, - onRemoveContact + onRemoveContact, + isLoading }: Props) => { const [searchContact, setSearchContact] = useState(''); const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ @@ -66,6 +74,10 @@ export const OrgIncidentContactsTable = ({ handlePopUpClose('removeContact'); }; + const filteredContacts = contacts.filter(({ email }) => + email.toLocaleLowerCase().includes(searchContact) + ); + return (
@@ -96,28 +108,25 @@ export const OrgIncidentContactsTable = ({
- {contacts - ?.filter(({ email }) => email.toLocaleLowerCase().includes(searchContact)) - ?.map(({ email }) => ( - - - - - ))} + {isLoading && } + {filteredContacts?.map(({ email }) => ( + + + + + ))}
{children}{children}
+ +
{email} - handlePopUpOpen('removeContact', { email })} - > - - -
{email} + handlePopUpOpen('removeContact', { email })} + > + + +
- {contacts - ?.filter(({ email }) => email.toLocaleLowerCase().includes(searchContact)) - ?.length === 0 && ( -
No incident contacts found
+ {filteredContacts?.length === 0 && !isLoading && ( + )}
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgMembersTable/OrgMembersTable.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgMembersTable/OrgMembersTable.tsx index 2911ae603..020eae963 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgMembersTable/OrgMembersTable.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgMembersTable/OrgMembersTable.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; -import { faMagnifyingGlass, faPlus, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { faMagnifyingGlass, faPlus, faTrash, faUsers } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; @@ -8,6 +8,7 @@ import * as yup from 'yup'; import { Button, DeleteActionModal, + EmptyState, FormControl, IconButton, Input, @@ -17,14 +18,14 @@ import { SelectItem, Table, TableContainer, + TableSkeleton, Tag, TBody, Td, Th, THead, Tr, - UpgradePlanModal -} from '@app/components/v2'; + UpgradePlanModal} from '@app/components/v2'; import { usePopUp } from '@app/hooks'; import { OrgUser, Workspace } from '@app/hooks/api/types'; @@ -32,6 +33,7 @@ type Props = { members?: OrgUser[]; workspaceMemberships?: Record; orgName: string; + isLoading?: boolean; isMoreUserNotAllowed: boolean; onRemoveMember: (userId: string) => Promise; onInviteMember: (email: string) => Promise; @@ -56,7 +58,8 @@ export const OrgMembersTable = ({ onInviteMember, onGrantAccess, onRoleChange, - userId + userId, + isLoading }: Props) => { const [searchMemberFilter, setSearchMemberFilter] = useState(''); const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ @@ -72,8 +75,8 @@ export const OrgMembersTable = ({ formState: { isSubmitting } } = useForm({ resolver: yupResolver(addMemberFormSchema) }); - const onAddMember = ({ email }: TAddMemberForm) => { - onInviteMember(email); + const onAddMember = async ({ email }: TAddMemberForm) => { + await onInviteMember(email); handlePopUpClose('addMember'); reset(); }; @@ -140,73 +143,79 @@ export const OrgMembersTable = ({ - {filterdUser.map(({ user, inviteEmail, role, _id: orgMembershipId, status }) => { - const name = user ? `${user.firstName} ${user.lastName}` : '-'; - const email = user?.email || inviteEmail; - const userWs = workspaceMemberships?.[user?._id]; + {isLoading && } + {!isLoading && + filterdUser.map(({ user, inviteEmail, role, _id: orgMembershipId, status }) => { + const name = user ? `${user.firstName} ${user.lastName}` : '-'; + const email = user?.email || inviteEmail; + const userWs = workspaceMemberships?.[user?._id]; - return ( - - {name} - {email} - - {status === 'accepted' && ( - - )} - {(status === 'invited' || status === 'verified') && ( - - )} - {status === 'completed' && ( - - )} - - - {userWs ? ( - userWs?.map(({ name: wsName, _id }) => ( - - {wsName} - - )) - ) : ( - This user isn't part of any projects yet - )} - - - {userId !== user?._id && handlePopUpOpen('removeMember', { id: orgMembershipId })} - > - - } - - - ); - })} + return ( + + {name} + {email} + + {status === 'accepted' && ( + + )} + {(status === 'invited' || status === 'verified') && ( + + )} + {status === 'completed' && ( + + )} + + + {userWs ? ( + userWs?.map(({ name: wsName, _id }) => ( + + {wsName} + + )) + ) : ( + This user isn't part of any projects yet + )} + + + {userId !== user?._id && ( + handlePopUpOpen('removeMember', { id: orgMembershipId })} + > + + + )} + + + ); + })} - {filterdUser.length === 0 && No project members found} + {!isLoading && filterdUser?.length === 0 && ( + + )}
{ const { t } = useTranslation(); - const { currentWorkspace, workspaces } = useWorkspace(); + const { currentWorkspace, workspaces, isLoading: isWorkspaceLoading } = useWorkspace(); const router = useRouter(); - const { data: serviceTokens } = useGetUserWsServiceTokens({ - workspaceID: currentWorkspace?._id || '' - }); + const workspaceID = currentWorkspace?._id || ''; const { createNotification } = useNotificationContext(); // delete action worksapce @@ -66,12 +64,15 @@ export const ProjectSettingsPage = () => { const deleteWsEnv = useDeleteWsEnvironment(); // service token + const { data: serviceTokens, isLoading: isServiceTokenLoading } = useGetUserWsServiceTokens({ + workspaceID: currentWorkspace?._id || '' + }); const { data: latestFileKey } = useGetUserWsKey(workspaceID); const createServiceToken = useCreateServiceToken(); const deleteServiceToken = useDeleteServiceToken(); // tag - const { data: wsTags } = useGetWsTags(workspaceID); + const { data: wsTags, isLoading: isTagLoading } = useGetWsTags(workspaceID); const createWsTag = useCreateWsTag(); const deleteWsTag = useDeleteWsTag(); @@ -300,7 +301,7 @@ export const ProjectSettingsPage = () => { }; return ( -
+
{/* TODO(akhilmhdh): Remove this right when layout is refactored */}
@@ -319,6 +320,7 @@ export const ProjectSettingsPage = () => { /> { isEnvServiceAllowed={isEnvServiceAllowed} /> { onCreateToken={onCreateServiceToken} /> { const { t } = useTranslation(); return ( -
-
-

- {t('settings-project:auto-capitalization')} -

- { - onAutoCapitalizationChange(state as boolean); - }} - > - {t('settings-project:auto-capitalization-description')} - -
-
+
+

{t('settings-project:auto-capitalization')}

+ { + onAutoCapitalizationChange(state as boolean); + }} + > + {t('settings-project:auto-capitalization-description')} + +
); }; diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx index ddcf7f4b3..817435a12 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx @@ -7,6 +7,7 @@ import * as yup from 'yup'; import { Button, DeleteActionModal, + EmptyState, FormControl, IconButton, Input, @@ -14,6 +15,7 @@ import { ModalContent, Table, TableContainer, + TableSkeleton, TBody, Td, Th, @@ -25,6 +27,7 @@ import { usePopUp } from '@app/hooks/usePopUp'; type Props = { environments: Array<{ name: string; slug: string }>; + isLoading?: boolean; isEnvServiceAllowed: boolean; onCreate: (data: CreateUpdateEnvFormData) => Promise; onUpdate: (oldEnvSlug: string, data: CreateUpdateEnvFormData) => Promise; @@ -43,6 +46,7 @@ export const EnvironmentSection = ({ isEnvServiceAllowed, onCreate, onDelete, + isLoading, onUpdate }: Props): JSX.Element => { const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ @@ -116,7 +120,8 @@ export const EnvironmentSection = ({ - {environments?.length > 0 ? ( + {isLoading && } + {!isLoading && environments.map(({ name, slug }) => ( {name} @@ -152,11 +157,11 @@ export const EnvironmentSection = ({ - )) - ) : ( + ))} + {!isLoading && environments?.length === 0 && ( - - No environments found + + )} diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsSection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsSection.tsx index 6aa4d26be..98019e0ad 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsSection.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/SecretTagsSection/SecretTagsSection.tsx @@ -1,5 +1,5 @@ import { Controller, useForm } from 'react-hook-form'; -import { faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons'; +import { faPlus, faTags, faTrashCan } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; @@ -7,6 +7,7 @@ import * as yup from 'yup'; import { Button, DeleteActionModal, + EmptyState, FormControl, IconButton, Input, @@ -16,23 +17,24 @@ import { ModalTrigger, Table, TableContainer, + TableSkeleton, TBody, Td, Th, THead, - Tr, -} from '@app/components/v2'; + Tr} from '@app/components/v2'; import { usePopUp } from '@app/hooks'; import { WorkspaceTag } from '@app/hooks/api/types'; const createTagSchema = yup.object({ - name: yup.string().required().label('Tag Name'), + name: yup.string().required().label('Tag Name') }); export type CreateWsTag = yup.InferType; type Props = { tags: WorkspaceTag[]; + isLoading?: boolean; workspaceName: string; onDeleteTag: (tagID: string) => Promise; onCreateTag: (data: CreateWsTag) => Promise; @@ -42,6 +44,7 @@ type DeleteModalData = { name: string; id: string }; export const SecretTagsSection = ({ tags = [], + isLoading, onDeleteTag, workspaceName, onCreateTag @@ -76,7 +79,10 @@ export const SecretTagsSection = ({

Secret Tags

-

Every secret can be assigned to one or more tags. Here you can add and remove tags for the current project.

+

+ Every secret can be assigned to one or more tags. Here you can add and remove tags for + the current project. +

( @@ -130,7 +136,7 @@ export const SecretTagsSection = ({
- + @@ -140,7 +146,8 @@ export const SecretTagsSection = ({ - {tags?.length > 0 ? ( + {isLoading && } + {!isLoading && tags.map(({ _id, name, slug }) => ( @@ -149,7 +156,7 @@ export const SecretTagsSection = ({ handlePopUpOpen('deleteTagConfirmation', { - name, + name, id: _id }) } @@ -160,11 +167,11 @@ export const SecretTagsSection = ({ - )) - ) : ( + ))} + {!isLoading && tags?.length === 0 && ( - )} diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/ServiceTokenSection/ServiceTokenSection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/ServiceTokenSection/ServiceTokenSection.tsx index 052fdc678..e4c4bbf2e 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/ServiceTokenSection/ServiceTokenSection.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/ServiceTokenSection/ServiceTokenSection.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { faCheck, faCopy, faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons'; +import { faCheck, faCopy, faKey, faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; @@ -9,6 +9,7 @@ import * as yup from 'yup'; import { Button, DeleteActionModal, + EmptyState, FormControl, IconButton, Input, @@ -20,6 +21,7 @@ import { SelectItem, Table, TableContainer, + TableSkeleton, TBody, Td, Th, @@ -47,6 +49,7 @@ export type CreateServiceToken = yup.InferType; type Props = { tokens: ServiceToken[]; + isLoading?: boolean; workspaceName: string; environments: WorkspaceEnv[]; onDeleteToken: (serviceTokenID: string) => Promise; @@ -57,6 +60,7 @@ type DeleteModalData = { name: string; id: string }; export const ServiceTokenSection = ({ tokens = [], + isLoading, onDeleteToken, workspaceName, environments = [], @@ -252,7 +256,7 @@ export const ServiceTokenSection = ({ isOpen={popUp.deleteAPITokenConfirmation.isOpen} title={`Delete ${ (popUp?.deleteAPITokenConfirmation?.data as DeleteModalData)?.name || ' ' - } api key?`} + } service token?`} onChange={(isOpen) => handlePopUpToggle('deleteAPITokenConfirmation', isOpen)} deleteKey={(popUp?.deleteAPITokenConfirmation?.data as DeleteModalData)?.name} onClose={() => handlePopUpClose('deleteAPITokenConfirmation')} @@ -269,7 +273,8 @@ export const ServiceTokenSection = ({ - {tokens?.length > 0 ? ( + {isLoading && } + {!isLoading && tokens.map((row) => ( @@ -290,11 +295,11 @@ export const ServiceTokenSection = ({ - )) - ) : ( + ))} + {!isLoading && tokens?.length === 0 && ( )} diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index f2c832acc..54f527c47 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1340,163 +1340,165 @@ module.exports = { 900: '#176437', DEFAULT: '#2ecc71' } + }, + keyframes: { + type: { + '0%': { transform: 'translateX(0ch)' }, + '5%, 10%': { transform: 'translateX(1ch)' }, + '15%, 20%': { transform: 'translateX(2ch)' }, + '25%, 30%': { transform: 'translateX(3ch)' }, + '35%, 40%': { transform: 'translateX(4ch)' }, + '45%, 50%': { transform: 'translateX(5ch)' }, + '55%, 60%': { transform: 'translateX(6ch)' }, + '65%, 70%': { transform: 'translateX(7ch)' }, + '75%, 80%': { transform: 'translateX(8ch)' }, + '85%, 90%': { transform: 'translateX(9ch)' }, + '95%, 100%': { transform: 'translateX(11ch)' } + }, + // REQUIRED BY DESIGN COMPONENT + // MODAL + fadeIn: { + '0%': { opacity: 0 }, + '100%': { opacity: 1 } + }, + popIn: { + from: { + opacity: 0, + transform: 'translate(-50%, -48%) scale(0.96)' + }, + to: { + opacity: 1, + transform: 'translate(-50%, -50%) scale(1)' + } + }, + // Dropdown + slideUpAndFade: { + from: { + opacity: 0, + transform: ' translateY(2px)' + }, + to: { + opacity: 1, + transform: ' translateY(0)' + } + }, + slideRightAndFade: { + from: { + opacity: 0, + transform: ' translateX(-2px)' + }, + to: { + opacity: 1, + transform: ' translateX(0)' + } + }, + slideDownAndFade: { + from: { + opacity: 0, + transform: ' translateY(-2px)' + }, + to: { + opacity: 1, + transform: ' translateY(0)' + } + }, + slideLeftAndFade: { + from: { + opacity: 0, + transform: ' translateX(2px)' + }, + to: { + opacity: 1, + transform: ' translateX(0)' + } + }, + // END + spin: { + '0%': { transform: 'rotate(0deg)' }, + '40%': { transform: 'rotate(360deg)' }, + '100%': { transform: 'rotate(360deg)' } + }, + bounce: { + '0%': { transform: 'translateY(-90%)' }, + '100%': { transform: 'translateY(-100%)' } + }, + wiggle: { + '0%, 100%': { transform: 'rotate(-3deg)' }, + '50%': { transform: 'rotate(3deg)' } + }, + ping: { + '75%, 100%': { + transform: 'scale(2)', + opacity: 0 + } + }, + popup: { + '0%': { + transform: 'scale(0.2)', + opacity: 0 + // transform: "translateY(120%)", + }, + '100%': { + transform: 'scale(1)', + opacity: 1 + // transform: "translateY(100%)", + } + }, + popright: { + '0%': { + transform: 'translateX(-100%)' + }, + '100%': { + transform: 'translateX(0%)' + } + }, + popleft: { + '0%': { + transform: 'translateX(100%)' + }, + '100%': { + transform: 'translateX(0%)' + } + }, + popdown: { + '0%': { + transform: 'scale(0.2)', + opacity: 0 + // transform: "translateY(80%)", + }, + '100%': { + transform: 'scale(1)', + opacity: 1 + // transform: "translateY(100%)", + } + } + }, + animation: { + // Design Lib + // MODAL + fadeIn: 'fadeIn 100ms cubic-bezier(0.16, 1, 0.3, 1)', + popIn: 'popIn 150ms cubic-bezier(0.16, 1, 0.3, 1);', + // Dropdown + slideDownAndFade: 'slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', + slideLeftAndFade: 'slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', + slideUpAndFade: 'slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', + slideRightAndFade: 'slideRightAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', + // END + // TODO:(akhilmhdh) remove all these unused and keep the config file as small as possible + // Make the whole color pallelte into simpler + bounce: 'bounce 1000ms ease-in-out infinite', + spin: 'spin 4000ms ease-in-out infinite', + cursor: 'cursor .6s linear infinite alternate', + type: 'type 2.7s ease-out .8s infinite alternate both', + 'type-reverse': 'type 1.8s ease-out 0s infinite alternate-reverse both', + wiggle: 'wiggle 200ms ease-in-out', + ping: 'ping 1000ms ease-in-out infinite', + popup: 'popup 300ms ease-in-out', + popdown: 'popdown 300ms ease-in-out', + popright: 'popright 100ms ease-in-out', + popleft: 'popleft 100ms ease-in-out' } }, - keyframes: { - type: { - '0%': { transform: 'translateX(0ch)' }, - '5%, 10%': { transform: 'translateX(1ch)' }, - '15%, 20%': { transform: 'translateX(2ch)' }, - '25%, 30%': { transform: 'translateX(3ch)' }, - '35%, 40%': { transform: 'translateX(4ch)' }, - '45%, 50%': { transform: 'translateX(5ch)' }, - '55%, 60%': { transform: 'translateX(6ch)' }, - '65%, 70%': { transform: 'translateX(7ch)' }, - '75%, 80%': { transform: 'translateX(8ch)' }, - '85%, 90%': { transform: 'translateX(9ch)' }, - '95%, 100%': { transform: 'translateX(11ch)' } - }, - // REQUIRED BY DEISGN COMPONENT - // MODAL - fadeIn: { - '0%': { opacity: 0 }, - '100%': { opacity: 1 } - }, - popIn: { - from: { - opacity: 0, - transform: 'translate(-50%, -48%) scale(0.96)' - }, - to: { - opacity: 1, - transform: 'translate(-50%, -50%) scale(1)' - } - }, - // Dropdown - slideUpAndFade: { - from: { - opacity: 0, - transform: ' translateY(2px)' - }, - to: { - opacity: 1, - transform: ' translateY(0)' - } - }, - slideRightAndFade: { - from: { - opacity: 0, - transform: ' translateX(-2px)' - }, - to: { - opacity: 1, - transform: ' translateX(0)' - } - }, - slideDownAndFade: { - from: { - opacity: 0, - transform: ' translateY(-2px)' - }, - to: { - opacity: 1, - transform: ' translateY(0)' - } - }, - slideLeftAndFade: { - from: { - opacity: 0, - transform: ' translateX(2px)' - }, - to: { - opacity: 1, - transform: ' translateX(0)' - } - }, - // END - spin: { - '0%': { transform: 'rotate(0deg)' }, - '40%': { transform: 'rotate(360deg)' }, - '100%': { transform: 'rotate(360deg)' } - }, - bounce: { - '0%': { transform: 'translateY(-90%)' }, - '100%': { transform: 'translateY(-100%)' } - }, - wiggle: { - '0%, 100%': { transform: 'rotate(-3deg)' }, - '50%': { transform: 'rotate(3deg)' } - }, - ping: { - '75%, 100%': { - transform: 'scale(2)', - opacity: 0 - } - }, - popup: { - '0%': { - transform: 'scale(0.2)', - opacity: 0 - // transform: "translateY(120%)", - }, - '100%': { - transform: 'scale(1)', - opacity: 1 - // transform: "translateY(100%)", - } - }, - popright: { - '0%': { - transform: 'translateX(-100%)' - }, - '100%': { - transform: 'translateX(0%)' - } - }, - popleft: { - '0%': { - transform: 'translateX(100%)' - }, - '100%': { - transform: 'translateX(0%)' - } - }, - popdown: { - '0%': { - transform: 'scale(0.2)', - opacity: 0 - // transform: "translateY(80%)", - }, - '100%': { - transform: 'scale(1)', - opacity: 1 - // transform: "translateY(100%)", - } - } - }, - animation: { - // Design Lib - // MODAL - fadeIn: 'fadeIn 100ms cubic-bezier(0.16, 1, 0.3, 1)', - popIn: 'popIn 150ms cubic-bezier(0.16, 1, 0.3, 1);', - // Dropdown - slideDownAndFade: 'slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', - slideLeftAndFade: 'slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', - slideUpAndFade: 'slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', - slideRightAndFade: 'slideRightAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', - // END - bounce: 'bounce 1000ms ease-in-out infinite', - spin: 'spin 4000ms ease-in-out infinite', - cursor: 'cursor .6s linear infinite alternate', - type: 'type 2.7s ease-out .8s infinite alternate both', - 'type-reverse': 'type 1.8s ease-out 0s infinite alternate-reverse both', - wiggle: 'wiggle 200ms ease-in-out', - ping: 'ping 1000ms ease-in-out infinite', - popup: 'popup 300ms ease-in-out', - popdown: 'popdown 300ms ease-in-out', - popright: 'popright 100ms ease-in-out', - popleft: 'popleft 100ms ease-in-out' - }, fontSize: { xxxs: '.23rem', xxs: '.5rem', diff --git a/i18n/README.de.md b/i18n/README.de.md new file mode 100644 index 000000000..6d6422d0c --- /dev/null +++ b/i18n/README.de.md @@ -0,0 +1,368 @@ +

+ infisical + infisical +

+

+

Open-Source-Tool mit End-to-End-Verschlüsselung zur Verwaltung von Privaten Schlüsseln (Secrets) und Konfigurationen in Ihrem Team, auf Geräten und in Ihrer Infrastruktur.

+

+ +

+ Slack | + Infisical Cloud | + Self-Hosting | + Dokumentation (en) | + Website +

+ +

+ + Infisical wird unter der MIT Lizens veröffentlicht. + + + PRs welcome! + + + git commit activity + + + Cloudsmith downloads + + + Slack community channel + + + Infisical Twitter + +

+ +Dashboard + +**In anderen Sprachen lesen**: [Englisch](i18n/README.en.md) +[Deutsch](i18n/README.de.md) +[Spanisch](i18n/README.es.md) +[Koreanisch](i18n/README.ko.md) +[Türkisch](i18n/README.tr.md) +[Bahasa Indonesisch](i18n/README.id.md) +[Portugiesisch-Brasilianisch](i18n/README.pt-br.md) + +**[Infisical](https://infisical.com)** ist ein Open-Source, End-To-End-Verschlüsseltes Verwaltungstool für Private Schlüssel (Secrets), mit welchem sich API-Keys und Konfigurationen zentral verwalten lassen. Von Infisical aus kannst du deine Schlüssel in deinen ganzen Entwicklungsprozess (Lokal, Staging, Live) integrieren. Es ist leicht verständlich und benutzerfreundlich aufgebaut. + +- **[Benutzerfreundliches Dashboard](https://infisical.com/docs/getting-started/dashboard/project)** um die Schlüssel und Konfigurationen deines Teams über verschiedene Projekte hinweg zu verwalten. +- **[Sprachunabhängige CLI](https://infisical.com/docs/cli/overview)** um deine Schlüssel und Konfigurationen in deinem lokalen Workflow zu integrieren. +- **[Volle Kontrolle über deine Daten](https://infisical.com/docs/self-hosting/overview)** - hoste Infisical auf deinem eigenen Server! +- **Mehrere Entwicklungsumgebungen** pro Projekt (bspw. „development“, „staging“, „production“, etc.) +- **Benutzerspezifisches Überschreiben** von Schlüsseln und Konfigurationen +- **[Integrationen](https://infisical.com/docs/integrations/overview)** mit CI/CD und Produktions-Infrastruktur +- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** - verwalte Schlüssel mit API-Anfragen +- **[Geheime Versionierung](https://infisical.com/docs/getting-started/dashboard/versioning)** um Veränderungen zu beobachten +- **[Aktivitätshistorien](https://infisical.com/docs/getting-started/dashboard/audit-logs)** um alle Veränderungen in einem Projekt zu prüfen +- **[Wiederherstellung von Schlüsseln](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** um zu bestimmten Zeitpunkten/Snapshots zurückzukehren +- **Rechte-basierte Zugriffskontrolle** pro Entwicklungsumgebung +- **2FA** (weitere Optionen folgen) +- 🔜 **1-Click Deploy** zu AWS +- 🔜 **Automatische Secret Rotation** +- 🔜 **Smarte Sicherheitshinweise** +- 🔜 **Secret Rotation** +- 🔜 **Slack & MS Teams** Integration + +und vieles mehr. + +## 🚀 Los gehts + +Um schnell loszulegen, lies unseren „[get started guide](https://infisical.com/docs/getting-started/introduction)“. + +

+ + +

+ +## 🔥 Was macht Infisical so großartig? + +Infisical erleichtert das Verwalten von privaten Schlüsseln und ist standardmäßig End-to-End-verschlüsselt. Unser Ziel ist es, das allen Entwicklern und nicht nur Sicherheitsteams zu erleichtern. + +Einem [Bericht](https://www.ekransystem.com/en/blog/secrets-management) zufolge, nutzen nur 10% aller Unternehmen Lösungen zur Verwaltung von Sicherheitsschlüsseln, obwohl alle zu einem gewissen Maße digitale Schlüssel verwenden. + +Wenn dir Effizienz und Sicherheit wichtig sind, ist Infisical genau richtig für dich! + +Wir arbeiten hart daran, Infisical stetig zu erweitern. Vermisst du bestimmte Integrationen oder wünscht dir ein neues Feature? [Erstelle ein Ticket](https://github.com/Infisical/infisical/issues) oder [trage direkt zum Repository bei](https://infisical.com/docs/contributing/overview). + +## 🔌 Integrationen + +Wir setzen gerade das Fundament und [integrieren](https://infisical.com/docs/integrations/overview) Infisical wo es nur geht, sodass Schlüssel überall synchronisiert werden können. Jede Hilfe ist Willkommen! :) + +
{name}
- No tags found for this project + +
{row.name}
- No service tokens found +
+ + + + + + + + +
Plattformen Frameworks
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + ✔️ Docker + + + + ✔️ Docker Compose + + + + ✔️ Heroku + +
+ + ✔️ Vercel + + + + ✔️ Kubernetes + + + + ✔️ Fly.io + +
+ 🔜 Supabase + + + ✔️ GitHub Actions + + + 🔜 Railway (https://github.com/Infisical/infisical/issues/271) +
+ 🔜 GCP SM (https://github.com/Infisical/infisical/issues/285) + + + ✔️ GitLab CI/CD + + + 🔜 CircleCI (https://github.com/Infisical/infisical/issues/91) +
+ 🔜 Jenkins + + 🔜 Digital Ocean + + 🔜 Azure +
+ 🔜 TravisCI + + + ✔️ AWS Secrets Manager + + + 🔜 Forge +
+ 🔜 Bitbucket + + + ✔️ AWS Parameter Store + + + + ✔️ Render + +
+ 🔜 BuddyCI + + 🔜 Serverless + + + ✔️ Netlify + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + ✔️ React + + + + ✔️ Express + +
+ + ✔️ Gatsby + + + + ✔️ Flask + +
+ + ✔️ Django + + + + ✔️ Laravel + +
+ + ✔️ NestJS + + + + ✔️ Remix + +
+ + ✔️ Next.js + + + + ✔️ Vite + +
+ + ✔️ Vue + + + + ✔️ Ruby on Rails + +
+ + ✔️ Fiber + + + + ✔️ Nuxt + +
+ + ✔️ .NET + + + And more... +
+ +
+ +## 💚 Community & Support + +- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (Für Diskussionen und Gespräche mit der Community und dem Infisical Team) +- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (Für Hilfe bei der Entwicklung und tiefergehenden Diskussionen zu bestimmten Features) +- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (Für jegliche Bugs und Fehler die dir beim Arbeiten mit Infisical auffallen) +- [Twitter](https://twitter.com/infisical) (Bleib immer aktuell!) + +## 🏘 Open-source vs. paid + +Dieses Repository ist MIT lizensiert, mit Ausnahme des `ee` Ordners, der „Premium Enterprise Features“ enthält, welche zukünftig einer Infisical Lizens bedürfen. Wir sind dennoch darauf fokussiert, zuerst „Non-Enterprise“-Lösungen zu entwickeln, welche die meisten Anwendungsfälle abdecken. + +## 🛡 Sicherheit + +Sie möchten eine Sicherheitslücke melden? Bitte verwenden dafür nicht die „Github Issues“. Nutze stattdesen die [SECURITY.md](./SECURITY.md). + +## 🚨 Bleibe immer auf dem Laufenden + +Infisical wurde offiziell unter der Version 1.0 am 21. November 2022 veröffentlicht. Es erscheinen regelmäßig neue Features. Um bei zukünftigen **releases** von diesem Repository benachrichtigt zu werden, klicke auf Watch: + +![infisical-star-github](https://github.com/Infisical/infisical/blob/main/.github/images/star-infisical.gif?raw=true) + +## 🌱 Hilf uns + +Egal wie groß oder klein deine Hilfe ist, wir freuen uns über Deine Beteiligung ❤️ Sieh dir unseren [Contributing-Guide](https://infisical.com/docs/contributing/overview) an. + +Nicht sicher wo du anfangen sollst? Du kannst: + +- [Du kannst eine kostenlose Session mit einem unserer Teammitglieder starten](mailto:tony@infisical.com?subject=Pairing%20session&body=I'd%20like%20to%20do%20a%20pairing%20session!)! +- Unserem Slack-Channel beitreten, und uns dort jegliche Fragen stellen. + +## 🦸 Contributors + +[//]: contributor-faces + + + + + + + +## 🌎 Übersetzungen + +Infisical ist zurzeit in Englisch, Koreanisch, Franzözisch, und Portugiesisch (Brasilien) verfügbar. Hilf uns, Infisical in deine Sprache zu übersetzen! + +Du kannst alle Infos dazu in [diesem Issue](https://github.com/Infisical/infisical/issues/181) finden. diff --git a/i18n/README.en.md b/i18n/README.en.md index 1ec8cff50..92f2388bf 100644 --- a/i18n/README.en.md +++ b/i18n/README.en.md @@ -3,7 +3,7 @@ infisical

-

Open-source, E2EE, simple tool to manage secrets and configs across your team and infrastructure.

+

Open-source, end-to-end encrypted tool to manage secrets and configs across your team, devices, and infrastructure.

@@ -16,7 +16,7 @@

- Medusa is released under the MIT license. + Infisical is released under the MIT license. PRs welcome! @@ -25,7 +25,7 @@ git commit activity - Cloudsmith downloads + Cloudsmith downloads Slack community channel @@ -37,29 +37,32 @@ Dashboard - -**Read this in other languages**: [English language](i18n/README.en.md) +**Read this in other languages**: [English language](i18n/README.en.md) [Spanish language](i18n/README.es.md) +[German language](i18n/README.de.md) [Korean language](i18n/README.ko.md) [Turkish language](i18n/README.tr.md) +[Bahasa Indonesia language](i18n/README.id.md) -**[Infisical](https://infisical.com)** is an open source, E2EE tool to help teams manage and sync secrets and configs across their development workflow and infrastructure. It's designed to be simple and take minutes to get going. +**[Infisical](https://infisical.com)** is an open source, end-to-end encrypted secret manager which you can use to centralize your API keys and configs. From Infisical, you can then distribute these secrets across your whole development lifecycle - from development to production . It's designed to be simple and take minutes to get going. -- **[User-Friendly Dashboard](https://infisical.com/docs/getting-started/dashboard/project)** to manage your team's secrets and configs within projects -- **[Language-Agnostic CLI](https://infisical.com/docs/cli/overview)** that pulls and injects esecrets and configs into your local workflow -- **[Complete control over your data](https://infisical.com/docs/self-hosting/overview)** - host it yourself on any infrastructure -- **Navigate Multiple Environments** per project (e.g. development, staging, production, etc.) -- **Personal overrides** for secrets and configs -- **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure -- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** - manage secrets via HTTPS requests to the platform -- **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** to view the change history for any secret -- **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** to record every action taken in a project -- **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** for rolling back to any snapshot of your secrets -- **Role-based Access Controls** per environment -- 🔜 **2FA** (next week) -- 🔜 **1-Click Deploy** to Digital Ocean and Heroku -- 🔜 **Automatic Secret Rotation** -- 🔜 **Slack & MS Teams** integrations +- **[User-Friendly Dashboard](https://infisical.com/docs/getting-started/dashboard/project)** to manage your team's secrets and configs within projects +- **[Language-Agnostic CLI](https://infisical.com/docs/cli/overview)** that pulls and injects esecrets and configs into your local workflow +- **[Complete control over your data](https://infisical.com/docs/self-hosting/overview)** - host it yourself on any infrastructure +- **Navigate Multiple Environments** per project (e.g. development, staging, production, etc.) +- **Personal overrides** for secrets and configs +- **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure +- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** - manage secrets via HTTPS requests to the platform +- **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** to view the change history for any secret +- **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** to record every action taken in a project +- **[Point-in-time Secrets Recovery](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** for rolling back to any snapshot of your secrets +- **Role-based Access Controls** per environment +- **2FA** (more options coming soon) +- 🔜 **1-Click Deploy** to AWS +- 🔜 **Automatic Secret Rotation** +- 🔜 **Smart Security Alerts** +- 🔜 **Secrets Rotation** +- 🔜 **Slack & MS Teams** integrations And more. @@ -68,9 +71,8 @@ And more. To quickly get started, visit our [get started guide](https://infisical.com/docs/getting-started/introduction).

- - - + +

## 🔥 What's cool about this? @@ -83,29 +85,7 @@ If you care about efficiency and security, then Infisical is right for you. We are currently working hard to make Infisical more extensive. Need any integrations or want a new feature? Feel free to [create an issue](https://github.com/Infisical/infisical/issues) or [contribute](https://infisical.com/docs/contributing/overview) directly to the repository. -## 🌱 Contributing - -Whether it's big or small, we love contributions ❤️ Check out our guide to see how to [get started](https://infisical.com/docs/contributing/overview). - -Not sure where to get started? You can: - -- [Book a free, non-pressure pairing sessions with one of our teammates](mailto:tony@infisical.com?subject=Pairing%20session&body=I'd%20like%20to%20do%20a%20pairing%20session!)! -- Join our Slack, and ask us any questions there. - -## 💚 Community & Support - -- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (For live discussion with the community and the Infisical team) -- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (For help with building and deeper conversations about features) -- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (For any bugs and errors you encounter using Infisical) -- [Twitter](https://twitter.com/infisical) (Get news fast) - -## 🌄 Status - -- [x] Public Alpha: Anyone can sign up over at [infisical.com](https://infisical.com) but there are some minor kinks. -- [ ] Public Beta: Stable enough for most non-enterprise use-cases. -- [ ] Public: Production-ready. - -## �🔌 Integrations +## 🔌 Integrations We're currently setting the foundation and building [integrations](https://infisical.com/docs/integrations/overview) so secrets can be synced everywhere. Any help is welcome! :) @@ -114,7 +94,7 @@ We're currently setting the foundation and building [integrations](https://infis Platforms Frameworks - + @@ -155,7 +135,7 @@ We're currently setting the foundation and building [integrations](https://infis @@ -331,12 +317,19 @@ We're currently setting the foundation and building [integrations](https://infis
- 🔜 AWS PS (https://github.com/Infisical/infisical/issues/286) + 🔜 Supabase @@ -171,7 +151,9 @@ We're currently setting the foundation and building [integrations](https://infis 🔜 GCP SM (https://github.com/Infisical/infisical/issues/285) - 🔜 GitLab CI/CD (https://github.com/Infisical/infisical/issues/134) + + ✔️ GitLab CI/CD + 🔜 CircleCI (https://github.com/Infisical/infisical/issues/91) @@ -193,8 +175,8 @@ We're currently setting the foundation and building [integrations](https://infis 🔜 TravisCI - - ✔️ Netlify + + ✔️ AWS Secrets Manager @@ -206,7 +188,9 @@ We're currently setting the foundation and building [integrations](https://infis 🔜 Bitbucket - 🔜 Supabase + + ✔️ AWS Parameter Store + @@ -222,7 +206,9 @@ We're currently setting the foundation and building [integrations](https://infis 🔜 Serverless - 🔜 AWS Lambda + + ✔️ Netlify +
- + +## 💚 Community & Support + +- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (For live discussion with the community and the Infisical team) +- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (For help with building and deeper conversations about features) +- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (For any bugs and errors you encounter using Infisical) +- [Twitter](https://twitter.com/infisical) (Get news fast) + ## 🏘 Open-source vs. paid -This repo is entirely MIT licensed, with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license in the future. We're currently focused on developing non-enterprise offerings first that should suit most use-cases. +This repo is entirely MIT licensed, with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license in the future. We're currently focused on developing non-enterprise offerings first that should suit most use cases. ## 🛡 Security @@ -348,6 +341,15 @@ Infisical officially launched as v.1.0 on November 21st, 2022. There are a lot o ![infisical-star-github](https://github.com/Infisical/infisical/blob/main/.github/images/star-infisical.gif?raw=true) +## 🌱 Contributing + +Whether it's big or small, we love contributions ❤️ Check out our guide to see how to [get started](https://infisical.com/docs/contributing/overview). + +Not sure where to get started? You can: + +- [Book a free, non-pressure pairing sessions with one of our teammates](mailto:tony@infisical.com?subject=Pairing%20session&body=I'd%20like%20to%20do%20a%20pairing%20session!)! +- Join our Slack, and ask us any questions there. + ## 🦸 Contributors [//]: contributor-faces @@ -356,7 +358,7 @@ Infisical officially launched as v.1.0 on November 21st, 2022. There are a lot o - + ## 🌎 Translations diff --git a/i18n/README.es.md b/i18n/README.es.md index d579bfa1c..f97f249d7 100644 --- a/i18n/README.es.md +++ b/i18n/README.es.md @@ -16,7 +16,7 @@

- Medusa is released under the MIT license. + Infisical is released under the MIT license. PRs welcome! @@ -105,12 +105,13 @@ Estamos actualmente en Alpha público. Actualmente estamos sentando bases y construyendo [integraciones](https://infisical.com/docs/integrations/overview) para que los secretos se puedan sincronizar en todas partes. ¡Cualquier ayuda es bienvenida! :) + - + - +
Plataformas Marcos
@@ -151,7 +152,7 @@ Actualmente estamos sentando bases y construyendo [integraciones](https://infisi @@ -327,7 +334,7 @@ Actualmente estamos sentando bases y construyendo [integraciones](https://infisi
- 🔜 AWS PS (https://github.com/Infisical/infisical/issues/286) + 🔜 Supabase @@ -167,7 +168,9 @@ Actualmente estamos sentando bases y construyendo [integraciones](https://infisi 🔜 GCP SM (https://github.com/Infisical/infisical/issues/285) - 🔜 GitLab CI/CD (https://github.com/Infisical/infisical/issues/134) + + ✔️ GitLab CI/CD + 🔜 CircleCI (https://github.com/Infisical/infisical/issues/91) @@ -189,8 +192,8 @@ Actualmente estamos sentando bases y construyendo [integraciones](https://infisi 🔜 TravisCI - - ✔️ Netlify + + ✔️ AWS Secrets Manager @@ -202,7 +205,9 @@ Actualmente estamos sentando bases y construyendo [integraciones](https://infisi 🔜 Bitbucket - 🔜 Supabase + + ✔️ AWS Parameter Store + @@ -218,7 +223,9 @@ Actualmente estamos sentando bases y construyendo [integraciones](https://infisi 🔜 Serverless - 🔜 AWS Lambda + + ✔️ Netlify +
## 🏘 Código abierto vs pagado diff --git a/i18n/README.id.md b/i18n/README.id.md new file mode 100644 index 000000000..2744873b2 --- /dev/null +++ b/i18n/README.id.md @@ -0,0 +1,372 @@ +

+ infisical + infisical +

+

+

Sumber terbuka, enkripsi ujung ke ujung, alat sederhana untuk mengelola rahasia dan konfigurasi di seluruh tim dan infrastruktur Anda.

+

+ +

+ Slack | + Infisical Cloud | + Hosting Mandiri | + Dokumentasi | + Website +

+ +

+ + Infisical is released under the MIT license. + + + PRs welcome! + + + git commit activity + + + Cloudsmith downloads + + + Slack community channel + + + Infisical Twitter + +

+ +Dashboard + +**Baca ini dalam bahasa lain**: [English language](i18n/README.en.md) +[Spanish language](i18n/README.es.md) +[German language](i18n/README.de.md) +[Korean language](i18n/README.ko.md) +[Turkish language](i18n/README.tr.md) +[Bahasa Indonesia language](i18n/README.id.md) + +**[Infisical](https://infisical.com)** adalah alat E2EE sumber terbuka untuk membantu tim mengelola dan menyinkronkan rahasia dan konfigurasi di seluruh alur kerja dan infrastruktur pengembangan mereka. Ini dirancang agar sederhana dan membutuhkan beberapa menit untuk memulai. + +- **[Dasbor User-friendly](https://infisical.com/docs/getting-started/dashboard/project)** untuk mengelola rahasia dan konfigurasi tim Anda dalam proyek +- **[CLI Language-Agnostic](https://infisical.com/docs/cli/overview)** yang menarik dan menyuntikkan rahasia dan konfigurasi ke dalam alur kerja lokal Anda +- **[Kontrol penuh atas data Anda](https://infisical.com/docs/self-hosting/overview)** - host sendiri di infrastruktur apa pun +- **Menavigasi Banyak Lingkungan** per proyek (misalnya pengembangan, pementasan, produksi, dll.) +- **Override personal** untuk rahasia dan konfigurasi +- **[Integrasi](https://infisical.com/docs/integrations/overview)** dengan CI/CD dan infrastruktur produksi +- **[API Infisical](https://infisical.com/docs/api-reference/overview/introduction)** - mengelola rahasia melalui permintaan HTTPS ke platform +- **[Versioning Rahasia](https://infisical.com/docs/getting-started/dashboard/versioning)** untuk melihat riwayat perubahan rahasia apa pun +- **[Log Aktivitas](https://infisical.com/docs/getting-started/dashboard/audit-logs)** untuk merekam setiap tindakan yang diambil dalam sebuah proyek +- **[Pemulihan Rahasia Point-in-time](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** untuk memutar kembali ke cuplikan rahasia Anda +- **Kontrol Akses Berbasis Peran** per lingkungan +- **2FA** +- 🔜 **1-Click Deploy** ke Digital Ocean dan Heroku +- 🔜 **Rotasi Rahasia Otomatis** +- 🔜 **Integrasi Slack & MS Teams** + +Dan banyak lagi. + +## 🚀 Memulai + +Untuk memulai dengan cepat, kunjungi [panduan memulai](https://infisical.com/docs/getting-started/introduction) kami. + +

+ + + +

+ +## 🔥 Apa yang keren tentang ini? + +Infisical membuat manajemen rahasia menjadi sederhana dan terenkripsi end-to-end secara default. Kami memiliki misi untuk membuatnya lebih mudah diakses oleh semua developer, bukan hanya tim keamanan. + +Menurut [laporan](https://www.ekransystem.com/en/blog/secrets-management), hanya 10% organisasi yang menggunakan solusi manajemen rahasia meskipun semuanya menggunakan rahasia digital sampai batas tertentu. + +Jika Anda peduli dengan efisiensi dan keamanan, maka Infisical tepat untuk Anda. + +Kami sedang bekerja keras untuk membuat Infisical lebih luas. Butuh integrasi atau ingin fitur baru? Jangan ragu untuk [membuat masalah](https://github.com/Infisical/infisical/issues) atau [berkontribusi](https://infisical.com/docs/contributing/overview) langsung ke repositori. + +## 🌱 Berkontribusi + +Besar atau kecil, kami menyukai kontribusi ❤️ Lihat panduan kami untuk mengetahui cara [memulai](https://infisical.com/docs/contributing/overview). + +Tidak yakin harus mulai dari mana? Kamu bisa: + +- [Pesan sesi berpasangan gratis tanpa tekanan dengan salah satu rekan tim kami](mailto:tony@infisical.com?subject=Pairing%20session&body=I'd%20like%20to%20do%20a%20pairing%20session!)! +- Bergabunglah dengan Slack kami, dan ajukan pertanyaan apa pun kepada kami di sana. + +## 💚 Komunitas & Dukungan + +- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (Untuk diskusi langsung dengan komunitas dan tim Infisical) +- [Diskusi GitHub](https://github.com/Infisical/infisical/discussions) (Untuk bantuan membangun dan percakapan yang lebih dalam tentang fitur) +- [Masalah GitHub](https://github.com/Infisical/infisical-cli/issues) (Untuk bug dan kesalahan apa pun yang Anda temui menggunakan Infisical) +- [Twitter](https://twitter.com/infisical) (Dapatkan berita dengan cepat) + +## 🌄 Status + +- [x] Alfa Publik: Siapa pun dapat mendaftar di [infisical.com](https://infisical.com) tetapi ada beberapa kekusutan kecil. +- [ ] Beta Publik: Cukup stabil untuk sebagian besar kasus penggunaan non-perusahaan. +- [ ] Umum: Siap produksi. + +## �🔌 Integrasi + +Saat ini kami sedang menyiapkan fondasi dan membangun [integrasi](https://infisical.com/docs/integrations/overview) sehingga rahasia dapat disinkronkan di mana saja. Bantuan apa pun diterima! :) + + + + + + + + + + +
Platforms Frameworks
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + ✔️ Docker + + + + ✔️ Docker Compose + + + + ✔️ Heroku + +
+ + ✔️ Vercel + + + + ✔️ Kubernetes + + + + ✔️ Fly.io + +
+ 🔜 Supabase + + + ✔️ GitHub Actions + + + 🔜 Railway (https://github.com/Infisical/infisical/issues/271) +
+ 🔜 GCP SM (https://github.com/Infisical/infisical/issues/285) + + + ✔️ GitLab CI/CD + + + 🔜 CircleCI (https://github.com/Infisical/infisical/issues/91) +
+ 🔜 Jenkins + + 🔜 Digital Ocean + + 🔜 Azure +
+ 🔜 TravisCI + + + ✔️ AWS Secrets Manager + + + 🔜 Forge +
+ 🔜 Bitbucket + + + ✔️ AWS Parameter Store + + + + ✔️ Render + +
+ 🔜 BuddyCI + + 🔜 Serverless + + + ✔️ Netlify + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + ✔️ React + + + + ✔️ Express + +
+ + ✔️ Gatsby + + + + ✔️ Flask + +
+ + ✔️ Django + + + + ✔️ Laravel + +
+ + ✔️ NestJS + + + + ✔️ Remix + +
+ + ✔️ Next.js + + + + ✔️ Vite + +
+ + ✔️ Vue + + + + ✔️ Ruby on Rails + +
+ + ✔️ Fiber + + + + ✔️ Nuxt + +
+ + ✔️ .NET + + + And more... +
+ +
+ +## 🏘 Sumber terbuka vs. berbayar + +Repo ini sepenuhnya berlisensi MIT, dengan pengecualian direktori `ee` yang akan berisi fitur perusahaan premium yang memerlukan lisensi Infisical di masa mendatang. Kami saat ini berfokus pada pengembangan penawaran non-perusahaan terlebih dahulu yang sesuai dengan sebagian besar kasus penggunaan. + +## 🛡 Keamanan + +Ingin melaporkan kerentanan keamanan? Tolong jangan posting tentang itu di masalah GitHub. Sebagai gantinya, lihat file [SECURITY.md](./SECURITY.md) kami. + +## 🚨 Tetap Terkini + +Infisical secara resmi diluncurkan sebagai v.1.0 pada tanggal 21 November 2022. Ada banyak fitur baru yang sangat sering hadir. Tonton **rilis** repositori ini untuk mendapatkan notifikasi tentang pembaruan di masa mendatang: + +![infisical-star-github](https://github.com/Infisical/infisical/blob/main/.github/images/star-infisical.gif?raw=true) + +## 🦸 Kontributor + +[//]: contributor-faces + + + + + + + +## 🌎 Terjemahan + +Infisical saat ini tersedia dalam bahasa Inggris, Korea, Prancis, Portugis (Brasil), dan Bahasa Indonesia. Bantu kami menerjemahkan Infisical ke bahasa Anda! + +Anda dapat menemukan semua info di [masalah ini](https://github.com/Infisical/infisical/issues/181). diff --git a/i18n/README.ko.md b/i18n/README.ko.md index bab9e4d49..e5471ba9c 100644 --- a/i18n/README.ko.md +++ b/i18n/README.ko.md @@ -49,10 +49,10 @@ - **[시크릿 버전 기록](https://infisical.com/docs/getting-started/dashboard/versioning)** 을 통해 어떤 시크릿이든 버전 기록을 볼 수 있어요 - **[활동 로그](https://infisical.com/docs/getting-started/dashboard/audit-logs)** 를 통해 프로젝트의 모든 활동을 볼 수 있어요 - **[지정 시점으로 시크릿 복구](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** 를 사용해 특정한 시점으로 시크릿을 복구할 수 있어요 +- **2단계 인증** +- **프로젝트 별 인증** (read/write 컨트롤도 곧 찾아옵니다) - 🔜 Digital Ocean 및 Heroku로 **원클릭 배포** -- 🔜 **프로젝트 별 인증** (read/write 컨트롤도 곧 찾아옵니다) - 🔜 **자동 시크릿 로테이션** -- 🔜 **2단계 인증** - 🔜 **Slack & MS Teams** 연동 그 외에 더 많은 기능들이 있어요. diff --git a/i18n/README.pt-br.md b/i18n/README.pt-br.md new file mode 100644 index 000000000..08bc9a8c3 --- /dev/null +++ b/i18n/README.pt-br.md @@ -0,0 +1,368 @@ +

+ infisical + infisical +

+

+

Uma ferramenta de código aberto para encriptação ponta-a-ponta para gerenciar suas chaves e configurações secretas de sua equipe, dispositivos e infraestrutura

+

+ +

+ Slack | + Infisical Cloud | + Self-Hosting | + Docs | + Website +

+ +

+ + Infisical is released under the MIT license. + + + PRs welcome! + + + git commit activity + + + Cloudsmith downloads + + + Slack community channel + + + Infisical Twitter + +

+ +Dashboard + +**Leia essa documentação em outra linguagem**: [Inglês](i18n/README.en.md) +[Espanhol](i18n/README.es.md) +[German language](i18n/README.de.md) +[Coreano](i18n/README.ko.md) +[Turco](i18n/README.tr.md) +[Bahasa Indonesia](i18n/README.id.md) +[Português - Brasil](i18n/README.id.md) + +**[Infisical](https://infisical.com)** é uma ferramenta de código aberto para gerenciamento de segredos encriptados ponta-a-ponta que você pode usar para centralizar suas chaves e configurações. A partir do Infisical, você pode distribuir esses segredos em todo o seu ciclo de vida de desenvolvimento - desde o desenvolvimento até a produção. Ele foi projetado para ser simples e levar minutos para começar. + +- **[Dashboard amigável](https://infisical.com/docs/getting-started/dashboard/project)** para gerenciar os segredos e configurações de sua equipe dentro de projetos +- **[CLI Fléxivel](https://infisical.com/docs/cli/overview)** puxa e injeta segredos e configurações em seu fluxo de trabalho local +- **[Controle completo de seus dados](https://infisical.com/docs/self-hosting/overview)** - hospede-o você mesmo em qualquer infraestrutura +- **Multiplos ambientes** por projeto (ex. desenvolvimento, testes, produção, etc.) +- **Substituições pessoais** para os segredos e configurações +- **[Integrações](https://infisical.com/docs/integrations/overview)** com CI/CD e infraestrutura de produção +- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** - gerencie segredos via solicitações HTTPS para a plataforma +- **[Versionamento dos seus segredos](https://infisical.com/docs/getting-started/dashboard/versioning)** para visualizar o histórico de alterações de qualquer segredo +- **[Logs de atividade](https://infisical.com/docs/getting-started/dashboard/audit-logs)** para registrar todas as ações realizadas em um projeto +- **[Recuperações dos secretos em ponto específico](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** para voltar para qualquer momento do histórico de versões de seus segredos +- **Controle de acesso baseada em regras** por ambiente +- **2FA** (mais opções em breve) +- 🔜 **Deploy em 1 clique** para AWS +- 🔜 **Rotação automática de segredos** +- 🔜 **Alertas de segurança inteligentes** +- 🔜 **Rotação de segredos** +- 🔜 Integração com **Slack e MS Teams** + +E muito mais! + +## 🚀 Começando + +para começar rapidamente visite [nosso guia inicial](https://infisical.com/docs/getting-started/introduction). + +

+ + +

+ +## 🔥 O que é legal nisso tudo? + +Infisical torna a gestão de segredos muito mais simples e de ponta-a-ponta criptografadas por padrão. Estamos em uma missão para torná-lo o mais acessível a todos os desenvolvedores, não somente a equipes de segurança. + +De acordo com esse [relátorio](https://www.ekransystem.com/en/blog/secrets-management), apenas 10% das organizações usam soluções de gerenciamento de segredos, apesar de todas usarem segredos digitais em algum grau. + +Se você se importa com eficiência e segurança, então Infisical é para você. + +Atualmente estamos trabalhando muito duro para tornar o Infisical mais extensivel. Você precisa de alguma integração ou deseja um novo recurso? Sinta-se livre para [criar uma issue](https://github.com/Infisical/infisical/issues) ou [contribuir](https://infisical.com/docs/contributing/overview) diretamente ao nosso repositório. + +## 🔌 Integrações + +Atualmente estamos definindo a base e a construção de [integrações](https://infisical.com/docs/integrations/overview) para que os segredos possam ser sincronizados em todos os lugares. Qualquer ajuda é muito bem-vinda! :) + + + + + + + + + + +
Plataforma Frameworks
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + ✔️ Docker + + + + ✔️ Docker Compose + + + + ✔️ Heroku + +
+ + ✔️ Vercel + + + + ✔️ Kubernetes + + + + ✔️ Fly.io + +
+ 🔜 Supabase + + + ✔️ GitHub Actions + + + 🔜 Railway (https://github.com/Infisical/infisical/issues/271) +
+ 🔜 GCP SM (https://github.com/Infisical/infisical/issues/285) + + + ✔️ GitLab CI/CD + + + 🔜 CircleCI (https://github.com/Infisical/infisical/issues/91) +
+ 🔜 Jenkins + + 🔜 Digital Ocean + + 🔜 Azure +
+ 🔜 TravisCI + + + ✔️ AWS Secrets Manager + + + 🔜 Forge +
+ 🔜 Bitbucket + + + ✔️ AWS Parameter Store + + + + ✔️ Render + +
+ 🔜 BuddyCI + + 🔜 Serverless + + + ✔️ Netlify + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + ✔️ React + + + + ✔️ Express + +
+ + ✔️ Gatsby + + + + ✔️ Flask + +
+ + ✔️ Django + + + + ✔️ Laravel + +
+ + ✔️ NestJS + + + + ✔️ Remix + +
+ + ✔️ Next.js + + + + ✔️ Vite + +
+ + ✔️ Vue + + + + ✔️ Ruby on Rails + +
+ + ✔️ Fiber + + + + ✔️ Nuxt + +
+ + ✔️ .NET + + + E muito mais... +
+ +
+ +## 💚 Comunidade & Suporte + +- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) (Para discusão em tempo real com a comunidade e a equipe do Infisical) +- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) (Para obter ajuda na construção e conversas mais aprofundadas sobre os recursos) +- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) (Para quaisquer bugs e erros que você encontrar usando Infisical) +- [Twitter](https://twitter.com/infisical) (Para receber novidades rapidamente) + +## 🏘 Código aberto vs. pago + +Esse repositório é totalmente licenciado sob a licença MIT, com a exceção da pasta `ee`, que conterá recursos premium de empresa exigindo uma licença Infisical no futuro. Atualmente, estamos focados em desenvolver ofertas não empresariais primeiro que devem atender a maioria dos casos de uso. + +## 🛡 Segurança + +Procurando relatar uma vulnerabilidade de segurança? Por favor, não poste sobre o problema no Github. Em vez disso, consulte nosso arquivo de [SECURITY.md](./SECURITY.md) + +## 🚨 Fique atualizado + +Infisical foi oficialmente lançado como v.1.0 em 21 de novembro de 2022. Existem muitos novos recursos chegando com muita frequência. Assista o **lançamentos** deste repositório para ser notificado sobre futuras atualizações: + +![infisical-star-github](https://github.com/Infisical/infisical/blob/main/.github/images/star-infisical.gif?raw=true) + +## 🌱 Contribuindo + +Seja grande ou pequeno, adoramos contribuições ❤️ Veja nosso guia para ver como [começar](https://infisical.com/docs/contributing/overview). + +Não sabe por onde começar? Você pode: + +- [Agendar gratuitamente uma sessão de alinhamento com um de nossos amigos de equipe](mailto:tony@infisical.com?subject=Pairing%20session&body=I'd%20like%20to%20do%20a%20pairing%20session!)! +- Juntar-se a nós no Slack, e perguntar sobre qualquer coisa. + +## 🦸 Contribuidores + +[//]: contributor-faces + + + + + + + +## 🌎 Traduções + +Infisical é atualmente disponível em inglês, coreano, francês e português (Brasil). Ajude-nos a traduzir Infisical para o seu idioma! + +Você pode procurar mais informações [nessa issue](https://github.com/Infisical/infisical/issues/181). diff --git a/i18n/README.tr.md b/i18n/README.tr.md index bef6aa6b8..263bd9a61 100644 --- a/i18n/README.tr.md +++ b/i18n/README.tr.md @@ -16,7 +16,7 @@

- Medusa is released under the MIT license. + Infisical is released under the MIT license. PRs welcome! @@ -47,10 +47,10 @@ Basit olması için tasarlandı, sadece birkaç dakika içerisinde harekete geç - **[Sırlar İçin Versiyon Kontrol](https://infisical.com/docs/getting-started/dashboard/versioning)** - herhangi bir sır için değişiklik geçmişini görüntüleyin. - **[Aktivite Günlükleri](https://infisical.com/docs/getting-started/dashboard/audit-logs)** - projedeki tüm değişikleri kayıt altına almak için. - **[Anında Geri Yükleme](https://infisical.com/docs/getting-started/dashboard/pit-recovery)** - sırlarınızın herhangi bir snapshotına geri yükleme yapmanız için. +- **2 Faktör Kimlik Doğrulama** - 🔜 **Tek tıkla Deploy** edin, Digital Ocean ve Heroku'ya. - 🔜 **Kimlik Doğrulama/Yetkilendirme** projeleriniz için okuma/yazma kontrolleri (pek yakında) - 🔜 **Otomatik Sır Rotasyonu** -- 🔜 **2 Faktör Kimlik Doğrulama** - 🔜 **Erişim Günlükleri** - 🔜 **Slack & MS Teams** entegrasyonları @@ -109,7 +109,7 @@ Nereden başlayacağınızdan emin değil misiniz? O zaman; Platforms Frameworks - + @@ -150,7 +150,7 @@ Nereden başlayacağınızdan emin değil misiniz? O zaman; @@ -326,7 +332,7 @@ Nereden başlayacağınızdan emin değil misiniz? O zaman;
- 🔜 AWS PS (https://github.com/Infisical/infisical/issues/286) + 🔜 Supabase @@ -166,7 +166,9 @@ Nereden başlayacağınızdan emin değil misiniz? O zaman; 🔜 GCP SM (https://github.com/Infisical/infisical/issues/285) - 🔜 GitLab CI/CD (https://github.com/Infisical/infisical/issues/134) + + ✔️ GitLab CI/CD + 🔜 CircleCI (https://github.com/Infisical/infisical/issues/91) @@ -188,8 +190,8 @@ Nereden başlayacağınızdan emin değil misiniz? O zaman; 🔜 TravisCI - - ✔️ Netlify + + ✔️ AWS Secrets Manager @@ -201,7 +203,9 @@ Nereden başlayacağınızdan emin değil misiniz? O zaman; 🔜 Bitbucket - 🔜 Supabase + + ✔️ AWS Parameter Store + @@ -217,7 +221,9 @@ Nereden başlayacağınızdan emin değil misiniz? O zaman; 🔜 Serverless - 🔜 AWS Lambda + + ✔️ Netlify +
- + ## 🏘 Açık kaynak mı yoksa ücretlimi diff --git a/img/infisical_github_repo.png b/img/infisical_github_repo.png index a9bde978e..581af330e 100644 Binary files a/img/infisical_github_repo.png and b/img/infisical_github_repo.png differ