Merge pull request #3802 from Infisical/ENG-2929

feat(secret-sync, app-connection): Fly.io Secret Sync + App Connection
This commit is contained in:
x032205
2025-06-17 16:57:58 -04:00
committed by GitHub
86 changed files with 1282 additions and 33 deletions

View File

@@ -2225,6 +2225,9 @@ export const AppConnections = {
ONEPASS: {
instanceUrl: "The URL of the 1Password Connect Server instance to authenticate with.",
apiToken: "The API token used to access the 1Password Connect Server."
},
FLYIO: {
accessToken: "The Access Token used to access fly.io."
}
}
};
@@ -2386,6 +2389,9 @@ export const SecretSyncs = {
},
ONEPASS: {
vaultId: "The ID of the 1Password vault to sync secrets to."
},
FLYIO: {
appId: "The ID of the Fly.io app to sync secrets to."
}
}
};

View File

@@ -39,6 +39,7 @@ import {
DatabricksConnectionListItemSchema,
SanitizedDatabricksConnectionSchema
} from "@app/services/app-connection/databricks";
import { FlyioConnectionListItemSchema, SanitizedFlyioConnectionSchema } from "@app/services/app-connection/flyio";
import { GcpConnectionListItemSchema, SanitizedGcpConnectionSchema } from "@app/services/app-connection/gcp";
import { GitHubConnectionListItemSchema, SanitizedGitHubConnectionSchema } from "@app/services/app-connection/github";
import {
@@ -100,7 +101,8 @@ const SanitizedAppConnectionSchema = z.union([
...SanitizedTeamCityConnectionSchema.options,
...SanitizedOCIConnectionSchema.options,
...SanitizedOracleDBConnectionSchema.options,
...SanitizedOnePassConnectionSchema.options
...SanitizedOnePassConnectionSchema.options,
...SanitizedFlyioConnectionSchema.options
]);
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
@@ -127,7 +129,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
TeamCityConnectionListItemSchema,
OCIConnectionListItemSchema,
OracleDBConnectionListItemSchema,
OnePassConnectionListItemSchema
OnePassConnectionListItemSchema,
FlyioConnectionListItemSchema
]);
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {

View File

@@ -0,0 +1,51 @@
import z from "zod";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
CreateFlyioConnectionSchema,
SanitizedFlyioConnectionSchema,
UpdateFlyioConnectionSchema
} from "@app/services/app-connection/flyio";
import { AuthMode } from "@app/services/auth/auth-type";
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
export const registerFlyioConnectionRouter = async (server: FastifyZodProvider) => {
registerAppConnectionEndpoints({
app: AppConnection.Flyio,
server,
sanitizedResponseSchema: SanitizedFlyioConnectionSchema,
createSchema: CreateFlyioConnectionSchema,
updateSchema: UpdateFlyioConnectionSchema
});
// The following endpoints are for internal Infisical App use only and not part of the public API
server.route({
method: "GET",
url: `/:connectionId/apps`,
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
connectionId: z.string().uuid()
}),
response: {
200: z
.object({
id: z.string(),
name: z.string()
})
.array()
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { connectionId } = req.params;
const apps = await server.services.appConnection.flyio.listApps(connectionId, req.permission);
return apps;
}
});
};

View File

@@ -11,6 +11,7 @@ import { registerAzureDevOpsConnectionRouter } from "./azure-devops-connection-r
import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router";
import { registerCamundaConnectionRouter } from "./camunda-connection-router";
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
import { registerFlyioConnectionRouter } from "./flyio-connection-router";
import { registerGcpConnectionRouter } from "./gcp-connection-router";
import { registerGitHubConnectionRouter } from "./github-connection-router";
import { registerGitHubRadarConnectionRouter } from "./github-radar-connection-router";
@@ -52,5 +53,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
[AppConnection.TeamCity]: registerTeamCityConnectionRouter,
[AppConnection.OCI]: registerOCIConnectionRouter,
[AppConnection.OracleDB]: registerOracleDBConnectionRouter,
[AppConnection.OnePass]: registerOnePassConnectionRouter
[AppConnection.OnePass]: registerOnePassConnectionRouter,
[AppConnection.Flyio]: registerFlyioConnectionRouter
};

View File

@@ -0,0 +1,13 @@
import { CreateFlyioSyncSchema, FlyioSyncSchema, UpdateFlyioSyncSchema } from "@app/services/secret-sync/flyio";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
export const registerFlyioSyncRouter = async (server: FastifyZodProvider) =>
registerSyncSecretsEndpoints({
destination: SecretSync.Flyio,
server,
responseSchema: FlyioSyncSchema,
createSchema: CreateFlyioSyncSchema,
updateSchema: UpdateFlyioSyncSchema
});

View File

@@ -9,6 +9,7 @@ import { registerAzureDevOpsSyncRouter } from "./azure-devops-sync-router";
import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
import { registerCamundaSyncRouter } from "./camunda-sync-router";
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
import { registerFlyioSyncRouter } from "./flyio-sync-router";
import { registerGcpSyncRouter } from "./gcp-sync-router";
import { registerGitHubSyncRouter } from "./github-sync-router";
import { registerHCVaultSyncRouter } from "./hc-vault-sync-router";
@@ -37,5 +38,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
[SecretSync.HCVault]: registerHCVaultSyncRouter,
[SecretSync.TeamCity]: registerTeamCitySyncRouter,
[SecretSync.OCIVault]: registerOCIVaultSyncRouter,
[SecretSync.OnePass]: registerOnePassSyncRouter
[SecretSync.OnePass]: registerOnePassSyncRouter,
[SecretSync.Flyio]: registerFlyioSyncRouter
};

View File

@@ -23,6 +23,7 @@ import { AzureDevOpsSyncListItemSchema, AzureDevOpsSyncSchema } from "@app/servi
import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/services/secret-sync/azure-key-vault";
import { CamundaSyncListItemSchema, CamundaSyncSchema } from "@app/services/secret-sync/camunda";
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
import { FlyioSyncListItemSchema, FlyioSyncSchema } from "@app/services/secret-sync/flyio";
import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp";
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
import { HCVaultSyncListItemSchema, HCVaultSyncSchema } from "@app/services/secret-sync/hc-vault";
@@ -49,7 +50,8 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
HCVaultSyncSchema,
TeamCitySyncSchema,
OCIVaultSyncSchema,
OnePassSyncSchema
OnePassSyncSchema,
FlyioSyncSchema
]);
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
@@ -69,7 +71,8 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
HCVaultSyncListItemSchema,
TeamCitySyncListItemSchema,
OCIVaultSyncListItemSchema,
OnePassSyncListItemSchema
OnePassSyncListItemSchema,
FlyioSyncListItemSchema
]);
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {

View File

@@ -22,7 +22,8 @@ export enum AppConnection {
TeamCity = "teamcity",
OCI = "oci",
OracleDB = "oracledb",
OnePass = "1password"
OnePass = "1password",
Flyio = "flyio"
}
export enum AWSRegion {

View File

@@ -56,6 +56,7 @@ import {
getDatabricksConnectionListItem,
validateDatabricksConnectionCredentials
} from "./databricks";
import { FlyioConnectionMethod, getFlyioConnectionListItem, validateFlyioConnectionCredentials } from "./flyio";
import { GcpConnectionMethod, getGcpConnectionListItem, validateGcpConnectionCredentials } from "./gcp";
import { getGitHubConnectionListItem, GitHubConnectionMethod, validateGitHubConnectionCredentials } from "./github";
import {
@@ -121,7 +122,8 @@ export const listAppConnectionOptions = () => {
getTeamCityConnectionListItem(),
getOCIConnectionListItem(),
getOracleDBConnectionListItem(),
getOnePassConnectionListItem()
getOnePassConnectionListItem(),
getFlyioConnectionListItem()
].sort((a, b) => a.name.localeCompare(b.name));
};
@@ -196,7 +198,8 @@ export const validateAppConnectionCredentials = async (
[AppConnection.TeamCity]: validateTeamCityConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.OCI]: validateOCIConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.OracleDB]: validateSqlConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.OnePass]: validateOnePassConnectionCredentials as TAppConnectionCredentialsValidator
[AppConnection.OnePass]: validateOnePassConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Flyio]: validateFlyioConnectionCredentials as TAppConnectionCredentialsValidator
};
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection);
@@ -238,6 +241,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
case HCVaultConnectionMethod.AccessToken:
case TeamCityConnectionMethod.AccessToken:
case AzureDevOpsConnectionMethod.AccessToken:
case FlyioConnectionMethod.AccessToken:
return "Access Token";
case Auth0ConnectionMethod.ClientCredentials:
return "Client Credentials";
@@ -299,7 +303,8 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
[AppConnection.TeamCity]: platformManagedCredentialsNotSupported,
[AppConnection.OCI]: platformManagedCredentialsNotSupported,
[AppConnection.OracleDB]: transferSqlConnectionCredentialsToPlatform as TAppConnectionTransitionCredentialsToPlatform,
[AppConnection.OnePass]: platformManagedCredentialsNotSupported
[AppConnection.OnePass]: platformManagedCredentialsNotSupported,
[AppConnection.Flyio]: platformManagedCredentialsNotSupported
};
export const enterpriseAppCheck = async (

View File

@@ -24,7 +24,8 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
[AppConnection.TeamCity]: "TeamCity",
[AppConnection.OCI]: "OCI",
[AppConnection.OracleDB]: "OracleDB",
[AppConnection.OnePass]: "1Password"
[AppConnection.OnePass]: "1Password",
[AppConnection.Flyio]: "Fly.io"
};
export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanType> = {
@@ -51,5 +52,6 @@ export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanTyp
[AppConnection.OCI]: AppConnectionPlanType.Enterprise,
[AppConnection.OracleDB]: AppConnectionPlanType.Enterprise,
[AppConnection.OnePass]: AppConnectionPlanType.Regular,
[AppConnection.MySql]: AppConnectionPlanType.Regular
[AppConnection.MySql]: AppConnectionPlanType.Regular,
[AppConnection.Flyio]: AppConnectionPlanType.Regular
};

