From 3e102fee3d40dec8ea5e8c4df395686261083ba3 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Fri, 6 Jan 2023 11:40:21 -0300 Subject: [PATCH 01/89] chore: add language locale --- frontend/next-i18next.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/next-i18next.config.js b/frontend/next-i18next.config.js index 5de9c97bc7..f95f23c892 100644 --- a/frontend/next-i18next.config.js +++ b/frontend/next-i18next.config.js @@ -8,7 +8,7 @@ module.exports = { debug: process.env.NODE_ENV === "development", i18n: { defaultLocale: "en", - locales: ["en", "ko"], + locales: ["en", "ko", "pt-BR"], }, fallbackLng: { default: ["en"], From e6c631586a2c0bfcf2d95b5983a4b89a5c3fb7f1 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Fri, 6 Jan 2023 11:40:51 -0300 Subject: [PATCH 02/89] refactor: add "pt-BR" option --- frontend/pages/login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index e72aa3b256..9addfb4cf1 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -160,7 +160,7 @@ export default function Login() { From c0d7b4ea88318e12f5fdd9868035b594d6eff2db Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Fri, 6 Jan 2023 11:41:32 -0300 Subject: [PATCH 03/89] feat: add "boilerplate" json --- frontend/public/locales/pt-br/activity.json | 8 +++++ frontend/public/locales/pt-br/billing.json | 28 +++++++++++++++++ frontend/public/locales/pt-br/common.json | 26 ++++++++++++++++ frontend/public/locales/pt-br/dashboard.json | 30 +++++++++++++++++++ .../public/locales/pt-br/integrations.json | 16 ++++++++++ frontend/public/locales/pt-br/login.json | 8 +++++ frontend/public/locales/pt-br/nav.json | 22 ++++++++++++++ .../locales/pt-br/section-incident.json | 11 +++++++ .../public/locales/pt-br/section-members.json | 14 +++++++++ .../locales/pt-br/section-password.json | 11 +++++++ .../public/locales/pt-br/section-token.json | 13 ++++++++ .../locales/pt-br/settings-members.json | 4 +++ .../public/locales/pt-br/settings-org.json | 4 +++ .../locales/pt-br/settings-personal.json | 11 +++++++ .../locales/pt-br/settings-project.json | 13 ++++++++ frontend/public/locales/pt-br/signup.json | 21 +++++++++++++ 16 files changed, 240 insertions(+) create mode 100644 frontend/public/locales/pt-br/activity.json create mode 100644 frontend/public/locales/pt-br/billing.json create mode 100644 frontend/public/locales/pt-br/common.json create mode 100644 frontend/public/locales/pt-br/dashboard.json create mode 100644 frontend/public/locales/pt-br/integrations.json create mode 100644 frontend/public/locales/pt-br/login.json create mode 100644 frontend/public/locales/pt-br/nav.json create mode 100644 frontend/public/locales/pt-br/section-incident.json create mode 100644 frontend/public/locales/pt-br/section-members.json create mode 100644 frontend/public/locales/pt-br/section-password.json create mode 100644 frontend/public/locales/pt-br/section-token.json create mode 100644 frontend/public/locales/pt-br/settings-members.json create mode 100644 frontend/public/locales/pt-br/settings-org.json create mode 100644 frontend/public/locales/pt-br/settings-personal.json create mode 100644 frontend/public/locales/pt-br/settings-project.json create mode 100644 frontend/public/locales/pt-br/signup.json diff --git a/frontend/public/locales/pt-br/activity.json b/frontend/public/locales/pt-br/activity.json new file mode 100644 index 0000000000..dfc5821aeb --- /dev/null +++ b/frontend/public/locales/pt-br/activity.json @@ -0,0 +1,8 @@ +{ + "event": { + "readSecrets": "Segredos Visualizados", + "updateSecrets": "Segredos Atualizados", + "addSecrets": "Segredos Adicionados", + "deleteSecrets": "Segredos Excluídos" + } +} \ No newline at end of file diff --git a/frontend/public/locales/pt-br/billing.json b/frontend/public/locales/pt-br/billing.json new file mode 100644 index 0000000000..92c963cfc2 --- /dev/null +++ b/frontend/public/locales/pt-br/billing.json @@ -0,0 +1,28 @@ +{ + "title": "Uso & Faturamento", + "description": "Visualize e gerencie a assinatura da sua organização aqui", + "subscription": "Inscrição", + "starter": { + "name": "Iniciante", + "price-explanation": "up to 5 team members", + "text": "Manage any project with 5 members for free!", + "subtext": "$5 per member/month afterwards." + }, + "professional": { + "name": "Professional", + "price-explanation": "/member/month", + "subtext": "Includes unlimited projects & members.", + "text": "Keep up with key management as you grow." + }, + "enterprise": { + "name": "Enterprise", + "text": "Keep up with key management as you grow." + }, + "current-usage": "Current Usage", + "free": "Free", + "downgrade": "Downgrade", + "upgrade": "Upgrade", + "learn-more": "Learn More", + "custom-pricing": "Custom Pricing", + "schedule-demo": "Schedule a Demo" +} \ No newline at end of file diff --git a/frontend/public/locales/pt-br/common.json b/frontend/public/locales/pt-br/common.json new file mode 100644 index 0000000000..badb4e8ad7 --- /dev/null +++ b/frontend/public/locales/pt-br/common.json @@ -0,0 +1,26 @@ +{ + "head-title": "{{title}} | Infiscal", + "error_project-already-exists": "A project with this name already exists.", + "no-mobile": " To use Infisical, please log in through a device with larger dimensions. ", + "email": "Email", + "password": "Password", + "first-name": "First Name", + "last-name": "Last Name", + "logout": "Log Out", + "validate-required": "Please input your {{name}}", + "maintenance-alert": "We are experiencing minor technical difficulties. We are working on solving it right now. Please come back in a few minutes.", + "click-to-copy": "Click to copy", + "project-id": "Project ID", + "save-changes": "Save Changes", + "saved": "Saved", + "drop-zone": "Drag and drop your .env file here.", + "drop-zone-keys": "Drag and drop your .env file here to add more keys.", + "role": "Role", + "role_admin": "admin", + "display-name": "Display Name", + "environment": "Environment", + "expired-in": "Expires in", + "language": "Language", + "search": "Search...", + "note": "Note" +} diff --git a/frontend/public/locales/pt-br/dashboard.json b/frontend/public/locales/pt-br/dashboard.json new file mode 100644 index 0000000000..b64fdbd64d --- /dev/null +++ b/frontend/public/locales/pt-br/dashboard.json @@ -0,0 +1,30 @@ +{ + "title": "Secrets", + "og-title": "Manage your .env files in seconds", + "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files.", + "search-keys": "Search keys...", + "add-key": "Add Key", + "personal": "Personal", + "personal-description": "Personal keys are only visible to you", + "shared": "Shared", + "shared-description": "Shared keys are visible to your whole team", + "make-shared": "Make Shared", + "make-personal": "Make Personal", + "check-docs": { + "button": "Check Docs", + "title": "Good job!", + "line1": "Congrats on adding more secrets.", + "line2": "Here is how to connect them to your codebase." + }, + "sidebar": { + "secret": "Secret", + "key": "Key", + "value": "Value", + "override": "Override value with a personal value", + "version-history": "Version History", + "comments": "Comments & Notes", + "personal-explanation": "This secret is personal. It is not shared with any of your teammates.", + "generate-random-hex": "Generate Random Hex", + "digits": "digits" + } +} diff --git a/frontend/public/locales/pt-br/integrations.json b/frontend/public/locales/pt-br/integrations.json new file mode 100644 index 0000000000..b72f9d87b2 --- /dev/null +++ b/frontend/public/locales/pt-br/integrations.json @@ -0,0 +1,16 @@ +{ + "title": "Project Integrations", + "description": "Manage your integrations of Infisical with third-party services.", + "no-integrations1": "You don't have any integrations set up yet. When you do, they will appear here.", + "no-integrations2": "To start, click on any of the options below. It takes 5 clicks to set up.", + "available": "Platform & Cloud Integrations", + "available-text1": "Click on the itegration you want to connect. This will let your environment variables flow automatically into selected third-party services.", + "available-text2": "Note: during an integration with Heroku, for security reasons, it is impossible to maintain end-to-end encryption. In theory, this lets Infisical decrypt yor environment variables. In practice, we can assure you that this will never be done, and it allows us to protect your secrets from bad actors online. The core Infisical service will always stay end-to-end encrypted. With any questions, reach out support@infisical.com.", + "cloud-integrations": "Cloud Integrations", + "framework-integrations": "Framework Integrations", + "click-to-start": "Click on an integration to begin syncing secrets to it.", + "click-to-setup": "Click on a framework to get the setup instructions.", + "grant-access-to-secrets": "Grant Infisical access to your secrets", + "why-infisical-needs-access": "Most cloud integrations require Infisical to be able to decrypt your secrets so they can be forwarded over.", + "grant-access-button": "Grant access" +} diff --git a/frontend/public/locales/pt-br/login.json b/frontend/public/locales/pt-br/login.json new file mode 100644 index 0000000000..ee097fd920 --- /dev/null +++ b/frontend/public/locales/pt-br/login.json @@ -0,0 +1,8 @@ +{ + "title": "Login", + "og-title": "Log In to Infisical", + "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files.", + "login": "Log In", + "need-account": "Need an Infisical account?", + "create-account": "Create an account" +} diff --git a/frontend/public/locales/pt-br/nav.json b/frontend/public/locales/pt-br/nav.json new file mode 100644 index 0000000000..de52186193 --- /dev/null +++ b/frontend/public/locales/pt-br/nav.json @@ -0,0 +1,22 @@ +{ + "support": { + "slack": "[NEW] Join Slack Forum", + "docs": "Read Docs", + "issue": "Open a Github Issue", + "email": "Send us an Email" + }, + "user": { + "signed-in-as": "SIGNED IN AS", + "current-organization": "CURRENT ORGANIZATION", + "usage-billing": "Usage & Billing", + "invite": "Invite Members", + "other-organizations": "OTHER ORGANIZATION" + }, + "menu": { + "project": "PROJECT", + "secrets": "Secrets", + "members": "Members", + "integrations": "Integrations", + "project-settings": "Project Settings" + } +} diff --git a/frontend/public/locales/pt-br/section-incident.json b/frontend/public/locales/pt-br/section-incident.json new file mode 100644 index 0000000000..de8f3e7b71 --- /dev/null +++ b/frontend/public/locales/pt-br/section-incident.json @@ -0,0 +1,11 @@ +{ + "incident-contacts": "Incident Contacts", + "incident-contacts-description": "These contacts will be notified in the unlikely event of a severe incident.", + "no-incident-contacts": "No incident contacts found.", + "add-contact": "Add Contact", + "add-dialog": { + "title": "Add an Incident Contact", + "description": "This contact will be notified in the unlikely event of a severe incident.", + "add-incident": "Add Incident Contact" + } +} diff --git a/frontend/public/locales/pt-br/section-members.json b/frontend/public/locales/pt-br/section-members.json new file mode 100644 index 0000000000..89dbd2fea0 --- /dev/null +++ b/frontend/public/locales/pt-br/section-members.json @@ -0,0 +1,14 @@ +{ + "add-member": "Add Member", + "org-members": "Organization Members", + "org-members-description": "Manage members of your organization. These users could afterwards be formed into projects.", + "search-members": "Search members...", + "add-dialog": { + "add-member-to-project": "Add a member to your project", + "already-all-invited": "All the users in your organization are already invited.", + "add-user-org-first": "Add more users to the organization first.", + "user-will-email": "The user will receive an email with the instructions.", + "looking-add": "<0>If you are looking to add users to your org,<1>click here", + "add-user-to-org": "Add Users to Organization" + } +} diff --git a/frontend/public/locales/pt-br/section-password.json b/frontend/public/locales/pt-br/section-password.json new file mode 100644 index 0000000000..9c0410d6cb --- /dev/null +++ b/frontend/public/locales/pt-br/section-password.json @@ -0,0 +1,11 @@ +{ + "password": "Password", + "change": "Change password", + "current": "Current password", + "current-wrong": "The current password may be wrong", + "new": "New password", + "validate-base": "Password should contain at least:", + "validate-length": "14 characters", + "validate-case": "1 lowercase character", + "validate-number": "1 number" +} diff --git a/frontend/public/locales/pt-br/section-token.json b/frontend/public/locales/pt-br/section-token.json new file mode 100644 index 0000000000..d12bd202c1 --- /dev/null +++ b/frontend/public/locales/pt-br/section-token.json @@ -0,0 +1,13 @@ +{ + "service-tokens": "Service Tokens", + "service-tokens-description": "Every service token is specific to you, a certain project and a certain environment within this project.", + "add-new": "Add New Token", + "add-dialog": { + "title": "Add a service token for {{target}}", + "description": "Specify the name, environment, and expiry period. When a token is generated, you will only be able to see it once before it disappears. Make sure to save it somewhere.", + "name": "Service Token Name", + "add": "Add Service Token", + "copy-service-token": "Copy your service token", + "copy-service-token-description": "Once you close this popup, you will never see your service token again" + } +} diff --git a/frontend/public/locales/pt-br/settings-members.json b/frontend/public/locales/pt-br/settings-members.json new file mode 100644 index 0000000000..91f4686e70 --- /dev/null +++ b/frontend/public/locales/pt-br/settings-members.json @@ -0,0 +1,4 @@ +{ + "title": "Project Members", + "description": "This pages shows the members of the selected project." +} diff --git a/frontend/public/locales/pt-br/settings-org.json b/frontend/public/locales/pt-br/settings-org.json new file mode 100644 index 0000000000..d17baa7cd7 --- /dev/null +++ b/frontend/public/locales/pt-br/settings-org.json @@ -0,0 +1,4 @@ +{ + "title": "Organization Settings", + "description": "Manage members of your organization. These users could afterwards be formed into projects." +} diff --git a/frontend/public/locales/pt-br/settings-personal.json b/frontend/public/locales/pt-br/settings-personal.json new file mode 100644 index 0000000000..8759c20f07 --- /dev/null +++ b/frontend/public/locales/pt-br/settings-personal.json @@ -0,0 +1,11 @@ +{ + "title": "Personal Settings", + "description": "View and manage your personal information here.", + "emergency": { + "name": "Emergency Kit", + "text1": "Your Emergency Kit contains the information you’ll need to sign in to your Infisical account.", + "text2": "Only the latest issued Emergency Kit remains valid. To get a new Emergency Kit, verify your password.", + "download": "Download Emergency Kit" + }, + "change-language": "Change Language" +} diff --git a/frontend/public/locales/pt-br/settings-project.json b/frontend/public/locales/pt-br/settings-project.json new file mode 100644 index 0000000000..03e4b14594 --- /dev/null +++ b/frontend/public/locales/pt-br/settings-project.json @@ -0,0 +1,13 @@ +{ + "title": "Project Settings", + "description": "These settings only apply to the currently selected Project.", + "danger-zone": "Danger Zone", + "delete-project": "Delete Project", + "project-to-delete": "Project to be Deleted", + "danger-zone-note": "As soon as you delete this project, you will not be able to undo it. This will immediately remove all the keys. If you still want to do that, please enter the name of the project below.", + "delete-project-note": "Note: You can only delete a project in case you have more than one", + "project-id-description": "To integrate Infisical into your code base and get automatic injection of environmental variables, you should use the following Project ID.", + "project-id-description2": "For more guidance, including code snipets for various languages and frameworks, see ", + "auto-generated": "This is your project's auto-generated unique identifier. It can't be changed.", + "docs": "Infisical Docs" +} diff --git a/frontend/public/locales/pt-br/signup.json b/frontend/public/locales/pt-br/signup.json new file mode 100644 index 0000000000..509520d56f --- /dev/null +++ b/frontend/public/locales/pt-br/signup.json @@ -0,0 +1,21 @@ +{ + "title": "Sign Up", + "og-title": "Replace .env files with 1 line of code. Sign Up for Infisical in 3 minutes.", + "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage API-keys and environemntal variables. Works with Node.js, Next.js, Gatsby, Nest.js...", + "signup": "Sign Up", + "already-have-account": "Have an account? Log in", + "forgot-password": "Forgot your password?", + "verify": "Verify", + "step1-start": "Let's get started", + "step1-privacy": "By creating an account, you agree to our Terms and have read and acknowledged the Privacy Policy.", + "step1-submit": "Get Started", + "step2-message": "We've sent a verification email to{{email}}", + "step2-code-error": "Oops. Your code is wrong. Please try again.", + "step2-spam-alert": "Make sure to check your spam inbox.", + "step3-message": "Almost there!", + "step4-message": "Save your Emergency Kit", + "step4-description1": "If you get locked out of your account, your Emergency Kit is the only way to sign in.", + "step4-description2": "We recommend you download it and keep it somewhere safe.", + "step4-description3": "It contains your Secret Key which we cannot access or recover for you if you lose it.", + "step4-download": "Download PDF" +} From 4c32f3dfd0b3bbdabda87605ce29f839e3a9cee8 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Fri, 6 Jan 2023 12:08:20 -0300 Subject: [PATCH 04/89] refactor: adding translate pt-br billing archive --- frontend/public/locales/pt-br/billing.json | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/public/locales/pt-br/billing.json b/frontend/public/locales/pt-br/billing.json index 92c963cfc2..1c920c053a 100644 --- a/frontend/public/locales/pt-br/billing.json +++ b/frontend/public/locales/pt-br/billing.json @@ -4,25 +4,25 @@ "subscription": "Inscrição", "starter": { "name": "Iniciante", - "price-explanation": "up to 5 team members", - "text": "Manage any project with 5 members for free!", - "subtext": "$5 per member/month afterwards." + "price-explanation": "Até 5 membros da equipe", + "text": "Gerencie qualquer projeto com 5 membros gratuitamente!", + "subtext": "$5 por membro / mês depois." }, "professional": { - "name": "Professional", - "price-explanation": "/member/month", - "subtext": "Includes unlimited projects & members.", - "text": "Keep up with key management as you grow." + "name": "Profissional", + "price-explanation": "/membro/mês", + "subtext": "Inclui projetos e membros ilimitados.", + "text": "Acompanhe o gerenciamento de chaves à medida que você cresce." }, "enterprise": { - "name": "Enterprise", - "text": "Keep up with key management as you grow." + "name": "Empreendimento", + "text": "Acompanhe o gerenciamento de chaves à medida que você cresce." }, - "current-usage": "Current Usage", - "free": "Free", - "downgrade": "Downgrade", - "upgrade": "Upgrade", - "learn-more": "Learn More", - "custom-pricing": "Custom Pricing", - "schedule-demo": "Schedule a Demo" + "current-usage": "Uso atual", + "free": "Grátis", + "downgrade": "Reduzir", + "upgrade": "Melhoria", + "learn-more": "Saber Mais", + "custom-pricing": "Preço Personalizado", + "schedule-demo": "Agende uma Demonstração" } \ No newline at end of file From 15553e972a458f74c62fc0db5b26e534297a830b Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sat, 7 Jan 2023 12:09:43 -0300 Subject: [PATCH 05/89] refactor: adding translate pt-br in common.json archive --- frontend/public/locales/pt-br/common.json | 40 +++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/frontend/public/locales/pt-br/common.json b/frontend/public/locales/pt-br/common.json index badb4e8ad7..12e657d7cc 100644 --- a/frontend/public/locales/pt-br/common.json +++ b/frontend/public/locales/pt-br/common.json @@ -1,26 +1,26 @@ { "head-title": "{{title}} | Infiscal", - "error_project-already-exists": "A project with this name already exists.", - "no-mobile": " To use Infisical, please log in through a device with larger dimensions. ", + "error_project-already-exists": "Já exite um projeto com este nome.", + "no-mobile": "Para usar o Infisical, faça o login através de um dispositivo com dimensões maiores.", "email": "Email", - "password": "Password", - "first-name": "First Name", - "last-name": "Last Name", - "logout": "Log Out", - "validate-required": "Please input your {{name}}", - "maintenance-alert": "We are experiencing minor technical difficulties. We are working on solving it right now. Please come back in a few minutes.", - "click-to-copy": "Click to copy", - "project-id": "Project ID", - "save-changes": "Save Changes", - "saved": "Saved", - "drop-zone": "Drag and drop your .env file here.", - "drop-zone-keys": "Drag and drop your .env file here to add more keys.", + "password": "Senha", + "first-name": "Primeiro Nome", + "last-name": "Ultimo Nome", + "logout": "Sair", + "validate-required": "Por favor insira o seu {{name}}", + "maintenance-alert": "Estamos passando por pequenas dificuldades técnicas. Estamos trabalhando para resolvê-lo agora. Por favor, volte em alguns minutos.", + "click-to-copy": "Clique para copiar", + "project-id": "ID do Projeto", + "save-changes": "Salvar Alterações", + "saved": "Salvou", + "drop-zone": "Arraste e solte seu arquivo .env aqui.", + "drop-zone-keys": "Arraste e solte seu arquivo .env aqui para adicionar mais chaves.", "role": "Role", "role_admin": "admin", - "display-name": "Display Name", - "environment": "Environment", - "expired-in": "Expires in", - "language": "Language", - "search": "Search...", + "display-name": "Nome de exibição", + "environment": "Ambiente", + "expired-in": "Expira em", + "language": "Linguagem", + "search": "Procurar...", "note": "Note" -} +} \ No newline at end of file From 3e56fe95d20051602c7f35eb484a45c422b86091 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sat, 7 Jan 2023 13:50:51 -0300 Subject: [PATCH 06/89] refactor: adding translate pt-br in dashboard.json archive --- frontend/public/locales/pt-br/dashboard.json | 50 ++++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/frontend/public/locales/pt-br/dashboard.json b/frontend/public/locales/pt-br/dashboard.json index b64fdbd64d..3edeacc891 100644 --- a/frontend/public/locales/pt-br/dashboard.json +++ b/frontend/public/locales/pt-br/dashboard.json @@ -1,30 +1,30 @@ { - "title": "Secrets", - "og-title": "Manage your .env files in seconds", - "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files.", - "search-keys": "Search keys...", - "add-key": "Add Key", - "personal": "Personal", - "personal-description": "Personal keys are only visible to you", - "shared": "Shared", - "shared-description": "Shared keys are visible to your whole team", - "make-shared": "Make Shared", - "make-personal": "Make Personal", + "title": "Segredos", + "og-title": "Gerencie seus arquivos .env em segundos", + "og-description": "Infisical é uma plataforma simples e criptografada de ponta a ponta que permite que as equipes sincronizem e gerenciem seus arquivos .env.", + "search-keys": "Pesquisar chaves...", + "add-key": "Adicionar Chave", + "personal": "Pessoal", + "personal-description": "As chaves pessoais são visíveis apenas para você", + "shared": "Compartilhado", + "shared-description": "As chaves compartilhadas ficam visíveis para toda a sua equipe", + "make-shared": "Tornar Compartilhado", + "make-personal": "Tornar individual", "check-docs": { - "button": "Check Docs", - "title": "Good job!", - "line1": "Congrats on adding more secrets.", - "line2": "Here is how to connect them to your codebase." + "button": "Checkar Documentação", + "title": "Bom trabalho!!", + "line1": "Parabéns por adicionar mais segredos.", + "line2": "Veja como conectá-los à sua base de código." }, "sidebar": { - "secret": "Secret", - "key": "Key", - "value": "Value", - "override": "Override value with a personal value", - "version-history": "Version History", - "comments": "Comments & Notes", - "personal-explanation": "This secret is personal. It is not shared with any of your teammates.", - "generate-random-hex": "Generate Random Hex", - "digits": "digits" + "secret": "Segredo", + "key": "Chave", + "value": "Valor", + "override": "Substitua o valor por um valor pessoal", + "version-history": "Histórico da versão", + "comments": "Comentários e Notas", + "personal-explanation": "Este segredo é pessoal. Não é compartilhado com nenhum de seus colegas de equipe.", + "generate-random-hex": "Gerar Hex Aleatório", + "digits": "Digitos" } -} +} \ No newline at end of file From 3f1eaa8d42d31f1fd2cb67421f9fd9d3ed33bd00 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sat, 7 Jan 2023 13:51:04 -0300 Subject: [PATCH 07/89] refactor: adding translate pt-br in integrations.json archive --- .../public/locales/pt-br/integrations.json | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/public/locales/pt-br/integrations.json b/frontend/public/locales/pt-br/integrations.json index b72f9d87b2..3e1533582f 100644 --- a/frontend/public/locales/pt-br/integrations.json +++ b/frontend/public/locales/pt-br/integrations.json @@ -1,16 +1,16 @@ { - "title": "Project Integrations", - "description": "Manage your integrations of Infisical with third-party services.", - "no-integrations1": "You don't have any integrations set up yet. When you do, they will appear here.", - "no-integrations2": "To start, click on any of the options below. It takes 5 clicks to set up.", - "available": "Platform & Cloud Integrations", - "available-text1": "Click on the itegration you want to connect. This will let your environment variables flow automatically into selected third-party services.", - "available-text2": "Note: during an integration with Heroku, for security reasons, it is impossible to maintain end-to-end encryption. In theory, this lets Infisical decrypt yor environment variables. In practice, we can assure you that this will never be done, and it allows us to protect your secrets from bad actors online. The core Infisical service will always stay end-to-end encrypted. With any questions, reach out support@infisical.com.", - "cloud-integrations": "Cloud Integrations", - "framework-integrations": "Framework Integrations", - "click-to-start": "Click on an integration to begin syncing secrets to it.", - "click-to-setup": "Click on a framework to get the setup instructions.", - "grant-access-to-secrets": "Grant Infisical access to your secrets", - "why-infisical-needs-access": "Most cloud integrations require Infisical to be able to decrypt your secrets so they can be forwarded over.", - "grant-access-button": "Grant access" -} + "title": "Integrações de Projetos", + "description": "Gerencie suas integrações da Infisical com serviços de terceiros.", + "no-integrations1": "Você ainda não tem integrações configuradas. Quando você fizer isso, eles aparecerão aqui.", + "no-integrations2": "Para começar, clique em qualquer uma das opções abaixo. Leva 5 cliques para configurar.", + "available": "Integrações de Plataforma e Nuvem", + "available-text1": "Clique na integração que deseja conectar. Isso permitirá que suas variáveis de ambiente fluam automaticamente para serviços de terceiros selecionados.", + "available-text2": "Observação: durante uma integração com o Heroku, por questões de segurança, é impossível manter a criptografia de ponta a ponta. Em teoria, isso permite que o Infisical descriptografe suas variáveis de ambiente. Na prática, podemos garantir que isso nunca será feito e nos permite proteger seus segredos de pessoas mal-intencionadas online. O serviço básico da Infisical sempre permanecerá criptografado de ponta a ponta. Em caso de dúvidas, entre em contato com support@infisical.com.", + "cloud-integrations": "Integrações na Nuvem", + "framework-integrations": "Integrações de framework", + "click-to-start": "Clique em uma integração para começar a sincronizar segredos com ela.", + "click-to-setup": "Clique em uma estrutura para obter as instruções de configuração.", + "grant-access-to-secrets": "Conceda acesso Infisical aos seus segredos", + "why-infisical-needs-access": "A maioria das integrações em nuvem exige que o Infisical seja capaz de descriptografar seus segredos para que possam ser encaminhados.", + "grant-access-button": "Garantir acesso" +} \ No newline at end of file From 96b254d7c3e707718b6468d18e302437ab369b4e Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sat, 7 Jan 2023 13:51:16 -0300 Subject: [PATCH 08/89] refactor: adding translate pt-br in login.json archive --- frontend/public/locales/pt-br/login.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/public/locales/pt-br/login.json b/frontend/public/locales/pt-br/login.json index ee097fd920..1798839a0a 100644 --- a/frontend/public/locales/pt-br/login.json +++ b/frontend/public/locales/pt-br/login.json @@ -1,8 +1,8 @@ { "title": "Login", - "og-title": "Log In to Infisical", - "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files.", - "login": "Log In", - "need-account": "Need an Infisical account?", - "create-account": "Create an account" -} + "og-title": "Entrar no Infisical", + "og-description": "Infisical é uma plataforma simples e criptografada de ponta a ponta que permite que as equipes sincronizem e gerenciem seus arquivos .env.", + "login": "Entrar", + "need-account": "Precisa de uma conta Infisical?", + "create-account": "Criar uma conta" +} \ No newline at end of file From 553be71ddfa5d469204716a31ca6561c04581074 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:23:17 -0300 Subject: [PATCH 09/89] refactor: translate pt-br in login.json archive --- frontend/public/locales/pt-br/login.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/locales/pt-br/login.json b/frontend/public/locales/pt-br/login.json index 1798839a0a..9510d1a22d 100644 --- a/frontend/public/locales/pt-br/login.json +++ b/frontend/public/locales/pt-br/login.json @@ -1,5 +1,5 @@ { - "title": "Login", + "title": "Entrar", "og-title": "Entrar no Infisical", "og-description": "Infisical é uma plataforma simples e criptografada de ponta a ponta que permite que as equipes sincronizem e gerenciem seus arquivos .env.", "login": "Entrar", From 2b147fce6eea8a081e48c9d7df2787b8ddbd5c8f Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:23:31 -0300 Subject: [PATCH 10/89] refactor: adding translate pt-br in nav.json archive --- frontend/public/locales/pt-br/nav.json | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/public/locales/pt-br/nav.json b/frontend/public/locales/pt-br/nav.json index de52186193..7922312dde 100644 --- a/frontend/public/locales/pt-br/nav.json +++ b/frontend/public/locales/pt-br/nav.json @@ -1,22 +1,22 @@ { "support": { - "slack": "[NEW] Join Slack Forum", - "docs": "Read Docs", - "issue": "Open a Github Issue", - "email": "Send us an Email" + "slack": "[NEW] Participe do fórum do Slack", + "docs": "Leia a Documentação", + "issue": "Abra uma Issue no Github", + "email": "Envie-nos um e-mail" }, "user": { - "signed-in-as": "SIGNED IN AS", - "current-organization": "CURRENT ORGANIZATION", - "usage-billing": "Usage & Billing", - "invite": "Invite Members", - "other-organizations": "OTHER ORGANIZATION" + "signed-in-as": "ASSINADO COMO", + "current-organization": "ORGANIZAÇÃO ATUAL", + "usage-billing": "Uso & Faturamento", + "invite": "Convide Membros", + "other-organizations": "OUTRA ORGANIZAÇÃO" }, "menu": { - "project": "PROJECT", - "secrets": "Secrets", - "members": "Members", - "integrations": "Integrations", - "project-settings": "Project Settings" + "project": "PROJETO", + "secrets": "Segredos", + "members": "Membros", + "integrations": "Integrações", + "project-settings": "Configurações do Projeto" } -} +} \ No newline at end of file From 556858d1a862ae472694d712d56f62e40559b673 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:24:12 -0300 Subject: [PATCH 11/89] refactor: adding translate pt-br in section-incident.json archive --- .../public/locales/pt-br/section-incident.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/public/locales/pt-br/section-incident.json b/frontend/public/locales/pt-br/section-incident.json index de8f3e7b71..bfa55d2ee0 100644 --- a/frontend/public/locales/pt-br/section-incident.json +++ b/frontend/public/locales/pt-br/section-incident.json @@ -1,11 +1,11 @@ { - "incident-contacts": "Incident Contacts", - "incident-contacts-description": "These contacts will be notified in the unlikely event of a severe incident.", - "no-incident-contacts": "No incident contacts found.", - "add-contact": "Add Contact", + "incident-contacts": "Contatos do Incidente", + "incident-contacts-description": "Esses contatos serão notificados no caso improvável de um incidente grave.", + "no-incident-contacts": "Nenhum contato de incidente encontrado.", + "add-contact": "Adicionar contato", "add-dialog": { - "title": "Add an Incident Contact", - "description": "This contact will be notified in the unlikely event of a severe incident.", - "add-incident": "Add Incident Contact" + "title": "Adicionar um contato de incidente", + "description": "Este contato será notificado no caso improvável de um incidente grave.", + "add-incident": "Adicionar contato de incidente" } -} +} \ No newline at end of file From 8c0046be879a26a6f99040e87807d8fe0ea1eb99 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:24:29 -0300 Subject: [PATCH 12/89] refactor: adding translate pt-br in section-members.json archive --- .../public/locales/pt-br/section-members.json | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/public/locales/pt-br/section-members.json b/frontend/public/locales/pt-br/section-members.json index 89dbd2fea0..67ef10ff0b 100644 --- a/frontend/public/locales/pt-br/section-members.json +++ b/frontend/public/locales/pt-br/section-members.json @@ -1,14 +1,14 @@ { - "add-member": "Add Member", - "org-members": "Organization Members", - "org-members-description": "Manage members of your organization. These users could afterwards be formed into projects.", - "search-members": "Search members...", + "add-member": "Adicionar membro", + "org-members": "Membros da Organização", + "org-members-description": "Gerencie os membros da sua organização. Esses usuários poderiam posteriormente ser formados em projetos.", + "search-members": "Pesquisar membros...", "add-dialog": { - "add-member-to-project": "Add a member to your project", - "already-all-invited": "All the users in your organization are already invited.", - "add-user-org-first": "Add more users to the organization first.", - "user-will-email": "The user will receive an email with the instructions.", - "looking-add": "<0>If you are looking to add users to your org,<1>click here", - "add-user-to-org": "Add Users to Organization" + "add-member-to-project": "Adicionar um membro ao seu projeto", + "already-all-invited": "Todos os usuários da sua organização já foram convidados.", + "add-user-org-first": "Adicione mais usuários à organização primeiro.", + "user-will-email": "O usuário receberá um e-mail com as instruções.", + "looking-add": "<0>Se você deseja adicionar usuários à sua organização,<1>clique aqui", + "add-user-to-org": "Adicionar usuários à organização" } -} +} \ No newline at end of file From f2372bb26535ca32b2f6b566cfe8942453362337 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:24:44 -0300 Subject: [PATCH 13/89] refactor: adding translate pt-br in section-password.json archive --- .../locales/pt-br/section-password.json | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/public/locales/pt-br/section-password.json b/frontend/public/locales/pt-br/section-password.json index 9c0410d6cb..7715817682 100644 --- a/frontend/public/locales/pt-br/section-password.json +++ b/frontend/public/locales/pt-br/section-password.json @@ -1,11 +1,11 @@ { - "password": "Password", - "change": "Change password", - "current": "Current password", - "current-wrong": "The current password may be wrong", - "new": "New password", - "validate-base": "Password should contain at least:", - "validate-length": "14 characters", - "validate-case": "1 lowercase character", - "validate-number": "1 number" -} + "password": "Senha", + "change": "Mudar senha", + "current": "Senha atual", + "current-wrong": "A senha atual pode estar errada", + "new": "Nova Senha", + "validate-base": "A senha deve conter pelo menos:", + "validate-length": "14 caracteres", + "validate-case": "1 caractere minúsculo", + "validate-number": "1 número" +} \ No newline at end of file From a990a5ee7dde6119b917501c7bc654e983574a56 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:24:55 -0300 Subject: [PATCH 14/89] refactor: adding translate pt-br in section-token.json archive --- .../public/locales/pt-br/section-token.json | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/public/locales/pt-br/section-token.json b/frontend/public/locales/pt-br/section-token.json index d12bd202c1..1713f8118d 100644 --- a/frontend/public/locales/pt-br/section-token.json +++ b/frontend/public/locales/pt-br/section-token.json @@ -1,13 +1,13 @@ { - "service-tokens": "Service Tokens", - "service-tokens-description": "Every service token is specific to you, a certain project and a certain environment within this project.", - "add-new": "Add New Token", + "service-tokens": "Tokens de Serviço", + "service-tokens-description": "Cada token de serviço é específico para você, um determinado projeto e um determinado ambiente dentro deste projeto.", + "add-new": "Adicionar novo token", "add-dialog": { - "title": "Add a service token for {{target}}", - "description": "Specify the name, environment, and expiry period. When a token is generated, you will only be able to see it once before it disappears. Make sure to save it somewhere.", - "name": "Service Token Name", - "add": "Add Service Token", - "copy-service-token": "Copy your service token", - "copy-service-token-description": "Once you close this popup, you will never see your service token again" + "title": "Adicione um token de serviço para {{target}}", + "description": "Especifique o nome, o ambiente e o período de expiração. Quando um token é gerado, você só poderá vê-lo uma vez antes que ele desapareça. Certifique-se de salvá-lo em algum lugar.", + "name": "Nome do token de serviço", + "add": "Adicionar token de serviço", + "copy-service-token": "Copie seu token de serviço", + "copy-service-token-description": "Depois de fechar este pop-up, você nunca mais verá seu token de serviço" } -} +} \ No newline at end of file From 36525325fd5bbc60929c6dc027cfefe28990b76e Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:25:16 -0300 Subject: [PATCH 15/89] refactor: adding translate pt-br in setting-members.json archive --- frontend/public/locales/pt-br/settings-members.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/public/locales/pt-br/settings-members.json b/frontend/public/locales/pt-br/settings-members.json index 91f4686e70..f48913547f 100644 --- a/frontend/public/locales/pt-br/settings-members.json +++ b/frontend/public/locales/pt-br/settings-members.json @@ -1,4 +1,4 @@ { - "title": "Project Members", - "description": "This pages shows the members of the selected project." -} + "title": "Membros do Projeto", + "description": "Esta página mostra os membros do projeto selecionado." +} \ No newline at end of file From 2f5186634c7b36154533634ec87d8c2b976ea6da Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:25:27 -0300 Subject: [PATCH 16/89] refactor: adding translate pt-br in setting-org.json archive --- frontend/public/locales/pt-br/settings-org.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/public/locales/pt-br/settings-org.json b/frontend/public/locales/pt-br/settings-org.json index d17baa7cd7..e017c2aee3 100644 --- a/frontend/public/locales/pt-br/settings-org.json +++ b/frontend/public/locales/pt-br/settings-org.json @@ -1,4 +1,4 @@ { - "title": "Organization Settings", - "description": "Manage members of your organization. These users could afterwards be formed into projects." -} + "title": "Configurações da organização", + "description": "Gerencie os membros da sua organização. Esses usuários poderiam posteriormente ser formados em projetos." +} \ No newline at end of file From c0a0252cf5e4bb49a95ba6595dfad29fc960e3c0 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:25:38 -0300 Subject: [PATCH 17/89] refactor: adding translate pt-br in setting-personal.json archive --- .../public/locales/pt-br/settings-personal.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/public/locales/pt-br/settings-personal.json b/frontend/public/locales/pt-br/settings-personal.json index 8759c20f07..9d5dd7f50f 100644 --- a/frontend/public/locales/pt-br/settings-personal.json +++ b/frontend/public/locales/pt-br/settings-personal.json @@ -1,11 +1,11 @@ { - "title": "Personal Settings", - "description": "View and manage your personal information here.", + "title": "Configurações Pessoais", + "description": "Visualize e gerencie suas informações pessoais aqui.", "emergency": { - "name": "Emergency Kit", - "text1": "Your Emergency Kit contains the information you’ll need to sign in to your Infisical account.", - "text2": "Only the latest issued Emergency Kit remains valid. To get a new Emergency Kit, verify your password.", - "download": "Download Emergency Kit" + "name": "Kit de emergência", + "text1": "Seu Kit de Emergência contém as informações necessárias para acessar sua conta Infisical.", + "text2": "Apenas o último kit de emergência emitido permanece válido. Para obter um novo Kit de emergência, verifique sua senha.", + "download": "Baixe o kit de emergência" }, - "change-language": "Change Language" -} + "change-language": "Mudar idioma" +} \ No newline at end of file From d870ecc62ae29525f46c647399a8eab761e08bb2 Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:25:52 -0300 Subject: [PATCH 18/89] refactor: adding translate pt-br in setting-project.json archive --- .../locales/pt-br/settings-project.json | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend/public/locales/pt-br/settings-project.json b/frontend/public/locales/pt-br/settings-project.json index 03e4b14594..f25af8fd43 100644 --- a/frontend/public/locales/pt-br/settings-project.json +++ b/frontend/public/locales/pt-br/settings-project.json @@ -1,13 +1,13 @@ { - "title": "Project Settings", - "description": "These settings only apply to the currently selected Project.", - "danger-zone": "Danger Zone", - "delete-project": "Delete Project", - "project-to-delete": "Project to be Deleted", - "danger-zone-note": "As soon as you delete this project, you will not be able to undo it. This will immediately remove all the keys. If you still want to do that, please enter the name of the project below.", - "delete-project-note": "Note: You can only delete a project in case you have more than one", - "project-id-description": "To integrate Infisical into your code base and get automatic injection of environmental variables, you should use the following Project ID.", - "project-id-description2": "For more guidance, including code snipets for various languages and frameworks, see ", - "auto-generated": "This is your project's auto-generated unique identifier. It can't be changed.", - "docs": "Infisical Docs" -} + "title": "Configurações do Projeto", + "description": "Essas configurações se aplicam apenas ao projeto atualmente selecionado.", + "danger-zone": "Zona de perigo", + "delete-project": "Excluir projeto", + "project-to-delete": "Projeto a ser deletado", + "danger-zone-note": "Assim que você excluir este projeto, não poderá desfazê-lo. Isso removerá imediatamente todas as chaves. Se você ainda quiser fazer isso, digite o nome do projeto abaixo.", + "delete-project-note": "Observação: você só pode excluir um projeto caso tenha mais de um", + "project-id-description": "Para integrar Infisical em sua base de código e obter injeção automática de variáveis ambientais, você deve usar o seguinte ID do projeto.", + "project-id-description2": "Para obter mais orientações, incluindo trechos de código para várias linguagens e estruturas, consulte", + "auto-generated": "Este é o identificador exclusivo gerado automaticamente do seu projeto. Não pode ser alterado.", + "docs": "Documentação do Infisical" +} \ No newline at end of file From 69b819e7c4fb49ef812aca1a43975b94b41bc13c Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 09:26:04 -0300 Subject: [PATCH 19/89] refactor: adding translate pt-br in signup.json archive --- frontend/public/locales/pt-br/signup.json | 40 +++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/frontend/public/locales/pt-br/signup.json b/frontend/public/locales/pt-br/signup.json index 509520d56f..b86ac1eff1 100644 --- a/frontend/public/locales/pt-br/signup.json +++ b/frontend/public/locales/pt-br/signup.json @@ -1,21 +1,21 @@ { - "title": "Sign Up", - "og-title": "Replace .env files with 1 line of code. Sign Up for Infisical in 3 minutes.", - "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage API-keys and environemntal variables. Works with Node.js, Next.js, Gatsby, Nest.js...", - "signup": "Sign Up", - "already-have-account": "Have an account? Log in", - "forgot-password": "Forgot your password?", - "verify": "Verify", - "step1-start": "Let's get started", - "step1-privacy": "By creating an account, you agree to our Terms and have read and acknowledged the Privacy Policy.", - "step1-submit": "Get Started", - "step2-message": "We've sent a verification email to{{email}}", - "step2-code-error": "Oops. Your code is wrong. Please try again.", - "step2-spam-alert": "Make sure to check your spam inbox.", - "step3-message": "Almost there!", - "step4-message": "Save your Emergency Kit", - "step4-description1": "If you get locked out of your account, your Emergency Kit is the only way to sign in.", - "step4-description2": "We recommend you download it and keep it somewhere safe.", - "step4-description3": "It contains your Secret Key which we cannot access or recover for you if you lose it.", - "step4-download": "Download PDF" -} + "title": "Inscrever-se", + "og-title": "Substitua os arquivos .env por 1 linha de código. Cadastre-se no Infisical em 3 minutos.", + "og-description": "Infisical é uma plataforma criptografada de ponta a ponta simples que permite que as equipes sincronizem e gerenciem chaves de API e variáveis ambientais. Funciona com Node.js, Next.js, Gatsby, Nest.js...", + "signup": "Inscrever-se", + "already-have-account": "Possui uma conta? Conecte-se", + "forgot-password": "Esqueceu sua senha?", + "verify": "Verificar", + "step1-start": "Vamos começar", + "step1-privacy": "Ao criar uma conta, você concorda com nossos Termos e leu e reconheceu a Política de Privacidade.", + "step1-submit": "Iniciar", + "step2-message": "Enviamos um e-mail de verificação para{{email}}", + "step2-code-error": "Ops. Seu código está errado. Por favor, tente novamente.", + "step2-spam-alert": "Certifique-se de verificar sua caixa de entrada de spam.", + "step3-message": "Quase lá!", + "step4-message": "Guarde o seu Kit de Emergência", + "step4-description1": "Se sua conta for bloqueada, seu Kit de emergência é a única maneira de fazer login.", + "step4-description2": "Recomendamos que você faça o download e guarde-o em algum lugar seguro.", + "step4-description3": "Ele contém sua chave secreta que não podemos acessar ou recuperar para você se você a perder.", + "step4-download": "Baixar PDF" +} \ No newline at end of file From 0bf8661350436b9540d33cfec8a4d2419d37c47f Mon Sep 17 00:00:00 2001 From: Gabriellopes232 Date: Sun, 8 Jan 2023 16:09:57 -0300 Subject: [PATCH 20/89] fix: pattern folder based on i18next locales --- frontend/public/locales/{pt-br => pt-BR}/activity.json | 0 frontend/public/locales/{pt-br => pt-BR}/billing.json | 0 frontend/public/locales/{pt-br => pt-BR}/common.json | 0 frontend/public/locales/{pt-br => pt-BR}/dashboard.json | 0 frontend/public/locales/{pt-br => pt-BR}/integrations.json | 0 frontend/public/locales/{pt-br => pt-BR}/login.json | 0 frontend/public/locales/{pt-br => pt-BR}/nav.json | 0 frontend/public/locales/{pt-br => pt-BR}/section-incident.json | 0 frontend/public/locales/{pt-br => pt-BR}/section-members.json | 0 frontend/public/locales/{pt-br => pt-BR}/section-password.json | 0 frontend/public/locales/{pt-br => pt-BR}/section-token.json | 0 frontend/public/locales/{pt-br => pt-BR}/settings-members.json | 0 frontend/public/locales/{pt-br => pt-BR}/settings-org.json | 0 frontend/public/locales/{pt-br => pt-BR}/settings-personal.json | 0 frontend/public/locales/{pt-br => pt-BR}/settings-project.json | 0 frontend/public/locales/{pt-br => pt-BR}/signup.json | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename frontend/public/locales/{pt-br => pt-BR}/activity.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/billing.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/common.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/dashboard.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/integrations.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/login.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/nav.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/section-incident.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/section-members.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/section-password.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/section-token.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/settings-members.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/settings-org.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/settings-personal.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/settings-project.json (100%) rename frontend/public/locales/{pt-br => pt-BR}/signup.json (100%) diff --git a/frontend/public/locales/pt-br/activity.json b/frontend/public/locales/pt-BR/activity.json similarity index 100% rename from frontend/public/locales/pt-br/activity.json rename to frontend/public/locales/pt-BR/activity.json diff --git a/frontend/public/locales/pt-br/billing.json b/frontend/public/locales/pt-BR/billing.json similarity index 100% rename from frontend/public/locales/pt-br/billing.json rename to frontend/public/locales/pt-BR/billing.json diff --git a/frontend/public/locales/pt-br/common.json b/frontend/public/locales/pt-BR/common.json similarity index 100% rename from frontend/public/locales/pt-br/common.json rename to frontend/public/locales/pt-BR/common.json diff --git a/frontend/public/locales/pt-br/dashboard.json b/frontend/public/locales/pt-BR/dashboard.json similarity index 100% rename from frontend/public/locales/pt-br/dashboard.json rename to frontend/public/locales/pt-BR/dashboard.json diff --git a/frontend/public/locales/pt-br/integrations.json b/frontend/public/locales/pt-BR/integrations.json similarity index 100% rename from frontend/public/locales/pt-br/integrations.json rename to frontend/public/locales/pt-BR/integrations.json diff --git a/frontend/public/locales/pt-br/login.json b/frontend/public/locales/pt-BR/login.json similarity index 100% rename from frontend/public/locales/pt-br/login.json rename to frontend/public/locales/pt-BR/login.json diff --git a/frontend/public/locales/pt-br/nav.json b/frontend/public/locales/pt-BR/nav.json similarity index 100% rename from frontend/public/locales/pt-br/nav.json rename to frontend/public/locales/pt-BR/nav.json diff --git a/frontend/public/locales/pt-br/section-incident.json b/frontend/public/locales/pt-BR/section-incident.json similarity index 100% rename from frontend/public/locales/pt-br/section-incident.json rename to frontend/public/locales/pt-BR/section-incident.json diff --git a/frontend/public/locales/pt-br/section-members.json b/frontend/public/locales/pt-BR/section-members.json similarity index 100% rename from frontend/public/locales/pt-br/section-members.json rename to frontend/public/locales/pt-BR/section-members.json diff --git a/frontend/public/locales/pt-br/section-password.json b/frontend/public/locales/pt-BR/section-password.json similarity index 100% rename from frontend/public/locales/pt-br/section-password.json rename to frontend/public/locales/pt-BR/section-password.json diff --git a/frontend/public/locales/pt-br/section-token.json b/frontend/public/locales/pt-BR/section-token.json similarity index 100% rename from frontend/public/locales/pt-br/section-token.json rename to frontend/public/locales/pt-BR/section-token.json diff --git a/frontend/public/locales/pt-br/settings-members.json b/frontend/public/locales/pt-BR/settings-members.json similarity index 100% rename from frontend/public/locales/pt-br/settings-members.json rename to frontend/public/locales/pt-BR/settings-members.json diff --git a/frontend/public/locales/pt-br/settings-org.json b/frontend/public/locales/pt-BR/settings-org.json similarity index 100% rename from frontend/public/locales/pt-br/settings-org.json rename to frontend/public/locales/pt-BR/settings-org.json diff --git a/frontend/public/locales/pt-br/settings-personal.json b/frontend/public/locales/pt-BR/settings-personal.json similarity index 100% rename from frontend/public/locales/pt-br/settings-personal.json rename to frontend/public/locales/pt-BR/settings-personal.json diff --git a/frontend/public/locales/pt-br/settings-project.json b/frontend/public/locales/pt-BR/settings-project.json similarity index 100% rename from frontend/public/locales/pt-br/settings-project.json rename to frontend/public/locales/pt-BR/settings-project.json diff --git a/frontend/public/locales/pt-br/signup.json b/frontend/public/locales/pt-BR/signup.json similarity index 100% rename from frontend/public/locales/pt-br/signup.json rename to frontend/public/locales/pt-BR/signup.json From 0c59007fa82443955f7630a433298004a836320a Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:44:23 +0100 Subject: [PATCH 21/89] feat(lang): translated activity.json --- frontend/public/locales/fr/activity.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/public/locales/fr/activity.json diff --git a/frontend/public/locales/fr/activity.json b/frontend/public/locales/fr/activity.json new file mode 100644 index 0000000000..4360d7d0c9 --- /dev/null +++ b/frontend/public/locales/fr/activity.json @@ -0,0 +1,11 @@ +{ + "title": "Journaux d'activité", + "subtitle": "Historique des événements pour ce projet Infisical.", + "event": { + "readSecrets": "Secrets Visualisés", + "updateSecrets": "Secrets Mis à jour", + "addSecrets": "Secrets Ajoutés", + "deleteSecrets": "Secrets Supprimés" + }, + "ip-address": "Adresse IP" +} From 295e93ac170bbd9135759259c50429cccda42a77 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:44:35 +0100 Subject: [PATCH 22/89] feat(lang): translated billing.json --- frontend/public/locales/fr/billing.json | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 frontend/public/locales/fr/billing.json diff --git a/frontend/public/locales/fr/billing.json b/frontend/public/locales/fr/billing.json new file mode 100644 index 0000000000..1acb44339e --- /dev/null +++ b/frontend/public/locales/fr/billing.json @@ -0,0 +1,28 @@ +{ + "title": "Utilisation et Facturation", + "description": "Voir et gérer l'abonnement de votre organisation ici", + "subscription": "Abonnement", + "starter": { + "name": "Starter", + "price-explanation": "jusqu'à 5 membres de l'équipe", + "text": "Gérez n'importe quel projet jusqu'à 5 membres gratuitement!", + "subtext": "$5 par membre / mois par la suite." + }, + "professional": { + "name": "Professionnel", + "price-explanation": "/membre/mois", + "subtext": "Comprend des projets et des membres illimités.", + "text": "Suivez la gestion clé à mesure que vous grandissez." + }, + "enterprise": { + "name": "Entreprise", + "text": "Suivez la gestion clé à mesure que vous grandissez." + }, + "current-usage": "Utilisation actuelle", + "free": "Gratuit", + "downgrade": "Rétrograder", + "upgrade": "Améliorer", + "learn-more": "En savoir plus", + "custom-pricing": "Prix personnalisés", + "schedule-demo": "Planifier une démo" +} From de1b75d99efaaed8911066bf45400855bd0f8cbc Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:44:44 +0100 Subject: [PATCH 23/89] feat(lang): translated common.json --- frontend/public/locales/fr/common.json | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 frontend/public/locales/fr/common.json diff --git a/frontend/public/locales/fr/common.json b/frontend/public/locales/fr/common.json new file mode 100644 index 0000000000..8552668aa9 --- /dev/null +++ b/frontend/public/locales/fr/common.json @@ -0,0 +1,34 @@ +{ + "head-title": "{{title}} | Infiscal", + "error_project-already-exists": "Un projet avec ce nom existe déjà.", + "no-mobile": " Pour utiliser Infisical, veuillez vous connecter avec un appareil avec des dimensions plus grandes. ", + "email": "Email", + "password": "Mot de passe", + "first-name": "Prénom", + "last-name": "Nom", + "logout": "Déconnexion", + "validate-required": "Veuillez saisir votre {{name}}", + "maintenance-alert": "Nous rencontrons des difficultés techniques mineures. Nous travaillons sur leurs résolution dès maintenant. Revenez dans quelques minutes.", + "click-to-copy": "Cliquez pour copiez", + "project-id": "Identifiant du Projet", + "save-changes": "Sauvegarder les modifications", + "saved": "Enregistrée", + "drop-zone": "Glissez et déposez un fichier .env ou .yml ici.", + "drop-zone-keys": "Glissez et déposez un fichier .env ou .yml ici pour ajouter plus de clés.", + "role": "Rôle", + "role_admin": "administrateur", + "display-name": "Nom d'affichage", + "environment": "Environnement", + "expired-in": "Expire dans", + "language": "Langue", + "search": "Recherche...", + "note": "Note", + "view-more": "Voir plus", + "end-of-history": "Fin de l'historique", + "select-event": "Sélectionnez un événement", + "event": "Événement", + "user": "Utilisateur", + "source": "Source", + "time": "Heure", + "timestamp": "Horodatage" +} From 1a90f27d6a15ee83fd1ae872ff3bbd355a3ea38e Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:44:56 +0100 Subject: [PATCH 24/89] feat(lang): translated dashboard.json --- frontend/public/locales/fr/dashboard.json | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 frontend/public/locales/fr/dashboard.json diff --git a/frontend/public/locales/fr/dashboard.json b/frontend/public/locales/fr/dashboard.json new file mode 100644 index 0000000000..f90bf1fc21 --- /dev/null +++ b/frontend/public/locales/fr/dashboard.json @@ -0,0 +1,36 @@ +{ + "title": "Secrets", + "og-title": "Gérez vos fichiers .env rapidement", + "og-description": "Infisical une plate-forme simple et chiffré de bout en bout qui permet aux équipes de synchroniser et de gérer leurs fichiers .env.", + "search-keys": "Recherche les clefs...", + "add-key": "Ajouter une clef", + "personal": "Personnel", + "personal-description": "Les clés personnelles ne sont visibles que pour vous", + "shared": "Partagé", + "shared-description": "Les clés partagées sont visibles à toute votre équipe", + "make-shared": "Rendre Partagé", + "make-personal": "Rendre Personnel", + "add-secret": "Ajouter un nouveau secret", + "check-docs": { + "button": "Vérifier la documentation", + "title": "Bon travail!", + "line1": "Félicitations pour avoir ajouté plus de secrets.", + "line2": "Voici comment les connecter à votre base de code." + }, + "sidebar": { + "secret": "Secret", + "key": "Clef", + "value": "Valeur", + "override": "Remplacer la valeur avec une valeur personnelle", + "version-history": "Historique des versions", + "comments": "Commentaires & Notes", + "personal-explanation": "Ce secret est personnel. Il n'est partagé avec aucun de vos coéquipiers.", + "generate-random-hex": "Générer un Hex aléatoire", + "digits": "chiffres", + "delete-key-dialog": { + "title": "Supprimer la clef", + "confirm-delete-message": "Êtes-vous sûr de vouloir supprimer ce secret? Cela ne peut pas être annulé." + } + } + +} From f1c52fe332562c7b8a0efbe499673bd03c3ce614 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:45:07 +0100 Subject: [PATCH 25/89] feat(lang): translated integrations.json --- frontend/public/locales/fr/integrations.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 frontend/public/locales/fr/integrations.json diff --git a/frontend/public/locales/fr/integrations.json b/frontend/public/locales/fr/integrations.json new file mode 100644 index 0000000000..887e79dfd6 --- /dev/null +++ b/frontend/public/locales/fr/integrations.json @@ -0,0 +1,16 @@ +{ + "title": "Intégrations de Projet", + "description": "Gérez vos intégrations d'Infisical avec des services tiers.", + "no-integrations1": "Vous n'avez pas encore d'intégration. Quand vous en aurez, elles apparaîtront ici.", + "no-integrations2": "Pour commencer, cliquez sur l'une des options ci-dessous. La configuration se fait en 5 clics.", + "available": "Intégrations de plate-forme et cloud", + "available-text1": "Cliquez sur l'intégration que vous souhaitez connecter. Cela permettra à vos variables d'environnement de circuler automatiquement dans les services tiers sélectionnés.", + "available-text2": "Remarque: Lors d'une intégration avec Heroku, pour des raisons de sécurité, il est impossible de maintenir le chiffrage de bout en bout. En théorie, cela permet à Infisical de déchiffrer les variables d'environnement. En pratique, nous pouvons vous assurer que cela ne sera jamais fait, et cela nous permet de protéger vos secrets des mauvais acteurs en ligne. Le service Infisical de base restera toujours chiffré de bout en bout. Pour toutes vos intérogations, contactez support@infisical.com.", + "cloud-integrations": "Intégrations Cloud", + "framework-integrations": "Intégrations Framework", + "click-to-start": "Cliquez sur une intégration pour commencer à synchroniser les secrets avec elle.", + "click-to-setup": "Cliquez sur un framework pour obtenir les instructions de configuration.", + "grant-access-to-secrets": "Accordez un accès Infisical à vos secrets", + "why-infisical-needs-access": "La plupart des intégrations cloud nécessitent qu'Infisical puisse déchiffrer vos secrets afin qu'ils puissent être transmis.", + "grant-access-button": "Autoriser l'accès" +} From 2f6702537642bf901b1983e0c704430a729d8e7a Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:45:18 +0100 Subject: [PATCH 26/89] feat(lang): translated login.json --- frontend/public/locales/fr/login.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 frontend/public/locales/fr/login.json diff --git a/frontend/public/locales/fr/login.json b/frontend/public/locales/fr/login.json new file mode 100644 index 0000000000..1200d09b90 --- /dev/null +++ b/frontend/public/locales/fr/login.json @@ -0,0 +1,8 @@ +{ + "title": "Connexion", + "og-title": "Connectez-vous à Infisical", + "og-description": "Infisical, une plate-forme simple et chiffré de bout en bout permettant aux équipes de synchroniser et de gérer leurs fichiers .env.", + "login": "Se connecter", + "need-account": "Besoin d'un compte Infisical?", + "create-account": "Créer un compte" +} From aaa771a7b77b591d41bb25715310cb63b434ec12 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:12 +0100 Subject: [PATCH 27/89] feat(lang): translated section-incident.json --- frontend/public/locales/fr/section-incident.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/public/locales/fr/section-incident.json diff --git a/frontend/public/locales/fr/section-incident.json b/frontend/public/locales/fr/section-incident.json new file mode 100644 index 0000000000..95b77ea13a --- /dev/null +++ b/frontend/public/locales/fr/section-incident.json @@ -0,0 +1,11 @@ +{ + "incident-contacts": "Contacts Incidents", + "incident-contacts-description": "Ces contacts seront informés dans le cas improbable d'un incident grave.", + "no-incident-contacts": "Aucun contact incident trouvé.", + "add-contact": "Ajouter un contact", + "add-dialog": { + "title": "Ajouter un contact incident", + "description": "Ce contact sera informé dans le cas improbable d'un incident grave.", + "add-incident": "Ajouter un contact incident" + } +} From 1d0f51bb4229be34d338345cc00d59193daeb77f Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:22 +0100 Subject: [PATCH 28/89] feat(lang): translated nav.json --- frontend/public/locales/fr/nav.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/public/locales/fr/nav.json diff --git a/frontend/public/locales/fr/nav.json b/frontend/public/locales/fr/nav.json new file mode 100644 index 0000000000..1fda27af97 --- /dev/null +++ b/frontend/public/locales/fr/nav.json @@ -0,0 +1,22 @@ +{ + "support": { + "slack": "[NEW] Rejoignez le forum Slack", + "docs": "Lire les documentations", + "issue": "Ouvrir une issue Github", + "email": "Envoyez-nous un email" + }, + "user": { + "signed-in-as": "CONNECTÉ EN TANT QUE", + "current-organization": "ORGANISATION ACTUELLE", + "usage-billing": "Utilisation & Facturation", + "invite": "Inviter des membres", + "other-organizations": "AUTRE ORGANISATION" + }, + "menu": { + "project": "PROJET", + "secrets": "Secrets", + "members": "Membres", + "integrations": "Intégrations", + "project-settings": "Paramètres du Projet" + } +} From 1423d05b52b2b0ab11e3506e8c7c81c406237631 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:36 +0100 Subject: [PATCH 29/89] feat(lang): translated section-members.json --- frontend/public/locales/fr/section-members.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 frontend/public/locales/fr/section-members.json diff --git a/frontend/public/locales/fr/section-members.json b/frontend/public/locales/fr/section-members.json new file mode 100644 index 0000000000..8faed80e76 --- /dev/null +++ b/frontend/public/locales/fr/section-members.json @@ -0,0 +1,14 @@ +{ + "add-member": "Ajouter un Membre", + "org-members": "Membres de l'organisation", + "org-members-description": "Gérer les membres de votre organisation. Ces utilisateurs pourraient ensuite être répartis en projets.", + "search-members": "Recherche des membres...", + "add-dialog": { + "add-member-to-project": "Ajoutez un membre à votre projet", + "already-all-invited": "Tous les utilisateurs de votre organisation sont déjà invités.", + "add-user-org-first": "Ajoutez d'abord plus d'utilisateurs à l'organisation.", + "user-will-email": "L'utilisateur recevra un email avec les instructions.", + "looking-add": "<0>Si vous cherchez à ajouter des utilisateurs à votre organisation,<1>cliquez ici", + "add-user-to-org": "Ajouter des Utilisateurs à l'Organisation" + } +} From 5bc8046f3f56c5d833aa1a9b661e3dc5aa599339 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:46 +0100 Subject: [PATCH 30/89] feat(lang): translated section-password.json --- frontend/public/locales/fr/section-password.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/public/locales/fr/section-password.json diff --git a/frontend/public/locales/fr/section-password.json b/frontend/public/locales/fr/section-password.json new file mode 100644 index 0000000000..0928233e64 --- /dev/null +++ b/frontend/public/locales/fr/section-password.json @@ -0,0 +1,11 @@ +{ + "password": "Mot de passe", + "change": "Changer le mot de passe", + "current": "Mot de passe actuel", + "current-wrong": "Le mot de passe actuel peut être érroné", + "new": "Nouveau mot de passe", + "validate-base": "Le mot de passe doit contenir au moins:", + "validate-length": "14 caractères", + "validate-case": "1 caractère miniscule", + "validate-number": "1 chiffre" +} From ee7cf7920d46125694740a6d0c0e103a834bfe75 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:46:55 +0100 Subject: [PATCH 31/89] feat(lang): translated section-token.json --- frontend/public/locales/fr/section-token.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 frontend/public/locales/fr/section-token.json diff --git a/frontend/public/locales/fr/section-token.json b/frontend/public/locales/fr/section-token.json new file mode 100644 index 0000000000..1f2a3b2332 --- /dev/null +++ b/frontend/public/locales/fr/section-token.json @@ -0,0 +1,13 @@ +{ + "service-tokens": "Jetons de service", + "service-tokens-description": "Chaque jeton de service vous est spécifique, à un certain projet et à un certain environnement dans ce projet.", + "add-new": "Ajouter un nouveau jeton", + "add-dialog": { + "title": "Ajouter un jeton de service pour {{target}}", + "description": "Spécifiez le nom, l'environnement et la période d'expiration. Lorsqu'un jeton est généré, vous ne pourrez le voir qu'une seule fois avant qu'il ne disparaisse. Assurez-vous de le sauvegarder quelque part.", + "name": "Nom du jeton de service", + "add": "Ajouter un jeton de service", + "copy-service-token": "Copiez votre jeton de service", + "copy-service-token-description": "Une fois que vous aurez fermé cette fenêtre, vous ne reverrez plus jamais votre jeton de service" + } +} From 77b34467b9823ba99882bf2033282410d706278f Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:47:19 +0100 Subject: [PATCH 32/89] feat(lang): translated settings-members.json --- frontend/public/locales/fr/settings-members.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 frontend/public/locales/fr/settings-members.json diff --git a/frontend/public/locales/fr/settings-members.json b/frontend/public/locales/fr/settings-members.json new file mode 100644 index 0000000000..9f34c7a68a --- /dev/null +++ b/frontend/public/locales/fr/settings-members.json @@ -0,0 +1,4 @@ +{ + "title": "Membres du projet", + "description": "Cette page affiche les membres du projet sélectionné." +} From 1394368a4386354648ac9fd824bc77de99613da1 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:47:50 +0100 Subject: [PATCH 33/89] feat(lang): translated settings-org.json --- frontend/public/locales/fr/settings-org.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 frontend/public/locales/fr/settings-org.json diff --git a/frontend/public/locales/fr/settings-org.json b/frontend/public/locales/fr/settings-org.json new file mode 100644 index 0000000000..952bd68c8f --- /dev/null +++ b/frontend/public/locales/fr/settings-org.json @@ -0,0 +1,4 @@ +{ + "title": "Paramètres d'Organisation", + "description": "Gérer les membres de votre organisation. Ces utilisateurs pourraient ensuite être répartis en projets." +} From ea9e185a650e3fd80f7d523a5ce92f332ea0604f Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:48:00 +0100 Subject: [PATCH 34/89] feat(lang): translated settings-personal.json --- frontend/public/locales/fr/settings-personal.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/public/locales/fr/settings-personal.json diff --git a/frontend/public/locales/fr/settings-personal.json b/frontend/public/locales/fr/settings-personal.json new file mode 100644 index 0000000000..30a3d6fa3b --- /dev/null +++ b/frontend/public/locales/fr/settings-personal.json @@ -0,0 +1,11 @@ +{ + "title": "Paramètres Personnels", + "description": "Consultez et gérez vos informations personnelles ici.", + "emergency": { + "name": "Kit d'urgence", + "text1": "Votre kit d'urgence contient les informations dont vous aurez besoin pour vous connecter à votre compte Infisical.", + "text2": "Seul le dernier kit d'urgence émis reste valide. Pour obtenir un nouveau kit d'urgence, vérifiez votre mot de passe.", + "download": "Télécharger le kit d'urgence" + }, + "change-language": "Changer de langue" +} From 878ca69f436e713a06327ee32ddfd88f7a6d5e41 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:48:08 +0100 Subject: [PATCH 35/89] feat(lang): translated settings-project.json --- frontend/public/locales/fr/settings-project.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 frontend/public/locales/fr/settings-project.json diff --git a/frontend/public/locales/fr/settings-project.json b/frontend/public/locales/fr/settings-project.json new file mode 100644 index 0000000000..a5a3f1ee35 --- /dev/null +++ b/frontend/public/locales/fr/settings-project.json @@ -0,0 +1,13 @@ +{ + "title": "Paramètres du Projet", + "description": "Ces paramètres ne s'appliquent qu'au Projet actuellement sélectionné.", + "danger-zone": "Zone de danger", + "delete-project": "Supprimer le Projet", + "project-to-delete": "Projet à Supprimer", + "danger-zone-note": "Dès que vous supprimez ce projet, vous ne pourrez plus revenir en arrière. Cela supprimera immédiatement toutes les clefs. Si vous voulez toujours le faire, veuillez saisir le nom du projet ci-dessous.", + "delete-project-note": "Remarque: Vous ne pouvez supprimer qu'un projet que si vous en avez plus d'un.", + "project-id-description": "Pour intégrer Infisical dans votre base de code et obtenir une injection automatique de variables d'environnement, vous devez utiliser l'ID du projet suivant.", + "project-id-description2": "Pour plus de conseils, y compris des extraits de code pour diverses langues et frameworks, voir ", + "auto-generated": "Ceci est l'identifiant unique généré automatiquement pour votre projet. Il ne peut pas être modifié.", + "docs": "Documentation Infisical" +} From a686462392af399cfe5e5723f2e25073268994d1 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:48:20 +0100 Subject: [PATCH 36/89] feat(lang): translated signup.json --- frontend/public/locales/fr/signup.json | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frontend/public/locales/fr/signup.json diff --git a/frontend/public/locales/fr/signup.json b/frontend/public/locales/fr/signup.json new file mode 100644 index 0000000000..3c74476b3f --- /dev/null +++ b/frontend/public/locales/fr/signup.json @@ -0,0 +1,25 @@ +{ + "title": "S'inscrire", + "og-title": "Remplacez les fichiers .env par 1 ligne de code. Inscrivez-vous à Infisical en 3 minutes.", + "og-description": "Infisical, une plate-forme simple et chiffré de bout en bout qui permet aux équipes de synchroniser et de gérer des clefs API et des variables d'environnement. Fonctionne avec Node.js, Next.js, Gatsby, Nest.js ...", + "signup": "S'inscrire", + "already-have-account": "Déjà inscris? Se connecter", + "forgot-password": "Mot de passe oublié?", + "verify": "Vérifier", + "step1-start": "Commençons", + "step1-privacy": "En créant votre compte, vous acceptez nos conditions et avez lu et reconnu notre politique de confidentialité.", + "step1-submit": "C'est parti", + "step2-message": "Nous avons envoyé un email de vérification à{{email}}", + "step2-code-error": "Oops. Votre code est faux. Veuillez réessayer.", + "step2-spam-alert": "Assurez-vous de vérifier vos spam.", + "step3-message": "Nous y sommes presque!", + "step4-message": "Enregistrez votre kit d'urgence", + "step4-description1": "Si vous n'arrivez plus à vous connecter à votre compte, votre kit d'urgence est le seul moyen d'y arriver'.", + "step4-description2": "Nous vous recommandons de le télécharger et de le garder en sécurité.", + "step4-description3": "Il contient votre clef secrète que nous ne pouvons pas récupérer pour vous si vous la perdez.", + "step4-download": "Téléchargez le PDF", + "step5-send-invites": "Envoyer des invitations", + "step5-invite-team": "Invitez votre équipe", + "step5-subtitle": "Infisical a pour but d'être utilisé avec vos coéquipiers. Invitez-les à le tester.", + "step5-skip": "Passer" +} From 7fe706ad0d5979ac89c8804cb93a29a19ca4b18c Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:50:28 +0100 Subject: [PATCH 37/89] fix(lang): configured fr locale --- frontend/next-i18next.config.js | 2 +- frontend/pages/login.tsx | 2 +- frontend/pages/settings/personal/[id].js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/next-i18next.config.js b/frontend/next-i18next.config.js index 5de9c97bc7..3feba43184 100644 --- a/frontend/next-i18next.config.js +++ b/frontend/next-i18next.config.js @@ -8,7 +8,7 @@ module.exports = { debug: process.env.NODE_ENV === "development", i18n: { defaultLocale: "en", - locales: ["en", "ko"], + locales: ["en", "ko", "fr"], }, fallbackLng: { default: ["en"], diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index e72aa3b256..84d5390341 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -160,7 +160,7 @@ export default function Login() { diff --git a/frontend/pages/settings/personal/[id].js b/frontend/pages/settings/personal/[id].js index 5eebb83283..a592bf45be 100644 --- a/frontend/pages/settings/personal/[id].js +++ b/frontend/pages/settings/personal/[id].js @@ -126,7 +126,7 @@ export default function PersonalSettings() { From 16d2746749bf8d9aa61b78280a12aae868e9ba1d Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Wed, 11 Jan 2023 01:30:15 +0100 Subject: [PATCH 38/89] fix(lang): add langugageMap fr --- frontend/const.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/const.js b/frontend/const.js index 195d54a6de..0e5ed9516e 100644 --- a/frontend/const.js +++ b/frontend/const.js @@ -20,4 +20,5 @@ export const publicPaths = [ export const languageMap = { en: "English", ko: "한국어", + fr: "Français", }; From d9b7f698383d14374a5f9157b8668087bc9f18b3 Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Wed, 11 Jan 2023 01:32:53 +0100 Subject: [PATCH 39/89] fix(lang): add remaining translation on login + signin --- frontend/components/signup/CodeInputStep.tsx | 6 +++--- frontend/components/signup/EnterEmailStep.tsx | 6 +++--- frontend/pages/login.tsx | 8 ++++++-- frontend/public/locales/en/login.json | 4 +++- frontend/public/locales/en/signup.json | 5 ++++- frontend/public/locales/fr/login.json | 4 +++- frontend/public/locales/fr/signup.json | 13 ++++++++----- 7 files changed, 30 insertions(+), 16 deletions(-) diff --git a/frontend/components/signup/CodeInputStep.tsx b/frontend/components/signup/CodeInputStep.tsx index 05c4973a87..9cb1aa5502 100644 --- a/frontend/components/signup/CodeInputStep.tsx +++ b/frontend/components/signup/CodeInputStep.tsx @@ -81,7 +81,7 @@ export default function CodeInputStep({ email, incrementStep, setCode, codeError return (

- {"We've"} sent a verification email to{" "} + {t("signup:step2-message")}

{email}{" "} @@ -119,11 +119,11 @@ export default function CodeInputStep({ email, incrementStep, setCode, codeError

- Not seeing an email? + {t("signup:step2-resend-alert")}
diff --git a/frontend/components/signup/EnterEmailStep.tsx b/frontend/components/signup/EnterEmailStep.tsx index 5f98710b75..85598a8a20 100644 --- a/frontend/components/signup/EnterEmailStep.tsx +++ b/frontend/components/signup/EnterEmailStep.tsx @@ -59,11 +59,11 @@ export default function EnterEmailStep({ email, setEmail, incrementStep }: Downl

- {'Let\''}s get started + {t("signup:step1-start")}

-
diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index 84d5390341..69126e6af7 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -117,11 +117,15 @@ export default function Login() { id="current-password" />
- Forgot password? + + +
{!isLoading && errorLogin && ( - + )}
diff --git a/frontend/public/locales/en/login.json b/frontend/public/locales/en/login.json index ee097fd920..75b56a014f 100644 --- a/frontend/public/locales/en/login.json +++ b/frontend/public/locales/en/login.json @@ -4,5 +4,7 @@ "og-description": "Infisical a simple end-to-end encrypted platform that enables teams to sync and manage their .env files.", "login": "Log In", "need-account": "Need an Infisical account?", - "create-account": "Create an account" + "create-account": "Create an account", + "forgot-password": "Forgot your password?", + "error-login": "Wrong credentials." } diff --git a/frontend/public/locales/en/signup.json b/frontend/public/locales/en/signup.json index 0cdbe310b1..bee993727f 100644 --- a/frontend/public/locales/en/signup.json +++ b/frontend/public/locales/en/signup.json @@ -9,8 +9,11 @@ "step1-start": "Let's get started", "step1-privacy": "By creating an account, you agree to our Terms and have read and acknowledged the Privacy Policy.", "step1-submit": "Get Started", - "step2-message": "We've sent a verification email to{{email}}", + "step2-message": "We've sent a verification email to", "step2-code-error": "Oops. Your code is wrong. Please try again.", + "step2-resend-alert": "Don't see the email?", + "step2-resend-submit": "Resend", + "step2-resend-progress": "Resending...", "step2-spam-alert": "Make sure to check your spam inbox.", "step3-message": "Almost there!", "step4-message": "Save your Emergency Kit", diff --git a/frontend/public/locales/fr/login.json b/frontend/public/locales/fr/login.json index 1200d09b90..8c93da9fbd 100644 --- a/frontend/public/locales/fr/login.json +++ b/frontend/public/locales/fr/login.json @@ -4,5 +4,7 @@ "og-description": "Infisical, une plate-forme simple et chiffré de bout en bout permettant aux équipes de synchroniser et de gérer leurs fichiers .env.", "login": "Se connecter", "need-account": "Besoin d'un compte Infisical?", - "create-account": "Créer un compte" + "create-account": "Créer un compte", + "forgot-password": "Mot de passe oublié?", + "error-login": "Mauvais identifiants." } diff --git a/frontend/public/locales/fr/signup.json b/frontend/public/locales/fr/signup.json index 3c74476b3f..48b36e2030 100644 --- a/frontend/public/locales/fr/signup.json +++ b/frontend/public/locales/fr/signup.json @@ -6,19 +6,22 @@ "already-have-account": "Déjà inscris? Se connecter", "forgot-password": "Mot de passe oublié?", "verify": "Vérifier", - "step1-start": "Commençons", + "step1-start": "Bon, on commence!", "step1-privacy": "En créant votre compte, vous acceptez nos conditions et avez lu et reconnu notre politique de confidentialité.", "step1-submit": "C'est parti", - "step2-message": "Nous avons envoyé un email de vérification à{{email}}", + "step2-message": "Nous avons envoyé un email de vérification à", "step2-code-error": "Oops. Votre code est faux. Veuillez réessayer.", - "step2-spam-alert": "Assurez-vous de vérifier vos spam.", + "step2-resend-alert": "Vous ne voyez pas l'email?", + "step2-resend-submit": "Renvoyer", + "step2-resend-progress": "Envoie en cours...", + "step2-spam-alert": "Assurez-vous de vérifier vos spams.", "step3-message": "Nous y sommes presque!", "step4-message": "Enregistrez votre kit d'urgence", - "step4-description1": "Si vous n'arrivez plus à vous connecter à votre compte, votre kit d'urgence est le seul moyen d'y arriver'.", + "step4-description1": "Si vous n'arrivez plus à vous connecter à votre compte, votre kit d'urgence est le seul moyen d'y arriver.", "step4-description2": "Nous vous recommandons de le télécharger et de le garder en sécurité.", "step4-description3": "Il contient votre clef secrète que nous ne pouvons pas récupérer pour vous si vous la perdez.", "step4-download": "Téléchargez le PDF", - "step5-send-invites": "Envoyer des invitations", + "step5-send-invites": "Envoyer les invitations", "step5-invite-team": "Invitez votre équipe", "step5-subtitle": "Infisical a pour but d'être utilisé avec vos coéquipiers. Invitez-les à le tester.", "step5-skip": "Passer" From 0b281a02d00dc5dc7eb3ab99b1d95ed0d3ea086d Mon Sep 17 00:00:00 2001 From: Grraahaam <72856427+Grraahaam@users.noreply.github.com> Date: Wed, 11 Jan 2023 01:54:16 +0100 Subject: [PATCH 40/89] fix(i18n): add default empty string --- frontend/components/signup/EnterEmailStep.tsx | 4 ++-- frontend/pages/login.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/components/signup/EnterEmailStep.tsx b/frontend/components/signup/EnterEmailStep.tsx index 85598a8a20..1b9177580f 100644 --- a/frontend/components/signup/EnterEmailStep.tsx +++ b/frontend/components/signup/EnterEmailStep.tsx @@ -63,7 +63,7 @@ export default function EnterEmailStep({ email, setEmail, incrementStep }: Downl

-
diff --git a/frontend/pages/login.tsx b/frontend/pages/login.tsx index 69126e6af7..4582710666 100644 --- a/frontend/pages/login.tsx +++ b/frontend/pages/login.tsx @@ -125,7 +125,7 @@ export default function Login() {
{!isLoading && errorLogin && ( - + )}
From c0f0d699b4f1f9b4ca556cb09baab748cf9e060e Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 11 Jan 2023 10:18:44 +0700 Subject: [PATCH 41/89] Add files to api-docs branch --- .../endpoints/secrets/create.mdx | 4 + .../endpoints/secrets/delete.mdx | 4 + docs/api-reference/endpoints/secrets/read.mdx | 4 + .../endpoints/secrets/update.mdx | 4 + .../api-reference/overview/authentication.mdx | 3 + docs/api-reference/overview/introduction.mdx | 3 + docs/spec.yaml | 1690 +++++++++++++++++ 7 files changed, 1712 insertions(+) create mode 100644 docs/api-reference/endpoints/secrets/create.mdx create mode 100644 docs/api-reference/endpoints/secrets/delete.mdx create mode 100644 docs/api-reference/endpoints/secrets/read.mdx create mode 100644 docs/api-reference/endpoints/secrets/update.mdx create mode 100644 docs/api-reference/overview/authentication.mdx create mode 100644 docs/api-reference/overview/introduction.mdx create mode 100644 docs/spec.yaml diff --git a/docs/api-reference/endpoints/secrets/create.mdx b/docs/api-reference/endpoints/secrets/create.mdx new file mode 100644 index 0000000000..27c167f76c --- /dev/null +++ b/docs/api-reference/endpoints/secrets/create.mdx @@ -0,0 +1,4 @@ +--- +title: "Create" +openapi: "POST /api/v2/secrets/" +--- diff --git a/docs/api-reference/endpoints/secrets/delete.mdx b/docs/api-reference/endpoints/secrets/delete.mdx new file mode 100644 index 0000000000..af87e7120a --- /dev/null +++ b/docs/api-reference/endpoints/secrets/delete.mdx @@ -0,0 +1,4 @@ +--- +title: "Delete" +openapi: "DELETE /api/v2/secrets/" +--- diff --git a/docs/api-reference/endpoints/secrets/read.mdx b/docs/api-reference/endpoints/secrets/read.mdx new file mode 100644 index 0000000000..4305f192c5 --- /dev/null +++ b/docs/api-reference/endpoints/secrets/read.mdx @@ -0,0 +1,4 @@ +--- +title: "Read" +openapi: "GET /api/v2/secrets/" +--- diff --git a/docs/api-reference/endpoints/secrets/update.mdx b/docs/api-reference/endpoints/secrets/update.mdx new file mode 100644 index 0000000000..2193fccc35 --- /dev/null +++ b/docs/api-reference/endpoints/secrets/update.mdx @@ -0,0 +1,4 @@ +--- +title: "Update" +openapi: "PATCH /api/v2/secrets/" +--- diff --git a/docs/api-reference/overview/authentication.mdx b/docs/api-reference/overview/authentication.mdx new file mode 100644 index 0000000000..27a2dc1345 --- /dev/null +++ b/docs/api-reference/overview/authentication.mdx @@ -0,0 +1,3 @@ +--- +title: "Authentication" +--- diff --git a/docs/api-reference/overview/introduction.mdx b/docs/api-reference/overview/introduction.mdx new file mode 100644 index 0000000000..9632e3788d --- /dev/null +++ b/docs/api-reference/overview/introduction.mdx @@ -0,0 +1,3 @@ +--- +title: "Introduction" +--- diff --git a/docs/spec.yaml b/docs/spec.yaml new file mode 100644 index 0000000000..a0271395b1 --- /dev/null +++ b/docs/spec.yaml @@ -0,0 +1,1690 @@ +openapi: 3.0.0 +info: + title: Infisical API + description: List of all available APIs that can be consumed + version: 1.0.0 +servers: + - url: http://localhost:8080 +paths: + /api/v1/secret/{secretId}/secret-versions: + get: + description: '' + parameters: + - name: secretId + in: path + required: true + schema: + type: string + - name: offset + in: query + schema: + type: string + - name: limit + in: query + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/secret/{secretId}/secret-versions/rollback: + post: + description: '' + parameters: + - name: secretId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + version: + example: any + /api/v1/secret-snapshot/{secretSnapshotId}: + get: + description: '' + parameters: + - name: secretSnapshotId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/{workspaceId}/secret-snapshots: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: offset + in: query + schema: + type: string + - name: limit + in: query + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/{workspaceId}/secret-snapshots/count: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/{workspaceId}/secret-snapshots/rollback: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + version: + example: any + /api/v1/workspace/{workspaceId}/logs: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: offset + in: query + schema: + type: string + - name: limit + in: query + schema: + type: string + - name: sortBy + in: query + schema: + type: string + - name: userId + in: query + schema: + type: string + - name: actionNames + in: query + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/action/{actionId}: + get: + description: '' + parameters: + - name: actionId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + /api/v1/signup/email/signup: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + '403': + description: Forbidden + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + /api/v1/signup/email/verify: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + '403': + description: Forbidden + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + code: + example: any + /api/v1/signup/complete-account/signup: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + '403': + description: Forbidden + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + firstName: + example: any + lastName: + example: any + publicKey: + example: any + encryptedPrivateKey: + example: any + iv: + example: any + tag: + example: any + salt: + example: any + verifier: + example: any + organizationName: + example: any + /api/v1/signup/complete-account/invite: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + '403': + description: Forbidden + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + firstName: + example: any + lastName: + example: any + publicKey: + example: any + encryptedPrivateKey: + example: any + iv: + example: any + tag: + example: any + salt: + example: any + verifier: + example: any + /api/v1/auth/token: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/auth/login1: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + clientPublicKey: + example: any + /api/v1/auth/login2: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + clientProof: + example: any + /api/v1/auth/logout: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/auth/checkAuth: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + /api/v1/bot/{workspaceId}: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/bot/{botId}/active: + patch: + description: '' + parameters: + - name: botId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + isActive: + example: any + botKey: + example: any + /api/v1/user/: + get: + description: '' + parameters: [] + responses: + '200': + description: OK + /api/v1/user-action/: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + action: + example: any + get: + description: '' + parameters: + - name: action + in: query + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/organization/: + get: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + organizationName: + example: any + /api/v1/organization/{organizationId}: + get: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/organization/{organizationId}/users: + get: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/organization/{organizationId}/my-workspaces: + get: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/organization/{organizationId}/name: + patch: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + example: any + /api/v1/organization/{organizationId}/incidentContactOrg: + get: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + post: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + delete: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + /api/v1/organization/{organizationId}/customer-portal-session: + post: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/organization/{organizationId}/subscriptions: + get: + description: '' + parameters: + - name: organizationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/{workspaceId}/keys: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/{workspaceId}/users: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/: + get: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + workspaceName: + example: any + organizationId: + example: any + /api/v1/workspace/{workspaceId}: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + delete: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/{workspaceId}/name: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + example: any + /api/v1/workspace/{workspaceId}/invite-signup: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + /api/v1/workspace/{workspaceId}/integrations: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/{workspaceId}/authorizations: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/workspace/{workspaceId}/service-tokens: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/membership-org/membershipOrg/{membershipOrgId}/change-role: + post: + description: '' + parameters: + - name: membershipOrgId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + /api/v1/membership-org/{membershipOrgId}: + delete: + description: '' + parameters: + - name: membershipOrgId + in: path + required: true + schema: + type: string + responses: + '400': + description: Bad Request + /api/v1/membership/{workspaceId}/connect: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/membership/{membershipId}: + delete: + description: '' + parameters: + - name: membershipId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/membership/{membershipId}/change-role: + post: + description: '' + parameters: + - name: membershipId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + role: + example: any + /api/v1/key/{workspaceId}: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + key: + example: any + /api/v1/key/{workspaceId}/latest: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/invite-org/signup: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + organizationId: + example: any + inviteeEmail: + example: any + /api/v1/invite-org/verify: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + code: + example: any + /api/v1/secret/{workspaceId}: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + secrets: + example: any + keys: + example: any + environment: + example: any + channel: + example: any + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environment + in: query + schema: + type: string + - name: channel + in: query + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/secret/{workspaceId}/service-token: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environment + in: query + schema: + type: string + - name: channel + in: query + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/service-token/: + get: + description: '' + parameters: [] + responses: + '200': + description: OK + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + example: any + workspaceId: + example: any + environment: + example: any + expiresIn: + example: any + publicKey: + example: any + encryptedKey: + example: any + nonce: + example: any + /api/v1/password/srp1: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + clientPublicKey: + example: any + /api/v1/password/change-password: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + clientProof: + example: any + encryptedPrivateKey: + example: any + iv: + example: any + tag: + example: any + salt: + example: any + verifier: + example: any + /api/v1/password/email/password-reset: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + '403': + description: Forbidden + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + /api/v1/password/email/password-reset-verify: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + '403': + description: Forbidden + requestBody: + content: + application/json: + schema: + type: object + properties: + email: + example: any + code: + example: any + /api/v1/password/backup-private-key: + get: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + clientProof: + example: any + encryptedPrivateKey: + example: any + iv: + example: any + tag: + example: any + salt: + example: any + verifier: + example: any + /api/v1/password/password-reset: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + encryptedPrivateKey: + example: any + iv: + example: any + tag: + example: any + salt: + example: any + verifier: + example: any + /api/v1/stripe/webhook: + post: + description: '' + parameters: + - name: stripe-signature + in: header + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/integration/{integrationId}: + patch: + description: '' + parameters: + - name: integrationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + app: + example: any + environment: + example: any + isActive: + example: any + target: + example: any + context: + example: any + siteId: + example: any + delete: + description: '' + parameters: + - name: integrationId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/integration-auth/integration-options: + get: + description: '' + parameters: [] + responses: + '200': + description: OK + /api/v1/integration-auth/oauth-token: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + workspaceId: + example: any + code: + example: any + integration: + example: any + /api/v1/integration-auth/{integrationAuthId}/apps: + get: + description: '' + parameters: + - name: integrationAuthId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v1/integration-auth/{integrationAuthId}: + delete: + description: '' + parameters: + - name: integrationAuthId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v2/workspace/{workspaceId}/secrets: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + secrets: + example: any + keys: + example: any + environment: + example: any + channel: + example: any + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environment + in: query + schema: + type: string + - name: channel + in: query + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v2/workspace/{workspaceId}/encrypted-key: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v2/workspace/{workspaceId}/service-token-data: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environment}: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environment + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + content: + application/json: + schema: + type: object + properties: + secrets: + example: any + /api/v2/secret/workspace/{workspaceId}/environment/{environment}: + post: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environment + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + content: + application/json: + schema: + type: object + properties: + secret: + example: any + /api/v2/secret/workspace/{workspaceId}: + get: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environment + in: query + schema: + type: string + responses: + '200': + description: OK + /api/v2/secret/{secretId}: + get: + description: '' + parameters: + - name: secretId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + delete: + description: '' + parameters: + - name: secretId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + /api/v2/secret/batch/workspace/{workspaceId}/environment/{environmentName}: + delete: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environmentName + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + content: + application/json: + schema: + type: object + properties: + secretIds: + example: any + /api/v2/secret/batch-modify/workspace/{workspaceId}/environment/{environmentName}: + patch: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environmentName + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + content: + application/json: + schema: + type: object + properties: + secrets: + example: any + /api/v2/secret/workspace/{workspaceId}/environment/{environmentName}: + patch: + description: '' + parameters: + - name: workspaceId + in: path + required: true + schema: + type: string + - name: environmentName + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + requestBody: + content: + application/json: + schema: + type: object + properties: + secret: + example: any + /api/v2/secrets/: + post: + description: '' + parameters: [] + responses: + '200': + description: OK + requestBody: + content: + application/json: + schema: + type: object + properties: + secrets: + example: any + workspaceId: + example: any + environment: + example: any + get: + description: '' + parameters: + - name: workspaceId + in: query + schema: + type: string + - name: environment + in: query + schema: + type: string + responses: + '200': + description: OK + patch: + description: '' + parameters: [] + responses: + '200': + description: OK + requestBody: + content: + application/json: + schema: + type: object + properties: + secrets: + example: any + delete: + description: '' + parameters: [] + responses: + '200': + description: OK + /api/v2/service-token/: + get: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + example: any + workspaceId: + example: any + environment: + example: any + encryptedKey: + example: any + iv: + example: any + tag: + example: any + expiresIn: + example: any + /api/v2/service-token/{serviceTokenDataId}: + delete: + description: '' + parameters: + - name: serviceTokenDataId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/v2/api-key-data/: + get: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + post: + description: '' + parameters: [] + responses: + '200': + description: OK + '400': + description: Bad Request + requestBody: + content: + application/json: + schema: + type: object + properties: + name: + example: any + expiresIn: + example: any + /api/v2/api-key-data/{apiKeyDataId}: + delete: + description: '' + parameters: + - name: apiKeyDataId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + '400': + description: Bad Request + /api/status: + get: + description: '' + parameters: [] + responses: + '200': + description: OK +components: + schemas: + secret: + type: object + properties: + type: + type: string + example: object + properties: + type: object + properties: + test: + type: object + properties: + type: + type: string + example: integer + description: + type: string + example: '123' + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT From b8f102493e549e21add206579d60a73f75722718 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 11 Jan 2023 11:19:56 +0700 Subject: [PATCH 42/89] Bring back sync integrations to CRUD secrets routes --- .../src/controllers/v2/secretsController.ts | 25 +- backend/src/variables/integration.ts | 6 +- docs/spec.yaml | 1690 ----------------- 3 files changed, 27 insertions(+), 1694 deletions(-) delete mode 100644 docs/spec.yaml diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index 69cacd75c6..ff9574711d 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -11,6 +11,8 @@ import { ACTION_DELETE_SECRETS } from '../../variables'; import { ValidationError } from '../../utils/errors'; +import { EventService } from '../../services'; +import { eventPushSecrets } from '../../events'; import { EESecretService, EELogService } from '../../ee/services'; import { postHogClient } from '../../services'; import { BadRequestError } from '../../utils/errors'; @@ -102,6 +104,13 @@ export const createSecrets = async (req: Request, res: Response) => { })) }); + // trigger event - push secrets + await EventService.handleEvent({ + event: eventPushSecrets({ + workspaceId + }) + }); + const addAction = await EELogService.createActionSecret({ name: ACTION_ADD_SECRETS, userId: req.user._id.toString(), @@ -194,7 +203,7 @@ export const getSecrets = async (req: Request, res: Response) => { if (postHogClient) { postHogClient.capture({ - event: 'secrets deleted', + event: 'secrets added', distinctId: req.user.email, properties: { numberOfSecrets: secrets.length, @@ -321,6 +330,7 @@ export const updateSecrets = async (req: Request, res: Response) => { }) }); + // group secrets into workspaces so updated secrets can // be logged and snapshotted separately for each workspace const workspaceSecretObj: any = {}; @@ -333,6 +343,13 @@ export const updateSecrets = async (req: Request, res: Response) => { }); Object.keys(workspaceSecretObj).forEach(async (key) => { + // trigger event - push secrets + await EventService.handleEvent({ + event: eventPushSecrets({ + workspaceId: key + }) + }); + const updateAction = await EELogService.createActionSecret({ name: ACTION_UPDATE_SECRETS, userId: req.user._id.toString(), @@ -409,6 +426,12 @@ export const deleteSecrets = async (req: Request, res: Response) => { }); Object.keys(workspaceSecretObj).forEach(async (key) => { + // trigger event - push secrets + await EventService.handleEvent({ + event: eventPushSecrets({ + workspaceId: key + }) + }); const deleteAction = await EELogService.createActionSecret({ name: ACTION_DELETE_SECRETS, userId: req.user._id.toString(), diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index ed18c5a2ac..00e817c572 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -48,7 +48,7 @@ const INTEGRATION_OPTIONS = [ name: 'Vercel', slug: 'vercel', image: 'Vercel', - isAvailable: false, + isAvailable: true, type: 'vercel', clientId: '', clientSlug: CLIENT_SLUG_VERCEL, @@ -58,7 +58,7 @@ const INTEGRATION_OPTIONS = [ name: 'Netlify', slug: 'netlify', image: 'Netlify', - isAvailable: false, + isAvailable: true, type: 'oauth2', clientId: CLIENT_ID_NETLIFY, docsLink: '' @@ -67,7 +67,7 @@ const INTEGRATION_OPTIONS = [ name: 'GitHub', slug: 'github', image: 'GitHub', - isAvailable: false, + isAvailable: true, type: 'oauth2', clientId: CLIENT_ID_GITHUB, docsLink: '' diff --git a/docs/spec.yaml b/docs/spec.yaml deleted file mode 100644 index a0271395b1..0000000000 --- a/docs/spec.yaml +++ /dev/null @@ -1,1690 +0,0 @@ -openapi: 3.0.0 -info: - title: Infisical API - description: List of all available APIs that can be consumed - version: 1.0.0 -servers: - - url: http://localhost:8080 -paths: - /api/v1/secret/{secretId}/secret-versions: - get: - description: '' - parameters: - - name: secretId - in: path - required: true - schema: - type: string - - name: offset - in: query - schema: - type: string - - name: limit - in: query - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/secret/{secretId}/secret-versions/rollback: - post: - description: '' - parameters: - - name: secretId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - version: - example: any - /api/v1/secret-snapshot/{secretSnapshotId}: - get: - description: '' - parameters: - - name: secretSnapshotId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/secret-snapshots: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: offset - in: query - schema: - type: string - - name: limit - in: query - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/secret-snapshots/count: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/secret-snapshots/rollback: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - version: - example: any - /api/v1/workspace/{workspaceId}/logs: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: offset - in: query - schema: - type: string - - name: limit - in: query - schema: - type: string - - name: sortBy - in: query - schema: - type: string - - name: userId - in: query - schema: - type: string - - name: actionNames - in: query - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/action/{actionId}: - get: - description: '' - parameters: - - name: actionId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/signup/email/signup: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - '403': - description: Forbidden - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - /api/v1/signup/email/verify: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - '403': - description: Forbidden - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - code: - example: any - /api/v1/signup/complete-account/signup: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - '403': - description: Forbidden - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - firstName: - example: any - lastName: - example: any - publicKey: - example: any - encryptedPrivateKey: - example: any - iv: - example: any - tag: - example: any - salt: - example: any - verifier: - example: any - organizationName: - example: any - /api/v1/signup/complete-account/invite: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - '403': - description: Forbidden - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - firstName: - example: any - lastName: - example: any - publicKey: - example: any - encryptedPrivateKey: - example: any - iv: - example: any - tag: - example: any - salt: - example: any - verifier: - example: any - /api/v1/auth/token: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/auth/login1: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - clientPublicKey: - example: any - /api/v1/auth/login2: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - clientProof: - example: any - /api/v1/auth/logout: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/auth/checkAuth: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - /api/v1/bot/{workspaceId}: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/bot/{botId}/active: - patch: - description: '' - parameters: - - name: botId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - isActive: - example: any - botKey: - example: any - /api/v1/user/: - get: - description: '' - parameters: [] - responses: - '200': - description: OK - /api/v1/user-action/: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - action: - example: any - get: - description: '' - parameters: - - name: action - in: query - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/organization/: - get: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - organizationName: - example: any - /api/v1/organization/{organizationId}: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/organization/{organizationId}/users: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/organization/{organizationId}/my-workspaces: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/organization/{organizationId}/name: - patch: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - example: any - /api/v1/organization/{organizationId}/incidentContactOrg: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - delete: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - /api/v1/organization/{organizationId}/customer-portal-session: - post: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/organization/{organizationId}/subscriptions: - get: - description: '' - parameters: - - name: organizationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/keys: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/users: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/: - get: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceName: - example: any - organizationId: - example: any - /api/v1/workspace/{workspaceId}: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - delete: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/name: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - example: any - /api/v1/workspace/{workspaceId}/invite-signup: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - /api/v1/workspace/{workspaceId}/integrations: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/authorizations: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/workspace/{workspaceId}/service-tokens: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/membership-org/membershipOrg/{membershipOrgId}/change-role: - post: - description: '' - parameters: - - name: membershipOrgId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v1/membership-org/{membershipOrgId}: - delete: - description: '' - parameters: - - name: membershipOrgId - in: path - required: true - schema: - type: string - responses: - '400': - description: Bad Request - /api/v1/membership/{workspaceId}/connect: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/membership/{membershipId}: - delete: - description: '' - parameters: - - name: membershipId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/membership/{membershipId}/change-role: - post: - description: '' - parameters: - - name: membershipId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - role: - example: any - /api/v1/key/{workspaceId}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - key: - example: any - /api/v1/key/{workspaceId}/latest: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/invite-org/signup: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - organizationId: - example: any - inviteeEmail: - example: any - /api/v1/invite-org/verify: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - code: - example: any - /api/v1/secret/{workspaceId}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - keys: - example: any - environment: - example: any - channel: - example: any - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: query - schema: - type: string - - name: channel - in: query - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/secret/{workspaceId}/service-token: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: query - schema: - type: string - - name: channel - in: query - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/service-token/: - get: - description: '' - parameters: [] - responses: - '200': - description: OK - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - example: any - workspaceId: - example: any - environment: - example: any - expiresIn: - example: any - publicKey: - example: any - encryptedKey: - example: any - nonce: - example: any - /api/v1/password/srp1: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - clientPublicKey: - example: any - /api/v1/password/change-password: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - clientProof: - example: any - encryptedPrivateKey: - example: any - iv: - example: any - tag: - example: any - salt: - example: any - verifier: - example: any - /api/v1/password/email/password-reset: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - '403': - description: Forbidden - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - /api/v1/password/email/password-reset-verify: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - '403': - description: Forbidden - requestBody: - content: - application/json: - schema: - type: object - properties: - email: - example: any - code: - example: any - /api/v1/password/backup-private-key: - get: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - clientProof: - example: any - encryptedPrivateKey: - example: any - iv: - example: any - tag: - example: any - salt: - example: any - verifier: - example: any - /api/v1/password/password-reset: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - encryptedPrivateKey: - example: any - iv: - example: any - tag: - example: any - salt: - example: any - verifier: - example: any - /api/v1/stripe/webhook: - post: - description: '' - parameters: - - name: stripe-signature - in: header - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/integration/{integrationId}: - patch: - description: '' - parameters: - - name: integrationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - app: - example: any - environment: - example: any - isActive: - example: any - target: - example: any - context: - example: any - siteId: - example: any - delete: - description: '' - parameters: - - name: integrationId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/integration-auth/integration-options: - get: - description: '' - parameters: [] - responses: - '200': - description: OK - /api/v1/integration-auth/oauth-token: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - workspaceId: - example: any - code: - example: any - integration: - example: any - /api/v1/integration-auth/{integrationAuthId}/apps: - get: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v1/integration-auth/{integrationAuthId}: - delete: - description: '' - parameters: - - name: integrationAuthId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v2/workspace/{workspaceId}/secrets: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - keys: - example: any - environment: - example: any - channel: - example: any - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: query - schema: - type: string - - name: channel - in: query - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v2/workspace/{workspaceId}/encrypted-key: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v2/workspace/{workspaceId}/service-token-data: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v2/secret/batch-create/workspace/{workspaceId}/environment/{environment}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - /api/v2/secret/workspace/{workspaceId}/environment/{environment}: - post: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secret: - example: any - /api/v2/secret/workspace/{workspaceId}: - get: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environment - in: query - schema: - type: string - responses: - '200': - description: OK - /api/v2/secret/{secretId}: - get: - description: '' - parameters: - - name: secretId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - delete: - description: '' - parameters: - - name: secretId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - /api/v2/secret/batch/workspace/{workspaceId}/environment/{environmentName}: - delete: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environmentName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secretIds: - example: any - /api/v2/secret/batch-modify/workspace/{workspaceId}/environment/{environmentName}: - patch: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environmentName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - /api/v2/secret/workspace/{workspaceId}/environment/{environmentName}: - patch: - description: '' - parameters: - - name: workspaceId - in: path - required: true - schema: - type: string - - name: environmentName - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secret: - example: any - /api/v2/secrets/: - post: - description: '' - parameters: [] - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - workspaceId: - example: any - environment: - example: any - get: - description: '' - parameters: - - name: workspaceId - in: query - schema: - type: string - - name: environment - in: query - schema: - type: string - responses: - '200': - description: OK - patch: - description: '' - parameters: [] - responses: - '200': - description: OK - requestBody: - content: - application/json: - schema: - type: object - properties: - secrets: - example: any - delete: - description: '' - parameters: [] - responses: - '200': - description: OK - /api/v2/service-token/: - get: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - example: any - workspaceId: - example: any - environment: - example: any - encryptedKey: - example: any - iv: - example: any - tag: - example: any - expiresIn: - example: any - /api/v2/service-token/{serviceTokenDataId}: - delete: - description: '' - parameters: - - name: serviceTokenDataId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/v2/api-key-data/: - get: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - post: - description: '' - parameters: [] - responses: - '200': - description: OK - '400': - description: Bad Request - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - example: any - expiresIn: - example: any - /api/v2/api-key-data/{apiKeyDataId}: - delete: - description: '' - parameters: - - name: apiKeyDataId - in: path - required: true - schema: - type: string - responses: - '200': - description: OK - '400': - description: Bad Request - /api/status: - get: - description: '' - parameters: [] - responses: - '200': - description: OK -components: - schemas: - secret: - type: object - properties: - type: - type: string - example: object - properties: - type: object - properties: - test: - type: object - properties: - type: - type: string - example: integer - description: - type: string - example: '123' - securitySchemes: - bearerAuth: - type: http - scheme: bearer - bearerFormat: JWT From 389f5c4f211de8f45b459d749ec18358a5263220 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Tue, 10 Jan 2023 21:11:23 -0800 Subject: [PATCH 43/89] Disabled integrations for now --- backend/src/variables/integration.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index 00e817c572..ed18c5a2ac 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -48,7 +48,7 @@ const INTEGRATION_OPTIONS = [ name: 'Vercel', slug: 'vercel', image: 'Vercel', - isAvailable: true, + isAvailable: false, type: 'vercel', clientId: '', clientSlug: CLIENT_SLUG_VERCEL, @@ -58,7 +58,7 @@ const INTEGRATION_OPTIONS = [ name: 'Netlify', slug: 'netlify', image: 'Netlify', - isAvailable: true, + isAvailable: false, type: 'oauth2', clientId: CLIENT_ID_NETLIFY, docsLink: '' @@ -67,7 +67,7 @@ const INTEGRATION_OPTIONS = [ name: 'GitHub', slug: 'github', image: 'GitHub', - isAvailable: true, + isAvailable: false, type: 'oauth2', clientId: CLIENT_ID_GITHUB, docsLink: '' From c527efad94029c1cd9981ad6441fb9ed66c0bd1f Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Wed, 11 Jan 2023 00:24:05 -0500 Subject: [PATCH 44/89] Revert "Disabled integrations for now" This reverts commit 389f5c4f211de8f45b459d749ec18358a5263220. --- backend/src/variables/integration.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index ed18c5a2ac..00e817c572 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -48,7 +48,7 @@ const INTEGRATION_OPTIONS = [ name: 'Vercel', slug: 'vercel', image: 'Vercel', - isAvailable: false, + isAvailable: true, type: 'vercel', clientId: '', clientSlug: CLIENT_SLUG_VERCEL, @@ -58,7 +58,7 @@ const INTEGRATION_OPTIONS = [ name: 'Netlify', slug: 'netlify', image: 'Netlify', - isAvailable: false, + isAvailable: true, type: 'oauth2', clientId: CLIENT_ID_NETLIFY, docsLink: '' @@ -67,7 +67,7 @@ const INTEGRATION_OPTIONS = [ name: 'GitHub', slug: 'github', image: 'GitHub', - isAvailable: false, + isAvailable: true, type: 'oauth2', clientId: CLIENT_ID_GITHUB, docsLink: '' From 37ed27111aee96715a1b360e394eb509ae09338d Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Wed, 11 Jan 2023 16:53:53 +0700 Subject: [PATCH 45/89] Patch Vercel API teamId requirement for team integrations --- backend/src/integrations/apps.ts | 23 +++++++----- backend/src/integrations/exchange.ts | 3 +- backend/src/integrations/sync.ts | 53 +++++++++++++++------------- backend/src/variables/index.ts | 2 -- backend/src/variables/integration.ts | 2 -- frontend/pages/dashboard/[id].tsx | 1 + frontend/pages/integrations/[id].js | 5 +-- 7 files changed, 47 insertions(+), 42 deletions(-) diff --git a/backend/src/integrations/apps.ts b/backend/src/integrations/apps.ts index e3b78c4811..02a1904c70 100644 --- a/backend/src/integrations/apps.ts +++ b/backend/src/integrations/apps.ts @@ -9,14 +9,9 @@ import { INTEGRATION_GITHUB, INTEGRATION_HEROKU_API_URL, INTEGRATION_VERCEL_API_URL, - INTEGRATION_NETLIFY_API_URL, - INTEGRATION_GITHUB_API_URL + INTEGRATION_NETLIFY_API_URL } from '../variables'; -interface GitHubApp { - name: string; -} - /** * Return list of names of apps for integration named [integration] * @param {Object} obj @@ -47,6 +42,7 @@ const getApps = async ({ break; case INTEGRATION_VERCEL: apps = await getAppsVercel({ + integrationAuth, accessToken }); break; @@ -110,17 +106,28 @@ const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => { * @returns {Object[]} apps - names of Vercel apps * @returns {String} apps.name - name of Vercel app */ -const getAppsVercel = async ({ accessToken }: { accessToken: string }) => { +const getAppsVercel = async ({ + integrationAuth, + accessToken +}: { + integrationAuth: IIntegrationAuth; + accessToken: string; +}) => { let apps; try { const res = ( await axios.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, { headers: { Authorization: `Bearer ${accessToken}` + }, + ...( integrationAuth?.teamId ? { + params: { + teamId: integrationAuth.teamId } + } : {}) }) ).data; - + apps = res.projects.map((a: any) => ({ name: a.name })); diff --git a/backend/src/integrations/exchange.ts b/backend/src/integrations/exchange.ts index 3e70761187..26aca5fdb2 100644 --- a/backend/src/integrations/exchange.ts +++ b/backend/src/integrations/exchange.ts @@ -8,8 +8,7 @@ import { INTEGRATION_HEROKU_TOKEN_URL, INTEGRATION_VERCEL_TOKEN_URL, INTEGRATION_NETLIFY_TOKEN_URL, - INTEGRATION_GITHUB_TOKEN_URL, - INTEGRATION_GITHUB_API_URL + INTEGRATION_GITHUB_TOKEN_URL } from '../variables'; import { SITE_URL, diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts index 30628fb9a5..c4a4f16eea 100644 --- a/backend/src/integrations/sync.ts +++ b/backend/src/integrations/sync.ts @@ -12,14 +12,10 @@ import { INTEGRATION_GITHUB, INTEGRATION_HEROKU_API_URL, INTEGRATION_VERCEL_API_URL, - INTEGRATION_NETLIFY_API_URL, - INTEGRATION_GITHUB_API_URL + INTEGRATION_NETLIFY_API_URL } from '../variables'; import { access, appendFile } from 'fs'; -// TODO: need a helper function in the future to handle integration -// envar priorities (i.e. prioritize secrets within integration or those on Infisical) - /** * Sync/push [secrets] to [app] in integration named [integration] * @param {Object} obj @@ -53,6 +49,7 @@ const syncSecrets = async ({ case INTEGRATION_VERCEL: await syncSecretsVercel({ integration, + integrationAuth, secrets, accessToken }); @@ -139,10 +136,12 @@ const syncSecretsHeroku = async ({ */ const syncSecretsVercel = async ({ integration, + integrationAuth, secrets, accessToken }: { integration: IIntegration, + integrationAuth: IIntegrationAuth, secrets: any; accessToken: string; }) => { @@ -158,9 +157,12 @@ const syncSecretsVercel = async ({ try { // Get all (decrypted) secrets back from Vercel in // decrypted format - const params = new URLSearchParams({ - decrypt: "true" - }); + const params: { [key: string]: string } = { + decrypt: 'true', + ...( integrationAuth?.teamId ? { + teamId: integrationAuth.teamId + } : {}) + } const res = (await Promise.all((await axios.get( `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, @@ -177,10 +179,10 @@ const syncSecretsVercel = async ({ .map(async (secret: VercelSecret) => (await axios.get( `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, { - headers: { - Authorization: `Bearer ${accessToken}` - } - + params, + headers: { + Authorization: `Bearer ${accessToken}` + } } )).data) )).reduce((obj: any, secret: any) => ({ @@ -236,9 +238,10 @@ const syncSecretsVercel = async ({ `${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`, newSecrets, { - headers: { - Authorization: `Bearer ${accessToken}` - } + params, + headers: { + Authorization: `Bearer ${accessToken}` + } } ); } @@ -254,9 +257,10 @@ const syncSecretsVercel = async ({ `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, updatedSecret, { - headers: { - Authorization: `Bearer ${accessToken}` - } + params, + headers: { + Authorization: `Bearer ${accessToken}` + } } ); }); @@ -268,17 +272,18 @@ const syncSecretsVercel = async ({ await axios.delete( `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, { - headers: { - Authorization: `Bearer ${accessToken}` - } + params, + headers: { + Authorization: `Bearer ${accessToken}` + } } ); }); } } catch (err) { - Sentry.setUser(null); - Sentry.captureException(err); - throw new Error('Failed to sync secrets to Vercel'); + Sentry.setUser(null); + Sentry.captureException(err); + throw new Error('Failed to sync secrets to Vercel'); } } diff --git a/backend/src/variables/index.ts b/backend/src/variables/index.ts index 16c068925c..4f7ffd8b0f 100644 --- a/backend/src/variables/index.ts +++ b/backend/src/variables/index.ts @@ -19,7 +19,6 @@ import { INTEGRATION_HEROKU_API_URL, INTEGRATION_VERCEL_API_URL, INTEGRATION_NETLIFY_API_URL, - INTEGRATION_GITHUB_API_URL, INTEGRATION_OPTIONS } from './integration'; import { @@ -66,7 +65,6 @@ export { INTEGRATION_HEROKU_API_URL, INTEGRATION_VERCEL_API_URL, INTEGRATION_NETLIFY_API_URL, - INTEGRATION_GITHUB_API_URL, EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS, ACTION_ADD_SECRETS, diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index 00e817c572..1625c39543 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -32,7 +32,6 @@ const INTEGRATION_GITHUB_TOKEN_URL = const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com'; const INTEGRATION_VERCEL_API_URL = 'https://api.vercel.com'; const INTEGRATION_NETLIFY_API_URL = 'https://api.netlify.com'; -const INTEGRATION_GITHUB_API_URL = 'https://api.github.com'; const INTEGRATION_OPTIONS = [ { @@ -134,6 +133,5 @@ export { INTEGRATION_HEROKU_API_URL, INTEGRATION_VERCEL_API_URL, INTEGRATION_NETLIFY_API_URL, - INTEGRATION_GITHUB_API_URL, INTEGRATION_OPTIONS }; diff --git a/frontend/pages/dashboard/[id].tsx b/frontend/pages/dashboard/[id].tsx index cb3717f683..4ce91002d3 100644 --- a/frontend/pages/dashboard/[id].tsx +++ b/frontend/pages/dashboard/[id].tsx @@ -222,6 +222,7 @@ export default function Dashboard() { dataToSort?.map((item) => item.key).indexOf(item) ).includes(row.key) && row.type == 'shared'))?.map((item) => item.id) ) + setIsLoading(false); } catch (error) { console.log('Error', error); diff --git a/frontend/pages/integrations/[id].js b/frontend/pages/integrations/[id].js index 32ff8d2d94..2a5460dfbd 100644 --- a/frontend/pages/integrations/[id].js +++ b/frontend/pages/integrations/[id].js @@ -132,9 +132,6 @@ export default function Integrations() { * @returns */ const handleIntegrationOption = async ({ integrationOption }) => { - - console.log('handleIntegrationOption', integrationOption); - try { // generate CSRF token for OAuth2 code-token exchange integrations const state = crypto.randomBytes(16).toString("hex"); @@ -218,7 +215,7 @@ export default function Integrations() { handleIntegrationOption={handleIntegrationOption} /> */} - {cloudIntegrationOptions.length > 0 ? ( + {(cloudIntegrationOptions.length > 0 && bot) ? ( Date: Wed, 28 Dec 2022 23:42:17 +0530 Subject: [PATCH 46/89] feat(#31): implemented api for environment crud operations --- backend/src/app.ts | 2 + .../v1/integrationAuthController.ts | 8 +- .../src/controllers/v1/secretController.ts | 10 +- .../controllers/v1/serviceTokenController.ts | 4 +- .../controllers/v2/environmentController.ts | 167 ++++++++++++++++++ backend/src/controllers/v2/index.ts | 4 +- .../src/controllers/v2/workspaceController.ts | 9 +- backend/src/helpers/integration.ts | 8 +- backend/src/models/integration.ts | 5 - backend/src/models/secret.ts | 5 - backend/src/models/serviceToken.ts | 4 - backend/src/models/workspace.ts | 32 +++- backend/src/routes/v2/environment.ts | 54 ++++++ backend/src/routes/v2/index.ts | 6 +- backend/src/services/IntegrationService.ts | 12 +- 15 files changed, 293 insertions(+), 37 deletions(-) create mode 100644 backend/src/controllers/v2/environmentController.ts create mode 100644 backend/src/routes/v2/environment.ts diff --git a/backend/src/app.ts b/backend/src/app.ts index aa7ac9b28e..571b84cd46 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -46,6 +46,7 @@ import { workspace as v2WorkspaceRouter, serviceTokenData as v2ServiceTokenDataRouter, apiKeyData as v2APIKeyDataRouter, + environment as v2EnvironmentRouter, } from './routes/v2'; import { healthCheck } from './routes/status'; @@ -108,6 +109,7 @@ app.use('/api/v2/secret', v2SecretRouter); // stop supporting, TODO: revise app.use('/api/v2/secrets', v2SecretsRouter); app.use('/api/v2/service-token', v2ServiceTokenDataRouter); // TODO: turn into plural route app.use('/api/v2/api-key-data', v2APIKeyDataRouter); +app.use('/api/v2/environments', v2EnvironmentRouter); // api docs app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile)) diff --git a/backend/src/controllers/v1/integrationAuthController.ts b/backend/src/controllers/v1/integrationAuthController.ts index 95d0066ae7..b2f93a8c29 100644 --- a/backend/src/controllers/v1/integrationAuthController.ts +++ b/backend/src/controllers/v1/integrationAuthController.ts @@ -31,11 +31,17 @@ export const oAuthExchange = async ( if (!INTEGRATION_SET.has(integration)) throw new Error('Failed to validate integration'); + + const environments = req.membership.workspace?.environments || []; + if(environments.length === 0){ + throw new Error("Failed to get environments") + } await IntegrationService.handleOAuthExchange({ workspaceId, integration, - code + code, + environment: environments[0].slug, }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/secretController.ts b/backend/src/controllers/v1/secretController.ts index 1b756ecc70..c76e5e8833 100644 --- a/backend/src/controllers/v1/secretController.ts +++ b/backend/src/controllers/v1/secretController.ts @@ -9,7 +9,6 @@ import { import { pushKeys } from '../../helpers/key'; import { eventPushSecrets } from '../../events'; import { EventService } from '../../services'; -import { ENV_SET } from '../../variables'; import { postHogClient } from '../../services'; interface PushSecret { @@ -44,7 +43,8 @@ export const pushSecrets = async (req: Request, res: Response) => { const { workspaceId } = req.params; // validate environment - if (!ENV_SET.has(environment)) { + const workspaceEnvs = req.membership.workspace.environments; + if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { throw new Error('Failed to validate environment'); } @@ -116,7 +116,8 @@ export const pullSecrets = async (req: Request, res: Response) => { const { workspaceId } = req.params; // validate environment - if (!ENV_SET.has(environment)) { + const workspaceEnvs = req.membership.workspace.environments; + if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { throw new Error('Failed to validate environment'); } @@ -183,7 +184,8 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => { const { workspaceId } = req.params; // validate environment - if (!ENV_SET.has(environment)) { + const workspaceEnvs = req.membership.workspace.environments; + if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { throw new Error('Failed to validate environment'); } diff --git a/backend/src/controllers/v1/serviceTokenController.ts b/backend/src/controllers/v1/serviceTokenController.ts index 244a587837..3fafb90433 100644 --- a/backend/src/controllers/v1/serviceTokenController.ts +++ b/backend/src/controllers/v1/serviceTokenController.ts @@ -1,7 +1,6 @@ import { Request, Response } from 'express'; import { ServiceToken } from '../../models'; import { createToken } from '../../helpers/auth'; -import { ENV_SET } from '../../variables'; import { JWT_SERVICE_SECRET } from '../../config'; /** @@ -36,7 +35,8 @@ export const createServiceToken = async (req: Request, res: Response) => { } = req.body; // validate environment - if (!ENV_SET.has(environment)) { + const workspaceEnvs = req.membership.workspace.environments; + if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { throw new Error('Failed to validate environment'); } diff --git a/backend/src/controllers/v2/environmentController.ts b/backend/src/controllers/v2/environmentController.ts new file mode 100644 index 0000000000..77c4fcb591 --- /dev/null +++ b/backend/src/controllers/v2/environmentController.ts @@ -0,0 +1,167 @@ +import { Request, Response } from 'express'; +import * as Sentry from '@sentry/node'; +import { Secret, ServiceToken, Workspace, Integration } from '../../models'; + +/** + * Create new workspace environment named [environmentName] under workspace with id + * @param req + * @param res + * @returns + */ +export const createWorkspaceEnvironment = async ( + req: Request, + res: Response +) => { + const { workspaceId, environmentName, environmentSlug } = req.body; + try { + // atomic create the environment + const workspace = await Workspace.findOneAndUpdate( + { + _id: workspaceId, + 'environments.slug': { $ne: environmentSlug }, + 'environments.name': { $ne: environmentName }, + }, + { + $addToSet: { + environments: { name: environmentName, slug: environmentSlug }, + }, + } + ); + + if (!workspace) { + throw new Error('Failed to update workspace environment'); + } + } catch (err) { + Sentry.setUser({ email: req.user.email }); + Sentry.captureException(err); + return res.status(400).send({ + message: 'Failed to create new workspace environment', + }); + } + + return res.status(200).send({ + message: 'Successfully created new environment', + workspace: workspaceId, + environment: { + name: environmentName, + slug: environmentSlug, + }, + }); +}; + +/** + * Rename workspace environment with new name and slug of a workspace with [workspaceId] + * Old slug [oldEnvironmentSlug] must be provided + * @param req + * @param res + * @returns + */ +export const renameWorkspaceEnvironment = async ( + req: Request, + res: Response +) => { + const { workspaceId, environmentName, environmentSlug, oldEnvironmentSlug } = + req.body; + try { + // user should pass both new slug and env name + if (!environmentSlug || !environmentName) { + throw new Error('Invalid environment given.'); + } + + // atomic update the env to avoid conflict + const workspace = await Workspace.findOneAndUpdate( + { _id: workspaceId, 'environments.slug': oldEnvironmentSlug }, + { + 'environments.$.name': environmentName, + 'environments.$.slug': environmentSlug, + } + ); + if (!workspace) { + throw new Error('Failed to update workspace'); + } + + await Secret.updateMany( + { workspace: workspaceId, environment: oldEnvironmentSlug }, + { environment: environmentSlug } + ); + await ServiceToken.updateMany( + { workspace: workspaceId, environment: oldEnvironmentSlug }, + { environment: environmentSlug } + ); + await Integration.updateMany( + { workspace: workspaceId, environment: oldEnvironmentSlug }, + { environment: environmentSlug } + ); + } catch (err) { + Sentry.setUser({ email: req.user.email }); + Sentry.captureException(err); + return res.status(400).send({ + message: 'Failed to update workspace environment', + }); + } + + return res.status(200).send({ + message: 'Successfully update environment', + workspace: workspaceId, + environment: { + name: environmentName, + slug: environmentSlug, + }, + }); +}; + +/** + * Delete workspace environment by [environmentSlug] of workspace [workspaceId] and do the clean up + * @param req + * @param res + * @returns + */ +export const deleteWorkspaceEnvironment = async ( + req: Request, + res: Response +) => { + const { workspaceId, environmentSlug } = req.body; + try { + // atomic delete the env in the workspacce + const workspace = await Workspace.findOneAndUpdate( + { _id: workspaceId }, + { + $pull: { + environments: { + slug: environmentSlug, + }, + }, + } + ); + if (!workspace) { + throw new Error('Failed to delete workspace environment'); + } + + // clean up + await Secret.deleteMany({ + workspace: workspaceId, + environment: environmentSlug, + }); + await ServiceToken.deleteMany({ + workspace: workspaceId, + environment: environmentSlug, + }); + await Integration.deleteMany({ + workspace: workspaceId, + environment: environmentSlug, + }); + + } catch (err) { + Sentry.setUser({ email: req.user.email }); + Sentry.captureException(err); + return res.status(400).send({ + message: 'Failed to delete workspace environment', + }); + } + + return res.status(200).send({ + message: 'Successfully deleted environment', + workspace: workspaceId, + environment: environmentSlug, + }); +}; diff --git a/backend/src/controllers/v2/index.ts b/backend/src/controllers/v2/index.ts index 1651c09ee5..601314a2ad 100644 --- a/backend/src/controllers/v2/index.ts +++ b/backend/src/controllers/v2/index.ts @@ -3,11 +3,13 @@ import * as serviceTokenDataController from './serviceTokenDataController'; import * as apiKeyDataController from './apiKeyDataController'; import * as secretController from './secretController'; import * as secretsController from './secretsController'; +import * as environmentController from './environmentController'; export { workspaceController, serviceTokenDataController, apiKeyDataController, secretController, - secretsController + secretsController, + environmentController } diff --git a/backend/src/controllers/v2/workspaceController.ts b/backend/src/controllers/v2/workspaceController.ts index 0dbdfa0769..efac159a59 100644 --- a/backend/src/controllers/v2/workspaceController.ts +++ b/backend/src/controllers/v2/workspaceController.ts @@ -19,7 +19,6 @@ import { import { pushKeys } from '../../helpers/key'; import { postHogClient, EventService } from '../../services'; import { eventPushSecrets } from '../../events'; -import { ENV_SET } from '../../variables'; interface V2PushSecret { type: string; // personal or shared @@ -52,7 +51,8 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => { const { workspaceId } = req.params; // validate environment - if (!ENV_SET.has(environment)) { + const workspaceEnvs = req.membership.workspace.environments; + if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { throw new Error('Failed to validate environment'); } @@ -129,6 +129,11 @@ export const pullSecrets = async (req: Request, res: Response) => { } else if (req.serviceTokenData) { userId = req.serviceTokenData.user._id } + // validate environment + const workspaceEnvs = req.membership.workspace.environments; + if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) { + throw new Error('Failed to validate environment'); + } secrets = await pull({ userId, diff --git a/backend/src/helpers/integration.ts b/backend/src/helpers/integration.ts index d92156ece0..f602f17291 100644 --- a/backend/src/helpers/integration.ts +++ b/backend/src/helpers/integration.ts @@ -7,8 +7,6 @@ import { import { exchangeCode, exchangeRefresh, syncSecrets } from '../integrations'; import { BotService } from '../services'; import { - ENV_DEV, - EVENT_PUSH_SECRETS, INTEGRATION_VERCEL, INTEGRATION_NETLIFY } from '../variables'; @@ -36,11 +34,13 @@ interface Update { const handleOAuthExchangeHelper = async ({ workspaceId, integration, - code + code, + environment }: { workspaceId: string; integration: string; code: string; + environment: string; }) => { let action; let integrationAuth; @@ -102,9 +102,9 @@ const handleOAuthExchangeHelper = async ({ // initialize new integration after exchange await new Integration({ workspace: workspaceId, - environment: ENV_DEV, isActive: false, app: null, + environment, integration, integrationAuth: integrationAuth._id }).save(); diff --git a/backend/src/models/integration.ts b/backend/src/models/integration.ts index 6da699216e..0b184ed67a 100644 --- a/backend/src/models/integration.ts +++ b/backend/src/models/integration.ts @@ -1,9 +1,5 @@ import { Schema, model, Types } from 'mongoose'; import { - ENV_DEV, - ENV_TESTING, - ENV_STAGING, - ENV_PROD, INTEGRATION_HEROKU, INTEGRATION_VERCEL, INTEGRATION_NETLIFY, @@ -32,7 +28,6 @@ const integrationSchema = new Schema( }, environment: { type: String, - enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD], required: true }, isActive: { diff --git a/backend/src/models/secret.ts b/backend/src/models/secret.ts index a01b92d80e..6887c8b0f6 100644 --- a/backend/src/models/secret.ts +++ b/backend/src/models/secret.ts @@ -2,10 +2,6 @@ import { Schema, model, Types } from 'mongoose'; import { SECRET_SHARED, SECRET_PERSONAL, - ENV_DEV, - ENV_TESTING, - ENV_STAGING, - ENV_PROD } from '../variables'; export interface ISecret { @@ -53,7 +49,6 @@ const secretSchema = new Schema( }, environment: { type: String, - enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD], required: true }, secretKeyCiphertext: { diff --git a/backend/src/models/serviceToken.ts b/backend/src/models/serviceToken.ts index b5a2f4ec97..9d91b076ea 100644 --- a/backend/src/models/serviceToken.ts +++ b/backend/src/models/serviceToken.ts @@ -1,7 +1,4 @@ import { Schema, model, Types } from 'mongoose'; -import { ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD } from '../variables'; - -// TODO: deprecate export interface IServiceToken { _id: Types.ObjectId; name: string; @@ -33,7 +30,6 @@ const serviceTokenSchema = new Schema( }, environment: { type: String, - enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD], required: true }, expiresAt: { diff --git a/backend/src/models/workspace.ts b/backend/src/models/workspace.ts index 1e886c5239..fa7dbc8b58 100644 --- a/backend/src/models/workspace.ts +++ b/backend/src/models/workspace.ts @@ -4,6 +4,10 @@ export interface IWorkspace { _id: Types.ObjectId; name: string; organization: Types.ObjectId; + environments: Array<{ + name: string; + slug: string; + }>; } const workspaceSchema = new Schema({ @@ -15,7 +19,33 @@ const workspaceSchema = new Schema({ type: Schema.Types.ObjectId, ref: 'Organization', required: true - } + }, + environments: { + type: [ + { + name: String, + slug: String, + }, + ], + default: [ + { + name: "development", + slug: "dev" + }, + { + name: "test", + slug: "test" + }, + { + name: "staging", + slug: "staging" + }, + { + name: "production", + slug: "prod" + } + ], + }, }); const Workspace = model('Workspace', workspaceSchema); diff --git a/backend/src/routes/v2/environment.ts b/backend/src/routes/v2/environment.ts new file mode 100644 index 0000000000..592a512482 --- /dev/null +++ b/backend/src/routes/v2/environment.ts @@ -0,0 +1,54 @@ +import express from 'express'; +const router = express.Router(); +import { body, param } from 'express-validator'; +import { environmentController } from '../../controllers/v2'; +import { + requireAuth, + requireWorkspaceAuth, + validateRequest, +} from '../../middleware'; +import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../variables'; + +router.post( + '/:workspaceId', + requireAuth, + requireWorkspaceAuth({ + acceptedRoles: [ADMIN, MEMBER], + acceptedStatuses: [COMPLETED, GRANTED], + }), + param('workspaceId').exists().trim(), + body('environmentSlug').exists().trim(), + body('environmentName').exists().trim(), + validateRequest, + environmentController.createWorkspaceEnvironment +); + +router.put( + '/:workspaceId', + requireAuth, + requireWorkspaceAuth({ + acceptedRoles: [ADMIN, MEMBER], + acceptedStatuses: [COMPLETED, GRANTED], + }), + param('workspaceId').exists().trim(), + body('environmentSlug').exists().trim(), + body('environmentName').exists().trim(), + body('oldEnvironmentSlug').exists().trim(), + validateRequest, + environmentController.renameWorkspaceEnvironment +); + +router.delete( + '/:workspaceId', + requireAuth, + requireWorkspaceAuth({ + acceptedRoles: [ADMIN], + acceptedStatuses: [GRANTED], + }), + param('workspaceId').exists().trim(), + body('environmentSlug').exists().trim(), + validateRequest, + environmentController.deleteWorkspaceEnvironment +); + +export default router; diff --git a/backend/src/routes/v2/index.ts b/backend/src/routes/v2/index.ts index 8bea42620b..2740c35304 100644 --- a/backend/src/routes/v2/index.ts +++ b/backend/src/routes/v2/index.ts @@ -3,11 +3,13 @@ import secrets from './secrets'; import workspace from './workspace'; import serviceTokenData from './serviceTokenData'; import apiKeyData from './apiKeyData'; +import environment from "./environment" export { secret, secrets, workspace, serviceTokenData, - apiKeyData -} + apiKeyData, + environment +} \ No newline at end of file diff --git a/backend/src/services/IntegrationService.ts b/backend/src/services/IntegrationService.ts index 32f5f5a88b..43746aee4a 100644 --- a/backend/src/services/IntegrationService.ts +++ b/backend/src/services/IntegrationService.ts @@ -11,10 +11,6 @@ import { setIntegrationAuthAccessHelper, } from '../helpers/integration'; import { exchangeCode } from '../integrations'; -import { - ENV_DEV, - EVENT_PUSH_SECRETS -} from '../variables'; // should sync stuff be here too? Probably. // TODO: move bot functions to IntegrationService. @@ -32,22 +28,26 @@ class IntegrationService { * - Create bot sequence for integration * @param {Object} obj * @param {String} obj.workspaceId - id of workspace + * @param {String} obj.environment - workspace environment * @param {String} obj.integration - name of integration * @param {String} obj.code - code */ static async handleOAuthExchange({ workspaceId, integration, - code + code, + environment }: { workspaceId: string; integration: string; code: string; + environment: string; }) { await handleOAuthExchangeHelper({ workspaceId, integration, - code + code, + environment }); } From 9116bf33441422deee8fe1d2b9b0a33f1cc36c8b Mon Sep 17 00:00:00 2001 From: akhilmhdh Date: Tue, 10 Jan 2023 19:39:19 +0530 Subject: [PATCH 47/89] feat(ui): implemented ui for env management table --- .../basic/dialog/AddEnvironmentDialog.tsx | 127 +++++++++ .../basic/dialog/DeleteActionModal.tsx | 100 +++++++ .../basic/table/EnvironmentsTable.tsx | 118 +++++++++ frontend/hooks/index.ts | 1 + frontend/hooks/usePopUp.tsx | 69 +++++ frontend/pages/settings/project/[id].tsx | 245 ++++++++++++++++++ frontend/tsconfig.json | 1 + 7 files changed, 661 insertions(+) create mode 100644 frontend/components/basic/dialog/AddEnvironmentDialog.tsx create mode 100644 frontend/components/basic/dialog/DeleteActionModal.tsx create mode 100644 frontend/components/basic/table/EnvironmentsTable.tsx create mode 100644 frontend/hooks/index.ts create mode 100644 frontend/hooks/usePopUp.tsx create mode 100644 frontend/pages/settings/project/[id].tsx diff --git a/frontend/components/basic/dialog/AddEnvironmentDialog.tsx b/frontend/components/basic/dialog/AddEnvironmentDialog.tsx new file mode 100644 index 0000000000..83b578d81b --- /dev/null +++ b/frontend/components/basic/dialog/AddEnvironmentDialog.tsx @@ -0,0 +1,127 @@ +import { FormEventHandler, Fragment, useEffect, useState } from 'react'; +import { Dialog, Transition } from '@headlessui/react'; + +import Button from '../buttons/Button'; +import InputField from '../InputField'; + +type FormFields = { name: string; slug: string }; + +type Props = { + isOpen?: boolean; + isEditMode?: boolean; + // on edit mode load up initial values + initialValues?: FormFields; + onClose: () => void; + onSubmit: (envName: string, envSlug: string) => void; +}; + +/** + * The dialog modal for when the user wants to create a new workspace + * @param {*} param0 + * @returns + */ +export const AddEnvironmentDialog = ({ isOpen, onClose, onSubmit, initialValues, isEditMode }: Props) => { + const [formInput, setFormInput] = useState({ + name: '', + slug: '', + }); + + // This use effect can be removed when the unmount is happening from outside the component + // When unmount happens outside state gets unmounted also + useEffect(() => { + setFormInput(initialValues || { name: '', slug: '' }); + }, [isOpen]); + + // REFACTOR: Move to react-hook-form with yup for better form management + const onInputChange = (fieldName: string, fieldValue: string) => { + setFormInput((state) => ({ ...state, [fieldName]: fieldValue })); + }; + + const onFormSubmit: FormEventHandler = (e) => { + e.preventDefault(); + console.log(formInput); + }; + + return ( +
+ + + +
+ + +
+
+ + + + {isEditMode + ? 'Update environment' + : 'Create a new environment'} + +
+
+ onInputChange('name', val)} + type='varName' + value={formInput.name} + placeholder='' + isRequired + // error={error.length > 0} + // errorText={error} + /> +
+
+ onInputChange('slug', val)} + type='varName' + value={formInput.slug} + placeholder='' + isRequired + // error={error.length > 0} + // errorText={error} + /> +
+

+ Slugs are shorthands used in cli to access environment +

+
+
+
+
+
+
+
+
+
+
+ ); +}; diff --git a/frontend/components/basic/dialog/DeleteActionModal.tsx b/frontend/components/basic/dialog/DeleteActionModal.tsx new file mode 100644 index 0000000000..cafd832aeb --- /dev/null +++ b/frontend/components/basic/dialog/DeleteActionModal.tsx @@ -0,0 +1,100 @@ +import { Fragment, useState } from 'react'; +import { Dialog, Transition } from '@headlessui/react'; + +import InputField from '../InputField'; + +// REFACTOR: Move all these modals into one reusable one +type Props = { + isOpen?: boolean; + onClose: ()=>void; + title: string; + onSubmit:()=>void; + deleteKey?:string; +} + +const DeleteActionModal = ({ + isOpen, + onClose, + title, + onSubmit, + deleteKey +}:Props) => { + const [deleteInputField, setDeleteInputField] = useState("") + + return ( +
+ + + +
+ +
+
+ + + + {title} + +
+

+ This action is irrevertible. +

+
+
+ setDeleteInputField(val)} + value={deleteInputField} + type='text' + /> +
+
+ + +
+
+
+
+
+
+
+
+ ); +}; + +export default DeleteActionModal; diff --git a/frontend/components/basic/table/EnvironmentsTable.tsx b/frontend/components/basic/table/EnvironmentsTable.tsx new file mode 100644 index 0000000000..501b7f9bc2 --- /dev/null +++ b/frontend/components/basic/table/EnvironmentsTable.tsx @@ -0,0 +1,118 @@ +import { faPencil,faPlus,faX } from '@fortawesome/free-solid-svg-icons'; + +import { usePopUp } from '../../../hooks/usePopUp'; +import Button from '../buttons/Button'; +import {AddEnvironmentDialog} from '../dialog/AddEnvironmentDialog'; +import DeleteActionModal from '../dialog/DeleteActionModal'; + +const EnvironmentTable = ({ data = [] }) => { + const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ + 'createUpdateEnv', + 'deleteEnv', + ] as const); + + return ( + <> +
+
+

Project Environments

+

+ Choose which environments will show up in your dashboard like + development, staging, production +

+

+ Note: the text in slugs shows how these environmant should be + accessed in CLI. +

+
+
+
+
+
+
+ + + + + + + + + + {data?.length > 0 ? ( + data.map(({ name, slug }) => { + return ( + + + + + + ); + }) + ) : ( + + + + )} + +
NameSlug
+ {name} + + {slug} + +
+
+
+
+
+ No environmants found +
+ handlePopUpClose('deleteEnv')} + onSubmit={() => handlePopUpClose('deleteEnv')} + /> + handlePopUpClose('createUpdateEnv')} + onSubmit={() => null} + /> +
+ + ); +}; + +export default EnvironmentTable; diff --git a/frontend/hooks/index.ts b/frontend/hooks/index.ts new file mode 100644 index 0000000000..dcea2eb7c4 --- /dev/null +++ b/frontend/hooks/index.ts @@ -0,0 +1 @@ +export { usePopUp } from './usePopUp'; diff --git a/frontend/hooks/usePopUp.tsx b/frontend/hooks/usePopUp.tsx new file mode 100644 index 0000000000..eb0835b495 --- /dev/null +++ b/frontend/hooks/usePopUp.tsx @@ -0,0 +1,69 @@ +import { useCallback, useState } from 'react'; + +interface usePopUpProps { + name: Readonly; + isOpen: boolean; +} + +/** + * to provide better intellisense + * checks which type of inputProps were given and converts them into key-names + * SIDENOTE: On inputting give it as const and not string with (as const) + */ +type usePopUpState | usePopUpProps[]> = { + [P in T extends usePopUpProps[] ? T[number]['name'] : T[number]]: { + isOpen: boolean; + data?: unknown; + }; +}; + +interface usePopUpReturn | usePopUpProps[]> { + popUp: usePopUpState; + handlePopUpOpen: (popUpName: keyof usePopUpState, data?: unknown) => void; + handlePopUpClose: (popUpName: keyof usePopUpState) => void; + handlePopUpToggle: (popUpName: keyof usePopUpState) => void; +} + +/** + * This hook is used to manage multiple popUps/modal/dialog in a page + * Provides api to open,close,toggle and also store temporary data for the popUp + * @param popUpNames: the names of popUp containers eg: ["popUp1","second"] or [{name:"popUp2",isOpen:bool}] + */ +export const usePopUp = | usePopUpProps[]>( + popUpNames: T +): usePopUpReturn => { + const [popUp, setPopUp] = useState>( + Object.fromEntries( + popUpNames.map((popUpName) => + typeof popUpName === 'string' + ? [popUpName, { isOpen: false }] + : [popUpName.name, { isOpen: popUpName.isOpen }] + ) // convert into an array of [[popUpName,state]] then into Object + ) as usePopUpState // to override generic string return type of the function + ); + + const handlePopUpOpen = useCallback( + (popUpName: keyof usePopUpState, data?: unknown) => { + setPopUp((popUp) => ({ ...popUp, [popUpName]: { isOpen: true, data } })); + }, + [] + ); + + const handlePopUpClose = useCallback((popUpName: keyof usePopUpState) => { + setPopUp((popUp) => ({ ...popUp, [popUpName]: { isOpen: false } })); + }, []); + + const handlePopUpToggle = useCallback((popUpName: keyof usePopUpState) => { + setPopUp((popUp) => ({ + ...popUp, + [popUpName]: { isOpen: !popUp[popUpName].isOpen }, + })); + }, []); + + return { + popUp, + handlePopUpOpen, + handlePopUpClose, + handlePopUpToggle, + }; +}; diff --git a/frontend/pages/settings/project/[id].tsx b/frontend/pages/settings/project/[id].tsx new file mode 100644 index 0000000000..4a64c3e655 --- /dev/null +++ b/frontend/pages/settings/project/[id].tsx @@ -0,0 +1,245 @@ +import { useEffect, useRef, useState } from "react"; +import Head from "next/head"; +import { useRouter } from "next/router"; +import { useTranslation } from "next-i18next"; +import { faCheck, faPlus } from "@fortawesome/free-solid-svg-icons"; + +import Button from "~/components/basic/buttons/Button"; +import AddServiceTokenDialog from "~/components/basic/dialog/AddServiceTokenDialog"; +import InputField from "~/components/basic/InputField"; +import EnvironmentTable from '~/components/basic/table/EnvironmentsTable'; +import ServiceTokenTable from "~/components/basic/table/ServiceTokenTable"; +import NavHeader from "~/components/navigation/NavHeader"; +import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps"; + +import getServiceTokens from "../../api/serviceToken/getServiceTokens"; +import deleteWorkspace from "../../api/workspace/deleteWorkspace"; +import getWorkspaces from "../../api/workspace/getWorkspaces"; +import renameWorkspace from "../../api/workspace/renameWorkspace"; + +export default function SettingsBasic() { + const [buttonReady, setButtonReady] = useState(false); + const router = useRouter(); + const [workspaceName, setWorkspaceName] = useState(""); + const [serviceTokens, setServiceTokens] = useState([]); + const [environments,setEnvironments] = useState([]); + const [workspaceToBeDeletedName, setWorkspaceToBeDeletedName] = useState(""); + const [isAddOpen, setIsAddOpen] = useState(false); + const [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] = useState(false); + + const { t } = useTranslation(); + + useEffect(async () => { + const userWorkspaces = await getWorkspaces(); + userWorkspaces.forEach((userWorkspace) => { + if (userWorkspace._id == router.query.id) { + setWorkspaceName(userWorkspace.name); + setEnvironments(userWorkspace.environments); + } + }); + const tempServiceTokens = await getServiceTokens({ + workspaceId: router.query.id, + }); + setServiceTokens(tempServiceTokens); + }, []); + + const modifyWorkspaceName = (newName) => { + setButtonReady(true); + setWorkspaceName(newName); + }; + + const submitChanges = (newWorkspaceName) => { + renameWorkspace(router.query.id, newWorkspaceName); + setButtonReady(false); + }; + + const closeAddServiceTokenModal = () => { + setIsAddServiceTokenDialogOpen(false); + }; + + /** + * This function deleted a workspace. + * It first checks if there is more than one workspace aviable. 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. + */ + const executeDeletingWorkspace = async () => { + const userWorkspaces = await getWorkspaces(); + + if (userWorkspaces.length > 1) { + if ( + userWorkspaces.filter( + (workspace) => workspace._id == router.query.id + )[0].name == workspaceToBeDeletedName + ) { + await deleteWorkspace(router.query.id); + const userWorkspaces = await getWorkspaces(); + router.push("/dashboard/" + userWorkspaces[0]._id); + } + } + }; + + return ( +
+ + + {t('common:head-title', { title: t('settings-project:title') })} + + + + +
+
+ +
+
+

+ {t('settings-project:title')} +

+

+ {t('settings-project:description')} +

+
+
+
+
+
+
+

+ {t('common:display-name')} +

+
+ +
+
+
+
+
+
+
+

+ {t('common:project-id')} +

+

+ {t('settings-project:project-id-description')} +

+

+ {t('settings-project:project-id-description2')} + {/* eslint-disable-next-line react/jsx-no-target-blank */} + + {t('settings-project:docs')} + +

+
+ +
+
+
+
+
+

+ {t('section-token:service-tokens')} +

+

+ {t('section-token:service-tokens-description')} +

+
+
+
+
+ +
+
+ +
+
+
+
+

+ {t('settings-project:danger-zone')} +

+

+ {t('settings-project:danger-zone-note')} +

+
+ +
+ +

+ {t('settings-project:delete-project-note')} +

+
+
+
+
+
+ ); +} + +SettingsBasic.requireAuth = true; + +export const getServerSideProps = getTranslatedServerSideProps([ + "settings", + "settings-project", + "section-token", +]); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index e936a69a2c..f51378ec30 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -3,6 +3,7 @@ "baseUrl": ".", "paths": { "~/components/*": ["components/*"], + "~/hooks/*": ["hooks/*"], "~/utilities/*": ["components/utilities/*"], "~/*": ["const"], "~/pages/*": ["pages/*"] From 84700308f5201b70c3680727de0d93eedf7ab32d Mon Sep 17 00:00:00 2001 From: akhilmhdh Date: Wed, 11 Jan 2023 23:09:34 +0530 Subject: [PATCH 48/89] feat(#31): implemented ui for multi env and integrated api with backend fix(#31): fixed all v2 release conflict --- backend/src/app.ts | 2 +- .../v1/integrationAuthController.ts | 2 +- .../controllers/v2/environmentController.ts | 121 ++- backend/src/ee/models/secretVersion.ts | 5 - backend/src/models/integration.ts | 2 +- backend/src/routes/v2/environment.ts | 25 +- backend/src/routes/v2/secrets.ts | 4 +- frontend/components/basic/Listbox.tsx | 2 +- .../basic/dialog/AddServiceTokenDialog.js | 164 ++-- ...log.tsx => AddUpdateEnvironmentDialog.tsx} | 26 +- .../basic/dialog/DeleteActionModal.tsx | 6 +- .../basic/table/EnvironmentsTable.tsx | 73 +- .../basic/table/ServiceTokenTable.tsx | 3 +- .../components/integrations/Integration.tsx | 10 +- .../utilities/secrets/downloadDotEnv.ts | 3 +- .../utilities/secrets/getSecretsForProject.ts | 6 +- .../api/environments/createEnvironment.ts | 29 + .../api/environments/deleteEnvironment.ts | 26 + .../api/environments/updateEnvironment.ts | 33 + frontend/pages/api/workspace/getWorkspaces.ts | 1 + frontend/pages/dashboard/[id].tsx | 829 +++++++++++------- frontend/pages/settings/project/[id].js | 306 ------- frontend/pages/settings/project/[id].tsx | 236 +++-- 23 files changed, 1057 insertions(+), 857 deletions(-) rename frontend/components/basic/dialog/{AddEnvironmentDialog.tsx => AddUpdateEnvironmentDialog.tsx} (87%) create mode 100644 frontend/pages/api/environments/createEnvironment.ts create mode 100644 frontend/pages/api/environments/deleteEnvironment.ts create mode 100644 frontend/pages/api/environments/updateEnvironment.ts delete mode 100644 frontend/pages/settings/project/[id].js diff --git a/backend/src/app.ts b/backend/src/app.ts index 571b84cd46..82275b9c9e 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -104,12 +104,12 @@ app.use('/api/v1/integration', v1IntegrationRouter); app.use('/api/v1/integration-auth', v1IntegrationAuthRouter); // v2 routes +app.use('/api/v2/workspace', v2EnvironmentRouter); app.use('/api/v2/workspace', v2WorkspaceRouter); // TODO: turn into plural route app.use('/api/v2/secret', v2SecretRouter); // stop supporting, TODO: revise app.use('/api/v2/secrets', v2SecretsRouter); app.use('/api/v2/service-token', v2ServiceTokenDataRouter); // TODO: turn into plural route app.use('/api/v2/api-key-data', v2APIKeyDataRouter); -app.use('/api/v2/environments', v2EnvironmentRouter); // api docs app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile)) diff --git a/backend/src/controllers/v1/integrationAuthController.ts b/backend/src/controllers/v1/integrationAuthController.ts index b2f93a8c29..5df8b4e910 100644 --- a/backend/src/controllers/v1/integrationAuthController.ts +++ b/backend/src/controllers/v1/integrationAuthController.ts @@ -3,7 +3,7 @@ import * as Sentry from '@sentry/node'; import axios from 'axios'; import { readFileSync } from 'fs'; import { IntegrationAuth, Integration } from '../../models'; -import { INTEGRATION_SET, INTEGRATION_OPTIONS, ENV_DEV } from '../../variables'; +import { INTEGRATION_SET, INTEGRATION_OPTIONS } from '../../variables'; import { IntegrationService } from '../../services'; import { getApps, revokeAccess } from '../../integrations'; diff --git a/backend/src/controllers/v2/environmentController.ts b/backend/src/controllers/v2/environmentController.ts index 77c4fcb591..1d1ffb6a69 100644 --- a/backend/src/controllers/v2/environmentController.ts +++ b/backend/src/controllers/v2/environmentController.ts @@ -1,6 +1,13 @@ import { Request, Response } from 'express'; import * as Sentry from '@sentry/node'; -import { Secret, ServiceToken, Workspace, Integration } from '../../models'; +import { + Secret, + ServiceToken, + Workspace, + Integration, + ServiceTokenData, +} from '../../models'; +import { SecretVersion } from '../../ee/models'; /** * Create new workspace environment named [environmentName] under workspace with id @@ -12,25 +19,24 @@ export const createWorkspaceEnvironment = async ( req: Request, res: Response ) => { - const { workspaceId, environmentName, environmentSlug } = req.body; + const { workspaceId } = req.params; + const { environmentName, environmentSlug } = req.body; try { - // atomic create the environment - const workspace = await Workspace.findOneAndUpdate( - { - _id: workspaceId, - 'environments.slug': { $ne: environmentSlug }, - 'environments.name': { $ne: environmentName }, - }, - { - $addToSet: { - environments: { name: environmentName, slug: environmentSlug }, - }, - } - ); - - if (!workspace) { - throw new Error('Failed to update workspace environment'); + const workspace = await Workspace.findById(workspaceId).exec(); + if ( + !workspace || + workspace?.environments.find( + ({ name, slug }) => slug === environmentSlug || environmentName === name + ) + ) { + throw new Error('Failed to create workspace environment'); } + + workspace?.environments.push({ + name: environmentName.toLowerCase(), + slug: environmentSlug.toLowerCase(), + }); + await workspace.save(); } catch (err) { Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); @@ -60,8 +66,8 @@ export const renameWorkspaceEnvironment = async ( req: Request, res: Response ) => { - const { workspaceId, environmentName, environmentSlug, oldEnvironmentSlug } = - req.body; + const { workspaceId } = req.params; + const { environmentName, environmentSlug, oldEnvironmentSlug } = req.body; try { // user should pass both new slug and env name if (!environmentSlug || !environmentName) { @@ -69,25 +75,47 @@ export const renameWorkspaceEnvironment = async ( } // atomic update the env to avoid conflict - const workspace = await Workspace.findOneAndUpdate( - { _id: workspaceId, 'environments.slug': oldEnvironmentSlug }, - { - 'environments.$.name': environmentName, - 'environments.$.slug': environmentSlug, - } - ); + const workspace = await Workspace.findById(workspaceId).exec(); if (!workspace) { - throw new Error('Failed to update workspace'); + throw new Error('Failed to create workspace environment'); + } + + const isEnvExist = workspace.environments.some( + ({ name, slug }) => + slug !== oldEnvironmentSlug && + (name === environmentName || slug === environmentSlug) + ); + if (isEnvExist) { + throw new Error('Invalid environment given'); + } + + const envIndex = workspace?.environments.findIndex( + ({ slug }) => slug === oldEnvironmentSlug + ); + if (envIndex === -1) { + throw new Error('Invalid environment given'); } + workspace.environments[envIndex].name = environmentName.toLowerCase(); + workspace.environments[envIndex].slug = environmentSlug.toLowerCase(); + + await workspace.save(); await Secret.updateMany( { workspace: workspaceId, environment: oldEnvironmentSlug }, { environment: environmentSlug } ); + await SecretVersion.updateMany( + { workspace: workspaceId, environment: oldEnvironmentSlug }, + { environment: environmentSlug } + ); await ServiceToken.updateMany( { workspace: workspaceId, environment: oldEnvironmentSlug }, { environment: environmentSlug } ); + await ServiceTokenData.updateMany( + { workspace: workspaceId, environment: oldEnvironmentSlug }, + { environment: environmentSlug } + ); await Integration.updateMany( { workspace: workspaceId, environment: oldEnvironmentSlug }, { environment: environmentSlug } @@ -120,37 +148,46 @@ export const deleteWorkspaceEnvironment = async ( req: Request, res: Response ) => { - const { workspaceId, environmentSlug } = req.body; + const { workspaceId } = req.params; + const { environmentSlug } = req.body; try { - // atomic delete the env in the workspacce - const workspace = await Workspace.findOneAndUpdate( - { _id: workspaceId }, - { - $pull: { - environments: { - slug: environmentSlug, - }, - }, - } - ); + // atomic update the env to avoid conflict + const workspace = await Workspace.findById(workspaceId).exec(); if (!workspace) { - throw new Error('Failed to delete workspace environment'); + throw new Error('Failed to create workspace environment'); + } + + const envIndex = workspace?.environments.findIndex( + ({ slug }) => slug === environmentSlug + ); + if (envIndex === -1) { + throw new Error('Invalid environment given'); } + workspace.environments.splice(envIndex, 1); + await workspace.save(); + // clean up await Secret.deleteMany({ workspace: workspaceId, environment: environmentSlug, }); + await SecretVersion.deleteMany({ + workspace: workspaceId, + environment: environmentSlug, + }); await ServiceToken.deleteMany({ workspace: workspaceId, environment: environmentSlug, }); + await ServiceTokenData.deleteMany({ + workspace: workspaceId, + environment: environmentSlug, + }); await Integration.deleteMany({ workspace: workspaceId, environment: environmentSlug, }); - } catch (err) { Sentry.setUser({ email: req.user.email }); Sentry.captureException(err); diff --git a/backend/src/ee/models/secretVersion.ts b/backend/src/ee/models/secretVersion.ts index 616d44fbdc..a4fec0a39d 100644 --- a/backend/src/ee/models/secretVersion.ts +++ b/backend/src/ee/models/secretVersion.ts @@ -2,10 +2,6 @@ import { Schema, model, Types } from 'mongoose'; import { SECRET_SHARED, SECRET_PERSONAL, - ENV_DEV, - ENV_TESTING, - ENV_STAGING, - ENV_PROD } from '../../variables'; export interface ISecretVersion { @@ -56,7 +52,6 @@ const secretVersionSchema = new Schema( }, environment: { type: String, - enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD], required: true }, isDeleted: { // consider removing field diff --git a/backend/src/models/integration.ts b/backend/src/models/integration.ts index 0b184ed67a..98e4934d90 100644 --- a/backend/src/models/integration.ts +++ b/backend/src/models/integration.ts @@ -9,7 +9,7 @@ import { export interface IIntegration { _id: Types.ObjectId; workspace: Types.ObjectId; - environment: 'dev' | 'test' | 'staging' | 'prod'; + environment: string; isActive: boolean; app: string; target: string; diff --git a/backend/src/routes/v2/environment.ts b/backend/src/routes/v2/environment.ts index 592a512482..924db18fcd 100644 --- a/backend/src/routes/v2/environment.ts +++ b/backend/src/routes/v2/environment.ts @@ -1,4 +1,4 @@ -import express from 'express'; +import express, { Response, Request } from 'express'; const router = express.Router(); import { body, param } from 'express-validator'; import { environmentController } from '../../controllers/v2'; @@ -7,14 +7,15 @@ import { requireWorkspaceAuth, validateRequest, } from '../../middleware'; -import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../variables'; +import { ADMIN, MEMBER } from '../../variables'; router.post( - '/:workspaceId', - requireAuth, + '/:workspaceId/environments', + requireAuth({ + acceptedAuthModes: ['jwt'], + }), requireWorkspaceAuth({ acceptedRoles: [ADMIN, MEMBER], - acceptedStatuses: [COMPLETED, GRANTED], }), param('workspaceId').exists().trim(), body('environmentSlug').exists().trim(), @@ -24,11 +25,12 @@ router.post( ); router.put( - '/:workspaceId', - requireAuth, + '/:workspaceId/environments', + requireAuth({ + acceptedAuthModes: ['jwt'], + }), requireWorkspaceAuth({ acceptedRoles: [ADMIN, MEMBER], - acceptedStatuses: [COMPLETED, GRANTED], }), param('workspaceId').exists().trim(), body('environmentSlug').exists().trim(), @@ -39,11 +41,12 @@ router.put( ); router.delete( - '/:workspaceId', - requireAuth, + '/:workspaceId/environments', + requireAuth({ + acceptedAuthModes: ['jwt'], + }), requireWorkspaceAuth({ acceptedRoles: [ADMIN], - acceptedStatuses: [GRANTED], }), param('workspaceId').exists().trim(), body('environmentSlug').exists().trim(), diff --git a/backend/src/routes/v2/secrets.ts b/backend/src/routes/v2/secrets.ts index 29eac042fe..4196cd8195 100644 --- a/backend/src/routes/v2/secrets.ts +++ b/backend/src/routes/v2/secrets.ts @@ -18,7 +18,7 @@ import { router.post( '/', body('workspaceId').exists().isString().trim(), - body('environment').exists().isString().trim().isIn(['dev', 'staging', 'prod', 'test']), + body('environment').exists().isString().trim(), body('secrets') .exists() .custom((value) => { @@ -73,7 +73,7 @@ router.post( router.get( '/', query('workspaceId').exists().trim(), - query('environment').exists().trim().isIn(['dev', 'staging', 'prod', 'test']), + query('environment').exists().trim(), validateRequest, requireAuth({ acceptedAuthModes: ['jwt', 'serviceToken'] diff --git a/frontend/components/basic/Listbox.tsx b/frontend/components/basic/Listbox.tsx index 95e3f33c64..d315df2e62 100644 --- a/frontend/components/basic/Listbox.tsx +++ b/frontend/components/basic/Listbox.tsx @@ -69,7 +69,7 @@ export default function ListBox({ - `my-0.5 relative cursor-default select-none py-2 pl-10 pr-4 rounded-md ${ + `my-0.5 relative cursor-default select-none py-2 pl-10 pr-4 rounded-md capitalize ${ selected ? 'bg-white/10 text-gray-400 font-bold' : '' } ${ active && !selected diff --git a/frontend/components/basic/dialog/AddServiceTokenDialog.js b/frontend/components/basic/dialog/AddServiceTokenDialog.js index 177911973a..a8c5c15a74 100644 --- a/frontend/components/basic/dialog/AddServiceTokenDialog.js +++ b/frontend/components/basic/dialog/AddServiceTokenDialog.js @@ -8,7 +8,6 @@ import nacl from "tweetnacl"; import addServiceToken from "~/pages/api/serviceToken/addServiceToken"; import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; -import { envMapping } from "../../../public/data/frequentConstants"; import { decryptAssymmetric, encryptAssymmetric, @@ -34,11 +33,12 @@ const AddServiceTokenDialog = ({ workspaceId, workspaceName, serviceTokens, + environments, setServiceTokens }) => { const [serviceToken, setServiceToken] = useState(""); const [serviceTokenName, setServiceTokenName] = useState(""); - const [serviceTokenEnv, setServiceTokenEnv] = useState("Development"); + const [selectedServiceTokenEnv, setSelectedServiceTokenEnv] = useState(environments?.[0]); const [serviceTokenExpiresIn, setServiceTokenExpiresIn] = useState("1 day"); const [serviceTokenCopied, setServiceTokenCopied] = useState(false); const { t } = useTranslation(); @@ -66,7 +66,7 @@ const AddServiceTokenDialog = ({ let newServiceToken = await addServiceToken({ name: serviceTokenName, workspaceId, - environment: envMapping[serviceTokenEnv], + environment: selectedServiceTokenEnv.slug, expiresIn: expiryMapping[serviceTokenExpiresIn], encryptedKey: ciphertext, iv, @@ -101,155 +101,159 @@ const AddServiceTokenDialog = ({ }; return ( -
+
- + -
+
-
-
+
+
- {serviceToken == "" ? ( - + {serviceToken == '' ? ( + - {t("section-token:add-dialog.title", { + {t('section-token:add-dialog.title', { target: workspaceName, })} -
-
-

- {t("section-token:add-dialog.description")} +

+
+

+ {t('section-token:add-dialog.description')}

-
+
-
+
name)} + onChange={(envName) => + setSelectedServiceTokenEnv( + environments.find( + ({ name }) => envName === name + ) || { + name: 'unknown', + slug: 'unknown', + } + ) + } isFull={true} - text={`${t("common:environment")}: `} + text={`${t('common:environment')}: `} />
-
+
-
-
+
+
) : ( - + - {t("section-token:add-dialog.copy-service-token")} + {t('section-token:add-dialog.copy-service-token')} -
-
-

+

+
+

{t( - "section-token:add-dialog.copy-service-token-description" + 'section-token:add-dialog.copy-service-token-description' )}

-
-
+
+
-
+
{serviceToken}
-
+
- - {t("common:click-to-copy")} + + {t('common:click-to-copy')}
-
+
diff --git a/frontend/components/basic/dialog/AddEnvironmentDialog.tsx b/frontend/components/basic/dialog/AddUpdateEnvironmentDialog.tsx similarity index 87% rename from frontend/components/basic/dialog/AddEnvironmentDialog.tsx rename to frontend/components/basic/dialog/AddUpdateEnvironmentDialog.tsx index 83b578d81b..9476dd8f65 100644 --- a/frontend/components/basic/dialog/AddEnvironmentDialog.tsx +++ b/frontend/components/basic/dialog/AddUpdateEnvironmentDialog.tsx @@ -12,15 +12,24 @@ type Props = { // on edit mode load up initial values initialValues?: FormFields; onClose: () => void; - onSubmit: (envName: string, envSlug: string) => void; + onCreateSubmit: (data: FormFields) => void; + onEditSubmit: (data: FormFields) => void; }; +// TODO: Migrate to better form management and validation. Preferable react-hook-form + yup /** * The dialog modal for when the user wants to create a new workspace * @param {*} param0 * @returns */ -export const AddEnvironmentDialog = ({ isOpen, onClose, onSubmit, initialValues, isEditMode }: Props) => { +export const AddUpdateEnvironmentDialog = ({ + isOpen, + onClose, + onCreateSubmit, + onEditSubmit, + initialValues, + isEditMode, +}: Props) => { const [formInput, setFormInput] = useState({ name: '', slug: '', @@ -39,7 +48,15 @@ export const AddEnvironmentDialog = ({ isOpen, onClose, onSubmit, initialValues, const onFormSubmit: FormEventHandler = (e) => { e.preventDefault(); - console.log(formInput); + const data = { + name: formInput.name.toLowerCase(), + slug: formInput.slug.toLowerCase(), + }; + if (isEditMode) { + onEditSubmit(data); + return; + } + onCreateSubmit(data); }; return ( @@ -81,7 +98,7 @@ export const AddEnvironmentDialog = ({ isOpen, onClose, onSubmit, initialValues,
onInputChange('name', val)} type='varName' value={formInput.name} @@ -112,6 +129,7 @@ export const AddEnvironmentDialog = ({ isOpen, onClose, onSubmit, initialValues, type='submit' color='mineshaft' text={isEditMode ? 'Update' : 'Create'} + active={formInput.name !== '' && formInput.slug !== ''} size='md' />
diff --git a/frontend/components/basic/dialog/DeleteActionModal.tsx b/frontend/components/basic/dialog/DeleteActionModal.tsx index cafd832aeb..9a1b7a46af 100644 --- a/frontend/components/basic/dialog/DeleteActionModal.tsx +++ b/frontend/components/basic/dialog/DeleteActionModal.tsx @@ -1,4 +1,4 @@ -import { Fragment, useState } from 'react'; +import { Fragment, useEffect, useState } from 'react'; import { Dialog, Transition } from '@headlessui/react'; import InputField from '../InputField'; @@ -21,6 +21,10 @@ const DeleteActionModal = ({ }:Props) => { const [deleteInputField, setDeleteInputField] = useState("") + useEffect(() => { + setDeleteInputField(""); + }, [isOpen]); + return (
diff --git a/frontend/components/basic/table/EnvironmentsTable.tsx b/frontend/components/basic/table/EnvironmentsTable.tsx index 501b7f9bc2..56536b679e 100644 --- a/frontend/components/basic/table/EnvironmentsTable.tsx +++ b/frontend/components/basic/table/EnvironmentsTable.tsx @@ -1,15 +1,61 @@ -import { faPencil,faPlus,faX } from '@fortawesome/free-solid-svg-icons'; +import { faPencil, faPlus, faX } from '@fortawesome/free-solid-svg-icons'; -import { usePopUp } from '../../../hooks/usePopUp'; +import { usePopUp } from '../../../hooks/usePopUp'; import Button from '../buttons/Button'; -import {AddEnvironmentDialog} from '../dialog/AddEnvironmentDialog'; +import { AddUpdateEnvironmentDialog } from '../dialog/AddUpdateEnvironmentDialog'; import DeleteActionModal from '../dialog/DeleteActionModal'; -const EnvironmentTable = ({ data = [] }) => { - const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ - 'createUpdateEnv', - 'deleteEnv', - ] as const); +type Env = { name: string; slug: string }; + +type Props = { + data: Env[]; + onCreateEnv: (arg0: Env) => Promise; + onUpdateEnv: (oldSlug: string, arg0: Env) => Promise; + onDeleteEnv: (slug: string) => Promise; +}; + +const EnvironmentTable = ({ + data = [], + onCreateEnv, + onDeleteEnv, + onUpdateEnv, +}: Props) => { + const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ + 'createUpdateEnv', + 'deleteEnv', + ] as const); + + const onEnvCreateCB = async (env: Env) => { + try { + await onCreateEnv(env); + handlePopUpClose('createUpdateEnv'); + } catch (error) { + console.error(error); + } + }; + + const onEnvUpdateCB = async (env: Env) => { + try { + await onUpdateEnv( + (popUp.createUpdateEnv?.data as Pick)?.slug, + env + ); + handlePopUpClose('createUpdateEnv'); + } catch (error) { + console.error(error); + } + }; + + const onEnvDeleteCB = async () => { + try { + await onDeleteEnv( + (popUp.deleteEnv?.data as Pick)?.slug + ); + handlePopUpClose('deleteEnv'); + } catch (error) { + console.error(error); + } + }; return ( <> @@ -62,7 +108,9 @@ const EnvironmentTable = ({ data = [] }) => {
diff --git a/frontend/components/basic/table/ServiceTokenTable.tsx b/frontend/components/basic/table/ServiceTokenTable.tsx index 412a0dbfbc..4d30b30139 100644 --- a/frontend/components/basic/table/ServiceTokenTable.tsx +++ b/frontend/components/basic/table/ServiceTokenTable.tsx @@ -3,7 +3,6 @@ import { faX } from '@fortawesome/free-solid-svg-icons'; import { useNotificationContext } from '~/components/context/Notifications/NotificationProvider'; import deleteServiceToken from "../../../pages/api/serviceToken/deleteServiceToken"; -import { reverseEnvMapping } from '../../../public/data/frequentConstants'; import guidGenerator from '../../utilities/randomId'; import Button from '../buttons/Button'; @@ -60,7 +59,7 @@ const ServiceTokenTable = ({ data, workspaceName, setServiceTokens }: ServiceTok {workspaceName} - {reverseEnvMapping[row.environment]} + {row.environment} {new Date(row.expiresAt).toUTCString()} diff --git a/frontend/components/integrations/Integration.tsx b/frontend/components/integrations/Integration.tsx index 11edf7cb70..73d6ee0dc8 100644 --- a/frontend/components/integrations/Integration.tsx +++ b/frontend/components/integrations/Integration.tsx @@ -15,9 +15,7 @@ import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps" import updateIntegration from "../../pages/api/integrations/updateIntegration" import { contextNetlifyMapping, - envMapping, reverseContextNetlifyMapping, - reverseEnvMapping, } from "../../public/data/frequentConstants"; interface Integration { @@ -41,9 +39,7 @@ const Integration = ({ }: { integration: Integration; }) => { - const [integrationEnvironment, setIntegrationEnvironment] = useState( - reverseEnvMapping[integration.environment] - ); + const [integrationEnvironment, setIntegrationEnvironment] = useState(integration.environment); const [fileState, setFileState] = useState([]); const router = useRouter(); const [apps, setApps] = useState([]); // integration app objects @@ -199,9 +195,9 @@ const Integration = ({ const siteApp = apps.find((app) => app.name === integrationApp); // obj or undefined const siteId = siteApp?.siteId ? siteApp.siteId : null; - const result = await updateIntegration({ + await updateIntegration({ integrationId: integration._id, - environment: envMapping[integrationEnvironment], + environment: integrationEnvironment, app: integrationApp, isActive: true, target: integrationTarget ? integrationTarget.toLowerCase() : null, diff --git a/frontend/components/utilities/secrets/downloadDotEnv.ts b/frontend/components/utilities/secrets/downloadDotEnv.ts index dbb29497b1..4a84aa17aa 100644 --- a/frontend/components/utilities/secrets/downloadDotEnv.ts +++ b/frontend/components/utilities/secrets/downloadDotEnv.ts @@ -1,4 +1,3 @@ -import { envMapping } from "../../../public/data/frequentConstants"; import checkOverrides from './checkOverrides'; @@ -39,7 +38,7 @@ const downloadDotEnv = async ({ data, env }: { data: SecretDataProps[]; env: str const fileDownloadUrl = URL.createObjectURL(blob); const alink = document.createElement('a'); alink.href = fileDownloadUrl; - alink.download = envMapping[env] + '.env'; + alink.download = env + '.env'; alink.click(); } diff --git a/frontend/components/utilities/secrets/getSecretsForProject.ts b/frontend/components/utilities/secrets/getSecretsForProject.ts index 81de27be09..629ac98ee3 100644 --- a/frontend/components/utilities/secrets/getSecretsForProject.ts +++ b/frontend/components/utilities/secrets/getSecretsForProject.ts @@ -1,8 +1,6 @@ import getSecrets from '~/pages/api/files/GetSecrets'; import getLatestFileKey from '~/pages/api/workspace/getLatestFileKey'; -import { envMapping } from '../../../public/data/frequentConstants'; - const { decryptAssymmetric, decryptSymmetric @@ -35,7 +33,7 @@ interface SecretProps { } interface FunctionProps { - env: keyof typeof envMapping; + env: string; setIsKeyAvailable: any; setData: any; workspaceId: string; @@ -58,7 +56,7 @@ const getSecretsForProject = async ({ try { let encryptedSecrets; try { - encryptedSecrets = await getSecrets(workspaceId, envMapping[env]); + encryptedSecrets = await getSecrets(workspaceId, env); } catch (error) { console.log('ERROR: Not able to access the latest version of secrets'); } diff --git a/frontend/pages/api/environments/createEnvironment.ts b/frontend/pages/api/environments/createEnvironment.ts new file mode 100644 index 0000000000..2d0ecfa379 --- /dev/null +++ b/frontend/pages/api/environments/createEnvironment.ts @@ -0,0 +1,29 @@ +import SecurityClient from '~/utilities/SecurityClient'; + +type NewEnvironmentInfo = { + environmentSlug: string; + environmentName: string; +}; + +/** + * This route deletes a specified workspace. + * @param {*} workspaceId + * @returns + */ +const createEnvironment = (workspaceId:string, newEnv: NewEnvironmentInfo) => { + return SecurityClient.fetchCall(`/api/v2/workspace/${workspaceId}/environments`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(newEnv) + }).then(async (res) => { + if (res && res.status == 200) { + return res; + } else { + console.log('Failed to create environment'); + } + }); +}; + +export default createEnvironment; diff --git a/frontend/pages/api/environments/deleteEnvironment.ts b/frontend/pages/api/environments/deleteEnvironment.ts new file mode 100644 index 0000000000..de86115321 --- /dev/null +++ b/frontend/pages/api/environments/deleteEnvironment.ts @@ -0,0 +1,26 @@ +import SecurityClient from '~/utilities/SecurityClient'; +/** + * This route deletes a specified env. + * @param {*} workspaceId + * @returns + */ +const deleteEnvironment = (workspaceId: string, environmentSlug: string) => { + return SecurityClient.fetchCall( + `/api/v2/workspace/${workspaceId}/environments`, + { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ environmentSlug }), + } + ).then(async (res) => { + if (res && res.status == 200) { + return res; + } else { + console.log('Failed to delete environment'); + } + }); +}; + +export default deleteEnvironment; diff --git a/frontend/pages/api/environments/updateEnvironment.ts b/frontend/pages/api/environments/updateEnvironment.ts new file mode 100644 index 0000000000..65fb449a8b --- /dev/null +++ b/frontend/pages/api/environments/updateEnvironment.ts @@ -0,0 +1,33 @@ +import SecurityClient from '~/utilities/SecurityClient'; + +type EnvironmentInfo = { + oldEnvironmentSlug: string; + environmentSlug: string; + environmentName: string; +}; + +/** + * This route updates a specified environment. + * @param {*} workspaceId + * @returns + */ +const updateEnvironment = (workspaceId: string, env: EnvironmentInfo) => { + return SecurityClient.fetchCall( + `/api/v2/workspace/${workspaceId}/environments`, + { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(env), + } + ).then(async (res) => { + if (res && res.status == 200) { + return res; + } else { + console.log('Failed to update environment'); + } + }); +}; + +export default updateEnvironment; diff --git a/frontend/pages/api/workspace/getWorkspaces.ts b/frontend/pages/api/workspace/getWorkspaces.ts index 1bbb42c7a7..d77c4c431b 100644 --- a/frontend/pages/api/workspace/getWorkspaces.ts +++ b/frontend/pages/api/workspace/getWorkspaces.ts @@ -5,6 +5,7 @@ interface Workspace { _id: string; name: string; organization: string; + environments: Array<{name:string, slug:string}> } /** diff --git a/frontend/pages/dashboard/[id].tsx b/frontend/pages/dashboard/[id].tsx index 4ce91002d3..e20ee73c72 100644 --- a/frontend/pages/dashboard/[id].tsx +++ b/frontend/pages/dashboard/[id].tsx @@ -2,7 +2,7 @@ import { Fragment, useCallback, useEffect, useState } from 'react'; import Head from 'next/head'; import Image from 'next/image'; import { useRouter } from 'next/router'; -import { useTranslation } from "next-i18next"; +import { useTranslation } from 'next-i18next'; import { faArrowDownAZ, faArrowDownZA, @@ -34,7 +34,6 @@ import getSecretsForProject from '~/components/utilities/secrets/getSecretsForPr import { getTranslatedServerSideProps } from '~/components/utilities/withTranslateProps'; import guidGenerator from '~/utilities/randomId'; -import { envMapping, reverseEnvMapping } from '../../public/data/frequentConstants'; import addSecrets from '../api/files/AddSecrets'; import deleteSecrets from '../api/files/DeleteSecrets'; import updateSecrets from '../api/files/UpdateSecrets'; @@ -43,6 +42,10 @@ import checkUserAction from '../api/userActions/checkUserAction'; import registerUserAction from '../api/userActions/registerUserAction'; import getWorkspaces from '../api/workspace/getWorkspaces'; +type WorkspaceEnv = { + name: string; + slug: string; +}; interface SecretDataProps { type: 'personal' | 'shared'; @@ -68,7 +71,7 @@ interface SnapshotProps { secretVersions: { id: string; pos: number; - type: "personal" | "shared"; + type: 'personal' | 'shared'; environment: string; key: string; value: string; @@ -99,14 +102,11 @@ function findDuplicates(arr: any[]) { */ export default function Dashboard() { const [data, setData] = useState(); - const [initialData, setInitialData] = useState([]); + const [initialData, setInitialData] = useState([]); const [buttonReady, setButtonReady] = useState(false); const router = useRouter(); - const [workspaceId, setWorkspaceId] = useState(''); const [blurred, setBlurred] = useState(true); const [isKeyAvailable, setIsKeyAvailable] = useState(true); - const [env, setEnv] = useState('Development'); - const [snapshotEnv, setSnapshotEnv] = useState('Development'); const [isNew, setIsNew] = useState(false); const [isLoading, setIsLoading] = useState(false); const [searchKeys, setSearchKeys] = useState(''); @@ -114,7 +114,7 @@ export default function Dashboard() { const [sortMethod, setSortMethod] = useState('alphabetical'); const [checkDocsPopUpVisible, setCheckDocsPopUpVisible] = useState(false); const [hasUserEverPushed, setHasUserEverPushed] = useState(false); - const [sidebarSecretId, toggleSidebar] = useState("None"); + const [sidebarSecretId, toggleSidebar] = useState('None'); const [PITSidebarOpen, togglePITSidebar] = useState(false); const [sharedToHide, setSharedToHide] = useState([]); const [snapshotData, setSnapshotData] = useState(); @@ -123,6 +123,16 @@ export default function Dashboard() { const { t } = useTranslation(); const { createNotification } = useNotificationContext(); + const workspaceId = router.query.id as string; + const [workspaceEnvs, setWorkspaceEnvs] = useState([]); + + const [selectedSnapshotEnv, setSelectedSnapshotEnv] = + useState(); + const [selectedEnv, setSelectedEnv] = useState({ + name: '', + slug: '', + }); + // #TODO: fix save message for changing reroutes // const beforeRouteHandler = (url) => { // const warningText = @@ -169,25 +179,37 @@ export default function Dashboard() { useEffect(() => { (async () => { try { - const tempNumSnapshots = await getProjectSercetSnapshotsCount({ workspaceId: String(router.query.id) }) + const tempNumSnapshots = await getProjectSercetSnapshotsCount({ + workspaceId, + }); setNumSnapshots(tempNumSnapshots); const userWorkspaces = await getWorkspaces(); - const listWorkspaces = userWorkspaces.map((workspace) => workspace._id); - if ( - !listWorkspaces.includes(router.asPath.split('/')[2]) - ) { - router.push('/dashboard/' + listWorkspaces[0]); + const workspace = userWorkspaces.find( + (workspace) => workspace._id === workspaceId + ); + if (!workspace) { + router.push('/dashboard/' + userWorkspaces?.[0]?._id); } + setWorkspaceEnvs(workspace?.environments || []); + // set env + const env = workspace?.environments?.[0] || { + name: 'unknown', + slug: 'unkown', + }; + setSelectedEnv(env); + setSelectedSnapshotEnv(env); const user = await getUser(); setIsNew( - (Date.parse(String(new Date())) - Date.parse(user.createdAt)) / 60000 < 3 + (Date.parse(String(new Date())) - Date.parse(user.createdAt)) / + 60000 < + 3 ? true : false ); const userAction = await checkUserAction({ - action: 'first_time_secrets_pushed' + action: 'first_time_secrets_pushed', }); setHasUserEverPushed(userAction ? true : false); } catch (error) { @@ -202,13 +224,12 @@ export default function Dashboard() { try { setIsLoading(true); setBlurred(true); - setWorkspaceId(String(router.query.id)); - + // ENV const dataToSort = await getSecretsForProject({ - env, + env: selectedEnv.slug, setIsKeyAvailable, setData, - workspaceId: String(router.query.id) + workspaceId, }); setInitialData(dataToSort); reorderRows(dataToSort); @@ -230,7 +251,7 @@ export default function Dashboard() { } })(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [env]); + }, [selectedEnv]); const addRow = () => { setIsNew(false); @@ -243,17 +264,17 @@ export default function Dashboard() { value: '', type: 'shared', comment: '', - } + }, ]); }; /** * This function add an ovverrided version of a certain secret to the current user - * @param {object} obj + * @param {object} obj * @param {string} obj.id - if of this secret that is about to be overriden * @param {string} obj.keyName - key name of this secret * @param {string} obj.value - value of this secret - * @param {string} obj.pos - position of this secret on the dashboard + * @param {string} obj.pos - position of this secret on the dashboard */ const addOverride = ({ id, keyName, value, pos, comment }: overrideProps) => { setIsNew(false); @@ -265,20 +286,32 @@ export default function Dashboard() { key: keyName, value: value, type: 'personal', - comment: comment - } + comment: comment, + }, ]; - sortValuesHandler(tempdata, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical"); + sortValuesHandler( + tempdata, + sortMethod == 'alhpabetical' ? '-alphabetical' : 'alphabetical' + ); }; - const deleteRow = ({ ids, secretName }: { ids: string[]; secretName: string; }) => { + const deleteRow = ({ + ids, + secretName, + }: { + ids: string[]; + secretName: string; + }) => { setButtonReady(true); - toggleSidebar("None"); + toggleSidebar('None'); createNotification({ text: `${secretName} has been deleted. Remember to save changes.`, - type: 'error' + type: 'error', }); - sortValuesHandler(data!.filter((row: SecretDataProps) => !ids.includes(row.id)), sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical"); + sortValuesHandler( + data!.filter((row: SecretDataProps) => !ids.includes(row.id)), + sortMethod == 'alhpabetical' ? '-alphabetical' : 'alphabetical' + ); }; /** @@ -289,15 +322,30 @@ export default function Dashboard() { setButtonReady(true); // find which shared secret corresponds to the overriden version - const sharedVersionOfOverride = data!.filter(secret => secret.type == "shared" && secret.key == data!.filter(row => row.id == id)[0]?.key)[0]?.id; - + const sharedVersionOfOverride = data!.filter( + (secret) => + secret.type == 'shared' && + secret.key == data!.filter((row) => row.id == id)[0]?.key + )[0]?.id; + // change the sidebar to this shared secret; and unhide it - toggleSidebar(sharedVersionOfOverride) - setSharedToHide(sharedToHide!.filter(tempId => tempId != sharedVersionOfOverride)) + toggleSidebar(sharedVersionOfOverride); + setSharedToHide( + sharedToHide!.filter((tempId) => tempId != sharedVersionOfOverride) + ); // resort secrets - const tempData = data!.filter((row: SecretDataProps) => !(row.key == data!.filter(row => row.id == id)[0]?.key && row.type == 'personal')) - sortValuesHandler(tempData, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical") + const tempData = data!.filter( + (row: SecretDataProps) => + !( + row.key == data!.filter((row) => row.id == id)[0]?.key && + row.type == 'personal' + ) + ); + sortValuesHandler( + tempData, + sortMethod == 'alhpabetical' ? '-alphabetical' : 'alphabetical' + ); }; const modifyValue = (value: string, pos: number) => { @@ -351,59 +399,97 @@ export default function Dashboard() { const obj = Object.assign( {}, - ...newData!.map((row: SecretDataProps) => ({ [row.type.charAt(0) + row.key]: [row.value, row.comment ?? ''] })) + ...newData!.map((row: SecretDataProps) => ({ + [row.type.charAt(0) + row.key]: [row.value, row.comment ?? ''], + })) ); // Checking if any of the secret keys start with a number - if so, don't do anything const nameErrors = !Object.keys(obj) .map((key) => !isNaN(Number(key[0].charAt(0)))) .every((v) => v === false); - const duplicatesExist = findDuplicates(data!.map((item: SecretDataProps) => item.key + item.type)).length > 0; + const duplicatesExist = + findDuplicates(data!.map((item: SecretDataProps) => item.key + item.type)) + .length > 0; if (nameErrors) { return createNotification({ text: 'Solve all name errors before saving secrets.', - type: 'error' + type: 'error', }); } if (duplicatesExist) { return createNotification({ text: 'Remove duplicated secret names before saving.', - type: 'error' + type: 'error', }); } // Once "Save changes" is clicked, disable that button setButtonReady(false); - const secretsToBeDeleted - = initialData - .filter(initDataPoint => !newData!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id)) - .map(secret => secret.id); - - const secretsToBeAdded - = newData! - .filter(newDataPoint => !initialData.map(initDataPoint => initDataPoint.id).includes(newDataPoint.id)); - - const secretsToBeUpdated - = newData!.filter(newDataPoint => initialData - .filter(initDataPoint => newData!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id) - && (newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].value != initDataPoint.value - || newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].key != initDataPoint.key - || newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].comment != initDataPoint.comment)) - .map(secret => secret.id).includes(newDataPoint.id)); - + const secretsToBeDeleted = initialData + .filter( + (initDataPoint) => + !newData! + .map((newDataPoint) => newDataPoint.id) + .includes(initDataPoint.id) + ) + .map((secret) => secret.id); + + const secretsToBeAdded = newData!.filter( + (newDataPoint) => + !initialData + .map((initDataPoint) => initDataPoint.id) + .includes(newDataPoint.id) + ); + + const secretsToBeUpdated = newData!.filter((newDataPoint) => + initialData + .filter( + (initDataPoint) => + newData! + .map((newDataPoint) => newDataPoint.id) + .includes(initDataPoint.id) && + (newData!.filter( + (newDataPoint) => newDataPoint.id == initDataPoint.id + )[0].value != initDataPoint.value || + newData!.filter( + (newDataPoint) => newDataPoint.id == initDataPoint.id + )[0].key != initDataPoint.key || + newData!.filter( + (newDataPoint) => newDataPoint.id == initDataPoint.id + )[0].comment != initDataPoint.comment) + ) + .map((secret) => secret.id) + .includes(newDataPoint.id) + ); + if (secretsToBeDeleted.length > 0) { await deleteSecrets({ secretIds: secretsToBeDeleted }); } + // ENV if (secretsToBeAdded.length > 0) { - const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded, workspaceId, env: envMapping[env] }) - secrets && await addSecrets({ secrets, env: envMapping[env], workspaceId }); + const secrets = await encryptSecrets({ + secretsToEncrypt: secretsToBeAdded, + workspaceId, + env: selectedEnv.slug, + }); + secrets && + (await addSecrets({ + secrets, + env: selectedEnv.slug, + workspaceId, + })); } if (secretsToBeUpdated.length > 0) { - const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeUpdated, workspaceId, env: envMapping[env] }) - secrets && await updateSecrets({ secrets }); + const secrets = await encryptSecrets({ + secretsToEncrypt: secretsToBeUpdated, + workspaceId, + env: selectedEnv.slug, + }); + secrets && (await updateSecrets({ secrets })); } // If this user has never saved environment variables before, show them a prompt to read docs @@ -425,299 +511,417 @@ export default function Dashboard() { setBlurred(!blurred); }; - const sortValuesHandler = (dataToSort: SecretDataProps[] | 1, specificSortMethod?: 'alphabetical' | '-alphabetical') => { - const howToSort = specificSortMethod == undefined ? sortMethod : specificSortMethod; + const sortValuesHandler = ( + dataToSort: SecretDataProps[] | 1, + specificSortMethod?: 'alphabetical' | '-alphabetical' + ) => { + const howToSort = + specificSortMethod == undefined ? sortMethod : specificSortMethod; const sortedData = (dataToSort != 1 ? dataToSort : data)! - .sort((a, b) => - howToSort == 'alphabetical' - ? a.key.localeCompare(b.key) - : b.key.localeCompare(a.key) - ) - .map((item: SecretDataProps, index: number) => { - return { - ...item, - pos: index - }; - }); + .sort((a, b) => + howToSort == 'alphabetical' + ? a.key.localeCompare(b.key) + : b.key.localeCompare(a.key) + ) + .map((item: SecretDataProps, index: number) => { + return { + ...item, + pos: index, + }; + }); setData(sortedData); }; - - const deleteCertainRow = ({ ids, secretName }: { ids: string[]; secretName: string; }) => { - deleteRow({ids, secretName}); + + const deleteCertainRow = ({ + ids, + secretName, + }: { + ids: string[]; + secretName: string; + }) => { + deleteRow({ ids, secretName }); }; return data ? ( -
+
- {t("common:head-title", { title: t("dashboard:title") })} - - - - + {t('common:head-title', { title: t('dashboard:title') })} + + + + -
- {sidebarSecretId != "None" && row.key == data.filter(row => row.id == sidebarSecretId)[0]?.key)} - modifyKey={listenChangeKey} - modifyValue={listenChangeValue} - modifyComment={listenChangeComment} - addOverride={addOverride} - deleteOverride={deleteOverride} - buttonReady={buttonReady} - savePush={savePush} - sharedToHide={sharedToHide} - setSharedToHide={setSharedToHide} - deleteRow={deleteCertainRow} - />} - {PITSidebarOpen && } -
- +
+ {sidebarSecretId != 'None' && ( + + row.key == + data.filter((row) => row.id == sidebarSecretId)[0]?.key + )} + modifyKey={listenChangeKey} + modifyValue={listenChangeValue} + modifyComment={listenChangeComment} + addOverride={addOverride} + deleteOverride={deleteOverride} + buttonReady={buttonReady} + savePush={savePush} + sharedToHide={sharedToHide} + setSharedToHide={setSharedToHide} + deleteRow={deleteCertainRow} + /> + )} + {PITSidebarOpen && ( + + )} +
+ {checkDocsPopUpVisible && ( )} -
- {snapshotData && -
-
} -
-
-

{snapshotData ? "Secret Snapshot" : t("dashboard:title")}

- {snapshotData && {new Date(snapshotData.createdAt).toLocaleString()}} +
+ {snapshotData && ( +
+
+ )} +
+
+

{snapshotData ? 'Secret Snapshot' : t('dashboard:title')}

+ {snapshotData && ( + + {new Date(snapshotData.createdAt).toLocaleString()} + + )}
{!snapshotData && data?.length == 0 && ( name)} + onChange={(envName) => + setSelectedEnv( + workspaceEnvs.find(({ name }) => envName === name) || { + name: 'unknown', + slug: 'unknown', + } + ) + } /> )}
-
+
{(data?.length !== 0 || buttonReady) && !snapshotData && (
+ )} + {snapshotData && ( +
+
)} - {snapshotData &&
-
}
-
-
-
+
+
+
{(snapshotData || data?.length !== 0) && ( <> - {!snapshotData - ? - : } -
+ {!snapshotData ? ( + name)} + onChange={(envName) => + setSelectedEnv( + workspaceEnvs.find( + ({ name }) => envName === name + ) || { + name: 'unknown', + slug: 'unknown', + } + ) + } + /> + ) : ( + name)} + onChange={(envName) => + setSelectedSnapshotEnv( + workspaceEnvs.find( + ({ name }) => envName === name + ) || { + name: 'unknown', + slug: 'unknown', + } + ) + } + /> + )} +
setSearchKeys(e.target.value)} - placeholder={String(t("dashboard:search-keys"))} + placeholder={String(t('dashboard:search-keys'))} />
- {!snapshotData &&
-
} - {!snapshotData &&
- -
} -
+ {!snapshotData && ( +
+
+ )} + {!snapshotData && ( +
+ +
+ )} +
- {!snapshotData &&
-
} + {!snapshotData && ( +
+
+ )} )}
{isLoading ? ( -
- infisical loading indicator -
- ) : ( - data?.length !== 0 ? ( -
+
+ infisical loading indicator +
+ ) : data?.length !== 0 ? ( +
-
- {!snapshotData && data?.filter(row => row.key?.toUpperCase().includes(searchKeys.toUpperCase())) - .filter(row => !(sharedToHide.includes(row.id) && row.type == 'shared')).map((keyPair) => ( - item.key + item.type) - )?.includes(keyPair.key + keyPair.type)} - toggleSidebar={toggleSidebar} - sidebarSecretId={sidebarSecretId} - isSnapshot={false} - /> - ))} - {snapshotData && snapshotData.secretVersions?.sort((a, b) => a.key.localeCompare(b.key)) - .filter(row => reverseEnvMapping[row.environment] == snapshotEnv) - .filter(row => row.key.toUpperCase().includes(searchKeys.toUpperCase())) - .filter(row => !(snapshotData.secretVersions?.filter(row => (snapshotData.secretVersions - ?.map((item) => item.key) - .filter( - (item, index) => - index !== - snapshotData.secretVersions?.map((item) => item.key).indexOf(item) - ).includes(row.key) && row.type == 'shared'))?.map((item) => item.id).includes(row.id) && row.type == 'shared')).map((keyPair) => ( - item.key + item.type) - )?.includes(keyPair.key + keyPair.type)} - toggleSidebar={toggleSidebar} - sidebarSecretId={sidebarSecretId} - isSnapshot={true} - /> - ))} +
+ {!snapshotData && + data + ?.filter((row) => + row.key + ?.toUpperCase() + .includes(searchKeys.toUpperCase()) + ) + .filter( + (row) => + !( + sharedToHide.includes(row.id) && + row.type == 'shared' + ) + ) + .map((keyPair) => ( + item.key + item.type) + )?.includes(keyPair.key + keyPair.type)} + toggleSidebar={toggleSidebar} + sidebarSecretId={sidebarSecretId} + isSnapshot={false} + /> + ))} + {snapshotData && + snapshotData.secretVersions + ?.sort((a, b) => a.key.localeCompare(b.key)) + .filter( + (row) => + row.environment == selectedSnapshotEnv?.slug + ) + .filter((row) => + row.key + .toUpperCase() + .includes(searchKeys.toUpperCase()) + ) + .filter( + (row) => + !( + snapshotData.secretVersions + ?.filter( + (row) => + snapshotData.secretVersions + ?.map((item) => item.key) + .filter( + (item, index) => + index !== + snapshotData.secretVersions + ?.map((item) => item.key) + .indexOf(item) + ) + .includes(row.key) && row.type == 'shared' + ) + ?.map((item) => item.id) + .includes(row.id) && row.type == 'shared' + ) + ) + .map((keyPair) => ( + item.key + item.type) + )?.includes(keyPair.key + keyPair.type)} + toggleSidebar={toggleSidebar} + sidebarSecretId={sidebarSecretId} + isSnapshot={true} + /> + ))}
- {!snapshotData &&
- -
} + {!snapshotData && ( +
+ +
+ )}
) : ( -
+
{isKeyAvailable && !snapshotData && ( )} - { - (!isKeyAvailable && ( - <> - -

- To view this file, contact your administrator for - permission. -

-

- They need to grant you access in the team tab. -

- - ))} + {!isKeyAvailable && ( + <> + +

+ To view this file, contact your administrator for + permission. +

+

+ They need to grant you access in the team tab. +

+ + )}
- ))} + )}
) : ( -
-
+
+
loading animation
); @@ -766,4 +969,4 @@ export default function Dashboard() { Dashboard.requireAuth = true; -export const getServerSideProps = getTranslatedServerSideProps(["dashboard"]); +export const getServerSideProps = getTranslatedServerSideProps(['dashboard']); diff --git a/frontend/pages/settings/project/[id].js b/frontend/pages/settings/project/[id].js deleted file mode 100644 index 3449df346a..0000000000 --- a/frontend/pages/settings/project/[id].js +++ /dev/null @@ -1,306 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import Head from "next/head"; -import { useRouter } from "next/router"; -import { useTranslation } from "next-i18next"; -import { faCheck, faCopy, faPlus } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -import Button from "~/components/basic/buttons/Button"; -import AddServiceTokenDialog from "~/components/basic/dialog/AddServiceTokenDialog"; -import InputField from "~/components/basic/InputField"; -import ServiceTokenTable from "~/components/basic/table/ServiceTokenTable.tsx"; -import NavHeader from "~/components/navigation/NavHeader"; -import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps"; - -import getServiceTokens from "../../api/serviceToken/getServiceTokens"; -import deleteWorkspace from "../../api/workspace/deleteWorkspace"; -import getWorkspaces from "../../api/workspace/getWorkspaces"; -import renameWorkspace from "../../api/workspace/renameWorkspace"; - - -export default function SettingsBasic() { - const [buttonReady, setButtonReady] = useState(false); - const router = useRouter(); - const [workspaceName, setWorkspaceName] = useState(""); - const [serviceTokens, setServiceTokens] = useState([]); - const [workspaceToBeDeletedName, setWorkspaceToBeDeletedName] = useState(""); - const [workspaceId, setWorkspaceId] = useState(""); - const [isAddOpen, setIsAddOpen] = useState(false); - let [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] = - useState(false); - const [projectIdCopied, setProjectIdCopied] = useState(false); - - const { t } = useTranslation(); - - /** - * This function copies the project id to the clipboard - */ - function copyToClipboard() { - // const copyText = document.getElementById('myInput') as HTMLInputElement; - const copyText = document.getElementById('myInput') - - if (copyText) { - copyText.select(); - copyText.setSelectionRange(0, 99999); // For mobile devices - - navigator.clipboard.writeText(copyText.value); - - setProjectIdCopied(true); - setTimeout(() => setProjectIdCopied(false), 2000); - } - } - - useEffect(async () => { - let userWorkspaces = await getWorkspaces(); - userWorkspaces.map((userWorkspace) => { - if (userWorkspace._id == router.query.id) { - setWorkspaceName(userWorkspace.name); - } - }); - let tempServiceTokens = await getServiceTokens({ - workspaceId: router.query.id, - }); - setServiceTokens(tempServiceTokens); - }, []); - - const modifyWorkspaceName = (newName) => { - setButtonReady(true); - setWorkspaceName(newName); - }; - - const submitChanges = (newWorkspaceName) => { - renameWorkspace(router.query.id, newWorkspaceName); - setButtonReady(false); - }; - - useEffect(async () => { - setWorkspaceId(router.query.id); - }, []); - - function closeAddModal() { - setIsAddOpen(false); - } - - function openAddModal() { - setIsAddOpen(true); - } - - const closeAddServiceTokenModal = () => { - setIsAddServiceTokenDialogOpen(false); - }; - - /** - * This function deleted a workspace. - * It first checks if there is more than one workspace aviable. 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. - */ - const executeDeletingWorkspace = async () => { - let userWorkspaces = await getWorkspaces(); - - if (userWorkspaces.length > 1) { - if ( - userWorkspaces.filter( - (workspace) => workspace._id == router.query.id - )[0].name == workspaceToBeDeletedName - ) { - await deleteWorkspace(router.query.id); - let userWorkspaces = await getWorkspaces(); - router.push("/dashboard/" + userWorkspaces[0]._id); - } - } - }; - - return ( -
- - - {t("common:head-title", { title: t("settings-project:title") })} - - - - -
-
- -
-
-

- {t("settings-project:title")} -

-

- {t("settings-project:description")} -

-
-
-
-
-
-
-

- {t("common:display-name")} -

-
- -
-
-
-
-
-
-
-

- {t("common:project-id")} -

-

- {t("settings-project:project-id-description")} -

-

- {t("settings-project:project-id-description2")} - {/* eslint-disable-next-line react/jsx-no-target-blank */} - - {t("settings-project:docs")} - -

-

{t("settings-project:auto-generated")}

-
-

{`${t( - "common:project-id" - )}:`}

- -
- - - {t("common:click-to-copy")} - -
-
-
-
-
-
-

- {t("section-token:service-tokens")} -

-

- {t("section-token:service-tokens-description")} -

-

- Please, make sure you are on the - - latest version of CLI - . -

-
-
-
-
- -
-
-
-
-

- {t("settings-project:danger-zone")} -

-

- {t("settings-project:danger-zone-note")} -

-
- -
- -

- {t("settings-project:delete-project-note")} -

-
-
-
-
-
- ); -} - -SettingsBasic.requireAuth = true; - -export const getServerSideProps = getTranslatedServerSideProps([ - "settings", - "settings-project", - "section-token", -]); diff --git a/frontend/pages/settings/project/[id].tsx b/frontend/pages/settings/project/[id].tsx index 4a64c3e655..619ef2e6be 100644 --- a/frontend/pages/settings/project/[id].tsx +++ b/frontend/pages/settings/project/[id].tsx @@ -1,55 +1,87 @@ -import { useEffect, useRef, useState } from "react"; -import Head from "next/head"; -import { useRouter } from "next/router"; -import { useTranslation } from "next-i18next"; -import { faCheck, faPlus } from "@fortawesome/free-solid-svg-icons"; - -import Button from "~/components/basic/buttons/Button"; -import AddServiceTokenDialog from "~/components/basic/dialog/AddServiceTokenDialog"; -import InputField from "~/components/basic/InputField"; +import { useEffect, useState } from 'react'; +import Head from 'next/head'; +import { useRouter } from 'next/router'; +import { useTranslation } from 'next-i18next'; +import { faCheck, faCopy, faPlus } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +import Button from '~/components/basic/buttons/Button'; +import AddServiceTokenDialog from '~/components/basic/dialog/AddServiceTokenDialog'; +import InputField from '~/components/basic/InputField'; import EnvironmentTable from '~/components/basic/table/EnvironmentsTable'; -import ServiceTokenTable from "~/components/basic/table/ServiceTokenTable"; -import NavHeader from "~/components/navigation/NavHeader"; -import { getTranslatedServerSideProps } from "~/utilities/withTranslateProps"; +import ServiceTokenTable from '~/components/basic/table/ServiceTokenTable'; +import NavHeader from '~/components/navigation/NavHeader'; +import deleteEnvironment from '~/pages/api/environments/deleteEnvironment'; +import updateEnvironment from '~/pages/api/environments/updateEnvironment'; +import { getTranslatedServerSideProps } from '~/utilities/withTranslateProps'; -import getServiceTokens from "../../api/serviceToken/getServiceTokens"; -import deleteWorkspace from "../../api/workspace/deleteWorkspace"; -import getWorkspaces from "../../api/workspace/getWorkspaces"; -import renameWorkspace from "../../api/workspace/renameWorkspace"; +import createEnvironment from '../../api/environments/createEnvironment'; +import getServiceTokens from '../../api/serviceToken/getServiceTokens'; +import deleteWorkspace from '../../api/workspace/deleteWorkspace'; +import getWorkspaces from '../../api/workspace/getWorkspaces'; +import renameWorkspace from '../../api/workspace/renameWorkspace'; +type EnvData = { + name: string; + slug: string; +}; export default function SettingsBasic() { const [buttonReady, setButtonReady] = useState(false); const router = useRouter(); - const [workspaceName, setWorkspaceName] = useState(""); + const [workspaceName, setWorkspaceName] = useState(''); const [serviceTokens, setServiceTokens] = useState([]); - const [environments,setEnvironments] = useState([]); - const [workspaceToBeDeletedName, setWorkspaceToBeDeletedName] = useState(""); + const [environments, setEnvironments] = useState>([]); + const [workspaceToBeDeletedName, setWorkspaceToBeDeletedName] = useState(''); const [isAddOpen, setIsAddOpen] = useState(false); - const [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] = useState(false); + const [isAddServiceTokenDialogOpen, setIsAddServiceTokenDialogOpen] = + useState(false); + const [projectIdCopied, setProjectIdCopied] = useState(false); + const workspaceId = router.query.id as string; const { t } = useTranslation(); - useEffect(async () => { - const userWorkspaces = await getWorkspaces(); - userWorkspaces.forEach((userWorkspace) => { - if (userWorkspace._id == router.query.id) { - setWorkspaceName(userWorkspace.name); - setEnvironments(userWorkspace.environments); - } - }); - const tempServiceTokens = await getServiceTokens({ - workspaceId: router.query.id, - }); - setServiceTokens(tempServiceTokens); + /** + * This function copies the project id to the clipboard + */ + function copyToClipboard() { + const copyText = document.getElementById('myInput') as HTMLInputElement; + + if (copyText) { + copyText.select(); + copyText.setSelectionRange(0, 99999); // For mobile devices + + navigator.clipboard.writeText(copyText.value); + + setProjectIdCopied(true); + setTimeout(() => setProjectIdCopied(false), 2000); + } + } + + useEffect(() => { + const load = async () => { + const userWorkspaces = await getWorkspaces(); + userWorkspaces.forEach((userWorkspace) => { + if (userWorkspace._id == workspaceId) { + setWorkspaceName(userWorkspace.name); + setEnvironments(userWorkspace.environments); + } + }); + const tempServiceTokens = await getServiceTokens({ + workspaceId, + }); + setServiceTokens(tempServiceTokens); + }; + + load(); }, []); - const modifyWorkspaceName = (newName) => { + const modifyWorkspaceName = (newName: string) => { setButtonReady(true); setWorkspaceName(newName); }; - const submitChanges = (newWorkspaceName) => { - renameWorkspace(router.query.id, newWorkspaceName); + const submitChanges = (newWorkspaceName: string) => { + renameWorkspace(workspaceId, newWorkspaceName); setButtonReady(false); }; @@ -69,16 +101,54 @@ export default function SettingsBasic() { if (userWorkspaces.length > 1) { if ( userWorkspaces.filter( - (workspace) => workspace._id == router.query.id + (workspace) => workspace._id === workspaceId )[0].name == workspaceToBeDeletedName ) { - await deleteWorkspace(router.query.id); + await deleteWorkspace(workspaceId); const userWorkspaces = await getWorkspaces(); - router.push("/dashboard/" + userWorkspaces[0]._id); + router.push('/dashboard/' + userWorkspaces[0]._id); } } }; + const onCreateEnvironment = async ({ name, slug }: EnvData) => { + const res = await createEnvironment(workspaceId, { + environmentName: name, + environmentSlug: slug, + }); + if (res) { + // TODO: on react-query migration do an api call to resync + setEnvironments((env) => [...env, { name, slug }]); + } + }; + + const onUpdateEnvironment = async ( + oldSlug: string, + { name, slug }: EnvData + ) => { + const res = await updateEnvironment(workspaceId, { + oldEnvironmentSlug: oldSlug, + environmentName: name, + environmentSlug: slug, + }); + // TODO: on react-query migration do an api call to resync + if (res) { + setEnvironments((env) => + env.map((el) => (el.slug === oldSlug ? { name, slug } : el)) + ); + } + }; + + const onDeleteEnvironment = async (slugToBeDelete: string) => { + const res = await deleteEnvironment(workspaceId, slugToBeDelete); + // TODO: on react-query migration do an api call to resync + if (res) { + setEnvironments((env) => + env.filter(({ slug }) => slug !== slugToBeDelete) + ); + } + }; + return (
@@ -89,9 +159,12 @@ export default function SettingsBasic() {
@@ -112,12 +185,13 @@ export default function SettingsBasic() {
-
-

+

+

{t('common:display-name')}

-
+

{t('common:project-id')}

@@ -158,30 +232,70 @@ export default function SettingsBasic() { {t('settings-project:docs')}

-
- +

+ {t('settings-project:auto-generated')} +

+
+

{`${t( + 'common:project-id' + )}:`}

+ +
+ + + {t('common:click-to-copy')} + +
+ +
+

{t('section-token:service-tokens')}

-

+

{t('section-token:service-tokens-description')}

+

+ Please, make sure you are on the + + latest version of CLI + + . +

-
+
-
- -
-
+

{t('settings-project:danger-zone')}

@@ -239,7 +351,7 @@ export default function SettingsBasic() { SettingsBasic.requireAuth = true; export const getServerSideProps = getTranslatedServerSideProps([ - "settings", - "settings-project", - "section-token", + 'settings', + 'settings-project', + 'section-token', ]); From 07c65ded40dad509d24b188882e313392b488b83 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Thu, 12 Jan 2023 01:05:13 -0800 Subject: [PATCH 49/89] Refactored the logic for frontend dashboard --- frontend/components/basic/Toggle.tsx | 42 +---- frontend/components/basic/buttons/Button.tsx | 3 +- .../dashboard/DashboardInputField.tsx | 6 +- .../dashboard/DownloadSecretsMenu.tsx | 11 +- frontend/components/dashboard/KeyPair.tsx | 23 +-- frontend/components/dashboard/SideBar.tsx | 53 ++---- .../utilities/secrets/checkOverrides.ts | 15 +- .../utilities/secrets/downloadDotEnv.ts | 11 +- .../utilities/secrets/downloadYaml.ts | 11 +- .../utilities/secrets/encryptSecrets.ts | 13 +- .../utilities/secrets/getSecretsForProject.ts | 18 +- frontend/ee/components/PITRecoverySidebar.tsx | 61 +++++-- frontend/ee/components/SecretVersionList.tsx | 47 ++--- frontend/pages/dashboard/[id].tsx | 164 +++++++++--------- frontend/public/data/frequentInterfaces.ts | 8 + 15 files changed, 219 insertions(+), 267 deletions(-) create mode 100644 frontend/public/data/frequentInterfaces.ts diff --git a/frontend/components/basic/Toggle.tsx b/frontend/components/basic/Toggle.tsx index c9957cc3c5..1a5ddaa826 100644 --- a/frontend/components/basic/Toggle.tsx +++ b/frontend/components/basic/Toggle.tsx @@ -1,27 +1,11 @@ import React from "react"; import { Switch } from "@headlessui/react"; - -interface OverrideProps { - id: string; - keyName: string; - value: string; - pos: number; - comment: string; -} - interface ToggleProps { enabled: boolean; setEnabled: (value: boolean) => void; - addOverride: (value: OverrideProps) => void; - keyName: string; - value: string; + addOverride: (value: string | undefined, pos: number) => void; pos: number; - id: string; - comment: string; - deleteOverride: (id: string) => void; - sharedToHide: string[]; - setSharedToHide: (values: string[]) => void; } /** @@ -30,41 +14,23 @@ interface ToggleProps { * @param {boolean} obj.enabled - whether the toggle is turned on or off * @param {function} obj.setEnabled - change the state of the toggle * @param {function} obj.addOverride - a function that adds an override to a certain secret - * @param {string} obj.keyName - key of a certain secret - * @param {string} obj.value - value of a certain secret * @param {number} obj.pos - position of a certain secret - #TODO: make the secret id persistent? - * @param {string} obj.id - id of a certain secret (NOTE: THIS IS THE ID OF THE MAIN SECRET - NOT OF AN OVERRIDE) - * @param {function} obj.deleteOverride - a function that deleted an override for a certain secret - * @param {string[]} obj.sharedToHide - an array of shared secrets that we want to hide visually because they are overriden. - * @param {function} obj.setSharedToHide - a function that updates the array of secrets that we want to hide visually * @returns */ export default function Toggle ({ enabled, setEnabled, addOverride, - keyName, - value, - pos, - id, - comment, - deleteOverride, - sharedToHide, - setSharedToHide + pos }: ToggleProps): JSX.Element { return ( { if (enabled == false) { - addOverride({ id, keyName, value, pos, comment }); - setSharedToHide([ - ...sharedToHide!, - id - ]) + addOverride('', pos); } else { - deleteOverride(id); + addOverride(undefined, pos); } setEnabled(!enabled); }} diff --git a/frontend/components/basic/buttons/Button.tsx b/frontend/components/basic/buttons/Button.tsx index 939d9b17de..2405abce77 100644 --- a/frontend/components/basic/buttons/Button.tsx +++ b/frontend/components/basic/buttons/Button.tsx @@ -3,7 +3,6 @@ import Image from "next/image"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon, - FontAwesomeIconProps, } from "@fortawesome/react-fontawesome"; const classNames = require("classnames"); @@ -101,7 +100,7 @@ export default function Button(props: ButtonProps): JSX.Element {
void; - value: string; + value: string | undefined; type: 'varName' | 'value'; blurred?: boolean; isDuplicate?: boolean; @@ -47,7 +47,7 @@ const DashboardInputField = ({ }; if (type === 'varName') { - const startsWithNumber = !isNaN(Number(value.charAt(0))) && value != ''; + const startsWithNumber = !isNaN(Number(value?.charAt(0))) && value != ''; const error = startsWithNumber || isDuplicate; return ( @@ -141,7 +141,7 @@ const DashboardInputField = ({ {blurred && (
- {value.split('').map(() => ( + {value?.split('').map(() => ( void; modifyValue: (value: string, position: number) => void; + modifyValueOverride: (value: string, position: number) => void; isBlurred: boolean; isDuplicate: boolean; toggleSidebar: (id: string) => void; @@ -30,6 +23,7 @@ interface KeyPairProps { * @param {String[]} obj.keyPair - data related to the environment variable (id, pos, key, value, public/private) * @param {function} obj.modifyKey - modify the key of a certain environment variable * @param {function} obj.modifyValue - modify the value of a certain environment variable + * @param {function} obj.modifyValueOverride - modify the value of a certain environment variable if it is overriden * @param {boolean} obj.isBlurred - if the blurring setting is turned on * @param {boolean} obj.isDuplicate - list of all the duplicates secret names on the dashboard * @param {function} obj.toggleSidebar - open/close/switch sidebar @@ -41,6 +35,7 @@ const KeyPair = ({ keyPair, modifyKey, modifyValue, + modifyValueOverride, isBlurred, isDuplicate, toggleSidebar, @@ -50,7 +45,7 @@ const KeyPair = ({ return (
- {keyPair.type == "personal" &&
+ {keyPair.valueOverride &&
This secret is overriden @@ -70,12 +65,12 @@ const KeyPair = ({
diff --git a/frontend/components/dashboard/SideBar.tsx b/frontend/components/dashboard/SideBar.tsx index 33b936f521..32cdf7341d 100644 --- a/frontend/components/dashboard/SideBar.tsx +++ b/frontend/components/dashboard/SideBar.tsx @@ -16,18 +16,15 @@ import GenerateSecretMenu from './GenerateSecretMenu'; interface SecretProps { key: string; value: string; + valueOverride: string | undefined; pos: number; - type: string; id: string; comment: string; } interface OverrideProps { id: string; - keyName: string; - value: string; - pos: number; - comment: string; + valueOverride: string; } export interface DeleteRowFunctionProps { ids: string[]; @@ -39,9 +36,8 @@ interface SideBarProps { data: SecretProps[]; modifyKey: (value: string, position: number) => void; modifyValue: (value: string, position: number) => void; + modifyValueOverride: (value: string | undefined, position: number) => void; modifyComment: (value: string, position: number) => void; - addOverride: (value: OverrideProps) => void; - deleteOverride: (id: string) => void; buttonReady: boolean; savePush: () => void; sharedToHide: string[]; @@ -55,12 +51,9 @@ interface SideBarProps { * @param {SecretProps[]} obj.data - data of a certain key valeu pair * @param {function} obj.modifyKey - function that modifies the secret key * @param {function} obj.modifyValue - function that modifies the secret value - * @param {function} obj.addOverride - override a certain secret - * @param {function} obj.deleteOverride - delete the personal override for a certain secret + * @param {function} obj.modifyValueOverride - function that modifies the secret value if it is an override * @param {boolean} obj.buttonReady - is the button for saving chagnes active * @param {function} obj.savePush - save changes andp ush secrets - * @param {string[]} obj.sharedToHide - an array of shared secrets that we want to hide visually because they are overriden. - * @param {function} obj.setSharedToHide - a function that updates the array of secrets that we want to hide visually * @param {function} obj.deleteRow - a function to delete a certain keyPair * @returns the sidebar with 'secret's settings' */ @@ -69,17 +62,14 @@ const SideBar = ({ data, modifyKey, modifyValue, + modifyValueOverride, modifyComment, - addOverride, - deleteOverride, buttonReady, savePush, - sharedToHide, - setSharedToHide, deleteRow }: SideBarProps) => { const [isLoading, setIsLoading] = useState(false); - const [overrideEnabled, setOverrideEnabled] = useState(data.map(secret => secret.type).includes("personal")); + const [overrideEnabled, setOverrideEnabled] = useState(data[0].valueOverride != undefined); const { t } = useTranslation(); return
@@ -111,19 +101,19 @@ const SideBar = ({ blurred={false} />
- {data.filter(secret => secret.type == "shared")[0]?.value + {data[0]?.value ?

{t("dashboard:sidebar.value")}

secret.type == "shared")[0]?.pos} - value={data.filter(secret => secret.type == "shared")[0]?.value} + position={data[0].pos} + value={data[0]?.value} isDuplicate={false} blurred={true} />
- secret.type == "shared")[0]?.pos} /> +
:
@@ -131,39 +121,32 @@ const SideBar = ({ {t("dashboard:sidebar.personal-explanation")}
}
- {data.filter(secret => secret.type == "shared")[0]?.value && + {data[0]?.value &&

{t("dashboard:sidebar.override")}

}
secret.type == "personal")[0]?.pos : data[0]?.pos} - value={overrideEnabled ? data.filter(secret => secret.type == "personal")[0]?.value : data[0]?.value} + position={data[0]?.pos} + value={overrideEnabled ? data[0]?.valueOverride : data[0]?.value} isDuplicate={false} blurred={true} />
- secret.type == "personal")[0]?.pos : data[0]?.pos} /> +
- secret.type == "shared")[0]?.comment} modifyComment={modifyComment} position={data.filter(secret => secret.type == "shared")[0]?.pos} /> +
)}
@@ -176,7 +159,7 @@ const SideBar = ({ textDisabled="Saved" /> deleteRow({ ids: overrideEnabled ? data.map(secret => secret.id) : [data.filter(secret => secret.type == "shared")[0]?.id], secretName: data[0]?.key })} + onSubmit={() => deleteRow({ ids: data.map(secret => secret.id), secretName: data[0]?.key })} />
diff --git a/frontend/components/utilities/secrets/checkOverrides.ts b/frontend/components/utilities/secrets/checkOverrides.ts index fae465d88c..83a779a85b 100644 --- a/frontend/components/utilities/secrets/checkOverrides.ts +++ b/frontend/components/utilities/secrets/checkOverrides.ts @@ -1,11 +1,4 @@ -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} +import { SecretDataProps } from "public/data/frequentInterfaces"; /** * This function downloads the secrets as a .env file @@ -16,16 +9,16 @@ interface SecretDataProps { const checkOverrides = async ({ data }: { data: SecretDataProps[]; }) => { let secrets : SecretDataProps[] = data!.map((secret) => Object.create(secret)); const overridenSecrets = data!.filter( - (secret) => secret.type === 'personal' + (secret) => (secret.valueOverride == undefined || secret?.value != secret?.valueOverride) ? 'shared' : 'personal' ); if (overridenSecrets.length) { overridenSecrets.forEach((secret) => { const index = secrets!.findIndex( - (_secret) => _secret.key === secret.key && _secret.type === 'shared' + (_secret) => _secret.key === secret.key && (secret.valueOverride == undefined || secret?.value != secret?.valueOverride) ); secrets![index].value = secret.value; }); - secrets = secrets!.filter((secret) => secret.type === 'shared'); + secrets = secrets!.filter((secret) => (secret.valueOverride == undefined || secret?.value != secret?.valueOverride)); } return secrets; } diff --git a/frontend/components/utilities/secrets/downloadDotEnv.ts b/frontend/components/utilities/secrets/downloadDotEnv.ts index dbb29497b1..93ac774baf 100644 --- a/frontend/components/utilities/secrets/downloadDotEnv.ts +++ b/frontend/components/utilities/secrets/downloadDotEnv.ts @@ -1,16 +1,9 @@ +import { SecretDataProps } from "public/data/frequentInterfaces"; + import { envMapping } from "../../../public/data/frequentConstants"; import checkOverrides from './checkOverrides'; -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} - /** * This function downloads the secrets as a .env file * @param {object} obj diff --git a/frontend/components/utilities/secrets/downloadYaml.ts b/frontend/components/utilities/secrets/downloadYaml.ts index 1c17b14109..26fca5f006 100644 --- a/frontend/components/utilities/secrets/downloadYaml.ts +++ b/frontend/components/utilities/secrets/downloadYaml.ts @@ -1,19 +1,12 @@ // import YAML from 'yaml'; // import { YAMLSeq } from 'yaml/types'; +import { SecretDataProps } from "public/data/frequentInterfaces"; + // import { envMapping } from "../../../public/data/frequentConstants"; // import checkOverrides from './checkOverrides'; -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} - /** * This function downloads the secrets as a .yml file * @param {object} obj diff --git a/frontend/components/utilities/secrets/encryptSecrets.ts b/frontend/components/utilities/secrets/encryptSecrets.ts index 46c3150ef5..a0f56990a2 100644 --- a/frontend/components/utilities/secrets/encryptSecrets.ts +++ b/frontend/components/utilities/secrets/encryptSecrets.ts @@ -1,3 +1,5 @@ +import { SecretDataProps } from "public/data/frequentInterfaces"; + import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; const crypto = require("crypto"); @@ -9,15 +11,6 @@ const nacl = require("tweetnacl"); nacl.util = require("tweetnacl-util"); -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} - interface EncryptedSecretProps { id: string; createdAt: string; @@ -106,7 +99,7 @@ const encryptSecrets = async ({ secretsToEncrypt, workspaceId, env }: { secretsT secretCommentCiphertext, secretCommentIV, secretCommentTag, - type: secret.type, + type: (secret.valueOverride == undefined || secret?.value != secret?.valueOverride) ? 'shared' : 'personal', }; return result; diff --git a/frontend/components/utilities/secrets/getSecretsForProject.ts b/frontend/components/utilities/secrets/getSecretsForProject.ts index 81de27be09..d2c4ddf549 100644 --- a/frontend/components/utilities/secrets/getSecretsForProject.ts +++ b/frontend/components/utilities/secrets/getSecretsForProject.ts @@ -117,15 +117,19 @@ const getSecretsForProject = async ({ }); } - const result = tempDecryptedSecrets.map((secret, index) => { + const secretKeys = [...new Set(tempDecryptedSecrets.map(secret => secret.key))]; + + + const result = secretKeys.map((key, index) => { return { - id: secret['id'], + id: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.id, + idOverride: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'personal')[0]?.id, pos: index, - key: secret['key'], - value: secret['value'], - type: secret['type'], - comment: secret['comment'] - }; + key: key, + value: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.value, + valueOverride: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'personal')[0]?.value, + comment: tempDecryptedSecrets.filter(secret => secret.key == key && secret.type == 'shared')[0]?.comment, + } }); setData(result); diff --git a/frontend/ee/components/PITRecoverySidebar.tsx b/frontend/ee/components/PITRecoverySidebar.tsx index 085c483ed9..e8a1041cd2 100644 --- a/frontend/ee/components/PITRecoverySidebar.tsx +++ b/frontend/ee/components/PITRecoverySidebar.tsx @@ -13,6 +13,15 @@ import { decryptAssymmetric, decryptSymmetric } from "~/components/utilities/cry import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey"; +export interface SecretDataProps { + pos: number; + key: string; + value: string; + type: string; + id: string; + environment: string; +} + interface SideBarProps { toggleSidebar: (value: boolean) => void; setSnapshotData: (value: any) => void; @@ -43,8 +52,6 @@ interface EncrypetedSecretVersionListProps { * @param {function} obj.toggleSidebar - function that opens or closes the sidebar * @param {function} obj.setSnapshotData - state manager for snapshot data * @param {string} obj.chosenSnaphshot - the snapshot id which is currently selected - * - * * @returns the sidebar with the options for point-in-time recovery (commits) */ const PITRecoverySidebar = ({ @@ -111,7 +118,21 @@ const PITRecoverySidebar = ({ } }) - setSnapshotData({ id: secretSnapshotData._id, version: secretSnapshotData.version, createdAt: secretSnapshotData.createdAt, secretVersions: decryptedSecretVersions }) + + const secretKeys = [...new Set(decryptedSecretVersions.map((secret: SecretDataProps) => secret.key))]; + + const result = secretKeys.map((key, index) => { + return { + id: decryptedSecretVersions.filter((secret: SecretDataProps) => secret.key == key && secret.type == 'shared')[0].id, + pos: index, + key: key, + environment: decryptedSecretVersions.filter((secret: SecretDataProps) => secret.key == key && secret.type == 'shared')[0].environment, + value: decryptedSecretVersions.filter((secret: SecretDataProps) => secret.key == key && secret.type == 'shared')[0]?.value, + valueOverride: decryptedSecretVersions.filter((secret: SecretDataProps) => secret.key == key && secret.type == 'personal')[0]?.value, + } + }); + + setSnapshotData({ id: secretSnapshotData._id, version: secretSnapshotData.version, createdAt: secretSnapshotData.createdAt, secretVersions: result, comment: '' }) } return
@@ -125,31 +146,35 @@ const PITRecoverySidebar = ({ >
) : ( -
+

{t("Point-in-time Recovery")}

toggleSidebar(false)}>
-
- {secretSnapshotsMetadata?.map((snapshot: SnaphotProps, id: number) =>
-
-
{timeSince(new Date(snapshot.createdAt))}
-
{" - " + snapshot.secretVersions.length + " Secrets"}
-
+
+ {secretSnapshotsMetadata?.map((snapshot: SnaphotProps, id: number) =>
exploreSnapshot({ snapshotId: snapshot._id })} - className={`${chosenSnapshot == snapshot._id || (id == 0 && chosenSnapshot === "") ? "text-bunker-800 pointer-events-none" : "text-bunker-200 hover:text-primary duration-200 cursor-pointer"} text-sm`}> - {id == 0 ? "Current Version" : chosenSnapshot == snapshot._id ? "Currently Viewing" : "Explore"} + key={snapshot._id} + onClick={() => exploreSnapshot({ snapshotId: snapshot._id })} + className={`${chosenSnapshot == snapshot._id || (id == 0 && chosenSnapshot === "") ? "bg-primary text-black pointer-events-none" : "bg-mineshaft-700 hover:bg-mineshaft-500 duration-200 cursor-pointer"} py-3 px-4 mb-2 rounded-md flex flex-row justify-between items-center`} + > +
+
{timeSince(new Date(snapshot.createdAt))}
+
{" - " + snapshot.secretVersions.length + " Secrets"}
+
+
+ {id == 0 ? "Current Version" : chosenSnapshot == snapshot._id ? "Currently Viewing" : "Explore"} +
+
)} +
+
+
-
)} -
-
-
-
)}
diff --git a/frontend/ee/components/SecretVersionList.tsx b/frontend/ee/components/SecretVersionList.tsx index 3eb40005f9..d07198dc06 100644 --- a/frontend/ee/components/SecretVersionList.tsx +++ b/frontend/ee/components/SecretVersionList.tsx @@ -52,7 +52,7 @@ const SecretVersionList = ({ secretId }: { secretId: string; }) => { }); } - const decryptedSecretVersions = encryptedSecretVersions.secretVersions.map((encryptedSecretVersion: EncrypetedSecretVersionListProps) => { + const decryptedSecretVersions = encryptedSecretVersions?.secretVersions.map((encryptedSecretVersion: EncrypetedSecretVersionListProps) => { return { createdAt: encryptedSecretVersion.createdAt, value: decryptSymmetric({ @@ -87,28 +87,33 @@ const SecretVersionList = ({ secretId }: { secretId: string; }) => {
) : (
- {secretVersions?.sort((a, b) => b.createdAt.localeCompare(a.createdAt)) - .map((version: DecryptedSecretVersionListProps, index: number) => -
-
-
-
-
-
-
- {(new Date(version.createdAt)).toLocaleDateString('en-US', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit' - })} + {secretVersions + ? secretVersions?.sort((a, b) => b.createdAt.localeCompare(a.createdAt)) + .map((version: DecryptedSecretVersionListProps, index: number) => +
+
+
+
+
+
+
+ {(new Date(version.createdAt)).toLocaleDateString('en-US', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + })} +
+

Value:{version.value}

-

Value:{version.value}

-
- )} + ) + : ( +
No version history yet.
+ ) + }
)}
diff --git a/frontend/pages/dashboard/[id].tsx b/frontend/pages/dashboard/[id].tsx index 4ce91002d3..07b4133a02 100644 --- a/frontend/pages/dashboard/[id].tsx +++ b/frontend/pages/dashboard/[id].tsx @@ -45,11 +45,12 @@ import getWorkspaces from '../api/workspace/getWorkspaces'; interface SecretDataProps { - type: 'personal' | 'shared'; pos: number; key: string; value: string; + valueOverride: string | undefined; id: string; + idOverride: string | undefined; comment: string; } @@ -68,10 +69,11 @@ interface SnapshotProps { secretVersions: { id: string; pos: number; - type: "personal" | "shared"; environment: string; key: string; value: string; + valueOverride: string; + comment: string; }[]; } @@ -99,7 +101,7 @@ function findDuplicates(arr: any[]) { */ export default function Dashboard() { const [data, setData] = useState(); - const [initialData, setInitialData] = useState([]); + const [initialData, setInitialData] = useState([]); const [buttonReady, setButtonReady] = useState(false); const router = useRouter(); const [workspaceId, setWorkspaceId] = useState(''); @@ -119,6 +121,7 @@ export default function Dashboard() { const [sharedToHide, setSharedToHide] = useState([]); const [snapshotData, setSnapshotData] = useState(); const [numSnapshots, setNumSnapshots] = useState(); + const [saveLoading, setSaveLoading] = useState(false); const { t } = useTranslation(); const { createNotification } = useNotificationContext(); @@ -213,16 +216,6 @@ export default function Dashboard() { setInitialData(dataToSort); reorderRows(dataToSort); - setSharedToHide( - dataToSort?.filter(row => (dataToSort - ?.map((item) => item.key) - .filter( - (item, index) => - index !== - dataToSort?.map((item) => item.key).indexOf(item) - ).includes(row.key) && row.type == 'shared'))?.map((item) => item.id) - ) - setIsLoading(false); } catch (error) { console.log('Error', error); @@ -238,39 +231,16 @@ export default function Dashboard() { ...data!, { id: guidGenerator(), + idOverride: guidGenerator(), pos: data!.length, key: '', value: '', - type: 'shared', + valueOverride: undefined, comment: '', } ]); }; - /** - * This function add an ovverrided version of a certain secret to the current user - * @param {object} obj - * @param {string} obj.id - if of this secret that is about to be overriden - * @param {string} obj.keyName - key name of this secret - * @param {string} obj.value - value of this secret - * @param {string} obj.pos - position of this secret on the dashboard - */ - const addOverride = ({ id, keyName, value, pos, comment }: overrideProps) => { - setIsNew(false); - const tempdata: SecretDataProps[] | 1 = [ - ...data!, - { - id: id, - pos: pos, - key: keyName, - value: value, - type: 'personal', - comment: comment - } - ]; - sortValuesHandler(tempdata, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical"); - }; - const deleteRow = ({ ids, secretName }: { ids: string[]; secretName: string; }) => { setButtonReady(true); toggleSidebar("None"); @@ -289,15 +259,15 @@ export default function Dashboard() { setButtonReady(true); // find which shared secret corresponds to the overriden version - const sharedVersionOfOverride = data!.filter(secret => secret.type == "shared" && secret.key == data!.filter(row => row.id == id)[0]?.key)[0]?.id; + // const sharedVersionOfOverride = data!.filter(secret => secret.type == "shared" && secret.key == data!.filter(row => row.id == id)[0]?.key)[0]?.id; // change the sidebar to this shared secret; and unhide it - toggleSidebar(sharedVersionOfOverride) - setSharedToHide(sharedToHide!.filter(tempId => tempId != sharedVersionOfOverride)) + // toggleSidebar(sharedVersionOfOverride) + // setSharedToHide(sharedToHide!.filter(tempId => tempId != sharedVersionOfOverride)) // resort secrets - const tempData = data!.filter((row: SecretDataProps) => !(row.key == data!.filter(row => row.id == id)[0]?.key && row.type == 'personal')) - sortValuesHandler(tempData, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical") + // const tempData = data!.filter((row: SecretDataProps) => !(row.key == data!.filter(row => row.id == id)[0]?.key && row.type == 'personal')) + // sortValuesHandler(tempData, sortMethod == "alhpabetical" ? "-alphabetical" : "alphabetical") }; const modifyValue = (value: string, pos: number) => { @@ -308,6 +278,14 @@ export default function Dashboard() { setButtonReady(true); }; + const modifyValueOverride = (value: string | undefined, pos: number) => { + setData((oldData) => { + oldData![pos].valueOverride = value; + return [...oldData!]; + }); + setButtonReady(true); + }; + const modifyKey = (value: string, pos: number) => { setData((oldData) => { oldData![pos].key = value; @@ -329,6 +307,10 @@ export default function Dashboard() { modifyValue(value, pos); }, []); + const listenChangeValueOverride = useCallback((value: string | undefined, pos: number) => { + modifyValueOverride(value, pos); + }, []); + const listenChangeKey = useCallback((value: string, pos: number) => { modifyKey(value, pos); }, []); @@ -341,6 +323,7 @@ export default function Dashboard() { * Save the changes of environment variables and push them to the database */ const savePush = async (dataToPush?: SecretDataProps[]) => { + setSaveLoading(true); let newData: SecretDataProps[] | null | undefined; // dataToPush is mostly used for rollbacks, otherwise we always take the current state data if ((dataToPush ?? [])?.length > 0) { @@ -349,16 +332,11 @@ export default function Dashboard() { newData = data; } - const obj = Object.assign( - {}, - ...newData!.map((row: SecretDataProps) => ({ [row.type.charAt(0) + row.key]: [row.value, row.comment ?? ''] })) - ); - // Checking if any of the secret keys start with a number - if so, don't do anything - const nameErrors = !Object.keys(obj) - .map((key) => !isNaN(Number(key[0].charAt(0)))) + const nameErrors = !newData! + .map((secret) => !isNaN(Number(secret.key.charAt(0)))) .every((v) => v === false); - const duplicatesExist = findDuplicates(data!.map((item: SecretDataProps) => item.key + item.type)).length > 0; + const duplicatesExist = findDuplicates(data!.map((item: SecretDataProps) => item.key)).length > 0; if (nameErrors) { return createNotification({ @@ -378,34 +356,64 @@ export default function Dashboard() { setButtonReady(false); const secretsToBeDeleted - = initialData + = initialData! .filter(initDataPoint => !newData!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id)) .map(secret => secret.id); + console.log('delete', secretsToBeDeleted.length) const secretsToBeAdded = newData! - .filter(newDataPoint => !initialData.map(initDataPoint => initDataPoint.id).includes(newDataPoint.id)); + .filter(newDataPoint => !initialData!.map(initDataPoint => initDataPoint.id).includes(newDataPoint.id)); + console.log('add', secretsToBeAdded.length) const secretsToBeUpdated - = newData!.filter(newDataPoint => initialData + = newData!.filter(newDataPoint => initialData! .filter(initDataPoint => newData!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id) && (newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].value != initDataPoint.value || newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].key != initDataPoint.key || newData!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].comment != initDataPoint.comment)) .map(secret => secret.id).includes(newDataPoint.id)); + console.log('update', secretsToBeUpdated.length) + + const newOverrides = newData!.filter(newDataPoint => newDataPoint.valueOverride != undefined) + const initOverrides = initialData!.filter(initDataPoint => initDataPoint.valueOverride != undefined) + + const overridesToBeDeleted + = initOverrides + .filter(initDataPoint => !newOverrides!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id)) + .map(secret => String(secret.idOverride)); + console.log('override delete', overridesToBeDeleted.length) + + const overridesToBeAdded + = newOverrides! + .filter(newDataPoint => !initOverrides.map(initDataPoint => initDataPoint.id).includes(newDataPoint.id)) + .map(override => ({pos: override.pos, key: override.key, value: String(override.valueOverride), valueOverride: override.valueOverride, comment: '', id: String(override.idOverride), idOverride: String(override.idOverride)})); + console.log('override add', overridesToBeAdded.length) + + const overridesToBeUpdated + = newOverrides!.filter(newDataPoint => initOverrides + .filter(initDataPoint => newOverrides!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id) + && (newOverrides!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].valueOverride != initDataPoint.valueOverride + || newOverrides!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].key != initDataPoint.key + || newOverrides!.filter(newDataPoint => newDataPoint.id == initDataPoint.id)[0].comment != initDataPoint.comment)) + .map(secret => secret.id).includes(newDataPoint.id)) + .map(override => ({pos: override.pos, key: override.key, value: String(override.valueOverride), valueOverride: override.valueOverride, comment: '', id: String(override.idOverride), idOverride: String(override.idOverride)})); + console.log('override update', overridesToBeUpdated.length) - if (secretsToBeDeleted.length > 0) { - await deleteSecrets({ secretIds: secretsToBeDeleted }); + if (secretsToBeDeleted.concat(overridesToBeDeleted).length > 0) { + await deleteSecrets({ secretIds: secretsToBeDeleted.concat(overridesToBeDeleted) }); } - if (secretsToBeAdded.length > 0) { - const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded, workspaceId, env: envMapping[env] }) + if (secretsToBeAdded.concat(overridesToBeAdded).length > 0) { + const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded.concat(overridesToBeAdded), workspaceId, env: envMapping[env] }); secrets && await addSecrets({ secrets, env: envMapping[env], workspaceId }); } - if (secretsToBeUpdated.length > 0) { - const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeUpdated, workspaceId, env: envMapping[env] }) + if (secretsToBeUpdated.concat(overridesToBeUpdated).length > 0) { + const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeUpdated.concat(overridesToBeUpdated), workspaceId, env: envMapping[env] }); secrets && await updateSecrets({ secrets }); } + setInitialData(newData); + // If this user has never saved environment variables before, show them a prompt to read docs if (!hasUserEverPushed) { setCheckDocsPopUpVisible(true); @@ -414,6 +422,7 @@ export default function Dashboard() { // increasing the number of project commits setNumSnapshots((numSnapshots ?? 0) + 1); + setSaveLoading(false); }; const addData = (newData: SecretDataProps[]) => { @@ -462,9 +471,8 @@ export default function Dashboard() { data={data.filter((row: SecretDataProps) => row.key == data.filter(row => row.id == sidebarSecretId)[0]?.key)} modifyKey={listenChangeKey} modifyValue={listenChangeValue} + modifyValueOverride={listenChangeValueOverride} modifyComment={listenChangeComment} - addOverride={addOverride} - deleteOverride={deleteOverride} buttonReady={buttonReady} savePush={savePush} sharedToHide={sharedToHide} @@ -533,6 +541,7 @@ export default function Dashboard() { active={buttonReady} iconDisabled={faCheck} textDisabled={String(t("common:saved"))} + loading={saveLoading} />
)} @@ -545,21 +554,11 @@ export default function Dashboard() { .filter(row => reverseEnvMapping[row.environment] == env) .map((sv, position) => { return { - id: sv.id, pos: position, type: sv.type, key: sv.key, value: sv.value, comment: '' + id: sv.id, idOverride: sv.id, pos: position, valueOverride: sv.valueOverride, key: sv.key, value: sv.value, comment: '' } }); setData(rolledBackSecrets); - setSharedToHide( - rolledBackSecrets?.filter(row => (rolledBackSecrets - ?.map((item) => item.key) - .filter( - (item, index) => - index !== - rolledBackSecrets?.map((item) => item.key).indexOf(item) - ).includes(row.key) && row.type == 'shared'))?.map((item) => item.id) - ) - // Perform the rollback globally performSecretRollback({ workspaceId, version: snapshotData.version }) @@ -663,16 +662,17 @@ export default function Dashboard() { >
{!snapshotData && data?.filter(row => row.key?.toUpperCase().includes(searchKeys.toUpperCase())) - .filter(row => !(sharedToHide.includes(row.id) && row.type == 'shared')).map((keyPair) => ( + .filter(row => !sharedToHide.includes(row.id)).map((keyPair) => ( item.key + item.type) - )?.includes(keyPair.key + keyPair.type)} + data?.map((item) => item.key) + )?.includes(keyPair.key)} toggleSidebar={toggleSidebar} sidebarSecretId={sidebarSecretId} isSnapshot={false} @@ -681,22 +681,26 @@ export default function Dashboard() { {snapshotData && snapshotData.secretVersions?.sort((a, b) => a.key.localeCompare(b.key)) .filter(row => reverseEnvMapping[row.environment] == snapshotEnv) .filter(row => row.key.toUpperCase().includes(searchKeys.toUpperCase())) - .filter(row => !(snapshotData.secretVersions?.filter(row => (snapshotData.secretVersions + .filter( + row => !(snapshotData.secretVersions?.filter(row => (snapshotData.secretVersions ?.map((item) => item.key) .filter( (item, index) => index !== snapshotData.secretVersions?.map((item) => item.key).indexOf(item) - ).includes(row.key) && row.type == 'shared'))?.map((item) => item.id).includes(row.id) && row.type == 'shared')).map((keyPair) => ( + ).includes(row.key)))?.map((item) => item.id).includes(row.id)) + ) + .map((keyPair) => ( item.key + item.type) - )?.includes(keyPair.key + keyPair.type)} + data?.map((item) => item.key) + )?.includes(keyPair.key)} toggleSidebar={toggleSidebar} sidebarSecretId={sidebarSecretId} isSnapshot={true} diff --git a/frontend/public/data/frequentInterfaces.ts b/frontend/public/data/frequentInterfaces.ts new file mode 100644 index 0000000000..c2fa797fd5 --- /dev/null +++ b/frontend/public/data/frequentInterfaces.ts @@ -0,0 +1,8 @@ +export interface SecretDataProps { + pos: number; + key: string; + value: string; + valueOverride: string | undefined; + id: string; + comment: string; +} \ No newline at end of file From 47fd48b7b01b8013073a3666ce5220324c492c1b Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Thu, 12 Jan 2023 13:57:00 -0800 Subject: [PATCH 50/89] Fixed the TS error during signup --- frontend/components/utilities/attemptLogin.ts | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/frontend/components/utilities/attemptLogin.ts b/frontend/components/utilities/attemptLogin.ts index c40dbb5481..f1aa03ffb9 100644 --- a/frontend/components/utilities/attemptLogin.ts +++ b/frontend/components/utilities/attemptLogin.ts @@ -1,3 +1,5 @@ +import { SecretDataProps } from 'public/data/frequentInterfaces'; + import Aes256Gcm from '~/components/utilities/cryptography/aes-256-gcm'; import login1 from '~/pages/api/auth/Login1'; import login2 from '~/pages/api/auth/Login2'; @@ -13,14 +15,6 @@ import Telemetry from './telemetry/Telemetry'; import { saveTokenToLocalStorage } from './saveTokenToLocalStorage'; import SecurityClient from './SecurityClient'; -interface SecretDataProps { - type: 'personal' | 'shared'; - pos: number; - key: string; - value: string; - id: string; - comment: string; -} const crypto = require("crypto"); const nacl = require('tweetnacl'); @@ -145,53 +139,53 @@ const attemptLogin = async ( ); const secretsToBeAdded: SecretDataProps[] = [{ - type: "shared", pos: 0, key: "DATABASE_URL", value: "mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net", + valueOverride: undefined, comment: "This is an example of secret referencing.", id: '' }, { - type: "shared", pos: 1, key: "DB_USERNAME", value: "OVERRIDE_THIS", + valueOverride: undefined, comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", id: '' }, { - type: "personal", pos: 2, - key: "DB_USERNAME", - value: "user1234", - comment: "", - id: '' - }, { - type: "shared", - pos: 3, key: "DB_PASSWORD", value: "OVERRIDE_THIS", + valueOverride: undefined, comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", id: '' }, { - type: "personal", + pos: 3, + key: "DB_USERNAME", + value: "user1234", + valueOverride: "user1234", + comment: "", + id: '' + }, { pos: 4, key: "DB_PASSWORD", value: "example_password", + valueOverride: "example_password", comment: "", id: '' }, { - type: "shared", pos: 5, key: "TWILIO_AUTH_TOKEN", value: "example_twillio_token", - comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", + valueOverride: undefined, + comment: "", id: '' }, { - type: "shared", pos: 6, key: "WEBSITE_URL", value: "http://localhost:3000", - comment: "This is an example of secret overriding. Your team can have a shared value of a secret, while you can override it to whatever value you need", + valueOverride: undefined, + comment: "", id: '' }] const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded, workspaceId: String(localStorage.getItem('projectData.id')), env: 'dev' }) From 71f60f1589c62ef3f6d7953a6e0e0299bd9636c6 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 15:31:23 -0800 Subject: [PATCH 51/89] Update modify secrets api v2 so that fields are optional --- .../src/controllers/v2/secretsController.ts | 113 +++++++++--------- backend/src/ee/helpers/secret.ts | 92 +++++++------- backend/src/ee/models/secretVersion.ts | 50 ++++---- backend/src/routes/v2/secrets.ts | 44 +++---- 4 files changed, 141 insertions(+), 158 deletions(-) diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index ff9574711d..f92fc4cee1 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -2,8 +2,8 @@ import to from 'await-to-js'; import { Types } from 'mongoose'; import { Request, Response } from 'express'; import { ISecret, Secret } from '../../models'; -import { - SECRET_PERSONAL, +import { + SECRET_PERSONAL, SECRET_SHARED, ACTION_ADD_SECRETS, ACTION_READ_SECRETS, @@ -23,9 +23,9 @@ import { BadRequestError } from '../../utils/errors'; * @param res */ export const createSecrets = async (req: Request, res: Response) => { - const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; + const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; const { workspaceId, environment } = req.body; - + let toAdd; if (Array.isArray(req.body.secrets)) { // case: create multiple secrets @@ -34,7 +34,7 @@ export const createSecrets = async (req: Request, res: Response) => { // case: create 1 secret toAdd = [req.body.secrets]; } - + const newSecrets = await Secret.insertMany( toAdd.map(({ type, @@ -66,7 +66,7 @@ export const createSecrets = async (req: Request, res: Response) => { secretValueTag })) ); - + // (EE) add secret versions for new secrets EESecretService.addSecretVersions({ secretVersions: newSecrets.map(({ @@ -160,7 +160,7 @@ export const createSecrets = async (req: Request, res: Response) => { */ export const getSecrets = async (req: Request, res: Response) => { const { workspaceId, environment } = req.query; - + let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user if (req.user) { userId = req.user._id; @@ -169,13 +169,13 @@ export const getSecrets = async (req: Request, res: Response) => { if (req.serviceTokenData) { userId = req.serviceTokenData.user._id } - + const [err, secrets] = await to(Secret.find( { workspace: workspaceId, environment, $or: [ - { user: userId }, + { user: userId }, { user: { $exists: false } } ], type: { $in: [SECRET_SHARED, SECRET_PERSONAL] } @@ -183,9 +183,9 @@ export const getSecrets = async (req: Request, res: Response) => { ).then()) if (err) throw ValidationError({ message: 'Failed to get secrets', stack: err.stack }); - + const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; - + const readAction = await EELogService.createActionSecret({ name: ACTION_READ_SECRETS, userId: req.user._id.toString(), @@ -214,7 +214,7 @@ export const getSecrets = async (req: Request, res: Response) => { } }); } - + return res.status(200).send({ secrets }); @@ -226,8 +226,8 @@ export const getSecrets = async (req: Request, res: Response) => { * @param res */ export const updateSecrets = async (req: Request, res: Response) => { - const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; - + const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; + // TODO: move type interface PatchSecret { id: string; @@ -242,7 +242,7 @@ export const updateSecrets = async (req: Request, res: Response) => { secretCommentTag: string; } - const ops = req.body.secrets.map((secret: PatchSecret) => { + const updateOperationsToPerform = req.body.secrets.map((secret: PatchSecret) => { const { secretKeyCiphertext, secretKeyIV, @@ -254,6 +254,7 @@ export const updateSecrets = async (req: Request, res: Response) => { secretCommentIV, secretCommentTag } = secret; + return ({ updateOne: { filter: { _id: new Types.ObjectId(secret.id) }, @@ -268,8 +269,8 @@ export const updateSecrets = async (req: Request, res: Response) => { secretValueIV, secretValueTag, ...(( - secretCommentCiphertext && - secretCommentIV && + secretCommentCiphertext && + secretCommentIV && secretCommentTag ) ? { secretCommentCiphertext, @@ -280,15 +281,17 @@ export const updateSecrets = async (req: Request, res: Response) => { } }); }); - await Secret.bulkWrite(ops); - - const newSecretsObj: { [key: string]: PatchSecret } = {}; + + await Secret.bulkWrite(updateOperationsToPerform); + + const secretModificationsBySecretId: { [key: string]: PatchSecret } = {}; req.body.secrets.forEach((secret: PatchSecret) => { - newSecretsObj[secret.id] = secret; + secretModificationsBySecretId[secret.id] = secret; }); - await EESecretService.addSecretVersions({ - secretVersions: req.secrets.map((secret: ISecret) => { + const ListOfSecretsBeforeModifications = req.secrets + const secretVersions = { + secretVersions: ListOfSecretsBeforeModifications.map((secret: ISecret) => { const { secretKeyCiphertext, secretKeyIV, @@ -298,37 +301,29 @@ export const updateSecrets = async (req: Request, res: Response) => { secretValueTag, secretCommentCiphertext, secretCommentIV, - secretCommentTag - } = newSecretsObj[secret._id.toString()] + secretCommentTag, + } = secretModificationsBySecretId[secret._id.toString()] + return ({ secret: secret._id, version: secret.version + 1, workspace: secret.workspace, type: secret.type, environment: secret.environment, - isDeleted: false, - secretKeyCiphertext, - secretKeyIV, - secretKeyTag, - secretValueCiphertext, - secretValueIV, - secretValueTag, - ...(( - secretCommentCiphertext && - secretCommentIV && - secretCommentTag - ) ? { - secretCommentCiphertext, - secretCommentIV, - secretCommentTag - } : { - secretCommentCiphertext: '', - secretCommentIV: '', - secretCommentTag: '' - }) + secretKeyCiphertext: secretKeyCiphertext ? secretKeyCiphertext : secret.secretKeyCiphertext, + secretKeyIV: secretKeyIV ? secretKeyIV : secret.secretKeyIV, + secretKeyTag: secretKeyTag ? secretKeyTag : secret.secretKeyTag, + secretValueCiphertext: secretValueCiphertext ? secretValueCiphertext : secret.secretValueCiphertext, + secretValueIV: secretValueIV ? secretValueIV : secret.secretValueIV, + secretValueTag: secretValueTag ? secretValueTag : secret.secretValueTag, + secretCommentCiphertext: secretCommentCiphertext ? secretCommentCiphertext : secret.secretCommentCiphertext, + secretCommentIV: secretCommentIV ? secretCommentIV : secret.secretCommentIV, + secretCommentTag: secretCommentTag ? secretCommentTag : secret.secretCommentTag, }); }) - }); + } + + await EESecretService.addSecretVersions(secretVersions); // group secrets into workspaces so updated secrets can @@ -355,7 +350,7 @@ export const updateSecrets = async (req: Request, res: Response) => { userId: req.user._id.toString(), workspaceId: key, secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) - }); + }); // (EE) create (audit) log updateAction && await EELogService.createLog({ @@ -367,9 +362,9 @@ export const updateSecrets = async (req: Request, res: Response) => { }); // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: key - }) + await EESecretService.takeSecretSnapshot({ + workspaceId: key + }) if (postHogClient) { postHogClient.capture({ @@ -385,7 +380,7 @@ export const updateSecrets = async (req: Request, res: Response) => { }); } }); - + return res.status(200).send({ secrets: await Secret.find({ _id: { @@ -401,15 +396,15 @@ export const updateSecrets = async (req: Request, res: Response) => { * @param res */ export const deleteSecrets = async (req: Request, res: Response) => { - const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; + const channel = req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli'; const toDelete = req.secrets.map((s: any) => s._id); - + await Secret.deleteMany({ _id: { $in: toDelete } }); - + await EESecretService.markDeletedSecretVersions({ secretIds: toDelete }); @@ -437,7 +432,7 @@ export const deleteSecrets = async (req: Request, res: Response) => { userId: req.user._id.toString(), workspaceId: key, secretIds: workspaceSecretObj[key].map((secret: ISecret) => secret._id) - }); + }); // (EE) create (audit) log deleteAction && await EELogService.createLog({ @@ -449,9 +444,9 @@ export const deleteSecrets = async (req: Request, res: Response) => { }); // (EE) take a secret snapshot - await EESecretService.takeSecretSnapshot({ - workspaceId: key - }) + await EESecretService.takeSecretSnapshot({ + workspaceId: key + }) if (postHogClient) { postHogClient.capture({ @@ -467,7 +462,7 @@ export const deleteSecrets = async (req: Request, res: Response) => { }); } }); - + return res.status(200).send({ secrets: req.secrets }); diff --git a/backend/src/ee/helpers/secret.ts b/backend/src/ee/helpers/secret.ts index 529c9a9801..7edee915f0 100644 --- a/backend/src/ee/helpers/secret.ts +++ b/backend/src/ee/helpers/secret.ts @@ -1,11 +1,11 @@ import { Types } from 'mongoose'; import * as Sentry from '@sentry/node'; import { - Secret, + Secret, ISecret } from '../../models'; import { - SecretSnapshot, + SecretSnapshot, SecretVersion, ISecretVersion } from '../models'; @@ -18,24 +18,24 @@ import { * @param {String} obj.workspaceId * @returns {SecretSnapshot} secretSnapshot - new secret snapshot */ - const takeSecretSnapshotHelper = async ({ +const takeSecretSnapshotHelper = async ({ workspaceId }: { workspaceId: string; }) => { - + let secretSnapshot; try { const secretIds = (await Secret.find({ workspace: workspaceId }, '_id')).map((s) => s._id); - + const latestSecretVersions = (await SecretVersion.aggregate([ { - $match: { - secret: { - $in: secretIds - } + $match: { + secret: { + $in: secretIds + } } }, { @@ -48,14 +48,14 @@ import { { $sort: { version: -1 } } - ]) + ]) .exec()) .map((s) => s.versionId); - + const latestSecretSnapshot = await SecretSnapshot.findOne({ workspace: workspaceId }).sort({ version: -1 }); - + secretSnapshot = await new SecretSnapshot({ workspace: workspaceId, version: latestSecretSnapshot ? latestSecretSnapshot.version + 1 : 1, @@ -66,7 +66,7 @@ import { Sentry.captureException(err); throw new Error('Failed to take a secret snapshot'); } - + return secretSnapshot; } @@ -87,9 +87,9 @@ const addSecretVersionsHelper = async ({ } catch (err) { Sentry.setUser(null); Sentry.captureException(err); - throw new Error('Failed to add secret versions'); + throw new Error(`Failed to add secret versions [err=${err}]`); } - + return newSecretVersions; } @@ -120,39 +120,39 @@ const markDeletedSecretVersionsHelper = async ({ const initSecretVersioningHelper = async () => { try { - await Secret.updateMany( + await Secret.updateMany( { version: { $exists: false } }, { $set: { version: 1 } } ); - - const unversionedSecrets: ISecret[] = await Secret.aggregate([ - { - $lookup: { - from: 'secretversions', - localField: '_id', - foreignField: 'secret', - as: 'versions', - }, - }, - { - $match: { - versions: { $size: 0 }, - }, - }, - ]); - - if (unversionedSecrets.length > 0) { - await addSecretVersionsHelper({ - secretVersions: unversionedSecrets.map((s, idx) => ({ - ...s, - secret: s._id, - version: s.version ? s.version : 1, - isDeleted: false, - workspace: s.workspace, - environment: s.environment - })) - }); - } + + const unversionedSecrets: ISecret[] = await Secret.aggregate([ + { + $lookup: { + from: 'secretversions', + localField: '_id', + foreignField: 'secret', + as: 'versions', + }, + }, + { + $match: { + versions: { $size: 0 }, + }, + }, + ]); + + if (unversionedSecrets.length > 0) { + await addSecretVersionsHelper({ + secretVersions: unversionedSecrets.map((s, idx) => ({ + ...s, + secret: s._id, + version: s.version ? s.version : 1, + isDeleted: false, + workspace: s.workspace, + environment: s.environment + })) + }); + } } catch (err) { Sentry.setUser(null); @@ -162,7 +162,7 @@ const initSecretVersioningHelper = async () => { } export { - takeSecretSnapshotHelper, + takeSecretSnapshotHelper, addSecretVersionsHelper, markDeletedSecretVersionsHelper, initSecretVersioningHelper diff --git a/backend/src/ee/models/secretVersion.ts b/backend/src/ee/models/secretVersion.ts index 616d44fbdc..391c0faec1 100644 --- a/backend/src/ee/models/secretVersion.ts +++ b/backend/src/ee/models/secretVersion.ts @@ -10,14 +10,14 @@ import { export interface ISecretVersion { _id: Types.ObjectId; - secret: Types.ObjectId; - version: number; + secret: Types.ObjectId; + version: number; workspace: Types.ObjectId; // new type: string; // new user: Types.ObjectId; // new environment: string; // new - isDeleted: boolean; - secretKeyCiphertext: string; + isDeleted: boolean; + secretKeyCiphertext: string; secretKeyIV: string; secretKeyTag: string; secretKeyHash: string; @@ -28,17 +28,17 @@ export interface ISecretVersion { } const secretVersionSchema = new Schema( - { - secret: { // could be deleted - type: Schema.Types.ObjectId, - ref: 'Secret', - required: true - }, - version: { - type: Number, - default: 1, - required: true - }, + { + secret: { // could be deleted + type: Schema.Types.ObjectId, + ref: 'Secret', + required: true + }, + version: { + type: Number, + default: 1, + required: true + }, workspace: { type: Schema.Types.ObjectId, ref: 'Workspace', @@ -59,12 +59,12 @@ const secretVersionSchema = new Schema( enum: [ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD], required: true }, - isDeleted: { // consider removing field - type: Boolean, - default: false, - required: true - }, - secretKeyCiphertext: { + isDeleted: { // consider removing field + type: Boolean, + default: false, + required: true + }, + secretKeyCiphertext: { type: String, required: true }, @@ -94,10 +94,10 @@ const secretVersionSchema = new Schema( secretValueHash: { type: String } - }, - { - timestamps: true - } + }, + { + timestamps: true + } ); const SecretVersion = model('SecretVersion', secretVersionSchema); diff --git a/backend/src/routes/v2/secrets.ts b/backend/src/routes/v2/secrets.ts index 29eac042fe..085d487cd7 100644 --- a/backend/src/routes/v2/secrets.ts +++ b/backend/src/routes/v2/secrets.ts @@ -8,8 +8,8 @@ import { } from '../../middleware'; import { query, check, body } from 'express-validator'; import { secretsController } from '../../controllers/v2'; -import { - ADMIN, +import { + ADMIN, MEMBER, SECRET_PERSONAL, SECRET_SHARED @@ -27,7 +27,7 @@ router.post( if (value.length === 0) throw new Error('secrets cannot be an empty array') for (const secret of value) { if ( - !secret.type || + !secret.type || !(secret.type === SECRET_PERSONAL || secret.type === SECRET_SHARED) || !secret.secretKeyCiphertext || !secret.secretKeyIV || @@ -42,7 +42,7 @@ router.post( } else if (typeof value === 'object') { // case: update 1 secret if ( - !value.type || + !value.type || !(value.type === SECRET_PERSONAL || value.type === SECRET_SHARED) || !value.secretKeyCiphertext || !value.secretKeyIV || @@ -52,13 +52,13 @@ router.post( !value.secretValueTag ) { throw new Error('secrets object is missing required secret properties'); - } + } } else { throw new Error('secrets must be an object or an array of objects') } - + return true; - }), + }), validateRequest, requireAuth({ acceptedAuthModes: ['jwt'] @@ -95,36 +95,24 @@ router.patch( if (value.length === 0) throw new Error('secrets cannot be an empty array') for (const secret of value) { if ( - !secret.id || - !secret.secretKeyCiphertext || - !secret.secretKeyIV || - !secret.secretKeyTag || - !secret.secretValueCiphertext || - !secret.secretValueIV || - !secret.secretValueTag + !secret.id ) { - throw new Error('secrets array must contain objects that have required secret properties'); + throw new Error('Each secret must contain a ID property'); } } } else if (typeof value === 'object') { // case: update 1 secret if ( - !value.id || - !value.secretKeyCiphertext || - !value.secretKeyIV || - !value.secretKeyTag || - !value.secretValueCiphertext || - !value.secretValueIV || - !value.secretValueTag + !value.id ) { - throw new Error('secrets object is missing required secret properties'); - } + throw new Error('secret must contain a ID property'); + } } else { throw new Error('secrets must be an object or an array of objects') } - + return true; - }), + }), validateRequest, requireAuth({ acceptedAuthModes: ['jwt'] @@ -142,13 +130,13 @@ router.delete( .custom((value) => { // case: delete 1 secret if (typeof value === 'string') return true; - + if (Array.isArray(value)) { // case: delete multiple secrets if (value.length === 0) throw new Error('secrets cannot be an empty array'); return value.every((id: string) => typeof id === 'string') } - + throw new Error('secretIds must be a string or an array of strings'); }) .not() From a707fe14982a620b314ab23e56afda1d180fb555 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 15:56:49 -0800 Subject: [PATCH 52/89] disable integration --- backend/src/variables/integration.ts | 66 ++++++++++++++-------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index 1625c39543..baea2b0930 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -11,10 +11,10 @@ const INTEGRATION_VERCEL = 'vercel'; const INTEGRATION_NETLIFY = 'netlify'; const INTEGRATION_GITHUB = 'github'; const INTEGRATION_SET = new Set([ - INTEGRATION_HEROKU, - INTEGRATION_VERCEL, - INTEGRATION_NETLIFY, - INTEGRATION_GITHUB + INTEGRATION_HEROKU, + INTEGRATION_VERCEL, + INTEGRATION_NETLIFY, + INTEGRATION_GITHUB ]); // integration types @@ -23,10 +23,10 @@ const INTEGRATION_OAUTH2 = 'oauth2'; // integration oauth endpoints const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token'; const INTEGRATION_VERCEL_TOKEN_URL = - 'https://api.vercel.com/v2/oauth/access_token'; + 'https://api.vercel.com/v2/oauth/access_token'; const INTEGRATION_NETLIFY_TOKEN_URL = 'https://api.netlify.com/oauth/token'; const INTEGRATION_GITHUB_TOKEN_URL = - 'https://github.com/login/oauth/access_token'; + 'https://github.com/login/oauth/access_token'; // integration apps endpoints const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com'; @@ -37,7 +37,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Heroku', slug: 'heroku', - image: 'Heroku', + image: 'Heroku', isAvailable: true, type: 'oauth2', clientId: CLIENT_ID_HEROKU, @@ -46,8 +46,8 @@ const INTEGRATION_OPTIONS = [ { name: 'Vercel', slug: 'vercel', - image: 'Vercel', - isAvailable: true, + image: 'Vercel', + isAvailable: false, type: 'vercel', clientId: '', clientSlug: CLIENT_SLUG_VERCEL, @@ -56,8 +56,8 @@ const INTEGRATION_OPTIONS = [ { name: 'Netlify', slug: 'netlify', - image: 'Netlify', - isAvailable: true, + image: 'Netlify', + isAvailable: false, type: 'oauth2', clientId: CLIENT_ID_NETLIFY, docsLink: '' @@ -65,17 +65,17 @@ const INTEGRATION_OPTIONS = [ { name: 'GitHub', slug: 'github', - image: 'GitHub', - isAvailable: true, + image: 'GitHub', + isAvailable: false, type: 'oauth2', clientId: CLIENT_ID_GITHUB, docsLink: '' - + }, { name: 'Google Cloud Platform', slug: 'gcp', - image: 'Google Cloud Platform', + image: 'Google Cloud Platform', isAvailable: false, type: '', clientId: '', @@ -84,7 +84,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Amazon Web Services', slug: 'aws', - image: 'Amazon Web Services', + image: 'Amazon Web Services', isAvailable: false, type: '', clientId: '', @@ -93,7 +93,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Microsoft Azure', slug: 'azure', - image: 'Microsoft Azure', + image: 'Microsoft Azure', isAvailable: false, type: '', clientId: '', @@ -102,7 +102,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Travis CI', slug: 'travisci', - image: 'Travis CI', + image: 'Travis CI', isAvailable: false, type: '', clientId: '', @@ -111,7 +111,7 @@ const INTEGRATION_OPTIONS = [ { name: 'Circle CI', slug: 'circleci', - image: 'Circle CI', + image: 'Circle CI', isAvailable: false, type: '', clientId: '', @@ -120,18 +120,18 @@ const INTEGRATION_OPTIONS = [ ] export { - INTEGRATION_HEROKU, - INTEGRATION_VERCEL, - INTEGRATION_NETLIFY, - INTEGRATION_GITHUB, - INTEGRATION_SET, - INTEGRATION_OAUTH2, - INTEGRATION_HEROKU_TOKEN_URL, - INTEGRATION_VERCEL_TOKEN_URL, - INTEGRATION_NETLIFY_TOKEN_URL, - INTEGRATION_GITHUB_TOKEN_URL, - INTEGRATION_HEROKU_API_URL, - INTEGRATION_VERCEL_API_URL, - INTEGRATION_NETLIFY_API_URL, - INTEGRATION_OPTIONS + INTEGRATION_HEROKU, + INTEGRATION_VERCEL, + INTEGRATION_NETLIFY, + INTEGRATION_GITHUB, + INTEGRATION_SET, + INTEGRATION_OAUTH2, + INTEGRATION_HEROKU_TOKEN_URL, + INTEGRATION_VERCEL_TOKEN_URL, + INTEGRATION_NETLIFY_TOKEN_URL, + INTEGRATION_GITHUB_TOKEN_URL, + INTEGRATION_HEROKU_API_URL, + INTEGRATION_VERCEL_API_URL, + INTEGRATION_NETLIFY_API_URL, + INTEGRATION_OPTIONS }; From 1ac94ee940bb432deeab6360232c95533e5aa954 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 16:37:59 -0800 Subject: [PATCH 53/89] selectively get user email from service toke/jwt --- .../src/controllers/v2/secretController.ts | 191 +++++++++--------- .../src/controllers/v2/secretsController.ts | 5 +- 2 files changed, 101 insertions(+), 95 deletions(-) diff --git a/backend/src/controllers/v2/secretController.ts b/backend/src/controllers/v2/secretController.ts index c2cf45f9ad..89567e616d 100644 --- a/backend/src/controllers/v2/secretController.ts +++ b/backend/src/controllers/v2/secretController.ts @@ -7,7 +7,7 @@ const { ValidationError } = mongoose.Error; import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors'; import { AnyBulkWriteOperation } from 'mongodb'; import { SECRET_PERSONAL, SECRET_SHARED } from "../../variables"; -// import { postHogClient } from '../../services'; +import { postHogClient } from '../../services'; /** * Create secret for workspace with id [workspaceId] and environment [environment] @@ -42,19 +42,19 @@ export const createSecret = async (req: Request, res: Response) => { throw RouteValidationError({ message: error.message, stack: error.stack }) } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets added', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: 1, - // workspaceId, - // environment, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets added', + distinctId: req.user.email, + properties: { + numberOfSecrets: 1, + workspaceId, + environment, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } res.status(200).send({ secret @@ -103,19 +103,19 @@ export const createSecrets = async (req: Request, res: Response) => { throw InternalServerError({ message: "Unable to process your batch create request. Please try again", stack: bulkCreateError.stack }) } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets added', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: (secretsToCreate ?? []).length, - // workspaceId, - // environment, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets added', + distinctId: req.user.email, + properties: { + numberOfSecrets: (secretsToCreate ?? []).length, + workspaceId, + environment, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } res.status(200).send({ secrets @@ -158,19 +158,19 @@ export const deleteSecrets = async (req: Request, res: Response) => { throw InternalServerError() } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets deleted', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: numSecretsDeleted, - // environment: environmentName, - // workspaceId, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets deleted', + distinctId: req.user.email, + properties: { + numberOfSecrets: numSecretsDeleted, + environment: environmentName, + workspaceId, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } res.status(200).send() } @@ -183,19 +183,19 @@ export const deleteSecrets = async (req: Request, res: Response) => { export const deleteSecret = async (req: Request, res: Response) => { await Secret.findByIdAndDelete(req._secret._id) - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets deleted', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: 1, - // workspaceId: req._secret.workspace.toString(), - // environment: req._secret.environment, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets deleted', + distinctId: req.user.email, + properties: { + numberOfSecrets: 1, + workspaceId: req._secret.workspace.toString(), + environment: req._secret.environment, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } res.status(200).send({ secret: req._secret @@ -252,19 +252,19 @@ export const updateSecrets = async (req: Request, res: Response) => { throw InternalServerError() } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets modified', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: (secretsModificationsRequested ?? []).length, - // environment: environmentName, - // workspaceId, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets modified', + distinctId: req.user.email, + properties: { + numberOfSecrets: (secretsModificationsRequested ?? []).length, + environment: environmentName, + workspaceId, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } return res.status(200).send() } @@ -304,19 +304,19 @@ export const updateSecret = async (req: Request, res: Response) => { throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: error.stack }) } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets modified', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: 1, - // environment: environmentName, - // workspaceId, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets modified', + distinctId: req.user.email, + properties: { + numberOfSecrets: 1, + environment: environmentName, + workspaceId, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } return res.status(200).send(singleModificationUpdate) } @@ -332,13 +332,16 @@ export const getSecrets = async (req: Request, res: Response) => { const { environment } = req.query; const { workspaceId } = req.params; - let userId: string | undefined = undefined // used for getting personal secrets for user + let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user + let userEmail: Types.ObjectId | undefined = undefined // used for posthog if (req.user) { - userId = req.user._id.toString(); + userId = req.user._id; + userEmail = req.user.email; } if (req.serviceTokenData) { userId = req.serviceTokenData.user._id + userEmail = req.serviceTokenData.user.email; } const [err, secrets] = await to(Secret.find( @@ -354,19 +357,19 @@ export const getSecrets = async (req: Request, res: Response) => { throw RouteValidationError({ message: "Failed to get secrets, please try again", stack: err.stack }) } - // if (postHogClient) { - // postHogClient.capture({ - // event: 'secrets pulled', - // distinctId: req.user.email, - // properties: { - // numberOfSecrets: (secrets ?? []).length, - // environment, - // workspaceId, - // channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', - // userAgent: req.headers?.['user-agent'] - // } - // }); - // } + if (postHogClient) { + postHogClient.capture({ + event: 'secrets pulled', + distinctId: userEmail, + properties: { + numberOfSecrets: (secrets ?? []).length, + environment, + workspaceId, + channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli', + userAgent: req.headers?.['user-agent'] + } + }); + } return res.json(secrets) } diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index f92fc4cee1..b5a27874f8 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -162,12 +162,15 @@ export const getSecrets = async (req: Request, res: Response) => { const { workspaceId, environment } = req.query; let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user + let userEmail: Types.ObjectId | undefined = undefined // used for posthog if (req.user) { userId = req.user._id; + userEmail = req.user.email; } if (req.serviceTokenData) { userId = req.serviceTokenData.user._id + userEmail = req.serviceTokenData.user.email; } const [err, secrets] = await to(Secret.find( @@ -204,7 +207,7 @@ export const getSecrets = async (req: Request, res: Response) => { if (postHogClient) { postHogClient.capture({ event: 'secrets added', - distinctId: req.user.email, + distinctId: userEmail, properties: { numberOfSecrets: secrets.length, environment, From 35466a7f4a72afe09ba4a5f8060e71841c76675a Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 21:58:03 -0800 Subject: [PATCH 54/89] Modify get secrets logic --- cli/packages/util/secrets.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cli/packages/util/secrets.go b/cli/packages/util/secrets.go index c0a3ad9526..131a8e66db 100644 --- a/cli/packages/util/secrets.go +++ b/cli/packages/util/secrets.go @@ -117,11 +117,9 @@ func GetAllEnvironmentVariables(envName string) ([]models.SingleEnvironmentVaria secrets, err := GetPlainTextSecretsViaJTW(loggedInUserDetails.UserCredentials.JTWToken, loggedInUserDetails.UserCredentials.PrivateKey, workspaceFile.WorkspaceId, envName) return secrets, err - } else if infisicalToken != "" { + } else { log.Debug("Trying to fetch secrets using service token") return GetPlainTextSecretsViaServiceToken(infisicalToken) - } else { - return nil, fmt.Errorf("unable to fetch secrets because we could not find a service token or a logged in user") } } From d579684d2f76655742ac94c4fde2c4a1acec825f Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 22:00:06 -0800 Subject: [PATCH 55/89] increase version --- cli/packages/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/packages/cmd/root.go b/cli/packages/cmd/root.go index b1b53d2ff2..e68c6826c6 100644 --- a/cli/packages/cmd/root.go +++ b/cli/packages/cmd/root.go @@ -15,7 +15,7 @@ var rootCmd = &cobra.Command{ Short: "Infisical CLI is used to inject environment variables into any process", Long: `Infisical is a simple, end-to-end encrypted service that enables teams to sync and manage their environment variables across their development life cycle.`, CompletionOptions: cobra.CompletionOptions{HiddenDefaultCmd: true}, - Version: "0.2.0", + Version: "0.2.1", } // Execute adds all child commands to the root command and sets flags appropriately. From d958341154e346d677edf640d40119b254c41e09 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Thu, 12 Jan 2023 22:15:17 -0800 Subject: [PATCH 56/89] Corrected the telemetery event name --- backend/src/controllers/v2/secretsController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index b5a27874f8..ea495ad87e 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -206,7 +206,7 @@ export const getSecrets = async (req: Request, res: Response) => { if (postHogClient) { postHogClient.capture({ - event: 'secrets added', + event: 'secrets pulled', distinctId: userEmail, properties: { numberOfSecrets: secrets.length, From f57f3e6475e3c00da577a2f6b5b2189bdfcdcb17 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Thu, 12 Jan 2023 23:26:52 -0800 Subject: [PATCH 57/89] enable integ --- backend/src/variables/integration.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index baea2b0930..a1aa5d94d3 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -47,7 +47,7 @@ const INTEGRATION_OPTIONS = [ name: 'Vercel', slug: 'vercel', image: 'Vercel', - isAvailable: false, + isAvailable: true, type: 'vercel', clientId: '', clientSlug: CLIENT_SLUG_VERCEL, @@ -57,7 +57,7 @@ const INTEGRATION_OPTIONS = [ name: 'Netlify', slug: 'netlify', image: 'Netlify', - isAvailable: false, + isAvailable: true, type: 'oauth2', clientId: CLIENT_ID_NETLIFY, docsLink: '' @@ -66,7 +66,7 @@ const INTEGRATION_OPTIONS = [ name: 'GitHub', slug: 'github', image: 'GitHub', - isAvailable: false, + isAvailable: true, type: 'oauth2', clientId: CLIENT_ID_GITHUB, docsLink: '' From b67abf94d4e4e265686735e35d406856216cffaf Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Fri, 13 Jan 2023 01:02:43 -0800 Subject: [PATCH 58/89] Fixing minor bugs for custom environments --- frontend/components/basic/Listbox.tsx | 2 +- .../basic/dialog/AddServiceTokenDialog.js | 4 +- frontend/pages/dashboard/[id].tsx | 75 ++----------------- frontend/pages/settings/project/[id].tsx | 1 + 4 files changed, 12 insertions(+), 70 deletions(-) diff --git a/frontend/components/basic/Listbox.tsx b/frontend/components/basic/Listbox.tsx index d315df2e62..c67735d6e5 100644 --- a/frontend/components/basic/Listbox.tsx +++ b/frontend/components/basic/Listbox.tsx @@ -48,7 +48,7 @@ export default function ListBox({ {text} {' '} - {selected} + {selected?.charAt(0).toUpperCase() + selected?.slice(1)}
{data && ( diff --git a/frontend/components/basic/dialog/AddServiceTokenDialog.js b/frontend/components/basic/dialog/AddServiceTokenDialog.js index a8c5c15a74..0be3307082 100644 --- a/frontend/components/basic/dialog/AddServiceTokenDialog.js +++ b/frontend/components/basic/dialog/AddServiceTokenDialog.js @@ -66,7 +66,7 @@ const AddServiceTokenDialog = ({ let newServiceToken = await addServiceToken({ name: serviceTokenName, workspaceId, - environment: selectedServiceTokenEnv.slug, + environment: selectedServiceTokenEnv?.slug ? selectedServiceTokenEnv.slug : environments[0]?.name, expiresIn: expiryMapping[serviceTokenExpiresIn], encryptedKey: ciphertext, iv, @@ -156,7 +156,7 @@ const AddServiceTokenDialog = ({
name)} onChange={(envName) => setSelectedServiceTokenEnv( diff --git a/frontend/pages/dashboard/[id].tsx b/frontend/pages/dashboard/[id].tsx index 8f6d21c132..395ed2924a 100644 --- a/frontend/pages/dashboard/[id].tsx +++ b/frontend/pages/dashboard/[id].tsx @@ -237,7 +237,9 @@ export default function Dashboard() { setInitialData(dataToSort); reorderRows(dataToSort); - setIsLoading(false); + setTimeout( + () => setIsLoading(false) + , 700); } catch (error) { console.log('Error', error); setData(undefined); @@ -361,67 +363,6 @@ export default function Dashboard() { // Once "Save changes" is clicked, disable that button setButtonReady(false); - const secretsToBeDeleted = initialData - .filter( - (initDataPoint) => - !newData! - .map((newDataPoint) => newDataPoint.id) - .includes(initDataPoint.id) - ) - .map((secret) => secret.id); - - const secretsToBeAdded = newData!.filter( - (newDataPoint) => - !initialData - .map((initDataPoint) => initDataPoint.id) - .includes(newDataPoint.id) - ); - - const secretsToBeUpdated = newData!.filter((newDataPoint) => - initialData - .filter( - (initDataPoint) => - newData! - .map((newDataPoint) => newDataPoint.id) - .includes(initDataPoint.id) && - (newData!.filter( - (newDataPoint) => newDataPoint.id == initDataPoint.id - )[0].value != initDataPoint.value || - newData!.filter( - (newDataPoint) => newDataPoint.id == initDataPoint.id - )[0].key != initDataPoint.key || - newData!.filter( - (newDataPoint) => newDataPoint.id == initDataPoint.id - )[0].comment != initDataPoint.comment) - ) - .map((secret) => secret.id) - .includes(newDataPoint.id) - ); - - if (secretsToBeDeleted.length > 0) { - await deleteSecrets({ secretIds: secretsToBeDeleted }); - } - // ENV - if (secretsToBeAdded.length > 0) { - const secrets = await encryptSecrets({ - secretsToEncrypt: secretsToBeAdded, - workspaceId, - env: selectedEnv.slug, - }); - secrets && - (await addSecrets({ - secrets, - env: selectedEnv.slug, - workspaceId, - })); - } - if (secretsToBeUpdated.length > 0) { - const secrets = await encryptSecrets({ - secretsToEncrypt: secretsToBeUpdated, - workspaceId, - env: selectedEnv.slug, - }); - secrets && (await updateSecrets({ secrets })); const secretsToBeDeleted = initialData! .filter(initDataPoint => !newData!.map(newDataPoint => newDataPoint.id).includes(initDataPoint.id)) @@ -471,11 +412,11 @@ export default function Dashboard() { await deleteSecrets({ secretIds: secretsToBeDeleted.concat(overridesToBeDeleted) }); } if (secretsToBeAdded.concat(overridesToBeAdded).length > 0) { - const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded.concat(overridesToBeAdded), workspaceId, env: envMapping[env] }); - secrets && await addSecrets({ secrets, env: envMapping[env], workspaceId }); + const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeAdded.concat(overridesToBeAdded), workspaceId, env: selectedEnv.slug }); + secrets && await addSecrets({ secrets, env: selectedEnv.slug, workspaceId }); } if (secretsToBeUpdated.concat(overridesToBeUpdated).length > 0) { - const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeUpdated.concat(overridesToBeUpdated), workspaceId, env: envMapping[env] }); + const secrets = await encryptSecrets({ secretsToEncrypt: secretsToBeUpdated.concat(overridesToBeUpdated), workspaceId, env: selectedEnv.slug }); secrets && await updateSecrets({ secrets }); } @@ -643,7 +584,7 @@ export default function Dashboard() { onButtonPressed={async () => { // Update secrets in the state only for the current environment const rolledBackSecrets = snapshotData.secretVersions - .filter(row => reverseEnvMapping[row.environment] == env) + .filter(row => row.environment == selectedEnv.slug) .map((sv, position) => { return { id: sv.id, idOverride: sv.id, pos: position, valueOverride: sv.valueOverride, key: sv.key, value: sv.value, comment: '' @@ -799,7 +740,7 @@ export default function Dashboard() { /> ))} {snapshotData && snapshotData.secretVersions?.sort((a, b) => a.key.localeCompare(b.key)) - .filter(row => reverseEnvMapping[row.environment] == snapshotEnv) + .filter(row => row.environment == selectedSnapshotEnv?.slug) .filter(row => row.key.toUpperCase().includes(searchKeys.toUpperCase())) .filter( row => !(snapshotData.secretVersions?.filter(row => (snapshotData.secretVersions diff --git a/frontend/pages/settings/project/[id].tsx b/frontend/pages/settings/project/[id].tsx index 619ef2e6be..5b9e3f5de9 100644 --- a/frontend/pages/settings/project/[id].tsx +++ b/frontend/pages/settings/project/[id].tsx @@ -25,6 +25,7 @@ type EnvData = { name: string; slug: string; }; + export default function SettingsBasic() { const [buttonReady, setButtonReady] = useState(false); const router = useRouter(); From 6c61aef526117ec32bf9234f7d20b32ea69fe88a Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Fri, 13 Jan 2023 01:29:14 -0800 Subject: [PATCH 59/89] hotfix: fix the bug with pushing multiple envars in a sequence --- frontend/pages/dashboard/[id].tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/dashboard/[id].tsx b/frontend/pages/dashboard/[id].tsx index 07b4133a02..949db52c0f 100644 --- a/frontend/pages/dashboard/[id].tsx +++ b/frontend/pages/dashboard/[id].tsx @@ -412,7 +412,7 @@ export default function Dashboard() { secrets && await updateSecrets({ secrets }); } - setInitialData(newData); + setInitialData(structuredClone(newData)); // If this user has never saved environment variables before, show them a prompt to read docs if (!hasUserEverPushed) { From 6992c51e17bcbde1a49a0078db329b1dd6a1bcb5 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Fri, 13 Jan 2023 16:36:34 +0700 Subject: [PATCH 60/89] Working fixing integrations race condition --- backend/src/helpers/secret.ts | 2 +- backend/src/integrations/sync.ts | 231 +++++++++++++++---------------- 2 files changed, 116 insertions(+), 117 deletions(-) diff --git a/backend/src/helpers/secret.ts b/backend/src/helpers/secret.ts index 59d72f4695..74ba062bda 100644 --- a/backend/src/helpers/secret.ts +++ b/backend/src/helpers/secret.ts @@ -39,7 +39,7 @@ const validateSecrets = async ({ try { secrets = await Secret.find({ _id: { - $in: secretIds + $in: secretIds.map((secretId: string) => new Types.ObjectId(secretId)) } }); diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts index c4a4f16eea..81bbe75490 100644 --- a/backend/src/integrations/sync.ts +++ b/backend/src/integrations/sync.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import axios, { AxiosError } from 'axios'; import * as Sentry from '@sentry/node'; import { Octokit } from '@octokit/rest'; // import * as sodium from 'libsodium-wrappers'; @@ -145,7 +145,6 @@ const syncSecretsVercel = async ({ secrets: any; accessToken: string; }) => { - interface VercelSecret { id?: string; type: string; @@ -155,131 +154,131 @@ const syncSecretsVercel = async ({ } try { - // Get all (decrypted) secrets back from Vercel in - // decrypted format - const params: { [key: string]: string } = { - decrypt: 'true', - ...( integrationAuth?.teamId ? { - teamId: integrationAuth.teamId - } : {}) - } - - const res = (await Promise.all((await axios.get( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, - { + // Get all (decrypted) secrets back from Vercel in + // decrypted format + const params: { [key: string]: string } = { + decrypt: 'true', + ...( integrationAuth?.teamId ? { + teamId: integrationAuth.teamId + } : {}) + } + + const res = (await Promise.all((await axios.get( + `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`, + { + params, + headers: { + Authorization: `Bearer ${accessToken}` + } + } + )) + .data + .envs + .filter((secret: VercelSecret) => secret.target.includes(integration.target)) + .map(async (secret: VercelSecret) => (await axios.get( + `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, + { params, headers: { Authorization: `Bearer ${accessToken}` } - } - )) - .data - .envs - .filter((secret: VercelSecret) => secret.target.includes(integration.target)) - .map(async (secret: VercelSecret) => (await axios.get( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, - { - params, - headers: { - Authorization: `Bearer ${accessToken}` - } - } - )).data) - )).reduce((obj: any, secret: any) => ({ - ...obj, - [secret.key]: secret - }), {}); - - const updateSecrets: VercelSecret[] = []; - const deleteSecrets: VercelSecret[] = []; - const newSecrets: VercelSecret[] = []; + } + )).data) + )).reduce((obj: any, secret: any) => ({ + ...obj, + [secret.key]: secret + }), {}); + + const updateSecrets: VercelSecret[] = []; + const deleteSecrets: VercelSecret[] = []; + const newSecrets: VercelSecret[] = []; - // Identify secrets to create - Object.keys(secrets).map((key) => { - if (!(key in res)) { - // case: secret has been created - newSecrets.push({ - key: key, - value: secrets[key], - type: 'encrypted', - target: [integration.target] - }); - } - }); - - // Identify secrets to update and delete - Object.keys(res).map((key) => { - if (key in secrets) { - if (res[key].value !== secrets[key]) { - // case: secret value has changed - updateSecrets.push({ - id: res[key].id, - key: key, - value: secrets[key], - type: 'encrypted', - target: [integration.target] - }); - } - } else { - // case: secret has been deleted - deleteSecrets.push({ - id: res[key].id, - key: key, - value: res[key].value, - type: 'encrypted', - target: [integration.target], - }); - } - }); + // Identify secrets to create + Object.keys(secrets).map((key) => { + if (!(key in res)) { + // case: secret has been created + newSecrets.push({ + key: key, + value: secrets[key], + type: 'encrypted', + target: [integration.target] + }); + } + }); + + // Identify secrets to update and delete + Object.keys(res).map((key) => { + if (key in secrets) { + if (res[key].value !== secrets[key]) { + // case: secret value has changed + updateSecrets.push({ + id: res[key].id, + key: key, + value: secrets[key], + type: 'encrypted', + target: [integration.target] + }); + } + } else { + // case: secret has been deleted + deleteSecrets.push({ + id: res[key].id, + key: key, + value: res[key].value, + type: 'encrypted', + target: [integration.target], + }); + } + }); - // Sync/push new secrets - if (newSecrets.length > 0) { - await axios.post( - `${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`, - newSecrets, - { - params, - headers: { - Authorization: `Bearer ${accessToken}` - } + // Sync/push new secrets + if (newSecrets.length > 0) { + await axios.post( + `${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`, + newSecrets, + { + params, + headers: { + Authorization: `Bearer ${accessToken}` } - ); - } + } + ); + } - // Sync/push updated secrets - if (updateSecrets.length > 0) { - updateSecrets.forEach(async (secret: VercelSecret) => { - const { - id, - ...updatedSecret - } = secret; - await axios.patch( - `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, - updatedSecret, - { - params, - headers: { - Authorization: `Bearer ${accessToken}` - } + // Sync/push updated secrets + if (updateSecrets.length > 0) { + updateSecrets.forEach(async (secret: VercelSecret) => { + const { + id, + ...updatedSecret + } = secret; + await axios.patch( + `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`, + updatedSecret, + { + params, + headers: { + Authorization: `Bearer ${accessToken}` } - ); - }); - } + } + ); + }); + } - // 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}` - } + // 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}` } - ); - }); - } + } + ); + }); + } } catch (err) { Sentry.setUser(null); Sentry.captureException(err); From cbd8302afe6897db134b5ed90fd5acd4fea8adbb Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Fri, 13 Jan 2023 18:15:17 +0700 Subject: [PATCH 61/89] Add temp patch for CRUD ops race conditions --- .../src/controllers/v2/secretsController.ts | 30 +- backend/src/integrations/sync.ts | 352 +++++++++--------- 2 files changed, 193 insertions(+), 189 deletions(-) diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index ea495ad87e..098cfed6d0 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -67,8 +67,17 @@ export const createSecrets = async (req: Request, res: Response) => { })) ); + setTimeout(async () => { + // trigger event - push secrets + await EventService.handleEvent({ + event: eventPushSecrets({ + workspaceId + }) + }); + }, 5000); + // (EE) add secret versions for new secrets - EESecretService.addSecretVersions({ + await EESecretService.addSecretVersions({ secretVersions: newSecrets.map(({ _id, version, @@ -104,13 +113,6 @@ export const createSecrets = async (req: Request, res: Response) => { })) }); - // trigger event - push secrets - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId - }) - }); - const addAction = await EELogService.createActionSecret({ name: ACTION_ADD_SECRETS, userId: req.user._id.toString(), @@ -342,11 +344,13 @@ export const updateSecrets = async (req: Request, res: Response) => { Object.keys(workspaceSecretObj).forEach(async (key) => { // trigger event - push secrets - await EventService.handleEvent({ - event: eventPushSecrets({ - workspaceId: key - }) - }); + setTimeout(async () => { + await EventService.handleEvent({ + event: eventPushSecrets({ + workspaceId: key + }) + }); + }, 10000); const updateAction = await EELogService.createActionSecret({ name: ACTION_UPDATE_SECRETS, diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts index 81bbe75490..0ae57dc594 100644 --- a/backend/src/integrations/sync.ts +++ b/backend/src/integrations/sync.ts @@ -306,188 +306,188 @@ const syncSecretsNetlify = async ({ }) => { try { - interface NetlifyValue { - id?: string; - context: string; // 'dev' | 'branch-deploy' | 'deploy-preview' | 'production', - value: string; - } - - interface NetlifySecret { - key: string; - values: NetlifyValue[]; - } - - interface NetlifySecretsRes { - [index: string]: NetlifySecret; - } - - const getParams = new URLSearchParams({ - context_name: 'all', // integration.context or all - site_id: integration.siteId - }); - - const res = (await axios.get( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, - { - params: getParams, - headers: { - Authorization: `Bearer ${accessToken}` - } - } - )) - .data - .reduce((obj: any, secret: any) => ({ - ...obj, - [secret.key]: secret - }), {}); - - const newSecrets: NetlifySecret[] = []; // createEnvVars - const deleteSecrets: string[] = []; // deleteEnvVar - const deleteSecretValues: NetlifySecret[] = []; // deleteEnvVarValue - const updateSecrets: NetlifySecret[] = []; // setEnvVarValue - - // identify secrets to create and update - Object.keys(secrets).map((key) => { - if (!(key in res)) { - // case: Infisical secret does not exist in Netlify -> create secret - newSecrets.push({ - key, - values: [{ - value: secrets[key], - context: integration.context - }] - }); - } else { - // case: Infisical secret exists in Netlify - const contexts = res[key].values - .reduce((obj: any, value: NetlifyValue) => ({ - ...obj, - [value.context]: value - }), {}); - - if (integration.context in contexts) { - // case: Netlify secret value exists in integration context - if (secrets[key] !== contexts[integration.context].value) { - // case: Infisical and Netlify secret values are different - // -> update Netlify secret context and value - updateSecrets.push({ - key, - values: [{ - context: integration.context, - value: secrets[key] - }] - }); - } - } else { - // case: Netlify secret value does not exist in integration context - // -> add the new Netlify secret context and value - updateSecrets.push({ - key, - values: [{ - context: integration.context, - value: secrets[key] - }] - }); - } - } - }) - - // identify secrets to delete - // TODO: revise (patch case where 1 context was deleted but others still there - Object.keys(res).map((key) => { - // loop through each key's context - if (!(key in secrets)) { - // case: Netlify secret does not exist in Infisical - - const numberOfValues = res[key].values.length; - - res[key].values.forEach((value: NetlifyValue) => { - if (value.context === integration.context) { - if (numberOfValues <= 1) { - // case: Netlify secret value has less than 1 context -> delete secret - deleteSecrets.push(key); - } else { - // case: Netlify secret value has more than 1 context -> delete secret value context - deleteSecretValues.push({ - key, - values: [{ - id: value.id, - context: integration.context, - value: value.value - }] - }); - } - } - }); - } - }); + interface NetlifyValue { + id?: string; + context: string; // 'dev' | 'branch-deploy' | 'deploy-preview' | 'production', + value: string; + } + + interface NetlifySecret { + key: string; + values: NetlifyValue[]; + } + + interface NetlifySecretsRes { + [index: string]: NetlifySecret; + } + + const getParams = new URLSearchParams({ + context_name: 'all', // integration.context or all + site_id: integration.siteId + }); + + const res = (await axios.get( + `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, + { + params: getParams, + headers: { + Authorization: `Bearer ${accessToken}` + } + } + )) + .data + .reduce((obj: any, secret: any) => ({ + ...obj, + [secret.key]: secret + }), {}); + + const newSecrets: NetlifySecret[] = []; // createEnvVars + const deleteSecrets: string[] = []; // deleteEnvVar + const deleteSecretValues: NetlifySecret[] = []; // deleteEnvVarValue + const updateSecrets: NetlifySecret[] = []; // setEnvVarValue + + // identify secrets to create and update + Object.keys(secrets).map((key) => { + if (!(key in res)) { + // case: Infisical secret does not exist in Netlify -> create secret + newSecrets.push({ + key, + values: [{ + value: secrets[key], + context: integration.context + }] + }); + } else { + // case: Infisical secret exists in Netlify + const contexts = res[key].values + .reduce((obj: any, value: NetlifyValue) => ({ + ...obj, + [value.context]: value + }), {}); + + if (integration.context in contexts) { + // case: Netlify secret value exists in integration context + if (secrets[key] !== contexts[integration.context].value) { + // case: Infisical and Netlify secret values are different + // -> update Netlify secret context and value + updateSecrets.push({ + key, + values: [{ + context: integration.context, + value: secrets[key] + }] + }); + } + } else { + // case: Netlify secret value does not exist in integration context + // -> add the new Netlify secret context and value + updateSecrets.push({ + key, + values: [{ + context: integration.context, + value: secrets[key] + }] + }); + } + } + }) + + // identify secrets to delete + // TODO: revise (patch case where 1 context was deleted but others still there + Object.keys(res).map((key) => { + // loop through each key's context + if (!(key in secrets)) { + // case: Netlify secret does not exist in Infisical + + const numberOfValues = res[key].values.length; + + res[key].values.forEach((value: NetlifyValue) => { + if (value.context === integration.context) { + if (numberOfValues <= 1) { + // case: Netlify secret value has less than 1 context -> delete secret + deleteSecrets.push(key); + } else { + // case: Netlify secret value has more than 1 context -> delete secret value context + deleteSecretValues.push({ + key, + values: [{ + id: value.id, + context: integration.context, + value: value.value + }] + }); + } + } + }); + } + }); - const syncParams = new URLSearchParams({ - site_id: integration.siteId - }); + const syncParams = new URLSearchParams({ + site_id: integration.siteId + }); - if (newSecrets.length > 0) { - await axios.post( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, - newSecrets, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - } + if (newSecrets.length > 0) { + await axios.post( + `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`, + newSecrets, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}` + } + } + ); + } - if (updateSecrets.length > 0) { - updateSecrets.forEach(async (secret: NetlifySecret) => { - await axios.patch( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`, - { - context: secret.values[0].context, - value: secret.values[0].value - }, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - }); - } + if (updateSecrets.length > 0) { + updateSecrets.forEach(async (secret: NetlifySecret) => { + await axios.patch( + `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`, + { + context: secret.values[0].context, + value: secret.values[0].value + }, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}` + } + } + ); + }); + } - if (deleteSecrets.length > 0) { - deleteSecrets.forEach(async (key: string) => { - await axios.delete( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - }); - } + if (deleteSecrets.length > 0) { + deleteSecrets.forEach(async (key: string) => { + await axios.delete( + `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}` + } + } + ); + }); + } - if (deleteSecretValues.length > 0) { - deleteSecretValues.forEach(async (secret: NetlifySecret) => { - await axios.delete( - `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`, - { - params: syncParams, - headers: { - Authorization: `Bearer ${accessToken}` - } - } - ); - }); - } + if (deleteSecretValues.length > 0) { + deleteSecretValues.forEach(async (secret: NetlifySecret) => { + await axios.delete( + `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`, + { + params: syncParams, + headers: { + Authorization: `Bearer ${accessToken}` + } + } + ); + }); + } } catch (err) { - Sentry.setUser(null); - Sentry.captureException(err); - throw new Error('Failed to sync secrets to Heroku'); + Sentry.setUser(null); + Sentry.captureException(err); + throw new Error('Failed to sync secrets to Heroku'); } } From 29592a1e9e89299c3d1dd084be38cf45811802b6 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Fri, 13 Jan 2023 20:25:13 +0700 Subject: [PATCH 62/89] Add SMTP support for AWS SES and docs for it --- backend/src/services/smtp.ts | 8 ++++++- docs/images/email-aws-ses-console.png | Bin 0 -> 378652 bytes docs/images/email-aws-ses-user.png | Bin 0 -> 176008 bytes docs/self-hosting/configuration/email.mdx | 28 +++++++++++++++++++--- 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 docs/images/email-aws-ses-console.png create mode 100644 docs/images/email-aws-ses-user.png diff --git a/backend/src/services/smtp.ts b/backend/src/services/smtp.ts index 12841eee7d..f960f6bae3 100644 --- a/backend/src/services/smtp.ts +++ b/backend/src/services/smtp.ts @@ -28,7 +28,13 @@ if (SMTP_SECURE) { } break; default: - mailOpts.secure = true; + if (SMTP_HOST.includes('amazonaws.com')) { + mailOpts.tls = { + ciphers: 'TLSv1.2' + } + } else { + mailOpts.secure = true; + } break; } } diff --git a/docs/images/email-aws-ses-console.png b/docs/images/email-aws-ses-console.png new file mode 100644 index 0000000000000000000000000000000000000000..2882ba4d5f6aef3febc27f9a75c61795eba5a8c2 GIT binary patch literal 378652 zcmbrl2Rz)(_AstX2qGeaC?SYw(OZ-xO7tFmbyi!^7ORtlC?O$wiyk#PyH<&A^|n}x z5WV-h{5Q|@+Z@w8EVpHqZ}51mnX^8^eR1<4Aug=4K7=bG{KjK0=*V5CyA@WgKe8t;hz#Y%JS)s! zE|I;t0lOIR-D&h4kU5d`+73jdovqB^29>nlBM~&W!4V+5qxH5~l!sS3_&q(|?Vw3~S4aHJ zWp;gqdjmyM?}z)N;fAlZt*tBNw(R7CJxZ2ml$jZaiDI0Ak;#u@dTa~2eTov5S}%4x ze%u1jUZ06w)mhuVDsWF?iHv_j5-uyGP2<5Q#r0|+BzNe${jxg;hC^JB?zU9_1Aak^ zlm@3v=Cj$Tp?Q1t<%^16u8&Fz?Uz6JvlbY}Q$HO1!fh##pUjie!=)CJfYR~d_PM+q zE1a|+_}TSVy-G|<0N5GESr?g0xqt417r2w$7_dJp*b%svIQV(zORfT8x3Asc*dWzY zTg!;WUp1~i(oKTW!)}3J+l@0+k|=pkR%}iYTMKEg&Hy*}Ukq%LCCao3fz3~aLSHfC zYPS&j#S#Krh=(ni321}J_8&Ex^vJ#JLt~wAu5_% z%y?{BL_(ijLMWdSPqZ+K;fl@^`aNg9fv?5P$W8PhmP8=<1~(z1l}__UM62z0GTjim z@5E!F`14PJ#2hV#ZzxR&k*&_Vx4T=Iy>BYC5I-e)ew&=-<=t0QBVnq{T-}V>FBxO` z?%tA@-P2@=$GgeQS^dC>nEGX&CR;VVLlF7%uB@sdn?edG#nH>QA>&ojs1T*^bS0#z zKithF`hr5|AA3`#;n}sFy}@m~Sr+8@6Z3}N?_Lwh+SclK|vy@kJ3{~`Q?*&?d&g%B&0Dkt2& zUAUdZQg9D%k8&?PBqZc(NaPPc!loY%??~Q-k|f?erRZ4ioZvRk8d0MWBHT@x) z1I`-PlW)!EhG+OYEF}^AfJAT_eTj!+lXB(X*F?7>(0J>+n9i84Wh!MFX!>=V z9PJJ*KP-d*AodU&NWtOzBhGWib3cp#X7rp7xGqG*6Z-nOqy|0z5dV#*v2>dvtHSpM zg#@ESOdKaIPbb5Ha-zy2PXyNm==di@$@u}G8SBfIx8^TIm4v52BhLH7V|_vs9&mfu zn!6yGV1i+jVcZm}du2m`Ppwbs84qa*A({nkSrfs4Sw zV8<z3EiGM6y52{?D_y8PjLLe}Vt%)M#Ugz+!*I_A?_uSx5( z#nAn|6VCm$BfX>O`T$g`L&rzH3z+cILSp>v|;CfU9f{1WeF?fYjW!Z)ek)`kT?eLXqxz3Zp# kXD^@FEgXJmL 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/images/email-aws-ses-user.png b/docs/images/email-aws-ses-user.png new file mode 100644 index 0000000000000000000000000000000000000000..f740e58a5eee015348fd93031b8b8652c9119ca7 GIT binary patch literal 176008 zcmdqJbzD^K+C2^kGKfkkNW+jy44p$rqXVcYA>AU~Lxa*tDBUUw79b%Y4BcHy=g{3< z-+P{Z&w1W+e(&?=58=aR&%Sruab4?LYi&dCsVZK%M0E)Z3+sxqlH3CGA3Lty{Y3i5IZ0McR{UwztR~uosLw1waX~K6upI7CnTf zPSOSZlvDDDw6Thqg-xj0_cCX0#z@{LK?Jy9RmTik-@k;#>{PGXHua9dK?xf&a;ek{ zyY@T8)70GDfn-g3kY-hPh$XSW0 zxl*=MA~Zbq)};_>fd>@MFbNK&&S&qszgo^Yv7NGsKB6L)=%C@{H%k6)oxyN8{-S%* zQg!Y)f5w6N%LmK3P;aJe?bz##U2&Yod|63c$t@fzQSl?{?wsx?CqF(6T6Rt~1*jH^ zNbo&r48BFV!(b-jpr4O_oN!X z$Cv?^((lR^GfKBbPPU!XC_Na-ovS~rHY3H{=@^&d zC`ZaJVvR;cL`BhOC(*?lsIl1~ZmwoP5-3#T^IW`5d=)BtO^K{G7{S2NO!ronE*f_2vb^->J!mWr z0mIEAn#cIpW#8XpDWZPrclCDD>%wlc4@6}|yRvoNItzp^o+*E&`a+me>tra_?)Q9> z9Z8akV^Mc#kBuV8^|P8fwWszXue>x7WVUe^c@6L0CH7mbSY{D#GZR1XJjA(0$s&^@ zcl}N4{cj)4AF_R-b|GVtt&s6nFzLA_aK$FVRTlqsW)F?CkOzwgQyHmVl&x%r;)L9O zregO5ujz}cjhCjwjEvAcHxu6{${<9|>9ejoVLMUthaCDf7=P5_|H3bXMIEJ5$6aSz zzgAyt*kyS9^}^tNnycBNg|*+OYwfL5aWz9m{70h1Bg*}$>%D6^CU-aYkWyYa`hEuC zuqI)&+LYa7@P-Ml*)xKgIAVoK(k%!}Kt+IRtza#K(Tfi<0!(FOnIV?-g7ue-`8RPk zNjB4-J$v@%S$M4%US;jmz)OM8FC|<%x-J>>P&uBN)sj*9&am7krZmPewhE@0mMl}4 zBaXJrxSTCR29p${?}K;j!|VmuzK4Cj_W9b#H9Pi74JdmOJ8vvG$1F!22krf`terba zudP3|KQ!madzkw`GRsTT{bAr+<80EbS04-RzrR0Q(CYLk_0z{sdABsbNxMWXiT=dC z6lN@E^gtxmCAK1#F!r|Km*ga>yId+cTC0AK^M*spJ{{@q-q_XMy|o*&#CI#~=^eBC z{2y3;)cp|u0V{O2;5FYgw@Lg7UvB&oMKw&NPX&*FMvzC0P#FkVCRZjeChG`{2+~`< z`NsKe&Wh{j@Ozir%W=iY#VJCHuP34BtM_K@>X=hm z-p&UPeGf|yGmq?@pxv8CbVpvNe5ZX!uqR6b7rCBm+!j}(=I!Q%@I+Is2rUTS;1}S3 zA*5?HXnZgj@fcL)X=n@_uHkAeTs&7?-LmKUoLOb5zPZmPH$#Naw7R`XuJdb24p$R`o}MT|c?{ zyIQS^t&R`t3_GoSUa=F6raht25Oo$oC0Pk32)hl8dPZJrB5vA^)Qg-*O-++i?NF`j zZ0ro}{MqTY_;knqNa2Wu;zf8#+pPpL?hL&&U6$Nny>sV6*Sl_3&YqH8)^v8`=JbNB=Z=9@R%avkzbuYyaS;5HAi?#3;$VLsjluq}O;hGmLsL!jCiB5vdCBLT zwcN(;a&F8lj>@JcGr{cZA6|V3?wRZ9UC6!hJ~5E@3NO8hxA9#QGY2QMM!jCdK-hef z8}lToG3g%53+@fQuQoN4GEISjgzKDqIDqZWm!jl_c)Qbl+*Z)5v6#Ct8QG=T)+&^$y9h2d?*@x4jzha% zoZhFsl3Bs;2i`|n)SDNXqWQ`AqX-`2Ek4`U_R#*W?JdJ$^yXgtYH|gar`$E>VYZ<9 z@OoNOK~w3am&td1qxa^KGY@CI9#a+WDCOSCWhi0m;pBFRo6PH%Tca^0}K7v2D}cF&bKSZ&dNugs#vw{8}84nxEBkX z+wH@445;=jS2@P_b5!rKeUADdH8DBb-Tk3Ys~1;TIVETxHtKuo*X18+zC1U^^&=kr zeDd|qWVmc-@EXlku~C-*pLd5JT6&f=?pWQY{t-VQ)SEucy&ST>AWn|_^0_akaJRH$ zvyAK_+b7je8Jwc>@JIGfPn^#dd-!Yl^E$IqX%`;%?r!Lge%G0w&seV@cAn~$uL_?p zqa4eIeK!zu7~0*+=h)#`)lADR-WAy^I$JuhvdUAp8xfqhq8-r7b=&J$7I&%JZX8OH z6o!o(kCl0d@3>B+Er{>0aiciOn=1BCd>T)Mi5%%(Na&tIwm&T7Z%uEpiK#vHEj-AP zpJNsuTJM|K7T8*>!|tqZn`o%=rR78__-IJJllrkSdUhCUR5i5>BS+ovy6Gjl_i2MD zPU@3EN|oeM_CSUNlLOOpJ2|_?30tr1!`!vg(S5zlsZ2FVQ=c2hT06?y>ZcY*^J}nt zgP_KEp9b%o1M|(CZO`s~#p$H!X_?{>3DHZhqw(0-E^HKE#d3sWT{8`0;7&~b%!ZeI zM3Kn6k%4@=mwt^6E15))myWj!%br*}>ofMfEgVB*fv_!{ug{-*sdQDxY##*}zLZKZ z*Yt9vU2R!ul-VFkN}fG`urDPTt$3PpMxnWId8LD}+Y~_9crz_!a}^b=TVNZ4g@;Xw zbpdQ)gMV1qR9JYwY-3^F!KVIW`vErlZ^z(ZVFg=a;r@2aWAGdE6%Bqs>)(D~col?& z5B?$rKLM|Cen0w>$m$LZ#K6D+cQiE@dmyLqTXXQ2 z1oVlsv%MG$=H}+c?Z(e-=V$@r6%`eQ@$kX;__)9kTu$z`&PGTsTPLPpPxAYDFy}QgwsUcofI=}B`s3@@`!qvZ{&^)^r{5k6JRl7735=JU2ljW(oGs1& zmu8qxer@*4bNzZbIOb$x_bicS)>?9wHlVA)tx56;@e09zxz4|R^yi)a+Vqi=nWL5ftiz?wF{;bAK6+uOY*^C zf4BQzTWSB3egKAy$O|nh%0wnTKXkx@B7T1NI&T(yx0NVcOexNA0NESJlp=J?6%G+$BfIM?#GK55E@}aAMr?;PG>kipU!_`|41eVf0HJ&P*Uc9I)fSfGh6@Z=!Zx*kwjPLPZ!7$kp9zW zh_1eG_D`3N-0Nwudwg*gK=2Tpa+$RM_yiU5lN=Q;#%Y>= z#saalYPJk7QaaC-{0($CET~KB9QA(2XZN$g6{k*73oX-^^0fn%d*9;G-T#*e&U2GA zy4vAEyNh1o?;R$sjza|BkQO;w$gCT&s~QuqPQv1E=aC(BoA9oCaF^>5YiaHOA;woT zs~HxYBZ`#MpJDyc%oH-}dw$w-)O#0}HYEA+3u>#C!>DA;l`zA5Pg@7(-g-0pa(24M z(?svRp_InsdNQ>D;c+ArneAk?e<)jXd*hSY*#v{DT@w4c=F6{dKd+S`I7Zj*4j9mLSM2l(EFP~{ z`QDwKZ-Z4K)-qw02hdQf9(jr@Qm2b*eaJTM`a`}BfxZCcZ?<7cp(+Tu5rl-M8@tD_&_Dcni|F&%D&DO>-M}H;UIbUo1$w0%`TxipdDr zANPb6`o^dTSt`Gd4}Djsxi@T;x3SZw#`ZDQrg*3}$s%1_v|+j_iYS=g2RT#i*XB;T1@(H5N2MpFb^#cBeqwY7dI%U`DIcFNcWs_*m z!FdC1O2&%6o{*>EzG-b?%Sc^gRSDrSVrggA#WdEk${zwV0Wg&=h#>IPle;)MO1Tra+U;f2<)GGJ8xio}$B)^G{+V0K( z?EB0sZggXw_o|iAe43kT-A)r;fRp{{K-F2?iF;Jo>83~db~16d&)MPp;!UA3=RQBp zRt=jJo8lH44={Vo`oBRzP}j!+BkeQjbG%yD{F13pip;lU9}%X48%98bob=bLk7#)F zP4PdDx->2o83;$b^sCo@&x?t;1gPV+@^5)Z=V$BZq-rt_a*xpCc2%e7x;myPIO$sD z@f!C?=)Riok$S?a=X!;k^vs~a6}vdZ8_v&alYMGZSHS#mVSSrZJbbHA-^I)yk6Z~Q zGVB0Wn90FL9g)qWDiFDBSgUtXd?X7hI7R9kA*mJ>Q_2D`+F{oJ2oJhTFln@8>+Z)t zZg=4V?nNRxnoh7D?DvK&g2D9qK6{Obl8^Z^y(Fq_6X{?o_SB9o9chfN=(el7z?{x- z9K$<%ldg3`sZrp^#9^mf?Zo-n{+V-Vg)0|=C4vIM-`Z8wr)u??>%7s7hx~pqP5#wu zzx2+62o4!5V&N5Q7VG(T#yL%-*wP2?5ucKAmE!j9o4+@jo9xs4+aMVe?{_F~L+#KtI`s~A$gUg>FgXH8A=@LjtREm_cxV+AINL4U}7 z)&6<58RZ+0VBjNKQ$f?IgE&_-T?$)w6_Bu$bAtU)}0dvs$goN1?+w z%!NSsFw?^=OTxOY$==i@d2EddL>#n-HTd)a6#7WUL#C^+wJ+E16Z(^|JFWSiZD(-N z=$#Rx=i&JbWr)izeOmj-yG(~i?fhNLvmM3XmJ^O&dcG{^{xmyw*Fo1s%RWPga=ub= za!gM{IBhIRlB`5M-N0kGN`U># z*V;?UI_4lcw6FAq{T{Ra7jzZNxDm(ae@)>pBlTJoyFLO!btq_J=$q!!PcWb1TWRxY zr){Sf!S6luQqeq;sq%`@AVWJ(-Q}s0s#6|2GTooxh51>n;m8GZ@< zdwC#N8bylEL6{U?DD@_-9Zb|x+o4E&j`}jph3K8yd6TS6=Nl*tXmYF0js!-Z5pv(~ z0lmexjC_2uY;c~uHsa7kOV%?2atb->!`fVm9f%6qcsFY2`*rL(uA4$n3~w#SQ|J|K z6wrGtyxC}yI^!krV2rLwyhsZRhd@e(%u{%VOk>O<^J>!;KPGAX7`eldGou+Zdp}cw zMZYxNYpaF(I4i^FsD7H=tVJzca(~n*IM4TVc`7qX@=he3TqUD?PABP}Ye^(= zQ!gExLd`^q4NlM$p&PBmb_Ff0%DTl@rwcp9I9^Z-b2BXjQqQ06l~mChJ{lk%P4ig# zIJGxo*9F<1OSa0bF52I2=U1D$xs;V3H$5$8UhRiH6|1gF0OqYbCM)pmUZ1_Bdh+cl zgzCF6d!7EE(i!aAj|Wqp6UX`#3Xj%U?rL&yu4n}Wm5e&xbr2G+ewvZWhQLblHn7m59cH63AxBu>1VUl@ri%@VWB0qDsqp-+(JtY~H@qb;T~k5|iI8(g&9F zs(c2)()Z@!3jbdw{;!Z|hXJ5gPvT-#mfyo`w;Mqs@=LQ79Xbq-jd$VLCD0S{DYq0I zX8PD%Z3C=vqBgD)-GrOTBvlN(ymjSFeKN^BoyFVX^Ae~`Mri3~BQ@E5=v!s}y(V?( zdH3aMF>eu=ecg2YM6hA-VtGY(Z|A^-&*qcjCOXfVf)c9=kWw!ALX-#e-1J6LPkcZa z3mqh6G_VfwPDi3cXVyVhA;bUz1ud;W&-9Q>MDNBEFQ(m82)}&4O>{mrCf2Q1mA^y4 z@?L#$l++or>3t5{49sy|$jNOp6{D`2G(}zsrg2n)>K1xG^8GGM zV(O^Y6f{sme=><5`W3QiX6@29xU}g&d%+4fSySKLEVX_{^6eUO>0>s|+lpHXW8RDJ z6IjuWsOe_kfV`6_7IOa>?Ksa+lgDN?`54p*mD}AX9I3dUz+_0vZ;m+Vu=9xt8ErN& zQ1wQ_!lt!SJStViLeJlZbKhBV^yQ8xiQH<3W-Q&PMJ|1VX0F_0+gy3u$?I>gnkrwd#aBeLE`$BC0Sfbbqe*ed^8$TQd@UboO7GP)BCCO zTKc%#q(8o^QIs1$JgOl`nmj*D809n3Ga0}ii!~2sPOz! zk|?){lR4q(cg*VcPF&+CH^E{i2Fsts4KtvXS9XSvhXQec61&={lH_0oI!f__OurELCZMQl*~QMf`6T>JNTO!E&`v{`5siGz$WLU(K38my7ZbFfq`&@ zOH@=}BAe}w5wT6x0Xp1Ay>C_l-FRS#QWUx)o9X$Hn-I0zxX-khIX;=bt&qehrITN_ zI72t{eXtpsRrEAuWUD^H-Q}uFtr|ejLAwSViyw5%m*{-!t_b5wnl_UOA1&XeX;!?V ze_1QLGuP>hnr1fF^d@p+vL?xN>QF#PE@~V}$6a}V_KAs?|6_3eZo?xHP)HJ&b+gHS z#~}+kxWod`{$v2lUwxFGzWISfs(_15KykDbWO`>3(r+HS$ZC_}MRTF^lkMQ2ng$XC zd+WaE;v_hYO+0w8+V2^sD;EA66PAp!HUZMDcQNxhu(w+VN~kBD-;RMT=Aig;zxH zGCT!^#C>MtG$Y*td&nfXJ;J{`5hY7sBk~*<5!NT)lnKqQE?vn0dGOm1j}2F?Ib0^W zyEofktW9T3pkHDj zkrxBmYS+r>))ON|i?_R^q&_;8W{_-o$|7GVj<{-%@u}}4aeFrLFBe_7Dl+M3Ur(aQ z+~p2K%Hu9(8x$Y4As>D$)RE|K9W1Hbp5Fc370rVet@Hd?VN!w<=3 zTD6}Qt8mW*n@B}?%vpU&n;Uj(eUZdLH}ztGgFeIaeOb7Hdz2Akwj7keZpVI-==U!D z+H3>Q^~b;MezS2;-@)L{cL_DlpF*~6zt6@yr%>RXt+`d*Eh+pu6-3Qy3>#6BL~j!D zz8S)xj(#nZD>@yg9n*A#ye7Bb!sz9WHk;_#)E89+e?C+`TLpV1NKE0SvZ@#=WU zG*cvz)Cte#ohHXevPvL{dcF_l`dW>nEUGGu(B*->3CTy_EXAOsYUlUnqx1O-U2C+s zp>5pd)_gAcf%D_T;G!{)>SKB7TLpvI1bl6mE~CbinlEoMqA8``!__sMMgfqDt-rrJ zfuR!+I@UOMQR!UA`3;3+c zuTSn38|OKJ(#!QhItW=ulgl^m3d2uh7hU>7n|hIVjF$Ld`m%m6AiR~roGKkYY>32e z7RXzvB~lUl35I&vNy~V*)#AJ*yIn9kLRc!v?QyT| zjuw7ASOV|{mDyK!XY-!w7xBaC+)cVNAwNh&O+rjb+C0MLU;1SH zw{R1L2{(1x79@X!n@n8d%M9;$-jFBMOiCED%aOA_ENGK;eqO>!rZ}|cdIBY_PSIX* zZ$mder;s7%ujd;lu0lF69hY7VcpO60K|E+ryIE9QTt!=@i~p?R%o;#StAw^Xt7$Oo z)WqkLW<*l~6nI0}5L%NS##&agRc(;~1O3TnUWkvE=vP1!I4D{#aFKtQ6m2KmB^I^; z7!F%auZ^=1fmYc9AanD+Y5eJ_iTH+nV^MjIR{LeU_*gU8RoR_D##iAIB1cY+dHVJ> zcs}Y0vn~UA!c6uvH@0xPOfj&L6~6z`ec(k!Hx~e-*fsCFnr>#N@`X0pWsX}unhepD zArpSZ6Yn-;mhcrIBGcYbhU+j-Om%dCczagzta`XU!ur`w^?HvkM`R0^)#k&NuYwMl z4gSs7r?OR4I$bj^5?WNb??2EZhx9ie!!xa&h@jGWRxW*%QaT$xp;$kuSBTfP;MNTY z&iN>LrH+bkCtGd74dgnUo2FLJr08>L+g#cZzA!ruJHA{%Xbk_-589pxAsR zG$#i`#K%ui4d{OGx74)dtFMq;;=YYw5~*iRfE*HW!=WOP}*t)nh#Fn6iiJ=LbwSO!T@Jb@|%hIcC;@=in+L^nWVhWBiozC9&9CEd{glp z8Ec`Wn?a`WU-*+_+%xfc&Gl^An|~^K{*f$xB|>tM5dq;6jJ5*OIyT?pxU#B{MJ8p2 zcH>MmBP6%UDP>6Qe1_GkDxOg0Ih$SS3UW72ENF7mEJX>-hR&QEPJiSany;v&1*D)# zpfIFkW2L#Mg|$1pf$J=_Bi_U)BG9g0g8cB%Y7!Z62ZOU^qrxGbiDi^T&k$WpIroRQ zr&>9gxVPypt==FD9xgkdrNg9%6hxBeR3JqVKD`&u=Gz+vB~pzrr_SpdS~Yfe0(fZF zC7zI0XJwmHh=3^$^NO{c*RI7-66wqy~sG$KSovTrsh|x zO^5R-mzXkwsAObij4}>wnxvzRU4@0rnR@|Ic*Y9gbVs8$RFv@lPc8x{H_xdm04f zzJ@|Z^i8;M<1+vxL{J(H@oZ{A$d6`|)YMNuMoEi=9RzLh174WNcC?ho=j4DJ6-p5mRCO{7tNWOpK5Vw+wAUrR zj-uRT9@+ZBzE0(!NVD9K>^=|}EC8oO2NJJ4?oasoW_cYJ@yu?M-sOCGk}mdi{Uq4RX<#debm|s>I@oH_+~w~(I@K-R~r1d?(RX-Tt3A!fgHn)0zWZ; zAKmcEGvbJUPtvmKK_2uAhRFwfMoFvFXWOKYo?iN6AxP0KC!0AaTQ1V>yUWswSW~0r zt-|yKDHqgKum7&WHN{v&4M9oR09g-={UTi8 z8kRtnmc3M|ZJzYLjj?3h!)g!&O(%(q59o}sXRHf5;yr?7Mor#cfv56D+)}49!BBO4 zv)e$$NEc;8(}M1RiUg>+Teiz}8@?xT3vk!pY53+oE!LjW4>XtS@diGJru$9wzNsO! zNsh;Wa@!Bah4(Nz)^Dv5$>~TKK{C*M<$bE+fq*U-i^+q~P!Bn_aSfOy&rh7~l_bgb z>pFEHQe}g$c8op;GKKTO&pe+v(X2F%8AkOVjX3Zve)43-m0y>oGPG=y-k25F`%K@= z<1wnVp%{<@Y<;&!Tm$lFEUo1+Sq{VA%su>3Rdv{5SNrNNcam+?dZDvoU|`vz+~qZi zgjzDY5MBz{voFyoX7(=T?XODSA=OSZ%`cVHT7QgTSOzfl#QAz&vEoL$WazB2^SiXs z$jBSmtpO(JO{;UuFXL;@_patns-nBT)%5(m=L@&iho59d@Zl(?SIqtFhmIhpBW&(t z$m;*_U4a0?x_&2Cn*JYX+Kxop-mgSKjtxS_|#0ktPt zCCZ>0-hrZ=bAJUY-($XKDH!gR2^}`Q#IX$FCWd-Mc|sbmN}X(cJwH3P$a>shLOEh0 zdt%|*DLNkz&Ry25L3aBmAYoI#F!7*?7#Hs=(#8cDVfQeMZk`PhG+Z=VD7tR z&giSHuievYWf|zYs8j}0LnnPXkjFd5R4~rlD+Omq%NAF`>f9#HXBp2S_RGm{r1JmR zR}tL}n7$s$uUM~m5G1>iGDe|b~{0>0qv}FC7H*?pt*dp{N zk)YlUpmiJN{ob_VHPZo91e?z-c|V^gMO@KXDv*A$eC2))g>V7v>U13im(#g>*V|v9 z&xh=dAc_I91U*;n`>&F16}8qX1o`lGgQ#s2M}r}OhvHk!Oc$We{M*c2`g3=AX3(XRVJZhl()3^;mpaY`ApLX!Wy(KN1oW8yFi*O@aaq?ZKV1Jq1@(jM zsNq1&T*q7nOnJt`&Ba!qlBDx0t^Y!`mVyG8)Cl`6_r+Bc5^N~xc5eM|7gRp-@qC$bSsWGO&VV)*PBCZi3J@ikmsK0P`0uF`2~yOW@z}J@m5<=XTAtQ zm&N$T(b z0N7U^*)vFZQgUgCi#1^E&ev<}&-Hm)6nZHWJ`32VP@Ke0$z2hLmDZcgVVGoY3&E&3 zpC-U-&TI0w&CtH3w1d!{N`z>Nt=+~|hJ@-Prvm+m_kg%^cRSi^XT{6QEI#l1Tr#t2 zKv_ss7ks-ND9%l2%h;QC`Iy;(Qqy$fVyfI#pd17`6IqS$v%b%{Ww+|bMOsP@DmNTv zKma01cm%J&(20v5KNPlf4Yv0&j+ns0tYt?PzDr+LqWqGg8lZ>t-VMTk*y%r*e4NI& z+7K*hoiq!$bOExJDBly44gIrHJCYU8LX+Xm(N7rmg?`{1Syz9|VdhtM0Tv%j=keqK z!_oKC{-_C2`z6`$y7ySS1GPAbL3e^4NCEFuXGDj{=%WO-x=Yu}mb1o@A7k8oj-_Cg z9l9YJe^O+BN6eH3`oTq92{q(z{XU1f69^_7qOeyL=I~e|Y8#G2%Cj%O#P749cO5iZ zytyf2p^w`aVT;|_@z%5fW4NxaZ*Qr*8R-+aF#|$-YX%EMOYWi z3;!zU2yg)O>HSg^2{4Y8N{h<>aNF(>&bFw@Tm{C2m$^-ZkmS#g^~ediOAV15HTZpf zTQqO=Z*xX`jVOq*@bPd*JwNgY-k|a9Rc2!eexL}1b;P$;7HH|Z*Sz=?NY@?A!A^tj zeBzI_YIJVZs`D@1S(4i#?Ie=extDMpZ4swEh6xgYi&@ISa2VUzwS)O47kuT5fxz}2 zXa5)69o|cBM=xZpl`!83vMb%e^3A$7v!3l(B8mTEMoE*ErlS#OgT<2mZb9?E${5WS z@>H|8jDHO!fVSk=!;Pbev_ZZ$S+c2T8ZVyk@vKTNUkUX7j(yaukVUJA6^8X8tOfuV zw@#X8!o@%wEDq0>fj=7a-X8;UFY>1>mSqr|P!>I{z>{d3rw8FppFh#fbJh-yFh7%8 z=duZ=byW(;B~Ley^T7Z)ikFv;f%H-(SKq?72MV+kw<+Iq&j*vu-#VnuPm;EsyfID< zs8$D=FzKMDd(rcj7$dSkQTL!5H@h?pym#xTEygR6)O`xmaXF8s$8F{)$&8-1S7E^0 z#L}*Y=+&;zo6cSGO?4mGrNSfgSApC&UV0{v3VB_sG}`QLa{DZdBlk(m&6AuMRVb)H zRZ{q5E{}eJld}-}8*&!2aT-6(B8!r5glKuEf1CdPsHFXe1RZuRMHofCvG};zCa1N= zFPvyv)evkkqRqn#F?1{_nY$vU&pcfgZDNsR7SCha99_`Ep8B6b8WAx7Y4{Aw*Zzp% z-r?1tOqGQ{dLo|dClXpN{-vj=J~ieeeK@L?QTn#l^Cn?*JS zYViE<$qO&>EQgH4xk$VG7jN5Q`!@o-PnP%!^(e(Ch!O`aW*73Q-S7q5y)1J(=2BCS zhpiWknfOsn4LD!oG#D+9lg8*n(z%OD)yqaW= zdmHk~j>evd_(k5I7>t4NzMDQzxqtRjkxsjreLWFnlT%P#x4!PPUtU|5%;(y#ZDCel zmpb9;ZF=f=;VLAy&-}$>6}I`;s?}r*w&knwI%*Hpfb>ntxc7K;L0cX*{$c;B=u9*> zjv0N>!KUtV#jAoU+TRK+fRNB##< zwU4{e8pH+cJEv!l!?}sqZ82IL5=hVC&mIm(`d!9W(6&RwLGy?YFt#4Je+3W?L)pz1 zy5C^K6wF?jGwIi-18Whp2wDPMzF#4XfZn&;1kX<_<*hxUh!_wRNNNOoDsdM$s{yeBQcE< z2Y|!G%TiL14*w9jb&c>~DJ#^nag+QGokIqbAs5qCH0qc5TQ;CScK~!Yk?#o`KUySI zv<*GxU3ETlTHkrPbg5TSf)|9=hj-{)gqqaJi6a;jsd)^gZE13{`khr>DU#2J4}Z}^O zso3us_ect8pyw$~Ap{tcn;h{j=$N9|O(dU;R=vRYjw}=FMO-l@++jH^Mq;`?Vl`vP zTZ{nyxK%2Tn&xNs{nO0o8g63}t(27?I}& z`qm#B%bz^U@3RZ+S>VJOa<3HQu(=cQXJFd7h5aipy*2Qepr=LtH9ll=%x=V{FqIb(*3RT-S7o~s8Do^6D=%m*{^;28t2K*(p z-ouQ)!-~HJsNJ3zPo%;g@>2~HmO6L-$zpZ}tGX+%ONNSav$JzSC$w6?7U5~W2NcUb z^1J8g&1J9-@76MrFGMb`-C@hRaDlIZW*?7EATiRR`}OVj*_zAMZOSqAMNbiUExQgj zu`B^JGg7_?e^C&N1-vrx41zdm`d(`!UHTbWXt4BV@B;1P+ZSiY6X&WDuG$A>Rmigf zOJbvKP=qE#lHMNz#H_X$3g_2OFAA;(;Qj{2?&DWVQa<^N@F@XJUd7%pqE;c2CJ7Of zGW6m4Z#}VKhO?YgqHpNolJsXU|IMV4p2yVa)LS#aigpE~abR3ZP9{xOev~+La9OUR zZkb}`stUv;hQqA?5P$h1I!V7MEP_^C2n81YA|vwFEe`9uL`%Vh>lOR@rGOeF!+u?b z0@DG$0*qxo>KTs}x{<($69Az1P2sUki7LR(3e&==B|MywhqDR%n<_xt4zZ+yjp zAi-F?SY#A@A$?yr6~}db4rhHdSIyR_*XUn}ZGr;)(>1nPoHx^8J=K9*`ts46qetBk z^UzNsuw*>4qAG!P=M@CQ>SL7rk{=rKa9@BKvIwR!NwPl2|7Jm!wj-WAg$j)e77rQ| zN@N)GJpR+5^q0B%+XVK%3I*#ah2P}lU~>vAj7uS*tvB?~5}}sBuxE6zF%@dj#MiVt z%L721+b!p0pQFV(pquipI8*o1cDze+Rhx^LnNtGoX8E>_D>Rd zO=^K$f1?;UBBF(x$>~j&kpXguQ2fD9dn<)qt|Y>w!Eq`m5!dAV<{M&%C)-`Y6b4?r zB*xspMwb?kc>jyX?TFSNn3Y(pfz5N#h4HIh1F{igURr#E6l^TdL;O~qdpxfOw-D!L z-?T!7vO-Ag{-}6)IZgfd+Wq%n@jn3jYp8#2MxN#i>6-JqewHV;sLjUuyDFTvrVJEO zf?(X#w^HV>M1R!DD_i;`*WNdggdWmv8O<^k-?c zOD}zCSwh0-*u{_MJT4IxJOE5OkGRP(YkJm1WU@z*-e$2>RWt;<85BOkfZHNVd&T@- z7IT|NT-fPd7cO2_I$sKz^oAq36aE-avT7X%18@9Q8&|Qa|1{%wE@HdeTa&rehayx~ zBR)RT1e7mN6OGeBM+=|&H}v9Wgz&i1zBibdEzW);)`cq7brA^MBQk%a2ineiS;q*> z&(*?&kO88sk-bI+%KZ~COl!hyZ3y8Gfi!xrjoW2WH*-knG^YI68iAkXy+f=vLk{ox zL8+bBG<7J&5VG9BV#&pzi}#z$__tx+2$b{Gcb6ba^RKTLVKNFgDgfs(1&n+I$ef*^ z*UoF-=QU^--#q!xeuRG;x}%#Q;EzP-Bhr@SrQ}5IC#&T+1K3L7)B;cL70~CoLTEdP zp;PazA0!qJOT*QWxCr=SP1%z|)Pszh@ksePg`EJxt4V~+k)`@ZWSavaN8iL=3 zLWTC8{A5UpR+hE7$G3t;(PT>FdPRqxGU3R=K7ewTMdfXk1Z1qvL;adU?ZOjYMt~jdU@l1#ZbcaWmpuFL0d+?k=Wq@0 zcx86StCr-dH-H0+p_|W&`yX4qUp6BLZs8<9LxL{T8~QZo7)K(YRJkQm7e9B1;LF@B z1OhG>h?(NpMDD=Qs8!weIDN`J;w$1@%&^*zXSJv=2uQhHOvSC@+~bX#^$jpIXZ=%T zUR?_H=3&uLJ~FM1%pt$SfckBTd2NZSUhw-IGM}VKik^P3kSHVp>#?z$=PS{on6<%@ z@rlX-1?aCS;A!xDjp8}oK=60Oo(P4t&C7qDZEsRbDt1(%+?eb7$vGjq*C9NC$PG1v zpF2pHWjXF!hIGkl8z&1zXQ%#FcHbv zG2@W5%!aXLGOL6|v5gH*5h6>y09xyN_{!k_`d*p;nHr!RlilHf#7A6)I3yG7@ZlqL z8R7c$8J=t3ohkbCQ>sb)`+>}rwx;8~`;#on9OAPEI#q7LR6Mi^a9xc30S-sUl?*M}kxbq)RTlCySzUQZOOTyN(d1aXNF>{r5d-8>VU<&13W zhk=Co0&B3?7Z5f{UW$n+!m~WJ6Bv2N3&?V=Pv+b(CU%SiPzwksPJ!QW_bhLSIX}?? zX@l(c1~3}Ff>3kGV2tQV$>aUhr|39h-p@%Cp2Nil6bkQ#tU;E-&U@Lx zxwqGRYAe%X-sIYP27rxZ%r&{mf7ant?l0IxVJMkyO$GZ{w>xqcpL2>Imk~~BFnIzQ ziIh<%lLtJV3^YWh^pWWbfS66B{$qd&)4awx3Ost)y!!l1l39ZaC_&@;kQdGQL;3Q@ zBkr{F^9vEHz)Wce9k+6|g_}iE^0Sg$;sz@6_ZpJRkyL`W7z(URXWD@KCJhk-f71P0 z;dYF-S*v!R(qo99^mZe)9Rhw&NGx@^G5l7ayd6kRp%%RgQ$xWPZNMKWscAp5qE$st z^vXxfU8@6l_$mGKiPjTHD7Q2lL?!^XO6nKYbt^myI^G0;OHJ+hyb>yBPjH)#U0Gb; z#O!ZNrvI?>G^2Xv^qjPO{ZbP7F!i&1^IAFjaTrtNjMnk?G1?6GasGU>$YnGeu3diV z%bPaaxT>&hg2W|$kxp`S(9VDmJf!aszUzC2;9Oq7_yaEV#^C#|uCB3zqdmLHymki$wRpXoX< z%?d|dQGajX*up}Y7verIA3b{2fyto@l-Wt@laHdkq)Vv?xA?HFzO)14W3fkz&$YIU8pfxr@*9Z0wvpCA8GA^t6@|M#5>*|;+oKPxmg;xqwYZ?S^)ERi=LX!-ns zWvQ|HS}uh1P?n+b$ItOZA&TY9%<8KtDNiRz_B7g<1lJpcV()MND5K>@*Ja{ z+z@p%o%+$~KsD_cuP|xhGE=E9A-!Qh@Kb4kKL6 zTu?WdC*WT#EvQ1MuV$wLkvur&xNPA7)Z7ANfrHl~J5XL(_|DBZE^T^lV{ytIAa*5a z5BxXF_rK;PkTwB;jqr-Viwt@1(Myc5DV+*3!ce$<(5)@~5ln{Wek%|D?!aqi|^(@NNr%MY_8=)fvFztt-MLDH;0xsLNT{V;H)y zuXIp%UQD-$Hva%9Jz0x>*w5+*iQA#XaGLM zEq%ssO%p~vc;G|gaCX?`dzG~p|lE4@rp;HezG&8x%|63RG+BcD%ZEOUTctL8hB}ed>If-iPNdkY1y>D1A}VH6^AI1 z6nT6Wt0PBA+(AqT=wTV+>Z06t31>4>viB{B7#o;$tzY^e+^y9zE=B7=+0; z=9YSG%dG~F0wIri+ejO_S74dF{(V0E;b>F7RbGWFg+peP6VO+#0B66?Et&>#3#OL_ z_eHlIik%ME%GW#Pg!>TuE+oQ7SjafJ74z8yZ=~NS2-x0Vx!b^8FniK;8itH z1zsl|cc?5#tAcN*5|0RO>o&U+)H%=GNXH`i#`g%Bem$-b^>uY{E-(0CV|=h3BMf7x zn1{p`efAWz!2?aMA=F$xxM3KPcPn^s19bS^fEu4+dqS(KkbU9=XcWm`nEh zX1D@)@)EtkDxYCvoR?AVI86$(UwF2X#Ze1nHFJL07m^!8s-rDaR?7@SOXN^$bJ{Pp z1Yp)bZ6H@vtV60_E*2bmp6(9Frf<9|;%nH`(6M>6^jMi_pBdVLDP@)E)FY=ie`MiU zFzrWCXrb|nVAKOILY#pEYNajVP0@dL_y238^MgFZggSXcNs+g=>=68;pQf(#hX33s z6>7MqKBEv!Q3&OKnXnp_omjjY;X@KUOZn^*cpLH-2|s!_PdrCFFH^ivPskQGGURZh zaz4V*PgDMEuqpNjgf1x_3Et+~xJ`CHCF5eM39uwCA`bZs5jeVXz-;ryRqx=8~n_?6kes%4bU;z1TFt0J( zWeZ=-QkiVNjWpavOQF7cWS%%DU!jAG+#Hc#l=+IO;Od`q9#VS>afzvp{ArQ-|1fbd zbu$qt2d!qzIPJeo*K7d2nh^NMypyT2VrT)qSGGn$MNoxO>ZMmV6HON*Uw?sdAei5o zMI8_`FcwVb<+?Pv6eY7FuZ60FqXByZ3wM1#vdJuhd5Z?rbQB&{0yWbZCIdh$G9ln2 zklT@GcESB-31#I*=7surW;)K*eX|m1Xmiam#~f31k4`|B&b z|Gr%QX3y(w$WHoj_O{kT)%%?GUVs%M%^w7Hsh%izR{m~~TOrhn8Ga1jeR+wXwrL_I zQoo|f3!5_;UImgg_q29YDdahX%npgvHld_Ll(fZucgYzWxX&ViPf|Srm~x%3;_G`_-VqFfRke7bDQ-7IU|jr(Jp97yfx>KiZP7MGRPZ;pUP5_06zmP*RpMpVc zj3BHFy7HOE3Ldu;%_w_m`pL}f75@=Hf~L@w_uk=PLDxCUHsHa9J@T&&plU8s7csy4 z?6~A=pcv=GL@N8^NuzTUIJi6~{wSgXDn$B)l<}=FuGNVVV&DYx<}r|$*_n#peHjCG zb7jg*nhlF_D9NA=0P-!Hs|N!gQok{siCoQMZY2B=SE5yPBY1x~NKkj+1Q`)*6oQZZViMwHg7Ra+3N zs9MFZYLBW_V$@1(rS_;=wbiWJqbRXg%~-W}Y=R{Ad@lF@x$pbM^Wu4%S8~bq{hsG> zevad07OmJt91JV-_6Ds3?%7c{-v95{{r~wy*7QdA8<>4>FbULt{86hnx=iSO%Ho9I z0EYCOC0huXwe++BeV~0&f-g3A0|Wfqc%C^uoeSL(=rCvhi*I}J(il*&jTK_P0`Ps9 zj89)5rYV~H-qP8Ud2j(V^=hI<Y!|^;M%aKA$ z7x=T%RsW9OIJVM8C#Tzljo14^^-S%Js=gu6a^7i9H-q;Io3pJ_>MAm`^EQ_I9?yBe zL?{m*A^Oz_{uzZf8ZHO8#VXPiJ}jbr^~vn599Qvv%EyW5as)u+s!xQu{C)Yuwc%xY z2M2Y4No^q$io9Sz;dsXIGie;|j*x?*gfq@bE1JnJ)$lAGuoZs;8QZMOoUX*FqZfTF0jZ4K^n7|ECHSqsC*gkio^-9w+D}c( zIR$^v1>95wO(3uv)AYf+e0`g6{`Whe9LOE~*zM+u!#5^u+jSKUAL4lS!6({cqMkG- z2fuSlcG07s5@pjIucY%enk-QxZc#A_V4%5mCH4`!DFNzDtOB513&hITe3;020V}T` z*U|Jc0;M4y15Gy9KtfJ`~yxpj~**j_ibF`N^3mT7%!HQ0k<-WV8#4Fr=@!L6-|hx=vx zV1_xSyow*vbr(9gUIqyN{B}@EM=j+`iqFzd33bG6xtp!*m{3-yQRLThhG3fzr$PJqf^( zKRl~H%5Ajl13Vis`)}=UbPGAn%nE%&w9^L(d~X685^gGEEY3fRdY%yGItZ|nzs-R{ zRs}pcpy3P@A(o$6qHW&BzYHlA>qr`az$yRU*nBt5;gl~IU#&+VMlCqfHKOtxy6m2O zu6EZ0uI-PW%PylVa6ns8eOGz|39iLsU3)_H0SCd6a6e!jDH*bPY!0zNV5^Y(^G|Gx zlUs(7&=F4i53U-Mq+C&|L~cbSpBOrgJ-`zdca)buQy*)yH2@=4ZP6e*_E7tW@1}f0 z9yz8EqT)ch)|CfhvtRE4Zlnn7|FNj2wE#~iDSRk3 zWcTss!J`{R0_Z>`0?vBKDEpT}zz#)uzQCOE#8n`I0GLBW>|gHrHIG%e-8mdbs0g8i zuNku(A{AEwa6#`(i>Mn=RhZ3ho{iQT$$>6(#H2z_@0&uZs4sLxf_VW{9byQ(uJ7CZ z*=enh*RgqofaQ!pjJU_mbwOwmr<17`LgEMpTo!PiIQcxesghVfMFN7u%&)~H>SlC8b6 zVWZYX+*#9ZZ>^OPCGDt{K$VA(AwOQVfvqXTt3LhpIU1lP@7;{Wt&S*0`~ZF?=}%-d zX}d_L5u$8cB|yhfelzIPzv%6~9`BX(Pk=Ol!emx>+}ipyJ&Yu9#bt4#o^Mx41IJiB z5?ssXXb?uwRc%Sh&^5>&8sqiQqBvC4KynA59!L`Xq~B&f$veaJlPYS8)Hw*pCqAl#X^dWN+brUdCG)&*(0brHDVItNT|7j3S1Z zBbdY=&8epQyuDU8&Z%Lau!nsHszJ#ez|rHg^ew)N{D}Y}BsT}e9vCn=i6#q704j9n zefer6>C3OdbR!`OPB&8a>RtZdQG=3uPr`{MP5Fccn?4^{y_baWB9lXun+FmR^i2Q; z_O0y9-q0HfJ8rI^m7JLv+%6aIyXqo%$QW{K%&UqgJc28*QB0$)lNq|aS&F?+(9D|S zeR6rVZAB<_RixSU@_?CtSsW2726$lLdS|WQ#l1Dl*p3b+(9J15s-)+as$%B4fcXxY z1)B>dp>foizrrOoe?W4a`CxHBwQ>Xl-;c4q%`d3AQ6if9&1w)XA7o6v;2;G%djD&P zEz3bQ37|v&lO92I=YvngErLEMXl7XJUoFYS_1G_|Yd@M2zF`e3*-o!_{NbdI)Fkw_ zpd^v$F2;8ss)g_!QHo1mp67s6^KcMQ&dLEo*iy>vz!k#2iZ2{Yt^Snh0V6WMa2X^@ zJ%mm3bPDnJH z_H`1Psim&bHlHXuXZe<|gZxeulx8RY1}Hlk^WE_W=DyF_9G<2hV7g7tvF-;s;7iUM zSzSG@ug)~*g*5m!;H46eSXf}_L+)LjO*E4O<{Nv*G?VrJ33Q+caNyWqm${a}SS;uK zy!sIL+OSGyRp0av4KN>5(0D@9(VpQ~C>tOA*BM}mRPt}F-JOke^3F5ki-SovhdwjJ zuB@TWJc&%U)Xn}z+>;kDnnW{=M3zm!ds+zslGVNXAQ}4bpe^8S9_W%z3BI0W7=ym+c!0E%*ULqQ(PoKX&p0mZqf-Tn4ep@N-vk6-cdeULeR=BCJ#_zc{y=#@~ND)7xqpyn>E zk-&Ve+sy!a1Aa#l40ZKxOK6GrM~G;OskM=EqYec#ZzlpgOu?t8fYk3~Mp3jQ{1Hz6 zqx~5QnRJf4Os|aEgaV4#hSQjqvfJx#?8&Y_2U9;(LjJt*I!>)FHFyWuCWhh&H}=|G za?}Xf-`??-jppDHI`agm>GfRD%$WhbW{WN1@%?4q`34H_CCk+P$|H($Y%yFn>ZoJh zr9z-Rncf6XKZ8ZXYs$p@u-Z2AFMIca2$RtdYCVi{i*sQWT=?Lw5KrXwqk;d-CfXWb zcrLq60BA)tjQb+0JIlM}h9NWuDCL9HkyYTG5i{-zG>~%W(&)N+kM@mj6d4}R2m4p( zK;fe7H%TX-9;|(T7OlDsNMh;AKotHFqpjsm6~6Qp7gfOglPs@7kGk#Rbah~oBf5$b zow28e-##wgrQcbM;J%_rc{1Bh@M_p>y(NI4A#}+=g*jpF27-51j@ge+VlfG)*vG0C zB#YGm0;H#}4vV@kxA7R7gzEHDBpUOK&1L9XmqJGHE#YVW>pMXksP0W^HP&AFzrnxp zkgKHJ5gE0-P^Yh44;w26!Nq@dOzQt?QpcT@2kHA=9$x0VB03+vBfDvT>!Y8Wm`vMj zIM3;s=*tZ$)gzx+-OOo5ZAVl{<^Q{S#kQR*#2lG?;~(BE^f5{=?>}TAJh0pbx<1fF zD>0F8W?nKzTEe&$KzE{Qj9}hBHR6Df_tXG=s|NcpNd)sg8Ap0%C+7OZslF zcAtw8`<;wiu&KKFJfC(~BQ+qE{h>~oz(8`@^Du&8)(oRwI@ll90JcE6fk=-ckep%g`CfSgxq|yCqmNag@k^If{NVYMP$Fij?hhEY@*eior(j-#NN++; z>HsCejpNqQ$g*ajIL}ZDQ5!k$DDtyvp64(?y@T#Y!2slh{KD2roIU68O3DAIG1JQT z#}}H{`N@&elfxcOtBHo}79?t#>R^Z;@bk+wIFtR=>})@AXaqcbO!%*N=pXiG2NO>m zI_xi=>2I$3c^6yh=-`^*K+p+c7ZVbYBWKu!WT(c8;E=pVdP!PrcSWT2Pr$U$_K2Qb z3_1D$9E~?@Nb(r2wEvSo`64vN15j9^$kHa8bp5c5J*o`+ z5R=K^7H(7@q3xn#NTEmLQqR{UVlNeQFm{OG7F)Tv}R(I>0iRgG(6cHPgwa;bSDcySx<#Z z@s+W>p}$C7AswR@w1BbXPjqK7ba37%pV3*GOb!2ekGE{65St21PSoXC zBjADYHW|{M`MK~@j3Slx!GE2fkCZ5NCRjX|=-cp_o=y53)>__;Zu8j(|4nIlec^8` zBsgvPe59_QGs+M%;W|yTsxnoFYonB5g>PYy{L}bouIVm(#?hk*J+abk7uR)nWI`?X zZV20acTYjK;9!U_oF>FA0l6}z&OA)>Vvl96=ZF5R(Kg^qOM#oM=j3pYe?;VCG>qj> zm!a9}az5`m4WvE1$qBvY|2j%NG{_j4??Ov1x7?T=@l80Oc|KQQ-~%RZ3Ms%i>^XI` zZ3VV^zCS#J7;Xm!q}eqmq7U}6+*LWP_f6Un?+%Uv!51uP!0FuMm9pRqKuXmB4i@CS zp8U5e)|QydQ zM=qw|w{oqp<(VGOPJW}I$Lm#|N$c;7HLb4#og|6nO9k(E5ei=XYlKb@d%tSskuB*L z{NVQMpS3$v$wkd}ih+d3l!XnRzU&w5NNL@l&4)rN)u1p!Jj42_-x&2oI?Q#M^7JH-9GEl^ws_)eQSo*F=p(MkzexpU_qv3FAvOa zH&b`!S@pf2ss<$sCF*j`Z#3!mVh+^XiuV;1ai|f6%*Z!7)>?H z;!I~zGUrpMZ#wG(X8)*dmwoe@AH4(ivOoZ+zPvw#3w>$(}); zU%iH`W{apia$Ko3jrpQRMxxJa#kPi(CjP6A@eDpo@=U7F|F^E8b%B`i!lhw2BXX0P z-DRk^p+)#f_jS<><=o`@Y;Mvm`ivgL;hSV(our}NW#iJ_25oO>1N+u5-N;IHDfk;*2vBjbDN(6MB43$-#Z9xodLwLlt5G@t?9JymM64{6Q5VZ)b6O6 zc?{Hn8pKa`R{KU=fGnE)j8Dgaq2s{L3jq8(0`|iOjqUdKi{*l%ZGQh$VEi!)45Sms zmR!kFo@BH$97Rj+>YeQTE(hiThJtVTn0j3azhmWPTsMYpJY*~GN^EH|K#U+S9G3=Y zd2?+5GIO3AwJ9U~BQC{`hmCx{nD-NMwgd=TtVy|I2Efzg824Z@`jO7<_mAPrcd~gQ zooK12WTx{L)v7%l$mtg%@${&V9R}5k@Mup*Pbo1J5P`O$K#nv%j{*PXz=e9RUA=!T zC>3}vy#JyNh?bff61s(5?gYiACV+d%m=$bdDE##Ckv$m&N$4N?So0*PE3!(R7d=I84QxIh)LP-vDX5b!l(JnQu~ayv#EdXvQT zHhTkGu14L(QZCl(^k^I7U0{+M?G2C}^-|q+9)FrCE6$D|_Dbl~Akb62UcYOB5ubRW zm_38M9>Ya`soAaBNc=vuAMdYE-w%(;_XU!ap4;mziv$&p^^PIp2kMw@-leCqZ}^d7 z%z7HwnJv1a7-(-yt1k~U52|IpKOQO8NLOjJLK%A{Vl`TY#4@r62a}FflTU#b`FnL- z@m>Jra}5a5L&nfFOQCGigx45!35yXfx#F;&70rQ)dn^loN9;}A)YoUuQz{(k@drgG z>vUL3_M_#$4QxE8uf{V|_l-zx6~_KsI~{ysg>e82vJ5U(icRxhP5rG(6>Pf|PcLte zYm2m75x;Ii0@(piQB#6Qo{Zg#yoH^c1T<~t;t~y$uf$`LQ7_5eGFk^p@5T7`Q^`qG z<91_yXvTo_5E`k&N9N_DWO5zR)Z6uw`_&uLM9aSuKUt4|lz<&U&9StX-;Qo(35DHw zLceOi>qSI(apD(5v*{sFaLN_p*6qq*l_9@9I^B*VI|e=0w;`eG|&x!;UET z25ZSR#?0s;w}pIa|0RqMvZ?Df!O15jC9Bw`T&9o~ZT{j&t#LSCtTmFrD+i{`;4c+9 zifQlc>u5iJfN`~urPI@Puw57inRflPIOm+g1$q4|jeC(4M^65|IREVDPRrD;x18|KBzfoP5YCuk4Z&DOdN6K9 zvs(0e2>GP}wXkVJl7yd$R~DynwedZaIBb|Wn1me`?%4ZfPco0s%D^5sa@^+x-MV|G zeDa)XK(|Em=N@29x?cHIHvt#R)-YH5<|~@fjVv&ayml@-zXH|h=oW4RiG@YM?{(YO z=-r;JW;WLuVR=G8Zh>HQ;Wvjt{RCALNJP!=PuDdDF*JF%%~x;CV@T>)@YuaEQ-sph zivg*ws>YqF&-+Ny(Ro(#qVB2aVWPaUzcHHUqTszE#XaFBXin2%m7uOPhwYEdM{=TF zF28m{a!TSmSDx;I54k6{&I@~}GoBdHqWpD>x2o!gB{niE*ExLl$9U(R+R6b-FMBH8 zwG1HY*B>|!ji2ARp@Vw7N-F}9IX>u_FH!~QBRQ(D^X{rR^br47n#d!X3=i9dp{AID zGb6KsiX4u?@OJzc@AAsmKA+cuJo4&L?9pX5=@kOgPE7+HjY`IU&>hDL_;LI0^UAp$ z{V4JPsO)lqW3nr?*yUig#IX6wrk*(Msmj7Na9StYc9+Mvi%nqmo_kpzqkOFhUX%<1 zN@}XjtMJ`C_kZR_921dUE%SCg99xuIAUNLowD(ZBk{hND2KjK$);7hE9aXo(m2g;{ zjDI~<$dl?4#~a5K11^l5nCLc~(!wmdmB=It_ObZYw++`H zZJlryEK7Dk`0EUEzL7U$e=Ff6fmxpiCC9cHHS-1uXah26zdDog6Cwq)DRES($E*=7 zkg2bwg9dZAseW>rRSr zZwW_l?JFCcRz?!vNodF$W{yPI7V8Y+mLqD1>2?0uI2m-`?Z@WJPvxjNgA)Gu*+hEO z#*&*bpRj>cp8KtEAylCwEQ==c{X;t$Y>lqE@gjs7Q77FVlqnSJ-G7UD={;NK zNjtS@cAK8}?Z08iqkOq+;Ylwcij>F`4f$!UjC*aQxYSB22BVFKQvl*B zbZ1hWaa$_TrW>wH=G%EkdAb-`=V>>7+TK^LStT=G;{rC50>e_j1kzi^i>e`GYz6*bm=NfaIo^{KabVKp zwxhO5P%X^{7br?Z?Fuz=QeJ*Muo=x;z z+6T5?G4f252+>EP5szd%mIA-O2W&L2&(xOjXKrx8x};+@7LzTPtBoMQWZyp8>b)F_ zpMw~~Zp~zJIHbOi#q27r-4l z0;a5G`U!4q!2;APYHT7mK{TFLoQmC7L+DoQ@oWqt;m=XMxy^MOgb%cGSIF@zadpw@ z$&;F0Px4R9gRsCoECQBZ?PB_dR-i@EN5y!x?@8ba*%3Tvpv`A-pgCfUs~~Egs?vty~qY zhw$!2gnq-5J)y>cC7ZweC^ZNBt*Bqj_e=5+oP-)%uKTkTKT{(okaYtB<^2t*uGCXsb`Q|Y4(b*X)WEj2urZiPFJ?sZB^750( zmfY5(mm>%Z1)6bR%0%3fRT}Y*H3Q?RLUHh7WwKW0q{wM`&4BNe@;UCt&xxA~u}t;~ zZeq9+m+>LJYWxi6;hV(uAHmwin~-k&8LL;T%!)P!AAo3BQz3X z%O6<*oo|=yECz)g?C%Gov!YJ@b3nx$v}t zgtH*)yacu`{Juf5hu?5Ujvo(-vuH;-Ug3;R^0{5S{(NWnw26H|MouYqZ_RR49b&Mg z&3@^&yP&&0ob5XNZfybfli&{sAL%uMo9&Xpw{^~hXN=NB*N4Xl!f9);@?FnL7UItG zY|;{O7PdH8r0$@Kr%kP%x1wd=2&}mzQBg9(G{YEq?4${|n47O~)@@g&ul$#x=S)7H_9q(BN<@iR*ko`uV=J5xeQ7at<+_e<()oAUmn`r_vV3^$K?&#@t zV)#c|ez$$D30N&^&d**2F06>a8VDv{oT)gD==-p4gqxxpu79v(p-CmHh%~YZ!NipO z@$sCL6P?;>UJ(B;Y)1EkzY+D|`0X&##nlM@>yta7w2vVE%S7-T*Zmg7Cyjum;%P6; zN;k<-wj5;x9cfi#Cfc+9ZQ=I4s$7+ zdg{P<9LxJl|s1Sn#Wg+~44l4tafa!5RVAKAz5beful z>yhN{MTDkaM%{2`(%|!;xAR-^){s?ntMD|pcRVEn(?fVKUUz*Knt`;G8L~Lvy!cTS zZku|VN08|H_BVVe*lUhRHY@_SLM$=a6~dGLA@@b;`M#%Km#5?crGxWajGCMODgYp!ltYOPY)w)3+XWw5{@5YYOYN~TMNLz;5>#}e3I~=L8T)INp zLaqEO|I+QpblOAh|J5YR)C>#4Dy5v+Fyb<14$ebR`>xJ5cV)?_<6SsVs{n@TcNP}1 zT@3Qljz;`@f79QS>56fK#%e=L(Soz-C^@N*t6>Zg8KWx^t9LJB=Id(ovZz30u^b0z z^ErJDpZaCXU45cn`#0TtD7)0Jj)*o^KO^rsUvOXO=Ws$xXT5R673tgrUTEBS_RFo(9bvgVDp?h^Ge<$O11!r2!V;`oMa4E$RA~~`iMCe4& zXQ+D3PG?k*@}ewLVEKHseW=E>anBw1)>UO*%1i; zEsA|)n-B8t4tq~U^cTzCFB)kba~!>kqUC3s{wPCr|NXyn!9IVhY3q|?7ZS^zp#>}oY7O#bQOe6RrBB1~AJFH~9l!~^o2dpOLW-310 zdw%X?g_=C?=c#wD-ZNl?y4X%9`y&|WUGhs8o7$g4mIQVsdtD!)O{&}Gf&W zRepTh&AM3a`Dkmm$o2`2S+)wS^XJ-=%M_oc(r}qJv*s{{zG@IjOe?B+dtZwcd{}{V2eA~$W99jt!vNW zs+5|$7`p}#JQ)3#j3)FmMnZkz#mr9o8jhy=8cP zn-rr{^MFCL_!~4g0TLN=^s4sv?N`KA5^t_ud`pv$VQp_Kbf;6Co-rMSwK6y~Ieajc z^3~-){W6Mab7v1IbG53B=l*}qw>QH;Kf(?8VkgBOAqh8fSbuP_(Czi95BAf=w}=9B z$gBW_5BqXi5#f5jrakVXM_Lj&5#+u=2WY&o>+Ed1EYtHp+k<~HOiu8fRRuy5SG1gK z2gT7BZmf|1`U5Qrg4^CORcm%8^`lSS`~%9a*1g#fA~>S?*PZJe!m61?Cx;xJ-iDX&dKHWV@Q}&wXkl{l98Z zOrA#z9~oZJ_|RE63!hnj4UE1Eaie5Bv#UkF;z&oSVM1EQLIObY;@i1wpo>KU=Ql(f zaC4S4F5!z?Y?gmPf>RRc1@2V|Xmh9K#BKhLN+?s%lWV%gwyi{KYWb(J>KDq;(Vp%Q zbztj>O7~a#Q&nX$+afQAh^mbwuk2SXujaL_r&-wCZq0Y>e;EE*TJC~Ab zTo1mAgJ;ho-~2(qrv`mA8A1c3h`u8IgX`nglfmNUbr+7wHS|^Qhgww|tw+1{8ObiD8bb;m+R02_ zolJnTs$#a+Re{Z{6*5P!`=qxeJ<#H?M#*X0jt2@1K^E!tqYH6}Kwc^IK7|b!cGgO} zY<4R@9R9_^v(1Jd7r^R9fuV;S4A`vi8(H1DyS=+^Hp`4o3%NkCu^e+C{BN}-Nr;c? z200qZL^4Z5M#m%3>jdh3al1SPM#`~I2A6%0x}5jwIFV|<3sx2t-!h+KtE4+a$ z)x1A7kIPVrTTr(j&nlrJ*}iuJK0EChWW`ei%Y?bzWuh=%OJfe24|M# zDp1lNFxt*cAhJdf!?Gp61#^rio*v4rjl}ZspfAJRRpuAjS?PD5;mFM2X6z3ZYDbHWd;EnCfBrtTEjaa(IU@fn-ANoa?$^@ziYcNs+N;;T zJ8YS1?`=#CFUA0QdV)M8SPq3?EQE+g!WKY7LaH=w9mlvFeA%9C#6SmzO*Oujs_<&grsi|2{&*}I6o z4E1ViPSOew4pQ3d5z`=nfQu??2*Kq=tl!v-_d}osH{rxL*{x@RhkEIJ7!ly4N#E!n zY7g&C@(gC2&i$z~9IFYt_mACTCz7kD&rNyg~%FWgLNes z%W`q`%MZjRY`to_TeIn_M(-VrUmrgy`pckwj%k0b5U1hM?Yl6j-+}?-g)Z;Jfx6yl z?pzBznEp~siWYnJ}o>IKrE=$X!HKxtO)=nayUNy_%#6Dp@Mwc z6$~#*#(~dc zhaAeIJyjzvQ}$z9xgq(Doo?j|6NY`&qx5fN$&~*Tz-VgkB8i80eaXAtyAm1D2~;2# zrx?A2-uFI_w1!^{N6?Z8vCznnstpaNuU6~NcTMzH!Mei&562ypLLWu=P-chLVk1VvW+|6*csiowwErfCn__2^|8dcNZ`_7sWT zeQ>;&q>5{bd1NQu$LbBAtsMy(SJbzUvF4RvP?pKVzrHWuSb4h+1!?)|`NcPKbS<3~ zs-tmFpot5}z!WW-?qd?-bF+BReILK*^oh-H$pZcGiPx*t(8mIl4Ba)jvrfDfOH*;( zaB0-B=7_>CA3_(Tu#U!v3HwFmo>H}O$!-=CMU{flM~wKijQ8Z&bA|5;5|6H1AIAo- zt9eDnFnOBqi&iy(&IhRE9$4P79X>Ak$EE3gQXEIViAOVa6dNN$Nqx*aBANv;kK2wi zqR=tdMHqJ{s;@2N_i9aRBhe|RN3}4#$-_nu#TnD;1I)@8kK{qB zVJ6SvC_q0Z-&LGjJg;7kNfZBj95S3CynT@3?wep4XG{4anO4j|iqjOyxeKh{s5Ji4 zm%p`jatFCSq}v5IS(Es>aBx0NtTRqbS$nu+oQkF&eWm};bwIKK5xQ<607bZYB>4_S@p5U^n~IiXJP$xFZFbPWb@>C!54MJ zPQlnwv?`MBQ3b+2bf8fHsW!t&o-?=i3zgzt>%-FD=;@qe9AP~W;@v1X-8LH{Hvi{D z2#=Y9p!(ZJKdn%G_g+yle(`@(TLNO`{F9z4bpbKxY#eRKpN>!22Y089q->+Kx;FnYBgEznLhxoW=okrh= z6mOig>CkRN)uJ`sUX?e#Q|e}7lCko>N5_!v=KET3!P%_aK)Lt<;j}xB!!)vs={Xsv zCzgX*^TePkHMjsIBcRAZklxdzGwc=jD>Z}ulb``i*G(GXfjuKkAoz#NHAi8lDSJK4NVuVOHif zhT2VDd(W|0*Op9RryX`pySZUXBZpi`c$HX-!v2LnMXi_j2Z@aJm2H8eQQsfY^iR>u zwy6k9Zie#{Rmq)Mkqm%_(qE zbhuQPLyXTn{#3T5aZhCP>|+=P4-t3d-o_3mRvq0<>oIIff9=UBNVK?F_UeI^AzgfH zA(W&oS8d5Bt&pYXd3>Pv_sysyO>f7Y^*Mi03fPkcj%6SkPQwT`{q}nP=5DFI1?wFIqIJZ6`S5J@q2A2@HSkR?kVT=sgNu~J!q)oGLqCV5#pYg02?I9?cpW1gK z&F02?hT0ESoZkL2{Lap#ZBYrDr`db97#c|XM=xzFo`WWsSLZVoF7R;REA)gep^2fF zz(N56J0AXS+@2V(l}PR_VSldT*u-l;qto{vGIfQ@w5sP~=7U6dOV7)o^223$DT9=5 zOWeh*3yNt$cgZoxy;{F3ZR$i%?} zf7Sed2D$1ReSc1-$M}!<9@b*e7N>Ln8K>V*b+ul7fQh_;bS3+nWpu=_u|k&Or#(>C zeefGChrHsC^)+74isM7aOK+Rv9n_m8z_2devF5e&14IF@T@%j_6IFUt#t|Rp9!{kf z?fk3M3fb~<#asC8(<6>%gNAU>iapO4Pz|EFBQ#L#jQnRGIwtwzg?jNjpK+7;uv(=v z8|^N=@AvNMoTpBAP3m7@^S`JsP`XieRjtyG`&iM`B(fy|+#%cX6xZ7SF_5zb$$rfG z3;B4-+hMA6?&F&dJYsu=5D|XWqqdhEEI>a`-mRl~QvR#>c)Px2`Mc+-$~|*?fs|HN zN7tsLO@IF9%)%2V?ong3Uv_e+M@ShFzDPC0?%Z_8zA;q^)TD=d>bkUKflZHpEv@(` zMkY2SnIT77dc;r(r-r*)ZIZXh7Vj>PnduJZb2M4B>MGa5z1~0NnuuAmOtI~q{h1V# zf}E$Kdyo8mt}?drt%-2o6XzJxtw=mi+ZSI!TI?eIXxz#$Wm67JKLq?-eTO$g%mOu9 zvaLV=jlG?sYY9&OOp6!lmp8@4b)PXe6v|aF@yS;^{Ka`gZY{ zAyA}Ikj|6;uDWThp{0YE9_v~-vFcW0va|keN7F<)$F0Xm>Rcj-4uOc}-F0}iYC?&6 z?Z6JSmyaG;$Y}An1z1TUnmU+_tBrQ6N>^fNC1s>HGh-R>h#cjLmhBJn6Sb^)usqc` zEXPzJG&525tnJ%>1yD>K{0;5WV@#zuOl&S+n1;j+o7>vLh6va`ukmN1mpIF;l?Uy8gX27l4qu3M|k-lXuk^;f1#1wC(9i;77yk!H)hOa&#+*$`nMn; zLTMh`o&L-G44upk6-6~A)D0x%u$$Bb=(Y*V$umNmxeTYk6JN%DuH$EW+3i4AoDDOme{}-*g^l6=c~r0 zTfcYU83CP1$8D@~1l@7hYREx6iz$MVq8kpp2fJhYZB~2DK>;VBM>IYn$Q(n@tc;g` zhq*DIpz;sTZ#@o(qrDP`Le}5e5umq;KN^ZK6KtY8qgnQ6|b9Wn}pha7_n*M zedROq9XLnVR%Wx|Vx3>?;C7H}QT^y|qQ%Ip!TjRtCayCRUJ&|8UAR6pYw%=l&o@c> z`5EB}-lx4#0CCNp{-zmcud~&1wwquFrzRhJoip*L z78jw*PhmQYRT}f6F=0pZ1C4MwmOW;6C*fFjlxS%T38*Ng)Wm;wm3Vl)8p9$SV5!l9mlGCMezlwYo9>az!VS+fyJx%00T=(VvlGL(tuMO_8 zZ9fj#`GcYF7i!v1w%`7?qLK_#e;-ow;TJ6{uc;HErjUO_6Ul%6cNTz;mF04-1yL$+ zz64%+Ka~96;$*-*e}A_RCx#Uo=FKHL5YrXhr~g(qL!IC~%g_C;ZgSVL`GwEDc>Ro; zOSjP!Wd$b75tFRZTL*a~5Ha-j`GTud_tL0VM&*AgvWtt720K!_-5$3lk93(t-PJy- zaL@$Cu*)vey>uXR7**LV*(iGDr&c@Br?wXl2_F0s-$?*3v8P~UU~@;ZASO+0^GT}mC2Ef?{hw9%OFzH@QIU=~&O zhlL+TVrr7jsTkqjl$>ZtFJA)hW}Wrd&d638BHa=nxyCmC5JX-%dJ8UADUf19lb~4W z&Tf1Ehr;aIbA|#z*z2~oc2b?Ol*I!36L-eVyqq823je4C+oXW)_!r8=$0 zH0ixmlp;;7$kRY9mpST?ox^H076t%oTdI$@1uELdD76mPjh_diQ7T2V(kC3kSk{4* zPzH87?D|(xDdJ|-c*OGDL)wOD-qgWn^`so{Hy@bAPQQ*r7Z0jBIxvn6wb{>5x3q*V zFEAO(++-aQYXiwm{L7(J$|x!8Y3=zflhKg3R*hlRldor$7%6TUO0VLzxP5ikW|}Hz zeHdB?)p;pn^nu_4`pD;BX)tW^g#SX-CWN&bOI4jf54}DBp`Okkr1?8_|97~f86njxxBQ952l=L!)9;%Wtpozg#3D?xH(YZF za_?z+1MLuH8Mpl@6;#w@S$m!mjn)pN!oJ{YQe-+Ww5^?XYwMUO!ZYl-v&*tD65fX! z;ION{;Zkj5f83i<>WF*afl^;pk(58Vq(dU_j;-^X1b$5FYZO`hVo89ozcnQh)SII^ zhEJzpd5L57OF<9P72tHq8FsRA9>p+Y-{ry0rU9pJ<;l>Ky{L2BXHF}6QuLkyY)mc{ z@E;5EjD%ZoUfb!j!&$VP*PdL}sMku-+2nWTvf{(+9gI@)%!9)a2olWIiF}ms2>Ze$>-GGqesz^#Te7?P zl$uxR56@3&pHb5KGd?DLSeeGHb4?fL-gk0kh**uuEX*^^Xua_`WBI-eeL4H$8h)|@ z#49lF(TNIU8RKrho7}h?U5$o)ZC0Z=2-A~rP`f}VZl>a9J6z#K{CFc?R9sofT$_u^ zKT?{I|C;vOJm=lMBXS}S+xs$NXphwlyNJ5NB$p-TL5SdL%U5k`Ij}> zDdJP&Sqx}=pWPb6zEyg&>()!d5^=hZcDptKua}(Y`Fgd6`}w-tYeA|m3!h0*LY*D^ zHAV&vX(mqQkDHPX?@}?Zvwd@zDClemMGyjEG~0JDil#~AZ6PIG(7D;Jkf(}Dml}`- zz)md$njT-@nrV!0ki=B$+^lF9)G|-Ub#$}64L-eG;l0|ZWyxd-%63HI7%ZRowA}1A zT`6=wT8YyPzS*|!$S}gULO@UWHdh5FY(9NZq`35`vw@2b>ju2|&L?hu_#egh7<*h+ zTyRA`Cttn_{0o`gs#j)oemQCsBD#mJ{1SGIBPXvOHZTI zZ2A5usCuttSHwpTGoJc7!sNohRlScNd)Wn`icN-YBA1EdGMdd&dzRz*OLLFW(hLD? zCbu`QD_uLFQdgpuP_RyIE$$6$Vx_&=A=MA{!$+@M+wFI=<8+5*XE(c(@+=%kv{)Zr zo=f-?f9wuuKha{dP6{&+C@72g`PF6nw7y~zZ(Kq6AZGli9#w3J)vjy2F&FIVZNL6M z{RQg~$?fgX_CT=d)>H%#1icgxQxgd!nINP`keqvXI)0+J$1 zOE(ON5`&Zo!q6fO5-K6xAq<_;9Rt!G!T{3op0oB|Ywi8q>$%_0^B)f5FxPdR=W(1z z{k}u@i09>2LK$b<76OIE>Q9)l&`zsuV)Vq*tIy2W!y+w#R~Kuue?yQdQOr5A#wW9h znb40oyai1@MbIsjO`97AOC)?$TdRt=5`NIFW#H7>z3}$jQn#&{(*|2Qj_Upq-;TW2 zqI3tN@Zjj_@f3N+Nw(Y$R-6F-;p&|S3pCpmlMavjSh9PoSRxDFuf$p4m!6}O^W466 z=XVLXFb-cU2K=yEW8Wegm4;JuWU?p2wNP#zHnyS}F7j97W4qp`Ooc;ih@lN2ftGL` zetbgdYsNmh<$4>YHsk#rXp99uiozDcm(JFv;cDgqoyR8aPYSf?yJ;z<;=GSecjFXc z?HhI!E=Bcf$lg-9qJ=Q&kLRF?mm0dhsMThyGc}FDummPIr6sES{8YakB3P*)pKnu@E zoe3012NpH;u3yH**{~Sy=(0pW1#BIcPrOh4*m`p3DCh&?Rq)++rIE()r)3v6J1r#z z_Hf8*2xj_>Cp&rEp%~@OQfTjI18ENPwbZf!|At72C5#efg=C#@&T}&O;d6kTh*P@% zXr;~Mc*cNHE8XK4Z_MF2I&a8vamJ*Jkj_R{=b{Oh$@G3wZ0zAl6aNI2ZRqo?0$ z-FJf$-wydRb6vc%m}Nnh>SUMf!BaGlvZwcYe3U?8Lm9AF&$#ZZE)e6i;R_1uJ&u0%s> z?E(jQm8-MzOgHcUE-;}|K-%H-15JG1e#d2Qo|*BLnMY*tTCk{NQV4ll)^24?sU*+2 zK^DE8{DfSpU8l8~&WxI^PkqIRm*shIWx*t!@8hIvO3eROM*c%p%{d7xnRIlbYcMl< zw%WM_W21dJ6HV_AG2cpE@Ht$lt^-_zjNUg>P9a(GZ7T%8cNLrF$!l;~mdX{L`!)jqp-`iR=Q zGVxvYNZm6XRuldDR1X9B!Q<{{3)4Gw9SwIaKA!7l!8)%|8h!U@t_h|}JMya4QIB!| z$?__ZY3C1>#DytKSsV`L<$EI{K&L82 zUu4ly|8ANeN#|wV`Ih74T@()*s|@H1FSqkw$*WCTq@}n&ZAJq7D!?eJ%O|ORY9kA) z=LQNeCvSKBpdLaOQ_mR6UUT+^_&+;1+Zi6tsmCVObM=B4{2uZYYud0-n(BC+EAXMO zbor|$9(Hy_Dp}mIo71$y0a zQs^p*=fL3_G?TjW)Mnjp=F8E6S&jSYJI%<-!Xw!m^F$Pb%qj=KJ)^&Ltx|_t}rhm*IM)OarLKN0yl?nDt7mu4_~m z)8dR4F@{_3DQrRLLv5O$Q|f$V)nHGTeZhkobed6O@IBNDNg%&c}ld@TV|7T!cw_wUT7y(!U_V7QOK=VFZTEzi>gbuA2sx> zcV^DygNS}h)4a?s@5L)HDn&^@y6TFQHH#j?AO)jLSV<&+*ktKBT~y-dJ1#6TJ_{l6 z2Rvq5bSIiL?6cFHAwOOd&h#v>9dq1uSoBP-<3@V(aKE4+NO^epQDEARU|=0mDve}p z%@zaFZMlL&H2>kgg>levR>-RM6qTpAK28dcMrc>tUEFCbus*6$LFu22RGluYh3*Fk zlS2yoiTXnB;b7yNRte$yoiSGg5qNo0dnfzqI3jOu103AM+bh%Xcs0`qiGz+>hVf!z z?*o))yOt*`Q+Fr%J?NyI5nA0oWONzU@FkgcH4Hct{b{grZA=8p7j~`h)5;?~S-wi3 z;A^G%P+x2Zmo&M>v42qTOW?TQ?G*kF0?R(=rDyELmay^$V$0G{rh)w0_0<-XWfH99 z>_-C&?hO^*9%m&W2pbS#TEPHaK$F0IWOI2xhSV8zU$whqzkT@HYPMC(CX6bDhLe%n zlO^@Tp{Zaaj!m-|!xpvJ)ar+?Q86#$)$+8pGP>=mfzL1D`dV$lmfQ&sL$|$dyHD%F z>%wIkkD1uJ_<$Z=5Lo#`Atu?)kd5cdW}Dwbzc++Lc~hA~rNR9s$Pjtjr+u`=Y!)|Q zw`e)S)w+D@}wo1{7wU2co&5N_sIX<_XI6zI3~Thjt5!{mN%B00x0No3>Y zPKoBo$HbPRHFsYjb~{jN7v39r?c22Q`Olka4Cd=Fe0lPgH-1~%^(W8kvm;%0@%-GU zIcDa1jTsH0v(hvZor%?1kveJ_>$<24M80Ar!a8AEpny@NZ&2%#BbwUU&}&Z3%WmlD z$;w*-y1eWa1Ofaq?grWMM*9XrnoEMY%~EN&YP0k;LY&d{4YT8j5EJgO)-R@B75Cc~ zv$q{DTLPhGy;v2=UC}7dZ{)NqZGAnA3qukt^0Z8c?X$HX&dlcbzq}bw)pYsk5n}7c zV-cE=!Fby9(Rrm6aq)AIab=7={>j*rEPg+!93H{O+bbF*rzPWo&%xV?GF0P~aN)=M znhLBA)`Onn37)owsHbCZMN=lb$v9e$XAWAGFR^oj5=Anfi!+m$rJo%z2$_*qPT_`X z;z;e5sY2CXBr1`9is&EcKIy?!F0^v*dI>L5;N`GIheV`0RIlx-QWkp3HU`IVZ>lY6 zBQuWow>M(nF$KbYvBgPu)Hgc_oiy&(AtOxpUnFqkQ)oEVl*9pJjh`*F3r=)Nhwc5_ zHl|{d-W6`5O-+f_%RVx8m-Ro&5FKGc*7l1YmviJS<*uV4)o)^+YyJ2lEny9_OiD?l z-Ie<-U4MPSi?_xx^UL?nyFYX`5HR~9#% zHQQz<9zQ0jQ~UONz8Wca$~)k00s(>{C95oYX=Zr^{D`i7{)-UX%;n-W_os8Ll3e#c zFnNjvoHcOL&)000DBoMPr`7Dt$Smf8eEv+|5+g0uH}%fpZcDM*Q{@dp^eOeuTmJF4_T;$tiCFNtlnR}~pwDx*t-;s2*oJgemo*M=PUEKJq(D6Z+|K|CyuV5<+AA1#`o%?ZfCYHT3g$s+qKZdwW3^3;{p){4YAB( z&)!4UeEy#=Bl&-(Q!UKLAR1o_in)m=4~{jhI_v&nT43)iCQb|VEggzmDVsC-ma2!N z>I&?ss4}U(G~X`%K!ZEL>G+`{wIuDanI12`BuvV;h&+tRvte~g-JPb#mE@fDdW*cM z!iqq0CqmF`+?5L_2rL2D+fGbSRGFuw+M=>xX1@{*PJou@)0O>Kw43@w8e~>cu|3tg zq?w(R_#XjkXxI2*Vo(Nc8w>Vf9iBB~62w0uBYmnK1$_z| zeU?(Z2m2B2hIU@7h*$zXamk0WAgYh-!HM&`Z$;0=884<~!HaKUR(&ep3>{ENg(de z&crZBPiun3XqMi|9m498^?U0jFEaIGDo5b)PT)ZS5xq#@#)M!c-#XWK1-Y2f*w1~4 zYSh@8(-EH#Kl{*}DRS%vN#B*v->7U^xDvu;it3v_oj2A znk0XE6lQJBHH61q!i4c@uPEx-EcH@c{|86ge**~p>Np?ZA&Hej*TLCh{5#D^Xb}bg(Iv_ zuet}JCXF#dx)Tu%H9suKb{)m*qAkiI^BCri)J`hAPCs4nwPACFe4{w#3whA>8ZEEK zzV$R9USnoRjfM2c^^Rao%jodKg_j}T!U>e*ID6CbK6T~v9ju5|ZVAn}BM+`%ZQga; zlhyeO=QB0cl!`a+s&)-Dw?iKtYPg;P@=cst75dXXa;dn}_fuAV#sg7I{ zx%HlzxNSjj>s|`)woq9Tybc2NfGq60L(iXw+GN}4-=Ku??ou4b6$=4f3hVZV-Q$6C zKTy-;N z-G?i!B4G5DF>Yu^>a;yXK)lwF9oADaw!WVbbKR)@x)3-o5u6@#xc+@$;$y&`x)gLl zY;&#T>H*23yVpqHsOnS}c3mrFjJILw&E{JzT2O}KGofgBqnuxA^FuQGX{?Q!D#YS= zFR1(1C#x-N9M8b*fd64>*)b*dLFPw|p3UaNR@?^)Y(~Oon@RQ% zrYq%ogT2Q6+4l5W7Qxe6W4}-HAm&&-A$8~c-pN8?*PD*FRP$M@-#xw;Hu2C}eUC0U zza^*;c#K>^+DfpbAU)~mE^>lv-bXXh>>+a5h zBMs-Z&E$`#)-Jq+aY(G%&flDN}Ha%L zg3JxHWkmF?iw`aWeSH0CS|`gK)V%;H|1*oxAa+r4w-0fi)bIKFP*~98AL=+h)2l1W z5>~qpkKd8Di(Ue`$R8JH-z$1OvWcFJYh^)-`9fmL%H0ix!bEY(Sw6G9+le;jz9nP| z-yr>>#sqDVKlV!_Vn|m#fuOkzMyr2h%p6zJ??tLoMRF&30JZyIsG7fZvgWZN;wP^k&ME4I43vDQ;I#MEiUFZA*pU zdRoLCo=YN5tONJ*^7HPm*WVW0kIdP=OzY)e!5(HDtq@S-A}7&<-Z+k!7~}F9R4|tp zZ}{ZZ&$bcU5V4CVzrE? zL#OH-fC!}t#iNNQKNG4<<-7G*+6I$cOCNAaFWKS z08c%R=Aa{{VMJJ*GqhY4SwG?^Mpu=kR(tQIH%-WPZMP_zU!_%^0FQV7(Hx+O18F|( zPFdGv*R=^KuQ+>o=+J9V!{mKpM?8L&bCpD1fjX`XR!+*g(eJJUOhLkQ=yawuo0)|g|2J*zDC;-Ohlza0#$$Wp}nqEE6{Ltb}kh|Onj%tPKk2noDEnHBrU7n0gH{9x&cRssif0MmP9iX zYl=hgygC6_3ceWn&WD_9^CEm?O~VI|#vGqV3{ZX^n#we&JgvNIa~m5g4>{p%X&{Ow0V0HS%QF&=?ia2 z#X-BT5+xt`wWxrrAZ;85j7=3R>H04C>w@G*( ztE2%ke}G4|y!~;dK`zDT(3qO+M&knYaYA1xp1S z(p>V$>8LJq@A5OUGW9+#AM21U%Q7Rkr3h(I+{4{KD`V!%GtP51s*7 zwJ*GG7&UuSSm6mQMu~)>_z}|7{^2u|TLve5PghE=&m~etwWAkVZ=_s@jXk@VyB#gM z&wJ!amn9rxHy{v`xwDBxA%p|YdoFYlWCs9)dPSseBJ zRo|pZh7T3L*C-3^N<0#2kw;vRhp+iR~mz7 zez7V{_*Lp672Fq&)Ve4b)ds|1lo!D#4vO_3UYpDk_c5q(fwLCLo2Lk@yLXbEG&e$y9ojxAY?vwlB->9X>9_X za86bscH*&|$(#%Bm%H*Wp6sj1yQ`iqgGAZw(aW{2C+qk87o2GCxI|o3t3oy~!{fU8 ziN)kTUMLo%7Ubbk;+$6srGsS!I>@g-3SFrJ+vx2 zygtw8QNMHw-j79MI2uWMZ>dJv#0!X03+n-&WWWj^pinbW>$k(_0=#wxVTKLP|ftcFa)VXZ;EZdIIY=3ni zqbpafV5QF&TT>x|R+wkwEYq-Xa85btIw?o=_fZG4iD;LaJ}Dv8xZ#u<)Tcv$4CLeOkBQa24YYrgf<+Pu5*Y$)-hJ-@W<1-F5fg z76c_y)=E=kr`=a}weLXV*Ha&IBYahNp_cOXw^8rM)^0f6KWrCVm_7<2cq?|jW*sOI zMWZ3S_$=qfx-mugYbiM`H9=G64xb!f^mFA`bqW?z?uf?r%d==5I z-(6)vUS}TPd!1|3g+pC+xVje%Ij$*!naZc*Q5U*T$Czc9&*QvETxfRaY`)e4#7=@# zX4ILcFiG11P*(gg`JIn#%k5*qdYjdqRP`4!x=QsdOY$*dLA!Bficq|b;l1>|lBREB zOOM5u%d~^2wl&jimwZ(#5 z8s+Z#Hp59fivSAa?w5vAlhJFfk115k-8|fT*oIhO3BW@NY_QMCP7EukqxbIZdE6Np zvG|oW!jB9=7sy^@oqd&Lh<<}O6r1)zkRGSmIDTBzpGI?RS8cnl1-jz8N1e$?W}Q~2>yLjq6=+OF|aDBIy!y%tYadtd0;^5zC z*#^1CKp8Y>w|FCy{RWQb52Y0@-YN703g;^RCDnE1)*!qHhd-c|x9_G2RBC*>`X}G= zKamgkDHtL9_?~H5IW&H^isT>MvsGd2mf7XC8v`dmy!cb-AOKaSx<9mJH%tR5%7^_^lHUaKWH~d;F_T zP7gBN7c!W>6E+5%d#~A3tn$Tg{ST&UJe(*L7zYUWL!wxajEQGO$MS!`7Nt^&?28)l z=P-~GjQBKV6cAe!*`b;QK-HVB0BHm-^$w9TB2wAiX5O$Pc6R#nvVDD7Ujp)hm6IMI zoe}Uj1b+bPo?bYhtHgEIaXmYWBuY)p<{azRzmSts%-AvC-&>dc_a*)7+SmwjFw|kt zAS7Nw+5v3x1s~u}^8rC4HL}CZBye}*cL*``+qp!$0wR^iJz_#Y@%%l=b%PXSN>mr_$Cjwq%G39?EmG9|0mE%ij)v+a@>yUf7&Pi*GvDePk0E>Bw7m@ zU;k@I{CQ{pT9yCH4{xNvMKDN>-u++R^S}Qp2LnsK%}f6ulYW0^8UB$l{eQgJj|(p1 z0IP)Of9F#FAOGt=|M?^g^mC5D*R20s_y1#S{r@~$Uk0*xB*9q!f87On4qv15b?rF- zXmiUL2UVLh6OijGbPgw>^o>+%*zEuuiCfFSHATLm4R*u!r80ywLx6VB#59n~;{sYR z#8(tZmX|TSD-6!vq+-JI+BL2ssU^iaAnZ8@YTq5b`kv_4W}xe7Bubpz{{p8TR4JRG z0F!~)SOMx@>zY4n<7F5{a2B^yFV9xyKwgEY53;9n1mM^VlKY=Y-vf0eLC<-LRKoj( z$XD^6yUt@Xppr+9L5ZBDWCEgmQMW+XVtrR0hGaHpba|rfy$WDOspoZoGq(pz&f@~i zWD}H4)^*UJ;e%mM>*F^%Vd(N0>ipI1atkbqj$RDa1bKk6GXa-N0#t)n5mmVu*5(vY zn9?<%(lw9d17`HWjqPd|Bi6`oG>D9I48@fTK#XE!G;5y_#gY8uMx|!PW23oUR3Kz3 zA>*Jj@NW@zusJ_r;hIzXg_Hy(H2|KW9!-NhE$n7nD2?VZB$*F=0}i+*P2g`+Wqx|q zsS?l6aR(#c`L^M@?0C!#H(dtlagV{r?AMisZ$$(UVuY0E#?2ynWIzGxyosuA=Y4?8 zFI7I5A$FW;SyG7yA)CPU(bet76z;RlS1Z}zU<5|t0M8|;F$AFS1}6}DkrJZ*7&q|v z1A%XPfCTsMy)dxwL;e_*-8>*aw?MxKn#;^j&%yx}HO*P#ED(Cej2R@n*nhbcqiy+a z5|A)njvwSeF(QvD(X|+5{P~`e6<&;WjCcxYZQQIYO<#y#B<8>_L zKkr1T2y9jvUQ}&WOybNdNq+k9JM}F}e2NwfTh zll2!{g#mziL^0Ic*xeW#ub6t()t&owruV@lT+cFR6PSj>37*VuNwDpgo=^DQ?sq|| z>WSM9f78Id-E|=slAUd`aHZ=!D5&7xe;=LxG+%zw!2UMENrK=1eSO3bUtmGJF6El})Hg9xoR42u&_d6FXL~Wu~X~H<=zE z)?Z41!FXH~fH*=Ywd>adm2n)IrfB9Tizl2IJne1|24OkpbS&+#M3H(VH;2Yhf&}K$ ziS9GHFGlWQS_6Uao_wD+BF)MNIt;Fqe;rg`>R^pf+3wf+XEPhGNCXyE(0=1a#b55| zjqF?B`TC3Bg>#^jA>XqR=<~B9f%S{&+ha$9b$+oTl~)f8%N*n7kwSje*C{GgyCep{Nv6htKCXeNo?oZ`tMZ|f94@?H^uHEoa)3&Sc5N*E zOy8R-wNu}tdeGHIA9E{KSaCAB>_wH(aqaWd_eBKx#H@9Wb;oQ2+ezg)+Ft3u>Mt+O zR)9rHENs88sU~VMZW2hp+p|2!H#4hc2^g+tV;HT>LaKZNmyzek1sKAx(e_Fw;8nEl z6i0Y*Md0AHi^+@KWpuIKLggec<92FvuB&bE{>pNg7>%gF(A}vn0Qb}4@B&Qnx}4|) zBx_fGqNf}uJQ}d6?f*5ZxB;SZqmnb3lg*Y6(zD;4iPj+_KM0bSFAdQHF8{Tm|8)#7 zCL`+bTVRsB)%y6)zUMSYJa53T3A{X?x$G&Z2YSB#8F7YUG;zgd?E#pgl8VId;W+?- zqjx2q#P~hW9LVI42lkhH&`S!;aPiJ(Qm+mnQhfm_XW%*BV@Qd#9HuJ|hE$^5^K>1) zFUi<4B{kaNixSbQOHScxVod`;g71#bb|rZhkTDmV)mQ;xM&B3Q2#k@#FkMK2``U$T z0LB?@Ghl(lz*Z8N;;Xm&8=Foa{n4|evF<_f~ zkR>4k)F61pF|hOW2cGfMXh8ZQA%&j3s7Qr8Y?j`j3hY6uLR3wjgEFb!5fiG=G z$$s*rMsQ5!cgjRSk7ed1>Pr&4ejx9m%CL7GMOA)1Bjk5z@Pm*yC3E@GN`Jx2YN z^~69ga0#$Td`SSgh`Tfl?xW4*Gdp*n^iO0>!JtjQmjhyZFUbWSbg74G9V8au&RCLYFW zgr4fUy|jqokC1J*e7V)h(zzU?rI&5-Qsq>MMpaV4?CRrRsVnN=V@sqhWKtG%&*w;e zsM)x50J5#5EV(*PVAA`jnK8f!P-|#N;rub&Q<#=wgUw(L+hmtfqUb)jr?PGgB;w`Z z;3nPc?#MWw?3bJ#9DYTZMPicTzWjr9!%D&+>%8GQ*&*Zks>0mm#rEaytCX#+&a1m? zfOvny8y#X4$_`04oEO!QeZFP-)Z@q z6^Mstt~;;8rWud+V8Y>9B%iNK={o_|y0Io_u72-H$WDRFlvBG!(!4-hcyrV`z1vje z`INEW^|t%U6Vge6C*ktt$1|{oouGanKVB}`KaNreYZ>j8-Xx>Y6gohA}&`en|-M1j-D`sqD z#$9AQnAabgVb>jvW==*c1&T>=K37}fP&LgNlCP?OTHOz0a{Pp}R&@)d2}My$+>r}& zf59^+-kIZGHvyBV8OKBWFE_wMkh|dlT@L)@v;7Q^t_}>s;Zli>{z_aC#ZF$F9%}PI zlc68MyM)!`jK8iBcKhMxzO===Cjn)*RybMIz5I(T=H62=xbj-~Jo$zG^o^CAIN{}J zs``nvL6GrrzbxtRnv&l+?kOcnD=I&m%E^TjtfvUnj=M`2>&TxTaoV5il8)IxU;4m5 z0=4ISfxa&n-nx#@wU`1{bg5|QI6d4m{T(iP@^>1qE#pwn2KWT1e?aMoHgy%W}Xt3~mQxlJBx`$|=81=0N& zw{f*Il~9I|&Gm{2#I>Oly{YVa67kIRWimaEN=8$k^k97Ae!zXI2e~vtOXkpJI2!3mL{OT ziCj^uk#ynii(47#6m-k_d$(#CzJ@w^BnaJe??>Hs?W-_91=#Lbr_Z6mP@$5Avl0i< zjb_tiQi09mDBh#hyF$~ad8+6L8K%cNoNWoEd%q)Zm8B}Vaeytk6rD`;P$dD4#i@%k>` zhf*MgZ#hj%gC&D__riTCCL!)4z!r&}Ze23pC|(#3FG>&9X$TzH7dz}o%cWtEn3b6# z!<{?EGm}cdHt1{VH3=eY51~b)-YGO`9Zd9j3sL0q!#m#^M~=68RPD3yLyYGhwT5mm zV|}uS)fM1YXNw72(IZKZt;K6cMkH&<^84Qhg`1}IDVkV<0 zgjlGXja90O-B=FFmGqNe$vl%Y;N#^ef=$cbWiUc&-%jB+{Mw;T_<*LWeWxWrv1bO? zZ^Cd&ak@$HeiMNYPanDO9h;W`2;RBNZPSZ)1^!siH0W;0h9`iL^~C01xOCnn87k;= z^}mI1R_sdG9~5#0RnWi@E&Y7glOzzMl=xaB15Jsme17K|^^Bg^W3cyBbUSqRwwhq< zI~LK7pfW^J-wYsHV(h!_OE}%P^J)B=tg(envegf-NPKd|Cl0r*@PgrHa#A;wf@nFl z(ffqF{$Yfod`&VOA#S>*(iTnUED*l0293%)pqQROl+_UhxxdF@(f<6!Q`dy5$g(Cz z+;arOmOIlOR#kUL_vFQ6a3Xp@VLrEZjWwdh(0C%8;O>1*@|R8wqko*AnM2O{p@WP* zIuj-D-o!;*>n1za>}9rpwDTzCp)`XGnqPK)CK`e80tkqzW? z9QfMn3)06$0s9edl7gK_B>}4`C5@r3IP+d86U44+^rKot`^K|G>k+N|DAI4d`628n(a$(4%hOn-?E&8yFty3tMm8eqXcu-E<7Lyz9dSK@lS!dJ5Vqf9LF#aJ`&mGM+ ztr}hGzYykzd!fiUoB-cCfXPidtju!LV3&=E+gDax*(l=<4t32fk8}?#5n_T-`uAmL z&x>bM1s6C@qAL(t#)2rPbcIgOUvaLLwWV`EhOW_V(Zwzwm#8G!K0#$Y-}C+Hn0)4F zV$J!FA*Juv!T(4RK9GOg$X5DXOZRSSbtH@!zW^Ze5=5|^Nd0tGgslvHcV1b)3xTg& z_=^Atg9Fdome;o#-ebmYaH!|8G7TK`M9M&DHBJ2LjaBtW`Uc)ag~W=$zR6v_XxKMn(}oL&tVPW zu{!gaiRmMw_z>+(tV3nxi2{?oxg~cPs9A81ynek-r4{1PyUR6rCFN`S(n5W$nMKNEd9{olE3I z%-66Tbz`aLr8JH~bdwJ)mx4x8iCRMBNnnU~UilP_f$N z34`pS={%Qsdu%%HU`P;!BKtJ!O^XyN5t4fpeaX&#eB&WsIA}RucVDAFR8JnsX72yY z@PL(j)@yk1_LR!34tpOap6l(zEogqQ$LY&D*&{NYm-_o<@_}D#s>Mjjy9z|P1X<63 z>rQ~1}j`VT$(|5hnG#W`$AiCR~d6I=|&0W@fRVk$GG&*843N zeWW-=-tmg(3%esrz23wng?tE! z(|X}D@+p@zi*duVL;CdX3E6@>Rm_6Z8H3Lm+GJk7WyUc2k?6Z88z7A8Qfsv{R+_aw zW^%|K{Xu~E!FB1RzDSjlKhZ|(NcSxYcF%S4SDb1z%wIdhGkY>z#uGMgyKaX(R|7%m zF-!x0r_PsMI`lBm6{kJ3a{_W>yO8O_GCkH;$S#foZu9o^j!f(gLviMr<3ZP60unnGp8{4zAG(VF%{n|?%)d?CPkbyHkxZ+5Qy z=}PNJmWr*CdH;cIyl;E11NA#DWOe+voG3fKqpe3T{P|mNYMQ^Jge({r?Z_0_FPvUJ z{UfQeu^dCJkqu<8&fvYvjUgUI(oPT$-8yqV(U{7eXUol)j3@Vac%15MX`Qv!FuBmO zx7O|5>PfIkFSmcLu^gOMbzJfe5vP|7Q(AqASJK3aLBK<_da6sPaE}@hp=yd z?+D*$8&t=3OnHCiLLFLJ^8H)rXU}nPplRz0Bt-Q9w)m@V$9?@tzPCPqEyn74*0zi${yDis-NFh2F94oI}7H|E^)Ecokgr z3>*`Yl{pqyzBmZtJuYTWM1T4GY`XY05Xz4~euKYVVo9h6B$2n$#qlDw4xg&;g`#LI zNZz+hV9gSmuJ=^_qC~+`u7GD)4whRj^lJI{|v$qax@EJ-Z~aVEj>|4~lcZovC3yIgKRoJc?Iv}FbSs{7 z8+k%<3Ru%0e$8KK4XgO+Ll$#ScuPcI=y|Umykh@LixH_i-JvB6D>rW3YI0LVJn&W6 zVQpsq+RE~yB#?4Bfe*9}En;G3Sk~G87O&6C5uuT^GWFcuyr@eF-u#gNa2bD9o`}Oj zi2hX>{MT=#1o6l=68PceAnR;Cly!{#5RyM@6P_JGgVnTyFps^>QQ~FDv?}p!!aJ6@ zx<%$zeSNQ(X@;v0;c~Z%d7C4CVhAytrT0rzMX?;-4q$;#JKihrbtfF>lY51B&V4kb z$%-W)yZU62#sV~ycfGeFo|o9`7If7!Q${9r114s9lWf{Nfwq@%I+eelrAlgC$urdY z74{_WM3KfX9mxKslw6-_PDf=*$|l;D^0Q-K+essri3e1BT({f(IGK7>@NDZhxPSl| z?+q4}_{0x11HM5HJe;WmR?N`^l_Wrs zcp0H(k@t+q2WI)N3kv0t-lH%Q(kwiBl>|@XziX}(Z++UuGp1t}w)lB^;Tx6F$d5gP zj<_pqZRLbdgf+FyBn{6C0>xp_aj%IITiwR8e!|9MvvqAp1U8zX-Fe=^zKgq(Zkul* z_$B+@?s+`C*4W;iqor2Cg)6Hr> zxuhhhM^)qpS>zaU)ACsG+EDj75mL8@(nPtx7sUSlUiMA9-Cf40_@aq}j9562+)-$P zf}(hMgor)Lk(aFE{HGxc*CN9iBaY-uFB{2A&U@{ePWydhaf}x_@v{?At~iW4=_rdw zn0TdOOtY?g*U9Xs57*-1MEQi)A^lg|bN{ryZH?x|^1j^K5_Ma-UrlFhY*B1h8}MAwhK=ij+ExmH2*3e z)`=HNyu(ix5%_IBw3o$}NJJ%rtM7$}vW zmz8xiHN4IC{ew^lC;oQ8#6-lIq=2lbqOydUy^-+azl^{T?i+>L& z9K}y*yM7IVE-ZMRc!BKS?oXFpi0>22L}BBW-nyxscM7~ucg8jqB&KXmnFEE&1|RGC zQins2Si2j)-h)xCmtnfFsQgYg(nraE#W%Dc43QLNr;(?*N0hpTa%(PCZr#`Wm_-(? zG0pl!mIEY$1uX&>$a|XhQ?@)OLfc2REE(uT+g6seR-XNg%hApaXhcV6pYgjqbiqbv za4Ws3B;}ng1}D$}z=CI|aHe_@xZhCPq{+I)4_VU{_03XQSeKBXd_S`9ii+Y?_ik|q zJyjFK4Ty@|vgRsAKuN-7k5jU#=!8^~83*$JOa8@n_sty+$B+nm-Gg>k~G1Kg^$mI?MTeROCN?mt#%mM>S7Fap`y zr9NrEMj!6BK<`kr@%qir{bpm^!m$0fWJ$hmyM#DIc&vn09kS!?k_W|8cFgtMyv5Tz zwa1VsB}e~(GUfm=(7Dn)pui~6ElbBjgui+$d$YZ)QY9hjMogyv!~A*CZz`U3eCkb) z4hQ6pHDmOXA$?vSywQ{$`(1>aWG_A#lA|Y{rKJ<3sx6e%sOSzkOH4gJZG={;bzPO~ zf~W7nC>b3RT#~|pB%w<+?0$2!nb)z+3MKdP+c^U0k#q%Z-{^8{ugCX;>B94N=8NO3 zCWU=9eP4Le6G0b)tjXZ@5w+&hJ?pM+iN)fNP`n7ZNsFC`yt^Af^irx>*_$x2lw^Oy z9H$Is5$uYkTJN5VXRh5HdgakqsOrDnwWgD<|M2XYmk~ST5wgD^kFB8W_57>s?2`PU z-Hdy(K9DaPAS3S(UujW4vzs+D>QL(-s(({*UXDWKX&=+e{b_4*qCM#`P^pf);;R!C z5%CMO(`*WxszH}r2V8O-w%Y*Bx+Q5115mq`lG_%tkXvzGBx`1CCU&zabOxuojy;`Fs6D}oRsm(Az8Id0!i z9l>HrjNk_94F{z!e=zCV=Oa5?+Feg84+Aiq$nx4xj4i4`BF4b6|!jx`pCN%YzGZ~8oAQju1=0Fd-;Qr$=WN^+{DoHT-cs6G~YpqQ0Hc#;Y${Y zc!C^E@jL!d(}&cXo}Z#^Hv8u!MuL^-#ktc@z3nS;4y`|%EZ(T^arslVC%d9JDXa@in98mxtnUR0cw%N>3`IoPQQ8{;E*99A6Fto zm*dZnYTEcQK~@YQCi5OJZP%7|VRVgJO6A7Fi*T!uxmKqnSj7)-{gbdR&%SgJX^kBC z{dCMyZV5wp_mEl7*}MVCdm_f;4g~=dj+BT=_t=9oE-s@uR=JZl;X0EVJ)VTl@;v4e zL0{IP43?L7RP`rXqst6G%s(>SLb0XaqOjOCQMy&?Ji7kr%bW5M(aq)>o-LY9AfHX> z*)q_ns2Ks-Rh$(yg~x?-Z6A5AKk;9U4uiv{%pxQNNE3u=C2&lQ>RMMK(q|0Ke}j-{ zIQY$a0tl4qB;;#HdY{^$R(ixN^lR*dYcGGWrxIg215yac@ic{))w1km8kEJoz-lh= zTyuyng+I9)UMA{(I0;tVYhTvmpa?cZO^|?%AJfaSlHgaoOGMSXJ-=uDf%Y-Wl;>X0 zam-FY&G1uY3x;RtPQ=(UXciRNsT;XYJA7cXZn&{{`3(N=2EYGaf)wJQm5Tk~X4>gr z(zC0Gw})?^-^B8Robk{0GKu<0v!}pKd8z~oMF@$yU^CgpMe~66rtFgjMj6}| z8KGPH_q^fn_i$LD)OPKY&ejJt>mM6EDez_=M@c`W5D~bZamb-CPMo6miP{`?_hpNC zJk82;e&}=i0e4l1HuRCny4ciLU0bnpy=VL_Dyg16zkFDk z_=V5KApsI4*5@e86uuJ#1?-%%e4$%{I86Rs-o~g@BUi5{2}f}LpDQ30uLD=!>I`q9 zu-p6=wX#(bjfS_#SX<&KfN9qKkSsMvKP}QY81%?v)ttUB%yJP8x*NN^mz61wmuPR? z&lbaxjv(^ZaRA{#kl+UFOL8$7Dzn(SzstXbyYyYx6Y7MjTzIMsO?H`*SES#r)IDu< z3>nkxo;Q11kA4ZmQG&FTQ;6JX1DzbP*XYn6U$l`CrGf@T-P2oj^XJ=qFa8r(me%BSRa<$v{zWBTb7W z(o(7oDub8Oprm^55HnqsG&o_DbQmHw>MW|P+aiu{czX1q86v`R(X40%&b2C!#lHM1 zN77YhuTNo3C8;pFF1}PLx`= zaG}RvON-1m{_Wx7?&{ZwwwlrzQpqD@$<<}jaZp$(m7Xc|Ip0`NC2wcy@4R35Nj1WL zWt<73S)Vw^ncqQOWp&Tuf<$7!|4TEzWewGh_$t0zqR6WWZ*jIYymMBE(aH9}HcT|h zT;q;T4z{`lu|+jlbG@Bqx>V@}Rq!Tdf!V&{3QM-pdr{%kp={j0n}F6r#hJI}m|3Ez zAaXvsy!un8LAz12Tz2+ehj=WspzdUSlgxwRtO%C~ zVKOc;J&0Fisx1L<8{oxh*iu>-bYIXuW0N#oJZE9XnHC_o+edj`WGAk07Z8Shd(GeZ z2GTc|Od(4~=^=ga=P|G5Gs)%LRu7&AA(^HNv-DVY7FWI#eWp3L0OXzS@z#~YNA0Iu zn9iAtr~`lZdvaBj>c{I@gYwp}ts)3kU?_~%WOd-syaOiEZn5!AwPRsH<&_i*N-+<6PZTL)>dQfF!XXjiFD3~Sdu$Io?%UGTidWAMVf#Js8m5k zK?IcEv7u6xPN*WC&>=twy{Jf&-a!PE-V$o43euaj1c>zBApt^p7yBG_?|ppE`M&4J z`{TXNpIj@e%r)1TV~%={Ie}G;sy9A|`1`38y>YHif{O)-OgtMLzZ(O@4I~myV7!Q< z7U?5?Wvd#FedA3(61y*V4FE90J79~P-r5d}eWCqcseDH&z)VYCTGahrf|2?JoO(q{ zeJt{aGL>l|GzD_c2fDKSnliz=m-|%ShBrdb|mZVxfq?49>PpLDh-sP`FG0$)E zvaR;`aLp`5Y8@M~851E!as>kb$dV4>mxmRSK@c<`&yz^(0T>yHM8KConI2_WW$X3C z&p`s%#G9QRaf39*Dl}o-#S%y{;XMScl@U|V_T&U+*tzx z-8^n=db(E%J}q6*WedI7NVJ-fH!yR-faJ8S^(*~l?IZxl(dy){GIs)@-U=cW(J+AR zNQn49muMe;96L>0%0BTZ`cp*W>4;lHscmNEd8`fZu%?~E@o8E|&vmnJs`mI3@#+uE0GD>SCl zeYl|4a>N3kt{%67{W1d{9iQ;1d3z;XnEQMa>d3Vc*jalzk(xgt?G8qy?B1Tdo5@Tt zJm`Qwe-MyJ4Tj+I+=41D-Z@o$;!pB5;agA)UjL5L*$ax)?eL93vt|-nQ`gDRkp0@| zB1x&*MccN!TazTf-u9z+KV)4;*xkH~tj7>)AnMFt%S7D_;U~mkm#L+6l69R{AEQ^7 z1W=}~pMpcoz!p*4F$tk!QZ1fCfJWwl0lSeCuALrfZ0WzP|4Mamt)#Q%(^K&_El1Cs z(T0pnFRvT4F6Heb59zh9b|=>0;(%&b@=Ggz1Hk^a zyS-taQK_>el_!w$BK&H#89r2Mf8IM*)*s>;ExY}yUlJNxGXDGt zAQc-Uk|Un@y||u${N+aA957826^A;%i}fE*?5&?fe38PF4?Ce0mSP&Nmy16gQHvSz zneG=2n*vyp^$=SRj$l?iHJp8WcaotlP7xGrc0MIDlPHCh#k6JwSsF6eeo742wH@wS zR64iNy)Y}bU<+6{=Ul7rQ8!+2e-q&nK%kF9-cNx6Sd$2g z2T&<-D^slfO;CF-w`K|-zBx8ujy05^b)A9GmoGuu&dH}uC9b@PVvxU8{9u5M0M~$Q z9NwgO|3F%6?M%QLpcG=jFJ$Pc9!_&;P58t(tx0mcE%6iI-v83eLF{tSKj)O@+n zpiHS&P9Pwh;q<-+2uvr0GSBG9<=#66))SySSqkYk(}XLn6@7rV3qI{mx{Yz0^4l^> zM#3}Gmlkg?o`cextieA`El#_!lK4wTT{~-}aN%<)PaFAIP7o~V&EQfQ>zhXF^5mPt zG;PmZ`o@6>@*x51C6(p~0}BWn7xt0%39K)mL{&&N@a{G4y_8MnV)`A59ge+y0EW<= z9XKcUiVA7hVo*IG#>&U$QsVAyj6BtxTD>=$Xtt1O%%cRdE`F%Nq9SF78F##K?}=BP zLYU>`wMnn%K6gi+Tq`m$7$v?+XkKWKWj4ycu{(q_6*xZxjiIK%7Jx+t~U zGI{^#(Rba!3fzeyJ3LYLnp0p(YGe3GvV}9DMRe6Umsfg)z~1Qxh)?%(w30n!Mhtft zEL={Ou3A*bhx;M6Pmog5Q))H)+6ZS<==%f2Lj8&VQUrKn#Ds6k^wxle?OLzJQdFBQ za-*1{lphYRIK-5YQw^1Qb-W5=;PnLXFx{BDD{)F$*;&Be8p&%Oob5wp2ElET;)vCE zC1{E;lt)=(HRB686iwNd>$!3BI@o@H>N)kAl>IH$C8SMi)})FHDEXjUFu}Y#HY7Kc z_FwVmKC(cW!&I8|l`qOatemuLLTYJOBSt>~*W%6Q@BHJS7q)h;-wngRWxc-tR`u#v zBkvJt?3C~sKtjFf`Do6iAtB0s>Jq8TdIJeb&9ytH!s6vBMOI&ttbh3Sy6DQe+m zr-+XZwkyUXRXtb;ZV*3LoUcEXtk}C0_K>X+)_ev6K$sM7%NW)($@;%$7fIb^`*{EH zH}HK|AMY=O{(vg+-O|6U+VTN0F!LaeVmIMcql6+qb!QqIVj_^y^h|aPXWXWQ;EE{j z^?DPd-{dQ2>w5SN%6Ek{r(JEp_o5m!)FCW>|zbe@ISi0jmX@z0v+F1-l< z!>#+|K;bvGu~p14Nn;~})Oy#Ug-fD)TAK&QRotCUYnp3Ml;!OL{Pvx?#LYov&U~amS_N~?h zUAvvhOSv^07`wzy1y$gOO-IND+QODV+Q`RACm`GDYrA~x^rbY2V{ad-=et|YEwoXI zG*A(CqaFPH9J)@nCdz%WjUumUza1lMxxa;a>LVn!K;`z}srP30Tu=SfNGW^RN36?At?HGiKkZj;LA9Qzm7@c5Mz zcTY+WE*<7vL!5l#e=tqLOgtGVcj02df{Pk0S=ffxB2%P6&@f8b%k1|hjNPf8Y}hLv zV$XOsd$_iJUPoNiQ*kmWC8&2D|aet@k?I!m5T?3Q~2*^n^jEnK7Z*wTJtguQA*A3Pj% z@uIEw^wPJ|?+NP-9A*a;)RIgt!$!=RomIM%6~i!k9wW7vqjK4S<>JJhohUb-FRBXw zlsCleM6Kgu15QciMYc0;ra<9QO&yTnH*0h>h;f7kb1Z!lkQtlUQlZggRm!2bdJwg@^&CGE^G36`*cs8! z;?L>)U@@UA)wQ6ET)bFp&s*rm4i~}7?xph#iwcWfYM_Bhl*Nd~?4=Pwt>$GEJmXDc zw$!5wF_v2m-gnNdEpEs$th9wIwQGBYTbLQ0w zao54v#5IaS4p(2J^*0M_?QeGD7MFm*@2Y|G8H&1Z&xaD%dw38VF)KfD)dm79kdjX- z@nog3%>sTwEbitmtvuzUcM|t2mUl1!=-E1A@ zemad2uxr!{yNkx|c3DNA*G<0)jGUw%Ci`0JE$LMn>co=Be3@+TcAqLtQ?Tk3Se?!- z@yh~Wi8`(TbeO;>PI!=O&%Ner)MyX+ZF{v_lx#MyAD-GMepqJ!fU3M>g(+ybe0@ur zbFcSH(-KdHhj~nMWVWl+q(}kCO>MC+#wt&fKte1V_5i@$G={a3*o20FtdEBMlMOoU z&K20?Ow>LZ71&d!*XHg`TF+z&gX;(1_z&}f0K?)ruFr;X&)XqlGomC4t8}a5q@J=@y{>(H0`(vans6X1#eanZCs*ye z@Y(Hnrc&@(7u4p^GId|XsY;*o7~-y&{I`nMKLT5{1Sjz<|Ll*@f0m8;1fEpm@~IFB z^AS?J^CvSmM_DRTX;F2AoO}5BGx&2WITOjv&^Pk9vuuX-sx<6pC8C|VYD7<>;Xvdm z^5F&E1R%5Efn(1PKjG4}GXy7Li(Oz#^!Vi0u}Yvdwj0Upe^~Z>1ZO|O#0Xg4y}$7r zOP`W<-lL!-kmLRLpO*f~L0Tdq^+9|EO~j!8Et}=*asiTe&ho8@Og_r;UsW4@1Y+8n0AlwmgzWW=M zppsBOof-Dc=P#G|8qbb;9Uo4-;s`wp!KpX%rfH8EK?Sr9q}fa*}|SX(C)#X^+Z2Db6v>F}IF4iRq2% zpmfx*J9#1BS;B9?zNy)1B0m$CwEd%AZd$xZR3c1You zjQ|iE8IX{-i~}I~O@Q}Auc2d+Nke-qM#C)W?xbaw>|DYY1MF-iCc7|7^Ege_e(GhU zB7KamLn}~S%S)J*+01d7g?S_D`iYeZ)FBrEHAw*{MTWEvf7B>;#PJ&eekoJb{7sgB z3>4*g6?~a&9NE*~^b{y^d~ziDl0#ti4dMssWMa>b0nq)te;Mk(hJAl?sfUUn z5u!sfXdDoRp~g_Rmz?zuxoj&-ANtAP(KZ;%=yc#1VrM& z#3#*0jDeSU@Y;*5uJc(bB{15}PYed+-A5}s%Si#!VybaQ2~^WKs|iK%feRCntcju+ zeV3UTd)t7Z;{)9C`oo~ERGS+~HfZr1yb>DGrcI>)>piK;6bRPtYIrA9eteW|@oK(+U_0`0ZnXl#4Co8Pmvx&1u-E6wY8gC!| zUgKh{O=d(j3m)W(PxcC+>f}@3-JRc@Q8ju1|J|Ywf3-61_+T^oqy4^-{#l~Xp7&uL zUw^zi6aJk{+!5Cueg4`JMz7mnwa`mM-0XNqnr=}0$aqBTScwtk*&crOY&HHfyu>8h z>lGv92!k$FJ%@H`EkgA+wXU zJkH~3tlMi>dNBdQ(kxy3BN-E!R~jrLt~8_fxXA<5=~*Smuhh8#wU)``tDWWD;&`Wt zcV{%Zyvjk9t6kDT9K44x&x`j^j#yp(w~XiGwfDb5G;}M?F}yEdWF>p8r#flOZd(NU z*P#n)WwgA$xHV1RfB!iLJ562~KwIF>)@}7^D-5~mQ#5EMeHn4Q=^LMR$GOA5Vx{}O zuLU#2TESHI1x$(%VX!%nP=Sjp0X}M{3p~zJwNf&hYH`1qb|51!O zJq9Adkh4nJe-YXBpPzpJw!}i#qHBN5XU{R2uT0?velTwqsM!MZbTX@0sGYM`o!H#C z(j7pBT#GMVXDgp-L#H^^9aW1xp|!bi%&5BnAHui`v&oF^SAZJp#;qo>{4|qWymd4P z(W4ju;45-lWICTZBECG=#*1s&6J^^7y01U;@`{AuRSLbW@2RFo zAR+Uh+TK%Sx(3?V8;ij#nTPH!?chbjT)nIQHFEaXGZz~OkHq&yd~NSN`;`#{>cm9} zvr_lZ7{#RDG>sqE6xuvHdVrs6TDY$-+3A8uu}^TP?wQAVE*>kfi+jU(StYx;?I?);^*9kbY0R%0{eQljo#qeD_VlGUP$B3 z!U9q$GHsV94J7#mW;Q++?J-CjWm{-R)~97fB$Q|N|HeiBlhF#y_CP@8e|-68bD~F; z0COopLr2JgbaeB`X=YSbLa%6yZn9>o9B(~*^a0egmf|Xk?r`(8G!h@=E!^KxHOel$ z#U#b&X0@@T?1?Ws0VuLRC`c`l3-XUJ-Rk6rP1w7y;Ya?B>T;jB%rm`*xx(&~hZ&8vy$fm4Lc9C;gymo(_45y2W zG%7irvN*h2yy}p0MfsJqqzAmJLBR7!@P4BP)P+!Afby{BzD0FZ#`09|!&Ay|mGkdp&iA#OLKFA9 zDig(_W-f@AAcV(pt&yn>u&sj`o2{p4b~UaUOD%KiG~qvfhDm+)?GzB|f{z6djyzt- z>W}?{4T3u~YagU=Vg)=Q?b{OEmW}6?mk%mkc9`-rH?fP&=PGMzUw|AcIm*ahSaEPq z$MU8MaEn4)bF$Km9en2k;G1wGaEgHkq|{8QJ$AxeH}_J3y=lb~TsH@jt@Uw}n^QP7 zSKT;o^}8f4w8dm1B?IKoh%|GUPsw<>`pu~I39F5D0aIL39JX=w=G~=^B4=@TmgEsn z=#tui^U*LG?V>q{HtWhUubOHuWVuDsadD!ybN3*?IJ^B|EQoW?t*No~^(B3Qd7L2# zW0Z_+#;O}*3c+CUril*I<1yQ z)KXPzR`0LnY=bvheo*Qc&3+yw5PtmrHQDJ8RJ;(@iB&LZ5>y zc9&|5#RvNaawf;HyAIlUXglsa?WL(hGm2Oa91C~p^)d{x=wU?_9vNVLbT~*{sf-?| z-H_F8n_baTmUexYB;r-`GKk>j)~K*JG?Z~*`VCqiqER>fZRC#Grb@QSsuu>m>`ykQ z#CW}KFeeyHN&aAD$6n4^@4+L=R%Z;tb=)X0nN)l61>atL($60M7KcpejQkWBF=ppGfb#zuGcp58th{0} z<^iSw<67oIQHrVZs)IKREuz|OJio-djMh#~zYS1*K3i?OtX~I`9ug{$G)sBQ(+SbX zY|h_tTR`&Md6vs~v|j3HvNLXKYeye6KU$T`sdv{%h*FHPXxb%8s&NNI!jytd`b(~4py12-!(Sk%#0b%3ytQ%brL@Bb*;)M9U7KuI_`0q>qa7<720cWGBq#k6d8*< zPN+c`@})rQ2j}viTeg_;jDUyt@wq~k!NdL%2`gXV9WFJR3=RcLwSUQ(EO@H$D^<|q6bA*6PUfpbN= z!9sftK?j+G?^vgUtLIXB@IB&>Vy4AnvKJt(7SEPa1=qxC9{rRUKe_*h6!94(IxB*( zX=3`PNRdgCBWe!05s{~&f`}S=tge@FRlw~NyFEZUN=Z{7#9GV${`E_k>(zqE8` zrFOtV!EeVqT7<&D%XEd zhwm$jpt_0cF*e#egE_MNot_V|tN0yLMeB2DDWq$LmU`WN?8Sk_oK!KVH^UtLMJl7L ztWbkn0&!Beu9}761;0BlWd?nnPj|Z+taYv0;P4P!Y2XK4Xp#!+1`=3MkJkP$Y9yVXWg*W3te&|LkAx+L$t8KMTP-S{sy#6P;^>J;z+Xl50U+7cEh{x)x!$Q!B zjo;hvdW31k^;0^bB&8f47f5VB?LXYyeeb-PIc0V=42^G;GPB{{;brPCrrrTvKIha@ z$xNY--UMbMXVBX2Ir3o=%a&m^{a6V(@)l1Vc1VYC!@9RXcM28wQP;6Maq~l=GKaM=PX)qZvZnM zQ)gb!538xp(yiS_GfXFx&b5;6!wfSxhDKuAox+Bl7&Y`Zw(2|zzVHio4nS4Azxd)b z6nEz4)-aR!LA@@#^_9^z%uXQW`R2~-8~4hE2$N0TL#gVOlKUnf5h-SW2;1NP$jlLm zVRJ!q<=qMYkjW>v<)39LntQotcPKQXrg06OyI9t!m)CS!j$AlUKtkxl4dpClrYrr; z&Ux-y+(CVu*XwZ40(3p5^?l_^zPW2cN|1Qf6w~qkGD)jUEEp0;Y?l>cQHM(7sWx0<0(fk~EWRsbh)8d*qxd_VjMX$SA;=+>7dM`4t#R^_Od62jL&}9TlZl+z- z%AC=PJ-0CR^fTAhs`T+f%s~m$`{ovNY8&bNSHd+FQ*_^LcKjK^K*HU*m@I)F zTgsOk(6M~6yZXqj@;*0*d--7`6!SFj^vXr$!8b0@c}a_b{glD(Bu2h6sAJnlc2V`* zL+807GAMS$nb{hRWmXAie5aEfE0xz~5b8;y@hvdYM>3b)^?RsL?^Mv7xcbxMwCh57 z`FndUoz%)%e3h*&JD!d~pl7`>463x8?E5&FP`rPr{IFc}+Ikg!{3)rhPJnFrb4$&F zjc7>CD&ea!W3iq3*CnwU4&Yaxrxo+rt)kDauvmb#;9Xk|kk(U@@>Lx}Cw#SUJTmag+$XBM?gE#7B1b)Y2J zf-P_iPX3^{dz2jfZsHM+rI-vMs(soG_60F*kH(GQ-H)((qk~0krNX1GZ#*^5sRbwH zrERToJmUPzIM8Z7~ z^qHEL!(Rf`m#cRA?niDxw*mq#jM*Q?p6dx$ar`2c`?Nyk{(X(sjzGAN^O?{w=TIBC zSZ;#rhz0cN6WJC&-&B#8C0YZLx!+8i2rTH?9sOPo61$}!_z^KksQJ=(C@~99k*Taa zIkDs}X5iA9P#3`UIpqse@8+#gr z=mymy*fJH@N92?obvBOfaj%V3Xr3t;UR_H>$7zwfjD4harhgXP#Wn${YbN*IJuf=*Ls*A#Asa@piU(D2%YM+71nv$-pr_5+U8)W?`6ft z9dP{NCFS;I+HG-kZT2XBx10*eoy{9}HQ=&}BCAV?ZRK?Mg{3X{cup7!Kh|=gHDBi$zj8N*84I%-P~cIsm|{i_p~yvaE`DPk6dFYZ zRX5ePD@z&rdhAyni(pLXP0QaPv9aeb{*sCRa?~U0Gke_aIX7s=^OJ3Tl*qK83w`fA z;w4SW9tb9tyY5w;BVi)nEHNqHXfkR39C1f2S44;V`}jqh^{i@~3Hb3CqJ`eGa`P^} z>}<2P%B)-}M)_Rb!Nl8Yujjd8m0;C6m$v07yNqhz06Th%F9jrUow(%oAjVviw)uMy zmqN)QtNCVTIvqM5nAUK2qt49b?Mu)4bQ=oAyMgKJ5?KhdcG)jL!)#@fZM`gci`k}< zkM&31PbR8AC=Ci&x%Jp&LWF#XZU#;v<_0nKsSo3gTQ1W?EV^nu>EhaT3nF3#4@;I{ zR-2MtkX`=zyDis*1vMF*A)0y&_FM?J+;gZJTUQ;C>UP9V4SJY;ZwV3_4m~O@dAOuc zK6|K&zJ+cItV}J;k69TJw}HNJ+gzcN78kxYYJT^Yx%;O;ZG&FEESSI5{z{$8Q56?A6X2wXa*QX23GIq?XhT^&{jYu0b2u7nxHd+is=7`lglYs^FB zse6?f;WQx>xfIKxw1U)2l)_d0xTJ5o#lv3 z7nfZ-1a1+YCnJ8T^{lo(#=WEf6c?K@Fqy#RVWN{Qo~eXO^)0MlWTgQR2-QHhH$OG%iHi_96VH1P;ns*C5N zJ_s+H725U{ebc+_U!wGI88HgD83wQBQv)kgYWeYAVg= zfxuE2>%ms?^z#R`fsW#|3CtO?mA0NQLUmmy6*xVtQf3pz?^N8B+4uE$%59-vAgcR1!zFA0MQv+ApJ#jRAJyHPsIY|F2Y5Be1U%5simo~V8F{Z=nI!& z*FYP55Q_>#mXnBkro#VbhF02DxtVtTrrEnf&z*-4P%t(F#34EJHwC^a{uE5c&1GC*p z;W}_$7_;qsr+7DGz35hpGSn=y21~qNmHSky?VQ-UsTbJDdgXck`IL}4a}x>Zu!!s6 z<7}~XxG3U{`FOA1oa@)+h&vzB4mGC6VdYr&#d-GPpa8cr+FaF%wam=KFFEDN;*99r zc!Q5T<@rx1RPt+wQALAogV%?1tfY1^f}863pDh~1J`xtz|5yuvF{}oq*3hG0PFGa2 zC&{l$bSoWqvE7CN^(!Djjp6nM=63kEkDPBj&uis3Db{GuNw;saWj-)~nNipc*d8Eelrp z$-8*)lXIk0+PxXgp}V~@%HzL;(ijMc1Sj$cBw~El5i4%f;j?zxZlIn)%IlDWuQ~>s zT41_lsVaw%rihCE{Sm~-xFAc`013}ktGO9wRvXK6BKmG~?3JtIU&co+85EA8zBQx8 z`w=UMQLIw=P%Vr3lr^1MsjuQn`jFMmGb>rJtUdm=UY7ccvBCL;mcBvg-4eEspx!?8 zbI-01=}O{zd;5oykj0{xi8~0yzc-mcfXRfG>Uj8riQLryhM~8tUU+s{D=CX7+lsNV zujisY3#npo=1_Kfdsm;z6m`6WY1~^Toq08H>o+#r^G4X+=5jTGtGH)ziP0X(3lA;q z9otRU4%AVD!_4E`Ok+TU8A68^YG3`kmL-aDfbs~{ks!;`{p_NrgWnP6^G7dn)FxQ-A3rM zeGNhQqv6xE$=(@yO$?Z^Jg}pjyz$lRRLIc_c4dn$^f#7Q>|^zL*I+#$d^yzQXnUe- zD!4^4mUaB_(dJQ6OfS9NsK&s-Uh&l8{9&D5;gfhXJzKlN@~-ac)QQWUORw!q##QO< z$~K0VtRhdhm|<*elwobBX~k-+$nWZ09HI==x!7~~7Ew+^p67`*>lO*thPNfwA=18Z z#TVRs+i z)ae#%h!(*QVKHy-^Zr2h#NbdRwcf&7-Ym@N&#|`8e>KDuUTUWvp)r;y==MSC5oMfR z#)Lb%Psl$5?5<2lLJl>PB@hZ7_!(~MjFP1L8cCbA(NH&DB#T-qOnGK>*WFtCP%LUJ2pl`L%HjEm@ zJp`fjPEKbYXEO^F=MUfqYKW0xN_De0=SroFJzgJR-=9HCc;=?Ku83|;ueY<>$CTp7 za`?{es7;j>#lS09zYg2tCf?zbu- ziEk>{X_S-`ZISLGHJ-B~n)#UIz7r{qw8DFq2C3(i#cbNbw660mK@cFizJe_xoyx)5 zI#BMZ>V9z(qMs(^k752P_KDMFwhFcV)~7{He%zE+cvd7oM?N^Ss^Ho6w@mEnf|Mo5 z9F0YILI$OkBDfux&R12NW7=HxvV=jZxUxth#nFv)jTaJ+(E{Z{)}C4XiXf?Ko`^6E zB#K|NN# zjwQu>1_FQ@o(=}89E(E)ynIK%cR{tWl7@||o03=^uTw0MA^qWclkBGT9nG>)K&-^b_vP!+4fJgMQA(Q^|?`mdA zQuh9c0-J(KldWb7-iX&3K^Edm>^Eh;xn<@>hP(O`~R>-Hb%Q9*?Sj2oF#f%BQ|av73zO zS8bt;9cvB?e@-brJM&Uhwz;v!`b2Rn02_)nz1f1dfgX#20t#D{-jg$?2&O-nRRWXM zX`{y&kKt-)$7~zE0$R`G9%w6>?&}Ad@fqT$RRhP)m@hfpT0S+Y4{D$H>Gahn^0a)*K%o ziu}}}il{y3uD>z{|M6Z&KR8_+QTFNK=BE?{rmla*cBuQ&zN;RQLJ*jHi$^?!`N~Mi@ z$c=cd&f<7qhyI+SpWgzyZTn~)*(^dw$TMm_l>;6BW~?(;c8xjLCpJk%2B9|C&RCTS zl2k|KwV@f{4sj5sk1OCY7u4L6XewmcDLWxC`M#T?R?5&|xv;zMBmDANFYLCcdl-Me z6}j2bzVDgoWdn(&4n*3pJqD&A1^p~AVwRx6z}$Bsu5`WI5kN5d@E^XQp)0 z4t{CH=&_a2{w~PKr)5xQ|2o5B5U-M`oo~O`2<1~fhkkjb@D9|N&XP-Rl$JU}450bS zG{$UU6_Rc-W>)f!kPdY(@+EEbOVI3FR%srF6u$c|{cW(F5Di)T;DY0b>Ixt^Veuk= z2+UNs=30rza3{Bg)1$<2n%%8#Ez7s>a#HlZ$*|APnL4(dA7-A*9_t<~8AiQES4=m@ zCXsz`%#MBo?><74=SFUg?o@A$=(to*`OImGUYYYaWJ#_@^TqSk)F;nKqPIy6L}*&2iuyokuu!IoV-hi=NoJOe017 zTb*_2bhvKudLGQS66X`p$*>6P)iX9LD21fDFfXSd0QstZh_)Ty7k53!PvTrBXwm)!)jFWI`O|) zIllzqJ0Lm|d7hHG+(!$?zz``j{jFGG4_}B1cY-nj%(k$U^1AW97(b!bU9^IAR>LldR zYzr=`_kNCd71@OgzlyWaBNsl}JUE0enyaSW?!U9p zA3A_&7+*=r;qif(9&WGf4B+VR95k5CwP{ecR84%ru5ydVVsu^8I#Q)1X3A<|&EwCt zaXF>TKs=! z2$4q~ep5w$izzKiR9D_cm|+Vr(L>lvj&-!l&CPQh;=;#=zzVZs#{s(6Hz&x+N~aE%XAp&c%i52Rob5cnz%?PXadz3hl5 zSDv{C@iLvHJgDqn8Ii(cBpq${dJQlR*U`8QZfnxZv2Y z0-@t(c}$X!IZ&DzioX9BpZxvkVje;Ku{73C-EZq>&S2l$n@*kk_Sb01tk8%vxTITK zW{TnQk#ZL8iyoY#=0+dJnY4A{i^>DxZOB8$pzCOLD9$RHns>;1f=ZBLHq?pKiqR^% z*VAR(V?+;>V|rO5eznW?cGTp-Y@2O-OuInmy(bV)tMmi4`@!_@tgW;Lnn}Fu$~3~Q zM~KnBccqUfJ@!yHOCxzw`^!z#>pTnQ><5ihRZVxcMYERkqJ`zG*ydleGIP$cAfepy z0*WaaJoP*s&PWYdN9$8$*gg0B(Fxd!QYqW;xN5?RQXHm45)Sg|8EnN`caJ>+bvMZD zkM%oPkVAKxzp5undmgHA-Wt+~mKRUyHqV`?gRR}r=Bqa0vwiR+bkw2siqcH80xmn? zb1h_WT&bti%51LUB!=5wP8EB%Yyi#^E4A#dwbS)S4Ea_u{Z_f2i+OXmKRo3)vMVuj zfNd;d!M%FxjwEQk8g7IMs_R+L!|7XqwG%x{vOD)Usgg%6U*+e6UFSxQx`&F;AqJ9K zd0f_3xVMTY;0lK|Iyn(p*)<`TS!ckchELXROcK4gqK=mWLi-0eFWgrl?tC4S-a1eG ztC!V_+hC@`hG~*J*2ZSisSCKr(4k5=^riCQx&5(+B&G*ApG#VXTb!4NrS7gk2RXrB z(!+w~)GHku*A@u7p!k~Moul44Ty&_%(96-Yv@}1($=}&Vrk-Z~nvNj6#Hz%4cKY?F; zjp_|Z&T*v={cHk9R238ii-6Vg0~{a$ipzEH+pR8}^kci;o>MBL=EfSUVD=(InKepWmW^E#BZ4PC`!CPe+CKJi5>>3DRBI&3@NEKL5A9JLzJNFaSNlb}ltO zyYkn2oSqpD`d>LQtj~uwoAiCXCu%%#A^uKIQ838F`QP_%9H9I#sBf9%Z{5=*qVHh2 zi8cHWq*p%ebu3{Wdik$U|HdKu>y#xf0o)@W4`)%~f7`o%X?6b-@SLangoyS3zvuq@ zzJDR*AENWW^!zv}Fr^|o&<@a>?FTebrF8}xWMW!f~MK~zr4~_m0wO&s8fE|85 z~hLf*nrQUdbbeeNQA*_YAYsocyFNTY|;-t zx^OYLWxKqwLO@7EBn*|X^DgqPn#1N(=MfcY(dq`rI6@|pZuqRi4;$P^mfJA>%RWl+ zxn}t_3+l6kWfVvQ3_eM^{(oPv$t9{$$rS3;JhR^(`*DqV1#)tH*Y@5=t_{oxs6DOq^3!pAIS)1R8J@Dl(D;OVO zuxdFX6~RW3HyHh*pi`ZU;*OZbM_xyv0b-9 z2g{FLaO&vlq9+$^enIo8KquqNbIT8DcDlzR@uxka09vnqnfdm^%kgR8q7Dv7mUeo; z;e1Zk#U(}xzfE<8X3o}tQiGw3@h2Pp59Zf%qS3SrXCLx_drA*mnwyJMA78tiAKUo; zHR?*2r|rvSveU-}!#D8{emxGXsB`O^mUiv)<&h(VIuPIN53Nd1kFYaVPA}E_PhVIm zbBmqAbA^7iAAEXT*NZ~>u5S1*U`)Q=DU4AcMY)30IvY{>blP zy{BVY%m9}0!y&x|IA^8&$)hz=zyp)C!utPw1lTt?gp#gL#bDN2l$*PK?+kG?BlJ{) z;(zM;S)l6xHW6hTA+%in1RwIQSNx>wE%C@-L_{0Zv) zpBpzPS{8mxE{3vwC$ud56rcaDcZ;AQ1w#b=lS7eaM!0At{6vB!LWV6(e0>3vM271K z9p(aDLwTPeeS=%Pbq9~?BwWs&xG5JO695~RX=2fkLLaB6N#s9A?k63*>)9?8afOPC zj&#{Z7Hd6NE^n*x?e$Z64^fWyHePct{pGay*qkpiG}arVH?wLmRAG;JEDVnCWb>|< zp((PP5*#`*O-njAcM^@*2KjK;K}Z4T1-c_*_dBjG^gg_Pi7X(=G879kQPTgkdw6Ay zy=K+)w2m6&?v2FzKWF7UvkxHpyFzb`UJPs5p`zIsAK{0}{=EDry8P*!kL*pux+f^x zNGxL}B~G|q_I0m3r(%6^Y}yFrPYTH3$OHteAwn0=?q0hb;=I;8a{1@AU)1^QQMhcq zXJQ%V2DqY5+f?iAld-G7j7Ve~q4@cv%o-`66z4H--z>5+5$q?~9mS}vtt3nS;_F{* z^5>&^L%JfeP%NWKzisrhV$AJJlAJ)(bJinwel<-+>d(lX%%h4d4)Pwk_4E8+yZCeF zm2rK~pPWB(8Ch)Xn13gudUyRc*f;uzCIfh5UeCv_!m{NYOwD##&mB7$LVzvD3)$Y3 zx9MHmYX2J1^lr`VXKd-uef{}Dz(rq%uta3d>uuS_!nU;V_-8bB%WIn9Q*u8>gE(I1 z>T~Yb4|ESar6-zYT~~>KXi%CcuTVtYgrd1OU;RU6TW!1iaG5hDoXb|7X0VA zi!TYVpv*&4i>5n#hP&=q(A8^10j8ov7?x{`C7wH!D+~}+C+Rq>%sd8 zV%RtN55-E$NRff-u>JCKg311zNHJLtsnptYkJ;il8kRjaNJ^a(jwa-?@BYwg|C8EE zTO${#Q(t|Ix#rchj1L-H3k~-+uB(2@1gz=u58Crp5WOgVM8Xy^4+26wDUXej->fko z!l;@T6-iOLL_wrM=~hGpWay5OlJ2fS zR6wPaR2nH^=x&f2x*LY>8hU_%;oaQNeZX^{bG)Db|I_>7{Q?)8*|V>`u9d&F)^$<# z;}~dTc)z(?U`L;3^Zrg4D17bh^gVK?-9xui(XPO})Kpa_CTJ|-k+|O-DJ!c^5ZrLT zq>smH{DHV1v6QYZP}Z{^_@tugmK0xA!K=$V=6#COa02K3g+n3&CzGs>UaW--hz;wnp3`5ijdHGy=SW{!U*o4aUrlca!<4eb znt>#hzeH?CAa}TyA*v}{vFuPhol%Rq=g4jgrBB{`Q{Xa8UR7TFy)DByQEO-Dvt;R~ zpe0V)#N%ng8}o=CGs8RKl#x|6($tUxGNA8N(Q=PU?pD#}h#vN0n-l}}gL*%F*>8*0 zrWQDDV1(zhA@GA}UTb>>!Nlrsk5ii&(S%cc`sesLIDSLqH zU2Bd9EJ5?I-9U8{L`vjjh#FYyUL@pX=aF-~2BW`&<0K=6`D~!damC8YWLs1)Phoj~ zhRW((YV_&Kdt&zTsZ2w^%|MD0fdLFS5v!rg>6Kv9J=itpkR1!ey0%)5E;~cKN9p*s zA4+jyi~>7Yv)OJd1Ua83dg!}R&#*dV4H~I-riUt;Bo!BTx=yI7#^tZA`cK??yn798 z@kB79Q6_>vScXkRYk$&<-T0*QxW0XyAT2Atnmzh8%075tVl4Mlj8){`nAK^8JwA7b z>7iVO+(yMIrz`qU!meWgY_4#)vz!Kw7~42-fAnw#{#FH62xHD+`vQe*s*v&LbMXrT z-YKP;hVLtLJH6kBSUPuXyb2ru4K}l&F8oex(JkL-m>~2Q-M576{p@@CXoA*874QDY zf=J9@zoTMSRyuP@W_Z+gpk`T~;!{Zc+~R^XMAv(6eoo4X!>~sJm;X6Ev1OQgGDtjl zzE{3Tm7HlvI@t=^P|r>7WRZb4ydYA+(Wl8}e3O@$h^TNl!)H9ZY~L$(DoU`g&Pz?l zQ@jaiO9&9w%0-aW9EVnZ*AsFE7H-2yVB2DVzOOz|^;>TSTWTs24`$iB#kA-q;On|&I3xP!D~LOB8=A!xN2sPb|*NSt_$nMB7W^>NEvTQxI^*VdSI zi(nU>Ac1W$1QV;S7IekdK$1WFN|vU6s_hIGcBAIQnrzw{-boL3qg#Thz&5PUs`mof zP25+^EKN`1RDC^!jK0x;-P3m_Mx#o&7uL!uC|w)8N4Lk>2X7t$`%Hi|j^k>Mm7o;5 zF^_#B9@?HPY-I(!(Ve%{v!LH=-x(8qg? zrM5|P25FS(@oAK<=tha{3{@+Kv5c{uD_-vRTrI0sxBlD0p*r%hi&))`Hm^18kD&)F z#A5C-{Nh82b=?@AZ1^Nu530?p&UmTsI1mya%PN6MuPEU0{?45{FXY9w_IXS0eaS7R z$_{gJeJZX?{wd%&ecT7L#-`G%E6|j@V(qtO;e_qXj!eS)?YI0hwGS2iWrs`z4S^P+ z@T&WGli-P-H&H5P4w5QHfjhlSI~rQX&coX^2ZyK4-L#FJ$r=Tz{Ht0j!p`clA$lg=HZpDJH=rV&X2slWl~XVIcRzc^ zN)WTfuuqmB8-t<^5dvU=Qzg5vodvcBdZ;$%oMXpNL!*uqv99#=68iY&DzPFDWXYpN z(i>5(_Qq{Sk#W(-_1l%M1+l7QKoKqM6Os8`QZP^Jd#oh5RMmc^{(MxJ=fDuJNh$Bl zRZ3O@_s)?t0Vi8rhVmpuL|&q}KX>MVi?sV%Pm*t&9K<30Hl)x9*nT=yC~wT)4i$T9 zqeg_C23hE1Fv=wOrv2f!R^}4Mi6Ve%)l^zga{b|Ey%7Twe1LKP_@P2}e~&z_@3Vdc zJ6pYsOo}oMead}$NoKwy7R$8zY><}pQ8tCFP0~KeDiyncrp(f$tM_i1TKou=H>fgP z<#D$Q7+86`JX!B|!FYhYf|^R+%WthpoZsspXoB^FPh) zvSjtD31re4joNorZCs_S$hXFpMk4~fq&67({NLe?!mvn3CqoJ@C1%YAHTp-kV3M4O zL8Uss@VkQ|mn-BV;!RY32H{}aX=1)`9^OZe8z6}O;CeT4)Q@G45deDr;$L8u@cDxEcwI%5|uu$Pkdr1~=JJAWEgboN* zJyC~#J)FL%2X?MgGSuf7CcugA$vt7 zV_CH$`t%R}Hgq@-Q+8c1V+47k32on8_ye9QwHfTs$?FOGBKj^{s>Fs9g(Wu9KXMqh zX~j)T-u_H z`7;l6t!Kgpk+&aVAac&Nf5bt*5?kz)a1)tRKX3Af?Klhi1Tk!}`JNXJ6pAdGmgfDq zD|Ztco1T{!i~&kekU2Tr6)js&yibZFtqbw)WbdCc5EJ*c#T95u25G5kaZ8#Ttta1g z-@Jp3y)z#LW&g4@t7WZYp;vh3DuQ*qDvmc-C}IXXonEXyDpcNRPE=LpQJGq8trpCu zK|BK7)+9yaTK{|oiy9@VDyZ%Rcz`-|M|(UYx&GagpD5TiVg#ZTat6MUfs z7UOX0^@-K?he+kswf3XT|C(pkL@N&Q28Sj+{Ud4xkT(3A>dhTFKN~;KEiOvMP3BBD zYGe82bM=`_7gJr9Tx`Bg?$&?k>ZABW1?ZV%qRYrizrXcCU2WxAUCtLy_vCAfZZkkb z>8_mDHtykOeUYXDf(jSO8oIj@umOo=2eTqgFRf=j28HQ~`zwtCJ3&}KrFbIcOd%1s z@*x&pQjrzwik@Q#3J(QoFU8CP#~m>ss*&FP{f1VzPyE3XOkSmjGO>9LzL*q@<;m7X1A?h9ydb&eP5xnNmLrPHmKa`S;k$9|7#r4gh=t*X?bQ;~}&mu|L9JJ)D~(hub7|W$>DfEx%7Cy&@8S?Jjm9w_SR1k=5t5 z1aK>JW~;8)l`rM7VygM026C|lD$NC-LU}Sj&7K8L0diCbWz)h-Q#Ge2iur-L?l{)f zW&K!upcuaABHi-If(vW>I@Pu#WgBBo@n)~#J-&BE3wo=mfapV!y#`%&lT z>{MrFKGk(~{CIpqBln>w)5(u&Y@5j3urscrBJ^mkfT}>H;iYxDC2a#PMM*N{&Qec( zED}atAciMuaT?~jY;9+^J2A!y1lCG=J)U5N9%b)7PwOtN5+gk3Q6a$ym&)|IS?U7k zP|y81)6hnYuf$FsOW@NlMdrK$edAuk!aQ2yK`EoJJ)(8*J9`}O8q6&v;n_`Y2mGDv zo?qi=zIuZXFCV*e`2v}Ah<`Il^I6_0=<4>(2CzI`;@7V9-XF_sw$vh)f|i(M+*1$p zJ{;`w|Hy*c+ix4b*plBeU+T%ROutWSsinj5@-4r_=%D(Q5MjRTnk{PAtw!DuNd=`o^2~i6Ru$)z$`tw{}@OkgwO4KzwL<{ukoG zV4y~OLLrMa#Cfmzj_ym*R6iP5RT0lYDaO!vcl2JE{Pzw@QQ!!w0F}}9{?~#=SK@ey z<19=}UV>av>+|=F=_du)<2*Nt`mqEjG@yH3p6Kl`*P+vG!AEv;9Jt7Z=AYNQR8VE3 z#K(U6tm$wC75MJx2B&rH77*uH-LfeJZPZjuvuTvs1y+=6du)y5j}?AwL0;5t{t~cU z2mvW6#vpI-FV5U0j97wA%wte3r`DAq{zHk7@Yuzx#Poy_&-y*NmdxFUQ6EgeW`Kzb ziHv3t_X}(NsxWKR{=QlJcuDEc^2CKRfOMYy5{5rEA3u_U>+ys7ZvDW znRTqObF4UVyNv0yg$jO7`2sTk>ATmeMb_)(vUUj1;8wU7q!}w+L%xIhv9YY|NKnOA z>_|xkNHQ+XL`52ro8d=%b2kVX>A3`0P+5@rUpuPACB@EG0yZ&+XnhO*6CNo3HK zbBK{(cmQJ%GMZ+Y>Y2!bb+v$=py#wQNP?bLuSYRJGcPY)Hb?2F0+d@|hwFw(IE6U5 z)sA^H=VzU}J>!ZWiG#aUhg5&&8K2(vL?2fH3=MwKtD>xI%3TFDY}#cE$+G-3>27W0 zFa5?HVZs>`8p;Y5Lfp2iN1VoJdjZ(j_B$Fh+o8ybPtn6TKD~g)Wy~nhq?Hz8oi7bU z7T*g(bs>+Hs0T7aKt8pW^Q@Y5Bt%3XRGim3IGxs-i7T`!4ZjW7YdPjOJ{6dz!55hq zdel*1Y8>nORvmk0}JozQhxgO(2YAQ0+s_=gB_4t=;W_levou}FD{g+Jn zC@u3WwQPcgqcq>i#R~j1Ww4@^bFMrZCCrmFpDk1h9!7JJ_j!+7K7b*j?f7?0lR zjWM2hn5qAq4Eu?P-M8!w!LmfWN?>p6>bvx&;Wx5orUBD8bZ^{bJ-Wud(T8f-49TX? znw-@;(}{;ITFi~7#Rp#N8PQwSd{LOC_j6#d2_`41xj$bycV$09#l^SbQSIZwv zVZXTd6&g&LHSth0S3x|06PLrx+>D|52d!ie@c>~T&wzm3?VxgPZ9?zQ`x(?s)yIi5 zLxcSg=+Tn-NO7t8^&z|U+d<%mk*1s78I;95ER-$_WDLzdBrzlUUkW;f6tb(1LWr|I zz%f}lT((>M+6F``)$RJ;+UWq!LGXT@zd!bTISu#$V4o5?Rv@Ug$1>Sf3#XkkX2wXGBiWA} zpqJ&xHb}Zq$@bY3j^eZ@FFF5?gcsMY1-xZ!YONYJ^&Yu7q+iAGb{aygRFA=b1_4NWsl{+Zf2H}NShfhbVc!V{3_+xtX$jMBOF#&0q;NA!rdoqRrvKHkKv2F zfL>JF(;rl1#facRi_ffDZH6zT@^ENG3hR-J2&+B@X$YVbq zlq%GO^7A}zWLK~h%lamf<+7-3)ZZJl_3Wko){h2#&2CQOkWvwbn?+^@AhF{k1{x&3 zXjQriZ9&krjz?5#@>k3ODBPC%+d>I<78GS=J~rX1>e$W1wP6wd9KqvuR77PEDhWdx z4sb56mAS*zmKs8cUEtO=!h(6PcbJGH)=gd&yJjt@ImPuA6_pGBZc`KB72nCcc{4jz zJ`uxu9tVlZ-7xVt!p`U{eG5K8?X#e|Cqm)o&dKKiq- zo279q_-^8>BEv&}I%qAscji8ndcR@1K=zzCf4IBn3gHj7Wxokzl>OCw41l2Fqog?T zAA14(`_3gAj6nv$TanMWSfVt;)Fr+>Yi*@hfQD1<WFBo34@a189X z?B{8i$0f{eBW#g*p~|O~&rCt*++fo}t#~i-SZK@d>B;Od|F8PzLZp5jC0swNn7zvu zecnVsKstp1D&@TQapjSeg(iJoAe63Lujm=!<&TRdt5Y!wGk|yYqc|lSnh|!Czo&?N zQq@pw5&DXbwt7nSOE8c0;0J{eQKO7Ipg0zv&<8J^q_!^k&F=pC%!`QWmy!4W*{*Tc z4TQg#8AIaS)pSgK^vx(K(CV=%i@z0kP$fjos7Zs=MTMoQ37$M&puIrpwGDns#y1#n zGRAPkNe2nr&mRq_zWe#+&YihEsKLUk-y0c%B{l#Afz!7*U6A%#u$a&T!k(4DBBGFo zK&zzY&mNkbK?{B5w;B+v=LUqC?4e?w+V=y(XL8abKeLhat^}2#f4sLljr@%jGH4&Q2rAO(|8q#H%~o4LO&2G;@JcE);`r7RHA!M?KHBk)BΝpO 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 ByB`1m literal 0 HcmV?d00001 diff --git a/docs/self-hosting/configuration/email.mdx b/docs/self-hosting/configuration/email.mdx index b1e911fb67..666ca76224 100644 --- a/docs/self-hosting/configuration/email.mdx +++ b/docs/self-hosting/configuration/email.mdx @@ -48,7 +48,7 @@ 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 @@ -70,6 +70,28 @@ 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 + +![opening AWS SES console](../../images/email-aws-ses-console.png) + +![creating AWS IAM SES user](../../images/email-aws-ses-user.png) + +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 +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 +``` + - Remember that you will need to restart Infisical for this to work properly. - \ No newline at end of file + Remember that you will need to restart Infisical for this to work properly. + From 036d32aeba2f899f9c999a85ec2aed0015388770 Mon Sep 17 00:00:00 2001 From: akhilmhdh Date: Fri, 13 Jan 2023 21:09:59 +0530 Subject: [PATCH 63/89] feat(#31): added multi env support in integration --- .../components/integrations/Integration.tsx | 164 ++++++++++-------- .../integrations/IntegrationSection.tsx | 7 +- frontend/pages/api/workspace/getAWorkspace.ts | 31 ++++ frontend/pages/integrations/[id].js | 20 ++- 4 files changed, 139 insertions(+), 83 deletions(-) create mode 100644 frontend/pages/api/workspace/getAWorkspace.ts diff --git a/frontend/components/integrations/Integration.tsx b/frontend/components/integrations/Integration.tsx index 73d6ee0dc8..4dba86841d 100644 --- a/frontend/components/integrations/Integration.tsx +++ b/frontend/components/integrations/Integration.tsx @@ -34,12 +34,24 @@ interface IntegrationApp { siteId: string; } -const Integration = ({ - integration -}: { +type Props = { integration: Integration; -}) => { - const [integrationEnvironment, setIntegrationEnvironment] = useState(integration.environment); + environments: Array<{ name: string; slug: string }>; +}; + +const Integration = ({ + integration, + environments = [] +}:Props ) => { + // set initial environment. This find will only execute when component is mounting + const [integrationEnvironment, setIntegrationEnvironment] = useState< + Props['environments'][0] + >( + environments.find(({ slug }) => slug === integration.environment) || { + name: '', + slug: '', + } + ); const [fileState, setFileState] = useState([]); const router = useRouter(); const [apps, setApps] = useState([]); // integration app objects @@ -132,42 +144,47 @@ const Integration = ({ if (!integrationApp || apps.length === 0) return
return ( -
-
+
+
-

ENVIRONMENT

- { - setIntegrationEnvironment(environment); - }} +

+ ENVIRONMENT +

+ name) + : null + } + selected={integrationEnvironment.name} + onChange={(envName) => + setIntegrationEnvironment( + environments.find(({ name }) => envName === name) || { + name: 'unknown', + slug: 'unknown', + } + ) + } isFull={true} />
-
+
+ className='mx-4 text-gray-400 mt-8' + />
-
-

+

+

INTEGRATION

-
+
{integration.integration.charAt(0).toUpperCase() + integration.integration.slice(1)}
-
-
- APP -
+
+
APP
app.name) : null} selected={integrationApp} @@ -178,52 +195,55 @@ const Integration = ({
{renderIntegrationSpecificParams(integration)}
-
- {integration.isActive ? ( -
- -
In Sync
-
- ) : ( -
); diff --git a/frontend/components/integrations/IntegrationSection.tsx b/frontend/components/integrations/IntegrationSection.tsx index 52d5565ff5..633a747afb 100644 --- a/frontend/components/integrations/IntegrationSection.tsx +++ b/frontend/components/integrations/IntegrationSection.tsx @@ -5,7 +5,8 @@ import guidGenerator from "~/utilities/randomId"; import Integration from "./Integration"; interface Props { - integrations: any + integrations: any; + environments: Array<{ name: string; slug: string }>; } interface IntegrationType { @@ -19,7 +20,8 @@ interface IntegrationType { } const ProjectIntegrationSection = ({ - integrations + integrations, + environments = [], }: Props) => { return integrations.length > 0 ? (
@@ -33,6 +35,7 @@ const ProjectIntegrationSection = ({ ))}
diff --git a/frontend/pages/api/workspace/getAWorkspace.ts b/frontend/pages/api/workspace/getAWorkspace.ts new file mode 100644 index 0000000000..71c5036eb4 --- /dev/null +++ b/frontend/pages/api/workspace/getAWorkspace.ts @@ -0,0 +1,31 @@ +import SecurityClient from '~/utilities/SecurityClient'; + +interface Workspace { + __v: number; + _id: string; + name: string; + organization: string; + environments: Array<{ name: string; slug: string }>; +} + +/** + * This route lets us get the workspaces of a certain user + * @returns + */ +const getAWorkspace = (workspaceID:string) => { + return SecurityClient.fetchCall(`/api/v1/workspace/${workspaceID}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }).then(async (res) => { + if (res?.status == 200) { + const data = (await res.json()) as unknown as { workspace: Workspace }; + return data.workspace; + } + + throw new Error('Failed to get workspace'); + }); +}; + +export default getAWorkspace; diff --git a/frontend/pages/integrations/[id].js b/frontend/pages/integrations/[id].js index 2a5460dfbd..8a11bc3c44 100644 --- a/frontend/pages/integrations/[id].js +++ b/frontend/pages/integrations/[id].js @@ -24,6 +24,7 @@ import setBotActiveStatus from "../api/bot/setBotActiveStatus"; import getIntegrationOptions from "../api/integrations/GetIntegrationOptions"; import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations"; import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations"; +import getAWorkspace from "../api/workspace/getAWorkspace"; import getLatestFileKey from "../api/workspace/getLatestFileKey"; const { decryptAssymmetric, @@ -34,6 +35,7 @@ const crypto = require("crypto"); export default function Integrations() { const [cloudIntegrationOptions, setCloudIntegrationOptions] = useState([]); const [integrationAuths, setIntegrationAuths] = useState([]); + const [environments,setEnvironments] = useState([]) const [integrations, setIntegrations] = useState([]); const [bot, setBot] = useState(null); const [isActivateBotDialogOpen, setIsActivateBotDialogOpen] = useState(false); @@ -41,11 +43,15 @@ export default function Integrations() { const [selectedIntegrationOption, setSelectedIntegrationOption] = useState(null); const router = useRouter(); + const workspaceId = router.query.id; const { t } = useTranslation(); useEffect(async () => { try { + const workspace = await getAWorkspace(workspaceId); + setEnvironments(workspace.environments); + // get cloud integration options setCloudIntegrationOptions( await getIntegrationOptions() @@ -54,23 +60,19 @@ export default function Integrations() { // get project integration authorizations setIntegrationAuths( await getWorkspaceAuthorizations({ - workspaceId: router.query.id, + workspaceId }) ); // get project integrations setIntegrations( await getWorkspaceIntegrations({ - workspaceId: router.query.id, + workspaceId, }) ); // get project bot - setBot( - await getBot({ - workspaceId: router.query.id - } - )); + setBot(await getBot({ workspaceId })); } catch (err) { console.log(err); @@ -90,7 +92,7 @@ export default function Integrations() { if (bot) { // case: there is a bot - const key = await getLatestFileKey({ workspaceId: router.query.id }); + const key = await getLatestFileKey({ workspaceId }); const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY'); const WORKSPACE_KEY = decryptAssymmetric({ @@ -214,7 +216,7 @@ export default function Integrations() { handleBotActivate={handleBotActivate} handleIntegrationOption={handleIntegrationOption} /> */} - + {(cloudIntegrationOptions.length > 0 && bot) ? ( Date: Fri, 13 Jan 2023 09:18:41 -0800 Subject: [PATCH 64/89] Remove netlify and gh --- backend/src/variables/integration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index a1aa5d94d3..b298a784a0 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -57,7 +57,7 @@ const INTEGRATION_OPTIONS = [ name: 'Netlify', slug: 'netlify', image: 'Netlify', - isAvailable: true, + isAvailable: false, type: 'oauth2', clientId: CLIENT_ID_NETLIFY, docsLink: '' @@ -66,7 +66,7 @@ const INTEGRATION_OPTIONS = [ name: 'GitHub', slug: 'github', image: 'GitHub', - isAvailable: true, + isAvailable: false, type: 'oauth2', clientId: CLIENT_ID_GITHUB, docsLink: '' From a8f38a5367dbbca680a07ddb511e7d6c991fc99f Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 11:40:29 -0800 Subject: [PATCH 65/89] Add github action for gamma deploy --- .github/workflows/deploy-gamma.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/deploy-gamma.yml diff --git a/.github/workflows/deploy-gamma.yml b/.github/workflows/deploy-gamma.yml new file mode 100644 index 0000000000..290f1f892f --- /dev/null +++ b/.github/workflows/deploy-gamma.yml @@ -0,0 +1,26 @@ +name: Deploy to Gamma stage + +on: [workflow_dispatch] + +jobs: + deployment: + name: Deploy to gamma + runs-on: ubuntu-latest + steps: + - name: ☁️ Checkout source + uses: actions/checkout@v3 + - name: Install Helm + uses: azure/setup-helm@v3 + with: + version: v3.10.0 + - name: Install kubectl + uses: azure/setup-kubectl@v3 + - name: Install doctl + uses: digitalocean/action-doctl@v2 + with: + token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} + - name: Save DigitalOcean kubeconfig with short-lived credentials + run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 k8s-1-25-4-do-0-nyc1-1670645170179 + - name: switch to gamma namespace + run: kubectl config set-context --current --namespace=gamma + \ No newline at end of file From 0242707e33e2c1e7846d5c86350940f5e0b73ebe Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 11:49:38 -0800 Subject: [PATCH 66/89] gh action test kubectl --- .github/workflows/deploy-gamma.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy-gamma.yml b/.github/workflows/deploy-gamma.yml index 290f1f892f..f9167c2964 100644 --- a/.github/workflows/deploy-gamma.yml +++ b/.github/workflows/deploy-gamma.yml @@ -23,4 +23,6 @@ jobs: run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 k8s-1-25-4-do-0-nyc1-1670645170179 - name: switch to gamma namespace run: kubectl config set-context --current --namespace=gamma + - name: test kubectl + run: kubectl get ingress \ No newline at end of file From 799a839940156d1ce944e55ce32d615a16ffb3a0 Mon Sep 17 00:00:00 2001 From: Michel Wilhelm Date: Fri, 13 Jan 2023 16:52:15 -0300 Subject: [PATCH 67/89] feat: adding new export format --- cli/packages/cmd/export.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index fd04e9ce2c..33021f1c1c 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -16,10 +16,11 @@ import ( ) const ( - FormatDotenv string = "dotenv" - FormatJson string = "json" - FormatCSV string = "csv" - FormatYaml string = "yaml" + FormatDotenv string = "dotenv" + FormatJson string = "json" + FormatCSV string = "csv" + FormatYaml string = "yaml" + FormatDotEnvExport string = "dotenv-export" ) // exportCmd represents the export command @@ -85,6 +86,8 @@ func formatEnvs(envs []models.SingleEnvironmentVariable, format string) (string, switch strings.ToLower(format) { case FormatDotenv: return formatAsDotEnv(envs), nil + case FormatDotEnvExport: + return formatAsDotEnvExport(envs), nil case FormatJson: return formatAsJson(envs), nil case FormatCSV: @@ -117,6 +120,15 @@ func formatAsDotEnv(envs []models.SingleEnvironmentVariable) string { return dotenv } +// Format environment variables as a dotenv file with export at the beginning +func formatAsDotEnvExport(envs []models.SingleEnvironmentVariable) string { + var dotenv string + for _, env := range envs { + dotenv += fmt.Sprintf("export %s='%s'\n", env.Key, env.Value) + } + return dotenv +} + func formatAsYaml(envs []models.SingleEnvironmentVariable) string { var dotenv string for _, env := range envs { From 58aee0239fa181719175d178720faee9f8755f9b Mon Sep 17 00:00:00 2001 From: Michel Wilhelm Date: Fri, 13 Jan 2023 16:59:22 -0300 Subject: [PATCH 68/89] docs: adding documentation about dotenv-export --- docs/cli/commands/export.mdx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/cli/commands/export.mdx b/docs/cli/commands/export.mdx index b80fb74703..bcbc89e896 100644 --- a/docs/cli/commands/export.mdx +++ b/docs/cli/commands/export.mdx @@ -12,12 +12,12 @@ Export environment variables from the platform into a file format. ## Options -| Option | Description | Default value | -| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -| `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` | -| `--projectId` | Only required if injecting via the [service token method](../token). If you are not using service token, the project id will be automatically retrieved from the `.infisical.json` located at the root of your local project. | `None` | -| `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` | -| `--format` | Format of the output file. Accepted values: `dotenv`, `csv` and `json` | `dotenv` | +| Option | Description | Default value | +| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` | +| `--projectId` | Only required if injecting via the [service token method](../token). If you are not using service token, the project id will be automatically retrieved from the `.infisical.json` located at the root of your local project. | `None` | +| `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` | +| `--format` | Format of the output file. Accepted values: `dotenv`, `dotenv-export`, `csv` and `json` | `dotenv` | ## Examples @@ -25,6 +25,9 @@ Export environment variables from the platform into a file format. # Export variables to a .env file infisical export > .env +# Export variables to a .env file (with export keyword) +infisical export --format=dotenv-export > .env + # Export variables to a CSV file infisical export --format=csv > secrets.csv @@ -33,4 +36,5 @@ infisical export --format=json > secrets.json # Export variables to a YAML file infisical export --format=yaml > secrets.yaml + ``` From dc6d036d86c865fe7b82cb1ec4f1e78663bd3a97 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 12:08:47 -0800 Subject: [PATCH 69/89] write helm values to file form secret --- .github/workflows/deploy-gamma.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/deploy-gamma.yml b/.github/workflows/deploy-gamma.yml index f9167c2964..2b62581096 100644 --- a/.github/workflows/deploy-gamma.yml +++ b/.github/workflows/deploy-gamma.yml @@ -13,6 +13,10 @@ jobs: uses: azure/setup-helm@v3 with: version: v3.10.0 + - name: Install infisical helm chart + run: | + helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/' + helm repo update - name: Install kubectl uses: azure/setup-kubectl@v3 - name: Install doctl @@ -25,4 +29,10 @@ jobs: run: kubectl config set-context --current --namespace=gamma - name: test kubectl run: kubectl get ingress + - name: Write helm values to file + run: | + echo "${{ secrets.HELM_VALUES_GAMMA }}" > values.yaml + cat values.yml + + \ No newline at end of file From d1f296b7e791a5558c10e78a5d80e20a3714bed6 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 12:11:05 -0800 Subject: [PATCH 70/89] fix indent gamma deploy gha --- .github/workflows/deploy-gamma.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-gamma.yml b/.github/workflows/deploy-gamma.yml index 2b62581096..f914ca8aaa 100644 --- a/.github/workflows/deploy-gamma.yml +++ b/.github/workflows/deploy-gamma.yml @@ -15,8 +15,8 @@ jobs: version: v3.10.0 - name: Install infisical helm chart run: | - helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/' - helm repo update + helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/' + helm repo update - name: Install kubectl uses: azure/setup-kubectl@v3 - name: Install doctl @@ -31,8 +31,8 @@ jobs: run: kubectl get ingress - name: Write helm values to file run: | - echo "${{ secrets.HELM_VALUES_GAMMA }}" > values.yaml - cat values.yml + echo "${{ secrets.HELM_VALUES_GAMMA }}" > values.yaml + cat values.yml \ No newline at end of file From eb9a8e0285d897c3250eae96a98e0b3e731a3e4f Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 12:16:36 -0800 Subject: [PATCH 71/89] echo values file --- .github/workflows/deploy-gamma.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/deploy-gamma.yml b/.github/workflows/deploy-gamma.yml index f914ca8aaa..6fb01abc52 100644 --- a/.github/workflows/deploy-gamma.yml +++ b/.github/workflows/deploy-gamma.yml @@ -31,8 +31,7 @@ jobs: run: kubectl get ingress - name: Write helm values to file run: | - echo "${{ secrets.HELM_VALUES_GAMMA }}" > values.yaml - cat values.yml + echo "${{ secrets.HELM_VALUES_GAMMA }}" \ No newline at end of file From fa60784a6b88ba1b27d4ec1862c6a910358e8ab6 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 12:27:14 -0800 Subject: [PATCH 72/89] Add files via upload --- .github/values.yaml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/values.yaml diff --git a/.github/values.yaml b/.github/values.yaml new file mode 100644 index 0000000000..f1551d61a0 --- /dev/null +++ b/.github/values.yaml @@ -0,0 +1,36 @@ +frontend: + replicaCount: 1 + image: + repository: + pullPolicy: Always + tag: "latest" + kubeSecretRef: managed-secret-frontend + +backend: + replicaCount: 1 + image: + repository: + pullPolicy: Always + tag: "latest" + kubeSecretRef: managed-backend-secret + +ingress: + enabled: true + annotations: + kubernetes.io/ingress.class: "nginx" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hostName: gamma.infisical.com + frontend: + path: / + pathType: Prefix + backend: + path: /api + pathType: Prefix + tls: + - secretName: echo-tls + hosts: + - gamma.infisical.com + +backendEnvironmentVariables: + +frontendEnvironmentVariables: \ No newline at end of file From 593765cb248656e3b3b6929c4932cfd29c8e2585 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 12:31:55 -0800 Subject: [PATCH 73/89] cat values file after downloading --- .github/workflows/deploy-gamma.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-gamma.yml b/.github/workflows/deploy-gamma.yml index 6fb01abc52..d46142707e 100644 --- a/.github/workflows/deploy-gamma.yml +++ b/.github/workflows/deploy-gamma.yml @@ -31,7 +31,8 @@ jobs: run: kubectl get ingress - name: Write helm values to file run: | - echo "${{ secrets.HELM_VALUES_GAMMA }}" + wget https://raw.githubusercontent.com/Infisical/infisical/main/.github/values.yaml + cat values.yaml \ No newline at end of file From 323701d432fcaf5a21f04fcbffe2c705077138c6 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 12:38:56 -0800 Subject: [PATCH 74/89] add gha upgrade helmchart --- .github/workflows/deploy-gamma.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-gamma.yml b/.github/workflows/deploy-gamma.yml index d46142707e..b72f87a61d 100644 --- a/.github/workflows/deploy-gamma.yml +++ b/.github/workflows/deploy-gamma.yml @@ -29,10 +29,11 @@ jobs: run: kubectl config set-context --current --namespace=gamma - name: test kubectl run: kubectl get ingress - - name: Write helm values to file + - name: Download helm values to file and upgrade gamma deploy run: | wget https://raw.githubusercontent.com/Infisical/infisical/main/.github/values.yaml - cat values.yaml + helm upgrade infisical infisical-helm-charts/infisical --values values.yaml --recreate-pods + \ No newline at end of file From 17bae52830f587752c59d28e57c1ceaeba5f7ff0 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 12:52:07 -0800 Subject: [PATCH 75/89] gamma deployment after image build --- .github/workflows/docker-image.yml | 34 +++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index ab7b939e7b..34847749b1 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -99,4 +99,36 @@ jobs: infisical/frontend:latest platforms: linux/amd64,linux/arm64 build-args: | - POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} \ No newline at end of file + POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} + gamma-deployment: + name: Deploy to gamma + runs-on: ubuntu-latest + needs: frontend-image backend-image + if: github.event_name == 'repository_dispatch' + steps: + - name: ☁️ Checkout source + uses: actions/checkout@v3 + - name: Install Helm + uses: azure/setup-helm@v3 + with: + version: v3.10.0 + - name: Install infisical helm chart + run: | + helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/' + helm repo update + - name: Install kubectl + uses: azure/setup-kubectl@v3 + - name: Install doctl + uses: digitalocean/action-doctl@v2 + with: + token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} + - name: Save DigitalOcean kubeconfig with short-lived credentials + run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 k8s-1-25-4-do-0-nyc1-1670645170179 + - name: switch to gamma namespace + run: kubectl config set-context --current --namespace=gamma + - name: test kubectl + run: kubectl get ingress + - name: Download helm values to file and upgrade gamma deploy + run: | + wget https://raw.githubusercontent.com/Infisical/infisical/main/.github/values.yaml + helm upgrade infisical infisical-helm-charts/infisical --values values.yaml --recreate-pods \ No newline at end of file From 189d24589e90ef5271b269b4765dd39f50b5d748 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 12:53:53 -0800 Subject: [PATCH 76/89] correct needs fields in gha --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 34847749b1..30e938a54e 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -103,7 +103,7 @@ jobs: gamma-deployment: name: Deploy to gamma runs-on: ubuntu-latest - needs: frontend-image backend-image + needs: [frontend-image, backend-image] if: github.event_name == 'repository_dispatch' steps: - name: ☁️ Checkout source From a00e6df59ff3db951a2f69835d158bd26ce679dc Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 13:09:57 -0800 Subject: [PATCH 77/89] add manual approval setp --- .github/workflows/docker-image.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 30e938a54e..9d8a6b0a89 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -100,10 +100,19 @@ jobs: platforms: linux/amd64,linux/arm64 build-args: | POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} + manualApprove: + needs: [frontend-image, backend-image] + environment: + name: gamma-deployment + runs-on: ubuntu-latest + steps: + - name: manual approve + run: | + echo "Manually approved" gamma-deployment: name: Deploy to gamma runs-on: ubuntu-latest - needs: [frontend-image, backend-image] + needs: [manualApprove] if: github.event_name == 'repository_dispatch' steps: - name: ☁️ Checkout source From 092e4a55bdb0cdf8f94fd757cd4d0c4c0eff84ee Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 13:13:15 -0800 Subject: [PATCH 78/89] enable auto deploy to gamma --- .github/workflows/docker-image.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 9d8a6b0a89..142245dc8f 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -100,20 +100,10 @@ jobs: platforms: linux/amd64,linux/arm64 build-args: | POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }} - manualApprove: - needs: [frontend-image, backend-image] - environment: - name: gamma-deployment - runs-on: ubuntu-latest - steps: - - name: manual approve - run: | - echo "Manually approved" gamma-deployment: name: Deploy to gamma runs-on: ubuntu-latest - needs: [manualApprove] - if: github.event_name == 'repository_dispatch' + needs: [frontend-image, backend-image] steps: - name: ☁️ Checkout source uses: actions/checkout@v3 From 64d1f252e29f1f415e90f5f73f0b62a811e32e33 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 13:39:30 -0800 Subject: [PATCH 79/89] Rename workflow file --- .github/workflows/docker-image.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 142245dc8f..ffc6e5c409 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,5 +1,4 @@ -name: Push frontend and backend to Dockerhub - +name: Build, Publish and Deploy to Gamma on: [workflow_dispatch] jobs: From 87a3f9a03c03bdeb6a8ed94500c75f97f97789ea Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 13:41:56 -0800 Subject: [PATCH 80/89] delete gamma deploy workflow file --- .github/workflows/deploy-gamma.yml | 39 ------------------------------ 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/deploy-gamma.yml diff --git a/.github/workflows/deploy-gamma.yml b/.github/workflows/deploy-gamma.yml deleted file mode 100644 index b72f87a61d..0000000000 --- a/.github/workflows/deploy-gamma.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Deploy to Gamma stage - -on: [workflow_dispatch] - -jobs: - deployment: - name: Deploy to gamma - runs-on: ubuntu-latest - steps: - - name: ☁️ Checkout source - uses: actions/checkout@v3 - - name: Install Helm - uses: azure/setup-helm@v3 - with: - version: v3.10.0 - - name: Install infisical helm chart - run: | - helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/' - helm repo update - - name: Install kubectl - uses: azure/setup-kubectl@v3 - - name: Install doctl - uses: digitalocean/action-doctl@v2 - with: - token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} - - name: Save DigitalOcean kubeconfig with short-lived credentials - run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 k8s-1-25-4-do-0-nyc1-1670645170179 - - name: switch to gamma namespace - run: kubectl config set-context --current --namespace=gamma - - name: test kubectl - run: kubectl get ingress - - name: Download helm values to file and upgrade gamma deploy - run: | - wget https://raw.githubusercontent.com/Infisical/infisical/main/.github/values.yaml - helm upgrade infisical infisical-helm-charts/infisical --values values.yaml --recreate-pods - - - - \ No newline at end of file From 986434d66ae7ddbef7f60d2fd9cb15f15b877792 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 14:01:23 -0800 Subject: [PATCH 81/89] Add infisical in Makefile for docker compose --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 266eaf9b59..9e1f5c3f61 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,9 @@ push: up-dev: docker-compose -f docker-compose.dev.yml up --build +i-dev: + infisical export && infisical export > .env && docker-compose -f docker-compose.dev.yml up --build + up-prod: docker-compose -f docker-compose.yml up --build From d386f2702d276db7d47226716daaa16e809e0bff Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Fri, 13 Jan 2023 16:05:50 -0800 Subject: [PATCH 82/89] Minor style changes to integrations --- frontend/components/integrations/CloudIntegration.tsx | 2 +- frontend/components/integrations/Integration.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/components/integrations/CloudIntegration.tsx b/frontend/components/integrations/CloudIntegration.tsx index 75a8019a5e..dd1b13f47e 100644 --- a/frontend/components/integrations/CloudIntegration.tsx +++ b/frontend/components/integrations/CloudIntegration.tsx @@ -74,7 +74,7 @@ const CloudIntegration = ({ integrationAuths .map((authorization) => authorization.integration) .includes(cloudIntegrationOption.name.toLowerCase()) && ( -
+
{ event.stopPropagation(); diff --git a/frontend/components/integrations/Integration.tsx b/frontend/components/integrations/Integration.tsx index 4dba86841d..bbc8861dc6 100644 --- a/frontend/components/integrations/Integration.tsx +++ b/frontend/components/integrations/Integration.tsx @@ -101,7 +101,7 @@ const Integration = ({ case "vercel": return (
-
+
ENVIRONMENT
); From 71fbf519ce86af02b5321c0d646e88ff96a12639 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Fri, 13 Jan 2023 16:17:40 -0800 Subject: [PATCH 83/89] Minor style changes - capitalization --- frontend/components/basic/Listbox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/components/basic/Listbox.tsx b/frontend/components/basic/Listbox.tsx index c67735d6e5..a65135dfad 100644 --- a/frontend/components/basic/Listbox.tsx +++ b/frontend/components/basic/Listbox.tsx @@ -46,9 +46,9 @@ export default function ListBox({ >
{text} - + {' '} - {selected?.charAt(0).toUpperCase() + selected?.slice(1)} + {selected}
{data && ( From 3e6206951eb17582b593fe9ef4d32981bdb27965 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Fri, 13 Jan 2023 17:19:22 -0800 Subject: [PATCH 84/89] updated Readme with new contributors --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2876c6ee80..0e680ff29f 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,8 @@ Infisical officially launched as v.1.0 on November 21st, 2022. There are a lot o -
+ + ## 🌎 Translations From 5d8c4ad03f3a3a699c4c88e836ab328f09e44adb Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Fri, 13 Jan 2023 17:32:36 -0800 Subject: [PATCH 85/89] Changed the formulation to secrets and configs --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0e680ff29f..67b6d519cb 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ infisical

-

Open-source, E2EE, simple tool to manage and sync environment variables across your team and infrastructure.

+

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

@@ -34,13 +34,13 @@ Dashboard -**[Infisical](https://infisical.com)** is an open source, E2EE tool to help teams manage and sync environment variables 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, 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. -- **[User-Friendly Dashboard](https://infisical.com/docs/getting-started/dashboard/project)** to manage your team's environment variables within projects -- **[Language-Agnostic CLI](https://infisical.com/docs/cli/overview)** that pulls and injects environment variables into your local workflow +- **[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 environment variables +- **Personal overrides** for secrets and configs - **[Integrations](https://infisical.com/docs/integrations/overview)** with CI/CD and production infrastructure - **[Secret Versioning](https://infisical.com/docs/getting-started/dashboard/versioning)** - check the history of change for any secret - **[Activity Logs](https://infisical.com/docs/getting-started/dashboard/audit-logs)** - check what user in the project is performing what actions with secrets From 98d84b6717e64413250ecc6d2abcc01bb1690cc0 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 19:48:40 -0800 Subject: [PATCH 86/89] add FormatDotEnvExport to list of available formats --- cli/packages/cmd/export.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/packages/cmd/export.go b/cli/packages/cmd/export.go index 33021f1c1c..689f507d38 100644 --- a/cli/packages/cmd/export.go +++ b/cli/packages/cmd/export.go @@ -95,7 +95,7 @@ func formatEnvs(envs []models.SingleEnvironmentVariable, format string) (string, case FormatYaml: return formatAsYaml(envs), nil default: - return "", fmt.Errorf("invalid format type: %s. Available format types are [%s]", format, []string{FormatDotenv, FormatJson, FormatCSV, FormatYaml}) + return "", fmt.Errorf("invalid format type: %s. Available format types are [%s]", format, []string{FormatDotenv, FormatJson, FormatCSV, FormatYaml, FormatDotEnvExport}) } } From 3cd9241aee5615f690b7aea09a2a05835a31fcd2 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 19:52:29 -0800 Subject: [PATCH 87/89] increase cli version --- cli/packages/cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/packages/cmd/root.go b/cli/packages/cmd/root.go index e68c6826c6..708982a22b 100644 --- a/cli/packages/cmd/root.go +++ b/cli/packages/cmd/root.go @@ -15,7 +15,7 @@ var rootCmd = &cobra.Command{ Short: "Infisical CLI is used to inject environment variables into any process", Long: `Infisical is a simple, end-to-end encrypted service that enables teams to sync and manage their environment variables across their development life cycle.`, CompletionOptions: cobra.CompletionOptions{HiddenDefaultCmd: true}, - Version: "0.2.1", + Version: "0.2.2", } // Execute adds all child commands to the root command and sets flags appropriately. From e552be0a8184e7e33ecfc9cf6608b732ae51f5b9 Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Fri, 13 Jan 2023 20:42:23 -0800 Subject: [PATCH 88/89] add deployment error check for gamma --- .github/workflows/docker-image.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index ffc6e5c409..b4f9546ae0 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -129,4 +129,10 @@ jobs: - name: Download helm values to file and upgrade gamma deploy run: | wget https://raw.githubusercontent.com/Infisical/infisical/main/.github/values.yaml - helm upgrade infisical infisical-helm-charts/infisical --values values.yaml --recreate-pods \ No newline at end of file + helm upgrade infisical infisical-helm-charts/infisical --values values.yaml --recreate-pods + if [[ $(helm status infisical) == *"FAILED"* ]]; then + echo "Helm upgrade failed" + exit 1 + else + echo "Helm upgrade was successful" + fi \ No newline at end of file From 08dd5174b30c6bfd22f703a962c9abcfd3e87fa8 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Fri, 13 Jan 2023 22:59:43 -0800 Subject: [PATCH 89/89] Making the dashboard less clunky --- README.md | 2 +- frontend/components/RouteGuard.js | 15 +-------------- frontend/pages/dashboard/[id].tsx | 2 +- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 67b6d519cb..756f65f927 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,7 @@ Infisical officially launched as v.1.0 on November 21st, 2022. There are a lot o - + ## 🌎 Translations diff --git a/frontend/components/RouteGuard.js b/frontend/components/RouteGuard.js index c5f6feb35e..f3693abec4 100644 --- a/frontend/components/RouteGuard.js +++ b/frontend/components/RouteGuard.js @@ -68,18 +68,5 @@ export default function RouteGuard({ children }) { } } - if (authorized) { - return children; - } else { - return ( -
- google logo -
- ); - } + return children; } diff --git a/frontend/pages/dashboard/[id].tsx b/frontend/pages/dashboard/[id].tsx index 9438cf1153..1a9cd41da1 100644 --- a/frontend/pages/dashboard/[id].tsx +++ b/frontend/pages/dashboard/[id].tsx @@ -220,7 +220,7 @@ export default function Dashboard() { setData(undefined); } })(); - }, []); + }, [workspaceId]); useEffect(() => { (async () => {