Compare commits

..

28 Commits

Author SHA1 Message Date
6e882aa46e Added kMS permissions to docs for parameter store 2024-04-28 20:53:03 -07:00
bf4db0a9ff made paths scrollable 2024-04-28 19:44:39 -07:00
3a3e3a7afc updated integrations page 2024-04-28 19:36:14 -07:00
0c324e804c added kms key delector for parameter store 2024-04-28 15:12:50 -07:00
47aca3f3e2 Update overview.mdx 2024-04-27 19:05:24 -07:00
31ef1a2183 Delete backend/src/db/migrations/20240426171026_test.ts 2024-04-26 20:33:13 -04:00
66a6f9de71 Merge pull request #1753 from Infisical/maidul98-patch-5
Create 20240426171026_test.ts
2024-04-26 17:52:11 -04:00
6333eccc4a Create 20240426171026_test.ts 2024-04-26 17:52:02 -04:00
0af2b113df Delete backend/src/db/migrations/20240426171026_test.ts 2024-04-26 17:51:52 -04:00
63a7941047 Update update-be-new-migration-latest-timestamp.yml 2024-04-26 17:51:20 -04:00
edeac08cb5 Merge pull request #1752 from Infisical/maidul98-patch-4
Update 20240426171026_test.ts
2024-04-26 14:54:26 -04:00
019b0ae09a Update 20240426171026_test.ts 2024-04-26 14:54:15 -04:00
1d00bb0a64 Update update-be-new-migration-latest-timestamp.yml 2024-04-26 14:52:47 -04:00
d96f1320ed Merge pull request #1751 from Infisical/revert-1750-revert-1749-revert-1748-revert-1747-revert-1746-revert-1745-revert-1744-revert-1743-revert-1742-revert-1741-revert-1740-revert-1739-test-db-rename
Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename""""""""""""
2024-04-26 14:44:10 -04:00
50dbefeb48 Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename"""""""""""" 2024-04-26 14:43:57 -04:00
56ac2c6780 Merge pull request #1750 from Infisical/revert-1749-revert-1748-revert-1747-revert-1746-revert-1745-revert-1744-revert-1743-revert-1742-revert-1741-revert-1740-revert-1739-test-db-rename
Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename"""""""""""
2024-04-26 14:43:54 -04:00
c2f16da411 Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename""""""""""" 2024-04-26 14:43:46 -04:00
8223aee2ef Update update-be-new-migration-latest-timestamp.yml 2024-04-26 14:43:38 -04:00
5bd2af9621 Merge pull request #1749 from Infisical/revert-1748-revert-1747-revert-1746-revert-1745-revert-1744-revert-1743-revert-1742-revert-1741-revert-1740-revert-1739-test-db-rename
Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename""""""""""
2024-04-26 14:28:44 -04:00
b3df6ce6b5 Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename"""""""""" 2024-04-26 14:28:34 -04:00
e12eb5347d Merge pull request #1748 from Infisical/revert-1747-revert-1746-revert-1745-revert-1744-revert-1743-revert-1742-revert-1741-revert-1740-revert-1739-test-db-rename
Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename"""""""""
2024-04-26 14:28:31 -04:00
83a4426d31 Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename""""""""" 2024-04-26 14:28:22 -04:00
3fd1fbc355 Update update-be-new-migration-latest-timestamp.yml 2024-04-26 14:28:13 -04:00
306d2b4bd9 Merge pull request #1747 from Infisical/revert-1746-revert-1745-revert-1744-revert-1743-revert-1742-revert-1741-revert-1740-revert-1739-test-db-rename
Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename""""""""
2024-04-26 14:17:42 -04:00
c2c66af1f9 Revert "Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename"""""""" 2024-04-26 14:17:30 -04:00
7ae65478aa Merge pull request #1746 from Infisical/revert-1745-revert-1744-revert-1743-revert-1742-revert-1741-revert-1740-revert-1739-test-db-rename
Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename"""""""
2024-04-26 14:17:26 -04:00
b1594e65c6 Revert "Revert "Revert "Revert "Revert "Revert "Revert "test migration rename""""""" 2024-04-26 14:17:17 -04:00
0bce5b1daa Update update-be-new-migration-latest-timestamp.yml 2024-04-26 14:16:29 -04:00
24 changed files with 255 additions and 78 deletions