View File

@@ -49,6 +49,8 @@ import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
import { camundaConnectionService } from "./camunda/camunda-connection-service";
import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks";
import { databricksConnectionService } from "./databricks/databricks-connection-service";
import { ValidateFlyioConnectionCredentialsSchema } from "./flyio";
import { flyioConnectionService } from "./flyio/flyio-connection-service";
import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
import { gcpConnectionService } from "./gcp/gcp-connection-service";
import { ValidateGitHubConnectionCredentialsSchema } from "./github";
@@ -104,7 +106,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
[AppConnection.TeamCity]: ValidateTeamCityConnectionCredentialsSchema,
[AppConnection.OCI]: ValidateOCIConnectionCredentialsSchema,
[AppConnection.OracleDB]: ValidateOracleDBConnectionCredentialsSchema,
[AppConnection.OnePass]: ValidateOnePassConnectionCredentialsSchema
[AppConnection.OnePass]: ValidateOnePassConnectionCredentialsSchema,
[AppConnection.Flyio]: ValidateFlyioConnectionCredentialsSchema
};
export const appConnectionServiceFactory = ({
@@ -509,6 +512,7 @@ export const appConnectionServiceFactory = ({
windmill: windmillConnectionService(connectAppConnectionById),
teamcity: teamcityConnectionService(connectAppConnectionById),
oci: ociConnectionService(connectAppConnectionById, licenseService),
onepass: onePassConnectionService(connectAppConnectionById)
onepass: onePassConnectionService(connectAppConnectionById),
flyio: flyioConnectionService(connectAppConnectionById)
};
};

View File

@@ -68,6 +68,12 @@ import {
TDatabricksConnectionInput,
TValidateDatabricksConnectionCredentialsSchema
} from "./databricks";
import {
TFlyioConnection,
TFlyioConnectionConfig,
TFlyioConnectionInput,
TValidateFlyioConnectionCredentialsSchema
} from "./flyio";
import {
TGcpConnection,
TGcpConnectionConfig,
@@ -161,6 +167,7 @@ export type TAppConnection = { id: string } & (
| TOCIConnection
| TOracleDBConnection
| TOnePassConnection
| TFlyioConnection
);
export type TAppConnectionRaw = NonNullable<Awaited<ReturnType<TAppConnectionDALFactory["findById"]>>>;
@@ -192,6 +199,7 @@ export type TAppConnectionInput = { id: string } & (
| TOCIConnectionInput
| TOracleDBConnectionInput
| TOnePassConnectionInput
| TFlyioConnectionInput
);
export type TSqlConnectionInput =
@@ -230,7 +238,8 @@ export type TAppConnectionConfig =
| TLdapConnectionConfig
| TTeamCityConnectionConfig
| TOCIConnectionConfig
| TOnePassConnectionConfig;
| TOnePassConnectionConfig
| TFlyioConnectionConfig;
export type TValidateAppConnectionCredentialsSchema =
| TValidateAwsConnectionCredentialsSchema
@@ -256,7 +265,8 @@ export type TValidateAppConnectionCredentialsSchema =
| TValidateTeamCityConnectionCredentialsSchema
| TValidateOCIConnectionCredentialsSchema
| TValidateOracleDBConnectionCredentialsSchema
| TValidateOnePassConnectionCredentialsSchema;
| TValidateOnePassConnectionCredentialsSchema
| TValidateFlyioConnectionCredentialsSchema;
export type TListAwsConnectionKmsKeys = {
connectionId: string;

View File

@@ -0,0 +1,3 @@
export enum FlyioConnectionMethod {
AccessToken = "access-token"
}

View File

@@ -0,0 +1,72 @@
import { AxiosError } from "axios";
import { request } from "@app/lib/config/request";
import { BadRequestError } from "@app/lib/errors";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
import { FlyioConnectionMethod } from "./flyio-connection-enums";
import { TFlyioApp, TFlyioConnection, TFlyioConnectionConfig } from "./flyio-connection-types";
export const getFlyioConnectionListItem = () => {
return {
name: "Fly.io" as const,
app: AppConnection.Flyio as const,
methods: Object.values(FlyioConnectionMethod) as [FlyioConnectionMethod.AccessToken]
};
};
export const validateFlyioConnectionCredentials = async (config: TFlyioConnectionConfig) => {
const { accessToken } = config.credentials;
try {
const resp = await request.post<{ data: { viewer: { id: string | null; email: string } } | null }>(
IntegrationUrls.FLYIO_API_URL,
{ query: "query { viewer { id email } }" },
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json"
}
}
);
if (resp.data.data === null) {
throw new BadRequestError({
message: "Unable to validate connection: Invalid access token provided."
});
}
} catch (error: unknown) {
if (error instanceof AxiosError) {
throw new BadRequestError({
message: `Failed to validate credentials: ${error.message || "Unknown error"}`
});
}
throw new BadRequestError({
message: "Unable to validate connection: verify credentials"
});
}
return config.credentials;
};
export const listFlyioApps = async (appConnection: TFlyioConnection) => {
const { accessToken } = appConnection.credentials;
const resp = await request.post<{ data: { apps: { nodes: TFlyioApp[] } } }>(
IntegrationUrls.FLYIO_API_URL,
{
query:
"query GetApps { apps { nodes { id name hostname status organization { id slug } currentRelease { version status createdAt } } } }"
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
Accept: "application/json"
}
}
);
return resp.data.data.apps.nodes;
};

View File

@@ -0,0 +1,62 @@
import z from "zod";
import { AppConnections } from "@app/lib/api-docs";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
BaseAppConnectionSchema,
GenericCreateAppConnectionFieldsSchema,
GenericUpdateAppConnectionFieldsSchema
} from "@app/services/app-connection/app-connection-schemas";
import { FlyioConnectionMethod } from "./flyio-connection-enums";
export const FlyioConnectionAccessTokenCredentialsSchema = z.object({
accessToken: z
.string()
.trim()
.min(1, "Access Token required")
.max(1000)
.startsWith("FlyV1", "Token must start with 'FlyV1'")
.describe(AppConnections.CREDENTIALS.FLYIO.accessToken)
});
const BaseFlyioConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Flyio) });
export const FlyioConnectionSchema = BaseFlyioConnectionSchema.extend({
method: z.literal(FlyioConnectionMethod.AccessToken),
credentials: FlyioConnectionAccessTokenCredentialsSchema
});
export const SanitizedFlyioConnectionSchema = z.discriminatedUnion("method", [
BaseFlyioConnectionSchema.extend({
method: z.literal(FlyioConnectionMethod.AccessToken),
credentials: FlyioConnectionAccessTokenCredentialsSchema.pick({})
})
]);
export const ValidateFlyioConnectionCredentialsSchema = z.discriminatedUnion("method", [
z.object({
method: z.literal(FlyioConnectionMethod.AccessToken).describe(AppConnections.CREATE(AppConnection.Flyio).method),
credentials: FlyioConnectionAccessTokenCredentialsSchema.describe(
AppConnections.CREATE(AppConnection.Flyio).credentials
)
})
]);
export const CreateFlyioConnectionSchema = ValidateFlyioConnectionCredentialsSchema.and(
GenericCreateAppConnectionFieldsSchema(AppConnection.Flyio)
);
export const UpdateFlyioConnectionSchema = z
.object({
credentials: FlyioConnectionAccessTokenCredentialsSchema.optional().describe(
AppConnections.UPDATE(AppConnection.Flyio).credentials
)
})
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Flyio));
export const FlyioConnectionListItemSchema = z.object({
name: z.literal("Fly.io"),
app: z.literal(AppConnection.Flyio),
methods: z.nativeEnum(FlyioConnectionMethod).array()
});

