Merge pull request #667 from Stijn-Kuijper/cloudflare-pages-integration

Cloudflare Pages integration
This commit is contained in:
BlackMagiq
2023-06-21 13:09:38 +07:00
committed by GitHub
22 changed files with 13006 additions and 80 deletions

12538
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,8 @@ import {
INTEGRATION_FLYIO_API_URL,
INTEGRATION_GITHUB,
INTEGRATION_GITLAB,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_CLOUDFLARE_PAGES_API_URL,
INTEGRATION_GITLAB_API_URL,
INTEGRATION_HEROKU,
INTEGRATION_HEROKU_API_URL,
@@ -129,6 +131,12 @@ const getApps = async ({
accessToken,
});
break;
case INTEGRATION_CLOUDFLARE_PAGES:
apps = await getAppsCloudflarePages({
accessToken,
accountId: accessId
})
break;
}
return apps;
@@ -638,4 +646,37 @@ const getAppsCheckly = async ({ accessToken }: { accessToken: string }) => {
return apps;
};
/**
* Return list of projects for the Cloudflare Pages integration
* @param {Object} obj
* @param {String} obj.accessToken - api key for the Cloudflare API
* @returns {Object[]} apps - Cloudflare Pages projects
* @returns {String} apps.name - name of Cloudflare Pages project
*/
const getAppsCloudflarePages = async ({
accessToken,
accountId
}: {
accessToken: string;
accountId?: string;
}) => {
const { data } = await standardRequest.get(
`${INTEGRATION_CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accountId}/pages/projects`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept": "application/json",
},
}
);
const apps = data.result.map((a: any) => {
return {
name: a.name,
appId: a.id,
};
});
return apps;
}
export { getApps };

View File

