= {};
+ formErrors.forEach((issue) => {
+ if (issue.path.length > 0) {
+ errorMap[String(issue.path[0])] = issue.message;
+ }
+ });
+ return errorMap;
+ }, [formErrors]);
+
+ const { currentOrg } = useOrganization();
+ const organizationId = currentOrg?.id || "";
+
+ const { permission } = useOrgPermission();
+ const canCreateToken = permission.can(
+ OrgPermissionIdentityActions.CreateToken,
+ OrgPermissionSubjects.Identity
+ );
+
+ const { data: identityMembershipOrgsData, isPending: isIdentitiesLoading } =
+ useGetIdentityMembershipOrgs({
+ organizationId,
+ limit: 20000
+ });
+ const identityMembershipOrgs = identityMembershipOrgsData?.identityMemberships || [];
+
+ const { mutateAsync: createToken, isPending: isCreatingToken } =
+ useCreateTokenIdentityTokenAuth();
+ const { mutateAsync: addIdentityTokenAuth, isPending: isAddingTokenAuth } =
+ useAddIdentityTokenAuth();
+ const { refetch } = useGetIdentityTokenAuth(identity?.id ?? "");
+
+ const handleGenerateCommand = async () => {
+ setFormErrors([]);
+
+ if (canCreateToken && autogenerateToken) {
+ const validation = formSchemaWithIdentity.safeParse({ name, host, identity });
+ if (!validation.success) {
+ setFormErrors(validation.error.issues);
+ return;
+ }
+
+ const validatedIdentity = validation.data.identity;
+
+ try {
+ const { data: identityTokenAuth } = await refetch();
+ if (!identityTokenAuth) {
+ await addIdentityTokenAuth({
+ identityId: validatedIdentity.id,
+ organizationId,
+ accessTokenTTL: 2592000,
+ accessTokenMaxTTL: 2592000,
+ accessTokenNumUsesLimit: 0,
+ accessTokenTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]
+ });
+ createNotification({
+ text: "Token authentication has been automatically enabled for the selected identity. By default, it is configured to allow all IP addresses with a default token TTL of 30 days. You can manage these settings in Access Control.",
+ type: "warning"
+ });
+ }
+
+ const token = await createToken({
+ identityId: validatedIdentity.id,
+ name: `relay token for ${name} (autogenerated)`
+ });
+ setIdentityToken(token.accessToken);
+ createNotification({
+ text: "Automatically generated a token for the selected identity.",
+ type: "info"
+ });
+ setStep("command");
+ } catch {
+ setIdentityToken("");
+ }
+ } else {
+ const validation = formSchemaWithToken.safeParse({
+ name,
+ host,
+ identityToken
+ });
+ if (!validation.success) {
+ setFormErrors(validation.error.issues);
+ return;
+ }
+ setStep("command");
+ }
+ };
+
+ const installCommand = useMemo(() => {
+ return `sudo infisical relay systemd install --name=${name} --domain=${siteURL} --host=${host} --token=${identityToken}`;
+ }, [name, siteURL, host, identityToken]);
+
+ const startServiceCommand = "sudo systemctl start infisical-relay";
+ const enableServiceCommand = "sudo systemctl enable infisical-relay";
+
+ if (step === "command") {
+ return (
+ <>
+
+
+
+ {
+ navigator.clipboard.writeText(installCommand);
+ createNotification({
+ text: "Installation command copied to clipboard",
+ type: "info"
+ });
+ }}
+ className="w-10"
+ >
+
+
+
+
+
+
+
+ {
+ navigator.clipboard.writeText(startServiceCommand);
+ createNotification({
+ text: "Start service command copied to clipboard",
+ type: "info"
+ });
+ }}
+ className="w-10"
+ >
+
+
+
+
+
+ {
+ navigator.clipboard.writeText(enableServiceCommand);
+ createNotification({
+ text: "Enable service command copied to clipboard",
+ type: "info"
+ });
+ }}
+ className="w-10"
+ >
+
+
+
+
+ Install the Infisical CLI
+
+
+
+
+
+
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+ setName(e.target.value)}
+ placeholder="Enter relay name..."
+ isError={Boolean(errors.name)}
+ />
+ {errors.name && {errors.name}
}
+
+
+ setHost(e.target.value)}
+ placeholder="0.0.0.0"
+ isError={Boolean(errors.host)}
+ />
+ {errors.host && {errors.host}
}
+
+ {canCreateToken && autogenerateToken ? (
+ <>
+
+
+ setIdentity(
+ e as SingleValue<{
+ id: string;
+ name: string;
+ }>
+ )
+ }
+ isLoading={isIdentitiesLoading}
+ placeholder="Select identity..."
+ options={identityMembershipOrgs.map((membership) => membership.identity)}
+ getOptionValue={(option) => option.id}
+ getOptionLabel={(option) => option.name}
+ />
+ {errors.identity && {errors.identity}
}
+ >
+ ) : (
+ <>
+
+ setIdentityToken(e.target.value)}
+ placeholder="Enter identity token..."
+ isError={Boolean(errors.identityToken)}
+ />
+ {errors.identityToken && {errors.identityToken}
}
+ >
+ )}
+
+ {canCreateToken && (
+
+
{
+ setAutogenerateToken(Boolean(e));
+ }}
+ id="autogenerate-token"
+ className="mr-2"
+ >
+
+ Automatically enable token auth and generate a token for identity
+
+ Token authentication will be automatically enabled for the selected identity if
+ it isn't already configured. By default, it will be configured to allow all
+ IP addresses with a token TTL of 30 days. You can manage these settings in
+ Access Control.
+
+
A token will automatically be generated to be used with the CLI command.
+ >
+ }
+ >
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/frontend/src/pages/organization/NetworkingPage/components/RelayTab/components/RelayDeployModal.tsx b/frontend/src/pages/organization/NetworkingPage/components/RelayTab/components/RelayDeployModal.tsx
index aab599925..ab4a58a32 100644
--- a/frontend/src/pages/organization/NetworkingPage/components/RelayTab/components/RelayDeployModal.tsx
+++ b/frontend/src/pages/organization/NetworkingPage/components/RelayTab/components/RelayDeployModal.tsx
@@ -4,6 +4,7 @@ import { Modal, ModalContent } from "@app/components/v2";
import { RelayDeploymentMethodSelect } from "@app/pages/organization/NetworkingPage/components/RelayTab/components/RelayDeploymentMethodSelect";
import { RelayCliDeploymentMethod } from "./RelayCliDeploymentMethod";
+import { RelayCliSystemdDeploymentMethod } from "./RelayCliSystemdDeploymentMethod";
import { RelayTerraformDeploymentMethod } from "./RelayTerraformDeploymentMethod";
type Props = {
@@ -13,6 +14,7 @@ type Props = {
export const RelayDeploymentInfoMap = {
cli: { name: "CLI", image: "SSH.png", component: RelayCliDeploymentMethod },
+ systemd: { name: "CLI (systemd)", image: "SSH.png", component: RelayCliSystemdDeploymentMethod },
terraform: {
name: "Terraform",
image: "Terraform.png",
diff --git a/frontend/src/pages/organization/NetworkingPage/components/RelayTab/components/RelayTerraformDeploymentMethod.tsx b/frontend/src/pages/organization/NetworkingPage/components/RelayTab/components/RelayTerraformDeploymentMethod.tsx
index a8e1693a5..d9c67d610 100644
--- a/frontend/src/pages/organization/NetworkingPage/components/RelayTab/components/RelayTerraformDeploymentMethod.tsx
+++ b/frontend/src/pages/organization/NetworkingPage/components/RelayTab/components/RelayTerraformDeploymentMethod.tsx
@@ -190,15 +190,6 @@ export const RelayTerraformDeploymentMethod = () => {
}
};
- const handleIdentityChange = (
- selectedIdentity: SingleValue<{
- id: string;
- name: string;
- }>
- ) => {
- setIdentity(selectedIdentity);
- };
-
const terraformCommand = useMemo(() => {
return `terraform {
required_providers {
@@ -365,7 +356,7 @@ resource "aws_eip_association" "eip_assoc" {
- handleIdentityChange(
+ setIdentity(
e as SingleValue<{
id: string;
name: string;