View File

@@ -0,0 +1,30 @@
import { logger } from "@app/lib/logger";
import { OrgServiceActor } from "@app/lib/types";
import { AppConnection } from "../app-connection-enums";
import { listFlyioApps } from "./flyio-connection-fns";
import { TFlyioConnection } from "./flyio-connection-types";
type TGetAppConnectionFunc = (
app: AppConnection,
connectionId: string,
actor: OrgServiceActor
) => Promise<TFlyioConnection>;
export const flyioConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
const listApps = async (connectionId: string, actor: OrgServiceActor) => {
const appConnection = await getAppConnection(AppConnection.Flyio, connectionId, actor);
try {
const apps = await listFlyioApps(appConnection);
return apps;
} catch (error) {
logger.error(error, "Failed to establish connection with fly.io");
return [];
}
};
return {
listApps
};
};

View File

@@ -0,0 +1,27 @@
import z from "zod";
import { DiscriminativePick } from "@app/lib/types";
import { AppConnection } from "../app-connection-enums";
import {
CreateFlyioConnectionSchema,
FlyioConnectionSchema,
ValidateFlyioConnectionCredentialsSchema
} from "./flyio-connection-schemas";
export type TFlyioConnection = z.infer<typeof FlyioConnectionSchema>;
export type TFlyioConnectionInput = z.infer<typeof CreateFlyioConnectionSchema> & {
app: AppConnection.Flyio;
};
export type TValidateFlyioConnectionCredentialsSchema = typeof ValidateFlyioConnectionCredentialsSchema;
export type TFlyioConnectionConfig = DiscriminativePick<TFlyioConnectionInput, "method" | "app" | "credentials"> & {
orgId: string;
};
export type TFlyioApp = {
id: string;
name: string;
};

View File

@@ -0,0 +1,4 @@
export * from "./flyio-connection-enums";
export * from "./flyio-connection-fns";
export * from "./flyio-connection-schemas";
export * from "./flyio-connection-types";

View File

@@ -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 FLYIO_SYNC_LIST_OPTION: TSecretSyncListItem = {
name: "Fly.io",
destination: SecretSync.Flyio,
connection: AppConnection.Flyio,
canImportSecrets: false
};

View File

@@ -0,0 +1,133 @@
import { request } from "@app/lib/config/request";
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
import {
TDeleteFlyioVariable,
TFlyioListVariables,
TFlyioSecret,
TFlyioSyncWithCredentials,
TPutFlyioVariable
} from "@app/services/secret-sync/flyio/flyio-sync-types";
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
const listFlyioSecrets = async ({ accessToken, appId }: TFlyioListVariables) => {
const { data } = await request.post<{ data: { app: { secrets: TFlyioSecret[] } } }>(
IntegrationUrls.FLYIO_API_URL,
{
query: "query GetAppSecrets($appId: String!) { app(id: $appId) { id name secrets { name createdAt } } }",
variables: { appId }
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
Accept: "application/json"
}
}
);
return data.data.app.secrets.map((s) => s.name);
};
const putFlyioSecrets = async ({ accessToken, appId, secretMap }: TPutFlyioVariable) => {
return request.post(
IntegrationUrls.FLYIO_API_URL,
{
query:
"mutation SetAppSecrets($appId: ID!, $secrets: [SecretInput!]!) { setSecrets(input: { appId: $appId, secrets: $secrets }) { app { name } release { version } } }",
variables: {
appId,
secrets: Object.entries(secretMap).map(([key, { value }]) => ({ key, value }))
}
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json"
}
}
);
};
const deleteFlyioSecrets = async ({ accessToken, appId, keys }: TDeleteFlyioVariable) => {
return request.post(
IntegrationUrls.FLYIO_API_URL,
{
query:
"mutation UnsetAppSecrets($appId: ID!, $keys: [String!]!) { unsetSecrets(input: { appId: $appId, keys: $keys }) { app { name } release { version } } }",
variables: {
appId,
keys
}
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json"
}
}
);
};
export const FlyioSyncFns = {
syncSecrets: async (secretSync: TFlyioSyncWithCredentials, secretMap: TSecretMap) => {
const {
connection,
environment,
destinationConfig: { appId }
} = secretSync;
const { accessToken } = connection.credentials;
try {
await putFlyioSecrets({ accessToken, appId, secretMap });
} catch (error) {
throw new SecretSyncError({
error
});
}
if (secretSync.syncOptions.disableSecretDeletion) return;
const secrets = await listFlyioSecrets({ accessToken, appId });
const keys = secrets.filter(
(secret) =>
matchesSchema(secret, environment?.slug || "", secretSync.syncOptions.keySchema) && !(secret in secretMap)
);
try {
await deleteFlyioSecrets({ accessToken, appId, keys });
} catch (error) {
throw new SecretSyncError({
error
});
}
},
removeSecrets: async (secretSync: TFlyioSyncWithCredentials, secretMap: TSecretMap) => {
const {
connection,
destinationConfig: { appId }
} = secretSync;
const { accessToken } = connection.credentials;
const secrets = await listFlyioSecrets({ accessToken, appId });
const keys = secrets.filter((secret) => secret in secretMap);
try {
await deleteFlyioSecrets({ accessToken, appId, keys });
} catch (error) {
throw new SecretSyncError({
error
});
}
},
getSecrets: async (secretSync: TFlyioSyncWithCredentials) => {
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
}
};

View File

