feature secret rotations v2

This commit is contained in:
Scott Wilson
2025-03-31 20:34:48 -07:00
parent da094383b8
commit 766c1242fd
275 changed files with 9692 additions and 783 deletions
@@ -0,0 +1,9 @@
export enum SecretRotation {
PostgresCredentials = "postgres-credentials",
MsSqlCredentials = "mssql-credentials"
}
export enum SecretRotationStatus {
Success = "success",
Failed = "failed"
}
@@ -0,0 +1,3 @@
export * from "./enums";
export * from "./queries";
export * from "./types";
@@ -0,0 +1,91 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { dashboardKeys } from "@app/hooks/api/dashboard/queries";
import {
TCreateSecretRotationV2DTO,
TDeleteSecretRotationV2DTO,
TRotateSecretRotationV2DTO,
TSecretRotationV2Response,
TUpdateSecretRotationV2DTO
} from "@app/hooks/api/secretRotationsV2/types";
export const useCreateSecretRotationV2 = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ type, ...params }: TCreateSecretRotationV2DTO) => {
const { data } = await apiRequest.post<TSecretRotationV2Response>(
`/api/v2/secret-rotations/${type}`,
params
);
return data.secretRotation;
},
onSuccess: (_, { projectId, secretPath }) =>
queryClient.invalidateQueries({
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
})
});
};
export const useUpdateSecretRotationV2 = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ type, rotationId, ...params }: TUpdateSecretRotationV2DTO) => {
const { data } = await apiRequest.patch<TSecretRotationV2Response>(
`/api/v2/secret-rotations/${type}/${rotationId}`,
params
);
return data.secretRotation;
},
onSuccess: (_, { projectId, secretPath }) =>
queryClient.invalidateQueries({
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
})
});
};
export const useRotateSecretRotationV2 = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ type, rotationId }: TRotateSecretRotationV2DTO) => {
const { data } = await apiRequest.post<TSecretRotationV2Response>(
`/api/v2/secret-rotations/${type}/${rotationId}/rotate-secrets`
);
return data.secretRotation;
},
onSuccess: (_, { projectId, secretPath }) =>
queryClient.invalidateQueries({
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
}),
onError: (_, { projectId, secretPath }) =>
queryClient.invalidateQueries({
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
})
});
};
export const useDeleteSecretRotationV2 = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
type,
rotationId,
deleteSecrets,
revokeGeneratedCredentials
}: TDeleteSecretRotationV2DTO) => {
const { data } = await apiRequest.delete<TSecretRotationV2Response>(
`/api/v2/secret-rotations/${type}/${rotationId}`,
{ params: { deleteSecrets, revokeGeneratedCredentials } }
);
return data.secretRotation;
},
onSuccess: (_, { projectId, secretPath }) =>
queryClient.invalidateQueries({
queryKey: dashboardKeys.getDashboardSecrets({ projectId, secretPath })
})
});
};
@@ -0,0 +1,73 @@
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2/enums";
import {
TListSecretRotationV2Options,
TSecretRotationV2Option,
TViewSecretRotationGeneratedCredentialsResponse,
TViewSecretRotationV2GeneratedCredentialsDTO
} from "@app/hooks/api/secretRotationsV2/types";
export const secretRotationV2Keys = {
all: ["secret-rotations-v2"] as const,
options: () => [...secretRotationV2Keys.all, "options"] as const,
viewGeneratedCredentials: ({ type, rotationId }: TViewSecretRotationV2GeneratedCredentialsDTO) =>
[...secretRotationV2Keys.all, type, rotationId] as const
};
export const useSecretRotationV2Options = (
options?: Omit<
UseQueryOptions<
TSecretRotationV2Option[],
unknown,
TSecretRotationV2Option[],
ReturnType<typeof secretRotationV2Keys.options>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretRotationV2Keys.options(),
queryFn: async () => {
const { data } = await apiRequest.get<TListSecretRotationV2Options>(
"/api/v2/secret-rotations/options"
);
return data.secretRotationOptions;
},
...options
});
};
export const useSecretRotationV2Option = (type: SecretRotation) => {
const { data: rotationOptions, isPending } = useSecretRotationV2Options();
const rotationOption = rotationOptions?.find((option) => option.type === type);
return { rotationOption, isPending };
};
export const useViewSecretRotationV2GeneratedCredentials = (
{ rotationId, type }: TViewSecretRotationV2GeneratedCredentialsDTO,
options?: Omit<
UseQueryOptions<
TViewSecretRotationGeneratedCredentialsResponse,
unknown,
TViewSecretRotationGeneratedCredentialsResponse,
ReturnType<typeof secretRotationV2Keys.viewGeneratedCredentials>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretRotationV2Keys.viewGeneratedCredentials({ rotationId, type }),
queryFn: async () => {
const { data } = await apiRequest.get<TViewSecretRotationGeneratedCredentialsResponse>(
`/api/v2/secret-rotations/${type}/${rotationId}/generated-credentials`
);
return data;
},
...options
});
};
@@ -0,0 +1,73 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import {
TMsSqlCredentialsRotation,
TMsSqlCredentialsRotationGeneratedCredentialsResponse
} from "@app/hooks/api/secretRotationsV2/types/mssql-credentials-rotation";
import {
TPostgresCredentialsRotation,
TPostgresCredentialsRotationGeneratedCredentialsResponse
} from "@app/hooks/api/secretRotationsV2/types/postgres-credentials-rotation";
import { TSqlOptionTemplate } from "@app/hooks/api/secretRotationsV2/types/shared";
import { SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
import { DiscriminativePick } from "@app/types";
export type TSecretRotationV2 = (TPostgresCredentialsRotation | TMsSqlCredentialsRotation) & {
secrets: (SecretV3RawSanitized | null)[];
};
export type TSecretRotationV2Option = {
name: string;
type: SecretRotation;
connection: AppConnection;
template: TSqlOptionTemplate;
};
export type TListSecretRotationV2Options = { secretRotationOptions: TSecretRotationV2Option[] };
export type TSecretRotationV2Response = { secretRotation: TSecretRotationV2 };
export type TViewSecretRotationGeneratedCredentialsResponse =
| TPostgresCredentialsRotationGeneratedCredentialsResponse
| TMsSqlCredentialsRotationGeneratedCredentialsResponse;
export type TCreateSecretRotationV2DTO = DiscriminativePick<
TSecretRotationV2,
| "name"
| "parameters"
| "secretsMapping"
| "description"
| "connectionId"
| "type"
| "isAutoRotationEnabled"
| "rotationInterval"
| "rotateAtUtc"
> & { environment: string; secretPath: string; projectId: string };
export type TUpdateSecretRotationV2DTO = Partial<
Omit<TCreateSecretRotationV2DTO, "type" | "secretPath" | "projectId">
> & {
type: SecretRotation;
rotationId: string;
// required for query invalidation
projectId: string;
secretPath: string;
};
export type TRotateSecretRotationV2DTO = {
rotationId: string;
type: SecretRotation;
// required for query invalidation
secretPath: string;
projectId: string;
};
export type TDeleteSecretRotationV2DTO = TRotateSecretRotationV2DTO & {
revokeGeneratedCredentials: boolean;
deleteSecrets: boolean;
};
export type TViewSecretRotationV2GeneratedCredentialsDTO = {
rotationId: string;
type: SecretRotation;
};
@@ -0,0 +1,17 @@
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import {
TSecretRotationV2Base,
TSecretRotationV2GeneratedCredentialsResponseBase,
TSqlCredentialsRotationGeneratedCredentials,
TSqlCredentialsRotationProperties
} from "@app/hooks/api/secretRotationsV2/types/shared";
export type TMsSqlCredentialsRotation = TSecretRotationV2Base & {
type: SecretRotation.MsSqlCredentials;
} & TSqlCredentialsRotationProperties;
export type TMsSqlCredentialsRotationGeneratedCredentialsResponse =
TSecretRotationV2GeneratedCredentialsResponseBase<
SecretRotation.MsSqlCredentials,
TSqlCredentialsRotationGeneratedCredentials
>;
@@ -0,0 +1,17 @@
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import {
TSecretRotationV2Base,
TSecretRotationV2GeneratedCredentialsResponseBase,
TSqlCredentialsRotationGeneratedCredentials,
TSqlCredentialsRotationProperties
} from "@app/hooks/api/secretRotationsV2/types/shared";
export type TPostgresCredentialsRotation = TSecretRotationV2Base & {
type: SecretRotation.PostgresCredentials;
} & TSqlCredentialsRotationProperties;
export type TPostgresCredentialsRotationGeneratedCredentialsResponse =
TSecretRotationV2GeneratedCredentialsResponseBase<
SecretRotation.PostgresCredentials,
TSqlCredentialsRotationGeneratedCredentials
>;
@@ -0,0 +1,2 @@
export * from "./secret-rotation-base";
export * from "./sql-credentials-rotation";
@@ -0,0 +1,53 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { SecretRotationStatus } from "@app/hooks/api/secretRotationsV2";
export type TSecretRotationV2Base = {
id: string;
name: string;
description?: string | null;
folderId: string;
connectionId: string;
createdAt: string;
updatedAt: string;
rotationInterval: number;
rotateAtUtc: {
hours: number;
minutes: number;
};
projectId: string;
rotationStatus: SecretRotationStatus | null;
lastRotationJobId: string | null;
lastRotatedAt: string;
lastRotationAttemptedAt: string;
lastRotationMessage?: string | null;
connection: {
app: AppConnection;
id: string;
name: string;
};
environment: {
id: string;
name: string;
slug: string;
};
folder: {
id: string;
path: string;
};
} & (
| {
nextRotationAt: string;
isAutoRotationEnabled: true;
}
| {
nextRotationAt?: null;
isAutoRotationEnabled: false;
}
);
export type TSecretRotationV2GeneratedCredentialsResponseBase<U, T> = {
activeIndex: 0 | 1;
generatedCredentials: [T, T | undefined];
type: U;
rotationId: string;
};
@@ -0,0 +1,20 @@
export type TSqlCredentialsRotationProperties = {
parameters: {
username1: string;
username2: string;
};
secretsMapping: {
username: string;
password: string;
};
};
export type TSqlOptionTemplate = {
secretsMapping: TSqlCredentialsRotationProperties["secretsMapping"];
createUserStatement: string;
};
export type TSqlCredentialsRotationGeneratedCredentials = {
username: string;
password: string;
};