diff --git a/backend/src/server/routes/v1/secret-sync-routers/index.ts b/backend/src/server/routes/v1/secret-sync-routers/index.ts index e778dbd7c..2acf2dfc9 100644 --- a/backend/src/server/routes/v1/secret-sync-routers/index.ts +++ b/backend/src/server/routes/v1/secret-sync-routers/index.ts @@ -23,6 +23,7 @@ import { registerHerokuSyncRouter } from "./heroku-sync-router"; import { registerHumanitecSyncRouter } from "./humanitec-sync-router"; import { registerLaravelForgeSyncRouter } from "./laravel-forge-sync-router"; import { registerNetlifySyncRouter } from "./netlify-sync-router"; +import { registerNorthflankSyncRouter } from "./northflank-sync-router"; import { registerRailwaySyncRouter } from "./railway-sync-router"; import { registerRenderSyncRouter } from "./render-sync-router"; import { registerSupabaseSyncRouter } from "./supabase-sync-router"; @@ -64,6 +65,7 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record + registerSyncSecretsEndpoints({ + destination: SecretSync.Northflank, + server, + responseSchema: NorthflankSyncSchema, + createSchema: CreateNorthflankSyncSchema, + updateSchema: UpdateNorthflankSyncSchema + }); diff --git a/backend/src/server/routes/v1/secret-sync-routers/secret-sync-router.ts b/backend/src/server/routes/v1/secret-sync-routers/secret-sync-router.ts index 1bfa32eeb..e6fd39e34 100644 --- a/backend/src/server/routes/v1/secret-sync-routers/secret-sync-router.ts +++ b/backend/src/server/routes/v1/secret-sync-routers/secret-sync-router.ts @@ -46,6 +46,7 @@ import { HerokuSyncListItemSchema, HerokuSyncSchema } from "@app/services/secret import { HumanitecSyncListItemSchema, HumanitecSyncSchema } from "@app/services/secret-sync/humanitec"; import { LaravelForgeSyncListItemSchema, LaravelForgeSyncSchema } from "@app/services/secret-sync/laravel-forge"; import { NetlifySyncListItemSchema, NetlifySyncSchema } from "@app/services/secret-sync/netlify"; +import { NorthflankSyncListItemSchema, NorthflankSyncSchema } from "@app/services/secret-sync/northflank"; import { RailwaySyncListItemSchema, RailwaySyncSchema } from "@app/services/secret-sync/railway/railway-sync-schemas"; import { RenderSyncListItemSchema, RenderSyncSchema } from "@app/services/secret-sync/render/render-sync-schemas"; import { SupabaseSyncListItemSchema, SupabaseSyncSchema } from "@app/services/secret-sync/supabase"; @@ -85,6 +86,7 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [ ChecklySyncSchema, DigitalOceanAppPlatformSyncSchema, NetlifySyncSchema, + NorthflankSyncSchema, BitbucketSyncSchema, LaravelForgeSyncSchema ]); @@ -119,6 +121,7 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [ ChecklySyncListItemSchema, SupabaseSyncListItemSchema, NetlifySyncListItemSchema, + NorthflankSyncListItemSchema, BitbucketSyncListItemSchema, LaravelForgeSyncListItemSchema ]); diff --git a/backend/src/services/secret-sync/northflank/index.ts b/backend/src/services/secret-sync/northflank/index.ts new file mode 100644 index 000000000..7fab276cf --- /dev/null +++ b/backend/src/services/secret-sync/northflank/index.ts @@ -0,0 +1,4 @@ +export * from "./northflank-sync-constants"; +export * from "./northflank-sync-fns"; +export * from "./northflank-sync-schemas"; +export * from "./northflank-sync-types"; diff --git a/backend/src/services/secret-sync/northflank/northflank-sync-constants.ts b/backend/src/services/secret-sync/northflank/northflank-sync-constants.ts new file mode 100644 index 000000000..d4b851217 --- /dev/null +++ b/backend/src/services/secret-sync/northflank/northflank-sync-constants.ts @@ -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 NORTHFLANK_SYNC_LIST_OPTION: TSecretSyncListItem = { + name: "Northflank", + destination: SecretSync.Northflank, + connection: AppConnection.Northflank, + canImportSecrets: true +}; diff --git a/backend/src/services/secret-sync/northflank/northflank-sync-fns.ts b/backend/src/services/secret-sync/northflank/northflank-sync-fns.ts new file mode 100644 index 000000000..f8fa2ef0b --- /dev/null +++ b/backend/src/services/secret-sync/northflank/northflank-sync-fns.ts @@ -0,0 +1,18 @@ +import { TSecretMap, TSecretSyncWithCredentials } from "@app/services/secret-sync/secret-sync-types"; + +export const NorthflankSyncFns = { + syncSecrets: async (secretSync: TSecretSyncWithCredentials, secretMap: TSecretMap): Promise => { + // TODO: Will be implemented in the follow up PR + throw new Error("Northflank secret sync not yet implemented"); + }, + + getSecrets: async (secretSync: TSecretSyncWithCredentials): Promise => { + // TODO: Will be implemented in the follow up PR + throw new Error("Northflank secret retrieval not yet implemented"); + }, + + removeSecrets: async (secretSync: TSecretSyncWithCredentials, secretMap: TSecretMap): Promise => { + // TODO: Will be implemented in the follow up PR + throw new Error("Northflank secret removal not yet implemented"); + } +}; diff --git a/backend/src/services/secret-sync/northflank/northflank-sync-schemas.ts b/backend/src/services/secret-sync/northflank/northflank-sync-schemas.ts new file mode 100644 index 000000000..a9e7fe21d --- /dev/null +++ b/backend/src/services/secret-sync/northflank/northflank-sync-schemas.ts @@ -0,0 +1,44 @@ +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 NorthflankSyncDestinationConfigSchema = z.object({ + // TODO: Will be implemented in the follow up secret sync PR + placeholder: z.string().optional() +}); + +const NorthflankSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true }; + +export const NorthflankSyncSchema = BaseSecretSyncSchema(SecretSync.Northflank, NorthflankSyncOptionsConfig).extend({ + destination: z.literal(SecretSync.Northflank), + destinationConfig: NorthflankSyncDestinationConfigSchema +}); + +export const CreateNorthflankSyncSchema = GenericCreateSecretSyncFieldsSchema( + SecretSync.Northflank, + NorthflankSyncOptionsConfig +).extend({ + destinationConfig: NorthflankSyncDestinationConfigSchema +}); + +export const UpdateNorthflankSyncSchema = GenericUpdateSecretSyncFieldsSchema( + SecretSync.Northflank, + NorthflankSyncOptionsConfig +).extend({ + destinationConfig: NorthflankSyncDestinationConfigSchema.optional() +}); + +export const NorthflankSyncListItemSchema = z.object({ + name: z.literal("Northflank"), + connection: z.literal(AppConnection.Northflank), + destination: z.literal(SecretSync.Northflank), + canImportSecrets: z.literal(true) +}); diff --git a/backend/src/services/secret-sync/northflank/northflank-sync-types.ts b/backend/src/services/secret-sync/northflank/northflank-sync-types.ts new file mode 100644 index 000000000..19c778012 --- /dev/null +++ b/backend/src/services/secret-sync/northflank/northflank-sync-types.ts @@ -0,0 +1,15 @@ +import { z } from "zod"; + +import { TNorthflankConnection } from "@app/services/app-connection/northflank"; + +import { CreateNorthflankSyncSchema, NorthflankSyncListItemSchema, NorthflankSyncSchema } from "./northflank-sync-schemas"; + +export type TNorthflankSyncListItem = z.infer; + +export type TNorthflankSync = z.infer; + +export type TNorthflankSyncInput = z.infer; + +export type TNorthflankSyncWithCredentials = TNorthflankSync & { + connection: TNorthflankConnection; +}; diff --git a/backend/src/services/secret-sync/secret-sync-fns.ts b/backend/src/services/secret-sync/secret-sync-fns.ts index 85fc27250..3068e803b 100644 --- a/backend/src/services/secret-sync/secret-sync-fns.ts +++ b/backend/src/services/secret-sync/secret-sync-fns.ts @@ -52,6 +52,7 @@ import { HumanitecSyncFns } from "./humanitec/humanitec-sync-fns"; import { LARAVEL_FORGE_SYNC_LIST_OPTION } from "./laravel-forge"; import { LaravelForgeSyncFns } from "./laravel-forge/laravel-forge-sync-fns"; import { NETLIFY_SYNC_LIST_OPTION, NetlifySyncFns } from "./netlify"; +import { NORTHFLANK_SYNC_LIST_OPTION, NorthflankSyncFns } from "./northflank"; import { RAILWAY_SYNC_LIST_OPTION } from "./railway/railway-sync-constants"; import { RailwaySyncFns } from "./railway/railway-sync-fns"; import { RENDER_SYNC_LIST_OPTION, RenderSyncFns } from "./render"; @@ -93,6 +94,7 @@ const SECRET_SYNC_LIST_OPTIONS: Record = { [SecretSync.Checkly]: CHECKLY_SYNC_LIST_OPTION, [SecretSync.DigitalOceanAppPlatform]: DIGITAL_OCEAN_APP_PLATFORM_SYNC_LIST_OPTION, [SecretSync.Netlify]: NETLIFY_SYNC_LIST_OPTION, + [SecretSync.Northflank]: NORTHFLANK_SYNC_LIST_OPTION, [SecretSync.Bitbucket]: BITBUCKET_SYNC_LIST_OPTION, [SecretSync.LaravelForge]: LARAVEL_FORGE_SYNC_LIST_OPTION }; @@ -278,6 +280,8 @@ export const SecretSyncFns = { return DigitalOceanAppPlatformSyncFns.syncSecrets(secretSync, schemaSecretMap); case SecretSync.Netlify: return NetlifySyncFns.syncSecrets(secretSync, schemaSecretMap); + case SecretSync.Northflank: + return NorthflankSyncFns.syncSecrets(secretSync, schemaSecretMap); case SecretSync.Bitbucket: return BitbucketSyncFns.syncSecrets(secretSync, schemaSecretMap); case SecretSync.LaravelForge: @@ -395,6 +399,9 @@ export const SecretSyncFns = { case SecretSync.Netlify: secretMap = await NetlifySyncFns.getSecrets(secretSync); break; + case SecretSync.Northflank: + secretMap = await NorthflankSyncFns.getSecrets(secretSync); + break; case SecretSync.Bitbucket: secretMap = await BitbucketSyncFns.getSecrets(secretSync); break; @@ -492,6 +499,8 @@ export const SecretSyncFns = { return DigitalOceanAppPlatformSyncFns.removeSecrets(secretSync, schemaSecretMap); case SecretSync.Netlify: return NetlifySyncFns.removeSecrets(secretSync, schemaSecretMap); + case SecretSync.Northflank: + return NorthflankSyncFns.removeSecrets(secretSync, schemaSecretMap); case SecretSync.Bitbucket: return BitbucketSyncFns.removeSecrets(secretSync, schemaSecretMap); case SecretSync.LaravelForge: diff --git a/backend/src/services/secret-sync/secret-sync-types.ts b/backend/src/services/secret-sync/secret-sync-types.ts index 9719f0a9e..649f91d44 100644 --- a/backend/src/services/secret-sync/secret-sync-types.ts +++ b/backend/src/services/secret-sync/secret-sync-types.ts @@ -124,6 +124,12 @@ import { TLaravelForgeSyncWithCredentials } from "./laravel-forge"; import { TNetlifySync, TNetlifySyncInput, TNetlifySyncListItem, TNetlifySyncWithCredentials } from "./netlify"; +import { + TNorthflankSync, + TNorthflankSyncInput, + TNorthflankSyncListItem, + TNorthflankSyncWithCredentials +} from "./northflank"; import { TRailwaySync, TRailwaySyncInput, @@ -187,6 +193,7 @@ export type TSecretSync = | TChecklySync | TSupabaseSync | TNetlifySync + | TNorthflankSync | TBitbucketSync; export type TSecretSyncWithCredentials = @@ -219,6 +226,7 @@ export type TSecretSyncWithCredentials = | TSupabaseSyncWithCredentials | TDigitalOceanAppPlatformSyncWithCredentials | TNetlifySyncWithCredentials + | TNorthflankSyncWithCredentials | TBitbucketSyncWithCredentials | TLaravelForgeSyncWithCredentials; @@ -252,6 +260,7 @@ export type TSecretSyncInput = | TSupabaseSyncInput | TDigitalOceanAppPlatformSyncInput | TNetlifySyncInput + | TNorthflankSyncInput | TBitbucketSyncInput | TLaravelForgeSyncInput; @@ -286,6 +295,7 @@ export type TSecretSyncListItem = | TSupabaseSyncListItem | TDigitalOceanAppPlatformSyncListItem | TNetlifySyncListItem + | TNorthflankSyncListItem | TBitbucketSyncListItem; export type TSyncOptionsConfig = { diff --git a/frontend/src/hooks/api/appConnections/types/app-options.ts b/frontend/src/hooks/api/appConnections/types/app-options.ts index 797e7a7c6..4d4425ef6 100644 --- a/frontend/src/hooks/api/appConnections/types/app-options.ts +++ b/frontend/src/hooks/api/appConnections/types/app-options.ts @@ -168,6 +168,10 @@ export type TLaravelForgeConnectionOption = TAppConnectionOptionBase & { app: AppConnection.LaravelForge; }; +export type TNorthflankConnectionOption = TAppConnectionOptionBase & { + app: AppConnection.Northflank; +}; + export type TAzureAdCsConnectionOption = TAppConnectionOptionBase & { app: AppConnection.AzureADCS; }; @@ -213,6 +217,7 @@ export type TAppConnectionOption = | TSupabaseConnectionOption | TDigitalOceanConnectionOption | TNetlifyConnectionOption + | TNorthflankConnectionOption | TOktaConnectionOption | TAzureAdCsConnectionOption | TLaravelForgeConnectionOption; @@ -254,6 +259,7 @@ export type TAppConnectionOptionMap = { [AppConnection.Supabase]: TSupabaseConnectionOption; [AppConnection.DigitalOcean]: TDigitalOceanConnectionOption; [AppConnection.Netlify]: TNetlifyConnectionOption; + [AppConnection.Northflank]: TNorthflankConnectionOption; [AppConnection.Okta]: TOktaConnectionOption; [AppConnection.AzureADCS]: TAzureAdCsConnectionOption; [AppConnection.Redis]: TRedisConnectionOption;