@@ -0,0 +1,43 @@
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 FlyioSyncDestinationConfigSchema = z.object({
appId: z.string().trim().min(1, "App required").max(255).describe(SecretSyncs.DESTINATION_CONFIG.FLYIO.appId)
});
const FlyioSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
export const FlyioSyncSchema = BaseSecretSyncSchema(SecretSync.Flyio, FlyioSyncOptionsConfig).extend({
destination: z.literal(SecretSync.Flyio),
destinationConfig: FlyioSyncDestinationConfigSchema
});
export const CreateFlyioSyncSchema = GenericCreateSecretSyncFieldsSchema(
SecretSync.Flyio,
FlyioSyncOptionsConfig
).extend({
destinationConfig: FlyioSyncDestinationConfigSchema
});
export const UpdateFlyioSyncSchema = GenericUpdateSecretSyncFieldsSchema(
SecretSync.Flyio,
FlyioSyncOptionsConfig
).extend({
destinationConfig: FlyioSyncDestinationConfigSchema.optional()
});
export const FlyioSyncListItemSchema = z.object({
name: z.literal("Fly.io"),
connection: z.literal(AppConnection.Flyio),
destination: z.literal(SecretSync.Flyio),
canImportSecrets: z.literal(false)
});

View File

@@ -0,0 +1,32 @@
import { z } from "zod";
import { TFlyioConnection } from "@app/services/app-connection/flyio";
import { CreateFlyioSyncSchema, FlyioSyncListItemSchema, FlyioSyncSchema } from "./flyio-sync-schemas";
export type TFlyioSync = z.infer<typeof FlyioSyncSchema>;
export type TFlyioSyncInput = z.infer<typeof CreateFlyioSyncSchema>;
export type TFlyioSyncListItem = z.infer<typeof FlyioSyncListItemSchema>;
export type TFlyioSyncWithCredentials = TFlyioSync & {
connection: TFlyioConnection;
};
export type TFlyioSecret = {
name: string;
};
export type TFlyioListVariables = {
accessToken: string;
appId: string;
};
export type TPutFlyioVariable = TFlyioListVariables & {
secretMap: { [key: string]: { value: string } };
};
export type TDeleteFlyioVariable = TFlyioListVariables & {
keys: string[];
};

View File

@@ -0,0 +1,4 @@
export * from "./flyio-sync-constants";
export * from "./flyio-sync-fns";
export * from "./flyio-sync-schemas";
export * from "./flyio-sync-types";

View File

