Merge pull request #712 from atimapreandrew/laravel-forge-integration

Laravel forge integration
This commit is contained in:
BlackMagiq
2023-07-05 23:43:43 +07:00
committed by GitHub
20 changed files with 479 additions and 88 deletions

View File

@@ -82,4 +82,4 @@ export const getHttpsEnabled = async () => {
}
return (await client.getSecret("HTTPS_ENABLED")).secretValue === "true" && true
}
}

View File

@@ -290,7 +290,7 @@ export const updateSecret = async (req: Request, res: Response) => {
const { workspaceId, environmentName } = req.params;
const secretModificationsRequested: ModifySecretRequestBody = req.body.secret;
const secretIdUserCanModify = await Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 });
await Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 });
const sanitizedSecret: SanitizedSecretModify = {
secretKeyCiphertext: secretModificationsRequested.secretKeyCiphertext,

View File

@@ -3,7 +3,6 @@ import { Types } from "mongoose";
import { Membership, Secret } from "../../models";
import Tag from "../../models/tag";
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { MongoError } from "mongodb";
export const createWorkspaceTag = async (req: Request, res: Response) => {
const { workspaceId } = req.params;

View File

@@ -9,15 +9,17 @@ import {
INTEGRATION_CHECKLY_API_URL,
INTEGRATION_CIRCLECI,
INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_CLOUDFLARE_PAGES_API_URL,
INTEGRATION_FLYIO,
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,
INTEGRATION_LARAVELFORGE,
INTEGRATION_LARAVELFORGE_API_URL,
INTEGRATION_NETLIFY,
INTEGRATION_NETLIFY_API_URL,
INTEGRATION_RAILWAY,
@@ -116,6 +118,12 @@ const getApps = async ({
accessToken,
});
break;
case INTEGRATION_LARAVELFORGE:
apps = await getAppsLaravelForge({
accessToken,
serverId: accessId
});
break;
case INTEGRATION_TRAVISCI:
apps = await getAppsTravisCI({
accessToken,
@@ -398,6 +406,40 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => {
return apps;
};
/**
* Return list of sites for Laravel Forge integration
* @param {Object} obj
* @param {String} obj.accessToken - access token for Laravel Forge API
* @param {String} obj.serverId - server id of Laravel Forge
* @returns {Object[]} apps - names and ids of Laravel Forge sites
* @returns {String} apps.name - name of Laravel Forge sites
* @returns {String} apps.appId - id of Laravel Forge sites
*/
const getAppsLaravelForge = async ({
accessToken,
serverId
}: {
accessToken: string;
serverId?: string;
}) => {
const res = (
await standardRequest.get(`${INTEGRATION_LARAVELFORGE_API_URL}/api/v1/servers/${serverId}/sites`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"Content-Type": "application/json",
},
})
).data.sites;
const apps = res.map((a: any) => ({
name: a.name,
appId: a.id,
}));
return apps;
};
/**
* Return list of apps for Fly.io integration
* @param {Object} obj

View File

@@ -18,6 +18,8 @@ import {
INTEGRATION_CHECKLY_API_URL,
INTEGRATION_CIRCLECI,
INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_CLOUDFLARE_PAGES_API_URL,
INTEGRATION_FLYIO,
INTEGRATION_FLYIO_API_URL,
INTEGRATION_GITHUB,
@@ -26,6 +28,8 @@ import {
INTEGRATION_HASHICORP_VAULT,
INTEGRATION_HEROKU,
INTEGRATION_HEROKU_API_URL,
INTEGRATION_LARAVELFORGE,
INTEGRATION_LARAVELFORGE_API_URL,
INTEGRATION_NETLIFY,
INTEGRATION_NETLIFY_API_URL,
INTEGRATION_RAILWAY,
@@ -34,8 +38,6 @@ 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,
@@ -154,6 +156,14 @@ const syncSecrets = async ({
accessToken,
});
break;
case INTEGRATION_LARAVELFORGE:
await syncSecretsLaravelForge({
integration,
secrets,
accessId,
accessToken,
});
break;
case INTEGRATION_TRAVISCI:
await syncSecretsTravisCI({
integration,
@@ -168,58 +178,30 @@ const syncSecrets = async ({
accessToken,
});
break;
case INTEGRATION_FLYIO:
await syncSecretsFlyio({
case INTEGRATION_CHECKLY:
await syncSecretsCheckly({
integration,
secrets,
accessToken,
});
break;
case INTEGRATION_HASHICORP_VAULT:
await syncSecretsHashiCorpVault({
integration,
integrationAuth,
secrets,
accessId,
accessToken,
});
break;
case INTEGRATION_CLOUDFLARE_PAGES:
await syncSecretsCloudflarePages({
integration,
secrets,
accessToken,
});
break;
case INTEGRATION_CIRCLECI:
await syncSecretsCircleCI({
integration,
secrets,
accessToken,
});
break;
case INTEGRATION_TRAVISCI:
await syncSecretsTravisCI({
integration,
secrets,
accessToken,
});
break;
case INTEGRATION_SUPABASE:
await syncSecretsSupabase({
integration,
secrets,
accessToken,
});
break;
case INTEGRATION_CHECKLY:
await syncSecretsCheckly({
integration,
secrets,
accessToken,
});
break;
case INTEGRATION_HASHICORP_VAULT:
await syncSecretsHashiCorpVault({
integration,
integrationAuth,
secrets,
accessId,
accessToken,
});
break;
case INTEGRATION_CLOUDFLARE_PAGES:
await syncSecretsCloudflarePages({
integration,
secrets,
accessId,
accessToken
});
break;
accessToken
});
break;
}
};
@@ -1167,6 +1149,48 @@ const syncSecretsRender = async ({
);
};
/**
* Sync/push [secrets] to Laravel Forge sites with id [integration.appId]
* @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 - access token for Laravel Forge integration
*/
const syncSecretsLaravelForge = async ({
integration,
secrets,
accessId,
accessToken,
}: {
integration: IIntegration;
secrets: any;
accessId: string | null;
accessToken: string;
}) => {
function transformObjectToString(obj: any) {
let result = "";
for (const key in obj) {
result += `${key}=${obj[key]}\n`;
}
return result;
}
await standardRequest.put(
`${INTEGRATION_LARAVELFORGE_API_URL}/api/v1/servers/${accessId}/sites/${integration.appId}/env`,
{
content: transformObjectToString(secrets),
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"Content-Type": "application/json",
},
}
);
};
/**
* Sync/push [secrets] to Railway project with id [integration.appId]
* @param {Object} obj
@@ -1874,7 +1898,7 @@ const syncSecretsCloudflarePages = async ({
}
)
)
.data.result['deployment_configs'][integration.targetEnvironment]['env_vars'];
.data.result["deployment_configs"][integration.targetEnvironment]["env_vars"];
// copy the secrets object, so we can set deleted keys to null
const secretsObj: any = {...secrets};

View File

@@ -5,16 +5,17 @@ import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_CHECKLY,
INTEGRATION_CIRCLECI,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_FLYIO,
INTEGRATION_GITHUB,
INTEGRATION_GITLAB,
INTEGRATION_HASHICORP_VAULT,
INTEGRATION_HEROKU,
INTEGRATION_LARAVELFORGE,
INTEGRATION_NETLIFY,
INTEGRATION_RAILWAY,
INTEGRATION_RENDER,
INTEGRATION_SUPABASE,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_TRAVISCI,
INTEGRATION_VERCEL,
} from "../variables";
@@ -48,6 +49,7 @@ export interface IIntegration {
| "railway"
| "flyio"
| "circleci"
| "laravel-forge"
| "travisci"
| "supabase"
| "checkly"
@@ -136,6 +138,7 @@ const integrationSchema = new Schema<IIntegration>(
INTEGRATION_RAILWAY,
INTEGRATION_FLYIO,
INTEGRATION_CIRCLECI,
INTEGRATION_LARAVELFORGE,
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
INTEGRATION_CHECKLY,

View File

@@ -7,24 +7,25 @@ import {
INTEGRATION_AWS_SECRET_MANAGER,
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_CIRCLECI,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_FLYIO,
INTEGRATION_GITHUB,
INTEGRATION_GITLAB,
INTEGRATION_HASHICORP_VAULT,
INTEGRATION_HEROKU,
INTEGRATION_LARAVELFORGE,
INTEGRATION_NETLIFY,
INTEGRATION_RAILWAY,
INTEGRATION_RENDER,
INTEGRATION_SUPABASE,
INTEGRATION_CLOUDFLARE_PAGES,
INTEGRATION_TRAVISCI,
INTEGRATION_VERCEL,
INTEGRATION_VERCEL
} from "../variables";
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' | 'cloudflare-pages';
integration: "heroku" | "vercel" | "netlify" | "github" | "gitlab" | "render" | "railway" | "flyio" | "azure-key-vault" | "laravel-forge" | "circleci" | "travisci" | "supabase" | "aws-parameter-store" | "aws-secret-manager" | "checkly" | "cloudflare-pages";
teamId: string;
accountId: string;
url: string;
@@ -65,6 +66,7 @@ const integrationAuthSchema = new Schema<IIntegrationAuth>(
INTEGRATION_RAILWAY,
INTEGRATION_FLYIO,
INTEGRATION_CIRCLECI,
INTEGRATION_LARAVELFORGE,
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
INTEGRATION_HASHICORP_VAULT,

View File

@@ -19,12 +19,13 @@ export const INTEGRATION_GITLAB = "gitlab";
export const INTEGRATION_RENDER = "render";
export const INTEGRATION_RAILWAY = "railway";
export const INTEGRATION_FLYIO = "flyio";
export const INTEGRATION_LARAVELFORGE = "laravel-forge"
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_CLOUDFLARE_PAGES = 'cloudflare-pages';
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,
@@ -35,6 +36,7 @@ export const INTEGRATION_SET = new Set([
INTEGRATION_RENDER,
INTEGRATION_FLYIO,
INTEGRATION_CIRCLECI,
INTEGRATION_LARAVELFORGE,
INTEGRATION_TRAVISCI,
INTEGRATION_SUPABASE,
INTEGRATION_CHECKLY,
@@ -65,9 +67,10 @@ 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_CLOUDFLARE_PAGES_API_URL = 'https://api.cloudflare.com';
export const INTEGRATION_SUPABASE_API_URL = "https://api.supabase.com";
export const INTEGRATION_LARAVELFORGE_API_URL = "https://forge.laravel.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 = [
@@ -144,6 +147,15 @@ export const getIntegrationOptions = async () => {
clientId: "",
docsLink: "",
},
{
name: "Laravel Forge",
slug: "laravel-forge",
image: "Laravel Forge.png",
isAvailable: true,
type: "pat",
clientId: "",
docsLink: "",
},
{
name: "AWS Secret Manager",
slug: "aws-secret-manager",
@@ -221,20 +233,20 @@ 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',
name: "Cloudflare Pages",
slug: "cloudflare-pages",
image: "Cloudflare.png",
isAvailable: true,
type: 'pat',
clientId: '',
docsLink: ''
type: "pat",
clientId: "",
docsLink: ""
}
]
return INTEGRATION_OPTIONS;
}
}

View File

@@ -0,0 +1,42 @@
---
title: "Laravel Forge"
description: "How to sync secrets from Infisical to Laravel Forge"
---
Prerequisites:
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
## Navigate to your project's integrations tab
![integrations](../../images/null)
## Enter your Laravel Forge Access Token and Server Id
Obtain a Laravel Forge access token in API Tokens
![integrations laravel forge dashboard](../../images/null)
![integrations laravel forge api tokens](../../images/null)
Obtain a Laravel Forge server id in Servers
![integrations laravel forge server](../../images/null)
![integrations laravel forge server id](../../images/null)
Press on the Laravel Forge tile and input your Laravel Forge access token and server id to grant Infisical access to your Laravel Forge account.
![integrations laravel forge authorization](../../images/null)
<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 which Laravel Forge site and press create integration to start syncing secrets to Laravel Forge.
![integrations laravel forge](../../images/null)
![integrations laravel forge](../../images/null)

View File

@@ -18,6 +18,7 @@ Missing an integration? [Throw in a request](https://github.com/Infisical/infisi
| [Vercel](/integrations/cloud/vercel) | Cloud | Available |
| [Netlify](/integrations/cloud/netlify) | Cloud | Available |
| [Render](/integrations/cloud/render) | Cloud | Available |
| [Laravel Forge](/integrations/cloud/laravel-forge) | Cloud | Available |
| [Railway](/integrations/cloud/railway) | Cloud | Available |
| [Fly.io](/integrations/cloud/flyio) | Cloud | Available |
| [Supabase](/integrations/cloud/supabase) | Cloud | Available |

View File

@@ -130,6 +130,7 @@
"self-hosting/deployment-options/standalone-infisical",
"self-hosting/deployment-options/fly.io",
"self-hosting/deployment-options/render",
"self-hosting/deployment-options/laravel-forge",
"self-hosting/deployment-options/digital-ocean-marketplace"
]
},
@@ -195,6 +196,7 @@
"integrations/cloud/render",
"integrations/cloud/railway",
"integrations/cloud/flyio",
"integrations/cloud/laravel-forge",
"integrations/cloud/supabase",
"integrations/cloud/cloudflare-pages",
"integrations/cloud/checkly",

View File

@@ -3,30 +3,53 @@ title: "Introduction"
description: "Explore deployment options for self hosting Infisical"
---
To meet various compliance requirements, you may want to self-host Infisical instead of using [Infisical Cloud](https://app.infisical.com/).
Self-hosted Infisical allows you to maintain your sensitive information within your own infrastructure and network, ensuring complete control over your data.
To meet various compliance requirements, you may want to self-host Infisical instead of using [Infisical Cloud](https://app.infisical.com/).
Self-hosted Infisical allows you to maintain your sensitive information within your own infrastructure and network, ensuring complete control over your data.
Choose from a variety of deployment options listed below to get started.
<Card title="Kubernetes" color="#ea5a0c" href="deployment-options/kubernetes-helm">
<Card
title="Kubernetes"
color="#ea5a0c"
href="deployment-options/kubernetes-helm"
>
Use our Helm chart to Install Infisical on your Kubernetes cluster
</Card>
<CardGroup cols={2}>
<Card title="Digital Ocean" color="#16a34a" href="deployment-options/digital-ocean-marketplace">
Automatically create and deploy Infisical on to a Kubernetes cluster
<Card
title="Digital Ocean"
color="#16a34a"
href="deployment-options/digital-ocean-marketplace"
>
Automatically create and deploy Infisical on to a Kubernetes cluster
</Card>
<Card title="Fly.io" color="#dc2626" href="deployment-options/fly.io">
Use our standalone docker image to deploy on Fly.io
</Card>
<Card
title="Laravel Forge"
color="#17B69B"
href="deployment-options/laravel-forge"
>
Use our standalone docker image to deploy on Laravel Forge
</Card>
<Card title="Render.com" color="#dc2626" href="deployment-options/render">
Install on Render using our standalone docker image
</Card>
<Card title="AWS EC2" color="#0285c7" href="deployment-options/aws-ec2">
Install infisical with just a few clicks using our Cloud Formation template
</Card>
<Card title="Docker Compose" color="#0285c7" href="deployment-options/docker-compose">
Install Infisical using our Docker Compose template
<Card
title="Docker Compose"
color="#0285c7"
href="deployment-options/docker-compose"
>
Install Infisical using our Docker Compose template
</Card>
<Card title="Docker" color="#0285c7" href="deployment-options/standalone-infisical">
<Card
title="Docker"
color="#0285c7"
href="deployment-options/standalone-infisical"
>
Use the fully packaged, single docker image Infisical to deploy anywhere
</Card>
</CardGroup>
</CardGroup>

View File

@@ -12,6 +12,7 @@ const integrationSlugNameMapping: Mapping = {
'github': 'GitHub',
'gitlab': 'GitLab',
'render': 'Render',
'laravel-forge': "Laravel Forge",
'railway': 'Railway',
'flyio': 'Fly.io',
'circleci': 'CircleCI',

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,80 @@
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 LaravelForgeCreateIntegrationPage() {
const router = useRouter();
const [apiKey, setApiKey] = useState("");
const [apiKeyErrorText, setApiKeyErrorText] = useState("");
const [serverId, setServerId] = useState("");
const [serverIdErrorText, setServerIdErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const handleButtonClick = async () => {
try {
setApiKeyErrorText("");
setServerIdErrorText("");
if (apiKey.length === 0) {
setApiKeyErrorText("Access Token cannot be blank");
return;
}
if (serverId.length === 0) {
setServerIdErrorText("Server Id cannot be blank");
return;
}
setIsLoading(true);
const integrationAuth = await saveIntegrationAccessToken({
workspaceId: localStorage.getItem("projectData.id"),
integration: "laravel-forge",
accessId: serverId,
accessToken: apiKey,
url: null,
namespace: null
});
setIsLoading(false);
router.push(`/integrations/laravel-forge/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-md rounded-md p-8">
<CardTitle className="text-center">Laravel Forge Integration</CardTitle>
<FormControl
label="Laravel Forge Access Token"
errorText={apiKeyErrorText}
isError={apiKeyErrorText !== "" ?? false}
>
<Input placeholder="Access Token" value={apiKey} onChange={(e) => setApiKey(e.target.value)} />
</FormControl>
<FormControl
label="Laravel Forge Server ID"
errorText={serverIdErrorText}
isError={serverIdErrorText !== "" ?? false}
>
<Input placeholder="123456" value={serverId} onChange={(e) => setServerId(e.target.value)} />
</FormControl>
<Button
onClick={handleButtonClick}
color="mineshaft"
className="mt-4"
isLoading={isLoading}
>
Connect to Laravel Forge
</Button>
</Card>
</div>
);
}
LaravelForgeCreateIntegrationPage.requireAuth = true;

View File

@@ -0,0 +1,155 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import queryString from "query-string";
import {
Button,
Card,
CardTitle,
FormControl,
Input,
Select,
SelectItem
} from "../../../components/v2";
import {
useGetIntegrationAuthApps,
useGetIntegrationAuthById
} from "../../../hooks/api/integrationAuth";
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
import createIntegration from "../../api/integrations/createIntegration";
export default function LaravelForgeCreateIntegrationPage() {
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 [secretPath, setSecretPath] = 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);
} else {
setTargetApp("none");
}
}
}, [integrationAuthApps]);
const handleButtonClick = async () => {
try {
if (!integrationAuth?._id) return;
setIsLoading(true);
await createIntegration({
integrationAuthId: integrationAuth?._id,
isActive: true,
app: targetApp,
appId:
integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.name === targetApp)
?.appId ?? null,
sourceEnvironment: selectedSourceEnvironment,
targetEnvironment: null,
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 &&
targetApp ? (
<div className="flex h-full w-full items-center justify-center">
<Card className="max-w-md rounded-md p-8">
<CardTitle className="text-center">Laravel Forge Integration</CardTitle>
<FormControl label="Project Environment" className="mt-4">
<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="Secrets Path">
<Input
value={secretPath}
onChange={(evt) => setSecretPath(evt.target.value)}
placeholder="Provide a path, default is /"
/>
</FormControl>
<FormControl label="Laravel Forge Sites" className="mt-4">
<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 sites found
</SelectItem>
)}
</Select>
</FormControl>
<Button
onClick={handleButtonClick}
color="mineshaft"
className="mt-4"
isLoading={isLoading}
isDisabled={integrationAuthApps.length === 0}
>
Create Integration
</Button>
</Card>
</div>
) : (
<div />
);
}
LaravelForgeCreateIntegrationPage.requireAuth = true;

View File

@@ -71,6 +71,9 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
case "circleci":
link = `${window.location.origin}/integrations/circleci/authorize`;
break;
case "laravel-forge":
link = `${window.location.origin}/integrations/laravel-forge/authorize`;
break;
case "travisci":
link = `${window.location.origin}/integrations/travisci/authorize`;
break;

View File

@@ -1,3 +1,4 @@
import { ChangeLanguageSection } from "../ChangeLanguageSection";
import { ChangePasswordSection } from "../ChangePasswordSection";
import { EmergencyKitSection } from "../EmergencyKitSection";
import { SecuritySection } from "../SecuritySection";
@@ -6,6 +7,7 @@ import { SessionsSection } from "../SessionsSection";
export const PersonalSecurityTab = () => {
return (
<div>
<ChangeLanguageSection />
<SecuritySection />
<SessionsSection />
<ChangePasswordSection />

View File

@@ -5,7 +5,7 @@ import { PersonalAPIKeyTab } from "../PersonalAPIKeyTab";
import { PersonalSecurityTab } from "../PersonalSecurityTab";
const tabs = [
{ name: "Security", key: "tab-account-security" },
{ name: "General", key: "tab-account-security" },
{ name: "API Keys", key: "tab-account-api-keys" }
];