@@ -34,6 +34,8 @@ import {
INTEGRATION_RENDER_API_URL,
INTEGRATION_SUPABASE,
INTEGRATION_SUPABASE_API_URL,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_CLOUDFLARE_PAGES_API_URL,
INTEGRATION_TRAVISCI,
INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_VERCEL,
@@ -210,6 +212,14 @@ const syncSecrets = async ({
accessToken,
});
break;
case INTEGRATION_CLOUDFLARE_PAGES:
await syncSecretsCloudflarePages({
integration,
secrets,
accessId,
accessToken
});
break;
}
};
@@ -1833,4 +1843,74 @@ const syncSecretsHashiCorpVault = async ({
);
};
/**
* Sync/push [secrets] to Cloudflare Pages project with name [integration.app]
* @param {Object} obj
* @param {IIntegration} obj.integration - integration details
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
* @param {String} obj.accessToken - API token for Cloudflare
*/
const syncSecretsCloudflarePages = async ({
integration,
secrets,
accessId,
accessToken,
}: {
integration: IIntegration;
secrets: any;
accessId: string | null;
accessToken: string;
}) => {
// get secrets from cloudflare pages
const getSecretsRes = (
await standardRequest.get(
`${INTEGRATION_CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept": "application/json",
},
}
)
)
.data.result['deployment_configs'][integration.targetEnvironment]['env_vars'];
// copy the secrets object, so we can set deleted keys to null
const secretsObj: any = {...secrets};
for (const [key, val] of Object.entries(secretsObj)) {
secretsObj[key] = { type: "secret_text", value: val };
}
if (getSecretsRes) {
for await (const key of Object.keys(getSecretsRes)) {
if (!(key in secrets)) {
// case: secret does not exist in infisical
// -> delete secret from cloudflare pages
secretsObj[key] = null;
}
}
}
const data = {
"deployment_configs": {
[integration.targetEnvironment]: {
"env_vars": secretsObj
}
}
};
await standardRequest.patch(
`${INTEGRATION_CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}`,
data,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept": "application/json",
},
}
);
}
export { syncSecrets };

View File

@@ -14,6 +14,7 @@ import {
INTEGRATION_RAILWAY,
INTEGRATION_RENDER,
INTEGRATION_SUPABASE,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_TRAVISCI,
INTEGRATION_VERCEL,
} from "../variables";
@@ -50,7 +51,8 @@ export interface IIntegration {
| "travisci"
| "supabase"
| "checkly"
| "hashicorp-vault";
| "hashicorp-vault"
| "cloudflare-pages";
integrationAuth: Types.ObjectId;
}
@@ -138,6 +140,7 @@ const integrationSchema = new Schema<IIntegration>(
INTEGRATION_SUPABASE,
INTEGRATION_CHECKLY,
INTEGRATION_HASHICORP_VAULT,
INTEGRATION_CLOUDFLARE_PAGES,
],
required: true,
},

View File

@@ -16,6 +16,7 @@ import {
INTEGRATION_RAILWAY,
INTEGRATION_RENDER,
INTEGRATION_SUPABASE,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_TRAVISCI,
INTEGRATION_VERCEL,
} from "../variables";
@@ -23,7 +24,7 @@ import {
export interface IIntegrationAuth extends Document {
_id: Types.ObjectId;
workspace: Types.ObjectId;
integration: "heroku" | "vercel" | "netlify" | "github" | "gitlab" | "render" | "railway" | "flyio" | "azure-key-vault" | "circleci" | "travisci" | "supabase" | "aws-parameter-store" | "aws-secret-manager" | "checkly";
integration: 'heroku' | 'vercel' | 'netlify' | 'github' | 'gitlab' | 'render' | 'railway' | 'flyio' | 'azure-key-vault' | 'circleci' | 'travisci' | 'supabase' | 'aws-parameter-store' | 'aws-secret-manager' | 'checkly' | 'cloudflare-pages';
teamId: string;
accountId: string;
url: string;
@@ -67,6 +68,7 @@ const integrationAuthSchema = new Schema<IIntegrationAuth>(
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
INTEGRATION_HASHICORP_VAULT,
INTEGRATION_CLOUDFLARE_PAGES,
],
required: true,
},

View File

@@ -21,9 +21,10 @@ export const INTEGRATION_RAILWAY = "railway";
export const INTEGRATION_FLYIO = "flyio";
export const INTEGRATION_CIRCLECI = "circleci";
export const INTEGRATION_TRAVISCI = "travisci";
export const INTEGRATION_SUPABASE = "supabase";
export const INTEGRATION_CHECKLY = "checkly";
export const INTEGRATION_HASHICORP_VAULT = "hashicorp-vault";
export const INTEGRATION_SUPABASE = 'supabase';
export const INTEGRATION_CHECKLY = 'checkly';
export const INTEGRATION_HASHICORP_VAULT = 'hashicorp-vault';
export const INTEGRATION_CLOUDFLARE_PAGES = 'cloudflare-pages';
export const INTEGRATION_SET = new Set([
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_HEROKU,
@@ -38,6 +39,7 @@ export const INTEGRATION_SET = new Set([
INTEGRATION_SUPABASE,
INTEGRATION_CHECKLY,
INTEGRATION_HASHICORP_VAULT,
INTEGRATION_CLOUDFLARE_PAGES
]);
// integration types
@@ -63,8 +65,9 @@ export const INTEGRATION_RAILWAY_API_URL = "https://backboard.railway.app/graphq
export const INTEGRATION_FLYIO_API_URL = "https://api.fly.io/graphql";
export const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api";
export const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com";
export const INTEGRATION_SUPABASE_API_URL = "https://api.supabase.com";
export const INTEGRATION_CHECKLY_API_URL = "https://api.checklyhq.com";
export const INTEGRATION_SUPABASE_API_URL = 'https://api.supabase.com';
export const INTEGRATION_CHECKLY_API_URL = 'https://api.checklyhq.com';
export const INTEGRATION_CLOUDFLARE_PAGES_API_URL = 'https://api.cloudflare.com';
export const getIntegrationOptions = async () => {
const INTEGRATION_OPTIONS = [
@@ -218,10 +221,19 @@ export const getIntegrationOptions = async () => {
slug: "gcp",
image: "Google Cloud Platform.png",
isAvailable: false,
type: "",
clientId: "",
docsLink: "",
type: '',
clientId: '',
docsLink: ''
},
{
name: 'Cloudflare Pages',
slug: 'cloudflare-pages',
image: 'Cloudflare.png',
isAvailable: true,
type: 'pat',
clientId: '',
docsLink: ''
}
]
return INTEGRATION_OPTIONS;

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 KiB

View File

@@ -31,7 +31,7 @@ Press on the Checkly tile and input your Checkly API Key to grant Infisical acce
## Start integration
Select which Infisical environment secrets you want to sync to Checkly press create integration to start syncing secrets.
Select which Infisical environment secrets you want to sync to Checkly and press create integration to start syncing secrets.
![integrations checkly](../../images/integrations-checkly-create.png)
![integrations checkly](../../images/integrations-checkly.png)
![integrations checkly](../../images/integrations-checkly.png)

View File

@@ -0,0 +1,44 @@
---
title: "Cloudflare Pages"
description: "How to sync secrets from Infisical to Cloudflare Pages"
---
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
## Navigate to your project's integrations tab
![integrations](../../images/integrations.png)
## Authorize Infisical for Cloudflare Pages
Obtain a Cloudflare [API token](https://dash.cloudflare.com/profile/api-tokens) and [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/):
1. Create a new [API token](https://dash.cloudflare.com/profile/api-tokens) in My Profile > API Tokens
![integrations cloudflare credentials 1](../../images/integrations-cloudflare-credentials-1.png)
![integrations cloudflare credentials 2](../../images/integrations-cloudflare-credentials-2.png)
![integrations cloudflare credentials 3](../../images/integrations-cloudflare-credentials-3.png)
2. Copy your [Account ID](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/) from Account > Workers & Pages > Overview
![integrations cloudflare credentials 4](../../images/integrations-cloudflare-credentials-4.png)
Press on the Cloudflare Pages tile and input your Cloudflare API token and account ID to grant Infisical access to your Cloudflare Pages.
![integrations cloudflare authorization](../../images/integrations-cloudflare-auth.png)
<Info>
If this is your project's first cloud integration, then you'll have to grant
Infisical access to your project's environment variables. Although this step
breaks E2EE, it's necessary for Infisical to sync the environment variables to
the cloud platform.
</Info>
## Start integration
Select which Infisical environment secrets you want to sync to Cloudflare and press create integration to start syncing secrets.
![integrations cloudflare](../../images/integrations-cloudflare-create.png)
![integrations cloudflare](../../images/integrations-cloudflare.png)

View File

@@ -7,43 +7,44 @@ Integrations allow environment variables to be synced from Infisical into your l
Missing an integration? [Throw in a request](https://github.com/Infisical/infisical/issues).
| Integration | Type | Status |
| -------------------------------------------------------------- | --------- | ----------- |
| [Docker](/integrations/platforms/docker) | Platform | Available |
| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available |
| [Terraform](/integrations/frameworks/terraform) | Infrastructure as code | Available |
| [PM2](/integrations/platforms/pm2) | Platform | Available |
| [Heroku](/integrations/cloud/heroku) | Cloud | Available |
| [Vercel](/integrations/cloud/vercel) | Cloud | Available |
| [Netlify](/integrations/cloud/netlify) | Cloud | Available |
| [Render](/integrations/cloud/render) | Cloud | Available |
| [Railway](/integrations/cloud/railway) | Cloud | Available |
| [Fly.io](/integrations/cloud/flyio) | Cloud | Available |
| [Supabase](/integrations/cloud/supabase) | Cloud | Available |
| [Checkly](/integrations/cloud/checkly) | Cloud | Available |
| [HashiCorp Vault](/integrations/cloud/hashicorp-vault) | Cloud | Available |
| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available |
| [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available |
| [Azure Key Vault](/integrations/cloud/azure-key-vault) | Cloud | Available |
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
| [GitLab](/integrations/cicd/gitlab) | CI/CD | Available |
| [CircleCI](/integrations/cicd/circleci) | CI/CD | Available |
| [Travis CI](/integrations/cicd/travisci) | CI/CD | Available |
| [React](/integrations/frameworks/react) | Framework | Available |
| [Vue](/integrations/frameworks/vue) | Framework | Available |
| [Express](/integrations/frameworks/express) | Framework | Available |
| [Next.js](/integrations/frameworks/nextjs) | Framework | Available |
| [NestJS](/integrations/frameworks/nestjs) | Framework | Available |
| [SvelteKit](/integrations/frameworks/sveltekit) | Framework | Available |
| [Nuxt](/integrations/frameworks/nuxt) | Framework | Available |
| [Gatsby](/integrations/frameworks/gatsby) | Framework | Available |
| [Remix](/integrations/frameworks/remix) | Framework | Available |
| [Vite](/integrations/frameworks/vite) | Framework | Available |
| [Fiber](/integrations/frameworks/fiber) | Framework | Available |
| [Django](/integrations/frameworks/django) | Framework | Available |
| [Flask](/integrations/frameworks/flask) | Framework | Available |
| [Laravel](/integrations/frameworks/laravel) | Framework | Available |
| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available |
| GCP Secret Manager | Cloud | Coming soon |
| Jenkins | CI/CD | Coming soon |
| Integration | Type | Status |
| -------------------------------------------------------------- | ---------------------- | ----------- |
| [Docker](/integrations/platforms/docker) | Platform | Available |
| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available |
| [Terraform](/integrations/frameworks/terraform) | Infrastructure as code | Available |
| [PM2](/integrations/platforms/pm2) | Platform | Available |
| [Heroku](/integrations/cloud/heroku) | Cloud | Available |
| [Vercel](/integrations/cloud/vercel) | Cloud | Available |
| [Netlify](/integrations/cloud/netlify) | Cloud | Available |
| [Render](/integrations/cloud/render) | Cloud | Available |
| [Railway](/integrations/cloud/railway) | Cloud | Available |
| [Fly.io](/integrations/cloud/flyio) | Cloud | Available |
| [Supabase](/integrations/cloud/supabase) | Cloud | Available |
| [Cloudflare Pages](/integrations/cloud/cloudflare-pages) | Cloud | Available |
| [Checkly](/integrations/cloud/checkly) | Cloud | Available |
| [HashiCorp Vault](/integrations/cloud/hashicorp-vault) | Cloud | Available |
| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available |
| [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available |
| [Azure Key Vault](/integrations/cloud/azure-key-vault) | Cloud | Available |
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
| [GitLab](/integrations/cicd/gitlab) | CI/CD | Available |
| [CircleCI](/integrations/cicd/circleci) | CI/CD | Available |
| [Travis CI](/integrations/cicd/travisci) | CI/CD | Available |
| [React](/integrations/frameworks/react) | Framework | Available |
| [Vue](/integrations/frameworks/vue) | Framework | Available |
| [Express](/integrations/frameworks/express) | Framework | Available |
| [Next.js](/integrations/frameworks/nextjs) | Framework | Available |
| [NestJS](/integrations/frameworks/nestjs) | Framework | Available |
| [SvelteKit](/integrations/frameworks/sveltekit) | Framework | Available |
| [Nuxt](/integrations/frameworks/nuxt) | Framework | Available |
| [Gatsby](/integrations/frameworks/gatsby) | Framework | Available |
| [Remix](/integrations/frameworks/remix) | Framework | Available |
| [Vite](/integrations/frameworks/vite) | Framework | Available |
| [Fiber](/integrations/frameworks/fiber) | Framework | Available |
| [Django](/integrations/frameworks/django) | Framework | Available |
| [Flask](/integrations/frameworks/flask) | Framework | Available |
| [Laravel](/integrations/frameworks/laravel) | Framework | Available |
| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available |
| GCP Secret Manager | Cloud | Coming soon |
| Jenkins | CI/CD | Coming soon |

View File

@@ -196,6 +196,7 @@
"integrations/cloud/railway",
"integrations/cloud/flyio",
"integrations/cloud/supabase",
"integrations/cloud/cloudflare-pages",
"integrations/cloud/checkly",
"integrations/cloud/hashicorp-vault",
"integrations/cloud/azure-key-vault",

View File

@@ -18,7 +18,8 @@ const integrationSlugNameMapping: Mapping = {
'travisci': 'TravisCI',
'supabase': 'Supabase',
'checkly': 'Checkly',
'hashicorp-vault': 'Vault'
'hashicorp-vault': 'Vault',
'cloudflare-pages': 'Cloudflare Pages'
}
const envMapping: Mapping = {

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -220,6 +220,9 @@ export default function Integrations() {
case "hashicorp-vault":
link = `${window.location.origin}/integrations/hashicorp-vault/authorize`;
break;
case "cloudflare-pages":
link = `${window.location.origin}/integrations/cloudflare-pages/authorize`;
break;
default:
break;
}
@@ -284,6 +287,9 @@ export default function Integrations() {
case "hashicorp-vault":
link = `${window.location.origin}/integrations/hashicorp-vault/create?integrationAuthId=${integrationAuth._id}`;
break;
case "cloudflare-pages":
link = `${window.location.origin}/integrations/cloudflare-pages/create?integrationAuthId=${integrationAuth._id}`;
break;
default:
break;
}

View File

@@ -0,0 +1,89 @@
import { useState } from "react";
import { useRouter } from "next/router";
import { Button,Card, CardTitle, FormControl, Input } from "../../../components/v2";
import saveIntegrationAccessToken from "../../api/integrations/saveIntegrationAccessToken";
export default function CloudflarePagesIntegrationPage() {
const router = useRouter();
const [accessKey, setAccessKey] = useState("");
const [accessKeyErrorText, setAccessKeyErrorText] = useState("");
const [accountId, setAccountId] = useState("");
const [accountIdErrorText, setAccountIdErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const handleButtonClick = async () => {
try {
setAccessKeyErrorText("");
setAccountIdErrorText("");
if (accessKey.length === 0 || accountId.length === 0) {
if (accessKey.length === 0) setAccessKeyErrorText("API token cannot be blank!");
if (accountId.length === 0) setAccountIdErrorText("Account ID cannot be blank!");
return;
}
setIsLoading(true);
const integrationAuth = await saveIntegrationAccessToken({
workspaceId: localStorage.getItem("projectData.id"),
integration: "cloudflare-pages",
accessId: accountId,
accessToken: accessKey,
url: null,
namespace: null
});
setAccessKey("");
setAccountId("");
setIsLoading(false);
router.push(`/integrations/cloudflare-pages/create?integrationAuthId=${integrationAuth._id}`);
} catch (err) {
console.error(err);
}
}
return (
<div className="flex h-full w-full items-center justify-center">
<Card className="max-w-lg rounded-md border border-mineshaft-600 mb-12">
<CardTitle className="text-left px-6" subTitle="After adding your API-key, you will be prompted to set up an integration for a particular Infisical project and environment.">Cloudflare Pages Integration</CardTitle>
<FormControl
label="Cloudflare Pages API token"
errorText={accessKeyErrorText}
isError={accessKeyErrorText !== "" ?? false}
className="mx-6"
>
<Input
placeholder=""
value={accessKey}
onChange={(e) => setAccessKey(e.target.value)}
/>
</FormControl>
<FormControl
label="Cloudflare Pages Account ID"
errorText={accountIdErrorText}
isError={accountIdErrorText !== "" ?? false}
className="mx-6"
>
<Input
placeholder=""
value={accountId}
onChange={(e) => setAccountId(e.target.value)}
/>
</FormControl>
<Button
onClick={handleButtonClick}
color="mineshaft"
variant="outline_bg"
className="mb-6 mt-2 ml-auto mr-6 w-min"
isFullWidth={false}
isLoading={isLoading}
>
Connect to Cloudflare Pages
</Button>
</Card>
</div>
);
}
CloudflarePagesIntegrationPage.requireAuth = true;

View File

@@ -0,0 +1,162 @@
import { useEffect,useState } from "react";
import { useRouter } from "next/router";
import queryString from "query-string";
import { Button, Card, CardTitle, FormControl, Select, SelectItem } from "../../../components/v2";
import { useGetWorkspaceById } from "../../../hooks/api";
import { useGetIntegrationAuthApps, useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
import createIntegration from "../../api/integrations/createIntegration";
const cloudflareEnvironments = [
{ name: "Production", slug: "production" },
{ name: "Preview", slug: "preview" }
];
export default function CloudflarePagesIntegrationPage() {
const router = useRouter();
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? "");
const { data: integrationAuthApps } = useGetIntegrationAuthApps({
integrationAuthId: (integrationAuthId as string) ?? ""
});
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
const [targetApp, setTargetApp] = useState("");
const [targetAppId, setTargetAppId] = useState("");
const [targetEnvironment, setTargetEnvironment] = useState("");
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (workspace) {
setSelectedSourceEnvironment(workspace.environments[0].slug);
}
}, [workspace]);
useEffect(() => {
if (integrationAuthApps) {
if (integrationAuthApps.length > 0) {
setTargetApp(integrationAuthApps[0].name);
setTargetAppId(String(integrationAuthApps[0].appId));
setTargetEnvironment(cloudflareEnvironments[0].slug);
} else {
setTargetApp("none");
setTargetEnvironment(cloudflareEnvironments[0].slug);
}
}
}, [integrationAuthApps]);
const handleButtonClick = async () => {
try {
if (!integrationAuth?._id) return;
setIsLoading(true);
await createIntegration({
integrationAuthId: integrationAuth?._id,
isActive: true,
app: targetApp,
appId: targetAppId,
sourceEnvironment: selectedSourceEnvironment,
targetEnvironment,
targetEnvironmentId: null,
targetService: null,
targetServiceId: null,
owner: null,
path: null,
region: null,
secretPath: "/",
});
setIsLoading(false);
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
} catch(err) {
console.error(err);
}
}
return integrationAuth &&
workspace &&
selectedSourceEnvironment &&
integrationAuthApps &&
targetEnvironment &&
targetApp ? (
<div className="flex h-full w-full items-center justify-center bg-gradient-to-tr from-mineshaft-900 to-bunker-900">
<Card className="max-w-lg rounded-md p-0 border border-mineshaft-600">
<CardTitle className="text-left px-6" subTitle="Choose which environment in Infisical you want to sync with your Cloudflare Pages project.">Cloudflare Pages Integration</CardTitle>
<FormControl label="Infisical Project Environment" className="mt-2 px-6">
<Select
value={selectedSourceEnvironment}
onValueChange={(val) => setSelectedSourceEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{workspace?.environments.map((sourceEnvironment) => (
<SelectItem
value={sourceEnvironment.slug}
key={`source-environment-${sourceEnvironment.slug}`}
>
{sourceEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<FormControl label="Cloudflare Pages Project" className="mt-4 px-6">
<Select
value={targetApp}
onValueChange={(val) => setTargetApp(val)}
className="w-full border border-mineshaft-500"
isDisabled={integrationAuthApps.length === 0}
>
{integrationAuthApps.length > 0 ? (
integrationAuthApps.map((integrationAuthApp) => (
<SelectItem
value={integrationAuthApp.name}
key={`target-app-${integrationAuthApp.name}`}
>
{integrationAuthApp.name}
</SelectItem>
))
) : (
<SelectItem value="none" key="target-app-none">
No apps found
</SelectItem>
)}
</Select>
</FormControl>
<FormControl label="Cloudflare Pages Environment" className="mt-4 px-6">
<Select
value={targetEnvironment}
onValueChange={(val) => setTargetEnvironment(val)}
className="w-full border border-mineshaft-500"
>
{cloudflareEnvironments.map((cloudflareEnvironment) => (
<SelectItem
value={cloudflareEnvironment.slug}
key={`target-environment-${cloudflareEnvironment.slug}`}
>
{cloudflareEnvironment.name}
</SelectItem>
))}
</Select>
</FormControl>
<Button
onClick={handleButtonClick}
color="mineshaft"
variant="outline_bg"
className="mt-2 mb-6 ml-auto mr-6"
isFullWidth={false}
isLoading={isLoading}
>
Create Integration
</Button>
</Card>
</div>
) : (
<div />
);
}
CloudflarePagesIntegrationPage.requireAuth = true;