@@ -15,7 +15,8 @@ export enum SecretSync {
HCVault = "hashicorp-vault",
TeamCity = "teamcity",
OCIVault = "oci-vault",
OnePass = "1password"
OnePass = "1password",
Flyio = "flyio"
}
export enum SecretSyncInitialSyncBehavior {

View File

@@ -29,6 +29,7 @@ import { AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION, azureAppConfigurationSyncFact
import { AZURE_DEVOPS_SYNC_LIST_OPTION, azureDevOpsSyncFactory } from "./azure-devops";
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault";
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
import { FLYIO_SYNC_LIST_OPTION, FlyioSyncFns } from "./flyio";
import { GCP_SYNC_LIST_OPTION } from "./gcp";
import { GcpSyncFns } from "./gcp/gcp-sync-fns";
import { HC_VAULT_SYNC_LIST_OPTION, HCVaultSyncFns } from "./hc-vault";
@@ -57,7 +58,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
[SecretSync.HCVault]: HC_VAULT_SYNC_LIST_OPTION,
[SecretSync.TeamCity]: TEAMCITY_SYNC_LIST_OPTION,
[SecretSync.OCIVault]: OCI_VAULT_SYNC_LIST_OPTION,
[SecretSync.OnePass]: ONEPASS_SYNC_LIST_OPTION
[SecretSync.OnePass]: ONEPASS_SYNC_LIST_OPTION,
[SecretSync.Flyio]: FLYIO_SYNC_LIST_OPTION
};
export const listSecretSyncOptions = () => {
@@ -215,6 +217,8 @@ export const SecretSyncFns = {
return OCIVaultSyncFns.syncSecrets(secretSync, schemaSecretMap);
case SecretSync.OnePass:
return OnePassSyncFns.syncSecrets(secretSync, schemaSecretMap);
case SecretSync.Flyio:
return FlyioSyncFns.syncSecrets(secretSync, schemaSecretMap);
default:
throw new Error(
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
@@ -292,6 +296,9 @@ export const SecretSyncFns = {
case SecretSync.OnePass:
secretMap = await OnePassSyncFns.getSecrets(secretSync);
break;
case SecretSync.Flyio:
secretMap = await FlyioSyncFns.getSecrets(secretSync);
break;
default:
throw new Error(
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
@@ -359,6 +366,8 @@ export const SecretSyncFns = {
return OCIVaultSyncFns.removeSecrets(secretSync, schemaSecretMap);
case SecretSync.OnePass:
return OnePassSyncFns.removeSecrets(secretSync, schemaSecretMap);
case SecretSync.Flyio:
return FlyioSyncFns.removeSecrets(secretSync, schemaSecretMap);
default:
throw new Error(
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`

View File

@@ -18,7 +18,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
[SecretSync.HCVault]: "Hashicorp Vault",
[SecretSync.TeamCity]: "TeamCity",
[SecretSync.OCIVault]: "OCI Vault",
[SecretSync.OnePass]: "1Password"
[SecretSync.OnePass]: "1Password",
[SecretSync.Flyio]: "Fly.io"
};
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
@@ -38,7 +39,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
[SecretSync.HCVault]: AppConnection.HCVault,
[SecretSync.TeamCity]: AppConnection.TeamCity,
[SecretSync.OCIVault]: AppConnection.OCI,
[SecretSync.OnePass]: AppConnection.OnePass
[SecretSync.OnePass]: AppConnection.OnePass,
[SecretSync.Flyio]: AppConnection.Flyio
};
export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
@@ -58,5 +60,6 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
[SecretSync.HCVault]: SecretSyncPlanType.Regular,
[SecretSync.TeamCity]: SecretSyncPlanType.Regular,
[SecretSync.OCIVault]: SecretSyncPlanType.Enterprise,
[SecretSync.OnePass]: SecretSyncPlanType.Regular
[SecretSync.OnePass]: SecretSyncPlanType.Regular,
[SecretSync.Flyio]: SecretSyncPlanType.Regular
};

View File

@@ -72,6 +72,7 @@ import {
TAzureKeyVaultSyncListItem,
TAzureKeyVaultSyncWithCredentials
} from "./azure-key-vault";
import { TFlyioSync, TFlyioSyncInput, TFlyioSyncListItem, TFlyioSyncWithCredentials } from "./flyio/flyio-sync-types";
import { TGcpSync, TGcpSyncInput, TGcpSyncListItem, TGcpSyncWithCredentials } from "./gcp";
import {
THCVaultSync,
@@ -116,7 +117,8 @@ export type TSecretSync =
| THCVaultSync
| TTeamCitySync
| TOCIVaultSync
| TOnePassSync;
| TOnePassSync
| TFlyioSync;
export type TSecretSyncWithCredentials =
| TAwsParameterStoreSyncWithCredentials
@@ -135,7 +137,8 @@ export type TSecretSyncWithCredentials =
| THCVaultSyncWithCredentials
| TTeamCitySyncWithCredentials
| TOCIVaultSyncWithCredentials
| TOnePassSyncWithCredentials;
| TOnePassSyncWithCredentials
| TFlyioSyncWithCredentials;
export type TSecretSyncInput =
| TAwsParameterStoreSyncInput
@@ -154,7 +157,8 @@ export type TSecretSyncInput =
| THCVaultSyncInput
| TTeamCitySyncInput
| TOCIVaultSyncInput
| TOnePassSyncInput;
| TOnePassSyncInput
| TFlyioSyncInput;
export type TSecretSyncListItem =
| TAwsParameterStoreSyncListItem
@@ -173,7 +177,8 @@ export type TSecretSyncListItem =
| THCVaultSyncListItem
| TTeamCitySyncListItem
| TOCIVaultSyncListItem
| TOnePassSyncListItem;
| TOnePassSyncListItem
| TFlyioSyncListItem;
export type TSyncOptionsConfig = {
canImportSecrets: boolean;

View File

@@ -0,0 +1,4 @@
---
title: "Available"
openapi: "GET /api/v1/app-connections/flyio/available"
---

View File

@@ -0,0 +1,8 @@
---
title: "Create"
openapi: "POST /api/v1/app-connections/flyio"
---
<Note>
Check out the configuration docs for [Fly.io Connections](/integrations/app-connections/flyio) to learn how to obtain the required credentials.
</Note>

View File

@@ -0,0 +1,4 @@
---
title: "Delete"
openapi: "DELETE /api/v1/app-connections/flyio/{connectionId}"
---

View File

@@ -0,0 +1,4 @@
---
title: "Get by ID"
openapi: "GET /api/v1/app-connections/flyio/{connectionId}"
---

View File

@@ -0,0 +1,4 @@
---
title: "Get by Name"
openapi: "GET /api/v1/app-connections/flyio/connection-name/{connectionName}"
---

View File

@@ -0,0 +1,4 @@
---
title: "List"
openapi: "GET /api/v1/app-connections/flyio"
---

View File

@@ -0,0 +1,8 @@
---
title: "Update"
openapi: "PATCH /api/v1/app-connections/flyio/{connectionId}"
---
<Note>
Check out the configuration docs for [Fly.io Connections](/integrations/app-connections/flyio) to learn how to obtain the required credentials.
</Note>

View File

@@ -0,0 +1,4 @@
---
title: "Create"
openapi: "POST /api/v1/secret-syncs/flyio"
---

View File

@@ -0,0 +1,4 @@
---
title: "Delete"
openapi: "DELETE /api/v1/secret-syncs/flyio/{syncId}"
---

View File

@@ -0,0 +1,4 @@
---
title: "Get by ID"
openapi: "GET /api/v1/secret-syncs/flyio/{syncId}"
---

View File

@@ -0,0 +1,4 @@
---
title: "Get by Name"
openapi: "GET /api/v1/secret-syncs/flyio/sync-name/{syncName}"
---

View File

@@ -0,0 +1,4 @@
---
title: "Import Secrets"
openapi: "POST /api/v1/secret-syncs/flyio/{syncId}/import-secrets"
---

View File

@@ -0,0 +1,4 @@
---
title: "List"
openapi: "GET /api/v1/secret-syncs/flyio"
---

View File

@@ -0,0 +1,4 @@
---
title: "Remove Secrets"
openapi: "POST /api/v1/secret-syncs/flyio/{syncId}/remove-secrets"
---

View File

@@ -0,0 +1,4 @@
---
title: "Sync Secrets"
openapi: "POST /api/v1/secret-syncs/flyio/{syncId}/sync-secrets"
---

View File

@@ -0,0 +1,4 @@
---
title: "Update"
openapi: "PATCH /api/v1/secret-syncs/flyio/{syncId}"
---

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -0,0 +1,96 @@
---
title: "Fly.io Connection"
description: "Learn how to configure a Fly.io Connection for Infisical."
---
Infisical supports the use of [Access Tokens](https://fly.io/docs/security/tokens/) to connect with Fly.io.
## Create Fly.io Access Token
<Steps>
<Step title="Navigate to 'Access Tokens'">
![Dashboard Page](/images/app-connections/flyio/dashboard-page.png)
</Step>
<Step title="Click 'Create Token'">
![Click Create Token](/images/app-connections/flyio/create-token.png)
</Step>
<Step title="Provide Token Information">
Ensure that you give this token access to the correct app, then click 'Create Token'.
![Create Token Page](/images/app-connections/flyio/create-token-page.png)
</Step>
<Step title="Save Token">
After clicking 'Create Token', a modal containing your access token will appear. Save this token for later steps.
</Step>
</Steps>
## Create Fly.io Connection in Infisical
<Tabs>
<Tab title="Infisical UI">
<Steps>
<Step title="Navigate to App Connections">
In your Infisical dashboard, go to **Organization Settings** and select the [**App Connections**](https://app.infisical.com/organization/app-connections) tab.
![App Connections Tab](/images/app-connections/general/add-connection.png)
</Step>
<Step title="Select Fly.io Connection">
Click the **+ Add Connection** button and select the **Fly.io Connection** option from the available integrations.
![Select Fly.io Connection](/images/app-connections/flyio/app-connection-option.png)
</Step>
<Step title="Fill out the Fly.io Connection Modal">
Complete the Fly.io Connection form by entering:
- A descriptive name for the connection
- An optional description for future reference
- The Access Token from earlier steps
![Fly.io Connection Modal](/images/app-connections/flyio/app-connection-modal.png)
</Step>
<Step title="Connection Created">
After clicking Create, your **Fly.io Connection** is established and ready to use with your Infisical projects.
![Fly.io Connection Created](/images/app-connections/flyio/app-connection-created.png)
</Step>
</Steps>
</Tab>
<Tab title="API">
To create a Fly.io Connection, make an API request to the [Create Fly.io Connection](/api-reference/endpoints/app-connections/flyio/create) API endpoint.
### Sample request
```bash Request
curl --request POST \
--url https://app.infisical.com/api/v1/app-connections/flyio \
--header 'Content-Type: application/json' \
--data '{
"name": "my-flyio-connection",
"method": "access-token",
"credentials": {
"accessToken": "[PRIVATE TOKEN]"
}
}'
```
### Sample response
```bash Response
{
"appConnection": {
"id": "e5d18aca-86f7-4026-a95e-efb8aeb0d8e6",
"name": "my-flyio-connection",
"description": null,
"version": 1,
"orgId": "6f03caa1-a5de-43ce-b127-95a145d3464c",
"createdAt": "2025-04-23T19:46:34.831Z",
"updatedAt": "2025-04-23T19:46:34.831Z",
"isPlatformManagedCredentials": false,
"credentialsHash": "7c2d371dec195f82a6a0d5b41c970a229cfcaf88e894a5b6395e2dbd0280661f",
"app": "flyio",
"method": "access-token",
"credentials": {}
}
}
```
</Tab>
</Tabs>

View File

@@ -0,0 +1,155 @@
---
title: "Fly.io Sync"
description: "Learn how to configure a Fly.io Sync for Infisical."
---
**Prerequisites:**
- Create a [Fly.io Connection](/integrations/app-connections/flyio)
<Tabs>
<Tab title="Infisical UI">
<Steps>
<Step title="Add Sync">
Navigate to **Project** > **Integrations** and select the **Secret Syncs** tab. Click on the **Add Sync** button.
![Secret Syncs Tab](/images/secret-syncs/general/secret-sync-tab.png)
</Step>
<Step title="Select 'Fly.io'">
![Select Fly.io](/images/secret-syncs/flyio/select-option.png)
</Step>
<Step title="Configure source">
Configure the **Source** from where secrets should be retrieved, then click **Next**.
![Configure Source](/images/secret-syncs/flyio/configure-source.png)
- **Environment**: The project environment to retrieve secrets from.
- **Secret Path**: The folder path to retrieve secrets from.
<Tip>
If you need to sync secrets from multiple folder locations, check out [secret imports](/documentation/platform/secret-reference#secret-imports).
</Tip>
</Step>
<Step title="Configure destination">
Configure the **Destination** to where secrets should be deployed, then click **Next**.
![Configure Destination](/images/secret-syncs/flyio/configure-destination.png)
- **Fly.io Connection**: The Fly.io Connection to authenticate with.
- **App**: The Fly.io app to sync secrets to.
</Step>
<Step title="Configure sync options">
Configure the **Sync Options** to specify how secrets should be synced, then click **Next**.
![Configure Sync Options](/images/secret-syncs/flyio/configure-sync-options.png)
- **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync.
- **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical.
<Note>
Fly.io does not support importing secrets.
</Note>
- **Key Schema**: Template that determines how secret names are transformed when syncing, using `{{secretKey}}` as a placeholder for the original secret name and `{{environment}}` for the environment.
<Note>
We highly recommend using a Key Schema to ensure that Infisical only manages the specific keys you intend, keeping everything else untouched.
</Note>
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
</Step>
<Step title="Configure details">
Configure the **Details** of your Fly.io Sync, then click **Next**.
![Configure Details](/images/secret-syncs/flyio/configure-details.png)
- **Name**: The name of your sync. Must be slug-friendly.
- **Description**: An optional description for your sync.
</Step>
<Step title="Review configuration">
Review your Fly.io Sync configuration, then click **Create Sync**.
![Review Configuration](/images/secret-syncs/flyio/review-configuration.png)
</Step>
<Step title="Sync created">
If enabled, your Fly.io Sync will begin syncing your secrets to the destination endpoint.
![Sync Created](/images/secret-syncs/flyio/sync-created.png)
</Step>
</Steps>
</Tab>
<Tab title="API">
To create a **Fly.io Sync**, make an API request to the [Create Fly.io Sync](/api-reference/endpoints/secret-syncs/flyio/create) API endpoint.
### Sample request
```bash Request
curl --request POST \
--url https://app.infisical.com/api/v1/secret-syncs/flyio \
--header 'Content-Type: application/json' \
--data '{
"name": "my-flyio-sync",
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"description": "an example sync",
"connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"environment": "dev",
"secretPath": "/my-secrets",
"isEnabled": true,
"syncOptions": {
"initialSyncBehavior": "overwrite-destination"
},
"destinationConfig": {
"appId": "..."
}
}'
```
### Sample response
```bash Response
{
"secretSync": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "my-flyio-sync",
"description": "an example sync",
"isEnabled": true,
"version": 1,
"folderId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"connectionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z",
"syncStatus": "succeeded",
"lastSyncJobId": "123",
"lastSyncMessage": null,
"lastSyncedAt": "2023-11-07T05:31:56Z",
"importStatus": null,
"lastImportJobId": null,
"lastImportMessage": null,
"lastImportedAt": null,
"removeStatus": null,
"lastRemoveJobId": null,
"lastRemoveMessage": null,
"lastRemovedAt": null,
"syncOptions": {
"initialSyncBehavior": "overwrite-destination"
},
"projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"connection": {
"app": "flyio",
"name": "my-flyio-connection",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
},
"environment": {
"slug": "dev",
"name": "Development",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
},
"folder": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"path": "/my-secrets"
},
"destination": "flyio",
"destinationConfig": {
"appId": "..."
}
}
}
```
</Tab>
</Tabs>

View File

@@ -504,6 +504,7 @@
"integrations/app-connections/azure-key-vault",
"integrations/app-connections/camunda",
"integrations/app-connections/databricks",
"integrations/app-connections/flyio",
"integrations/app-connections/gcp",
"integrations/app-connections/github",
"integrations/app-connections/github-radar",
@@ -538,6 +539,7 @@
"integrations/secret-syncs/azure-key-vault",
"integrations/secret-syncs/camunda",
"integrations/secret-syncs/databricks",
"integrations/secret-syncs/flyio",
"integrations/secret-syncs/gcp-secret-manager",
"integrations/secret-syncs/github",
"integrations/secret-syncs/hashicorp-vault",
@@ -1263,6 +1265,18 @@
"api-reference/endpoints/app-connections/databricks/delete"
]
},
{
"group": "Fly.io",
"pages": [
"api-reference/endpoints/app-connections/flyio/list",
"api-reference/endpoints/app-connections/flyio/available",
"api-reference/endpoints/app-connections/flyio/get-by-id",
"api-reference/endpoints/app-connections/flyio/get-by-name",
"api-reference/endpoints/app-connections/flyio/create",
"api-reference/endpoints/app-connections/flyio/update",
"api-reference/endpoints/app-connections/flyio/delete"
]
},
{
"group": "GCP",
"pages": [
@@ -1560,6 +1574,19 @@
"api-reference/endpoints/secret-syncs/databricks/remove-secrets"
]
},
{
"group": "Fly.io",
"pages": [
"api-reference/endpoints/secret-syncs/flyio/list",
"api-reference/endpoints/secret-syncs/flyio/get-by-id",
"api-reference/endpoints/secret-syncs/flyio/get-by-name",
"api-reference/endpoints/secret-syncs/flyio/create",
"api-reference/endpoints/secret-syncs/flyio/update",
"api-reference/endpoints/secret-syncs/flyio/delete",
"api-reference/endpoints/secret-syncs/flyio/sync-secrets",
"api-reference/endpoints/secret-syncs/flyio/remove-secrets"
]
},
{
"group": "GCP Secret Manager",
"pages": [

View File

@@ -0,0 +1,51 @@
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 { TFlyioApp, useFlyioConnectionListApps } from "@app/hooks/api/appConnections/flyio";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { TSecretSyncForm } from "../schemas";
export const FlyioSyncFields = () => {
const { control, setValue } = useFormContext<
TSecretSyncForm & { destination: SecretSync.Flyio }
>();
const connectionId = useWatch({ name: "connection.id", control });
const { data: apps, isLoading: isAppsLoading } = useFlyioConnectionListApps(connectionId, {
enabled: Boolean(connectionId)
});
return (
<>
<SecretSyncConnectionField
onChange={() => {
setValue("destinationConfig.appId", "");
}}
/>
<Controller
name="destinationConfig.appId"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error)} errorText={error?.message} label="App">
<FilterableSelect
menuPlacement="top"
isLoading={isAppsLoading && Boolean(connectionId)}
isDisabled={!connectionId}
value={apps?.find((v) => v.id === value) ?? null}
onChange={(option) => onChange((option as SingleValue<TFlyioApp>)?.id ?? null)}
options={apps}
placeholder="Select an app..."
getOptionLabel={(option) => option.name}
getOptionValue={(option) => option.id}
/>
</FormControl>
)}
/>
</>
);
};

View File

@@ -11,6 +11,7 @@ import { AzureDevOpsSyncFields } from "./AzureDevOpsSyncFields";
import { AzureKeyVaultSyncFields } from "./AzureKeyVaultSyncFields";
import { CamundaSyncFields } from "./CamundaSyncFields";
import { DatabricksSyncFields } from "./DatabricksSyncFields";
import { FlyioSyncFields } from "./FlyioSyncFields";
import { GcpSyncFields } from "./GcpSyncFields";
import { GitHubSyncFields } from "./GitHubSyncFields";
import { HCVaultSyncFields } from "./HCVaultSyncFields";
@@ -61,6 +62,8 @@ export const SecretSyncDestinationFields = () => {
return <OCIVaultSyncFields />;
case SecretSync.OnePass:
return <OnePassSyncFields />;
case SecretSync.Flyio:
return <FlyioSyncFields />;
default:
throw new Error(`Unhandled Destination Config Field: ${destination}`);
}

View File

@@ -52,6 +52,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
case SecretSync.TeamCity:
case SecretSync.OnePass:
case SecretSync.OCIVault:
case SecretSync.Flyio:
AdditionalSyncOptionsFieldsComponent = null;
break;
default:

View File

@@ -0,0 +1,12 @@
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 FlyioSyncReviewFields = () => {
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.Flyio }>();
const appId = watch("destinationConfig.appId");
return <GenericFieldLabel label="App ID">{appId}</GenericFieldLabel>;
};

View File

@@ -20,6 +20,7 @@ import { AzureDevOpsSyncReviewFields } from "./AzureDevOpsSyncReviewFields";
import { AzureKeyVaultSyncReviewFields } from "./AzureKeyVaultSyncReviewFields";
import { CamundaSyncReviewFields } from "./CamundaSyncReviewFields";
import { DatabricksSyncReviewFields } from "./DatabricksSyncReviewFields";
import { FlyioSyncReviewFields } from "./FlyioSyncReviewFields";
import { GcpSyncReviewFields } from "./GcpSyncReviewFields";
import { GitHubSyncReviewFields } from "./GitHubSyncReviewFields";
import { HCVaultSyncReviewFields } from "./HCVaultSyncReviewFields";
@@ -104,6 +105,9 @@ export const SecretSyncReviewFields = () => {
case SecretSync.OnePass:
DestinationFieldsComponent = <OnePassSyncReviewFields />;
break;
case SecretSync.Flyio:
DestinationFieldsComponent = <FlyioSyncReviewFields />;
break;
default:
throw new Error(`Unhandled Destination Review Fields: ${destination}`);
}

View File

@@ -0,0 +1,13 @@
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 FlyioSyncDestinationSchema = BaseSecretSyncSchema().merge(
z.object({
destination: z.literal(SecretSync.Flyio),
destinationConfig: z.object({
appId: z.string().trim().min(1, "App ID required")
})
})
);

View File

@@ -8,6 +8,7 @@ import { AzureDevOpsSyncDestinationSchema } from "./azure-devops-sync-destinatio
import { AzureKeyVaultSyncDestinationSchema } from "./azure-key-vault-sync-destination-schema";
import { CamundaSyncDestinationSchema } from "./camunda-sync-destination-schema";
import { DatabricksSyncDestinationSchema } from "./databricks-sync-destination-schema";
import { FlyioSyncDestinationSchema } from "./flyio-sync-destination-schema";
import { GcpSyncDestinationSchema } from "./gcp-sync-destination-schema";
import { GitHubSyncDestinationSchema } from "./github-sync-destination-schema";
import { HCVaultSyncDestinationSchema } from "./hc-vault-sync-destination-schema";
@@ -35,7 +36,8 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
HCVaultSyncDestinationSchema,
TeamCitySyncDestinationSchema,
OCIVaultSyncDestinationSchema,
OnePassSyncDestinationSchema
OnePassSyncDestinationSchema,
FlyioSyncDestinationSchema
]);
export const SecretSyncFormSchema = SecretSyncUnionSchema;

View File

@@ -19,6 +19,7 @@ import {
AzureKeyVaultConnectionMethod,
CamundaConnectionMethod,
DatabricksConnectionMethod,
FlyioConnectionMethod,
GcpConnectionMethod,
GitHubConnectionMethod,
GitHubRadarConnectionMethod,
@@ -78,7 +79,8 @@ export const APP_CONNECTION_MAP: Record<
[AppConnection.LDAP]: { name: "LDAP", image: "LDAP.png", size: 65 },
[AppConnection.TeamCity]: { name: "TeamCity", image: "TeamCity.png" },
[AppConnection.OCI]: { name: "OCI", image: "Oracle.png", enterprise: true },
[AppConnection.OnePass]: { name: "1Password", image: "1Password.png" }
[AppConnection.OnePass]: { name: "1Password", image: "1Password.png" },
[AppConnection.Flyio]: { name: "Fly.io", image: "Flyio.svg" }
};
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
@@ -117,6 +119,7 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
case TeamCityConnectionMethod.AccessToken:
case AzureDevOpsConnectionMethod.AccessToken:
case WindmillConnectionMethod.AccessToken:
case FlyioConnectionMethod.AccessToken:
return { name: "Access Token", icon: faKey };
case Auth0ConnectionMethod.ClientCredentials:
return { name: "Client Credentials", icon: faServer };

View File

@@ -60,6 +60,10 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
[SecretSync.OnePass]: {
name: "1Password",
image: "1Password.png"
},
[SecretSync.Flyio]: {
name: "Fly.io",
image: "Flyio.svg"
}
};
@@ -80,7 +84,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
[SecretSync.HCVault]: AppConnection.HCVault,
[SecretSync.TeamCity]: AppConnection.TeamCity,
[SecretSync.OCIVault]: AppConnection.OCI,
[SecretSync.OnePass]: AppConnection.OnePass
[SecretSync.OnePass]: AppConnection.OnePass,
[SecretSync.Flyio]: AppConnection.Flyio
};
export const SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP: Record<

View File

@@ -22,5 +22,6 @@ export enum AppConnection {
LDAP = "ldap",
TeamCity = "teamcity",
OCI = "oci",
OnePass = "1password"
OnePass = "1password",
Flyio = "flyio"
}

View File

@@ -0,0 +1,2 @@
export * from "./queries";
export * from "./types";

View File

@@ -0,0 +1,36 @@
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { appConnectionKeys } from "../queries";
import { TFlyioApp } from "./types";
const flyioConnectionKeys = {
all: [...appConnectionKeys.all, "flyio"] as const,
listApps: (connectionId: string) => [...flyioConnectionKeys.all, "apps", connectionId] as const
};
export const useFlyioConnectionListApps = (
connectionId: string,
options?: Omit<
UseQueryOptions<
TFlyioApp[],
unknown,
TFlyioApp[],
ReturnType<typeof flyioConnectionKeys.listApps>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: flyioConnectionKeys.listApps(connectionId),
queryFn: async () => {
const { data } = await apiRequest.get<TFlyioApp[]>(
`/api/v1/app-connections/flyio/${connectionId}/apps`
);
return data;
},
...options
});
};

View File

@@ -0,0 +1,4 @@
export type TFlyioApp = {
id: string;
name: string;
};

View File

@@ -110,6 +110,10 @@ export type TOnePassConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.OnePass;
};
export type TFlyioConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.Flyio;
};
export type TAppConnectionOption =
| TAwsConnectionOption
| TGitHubConnectionOption
@@ -132,7 +136,8 @@ export type TAppConnectionOption =
| THCVaultConnectionOption
| TTeamCityConnectionOption
| TOCIConnectionOption
| TOnePassConnectionOption;
| TOnePassConnectionOption
| TFlyioConnectionOption;
export type TAppConnectionOptionMap = {
[AppConnection.AWS]: TAwsConnectionOption;
@@ -159,4 +164,5 @@ export type TAppConnectionOptionMap = {
[AppConnection.TeamCity]: TTeamCityConnectionOption;
[AppConnection.OCI]: TOCIConnectionOption;
[AppConnection.OnePass]: TOnePassConnectionOption;
[AppConnection.Flyio]: TFlyioConnectionOption;
};

View File

@@ -0,0 +1,13 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
export enum FlyioConnectionMethod {
AccessToken = "access-token"
}
export type TFlyioConnection = TRootAppConnection & { app: AppConnection.Flyio } & {
method: FlyioConnectionMethod.AccessToken;
credentials: {
accessToken: string;
};
};

View File

@@ -9,6 +9,7 @@ import { TAzureDevOpsConnection } from "./azure-devops-connection";
import { TAzureKeyVaultConnection } from "./azure-key-vault-connection";
import { TCamundaConnection } from "./camunda-connection";
import { TDatabricksConnection } from "./databricks-connection";
import { TFlyioConnection } from "./flyio-connection";
import { TGcpConnection } from "./gcp-connection";
import { TGitHubConnection } from "./github-connection";
import { TGitHubRadarConnection } from "./github-radar-connection";
@@ -34,6 +35,7 @@ export * from "./azure-devops-connection";
export * from "./azure-key-vault-connection";
export * from "./camunda-connection";
export * from "./databricks-connection";
export * from "./flyio-connection";
export * from "./gcp-connection";
export * from "./github-connection";
export * from "./github-radar-connection";
@@ -74,7 +76,8 @@ export type TAppConnection =
| TLdapConnection
| TTeamCityConnection
| TOCIConnection
| TOnePassConnection;
| TOnePassConnection
| TFlyioConnection;
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
@@ -126,4 +129,5 @@ export type TAppConnectionMap = {
[AppConnection.TeamCity]: TTeamCityConnection;
[AppConnection.OCI]: TOCIConnection;
[AppConnection.OnePass]: TOnePassConnection;
[AppConnection.Flyio]: TFlyioConnection;
};

View File

@@ -15,7 +15,8 @@ export enum SecretSync {
HCVault = "hashicorp-vault",
TeamCity = "teamcity",
OCIVault = "oci-vault",
OnePass = "1password"
OnePass = "1password",
Flyio = "flyio"
}
export enum SecretSyncStatus {

View File

@@ -0,0 +1,15 @@
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 TFlyioSync = TRootSecretSync & {
destination: SecretSync.Flyio;
destinationConfig: {
appId: string;
};
connection: {
app: AppConnection.Flyio;
name: string;
id: string;
};
};

View File

@@ -9,6 +9,7 @@ import { TAzureDevOpsSync } from "./azure-devops-sync";
import { TAzureKeyVaultSync } from "./azure-key-vault-sync";
import { TCamundaSync } from "./camunda-sync";
import { TDatabricksSync } from "./databricks-sync";
import { TFlyioSync } from "./flyio-sync";
import { TGcpSync } from "./gcp-sync";
import { TGitHubSync } from "./github-sync";
import { THCVaultSync } from "./hc-vault-sync";
@@ -43,7 +44,8 @@ export type TSecretSync =
| THCVaultSync
| TTeamCitySync
| TOCIVaultSync
| TOnePassSync;
| TOnePassSync
| TFlyioSync;
export type TListSecretSyncs = { secretSyncs: TSecretSync[] };

View File

@@ -18,6 +18,7 @@ import { AzureDevOpsConnectionForm } from "./AzureDevOpsConnectionForm";
import { AzureKeyVaultConnectionForm } from "./AzureKeyVaultConnectionForm";
import { CamundaConnectionForm } from "./CamundaConnectionForm";
import { DatabricksConnectionForm } from "./DatabricksConnectionForm";
import { FlyioConnectionForm } from "./FlyioConnectionForm";
import { GcpConnectionForm } from "./GcpConnectionForm";
import { GitHubConnectionForm } from "./GitHubConnectionForm";
import { GitHubRadarConnectionForm } from "./GitHubRadarConnectionForm";
@@ -119,6 +120,8 @@ const CreateForm = ({ app, onComplete }: CreateFormProps) => {
return <OCIConnectionForm onSubmit={onSubmit} />;
case AppConnection.OnePass:
return <OnePassConnectionForm onSubmit={onSubmit} />;
case AppConnection.Flyio:
return <FlyioConnectionForm onSubmit={onSubmit} />;
default:
throw new Error(`Unhandled App ${app}`);
}
@@ -203,6 +206,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
return <OCIConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.OnePass:
return <OnePassConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.Flyio:
return <FlyioConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
default:
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
}

View File

@@ -0,0 +1,136 @@
import { Controller, FormProvider, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
Button,
FormControl,
ModalClose,
SecretInput,
Select,
SelectItem
} from "@app/components/v2";
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
import { FlyioConnectionMethod, TFlyioConnection } from "@app/hooks/api/appConnections";
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import {
genericAppConnectionFieldsSchema,
GenericAppConnectionsFields
} from "./GenericAppConnectionFields";
type Props = {
appConnection?: TFlyioConnection;
onSubmit: (formData: FormData) => void;
};
const rootSchema = genericAppConnectionFieldsSchema.extend({
app: z.literal(AppConnection.Flyio)
});
const formSchema = z.discriminatedUnion("method", [
rootSchema.extend({
method: z.literal(FlyioConnectionMethod.AccessToken),
credentials: z.object({
accessToken: z
.string()
.trim()
.min(1, "Access Token required")
.startsWith("FlyV1", "Token must start with 'FlyV1'")
})
})
]);
type FormData = z.infer<typeof formSchema>;
export const FlyioConnectionForm = ({ appConnection, onSubmit }: Props) => {
const isUpdate = Boolean(appConnection);
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: appConnection ?? {
app: AppConnection.Flyio,
method: FlyioConnectionMethod.AccessToken
}
});
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.Flyio].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(FlyioConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<Controller
name="credentials.accessToken"
control={control}
shouldUnregister
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Access Token"
>
<SecretInput
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
value={value}
onChange={(e) => onChange(e.target.value)}
/>
</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 Fly.io"}
</Button>
<ModalClose asChild>
<Button colorSchema="secondary" variant="plain">
Cancel
</Button>
</ModalClose>
</div>
</form>
</FormProvider>
);
};

View File

@@ -0,0 +1,14 @@
import { TFlyioSync } from "@app/hooks/api/secretSyncs/types/flyio-sync";
import { getSecretSyncDestinationColValues } from "../helpers";
import { SecretSyncTableCell } from "../SecretSyncTableCell";
type Props = {
secretSync: TFlyioSync;
};
export const FlyioSyncDestinationCol = ({ secretSync }: Props) => {
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
};

View File

@@ -8,6 +8,7 @@ import { AzureDevOpsSyncDestinationCol } from "./AzureDevOpsSyncDestinationCol";
import { AzureKeyVaultDestinationSyncCol } from "./AzureKeyVaultDestinationSyncCol";
import { CamundaSyncDestinationCol } from "./CamundaSyncDestinationCol";
import { DatabricksSyncDestinationCol } from "./DatabricksSyncDestinationCol";
import { FlyioSyncDestinationCol } from "./FlyioSyncDestinationCol";
import { GcpSyncDestinationCol } from "./GcpSyncDestinationCol";
import { GitHubSyncDestinationCol } from "./GitHubSyncDestinationCol";
import { HCVaultSyncDestinationCol } from "./HCVaultSyncDestinationCol";
@@ -58,6 +59,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
return <OnePassSyncDestinationCol secretSync={secretSync} />;
case SecretSync.AzureDevOps:
return <AzureDevOpsSyncDestinationCol secretSync={secretSync} />;
case SecretSync.Flyio:
return <FlyioSyncDestinationCol secretSync={secretSync} />;
default:
throw new Error(
`Unhandled Secret Sync Destination Col: ${(secretSync as TSecretSync).destination}`

View File

@@ -116,6 +116,10 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
primaryText = destinationConfig.devopsProjectName;
secondaryText = destinationConfig.devopsProjectId;
break;
case SecretSync.Flyio:
primaryText = destinationConfig.appId;
secondaryText = "App ID";
break;
default:
throw new Error(`Unhandled Destination Col Values ${destination}`);
}

View File

@@ -0,0 +1,14 @@
import { GenericFieldLabel } from "@app/components/secret-syncs";
import { TFlyioSync } from "@app/hooks/api/secretSyncs/types/flyio-sync";
type Props = {
secretSync: TFlyioSync;
};
export const FlyioSyncDestinationSection = ({ secretSync }: Props) => {
const {
destinationConfig: { appId }
} = secretSync;
return <GenericFieldLabel label="App ID">{appId}</GenericFieldLabel>;
};

View File

@@ -18,6 +18,7 @@ import { AzureDevOpsSyncDestinationSection } from "./AzureDevOpsSyncDestinationS
import { AzureKeyVaultSyncDestinationSection } from "./AzureKeyVaultSyncDestinationSection";
import { CamundaSyncDestinationSection } from "./CamundaSyncDestinationSection";
import { DatabricksSyncDestinationSection } from "./DatabricksSyncDestinationSection";
import { FlyioSyncDestinationSection } from "./FlyioSyncDestinationSection";
import { GcpSyncDestinationSection } from "./GcpSyncDestinationSection";
import { GitHubSyncDestinationSection } from "./GitHubSyncDestinationSection";
import { HCVaultSyncDestinationSection } from "./HCVaultSyncDestinationSection";
@@ -93,6 +94,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
case SecretSync.AzureDevOps:
DestinationComponents = <AzureDevOpsSyncDestinationSection secretSync={secretSync} />;
break;
case SecretSync.Flyio:
DestinationComponents = <FlyioSyncDestinationSection secretSync={secretSync} />;
break;
default:
throw new Error(`Unhandled Destination Section components: ${destination}`);
}

View File

@@ -52,6 +52,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
case SecretSync.TeamCity:
case SecretSync.OCIVault:
case SecretSync.OnePass:
case SecretSync.Flyio:
AdditionalSyncOptionsComponent = null;
break;
default: