mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 11:29:42 +00:00
Merge pull request #3870 from Infisical/feat/zabbixSyncIntegration
feat(secret-sync): add Zabbix secret sync
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
+3
@@ -25,6 +25,7 @@ import { TeamCitySyncFields } from "./TeamCitySyncFields";
|
||||
import { TerraformCloudSyncFields } from "./TerraformCloudSyncFields";
|
||||
import { VercelSyncFields } from "./VercelSyncFields";
|
||||
import { WindmillSyncFields } from "./WindmillSyncFields";
|
||||
import { ZabbixSyncFields } from "./ZabbixSyncFields";
|
||||
|
||||
export const SecretSyncDestinationFields = () => {
|
||||
const { watch } = useFormContext<TSecretSyncForm>();
|
||||
@@ -76,6 +77,8 @@ export const SecretSyncDestinationFields = () => {
|
||||
return <GitLabSyncFields />;
|
||||
case SecretSync.CloudflarePages:
|
||||
return <CloudflarePagesSyncFields />;
|
||||
case SecretSync.Zabbix:
|
||||
return <ZabbixSyncFields />;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Config Field: ${destination}`);
|
||||
}
|
||||
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||
import { SingleValue } from "react-select";
|
||||
|
||||
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
||||
import { FilterableSelect, FormControl, Select, SelectItem } from "@app/components/v2";
|
||||
import {
|
||||
TZabbixHost,
|
||||
useZabbixConnectionListHosts,
|
||||
ZABBIX_SYNC_SCOPES,
|
||||
ZabbixMacroType,
|
||||
ZabbixSyncScope
|
||||
} from "@app/hooks/api/appConnections/zabbix";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
import { TSecretSyncForm } from "../schemas";
|
||||
|
||||
export const ZabbixSyncFields = () => {
|
||||
const { control, watch, setValue } = useFormContext<
|
||||
TSecretSyncForm & { destination: SecretSync.Zabbix }
|
||||
>();
|
||||
|
||||
const connectionId = useWatch({ name: "connection.id", control });
|
||||
const currentScope = watch("destinationConfig.scope");
|
||||
|
||||
const { data: hosts = [], isPending: isHostsPending } = useZabbixConnectionListHosts(
|
||||
connectionId,
|
||||
{
|
||||
enabled: Boolean(connectionId)
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SecretSyncConnectionField
|
||||
onChange={() => {
|
||||
setValue("destinationConfig.scope", ZabbixSyncScope.Global);
|
||||
setValue("destinationConfig.hostId", "");
|
||||
setValue("destinationConfig.hostName", "");
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
name="destinationConfig.scope"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Scope"
|
||||
tooltipClassName="max-w-lg py-3"
|
||||
tooltipText={
|
||||
<div className="flex flex-col gap-3">
|
||||
<p>
|
||||
Specify how Infisical should manage secrets from Zabbix. The following options are
|
||||
available:
|
||||
</p>
|
||||
<ul className="flex list-disc flex-col gap-3 pl-4">
|
||||
{Object.values(ZABBIX_SYNC_SCOPES).map(({ name, description }) => {
|
||||
return (
|
||||
<li key={name}>
|
||||
<p className="text-mineshaft-300">
|
||||
<span className="font-medium text-bunker-200">{name}</span>: {description}
|
||||
</p>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Select
|
||||
value={value}
|
||||
onValueChange={(val) => {
|
||||
onChange(val);
|
||||
setValue("destinationConfig.hostId", "");
|
||||
setValue("destinationConfig.hostName", "");
|
||||
}}
|
||||
className="w-full border border-mineshaft-500 capitalize"
|
||||
position="popper"
|
||||
placeholder="Select a scope..."
|
||||
dropdownContainerClassName="max-w-none"
|
||||
>
|
||||
{Object.values(ZabbixSyncScope).map((scope) => (
|
||||
<SelectItem className="capitalize" value={scope} key={scope}>
|
||||
{scope.replace("-", " ")}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{currentScope === ZabbixSyncScope.Host && (
|
||||
<Controller
|
||||
name="destinationConfig.hostId"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message} label="Host">
|
||||
<FilterableSelect
|
||||
menuPlacement="top"
|
||||
isLoading={isHostsPending && Boolean(connectionId)}
|
||||
isDisabled={!connectionId}
|
||||
value={hosts.find((host) => host.hostId === value) ?? null}
|
||||
onChange={(option) => {
|
||||
const selectedOption = option as SingleValue<TZabbixHost>;
|
||||
onChange(selectedOption?.hostId ?? null);
|
||||
|
||||
if (selectedOption) {
|
||||
setValue("destinationConfig.hostName", selectedOption.host);
|
||||
} else {
|
||||
setValue("destinationConfig.hostName", "");
|
||||
}
|
||||
}}
|
||||
options={hosts}
|
||||
placeholder="Select a host..."
|
||||
getOptionLabel={(option) => option.host}
|
||||
getOptionValue={(option) => option.hostId}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Controller
|
||||
control={control}
|
||||
name="destinationConfig.macroType"
|
||||
defaultValue={ZabbixMacroType.Secret}
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message} label="Macro Type">
|
||||
<Select
|
||||
value={String(value)}
|
||||
onValueChange={(val) => onChange(parseInt(val, 10))}
|
||||
className="w-full border border-mineshaft-500 capitalize"
|
||||
position="popper"
|
||||
placeholder="Select a macro type..."
|
||||
dropdownContainerClassName="max-w-none"
|
||||
>
|
||||
<SelectItem value={String(ZabbixMacroType.Text)} key="text">
|
||||
Text
|
||||
</SelectItem>
|
||||
<SelectItem value={String(ZabbixMacroType.Secret)} key="secret">
|
||||
Secret
|
||||
</SelectItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+1
@@ -58,6 +58,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
|
||||
case SecretSync.Flyio:
|
||||
case SecretSync.GitLab:
|
||||
case SecretSync.CloudflarePages:
|
||||
case SecretSync.Zabbix:
|
||||
AdditionalSyncOptionsFieldsComponent = null;
|
||||
break;
|
||||
default:
|
||||
|
||||
+4
@@ -35,6 +35,7 @@ import { TeamCitySyncReviewFields } from "./TeamCitySyncReviewFields";
|
||||
import { TerraformCloudSyncReviewFields } from "./TerraformCloudSyncReviewFields";
|
||||
import { VercelSyncReviewFields } from "./VercelSyncReviewFields";
|
||||
import { WindmillSyncReviewFields } from "./WindmillSyncReviewFields";
|
||||
import { ZabbixSyncReviewFields } from "./ZabbixSyncReviewFields";
|
||||
|
||||
export const SecretSyncReviewFields = () => {
|
||||
const { watch } = useFormContext<TSecretSyncForm>();
|
||||
@@ -124,6 +125,9 @@ export const SecretSyncReviewFields = () => {
|
||||
case SecretSync.CloudflarePages:
|
||||
DestinationFieldsComponent = <CloudflarePagesSyncReviewFields />;
|
||||
break;
|
||||
case SecretSync.Zabbix:
|
||||
DestinationFieldsComponent = <ZabbixSyncReviewFields />;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Review Fields: ${destination}`);
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { GenericFieldLabel } from "@app/components/v2";
|
||||
import { ZabbixSyncScope } from "@app/hooks/api/appConnections/zabbix";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
const isTextMacro = (macroType: number) => macroType === 0;
|
||||
|
||||
export const ZabbixSyncReviewFields = () => {
|
||||
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Zabbix }>();
|
||||
const scope = watch("destinationConfig.scope");
|
||||
const hostId = watch("destinationConfig.hostId");
|
||||
const hostName = watch("destinationConfig.hostName");
|
||||
const macroType = watch("destinationConfig.macroType");
|
||||
|
||||
return (
|
||||
<>
|
||||
<GenericFieldLabel label="Scope">{scope}</GenericFieldLabel>
|
||||
{scope === ZabbixSyncScope.Host && (
|
||||
<>
|
||||
<GenericFieldLabel label="Host ID">{hostId}</GenericFieldLabel>
|
||||
<GenericFieldLabel label="Host Name">{hostName}</GenericFieldLabel>
|
||||
</>
|
||||
)}
|
||||
<GenericFieldLabel label="Macro Type">
|
||||
{isTextMacro(macroType) ? "Text" : "Secret"}
|
||||
</GenericFieldLabel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -22,6 +22,7 @@ import { TeamCitySyncDestinationSchema } from "./teamcity-sync-destination-schem
|
||||
import { TerraformCloudSyncDestinationSchema } from "./terraform-cloud-destination-schema";
|
||||
import { VercelSyncDestinationSchema } from "./vercel-sync-destination-schema";
|
||||
import { WindmillSyncDestinationSchema } from "./windmill-sync-destination-schema";
|
||||
import { ZabbixSyncDestinationSchema } from "./zabbix-sync-destination-schema";
|
||||
|
||||
const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
|
||||
AwsParameterStoreSyncDestinationSchema,
|
||||
@@ -45,7 +46,8 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
|
||||
RenderSyncDestinationSchema,
|
||||
FlyioSyncDestinationSchema,
|
||||
GitlabSyncDestinationSchema,
|
||||
CloudflarePagesSyncDestinationSchema
|
||||
CloudflarePagesSyncDestinationSchema,
|
||||
ZabbixSyncDestinationSchema
|
||||
]);
|
||||
|
||||
export const SecretSyncFormSchema = SecretSyncUnionSchema;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
|
||||
import { ZabbixMacroType, ZabbixSyncScope } from "@app/hooks/api/appConnections/zabbix";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
export const ZabbixSyncDestinationSchema = BaseSecretSyncSchema().merge(
|
||||
z.object({
|
||||
destination: z.literal(SecretSync.Zabbix),
|
||||
destinationConfig: z.discriminatedUnion("scope", [
|
||||
z.object({
|
||||
scope: z.literal(ZabbixSyncScope.Host),
|
||||
hostId: z.string().trim().min(1, "Host ID required"),
|
||||
hostName: z.string().trim().min(1, "Host name required"),
|
||||
macroType: z.nativeEnum(ZabbixMacroType, {
|
||||
errorMap: () => ({ message: "Macro type must be either 'text' or 'secret'" })
|
||||
})
|
||||
}),
|
||||
z.object({
|
||||
scope: z.literal(ZabbixSyncScope.Global),
|
||||
macroType: z.nativeEnum(ZabbixMacroType, {
|
||||
errorMap: () => ({ message: "Macro type must be either 'text' or 'secret'" })
|
||||
})
|
||||
})
|
||||
])
|
||||
})
|
||||
);
|
||||
@@ -37,7 +37,8 @@ import {
|
||||
TeamCityConnectionMethod,
|
||||
TerraformCloudConnectionMethod,
|
||||
VercelConnectionMethod,
|
||||
WindmillConnectionMethod
|
||||
WindmillConnectionMethod,
|
||||
ZabbixConnectionMethod
|
||||
} from "@app/hooks/api/appConnections/types";
|
||||
import { HerokuConnectionMethod } from "@app/hooks/api/appConnections/types/heroku-connection";
|
||||
import { OCIConnectionMethod } from "@app/hooks/api/appConnections/types/oci-connection";
|
||||
@@ -88,7 +89,8 @@ export const APP_CONNECTION_MAP: Record<
|
||||
[AppConnection.Render]: { name: "Render", image: "Render.png" },
|
||||
[AppConnection.Flyio]: { name: "Fly.io", image: "Flyio.svg" },
|
||||
[AppConnection.Gitlab]: { name: "GitLab", image: "GitLab.png" },
|
||||
[AppConnection.Cloudflare]: { name: "Cloudflare", image: "Cloudflare.png" }
|
||||
[AppConnection.Cloudflare]: { name: "Cloudflare", image: "Cloudflare.png" },
|
||||
[AppConnection.Zabbix]: { name: "Zabbix", image: "Zabbix.png" }
|
||||
};
|
||||
|
||||
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
|
||||
@@ -120,6 +122,7 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
|
||||
case VercelConnectionMethod.ApiToken:
|
||||
case OnePassConnectionMethod.ApiToken:
|
||||
case CloudflareConnectionMethod.ApiToken:
|
||||
case ZabbixConnectionMethod.ApiToken:
|
||||
return { name: "API Token", icon: faKey };
|
||||
case PostgresConnectionMethod.UsernameAndPassword:
|
||||
case MsSqlConnectionMethod.UsernameAndPassword:
|
||||
|
||||
@@ -81,6 +81,10 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
|
||||
[SecretSync.CloudflarePages]: {
|
||||
name: "Cloudflare Pages",
|
||||
image: "Cloudflare.png"
|
||||
},
|
||||
[SecretSync.Zabbix]: {
|
||||
name: "Zabbix",
|
||||
image: "Zabbix.png"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,7 +110,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.Render]: AppConnection.Render,
|
||||
[SecretSync.Flyio]: AppConnection.Flyio,
|
||||
[SecretSync.GitLab]: AppConnection.Gitlab,
|
||||
[SecretSync.CloudflarePages]: AppConnection.Cloudflare
|
||||
[SecretSync.CloudflarePages]: AppConnection.Cloudflare,
|
||||
[SecretSync.Zabbix]: AppConnection.Zabbix
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP: Record<
|
||||
|
||||
@@ -27,5 +27,6 @@ export enum AppConnection {
|
||||
Render = "render",
|
||||
Flyio = "flyio",
|
||||
Gitlab = "gitlab",
|
||||
Cloudflare = "cloudflare"
|
||||
Cloudflare = "cloudflare",
|
||||
Zabbix = "zabbix"
|
||||
}
|
||||
|
||||
@@ -132,6 +132,10 @@ export type TCloudflareConnectionOption = TAppConnectionOptionBase & {
|
||||
app: AppConnection.Cloudflare;
|
||||
};
|
||||
|
||||
export type TZabbixConnectionOption = TAppConnectionOptionBase & {
|
||||
app: AppConnection.Zabbix;
|
||||
};
|
||||
|
||||
export type TAppConnectionOption =
|
||||
| TAwsConnectionOption
|
||||
| TGitHubConnectionOption
|
||||
@@ -159,7 +163,8 @@ export type TAppConnectionOption =
|
||||
| TRenderConnectionOption
|
||||
| TFlyioConnectionOption
|
||||
| TGitlabConnectionOption
|
||||
| TCloudflareConnectionOption;
|
||||
| TCloudflareConnectionOption
|
||||
| TZabbixConnectionOption;
|
||||
|
||||
export type TAppConnectionOptionMap = {
|
||||
[AppConnection.AWS]: TAwsConnectionOption;
|
||||
@@ -191,4 +196,5 @@ export type TAppConnectionOptionMap = {
|
||||
[AppConnection.Flyio]: TFlyioConnectionOption;
|
||||
[AppConnection.Gitlab]: TGitlabConnectionOption;
|
||||
[AppConnection.Cloudflare]: TCloudflareConnectionOption;
|
||||
[AppConnection.Zabbix]: TZabbixConnectionOption;
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ import { TTeamCityConnection } from "./teamcity-connection";
|
||||
import { TTerraformCloudConnection } from "./terraform-cloud-connection";
|
||||
import { TVercelConnection } from "./vercel-connection";
|
||||
import { TWindmillConnection } from "./windmill-connection";
|
||||
import { TZabbixConnection } from "./zabbix-connection";
|
||||
|
||||
export * from "./1password-connection";
|
||||
export * from "./auth0-connection";
|
||||
@@ -59,6 +60,7 @@ export * from "./teamcity-connection";
|
||||
export * from "./terraform-cloud-connection";
|
||||
export * from "./vercel-connection";
|
||||
export * from "./windmill-connection";
|
||||
export * from "./zabbix-connection";
|
||||
|
||||
export type TAppConnection =
|
||||
| TAwsConnection
|
||||
@@ -89,7 +91,8 @@ export type TAppConnection =
|
||||
| TRenderConnection
|
||||
| TFlyioConnection
|
||||
| TGitLabConnection
|
||||
| TCloudflareConnection;
|
||||
| TCloudflareConnection
|
||||
| TZabbixConnection;
|
||||
|
||||
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
|
||||
|
||||
@@ -146,4 +149,5 @@ export type TAppConnectionMap = {
|
||||
[AppConnection.Flyio]: TFlyioConnection;
|
||||
[AppConnection.Gitlab]: TGitLabConnection;
|
||||
[AppConnection.Cloudflare]: TCloudflareConnection;
|
||||
[AppConnection.Zabbix]: TZabbixConnection;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
||||
|
||||
export enum ZabbixConnectionMethod {
|
||||
ApiToken = "api-token"
|
||||
}
|
||||
|
||||
export type TZabbixConnection = TRootAppConnection & { app: AppConnection.Zabbix } & {
|
||||
method: ZabbixConnectionMethod.ApiToken;
|
||||
credentials: {
|
||||
apiToken: string;
|
||||
instanceUrl: string;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./queries";
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { appConnectionKeys } from "../queries";
|
||||
import { TZabbixHost } from "./types";
|
||||
|
||||
const zabbixConnectionKeys = {
|
||||
all: [...appConnectionKeys.all, "zabbix"] as const,
|
||||
listHosts: (connectionId: string) => [...zabbixConnectionKeys.all, "hosts", connectionId] as const
|
||||
};
|
||||
|
||||
export const useZabbixConnectionListHosts = (
|
||||
connectionId: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TZabbixHost[],
|
||||
unknown,
|
||||
TZabbixHost[],
|
||||
ReturnType<typeof zabbixConnectionKeys.listHosts>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: zabbixConnectionKeys.listHosts(connectionId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TZabbixHost[]>(
|
||||
`/api/v1/app-connections/zabbix/${connectionId}/hosts`
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
export type TZabbixHost = {
|
||||
host: string;
|
||||
hostId: string;
|
||||
};
|
||||
|
||||
export enum ZabbixSyncScope {
|
||||
Host = "host",
|
||||
Global = "global"
|
||||
}
|
||||
|
||||
export enum ZabbixMacroType {
|
||||
Text = 0,
|
||||
Secret = 1
|
||||
}
|
||||
|
||||
export const ZABBIX_SYNC_SCOPES = {
|
||||
[ZabbixSyncScope.Host]: {
|
||||
name: "Host",
|
||||
description: "Sync secrets to a specific host in Zabbix."
|
||||
},
|
||||
[ZabbixSyncScope.Global]: {
|
||||
name: "Global",
|
||||
description: "Sync secrets to a global scope in Zabbix."
|
||||
}
|
||||
};
|
||||
@@ -20,7 +20,8 @@ export enum SecretSync {
|
||||
Render = "render",
|
||||
Flyio = "flyio",
|
||||
GitLab = "gitlab",
|
||||
CloudflarePages = "cloudflare-pages"
|
||||
CloudflarePages = "cloudflare-pages",
|
||||
Zabbix = "zabbix"
|
||||
}
|
||||
|
||||
export enum SecretSyncStatus {
|
||||
|
||||
@@ -23,6 +23,7 @@ import { TTeamCitySync } from "./teamcity-sync";
|
||||
import { TTerraformCloudSync } from "./terraform-cloud-sync";
|
||||
import { TVercelSync } from "./vercel-sync";
|
||||
import { TWindmillSync } from "./windmill-sync";
|
||||
import { TZabbixSync } from "./zabbix-sync";
|
||||
|
||||
export type TSecretSyncOption = {
|
||||
name: string;
|
||||
@@ -53,7 +54,8 @@ export type TSecretSync =
|
||||
| TRenderSync
|
||||
| TFlyioSync
|
||||
| TGitLabSync
|
||||
| TCloudflarePagesSync;
|
||||
| TCloudflarePagesSync
|
||||
| TZabbixSync;
|
||||
|
||||
export type TListSecretSyncs = { secretSyncs: TSecretSync[] };
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { TRootSecretSync } from "@app/hooks/api/secretSyncs/types/root-sync";
|
||||
|
||||
import { ZabbixSyncScope } from "../../appConnections/zabbix";
|
||||
|
||||
export type TZabbixSync = TRootSecretSync & {
|
||||
destination: SecretSync.Zabbix;
|
||||
destinationConfig:
|
||||
| {
|
||||
scope: ZabbixSyncScope.Host;
|
||||
hostId: string;
|
||||
hostName: string;
|
||||
macroType: number;
|
||||
}
|
||||
| {
|
||||
scope: ZabbixSyncScope.Global;
|
||||
macroType: number;
|
||||
};
|
||||
connection: {
|
||||
app: AppConnection.Zabbix;
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
+5
@@ -38,6 +38,7 @@ import { TeamCityConnectionForm } from "./TeamCityConnectionForm";
|
||||
import { TerraformCloudConnectionForm } from "./TerraformCloudConnectionForm";
|
||||
import { VercelConnectionForm } from "./VercelConnectionForm";
|
||||
import { WindmillConnectionForm } from "./WindmillConnectionForm";
|
||||
import { ZabbixConnectionForm } from "./ZabbixConnectionForm";
|
||||
|
||||
type FormProps = {
|
||||
onComplete: (appConnection: TAppConnection) => void;
|
||||
@@ -134,6 +135,8 @@ const CreateForm = ({ app, onComplete }: CreateFormProps) => {
|
||||
return <GitLabConnectionForm onSubmit={onSubmit} />;
|
||||
case AppConnection.Cloudflare:
|
||||
return <CloudflareConnectionForm onSubmit={onSubmit} />;
|
||||
case AppConnection.Zabbix:
|
||||
return <ZabbixConnectionForm onSubmit={onSubmit} />;
|
||||
default:
|
||||
throw new Error(`Unhandled App ${app}`);
|
||||
}
|
||||
@@ -228,6 +231,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
|
||||
return <GitLabConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
case AppConnection.Cloudflare:
|
||||
return <CloudflareConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
case AppConnection.Zabbix:
|
||||
return <ZabbixConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
default:
|
||||
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
|
||||
}
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Button, FormControl, Input, ModalClose, Select, SelectItem } from "@app/components/v2";
|
||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||
import { TZabbixConnection, ZabbixConnectionMethod } from "@app/hooks/api/appConnections";
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
|
||||
import {
|
||||
genericAppConnectionFieldsSchema,
|
||||
GenericAppConnectionsFields
|
||||
} from "./GenericAppConnectionFields";
|
||||
|
||||
type Props = {
|
||||
appConnection?: TZabbixConnection;
|
||||
onSubmit: (formData: FormData) => void;
|
||||
};
|
||||
|
||||
const rootSchema = genericAppConnectionFieldsSchema.extend({
|
||||
app: z.literal(AppConnection.Zabbix)
|
||||
});
|
||||
|
||||
const formSchema = z.discriminatedUnion("method", [
|
||||
rootSchema.extend({
|
||||
method: z.literal(ZabbixConnectionMethod.ApiToken),
|
||||
credentials: z.object({
|
||||
apiToken: z.string().trim().min(1, "API Token required"),
|
||||
instanceUrl: z.string().trim().url("Invalid instance URL")
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
type FormData = z.infer<typeof formSchema>;
|
||||
|
||||
export const ZabbixConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
const isUpdate = Boolean(appConnection);
|
||||
|
||||
const form = useForm<FormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: appConnection ?? {
|
||||
app: AppConnection.Zabbix,
|
||||
method: ZabbixConnectionMethod.ApiToken
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { isSubmitting, isDirty }
|
||||
} = form;
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
{!isUpdate && <GenericAppConnectionsFields />}
|
||||
<Controller
|
||||
name="method"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipText={`The method you would like to use to connect with ${
|
||||
APP_CONNECTION_MAP[AppConnection.Zabbix].name
|
||||
}. This field cannot be changed after creation.`}
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Method"
|
||||
>
|
||||
<Select
|
||||
isDisabled={isUpdate}
|
||||
value={value}
|
||||
onValueChange={(val) => onChange(val)}
|
||||
className="w-full border border-mineshaft-500"
|
||||
position="popper"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
>
|
||||
{Object.values(ZabbixConnectionMethod).map((method) => {
|
||||
return (
|
||||
<SelectItem value={method} key={method}>
|
||||
{getAppConnectionMethodDetails(method).name}{" "}
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="credentials.apiToken"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="API Token"
|
||||
>
|
||||
<Input {...field} type="password" placeholder="Enter your API Token" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="credentials.instanceUrl"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Instance URL"
|
||||
tooltipClassName="max-w-sm"
|
||||
>
|
||||
<Input {...field} placeholder="https://your-zabbix-instance.com" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
type="submit"
|
||||
colorSchema="secondary"
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting || !isDirty}
|
||||
>
|
||||
{isUpdate ? "Update Credentials" : "Connect to Zabbix"}
|
||||
</Button>
|
||||
<ModalClose asChild>
|
||||
<Button colorSchema="secondary" variant="plain">
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
+3
@@ -22,6 +22,7 @@ import { TeamCitySyncDestinationCol } from "./TeamCitySyncDestinationCol";
|
||||
import { TerraformCloudSyncDestinationCol } from "./TerraformCloudSyncDestinationCol";
|
||||
import { VercelSyncDestinationCol } from "./VercelSyncDestinationCol";
|
||||
import { WindmillSyncDestinationCol } from "./WindmillSyncDestinationCol";
|
||||
import { ZabbixSyncDestinationCol } from "./ZabbixSyncDestinationCol";
|
||||
|
||||
type Props = {
|
||||
secretSync: TSecretSync;
|
||||
@@ -73,6 +74,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
return <GitLabSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.CloudflarePages:
|
||||
return <CloudflarePagesSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.Zabbix:
|
||||
return <ZabbixSyncDestinationCol secretSync={secretSync} />;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled Secret Sync Destination Col: ${(secretSync as TSecretSync).destination}`
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { TZabbixSync } from "@app/hooks/api/secretSyncs/types/zabbix-sync";
|
||||
|
||||
import { getSecretSyncDestinationColValues } from "../helpers";
|
||||
import { SecretSyncTableCell } from "../SecretSyncTableCell";
|
||||
|
||||
type Props = {
|
||||
secretSync: TZabbixSync;
|
||||
};
|
||||
|
||||
export const ZabbixSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
|
||||
|
||||
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
|
||||
};
|
||||
+12
@@ -1,4 +1,5 @@
|
||||
import { TerraformCloudSyncScope } from "@app/hooks/api/appConnections/terraform-cloud";
|
||||
import { ZabbixSyncScope } from "@app/hooks/api/appConnections/zabbix";
|
||||
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { GcpSyncScope } from "@app/hooks/api/secretSyncs/types/gcp-sync";
|
||||
import {
|
||||
@@ -144,6 +145,17 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
|
||||
primaryText = destinationConfig.projectName;
|
||||
secondaryText = destinationConfig.environment;
|
||||
break;
|
||||
case SecretSync.Zabbix:
|
||||
if (destinationConfig.scope === ZabbixSyncScope.Host) {
|
||||
primaryText = destinationConfig.hostName;
|
||||
secondaryText = destinationConfig.hostId;
|
||||
} else if (destinationConfig.scope === ZabbixSyncScope.Global) {
|
||||
primaryText = "Global";
|
||||
secondaryText = "";
|
||||
} else {
|
||||
throw new Error(`Unhandled Zabbix Scope Destination Col Values ${destination}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Col Values ${destination}`);
|
||||
}
|
||||
|
||||
+4
@@ -33,6 +33,7 @@ import { TeamCitySyncDestinationSection } from "./TeamCitySyncDestinationSection
|
||||
import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestinationSection";
|
||||
import { VercelSyncDestinationSection } from "./VercelSyncDestinationSection";
|
||||
import { WindmillSyncDestinationSection } from "./WindmillSyncDestinationSection";
|
||||
import { ZabbixSyncDestinationSection } from "./ZabbixSyncDestinationSection";
|
||||
|
||||
type Props = {
|
||||
secretSync: TSecretSync;
|
||||
@@ -114,6 +115,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
|
||||
case SecretSync.CloudflarePages:
|
||||
DestinationComponents = <CloudflarePagesSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
case SecretSync.Zabbix:
|
||||
DestinationComponents = <ZabbixSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Section components: ${destination}`);
|
||||
}
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { ZabbixSyncScope } from "@app/hooks/api/appConnections/zabbix";
|
||||
import { TZabbixSync } from "@app/hooks/api/secretSyncs/types/zabbix-sync";
|
||||
|
||||
type Props = {
|
||||
secretSync: TZabbixSync;
|
||||
};
|
||||
|
||||
const isTextMacro = (macroType: number) => macroType === 0;
|
||||
|
||||
export const ZabbixSyncDestinationSection = ({ secretSync }: Props) => {
|
||||
const {
|
||||
destinationConfig: { macroType }
|
||||
} = secretSync;
|
||||
|
||||
return (
|
||||
<>
|
||||
{secretSync.destinationConfig.scope === ZabbixSyncScope.Host && (
|
||||
<>
|
||||
<GenericFieldLabel label="Host Name">
|
||||
{secretSync.destinationConfig.hostName}
|
||||
</GenericFieldLabel>
|
||||
<GenericFieldLabel label="Host ID">
|
||||
{secretSync.destinationConfig.hostId}
|
||||
</GenericFieldLabel>
|
||||
</>
|
||||
)}
|
||||
<GenericFieldLabel label="Macro Type">
|
||||
{isTextMacro(macroType) ? "Text" : "Secret"}
|
||||
</GenericFieldLabel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+1
@@ -60,6 +60,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
case SecretSync.Flyio:
|
||||
case SecretSync.GitLab:
|
||||
case SecretSync.CloudflarePages:
|
||||
case SecretSync.Zabbix:
|
||||
AdditionalSyncOptionsComponent = null;
|
||||
break;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user