mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 12:27:28 +00:00
feat: chef secret-sync
This commit is contained in:
@@ -2626,6 +2626,10 @@ export const SecretSyncs = {
|
|||||||
siteName: "The name of the Netlify site to sync secrets to.",
|
siteName: "The name of the Netlify site to sync secrets to.",
|
||||||
siteId: "The ID of the Netlify site to sync secrets to.",
|
siteId: "The ID of the Netlify site to sync secrets to.",
|
||||||
context: "The Netlify context to sync secrets to."
|
context: "The Netlify context to sync secrets to."
|
||||||
|
},
|
||||||
|
CHEF: {
|
||||||
|
dataBagName: "The name of the Chef data bag to sync secrets to.",
|
||||||
|
dataBagItemName: "The name of the Chef data bag item to sync secrets to."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
// import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
// import { readLimit } from "@app/server/config/rateLimiter";
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
// import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import {
|
import {
|
||||||
CreateChefConnectionSchema,
|
CreateChefConnectionSchema,
|
||||||
SanitizedChefConnectionSchema,
|
SanitizedChefConnectionSchema,
|
||||||
UpdateChefConnectionSchema
|
UpdateChefConnectionSchema
|
||||||
} from "@app/services/app-connection/chef";
|
} from "@app/services/app-connection/chef";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
// import { AuthMode } from "@app/services/auth/auth-type";
|
|
||||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||||
|
|
||||||
export const registerChefConnectionRouter = async (server: FastifyZodProvider) => {
|
export const registerChefConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
@@ -21,64 +21,65 @@ export const registerChefConnectionRouter = async (server: FastifyZodProvider) =
|
|||||||
updateSchema: UpdateChefConnectionSchema
|
updateSchema: UpdateChefConnectionSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
// server.route({
|
server.route({
|
||||||
// method: "GET",
|
method: "GET",
|
||||||
// url: `/:connectionId/data-bags`,
|
url: `/:connectionId/data-bags`,
|
||||||
// config: {
|
config: {
|
||||||
// rateLimit: readLimit
|
rateLimit: readLimit
|
||||||
// },
|
},
|
||||||
// schema: {
|
schema: {
|
||||||
// params: z.object({
|
params: z.object({
|
||||||
// connectionId: z.string().uuid()
|
connectionId: z.string().uuid()
|
||||||
// }),
|
}),
|
||||||
// response: {
|
response: {
|
||||||
// 200: z
|
200: z
|
||||||
// .object({
|
.object({
|
||||||
// id: z.string(),
|
name: z.string()
|
||||||
// name: z.string()
|
})
|
||||||
// })
|
.array()
|
||||||
// .array()
|
}
|
||||||
// }
|
},
|
||||||
// },
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
// onRequest: verifyAuth([AuthMode.JWT]),
|
handler: async (req) => {
|
||||||
// handler: async (req) => {
|
const { connectionId } = req.params;
|
||||||
// const { connectionId } = req.params;
|
const dataBags = await server.services.appConnection.chef.listDataBags(connectionId, req.permission);
|
||||||
// const dataBags = await server.services.appConnection.chef.listDataBags(connectionId, req.permission);
|
|
||||||
|
|
||||||
// return dataBags;
|
return dataBags;
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
|
|
||||||
// server.route({
|
server.route({
|
||||||
// method: "GET",
|
method: "GET",
|
||||||
// url: `/:connectionId/data-bags/:dataBagName/items`,
|
url: `/:connectionId/data-bag-items`,
|
||||||
// config: {
|
config: {
|
||||||
// rateLimit: readLimit
|
rateLimit: readLimit
|
||||||
// },
|
},
|
||||||
// schema: {
|
schema: {
|
||||||
// params: z.object({
|
params: z.object({
|
||||||
// connectionId: z.string().uuid(),
|
connectionId: z.string().uuid()
|
||||||
// dataBagName: z.string()
|
}),
|
||||||
// }),
|
querystring: z.object({
|
||||||
// response: {
|
dataBagName: z.string()
|
||||||
// 200: z
|
}),
|
||||||
// .object({
|
response: {
|
||||||
// id: z.string(),
|
200: z
|
||||||
// name: z.string()
|
.object({
|
||||||
// })
|
name: z.string()
|
||||||
// .array()
|
})
|
||||||
// }
|
.array()
|
||||||
// },
|
}
|
||||||
// onRequest: verifyAuth([AuthMode.JWT]),
|
},
|
||||||
// handler: async (req) => {
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
// const { connectionId, dataBagName } = req.params;
|
handler: async (req) => {
|
||||||
// const dataBagItems = await server.services.appConnection.chef.listDataBagItems(
|
const { connectionId } = req.params;
|
||||||
// connectionId,
|
const { dataBagName } = req.query;
|
||||||
// dataBagName,
|
const dataBagItems = await server.services.appConnection.chef.listDataBagItems(
|
||||||
// req.permission
|
connectionId,
|
||||||
// );
|
dataBagName,
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
|
||||||
// return dataBagItems;
|
return dataBagItems;
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { ChefSyncSchema, CreateChefSyncSchema, UpdateChefSyncSchema } from "@app/services/secret-sync/chef";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
|
||||||
|
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||||
|
|
||||||
|
export const registerChefSyncRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSyncSecretsEndpoints({
|
||||||
|
destination: SecretSync.Chef,
|
||||||
|
server,
|
||||||
|
responseSchema: ChefSyncSchema,
|
||||||
|
createSchema: CreateChefSyncSchema,
|
||||||
|
updateSchema: UpdateChefSyncSchema
|
||||||
|
});
|
||||||
@@ -10,6 +10,7 @@ import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
|
|||||||
import { registerBitbucketSyncRouter } from "./bitbucket-sync-router";
|
import { registerBitbucketSyncRouter } from "./bitbucket-sync-router";
|
||||||
import { registerCamundaSyncRouter } from "./camunda-sync-router";
|
import { registerCamundaSyncRouter } from "./camunda-sync-router";
|
||||||
import { registerChecklySyncRouter } from "./checkly-sync-router";
|
import { registerChecklySyncRouter } from "./checkly-sync-router";
|
||||||
|
import { registerChefSyncRouter } from "./chef-sync-router";
|
||||||
import { registerCloudflarePagesSyncRouter } from "./cloudflare-pages-sync-router";
|
import { registerCloudflarePagesSyncRouter } from "./cloudflare-pages-sync-router";
|
||||||
import { registerCloudflareWorkersSyncRouter } from "./cloudflare-workers-sync-router";
|
import { registerCloudflareWorkersSyncRouter } from "./cloudflare-workers-sync-router";
|
||||||
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
||||||
@@ -65,5 +66,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
|
|||||||
[SecretSync.DigitalOceanAppPlatform]: registerDigitalOceanAppPlatformSyncRouter,
|
[SecretSync.DigitalOceanAppPlatform]: registerDigitalOceanAppPlatformSyncRouter,
|
||||||
[SecretSync.Netlify]: registerNetlifySyncRouter,
|
[SecretSync.Netlify]: registerNetlifySyncRouter,
|
||||||
[SecretSync.Bitbucket]: registerBitbucketSyncRouter,
|
[SecretSync.Bitbucket]: registerBitbucketSyncRouter,
|
||||||
[SecretSync.LaravelForge]: registerLaravelForgeSyncRouter
|
[SecretSync.LaravelForge]: registerLaravelForgeSyncRouter,
|
||||||
|
[SecretSync.Chef]: registerChefSyncRouter
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/s
|
|||||||
import { BitbucketSyncListItemSchema, BitbucketSyncSchema } from "@app/services/secret-sync/bitbucket";
|
import { BitbucketSyncListItemSchema, BitbucketSyncSchema } from "@app/services/secret-sync/bitbucket";
|
||||||
import { CamundaSyncListItemSchema, CamundaSyncSchema } from "@app/services/secret-sync/camunda";
|
import { CamundaSyncListItemSchema, CamundaSyncSchema } from "@app/services/secret-sync/camunda";
|
||||||
import { ChecklySyncListItemSchema, ChecklySyncSchema } from "@app/services/secret-sync/checkly/checkly-sync-schemas";
|
import { ChecklySyncListItemSchema, ChecklySyncSchema } from "@app/services/secret-sync/checkly/checkly-sync-schemas";
|
||||||
|
import { ChefSyncListItemSchema, ChefSyncSchema } from "@app/services/secret-sync/chef";
|
||||||
import {
|
import {
|
||||||
CloudflarePagesSyncListItemSchema,
|
CloudflarePagesSyncListItemSchema,
|
||||||
CloudflarePagesSyncSchema
|
CloudflarePagesSyncSchema
|
||||||
@@ -86,7 +87,8 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
|
|||||||
DigitalOceanAppPlatformSyncSchema,
|
DigitalOceanAppPlatformSyncSchema,
|
||||||
NetlifySyncSchema,
|
NetlifySyncSchema,
|
||||||
BitbucketSyncSchema,
|
BitbucketSyncSchema,
|
||||||
LaravelForgeSyncSchema
|
LaravelForgeSyncSchema,
|
||||||
|
ChefSyncSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||||
@@ -120,7 +122,8 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
|||||||
SupabaseSyncListItemSchema,
|
SupabaseSyncListItemSchema,
|
||||||
NetlifySyncListItemSchema,
|
NetlifySyncListItemSchema,
|
||||||
BitbucketSyncListItemSchema,
|
BitbucketSyncListItemSchema,
|
||||||
LaravelForgeSyncListItemSchema
|
LaravelForgeSyncListItemSchema,
|
||||||
|
ChefSyncListItemSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import { removeTrailingSlash } from "@app/lib/fn";
|
|||||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
|
|
||||||
|
import { TChefDataBagItemContent } from "../../secret-sync/chef/chef-sync-types";
|
||||||
import { AppConnection } from "../app-connection-enums";
|
import { AppConnection } from "../app-connection-enums";
|
||||||
import { ChefConnectionMethod } from "./chef-connection-enums";
|
import { ChefConnectionMethod } from "./chef-connection-enums";
|
||||||
import { TChefConnection, TChefConnectionConfig, TChefDataBag } from "./chef-connection-types";
|
import { TChefConnection, TChefConnectionConfig, TChefDataBag, TChefDataBagItem } from "./chef-connection-types";
|
||||||
|
|
||||||
export const getChefServerUrl = async (serverUrl?: string) => {
|
export const getChefServerUrl = async (serverUrl?: string) => {
|
||||||
const chefServerUrl = serverUrl ? removeTrailingSlash(serverUrl) : IntegrationUrls.CHEF_API_URL;
|
const chefServerUrl = serverUrl ? removeTrailingSlash(serverUrl) : IntegrationUrls.CHEF_API_URL;
|
||||||
@@ -120,40 +121,6 @@ export const getChefConnectionListItem = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listChefDataBags = async (appConnection: TChefConnection): Promise<TChefDataBag[]> => {
|
|
||||||
const {
|
|
||||||
credentials: { serverUrl, userName, privateKey }
|
|
||||||
} = appConnection;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const path = "/data";
|
|
||||||
const body = "";
|
|
||||||
|
|
||||||
const hostServerUrl = await getChefServerUrl(serverUrl);
|
|
||||||
|
|
||||||
const headers = getChefAuthHeaders("GET", path, body, userName, privateKey);
|
|
||||||
|
|
||||||
const res = await request.get<Record<string, string>>(`${hostServerUrl}${path}`, {
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
|
|
||||||
// Chef returns data bags as an object with keys being data bag names
|
|
||||||
return Object.keys(res.data).map((name) => ({
|
|
||||||
name,
|
|
||||||
id: name
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof AxiosError) {
|
|
||||||
// throw new BadRequestError({
|
|
||||||
// message: `Failed to list Chef data bags: ${error.response?.data?.error || error.message}`
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Unable to list Chef data bags"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const validateChefConnectionCredentials = async (config: TChefConnectionConfig) => {
|
export const validateChefConnectionCredentials = async (config: TChefConnectionConfig) => {
|
||||||
const { credentials: inputCredentials } = config;
|
const { credentials: inputCredentials } = config;
|
||||||
|
|
||||||
@@ -181,16 +148,13 @@ export const validateChefConnectionCredentials = async (config: TChefConnectionC
|
|||||||
return inputCredentials;
|
return inputCredentials;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listChefDataBagItems = async (
|
export const listChefDataBags = async (appConnection: TChefConnection): Promise<TChefDataBag[]> => {
|
||||||
appConnection: TChefConnection,
|
|
||||||
dataBagName: string
|
|
||||||
): Promise<TChefDataBag[]> => {
|
|
||||||
const {
|
const {
|
||||||
credentials: { serverUrl, userName, privateKey }
|
credentials: { serverUrl, userName, privateKey, orgName }
|
||||||
} = appConnection;
|
} = appConnection;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const path = `/data/${dataBagName}`;
|
const path = `/organizations/${orgName}/data`;
|
||||||
const body = "";
|
const body = "";
|
||||||
|
|
||||||
const hostServerUrl = await getChefServerUrl(serverUrl);
|
const hostServerUrl = await getChefServerUrl(serverUrl);
|
||||||
@@ -201,10 +165,43 @@ export const listChefDataBagItems = async (
|
|||||||
headers
|
headers
|
||||||
});
|
});
|
||||||
|
|
||||||
// Chef returns data bag items as an object with keys being item names
|
|
||||||
return Object.keys(res.data).map((name) => ({
|
return Object.keys(res.data).map((name) => ({
|
||||||
name,
|
name
|
||||||
id: name
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Failed to list Chef data bags: ${error.message || "Unknown error"}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Unable to list Chef data bags"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listChefDataBagItems = async (
|
||||||
|
appConnection: TChefConnection,
|
||||||
|
dataBagName: string
|
||||||
|
): Promise<TChefDataBagItem[]> => {
|
||||||
|
const {
|
||||||
|
credentials: { serverUrl, userName, privateKey, orgName }
|
||||||
|
} = appConnection;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const path = `/organizations/${orgName}/data/${dataBagName}`;
|
||||||
|
const body = "";
|
||||||
|
|
||||||
|
const hostServerUrl = await getChefServerUrl(serverUrl);
|
||||||
|
|
||||||
|
const headers = getChefAuthHeaders("GET", path, body, userName, privateKey);
|
||||||
|
|
||||||
|
const res = await request.get<Record<string, string>>(`${hostServerUrl}${path}`, {
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.keys(res.data).map((name) => ({
|
||||||
|
name
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof AxiosError) {
|
if (error instanceof AxiosError) {
|
||||||
@@ -217,3 +214,87 @@ export const listChefDataBagItems = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TGetChefDataBagItem = {
|
||||||
|
serverUrl?: string;
|
||||||
|
userName: string;
|
||||||
|
privateKey: string;
|
||||||
|
orgName: string;
|
||||||
|
dataBagName: string;
|
||||||
|
dataBagItemName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getChefDataBagItem = async ({
|
||||||
|
serverUrl,
|
||||||
|
userName,
|
||||||
|
privateKey,
|
||||||
|
orgName,
|
||||||
|
dataBagName,
|
||||||
|
dataBagItemName
|
||||||
|
}: TGetChefDataBagItem): Promise<TChefDataBagItemContent> => {
|
||||||
|
try {
|
||||||
|
const path = `/organizations/${orgName}/data/${dataBagName}/${dataBagItemName}`;
|
||||||
|
const body = "";
|
||||||
|
|
||||||
|
const hostServerUrl = await getChefServerUrl(serverUrl);
|
||||||
|
|
||||||
|
const headers = getChefAuthHeaders("GET", path, body, userName, privateKey);
|
||||||
|
|
||||||
|
const res = await request.get<TChefDataBagItemContent>(`${hostServerUrl}${path}`, {
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Failed to get Chef data bag item: ${error.message || "Unknown error"}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Unable to get Chef data bag item"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type TUpdateChefDataBagItem = {
|
||||||
|
serverUrl?: string;
|
||||||
|
userName: string;
|
||||||
|
privateKey: string;
|
||||||
|
orgName: string;
|
||||||
|
dataBagName: string;
|
||||||
|
dataBagItemName: string;
|
||||||
|
data: TChefDataBagItemContent;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateChefDataBagItem = async ({
|
||||||
|
serverUrl,
|
||||||
|
userName,
|
||||||
|
privateKey,
|
||||||
|
orgName,
|
||||||
|
dataBagName,
|
||||||
|
dataBagItemName,
|
||||||
|
data
|
||||||
|
}: TUpdateChefDataBagItem): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const path = `/organizations/${orgName}/data/${dataBagName}/${dataBagItemName}`;
|
||||||
|
const body = JSON.stringify(data);
|
||||||
|
|
||||||
|
const hostServerUrl = await getChefServerUrl(serverUrl);
|
||||||
|
|
||||||
|
const headers = getChefAuthHeaders("PUT", path, body, userName, privateKey);
|
||||||
|
|
||||||
|
await request.put(`${hostServerUrl}${path}`, data, {
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Failed to update Chef data bag item: ${error.message || "Unknown error"}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Unable to update Chef data bag item"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -18,15 +18,13 @@ export type TChefConnectionInput = z.infer<typeof CreateChefConnectionSchema> &
|
|||||||
export type TValidateChefConnectionCredentialsSchema = typeof ValidateChefConnectionCredentialsSchema;
|
export type TValidateChefConnectionCredentialsSchema = typeof ValidateChefConnectionCredentialsSchema;
|
||||||
|
|
||||||
export type TChefConnectionConfig = DiscriminativePick<TChefConnectionInput, "method" | "app" | "credentials"> & {
|
export type TChefConnectionConfig = DiscriminativePick<TChefConnectionInput, "method" | "app" | "credentials"> & {
|
||||||
orgId: string;
|
orgName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TChefDataBag = {
|
export type TChefDataBag = {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TChefDataBagItem = {
|
export type TChefDataBagItem = {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
|
export const CHEF_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||||
|
name: "Chef",
|
||||||
|
destination: SecretSync.Chef,
|
||||||
|
connection: AppConnection.Chef,
|
||||||
|
canImportSecrets: true
|
||||||
|
};
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
import { getChefDataBagItem, updateChefDataBagItem } from "@app/services/app-connection/chef";
|
||||||
|
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||||
|
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
|
import { ChefSecret, TChefDataBagItemContent, TChefSyncWithCredentials, TGetChefSecrets } from "./chef-sync-types";
|
||||||
|
|
||||||
|
const getChefSecretsRaw = async ({
|
||||||
|
serverUrl,
|
||||||
|
userName,
|
||||||
|
privateKey,
|
||||||
|
orgName,
|
||||||
|
dataBagName,
|
||||||
|
dataBagItemName
|
||||||
|
}: TGetChefSecrets): Promise<TChefDataBagItemContent> => {
|
||||||
|
const dataBagItem = await getChefDataBagItem({
|
||||||
|
serverUrl,
|
||||||
|
userName,
|
||||||
|
privateKey,
|
||||||
|
orgName,
|
||||||
|
dataBagName,
|
||||||
|
dataBagItemName
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure the data bag item has an id field
|
||||||
|
if (!dataBagItem.id) {
|
||||||
|
dataBagItem.id = dataBagItemName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataBagItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getChefSecrets = async (secretSync: TChefSyncWithCredentials): Promise<ChefSecret[]> => {
|
||||||
|
const {
|
||||||
|
connection,
|
||||||
|
destinationConfig: { dataBagName, dataBagItemName }
|
||||||
|
} = secretSync;
|
||||||
|
|
||||||
|
const { serverUrl, userName, privateKey, orgName } = connection.credentials;
|
||||||
|
|
||||||
|
const dataBagItem = await getChefSecretsRaw({
|
||||||
|
serverUrl,
|
||||||
|
orgName,
|
||||||
|
userName,
|
||||||
|
privateKey,
|
||||||
|
dataBagName,
|
||||||
|
dataBagItemName
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert data bag item to key-value pairs
|
||||||
|
// Exclude the 'id' field as it's metadata
|
||||||
|
const secrets: ChefSecret[] = [];
|
||||||
|
Object.entries(dataBagItem).forEach(([key, value]) => {
|
||||||
|
if (key !== "id" && value !== null && value !== undefined) {
|
||||||
|
secrets.push({ key, value: String(value) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return secrets;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateChefSecrets = async (
|
||||||
|
secretSync: TChefSyncWithCredentials,
|
||||||
|
secrets: Record<string, string | number | boolean>
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
connection,
|
||||||
|
destinationConfig: { dataBagName, dataBagItemName }
|
||||||
|
} = secretSync;
|
||||||
|
|
||||||
|
const { serverUrl, userName, privateKey, orgName } = connection.credentials;
|
||||||
|
|
||||||
|
// Chef data bag items must have an 'id' field
|
||||||
|
const dataBagItemContent: TChefDataBagItemContent = {
|
||||||
|
id: dataBagItemName,
|
||||||
|
...secrets
|
||||||
|
};
|
||||||
|
|
||||||
|
await updateChefDataBagItem({
|
||||||
|
serverUrl,
|
||||||
|
orgName,
|
||||||
|
userName,
|
||||||
|
privateKey,
|
||||||
|
dataBagName,
|
||||||
|
dataBagItemName,
|
||||||
|
data: dataBagItemContent
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ChefSyncFns = {
|
||||||
|
async syncSecrets(secretSync: TChefSyncWithCredentials, secretMap: TSecretMap) {
|
||||||
|
const {
|
||||||
|
environment,
|
||||||
|
syncOptions: { disableSecretDeletion, keySchema }
|
||||||
|
} = secretSync;
|
||||||
|
|
||||||
|
const secrets = await getChefSecrets(secretSync);
|
||||||
|
|
||||||
|
// Create a map of the existing secrets
|
||||||
|
const updatedSecretsMap = new Map(secrets.map((secret) => [secret.key, secret.value]));
|
||||||
|
|
||||||
|
// Add/update new secrets
|
||||||
|
for (const [key, { value }] of Object.entries(secretMap)) {
|
||||||
|
updatedSecretsMap.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete secrets if not disabled
|
||||||
|
if (!disableSecretDeletion) {
|
||||||
|
secrets.forEach((secret) => {
|
||||||
|
if (!matchesSchema(secret.key, environment?.slug || "", keySchema)) return;
|
||||||
|
|
||||||
|
if (!secretMap[secret.key]) {
|
||||||
|
updatedSecretsMap.delete(secret.key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert map to object for Chef API
|
||||||
|
const updatedSecrets = Object.fromEntries(updatedSecretsMap.entries());
|
||||||
|
|
||||||
|
await updateChefSecrets(secretSync, updatedSecrets);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getSecrets(secretSync: TChefSyncWithCredentials): Promise<TSecretMap> {
|
||||||
|
const secrets = await getChefSecrets(secretSync);
|
||||||
|
return Object.fromEntries(secrets.map((secret) => [secret.key, { value: secret.value }]));
|
||||||
|
},
|
||||||
|
|
||||||
|
async removeSecrets(secretSync: TChefSyncWithCredentials, secretMap: TSecretMap) {
|
||||||
|
const existingSecrets = await getChefSecrets(secretSync);
|
||||||
|
|
||||||
|
const newSecrets = existingSecrets.filter((secret) => !Object.hasOwn(secretMap, secret.key));
|
||||||
|
|
||||||
|
// If nothing changed, return early
|
||||||
|
if (newSecrets.length === existingSecrets.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to object for Chef API
|
||||||
|
const updatedSecrets = Object.fromEntries(newSecrets.map((secret) => [secret.key, secret.value]));
|
||||||
|
|
||||||
|
await updateChefSecrets(secretSync, updatedSecrets);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { SecretSyncs } from "@app/lib/api-docs";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
import {
|
||||||
|
BaseSecretSyncSchema,
|
||||||
|
GenericCreateSecretSyncFieldsSchema,
|
||||||
|
GenericUpdateSecretSyncFieldsSchema
|
||||||
|
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||||
|
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
|
const ChefSyncDestinationConfigSchema = z.object({
|
||||||
|
dataBagName: z
|
||||||
|
.string()
|
||||||
|
.min(1, "Data Bag Name is required")
|
||||||
|
.max(256, "Data Bag Name cannot exceed 256 characters")
|
||||||
|
.describe(SecretSyncs.DESTINATION_CONFIG.CHEF.dataBagName),
|
||||||
|
dataBagItemName: z
|
||||||
|
.string()
|
||||||
|
.min(1, "Data Bag Item Name is required")
|
||||||
|
.max(256, "Data Bag Item Name cannot exceed 256 characters")
|
||||||
|
.describe(SecretSyncs.DESTINATION_CONFIG.CHEF.dataBagItemName)
|
||||||
|
});
|
||||||
|
|
||||||
|
const ChefSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
|
||||||
|
|
||||||
|
export const ChefSyncSchema = BaseSecretSyncSchema(SecretSync.Chef, ChefSyncOptionsConfig).extend({
|
||||||
|
destination: z.literal(SecretSync.Chef),
|
||||||
|
destinationConfig: ChefSyncDestinationConfigSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateChefSyncSchema = GenericCreateSecretSyncFieldsSchema(SecretSync.Chef, ChefSyncOptionsConfig).extend({
|
||||||
|
destinationConfig: ChefSyncDestinationConfigSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateChefSyncSchema = GenericUpdateSecretSyncFieldsSchema(SecretSync.Chef, ChefSyncOptionsConfig).extend({
|
||||||
|
destinationConfig: ChefSyncDestinationConfigSchema.optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChefSyncListItemSchema = z.object({
|
||||||
|
name: z.literal("Chef"),
|
||||||
|
connection: z.literal(AppConnection.Chef),
|
||||||
|
destination: z.literal(SecretSync.Chef),
|
||||||
|
canImportSecrets: z.literal(true)
|
||||||
|
});
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { TChefConnection } from "@app/services/app-connection/chef";
|
||||||
|
|
||||||
|
import { ChefSyncListItemSchema, ChefSyncSchema, CreateChefSyncSchema } from "./chef-sync-schemas";
|
||||||
|
|
||||||
|
export type TChefSyncListItem = z.infer<typeof ChefSyncListItemSchema>;
|
||||||
|
|
||||||
|
export type TChefSync = z.infer<typeof ChefSyncSchema>;
|
||||||
|
|
||||||
|
export type TChefSyncInput = z.infer<typeof CreateChefSyncSchema>;
|
||||||
|
|
||||||
|
export type TChefSyncWithCredentials = TChefSync & {
|
||||||
|
connection: TChefConnection;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetChefSecrets = {
|
||||||
|
serverUrl?: string;
|
||||||
|
userName: string;
|
||||||
|
privateKey: string;
|
||||||
|
orgName: string;
|
||||||
|
dataBagName: string;
|
||||||
|
dataBagItemName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TChefDataBagItemContent = {
|
||||||
|
id: string;
|
||||||
|
[key: string]: string | number | boolean | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ChefSecret = {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export * from "./chef-sync-constants";
|
||||||
|
export * from "./chef-sync-fns";
|
||||||
|
export * from "./chef-sync-schemas";
|
||||||
|
export * from "./chef-sync-types";
|
||||||
@@ -29,7 +29,8 @@ export enum SecretSync {
|
|||||||
DigitalOceanAppPlatform = "digital-ocean-app-platform",
|
DigitalOceanAppPlatform = "digital-ocean-app-platform",
|
||||||
Netlify = "netlify",
|
Netlify = "netlify",
|
||||||
Bitbucket = "bitbucket",
|
Bitbucket = "bitbucket",
|
||||||
LaravelForge = "laravel-forge"
|
LaravelForge = "laravel-forge",
|
||||||
|
Chef = "chef"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SecretSyncInitialSyncBehavior {
|
export enum SecretSyncInitialSyncBehavior {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { BITBUCKET_SYNC_LIST_OPTION, BitbucketSyncFns } from "./bitbucket";
|
|||||||
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
|
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
|
||||||
import { CHECKLY_SYNC_LIST_OPTION } from "./checkly/checkly-sync-constants";
|
import { CHECKLY_SYNC_LIST_OPTION } from "./checkly/checkly-sync-constants";
|
||||||
import { ChecklySyncFns } from "./checkly/checkly-sync-fns";
|
import { ChecklySyncFns } from "./checkly/checkly-sync-fns";
|
||||||
|
import { CHEF_SYNC_LIST_OPTION, ChefSyncFns } from "./chef";
|
||||||
import { CLOUDFLARE_PAGES_SYNC_LIST_OPTION } from "./cloudflare-pages/cloudflare-pages-constants";
|
import { CLOUDFLARE_PAGES_SYNC_LIST_OPTION } from "./cloudflare-pages/cloudflare-pages-constants";
|
||||||
import { CloudflarePagesSyncFns } from "./cloudflare-pages/cloudflare-pages-fns";
|
import { CloudflarePagesSyncFns } from "./cloudflare-pages/cloudflare-pages-fns";
|
||||||
import { CLOUDFLARE_WORKERS_SYNC_LIST_OPTION, CloudflareWorkersSyncFns } from "./cloudflare-workers";
|
import { CLOUDFLARE_WORKERS_SYNC_LIST_OPTION, CloudflareWorkersSyncFns } from "./cloudflare-workers";
|
||||||
@@ -49,8 +50,7 @@ import { HC_VAULT_SYNC_LIST_OPTION, HCVaultSyncFns } from "./hc-vault";
|
|||||||
import { HEROKU_SYNC_LIST_OPTION, HerokuSyncFns } from "./heroku";
|
import { HEROKU_SYNC_LIST_OPTION, HerokuSyncFns } from "./heroku";
|
||||||
import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec";
|
import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec";
|
||||||
import { HumanitecSyncFns } from "./humanitec/humanitec-sync-fns";
|
import { HumanitecSyncFns } from "./humanitec/humanitec-sync-fns";
|
||||||
import { LARAVEL_FORGE_SYNC_LIST_OPTION } from "./laravel-forge";
|
import { LARAVEL_FORGE_SYNC_LIST_OPTION, LaravelForgeSyncFns } from "./laravel-forge";
|
||||||
import { LaravelForgeSyncFns } from "./laravel-forge/laravel-forge-sync-fns";
|
|
||||||
import { NETLIFY_SYNC_LIST_OPTION, NetlifySyncFns } from "./netlify";
|
import { NETLIFY_SYNC_LIST_OPTION, NetlifySyncFns } from "./netlify";
|
||||||
import { RAILWAY_SYNC_LIST_OPTION } from "./railway/railway-sync-constants";
|
import { RAILWAY_SYNC_LIST_OPTION } from "./railway/railway-sync-constants";
|
||||||
import { RailwaySyncFns } from "./railway/railway-sync-fns";
|
import { RailwaySyncFns } from "./railway/railway-sync-fns";
|
||||||
@@ -94,7 +94,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
|||||||
[SecretSync.DigitalOceanAppPlatform]: DIGITAL_OCEAN_APP_PLATFORM_SYNC_LIST_OPTION,
|
[SecretSync.DigitalOceanAppPlatform]: DIGITAL_OCEAN_APP_PLATFORM_SYNC_LIST_OPTION,
|
||||||
[SecretSync.Netlify]: NETLIFY_SYNC_LIST_OPTION,
|
[SecretSync.Netlify]: NETLIFY_SYNC_LIST_OPTION,
|
||||||
[SecretSync.Bitbucket]: BITBUCKET_SYNC_LIST_OPTION,
|
[SecretSync.Bitbucket]: BITBUCKET_SYNC_LIST_OPTION,
|
||||||
[SecretSync.LaravelForge]: LARAVEL_FORGE_SYNC_LIST_OPTION
|
[SecretSync.LaravelForge]: LARAVEL_FORGE_SYNC_LIST_OPTION,
|
||||||
|
[SecretSync.Chef]: CHEF_SYNC_LIST_OPTION
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listSecretSyncOptions = () => {
|
export const listSecretSyncOptions = () => {
|
||||||
@@ -282,6 +283,8 @@ export const SecretSyncFns = {
|
|||||||
return BitbucketSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
return BitbucketSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
return LaravelForgeSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
return LaravelForgeSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||||
|
case SecretSync.Chef:
|
||||||
|
return ChefSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||||
@@ -401,6 +404,9 @@ export const SecretSyncFns = {
|
|||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
secretMap = await LaravelForgeSyncFns.getSecrets(secretSync);
|
secretMap = await LaravelForgeSyncFns.getSecrets(secretSync);
|
||||||
break;
|
break;
|
||||||
|
case SecretSync.Chef:
|
||||||
|
secretMap = await ChefSyncFns.getSecrets(secretSync);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||||
@@ -496,6 +502,8 @@ export const SecretSyncFns = {
|
|||||||
return BitbucketSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
return BitbucketSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
return LaravelForgeSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
return LaravelForgeSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||||
|
case SecretSync.Chef:
|
||||||
|
return ChefSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
|||||||
[SecretSync.DigitalOceanAppPlatform]: "Digital Ocean App Platform",
|
[SecretSync.DigitalOceanAppPlatform]: "Digital Ocean App Platform",
|
||||||
[SecretSync.Netlify]: "Netlify",
|
[SecretSync.Netlify]: "Netlify",
|
||||||
[SecretSync.Bitbucket]: "Bitbucket",
|
[SecretSync.Bitbucket]: "Bitbucket",
|
||||||
[SecretSync.LaravelForge]: "Laravel Forge"
|
[SecretSync.LaravelForge]: "Laravel Forge",
|
||||||
|
[SecretSync.Chef]: "Chef"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||||
@@ -67,7 +68,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
|||||||
[SecretSync.DigitalOceanAppPlatform]: AppConnection.DigitalOcean,
|
[SecretSync.DigitalOceanAppPlatform]: AppConnection.DigitalOcean,
|
||||||
[SecretSync.Netlify]: AppConnection.Netlify,
|
[SecretSync.Netlify]: AppConnection.Netlify,
|
||||||
[SecretSync.Bitbucket]: AppConnection.Bitbucket,
|
[SecretSync.Bitbucket]: AppConnection.Bitbucket,
|
||||||
[SecretSync.LaravelForge]: AppConnection.LaravelForge
|
[SecretSync.LaravelForge]: AppConnection.LaravelForge,
|
||||||
|
[SecretSync.Chef]: AppConnection.Chef
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||||
@@ -101,7 +103,8 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
|||||||
[SecretSync.DigitalOceanAppPlatform]: SecretSyncPlanType.Regular,
|
[SecretSync.DigitalOceanAppPlatform]: SecretSyncPlanType.Regular,
|
||||||
[SecretSync.Netlify]: SecretSyncPlanType.Regular,
|
[SecretSync.Netlify]: SecretSyncPlanType.Regular,
|
||||||
[SecretSync.Bitbucket]: SecretSyncPlanType.Regular,
|
[SecretSync.Bitbucket]: SecretSyncPlanType.Regular,
|
||||||
[SecretSync.LaravelForge]: SecretSyncPlanType.Regular
|
[SecretSync.LaravelForge]: SecretSyncPlanType.Regular,
|
||||||
|
[SecretSync.Chef]: SecretSyncPlanType.Regular
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SECRET_SYNC_SKIP_FIELDS_MAP: Record<SecretSync, string[]> = {
|
export const SECRET_SYNC_SKIP_FIELDS_MAP: Record<SecretSync, string[]> = {
|
||||||
@@ -144,7 +147,8 @@ export const SECRET_SYNC_SKIP_FIELDS_MAP: Record<SecretSync, string[]> = {
|
|||||||
[SecretSync.DigitalOceanAppPlatform]: ["appName"],
|
[SecretSync.DigitalOceanAppPlatform]: ["appName"],
|
||||||
[SecretSync.Netlify]: ["accountName", "siteName"],
|
[SecretSync.Netlify]: ["accountName", "siteName"],
|
||||||
[SecretSync.Bitbucket]: [],
|
[SecretSync.Bitbucket]: [],
|
||||||
[SecretSync.LaravelForge]: []
|
[SecretSync.LaravelForge]: [],
|
||||||
|
[SecretSync.Chef]: []
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultDuplicateCheck: DestinationDuplicateCheckFn = () => true;
|
const defaultDuplicateCheck: DestinationDuplicateCheckFn = () => true;
|
||||||
@@ -204,5 +208,6 @@ export const DESTINATION_DUPLICATE_CHECK_MAP: Record<SecretSync, DestinationDupl
|
|||||||
[SecretSync.DigitalOceanAppPlatform]: defaultDuplicateCheck,
|
[SecretSync.DigitalOceanAppPlatform]: defaultDuplicateCheck,
|
||||||
[SecretSync.Netlify]: defaultDuplicateCheck,
|
[SecretSync.Netlify]: defaultDuplicateCheck,
|
||||||
[SecretSync.Bitbucket]: defaultDuplicateCheck,
|
[SecretSync.Bitbucket]: defaultDuplicateCheck,
|
||||||
[SecretSync.LaravelForge]: defaultDuplicateCheck
|
[SecretSync.LaravelForge]: defaultDuplicateCheck,
|
||||||
|
[SecretSync.Chef]: defaultDuplicateCheck
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
TCamundaSyncListItem,
|
TCamundaSyncListItem,
|
||||||
TCamundaSyncWithCredentials
|
TCamundaSyncWithCredentials
|
||||||
} from "@app/services/secret-sync/camunda";
|
} from "@app/services/secret-sync/camunda";
|
||||||
|
import { TChefSync, TChefSyncInput, TChefSyncListItem, TChefSyncWithCredentials } from "@app/services/secret-sync/chef";
|
||||||
import {
|
import {
|
||||||
TDatabricksSync,
|
TDatabricksSync,
|
||||||
TDatabricksSyncInput,
|
TDatabricksSyncInput,
|
||||||
@@ -163,6 +164,7 @@ export type TSecretSync =
|
|||||||
| TGitHubSync
|
| TGitHubSync
|
||||||
| TGcpSync
|
| TGcpSync
|
||||||
| TAzureKeyVaultSync
|
| TAzureKeyVaultSync
|
||||||
|
| TChefSync
|
||||||
| TAzureAppConfigurationSync
|
| TAzureAppConfigurationSync
|
||||||
| TAzureDevOpsSync
|
| TAzureDevOpsSync
|
||||||
| TDatabricksSync
|
| TDatabricksSync
|
||||||
@@ -195,6 +197,7 @@ export type TSecretSyncWithCredentials =
|
|||||||
| TGitHubSyncWithCredentials
|
| TGitHubSyncWithCredentials
|
||||||
| TGcpSyncWithCredentials
|
| TGcpSyncWithCredentials
|
||||||
| TAzureKeyVaultSyncWithCredentials
|
| TAzureKeyVaultSyncWithCredentials
|
||||||
|
| TChefSyncWithCredentials
|
||||||
| TAzureAppConfigurationSyncWithCredentials
|
| TAzureAppConfigurationSyncWithCredentials
|
||||||
| TAzureDevOpsSyncWithCredentials
|
| TAzureDevOpsSyncWithCredentials
|
||||||
| TDatabricksSyncWithCredentials
|
| TDatabricksSyncWithCredentials
|
||||||
@@ -228,6 +231,7 @@ export type TSecretSyncInput =
|
|||||||
| TGitHubSyncInput
|
| TGitHubSyncInput
|
||||||
| TGcpSyncInput
|
| TGcpSyncInput
|
||||||
| TAzureKeyVaultSyncInput
|
| TAzureKeyVaultSyncInput
|
||||||
|
| TChefSyncInput
|
||||||
| TAzureAppConfigurationSyncInput
|
| TAzureAppConfigurationSyncInput
|
||||||
| TAzureDevOpsSyncInput
|
| TAzureDevOpsSyncInput
|
||||||
| TDatabricksSyncInput
|
| TDatabricksSyncInput
|
||||||
@@ -261,6 +265,7 @@ export type TSecretSyncListItem =
|
|||||||
| TGitHubSyncListItem
|
| TGitHubSyncListItem
|
||||||
| TGcpSyncListItem
|
| TGcpSyncListItem
|
||||||
| TAzureKeyVaultSyncListItem
|
| TAzureKeyVaultSyncListItem
|
||||||
|
| TChefSyncListItem
|
||||||
| TAzureAppConfigurationSyncListItem
|
| TAzureAppConfigurationSyncListItem
|
||||||
| TAzureDevOpsSyncListItem
|
| TAzureDevOpsSyncListItem
|
||||||
| TDatabricksSyncListItem
|
| TDatabricksSyncListItem
|
||||||
|
|||||||
+97
@@ -0,0 +1,97 @@
|
|||||||
|
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 } from "@app/components/v2";
|
||||||
|
import {
|
||||||
|
TChefDataBag,
|
||||||
|
useChefConnectionListDataBags,
|
||||||
|
useChefConnectionListDataBagItems,
|
||||||
|
TChefDataBagItem
|
||||||
|
} from "@app/hooks/api/appConnections/chef";
|
||||||
|
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||||
|
|
||||||
|
import { TSecretSyncForm } from "../schemas";
|
||||||
|
|
||||||
|
export const ChefSyncFields = () => {
|
||||||
|
const { control, setValue } = useFormContext<
|
||||||
|
TSecretSyncForm & { destination: SecretSync.Chef }
|
||||||
|
>();
|
||||||
|
|
||||||
|
const connectionId = useWatch({ name: "connection.id", control });
|
||||||
|
const dataBagName = useWatch({ name: "destinationConfig.dataBagName", control });
|
||||||
|
const dataBagItemName = useWatch({ name: "destinationConfig.dataBagItemName", control });
|
||||||
|
|
||||||
|
const { data: dataBags, isLoading: isDataBagsLoading } = useChefConnectionListDataBags(
|
||||||
|
connectionId,
|
||||||
|
{
|
||||||
|
enabled: Boolean(connectionId)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: dataBagItems, isLoading: isDataBagItemsLoading } =
|
||||||
|
useChefConnectionListDataBagItems(connectionId, dataBagName, {
|
||||||
|
enabled: Boolean(connectionId && dataBagName)
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log({ dataBags, dataBagItems });
|
||||||
|
|
||||||
|
const handleChangeConnection = () => {
|
||||||
|
setValue("destinationConfig.dataBagName", "");
|
||||||
|
setValue("destinationConfig.dataBagItemName", "");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SecretSyncConnectionField onChange={handleChangeConnection} />
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
name="destinationConfig.dataBagName"
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message} label="Data Bag">
|
||||||
|
<FilterableSelect
|
||||||
|
menuPlacement="top"
|
||||||
|
isLoading={isDataBagsLoading && Boolean(connectionId)}
|
||||||
|
isDisabled={!connectionId}
|
||||||
|
value={dataBags?.find((dataBag) => dataBag.name === value) ?? null}
|
||||||
|
onChange={(option) => {
|
||||||
|
const selectedDataBag = option as SingleValue<TChefDataBag>;
|
||||||
|
onChange(selectedDataBag?.name ?? "");
|
||||||
|
setValue("destinationConfig.dataBagItemName", "");
|
||||||
|
}}
|
||||||
|
options={dataBags}
|
||||||
|
placeholder="Select a data bag..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.name}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
name="destinationConfig.dataBagItemName"
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message} label="Data Bag Item">
|
||||||
|
<FilterableSelect
|
||||||
|
menuPlacement="top"
|
||||||
|
isLoading={isDataBagItemsLoading && Boolean(connectionId && dataBagName)}
|
||||||
|
isDisabled={!connectionId || !dataBagName}
|
||||||
|
value={dataBagItems?.find((dataBagItem) => dataBagItem.name === value) ?? null}
|
||||||
|
onChange={(option) => {
|
||||||
|
const selectedDataBagItem = option as SingleValue<TChefDataBagItem>;
|
||||||
|
onChange(selectedDataBagItem?.name ?? "");
|
||||||
|
setValue("destinationConfig.dataBagItemName", selectedDataBagItem?.name ?? "");
|
||||||
|
}}
|
||||||
|
options={dataBagItems}
|
||||||
|
placeholder="Select a data bag item..."
|
||||||
|
getOptionLabel={(option) => option.name}
|
||||||
|
getOptionValue={(option) => option.name}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
+3
@@ -34,6 +34,7 @@ import { TerraformCloudSyncFields } from "./TerraformCloudSyncFields";
|
|||||||
import { VercelSyncFields } from "./VercelSyncFields";
|
import { VercelSyncFields } from "./VercelSyncFields";
|
||||||
import { WindmillSyncFields } from "./WindmillSyncFields";
|
import { WindmillSyncFields } from "./WindmillSyncFields";
|
||||||
import { ZabbixSyncFields } from "./ZabbixSyncFields";
|
import { ZabbixSyncFields } from "./ZabbixSyncFields";
|
||||||
|
import { ChefSyncFields } from "./ChefSyncFields";
|
||||||
|
|
||||||
export const SecretSyncDestinationFields = () => {
|
export const SecretSyncDestinationFields = () => {
|
||||||
const { watch } = useFormContext<TSecretSyncForm>();
|
const { watch } = useFormContext<TSecretSyncForm>();
|
||||||
@@ -103,6 +104,8 @@ export const SecretSyncDestinationFields = () => {
|
|||||||
return <BitbucketSyncFields />;
|
return <BitbucketSyncFields />;
|
||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
return <LaravelForgeSyncFields />;
|
return <LaravelForgeSyncFields />;
|
||||||
|
case SecretSync.Chef:
|
||||||
|
return <ChefSyncFields />;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled Destination Config Field: ${destination}`);
|
throw new Error(`Unhandled Destination Config Field: ${destination}`);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -70,6 +70,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
|
|||||||
case SecretSync.Netlify:
|
case SecretSync.Netlify:
|
||||||
case SecretSync.Bitbucket:
|
case SecretSync.Bitbucket:
|
||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
|
case SecretSync.Chef:
|
||||||
AdditionalSyncOptionsFieldsComponent = null;
|
AdditionalSyncOptionsFieldsComponent = null;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
|
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||||
|
import { GenericFieldLabel } from "@app/components/v2";
|
||||||
|
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||||
|
|
||||||
|
export const ChefSyncReviewFields = () => {
|
||||||
|
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Chef }>();
|
||||||
|
const dataBagName = watch("destinationConfig.dataBagName");
|
||||||
|
const dataBagItemName = watch("destinationConfig.dataBagItemName");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GenericFieldLabel label="Data Bag">{dataBagName}</GenericFieldLabel>
|
||||||
|
<GenericFieldLabel label="Data Bag Item">{dataBagItemName}</GenericFieldLabel>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
+4
@@ -47,6 +47,7 @@ import { TerraformCloudSyncReviewFields } from "./TerraformCloudSyncReviewFields
|
|||||||
import { VercelSyncReviewFields } from "./VercelSyncReviewFields";
|
import { VercelSyncReviewFields } from "./VercelSyncReviewFields";
|
||||||
import { WindmillSyncReviewFields } from "./WindmillSyncReviewFields";
|
import { WindmillSyncReviewFields } from "./WindmillSyncReviewFields";
|
||||||
import { ZabbixSyncReviewFields } from "./ZabbixSyncReviewFields";
|
import { ZabbixSyncReviewFields } from "./ZabbixSyncReviewFields";
|
||||||
|
import { ChefSyncReviewFields } from "./ChefSyncReviewFields";
|
||||||
|
|
||||||
export const SecretSyncReviewFields = () => {
|
export const SecretSyncReviewFields = () => {
|
||||||
const { watch } = useFormContext<TSecretSyncForm>();
|
const { watch } = useFormContext<TSecretSyncForm>();
|
||||||
@@ -173,6 +174,9 @@ export const SecretSyncReviewFields = () => {
|
|||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
DestinationFieldsComponent = <LaravelForgeSyncReviewFields />;
|
DestinationFieldsComponent = <LaravelForgeSyncReviewFields />;
|
||||||
break;
|
break;
|
||||||
|
case SecretSync.Chef:
|
||||||
|
DestinationFieldsComponent = <ChefSyncReviewFields />;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled Destination Review Fields: ${destination}`);
|
throw new Error(`Unhandled Destination Review Fields: ${destination}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
|
||||||
|
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||||
|
|
||||||
|
export const ChefSyncDestinationSchema = BaseSecretSyncSchema().merge(
|
||||||
|
z.object({
|
||||||
|
destination: z.literal(SecretSync.Chef),
|
||||||
|
destinationConfig: z.object({
|
||||||
|
dataBagName: z.string().trim().min(1, "Data Bag Name required"),
|
||||||
|
dataBagItemName: z.string().trim().min(1, "Data Bag Item Name required")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
);
|
||||||
@@ -31,6 +31,7 @@ import { TerraformCloudSyncDestinationSchema } from "./terraform-cloud-destinati
|
|||||||
import { VercelSyncDestinationSchema } from "./vercel-sync-destination-schema";
|
import { VercelSyncDestinationSchema } from "./vercel-sync-destination-schema";
|
||||||
import { WindmillSyncDestinationSchema } from "./windmill-sync-destination-schema";
|
import { WindmillSyncDestinationSchema } from "./windmill-sync-destination-schema";
|
||||||
import { ZabbixSyncDestinationSchema } from "./zabbix-sync-destination-schema";
|
import { ZabbixSyncDestinationSchema } from "./zabbix-sync-destination-schema";
|
||||||
|
import { ChefSyncDestinationSchema } from "./chef-sync-destination-schema";
|
||||||
|
|
||||||
const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
|
const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
|
||||||
AwsParameterStoreSyncDestinationSchema,
|
AwsParameterStoreSyncDestinationSchema,
|
||||||
@@ -63,7 +64,8 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
|
|||||||
DigitalOceanAppPlatformSyncDestinationSchema,
|
DigitalOceanAppPlatformSyncDestinationSchema,
|
||||||
NetlifySyncDestinationSchema,
|
NetlifySyncDestinationSchema,
|
||||||
BitbucketSyncDestinationSchema,
|
BitbucketSyncDestinationSchema,
|
||||||
LaravelForgeSyncDestinationSchema
|
LaravelForgeSyncDestinationSchema,
|
||||||
|
ChefSyncDestinationSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const SecretSyncFormSchema = SecretSyncUnionSchema;
|
export const SecretSyncFormSchema = SecretSyncUnionSchema;
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
|
|||||||
[SecretSync.LaravelForge]: {
|
[SecretSync.LaravelForge]: {
|
||||||
name: "Laravel Forge",
|
name: "Laravel Forge",
|
||||||
image: "Laravel Forge.png"
|
image: "Laravel Forge.png"
|
||||||
|
},
|
||||||
|
[SecretSync.Chef]: {
|
||||||
|
name: "Chef",
|
||||||
|
image: "Chef.png"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,7 +155,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
|||||||
[SecretSync.DigitalOceanAppPlatform]: AppConnection.DigitalOcean,
|
[SecretSync.DigitalOceanAppPlatform]: AppConnection.DigitalOcean,
|
||||||
[SecretSync.Netlify]: AppConnection.Netlify,
|
[SecretSync.Netlify]: AppConnection.Netlify,
|
||||||
[SecretSync.Bitbucket]: AppConnection.Bitbucket,
|
[SecretSync.Bitbucket]: AppConnection.Bitbucket,
|
||||||
[SecretSync.LaravelForge]: AppConnection.LaravelForge
|
[SecretSync.LaravelForge]: AppConnection.LaravelForge,
|
||||||
|
[SecretSync.Chef]: AppConnection.Chef
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP: Record<
|
export const SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP: Record<
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./queries";
|
||||||
|
export * from "./types";
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import { apiRequest } from "@app/config/request";
|
||||||
|
import { appConnectionKeys } from "@app/hooks/api/appConnections";
|
||||||
|
|
||||||
|
import { TChefDataBag, TChefDataBagItem } from "./types";
|
||||||
|
|
||||||
|
const chefConnectionKeys = {
|
||||||
|
all: [...appConnectionKeys.all, "chef"] as const,
|
||||||
|
listDataBags: (connectionId: string) =>
|
||||||
|
[...chefConnectionKeys.all, "data-bags", connectionId] as const,
|
||||||
|
listDataBagItems: (connectionId: string, dataBagName: string) =>
|
||||||
|
[...chefConnectionKeys.all, "data-bag-items", connectionId, dataBagName] as const
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useChefConnectionListDataBags = (
|
||||||
|
connectionId: string,
|
||||||
|
options?: Omit<
|
||||||
|
UseQueryOptions<
|
||||||
|
TChefDataBag[],
|
||||||
|
unknown,
|
||||||
|
TChefDataBag[],
|
||||||
|
ReturnType<typeof chefConnectionKeys.listDataBags>
|
||||||
|
>,
|
||||||
|
"queryKey" | "queryFn"
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: chefConnectionKeys.listDataBags(connectionId),
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await apiRequest.get<TChefDataBag[]>(
|
||||||
|
`/api/v1/app-connections/chef/${connectionId}/data-bags`
|
||||||
|
);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useChefConnectionListDataBagItems = (
|
||||||
|
connectionId: string,
|
||||||
|
dataBagName: string,
|
||||||
|
options?: Omit<
|
||||||
|
UseQueryOptions<
|
||||||
|
TChefDataBagItem[],
|
||||||
|
unknown,
|
||||||
|
TChefDataBagItem[],
|
||||||
|
ReturnType<typeof chefConnectionKeys.listDataBagItems>
|
||||||
|
>,
|
||||||
|
"queryKey" | "queryFn"
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: chefConnectionKeys.listDataBagItems(connectionId, dataBagName),
|
||||||
|
queryFn: async () => {
|
||||||
|
const params = { dataBagName };
|
||||||
|
const { data } = await apiRequest.get<TChefDataBagItem[]>(
|
||||||
|
`/api/v1/app-connections/chef/${connectionId}/data-bag-items`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
enabled: Boolean(connectionId && dataBagName),
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export type TChefDataBag = {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TChefDataBagItem = {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
@@ -29,7 +29,8 @@ export enum SecretSync {
|
|||||||
DigitalOceanAppPlatform = "digital-ocean-app-platform",
|
DigitalOceanAppPlatform = "digital-ocean-app-platform",
|
||||||
Netlify = "netlify",
|
Netlify = "netlify",
|
||||||
Bitbucket = "bitbucket",
|
Bitbucket = "bitbucket",
|
||||||
LaravelForge = "laravel-forge"
|
LaravelForge = "laravel-forge",
|
||||||
|
Chef = "chef"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SecretSyncStatus {
|
export enum SecretSyncStatus {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
export type TChefSync = TRootSecretSync & {
|
||||||
|
destination: SecretSync.Chef;
|
||||||
|
destinationConfig: {
|
||||||
|
dataBagName: string;
|
||||||
|
dataBagItemName: string;
|
||||||
|
};
|
||||||
|
connection: {
|
||||||
|
app: AppConnection.Chef;
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -32,6 +32,7 @@ import { TTerraformCloudSync } from "./terraform-cloud-sync";
|
|||||||
import { TVercelSync } from "./vercel-sync";
|
import { TVercelSync } from "./vercel-sync";
|
||||||
import { TWindmillSync } from "./windmill-sync";
|
import { TWindmillSync } from "./windmill-sync";
|
||||||
import { TZabbixSync } from "./zabbix-sync";
|
import { TZabbixSync } from "./zabbix-sync";
|
||||||
|
import { TChefSync } from "./chef-sync";
|
||||||
|
|
||||||
export type TSecretSyncOption = {
|
export type TSecretSyncOption = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -71,7 +72,8 @@ export type TSecretSync =
|
|||||||
| TDigitalOceanAppPlatformSync
|
| TDigitalOceanAppPlatformSync
|
||||||
| TNetlifySync
|
| TNetlifySync
|
||||||
| TBitbucketSync
|
| TBitbucketSync
|
||||||
| TLaravelForgeSync;
|
| TLaravelForgeSync
|
||||||
|
| TChefSync;
|
||||||
|
|
||||||
export type TListSecretSyncs = { secretSyncs: TSecretSync[] };
|
export type TListSecretSyncs = { secretSyncs: TSecretSync[] };
|
||||||
|
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
import { TChefSync } from "@app/hooks/api/secretSyncs/types/chef-sync";
|
||||||
|
|
||||||
|
import { getSecretSyncDestinationColValues } from "../helpers";
|
||||||
|
import { SecretSyncTableCell } from "../SecretSyncTableCell";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
secretSync: TChefSync;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ChefSyncDestinationCol = ({ secretSync }: Props) => {
|
||||||
|
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
|
||||||
|
|
||||||
|
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
|
||||||
|
};
|
||||||
+3
@@ -31,6 +31,7 @@ import { TerraformCloudSyncDestinationCol } from "./TerraformCloudSyncDestinatio
|
|||||||
import { VercelSyncDestinationCol } from "./VercelSyncDestinationCol";
|
import { VercelSyncDestinationCol } from "./VercelSyncDestinationCol";
|
||||||
import { WindmillSyncDestinationCol } from "./WindmillSyncDestinationCol";
|
import { WindmillSyncDestinationCol } from "./WindmillSyncDestinationCol";
|
||||||
import { ZabbixSyncDestinationCol } from "./ZabbixSyncDestinationCol";
|
import { ZabbixSyncDestinationCol } from "./ZabbixSyncDestinationCol";
|
||||||
|
import { ChefSyncDestinationCol } from "./ChefSyncDestinationCol";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
secretSync: TSecretSync;
|
secretSync: TSecretSync;
|
||||||
@@ -100,6 +101,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
|
|||||||
return <BitbucketSyncDestinationCol secretSync={secretSync} />;
|
return <BitbucketSyncDestinationCol secretSync={secretSync} />;
|
||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
return <LaravelForgeSyncDestinationCol secretSync={secretSync} />;
|
return <LaravelForgeSyncDestinationCol secretSync={secretSync} />;
|
||||||
|
case SecretSync.Chef:
|
||||||
|
return <ChefSyncDestinationCol secretSync={secretSync} />;
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled Secret Sync Destination Col: ${(secretSync as TSecretSync).destination}`
|
`Unhandled Secret Sync Destination Col: ${(secretSync as TSecretSync).destination}`
|
||||||
|
|||||||
+4
@@ -198,6 +198,10 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
|
|||||||
primaryText = destinationConfig.siteName || destinationConfig.siteId;
|
primaryText = destinationConfig.siteName || destinationConfig.siteId;
|
||||||
secondaryText = destinationConfig.orgName || destinationConfig.orgSlug;
|
secondaryText = destinationConfig.orgName || destinationConfig.orgSlug;
|
||||||
break;
|
break;
|
||||||
|
case SecretSync.Chef:
|
||||||
|
primaryText = destinationConfig.dataBagName;
|
||||||
|
secondaryText = destinationConfig.dataBagItemName;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled Destination Col Values ${destination}`);
|
throw new Error(`Unhandled Destination Col Values ${destination}`);
|
||||||
}
|
}
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||||
|
import { TChefSync } from "@app/hooks/api/secretSyncs/types/chef-sync";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
secretSync: TChefSync;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ChefSyncDestinationSection = ({ secretSync }: Props) => {
|
||||||
|
const { destinationConfig } = secretSync;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GenericFieldLabel label="Data Bag">{destinationConfig.dataBagName}</GenericFieldLabel>
|
||||||
|
<GenericFieldLabel label="Data Bag Item">
|
||||||
|
{destinationConfig.dataBagItemName}
|
||||||
|
</GenericFieldLabel>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
+4
@@ -42,6 +42,7 @@ import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestin
|
|||||||
import { VercelSyncDestinationSection } from "./VercelSyncDestinationSection";
|
import { VercelSyncDestinationSection } from "./VercelSyncDestinationSection";
|
||||||
import { WindmillSyncDestinationSection } from "./WindmillSyncDestinationSection";
|
import { WindmillSyncDestinationSection } from "./WindmillSyncDestinationSection";
|
||||||
import { ZabbixSyncDestinationSection } from "./ZabbixSyncDestinationSection";
|
import { ZabbixSyncDestinationSection } from "./ZabbixSyncDestinationSection";
|
||||||
|
import { ChefSyncDestinationSection } from "./ChefSyncDestinationSection";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
secretSync: TSecretSync;
|
secretSync: TSecretSync;
|
||||||
@@ -152,6 +153,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
|
|||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
DestinationComponents = <LaravelForgeSyncDestinationSection secretSync={secretSync} />;
|
DestinationComponents = <LaravelForgeSyncDestinationSection secretSync={secretSync} />;
|
||||||
break;
|
break;
|
||||||
|
case SecretSync.Chef:
|
||||||
|
DestinationComponents = <ChefSyncDestinationSection secretSync={secretSync} />;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled Destination Section components: ${destination}`);
|
throw new Error(`Unhandled Destination Section components: ${destination}`);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -73,6 +73,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
|||||||
case SecretSync.Netlify:
|
case SecretSync.Netlify:
|
||||||
case SecretSync.Bitbucket:
|
case SecretSync.Bitbucket:
|
||||||
case SecretSync.LaravelForge:
|
case SecretSync.LaravelForge:
|
||||||
|
case SecretSync.Chef:
|
||||||
AdditionalSyncOptionsComponent = null;
|
AdditionalSyncOptionsComponent = null;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user