feat: Add Netlify app connection and secrets sync (#4205)

* fix: save wip

* feat: final impl

* feat: docs

* Update backend/src/services/app-connection/digital-ocean/digital-ocean-connection-service.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* chore: remove empty conflict files

* Update backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update frontend/src/components/secret-syncs/forms/schemas/digital-ocean-app-platform-sync-destination-schema.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update frontend/src/components/secret-syncs/forms/schemas/digital-ocean-app-platform-sync-destination-schema.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update frontend/src/components/secret-syncs/forms/SecretSyncDestinationFields/DigitalOceanAppPlatformSyncFields.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update backend/src/services/secret-sync/digital-ocean-app-platform/digital-ocean-app-platform-sync-schemas.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: lint

* feat: Netlify app connection and secrets sync

* feat: docs

* fix: type check

* fix: api client

* fix: lint and types

* fix: typecheck lint

* fix: docs

* fix: lint

* fix: lint

* fix: PR changes

* fix: typecheck

* fix: PR changes

* fix PR changes

* fix: PR Change

* fix: type error

* Small tweaks

* fix: support is_secret

* fix: revert is_secret

* fix: force update existing netlify secret

---------

Co-authored-by: sidwebworks <[email protected]>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: x032205 <[email protected]>
This commit is contained in:
Sid
2025-08-01 00:24:40 +05:30
committed by GitHub
co-authored by greptile-apps[bot] sidwebworks x032205
parent 3877fe524d
commit 79de7c5f08
90 changed files with 1747 additions and 2 deletions
@@ -34,5 +34,6 @@ export enum AppConnection {
Checkly = "checkly",
Supabase = "supabase",
DigitalOcean = "digital-ocean",
Netlify = "netlify",
Okta = "okta"
}
@@ -0,0 +1,2 @@
export * from "./queries";
export * from "./types";
@@ -0,0 +1,65 @@
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { appConnectionKeys } from "@app/hooks/api/appConnections";
import { TNetlifyAccount, TNetlifySite } from "./types";
const netlifyConnectionKeys = {
all: [...appConnectionKeys.all, "netlify"] as const,
listAccounts: (connectionId: string) =>
[...netlifyConnectionKeys.all, "accounts", connectionId] as const,
listSites: (connectionId: string, accountId: string) =>
[...netlifyConnectionKeys.all, "sites", connectionId, accountId] as const
};
export const useNetlifyConnectionListAccounts = (
connectionId: string,
options?: Omit<
UseQueryOptions<
TNetlifyAccount[],
unknown,
TNetlifyAccount[],
ReturnType<typeof netlifyConnectionKeys.listAccounts>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: netlifyConnectionKeys.listAccounts(connectionId),
queryFn: async () => {
const { data } = await apiRequest.get<{ accounts: TNetlifyAccount[] }>(
`/api/v1/app-connections/netlify/${connectionId}/accounts`
);
return data.accounts;
},
...options
});
};
export const useNetlifyConnectionListSites = (
connectionId: string,
accountId: string,
options?: Omit<
UseQueryOptions<
TNetlifySite[],
unknown,
TNetlifySite[],
ReturnType<typeof netlifyConnectionKeys.listSites>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: netlifyConnectionKeys.listSites(connectionId, accountId),
queryFn: async () => {
const { data } = await apiRequest.get<{ sites: TNetlifySite[] }>(
`/api/v1/app-connections/netlify/${connectionId}/accounts/${accountId}/sites`
);
return data.sites;
},
...options
});
};
@@ -0,0 +1,9 @@
export type TNetlifyAccount = {
id: string;
name: string;
};
export type TNetlifySite = {
id: string;
name: string;
};
@@ -156,6 +156,10 @@ export type TDigitalOceanConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.DigitalOcean;
};
export type TNetlifyConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.Netlify;
};
export type TOktaConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.Okta;
};
@@ -193,6 +197,7 @@ export type TAppConnectionOption =
| TRailwayConnectionOption
| TChecklyConnectionOption
| TDigitalOceanConnectionOption
| TNetlifyConnectionOption
| TOktaConnectionOption;
export type TAppConnectionOptionMap = {
@@ -231,5 +236,6 @@ export type TAppConnectionOptionMap = {
[AppConnection.Checkly]: TChecklyConnectionOption;
[AppConnection.Supabase]: TSupabaseConnectionOption;
[AppConnection.DigitalOcean]: TDigitalOceanConnectionOption;
[AppConnection.Netlify]: TNetlifyConnectionOption;
[AppConnection.Okta]: TOktaConnectionOption;
};
@@ -24,6 +24,7 @@ import { THumanitecConnection } from "./humanitec-connection";
import { TLdapConnection } from "./ldap-connection";
import { TMsSqlConnection } from "./mssql-connection";
import { TMySqlConnection } from "./mysql-connection";
import { TNetlifyConnection } from "./netlify-connection";
import { TOCIConnection } from "./oci-connection";
import { TOktaConnection } from "./okta-connection";
import { TOracleDBConnection } from "./oracledb-connection";
@@ -109,6 +110,7 @@ export type TAppConnection =
| TChecklyConnection
| TSupabaseConnection
| TDigitalOceanConnection
| TNetlifyConnection
| TOktaConnection;
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
@@ -181,5 +183,6 @@ export type TAppConnectionMap = {
[AppConnection.Checkly]: TChecklyConnection;
[AppConnection.Supabase]: TSupabaseConnection;
[AppConnection.DigitalOcean]: TDigitalOceanConnection;
[AppConnection.Netlify]: TNetlifyConnection;
[AppConnection.Okta]: TOktaConnection;
};
@@ -0,0 +1,14 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
export enum NetlifyConnectionMethod {
AccessToken = "access-token"
}
export type TNetlifyConnection = TRootAppConnection & {
app: AppConnection.Netlify;
method: NetlifyConnectionMethod.AccessToken;
credentials: {
accessToken: string;
};
};
@@ -27,6 +27,7 @@ export enum SecretSync {
Railway = "railway",
Checkly = "checkly",
DigitalOceanAppPlatform = "digital-ocean-app-platform",
Netlify = "netlify",
Bitbucket = "bitbucket"
}
@@ -21,6 +21,7 @@ import { TGitLabSync } from "./gitlab-sync";
import { THCVaultSync } from "./hc-vault-sync";
import { THerokuSync } from "./heroku-sync";
import { THumanitecSync } from "./humanitec-sync";
import { TNetlifySync } from "./netlify-sync";
import { TOCIVaultSync } from "./oci-vault-sync";
import { TRailwaySync } from "./railway-sync";
import { TRenderSync } from "./render-sync";
@@ -67,6 +68,7 @@ export type TSecretSync =
| TChecklySync
| TSupabaseSync
| TDigitalOceanAppPlatformSync
| TNetlifySync
| TBitbucketSync;
export type TListSecretSyncs = { secretSyncs: TSecretSync[] };
@@ -0,0 +1,20 @@
/* eslint-disable @typescript-eslint/no-empty-object-type */
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 TNetlifySync = TRootSecretSync & {
destination: SecretSync.Netlify;
destinationConfig: {
accountId: string;
accountName: string;
siteId?: string;
siteName?: string;
context?: string;
};
connection: {
app: AppConnection.Netlify;
name: string;
id: string;
};
};