View File

@ -2,8 +2,7 @@ name: Rename Migrations
on:
pull_request:
types:
- closed
types: [closed]
paths:
- 'backend/src/db/migrations/**'
@ -11,27 +10,39 @@ jobs:
rename:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get list of newly added files in migration folder
run: git diff --name-status HEAD^ HEAD backend/src/db/migrations | grep '^A' | cut -f2 | xargs -n1 basename > added_files.txt
- name: Script to rename migrations
run: |
git diff --name-status HEAD^ HEAD backend/src/db/migrations | grep '^A' | cut -f2 | xargs -n1 basename > added_files.txt
if [ ! -s added_files.txt ]; then
echo "No new files added. Skipping"
echo "SKIP_RENAME=true" >> $GITHUB_ENV
fi
- name: Script to rename migrations
if: env.SKIP_RENAME != 'true'
run: python .github/resources/rename_migration_files.py
- name: Commit and push changes
if: env.SKIP_RENAME != 'true'
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add ./backend/src/db/migrations
rm added_files.txt
git commit -m "chore: renamed new migration files to latest timestamp (gh-action)"
- name: Push changes
env:
TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }}
run: |
git push https://$GITHUB_ACTOR:$TOKEN@github.com/${{ github.repository }}.git HEAD:origin/main
- name: Create Pull Request
if: env.SKIP_RENAME != 'true'
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
title: 'GH Action: rename new migration file timestamp'
branch-suffix: timestamp

View File

@ -1,10 +0,0 @@
import { Knex } from "knex";
export async function up(knex: Knex): Promise<void> {
}
export async function down(knex: Knex): Promise<void> {
}

View File

@ -572,14 +572,14 @@ export const integrationAuthServiceFactory = ({
const response = keys
.Keys!.map((key) => {
const keyAlias = aliases.Aliases!.find((alias) => key.KeyId === alias.TargetKeyId);
if (!keyAlias?.AliasName?.includes("alias/aws/") || keyAlias?.AliasName?.includes("alias/aws/secretsmanager")) {
if (!keyAlias?.AliasName?.includes("alias/aws/")) {
return { id: String(key.KeyId), alias: String(keyAlias?.AliasName || key.KeyId) };
}
return { id: "null", alias: "null" };
})
.filter((elem) => elem.id !== "null");
return response;
return [...response, { id: "null", alias: "default" }];
};
const getQoveryProjects = async ({

View File

@ -477,24 +477,29 @@ const syncSecretsAWSParameterStore = async ({
}),
{} as Record<string, AWS.SSM.Parameter>
);
// Identify secrets to create
await Promise.all(
Object.keys(secrets).map(async (key) => {
if (!(key in awsParameterStoreSecretsObj)) {
// case: secret does not exist in AWS parameter store
// -> create secret
await ssm
.putParameter({
Name: `${integration.path}${key}`,
Type: "SecureString",
Value: secrets[key].value,
// Overwrite: true,
Tags: metadata.secretAWSTag
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({ Key: tag.key, Value: tag.value }))
: []
})
.promise();
if (secrets[key].value) {
await ssm
.putParameter({
Name: `${integration.path}${key}`,
Type: "SecureString",
Value: secrets[key].value,
KeyId: metadata.kmsKeyId ? metadata.kmsKeyId : undefined,
// Overwrite: true,
Tags: metadata.secretAWSTag
? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({
Key: tag.key,
Value: tag.value
}))
: []
})
.promise();
}
// case: secret exists in AWS parameter store
} else if (awsParameterStoreSecretsObj[key].Value !== secrets[key].value) {
// case: secret value doesn't match one in AWS parameter store

View File

@ -1,13 +1,14 @@
---
title: "Overview"
title: "Dynamic Secrets"
sidebarTitle: "Overview"
description: "Learn how to generate secrets dynamically on-demand."
---
## Introduction
Contrary to static key-value secrets, which require manual input of data into the secure Infisical storage, dynamic secrets are generated on-demand upon access.
Contrary to static key-value secrets, which require manual input of data into the secure Infisical storage, **dynamic secrets are generated on-demand upon access**.
Dynamic secrets are unique to every identity using them. Such secrets come are generated only at the moment they are retrieved, eliminating the possibility of theft or reuse by another identity. Thanks to Infisical's integrated revocation capabilities, dynamic secrets can be promptly invalidated post-use, significantly reducing their lifespan.
**Dynamic secrets are unique to every identity using them**. Such secrets come are generated only at the moment they are retrieved, eliminating the possibility of theft or reuse by another identity. Thanks to Infisical's integrated revocation capabilities, dynamic secrets can be promptly invalidated post-use, significantly reducing their lifespan.
## Benefits of Dynamic Secrets
@ -28,3 +29,6 @@ Dynamic secrets are particularly useful in environments with stringent security
## Infisical Dynamic Secret Templates
1. [PostgreSQL](./postgresql)
2. [MySQL](./mysql)
3. [Cassandra](./cassandra)
4. [Oracle](./oracle)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View File

@ -30,13 +30,18 @@ Prerequisites:
"ssm:DeleteParameter",
"ssm:GetParametersByPath",
"ssm:DeleteParameters",
"ssm:AddTagsToResource" // if you need to add tags to secrets
"ssm:AddTagsToResource", // if you need to add tags to secrets
"kms:ListKeys", // if you need to specify the KMS key
"kms:ListAliases", // if you need to specify the KMS key
"kms:Encrypt", // if you need to specify the KMS key
"kms:Decrypt" // if you need to specify the KMS key
],
"Resource": "*"
}
]
}
```
</Step>
<Step title="Authorize Infisical for AWS Parameter store">
Obtain a AWS access key ID and secret access key for your IAM user in IAM > Users > User > Security credentials > Access keys
@ -44,7 +49,7 @@ Prerequisites:
![access key 1](../../images/integrations/aws/integrations-aws-access-key-1.png)
![access key 2](../../images/integrations/aws/integrations-aws-access-key-2.png)
![access key 3](../../images/integrations/aws/integrations-aws-access-key-3.png)
Navigate to your project's integrations tab in Infisical.
![integrations](../../images/integrations.png)
@ -59,6 +64,7 @@ Prerequisites:
breaks E2EE, it's necessary for Infisical to sync the environment variables to
the cloud platform.
</Info>
</Step>
<Step title="Start integration">
Select which Infisical environment secrets you want to sync to which AWS Parameter Store region and indicate the path for your secrets. Then, press create integration to start syncing secrets to AWS Parameter Store.
@ -72,6 +78,6 @@ Prerequisites:
secret like `TEST` to be stored as `/[project_name]/[environment]/TEST` in AWS
Parameter Store.
</Tip>
</Step>
</Steps>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,28 +1,4 @@
[
{
"name": "Docker",
"slug": "docker",
"image": "Docker",
"docsLink": "https://infisical.com/docs/integrations/platforms/docker"
},
{
"name": "Docker Compose",
"slug": "docker-compose",
"image": "Docker Compose",
"docsLink": "https://infisical.com/docs/integrations/platforms/docker-compose"
},
{
"name": "Kubernetes",
"slug": "kubernetes",
"image": "Kubernetes",
"docsLink": "https://infisical.com/docs/integrations/platforms/kubernetes"
},
{
"name": "Terraform",
"slug": "terraform",
"image": "Terraform",
"docsLink": "https://infisical.com/docs/integrations/frameworks/terraform"
},
{
"name": "React",
"slug": "react",

View File

@ -0,0 +1,50 @@
[
{
"name": "Docker",
"slug": "docker",
"image": "Docker",
"docsLink": "https://infisical.com/docs/integrations/platforms/docker"
},
{
"name": "Docker Compose",
"slug": "docker-compose",
"image": "Docker Compose",
"docsLink": "https://infisical.com/docs/integrations/platforms/docker-compose"
},
{
"name": "Kubernetes",
"slug": "kubernetes",
"image": "Kubernetes",
"docsLink": "https://infisical.com/docs/integrations/platforms/kubernetes"
},
{
"name": "Terraform",
"slug": "terraform",
"image": "Terraform",
"docsLink": "https://infisical.com/docs/integrations/frameworks/terraform"
},
{
"name": "Jenkins",
"slug": "jenkins",
"image": "Jenkins",
"docsLink": "https://infisical.com/docs/integrations/cicd/jenkins"
},
{
"name": "Infisical Agent",
"slug": "agent",
"image": "Agent",
"docsLink": "https://infisical.com/docs/integrations/platforms/infisical-agent"
},
{
"name": "Amazon ECS",
"slug": "ecs",
"image": "ECS",
"docsLink": "https://infisical.com/docs/integrations/platforms/ecs-with-agent"
},
{
"name": "Ansible",
"slug": "ansible",
"image": "Ansible",
"docsLink": "https://infisical.com/docs/integrations/platforms/ansible"
}
]

View File

@ -120,7 +120,7 @@
"available": "Platform & Cloud Integrations",
"available-text1": "Click on the integration 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",
"cloud-integrations": "Native 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.",

View File

@ -1,14 +1,16 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import frameworkIntegrationOptions from "public/json/frameworkIntegrations.json";
import infrastructureIntegrationOptions from "public/json/infrastructureIntegrations.json";
import { IntegrationsPage } from "@app/views/IntegrationsPage";
type Props = {
frameworkIntegrations: typeof frameworkIntegrationOptions;
infrastructureIntegrations: typeof infrastructureIntegrationOptions;
};
const Integration = ({ frameworkIntegrations }: Props) => {
const Integration = ({ frameworkIntegrations, infrastructureIntegrations }: Props) => {
const { t } = useTranslation();
return (
@ -20,7 +22,7 @@ const Integration = ({ frameworkIntegrations }: Props) => {
<meta property="og:title" content="Manage your .env files in seconds" />
<meta name="og:description" content={t("integrations.description") as string} />
</Head>
<IntegrationsPage frameworkIntegrations={frameworkIntegrations} />
<IntegrationsPage frameworkIntegrations={frameworkIntegrations} infrastructureIntegrations={infrastructureIntegrations} />
</>
);
};
@ -28,7 +30,8 @@ const Integration = ({ frameworkIntegrations }: Props) => {
export const getStaticProps = () => {
return {
props: {
frameworkIntegrations: frameworkIntegrationOptions
frameworkIntegrations: frameworkIntegrationOptions,
infrastructureIntegrations: infrastructureIntegrationOptions
}
};
};

View File

@ -14,6 +14,7 @@ import { motion } from "framer-motion";
import queryString from "query-string";
import { useCreateIntegration } from "@app/hooks/api";
import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries";
import {
Button,
@ -90,6 +91,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
const [shouldTag, setShouldTag] = useState(false);
const [tagKey, setTagKey] = useState("");
const [tagValue, setTagValue] = useState("");
const [kmsKeyId, setKmsKeyId] = useState("");
useEffect(() => {
if (workspace) {
@ -98,6 +100,19 @@ export default function AWSParameterStoreCreateIntegrationPage() {
}
}, [workspace]);
const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } =
useGetIntegrationAuthAwsKmsKeys({
integrationAuthId: String(integrationAuthId),
region: selectedAWSRegion
});
useEffect(() => {
if (integrationAuthAwsKmsKeys) {
setKmsKeyId(String(integrationAuthAwsKmsKeys?.filter(key => key.alias === "default")[0]?.id))
}
}, [integrationAuthAwsKmsKeys])
const isValidAWSParameterStorePath = (awsStorePath: string) => {
const pattern = /^\/([\w-]+\/)*[\w-]+\/$/;
return pattern.test(awsStorePath) && awsStorePath.length <= 2048;
@ -133,7 +148,11 @@ export default function AWSParameterStoreCreateIntegrationPage() {
value: tagValue
}]
}
: {})
: {}),
...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "default") ?
{
kmsKeyId
}: {})
}
});
@ -146,7 +165,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
}
};
return integrationAuth && workspace && selectedSourceEnvironment ? (
return (integrationAuth && workspace && selectedSourceEnvironment && !isIntegrationAuthAwsKmsKeysLoading) ? (
<div className="flex h-full w-full flex-col items-center justify-center">
<Head>
<title>Set Up AWS Parameter Integration</title>
@ -286,6 +305,31 @@ export default function AWSParameterStoreCreateIntegrationPage() {
</FormControl>
</div>
)}
<FormControl label="Encryption Key" className="mt-4">
<Select
value={kmsKeyId}
onValueChange={(e) => {
setKmsKeyId(e)
}}
className="w-full border border-mineshaft-500"
>
{integrationAuthAwsKmsKeys?.length ? (
integrationAuthAwsKmsKeys.map((key) => {
return (
<SelectItem
value={key.id as string}
key={`repo-id-${key.id}`}
className="w-[28.4rem] text-sm"
>
{key.alias}
</SelectItem>
);
})
) : (
<div />
)}
</Select>
</FormControl>
</motion.div>
</TabPanel>
</Tabs>
@ -318,7 +362,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
<title>Set Up AWS Parameter Store Integration</title>
<link rel="icon" href="/infisical.ico" />
</Head>
{isintegrationAuthLoading ? (
{(isintegrationAuthLoading || isIntegrationAuthAwsKmsKeysLoading) ? (
<img
src="/images/loading/loading.gif"
height={70}

View File

@ -148,7 +148,7 @@ export default function AWSSecretManagerCreateIntegrationPage() {
}]
}
: {}),
...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "alias/aws/secretsmanager") ?
...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "default") ?
{
kmsKeyId
}: {})

View File

@ -21,15 +21,17 @@ import { ProjectVersion } from "@app/hooks/api/workspace/types";
import { CloudIntegrationSection } from "./components/CloudIntegrationSection";
import { FrameworkIntegrationSection } from "./components/FrameworkIntegrationSection";
import { InfrastructureIntegrationSection } from "./components/InfrastructureIntegrationSection/InfrastructureIntegrationSection";
import { IntegrationsSection } from "./components/IntegrationsSection";
import { generateBotKey, redirectForProviderAuth } from "./IntegrationPage.utils";
type Props = {
frameworkIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>;
infrastructureIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>;
};
export const IntegrationsPage = withProjectPermission(
({ frameworkIntegrations }: Props) => {
({ frameworkIntegrations, infrastructureIntegrations }: Props) => {
const { t } = useTranslation();
@ -228,6 +230,7 @@ export const IntegrationsPage = withProjectPermission(
</ModalContent>
</Modal>
<FrameworkIntegrationSection frameworks={frameworkIntegrations} />
<InfrastructureIntegrationSection integrations={infrastructureIntegrations} />
</div>
);
},

View File

@ -109,7 +109,7 @@ export const CloudIntegrationSection = ({
</div>
{cloudIntegration.isAvailable &&
Boolean(integrationAuths?.[cloudIntegration.slug]) && (
<div className="absolute top-0 right-0 z-40 h-full">
<div className="absolute top-0 right-0 z-30 h-full">
<div className="relative h-full">
<div className="absolute top-0 right-0 w-24 flex-row items-center overflow-hidden whitespace-nowrap rounded-tr-md rounded-bl-md bg-primary py-0.5 px-2 text-xs text-black opacity-80 transition-all duration-300 group-hover:w-0 group-hover:p-0">
<FontAwesomeIcon icon={faCheck} className="mr-2 text-xs" />

View File

@ -1,4 +1,7 @@
import { useTranslation } from "react-i18next";
import { faKeyboard } from "@fortawesome/free-regular-svg-icons";
import { faComputer } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
type Props = {
frameworks: Array<{
@ -50,6 +53,36 @@ export const FrameworkIntegrationSection = ({ frameworks }: Props) => {
</div>
</a>
))}
<a
key="framework-integration-more"
href="https://infisical.com/docs/cli/commands/run"
rel="noopener noreferrer"
target="_blank"
className="relative flex h-32 cursor-pointer flex-row items-center justify-center rounded-md p-0.5 duration-200"
>
<div
className="flex h-full w-full cursor-pointer flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 font-semibold text-gray-300 duration-200 hover:bg-mineshaft-700 group-hover:text-gray-200 px-1 text-xl w-full max-w-xs text-center"
>
<FontAwesomeIcon className="text-5xl mb-2 text-white/90" icon={faKeyboard} />
<div className="h-2" />
CLI
</div>
</a>
<a
key="framework-integration-more"
href="https://infisical.com/docs/sdks/overview"
rel="noopener noreferrer"
target="_blank"
className="relative flex h-32 cursor-pointer flex-row items-center justify-center rounded-md p-0.5 duration-200"
>
<div
className="flex h-full w-full cursor-pointer flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 font-semibold text-gray-300 duration-200 hover:bg-mineshaft-700 group-hover:text-gray-200 px-1 text-xl w-full max-w-xs text-center"
>
<FontAwesomeIcon className="text-5xl mb-1 text-white/90" icon={faComputer} />
<div className="h-2" />
SDKs
</div>
</a>
</div>
</>
);

View File

@ -0,0 +1,50 @@
type Props = {
integrations: Array<{
name: string;
image: string;
slug: string;
docsLink: string;
}>;
};
export const InfrastructureIntegrationSection = ({ integrations }: Props) => {
const sortedIntegrations = integrations.sort((a, b) => a.name.localeCompare(b.name));
return (
<>
<div className="mx-4 mt-12 mb-4 flex flex-col items-start justify-between px-2 text-xl">
<h1 className="text-3xl font-semibold">Infrastructure Integrations</h1>
<p className="text-base text-gray-400">Click on of the integration to read the documentation.</p>
</div>
<div className="mx-6 grid grid-cols-2 gap-4 lg:grid-cols-3 2xl:grid-cols-4">
{sortedIntegrations.map((integration) => (
<a
key={`framework-integration-${integration.slug}`}
href={integration.docsLink}
rel="noopener noreferrer"
target="_blank"
className="relative w-full flex h-32 cursor-pointer flex-row items-center justify-center rounded-md p-0.5 duration-200"
>
<div
onKeyDown={() => null}
role="button"
tabIndex={0}
className="group relative w-full cursor-pointer duration-200 hover:bg-mineshaft-700 flex h-32 flex-row items-center rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
key={integration?.name}
>
<img
src={`/images/integrations/${integration.image}.png`}
height={integration?.name ? 60 : 90}
width={integration?.name ? 60 : 90}
alt="integration logo"
/>
<div className="ml-4 max-w-xs text-xl font-semibold text-gray-300 duration-200 group-hover:text-gray-200">
{integration?.name && integration.name}
</div>
</div>
</a>
))}
</div>
</>
);
};

View File

@ -0,0 +1 @@
export { InfrastructureIntegrationSection } from "./InfrastructureIntegrationSection";

View File

@ -137,7 +137,7 @@ export const IntegrationsSection = ({
"App"
}
/>
<div className="min-w-[8rem] max-w-[12rem] overflow-clip text-ellipsis whitespace-nowrap rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
<div className="min-w-[8rem] max-w-[12rem] overflow-scroll no-scrollbar no-scrollbar::-webkit-scrollbar whitespace-nowrap rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
{(integration.integration === "hashicorp-vault" &&
`${integration.app} - path: ${integration.path}`) ||
(integration.scope === "github-org" && `${integration.owner}`) ||
@ -217,11 +217,12 @@ export const IntegrationsSection = ({
isOpen={popUp.deleteConfirmation.isOpen}
title={`Are you sure want to remove ${
(popUp?.deleteConfirmation.data as TIntegration)?.integration || " "
} integration for ${(popUp?.deleteConfirmation.data as TIntegration)?.app || " "}?`}
} integration for ${(popUp?.deleteConfirmation.data as TIntegration)?.app || "this project"}?`}
onChange={(isOpen) => handlePopUpToggle("deleteConfirmation", isOpen)}
deleteKey={
(popUp?.deleteConfirmation?.data as TIntegration)?.app ||
(popUp?.deleteConfirmation?.data as TIntegration)?.owner ||
(popUp?.deleteConfirmation?.data as TIntegration)?.path ||
""
}
onDeleteApproved={async () =>