+
{t('login.create-account')}
+
+ Forgot password?
+
+ Recover your account
+
+
}
diff --git a/frontend/src/hooks/api/secretFolders/index.tsx b/frontend/src/hooks/api/secretFolders/index.tsx
index c1cc056d1b..8b48f3e204 100644
--- a/frontend/src/hooks/api/secretFolders/index.tsx
+++ b/frontend/src/hooks/api/secretFolders/index.tsx
@@ -1 +1,7 @@
-export { useCreateFolder, useDeleteFolder, useGetProjectFolders, useUpdateFolder } from './queries';
+export {
+ useCreateFolder,
+ useDeleteFolder,
+ useGetProjectFolders,
+ useGetProjectFoldersBatch,
+ useUpdateFolder
+} from './queries';
diff --git a/frontend/src/hooks/api/secretFolders/queries.tsx b/frontend/src/hooks/api/secretFolders/queries.tsx
index 008d6f26f5..57b58afea4 100644
--- a/frontend/src/hooks/api/secretFolders/queries.tsx
+++ b/frontend/src/hooks/api/secretFolders/queries.tsx
@@ -1,5 +1,5 @@
import { useCallback } from 'react';
-import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { useMutation, useQueries, useQuery, useQueryClient } from '@tanstack/react-query';
import { apiRequest } from '@app/config/request';
@@ -7,6 +7,7 @@ import { secretSnapshotKeys } from '../secretSnapshots/queries';
import {
CreateFolderDTO,
DeleteFolderDTO,
+ GetProjectFoldersBatchDTO,
GetProjectFoldersDTO,
TSecretFolder,
UpdateFolderDTO
@@ -17,6 +18,26 @@ const queryKeys = {
['secret-folders', { workspaceId, environment, parentFolderId }] as const
};
+const fetchProjectFolders = async (
+ workspaceId: string,
+ environment: string,
+ parentFolderId?: string,
+ parentFolderPath?: string
+) => {
+ const { data } = await apiRequest.get<{ folders: TSecretFolder[]; dir: TSecretFolder[] }>(
+ '/api/v1/folders',
+ {
+ params: {
+ workspaceId,
+ environment,
+ parentFolderId,
+ parentFolderPath
+ }
+ }
+ );
+ return data;
+};
+
export const useGetProjectFolders = ({
workspaceId,
parentFolderId,
@@ -27,19 +48,7 @@ export const useGetProjectFolders = ({
useQuery({
queryKey: queryKeys.getSecretFolders(workspaceId, environment, parentFolderId),
enabled: Boolean(workspaceId) && Boolean(environment) && !isPaused,
- queryFn: async () => {
- const { data } = await apiRequest.get<{ folders: TSecretFolder[]; dir: TSecretFolder[] }>(
- '/api/v1/folders',
- {
- params: {
- workspaceId,
- environment,
- parentFolderId
- }
- }
- );
- return data;
- },
+ queryFn: async () => fetchProjectFolders(workspaceId, environment, parentFolderId),
select: useCallback(
({ folders, dir }: { folders: TSecretFolder[]; dir: TSecretFolder[] }) => ({
dir,
@@ -53,6 +62,25 @@ export const useGetProjectFolders = ({
)
});
+export const useGetProjectFoldersBatch = ({
+ folders = [],
+ isPaused,
+ parentFolderPath
+}: GetProjectFoldersBatchDTO) =>
+ useQueries({
+ queries: folders.map(({ workspaceId, environment, parentFolderId }) => ({
+ queryKey: queryKeys.getSecretFolders(workspaceId, environment, parentFolderPath),
+ queryFn: async () =>
+ fetchProjectFolders(workspaceId, environment, parentFolderId, parentFolderPath),
+ enabled: Boolean(workspaceId) && Boolean(environment) && !isPaused,
+ select: (data: { folders: TSecretFolder[]; dir: TSecretFolder[] }) => ({
+ environment,
+ folders: data.folders,
+ dir: data.dir
+ })
+ }))
+ });
+
export const useCreateFolder = () => {
const queryClient = useQueryClient();
diff --git a/frontend/src/hooks/api/secretFolders/types.ts b/frontend/src/hooks/api/secretFolders/types.ts
index a9ca176411..44a6a0859d 100644
--- a/frontend/src/hooks/api/secretFolders/types.ts
+++ b/frontend/src/hooks/api/secretFolders/types.ts
@@ -11,6 +11,12 @@ export type GetProjectFoldersDTO = {
sortDir?: 'asc' | 'desc';
};
+export type GetProjectFoldersBatchDTO = {
+ folders: Omit
[];
+ isPaused?: boolean;
+ parentFolderPath?: string;
+};
+
export type CreateFolderDTO = {
workspaceId: string;
environment: string;
diff --git a/frontend/src/hooks/api/secrets/queries.tsx b/frontend/src/hooks/api/secrets/queries.tsx
index e4ad89dd6f..39ac39b5e4 100644
--- a/frontend/src/hooks/api/secrets/queries.tsx
+++ b/frontend/src/hooks/api/secrets/queries.tsx
@@ -1,4 +1,5 @@
/* eslint-disable no-param-reassign */
+import { useCallback } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {
@@ -29,14 +30,16 @@ export const secretKeys = {
const fetchProjectEncryptedSecrets = async (
workspaceId: string,
env: string | string[],
- folderId?: string
+ folderId?: string,
+ secretPath?: string
) => {
if (typeof env === 'string') {
const { data } = await apiRequest.get<{ secrets: EncryptedSecret[] }>('/api/v2/secrets', {
params: {
environment: env,
workspaceId,
- folderId: folderId || undefined
+ folderId: folderId || undefined,
+ secretPath
}
});
return data.secrets;
@@ -52,7 +55,8 @@ const fetchProjectEncryptedSecrets = async (
params: {
environment: envPoint,
workspaceId,
- folderId
+ folderId,
+ secretPath
}
});
allEnvData = allEnvData.concat(data.secrets);
@@ -77,7 +81,7 @@ export const useGetProjectSecrets = ({
enabled: Boolean(decryptFileKey && workspaceId && env) && !isPaused,
queryKey: secretKeys.getProjectSecret(workspaceId, env, folderId),
queryFn: () => fetchProjectEncryptedSecrets(workspaceId, env, folderId),
- select: (data) => {
+ select: useCallback((data: EncryptedSecret[]) => {
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
const latestKey = decryptFileKey;
const key = decryptAssymmetric({
@@ -146,21 +150,24 @@ export const useGetProjectSecrets = ({
}
});
return { secrets: sharedSecrets };
- }
+ }, [decryptFileKey])
});
export const useGetProjectSecretsByKey = ({
workspaceId,
env,
decryptFileKey,
- isPaused
+ isPaused,
+ folderId,
+ secretPath
}: GetProjectSecretsDTO) =>
useQuery({
// wait for all values to be available
enabled: Boolean(decryptFileKey && workspaceId && env) && !isPaused,
- queryKey: secretKeys.getProjectSecret(workspaceId, env),
- queryFn: () => fetchProjectEncryptedSecrets(workspaceId, env),
- select: (data) => {
+ // right now secretpath is passed as folderid as only this is used in overview
+ queryKey: secretKeys.getProjectSecret(workspaceId, env, secretPath),
+ queryFn: () => fetchProjectEncryptedSecrets(workspaceId, env, folderId, secretPath),
+ select: useCallback((data: EncryptedSecret[]) => {
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
const latestKey = decryptFileKey;
const key = decryptAssymmetric({
@@ -235,7 +242,7 @@ export const useGetProjectSecretsByKey = ({
});
return { secrets: sharedSecrets, uniqueSecCount: Object.keys(uniqSecKeys).length };
- }
+ }, [decryptFileKey])
});
const fetchEncryptedSecretVersion = async (secretId: string, offset: number, limit: number) => {
@@ -256,7 +263,7 @@ export const useGetSecretVersion = (dto: GetSecretVersionsDTO) =>
enabled: Boolean(dto.secretId && dto.decryptFileKey),
queryKey: secretKeys.getSecretVersion(dto.secretId),
queryFn: () => fetchEncryptedSecretVersion(dto.secretId, dto.offset, dto.limit),
- select: (data) => {
+ select: useCallback((data: EncryptedSecretVersion[]) => {
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
const latestKey = dto.decryptFileKey;
const key = decryptAssymmetric({
@@ -278,7 +285,7 @@ export const useGetSecretVersion = (dto: GetSecretVersionsDTO) =>
})
}))
.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
- }
+ }, [])
});
export const useBatchSecretsOp = () => {
diff --git a/frontend/src/hooks/api/secrets/types.ts b/frontend/src/hooks/api/secrets/types.ts
index 2a5938c681..5bde496e26 100644
--- a/frontend/src/hooks/api/secrets/types.ts
+++ b/frontend/src/hooks/api/secrets/types.ts
@@ -96,6 +96,7 @@ export type GetProjectSecretsDTO = {
env: string | string[];
decryptFileKey: UserWsKeyPair;
folderId?: string;
+ secretPath?: string;
isPaused?: boolean;
onSuccess?: (data: DecryptedSecret[]) => void;
};
diff --git a/frontend/src/hooks/api/subscriptions/types.ts b/frontend/src/hooks/api/subscriptions/types.ts
index 61736b4f4e..af1b315d1d 100644
--- a/frontend/src/hooks/api/subscriptions/types.ts
+++ b/frontend/src/hooks/api/subscriptions/types.ts
@@ -12,5 +12,5 @@ export type SubscriptionPlan = {
tier: number;
workspaceLimit: number;
workspacesUsed: number;
- envLimit: number;
+ environmentLimit: number;
};
diff --git a/frontend/src/pages/api/integrations/createIntegration.ts b/frontend/src/pages/api/integrations/createIntegration.ts
index e3e7c010a2..331adff51f 100644
--- a/frontend/src/pages/api/integrations/createIntegration.ts
+++ b/frontend/src/pages/api/integrations/createIntegration.ts
@@ -3,6 +3,7 @@ import SecurityClient from '@app/components/utilities/SecurityClient';
interface Props {
integrationAuthId: string;
isActive: boolean;
+ secretPath: string;
app: string | null;
appId: string | null;
sourceEnvironment: string;
@@ -20,19 +21,20 @@ interface Props {
* @param {String} obj.accessToken - id of integration authorization for which to create the integration
* @returns
*/
-const createIntegration = ({
- integrationAuthId,
- isActive,
- app,
- appId,
- sourceEnvironment,
- targetEnvironment,
- targetEnvironmentId,
- targetService,
- targetServiceId,
- owner,
- path,
- region
+const createIntegration = ({
+ integrationAuthId,
+ isActive,
+ app,
+ appId,
+ sourceEnvironment,
+ targetEnvironment,
+ targetEnvironmentId,
+ targetService,
+ targetServiceId,
+ owner,
+ path,
+ region,
+ secretPath
}: Props) =>
SecurityClient.fetchCall('/api/v1/integration', {
method: 'POST',
@@ -40,18 +42,19 @@ const createIntegration = ({
'Content-Type': 'application/json'
},
body: JSON.stringify({
- integrationAuthId,
- isActive,
- app,
- appId,
- sourceEnvironment,
- targetEnvironment,
- targetEnvironmentId,
- targetService,
- targetServiceId,
- owner,
- path,
- region
+ integrationAuthId,
+ isActive,
+ app,
+ appId,
+ sourceEnvironment,
+ targetEnvironment,
+ targetEnvironmentId,
+ targetService,
+ targetServiceId,
+ owner,
+ path,
+ region,
+ secretPath
})
}).then(async (res) => {
if (res && res.status === 200) {
@@ -61,4 +64,4 @@ const createIntegration = ({
return undefined;
});
-export default createIntegration;
\ No newline at end of file
+export default createIntegration;
diff --git a/frontend/src/pages/dashboard/[id].tsx b/frontend/src/pages/dashboard/[id].tsx
index 8d31b42917..3c6fdf9951 100644
--- a/frontend/src/pages/dashboard/[id].tsx
+++ b/frontend/src/pages/dashboard/[id].tsx
@@ -12,13 +12,6 @@ const Dashboard = () => {
const queryEnv = router.query.env as string;
const isOverviewMode = !queryEnv;
- const onExploreEnv = (slug: string) => {
- router.push({
- pathname: router.pathname,
- query: { ...router.query, env: slug }
- });
- };
-
return (
<>
@@ -29,11 +22,7 @@ const Dashboard = () => {
- {isOverviewMode ? (
-
- ) : (
-
- )}
+ {isOverviewMode ? : }
>
);
diff --git a/frontend/src/pages/integrations/[id].tsx b/frontend/src/pages/integrations/[id].tsx
index 48f34532bb..4fb0070625 100644
--- a/frontend/src/pages/integrations/[id].tsx
+++ b/frontend/src/pages/integrations/[id].tsx
@@ -44,6 +44,7 @@ interface Integration {
integration: string;
targetEnvironment: string;
workspace: string;
+ secretPath:string;
integrationAuth: string;
}
@@ -441,7 +442,15 @@ export default function Integrations() {
handleDeleteIntegrationAuth={handleDeleteIntegrationAuth}
/>
) : (
-
+ <>
+
+
{t('integrations.cloud-integrations')}
+
{t('integrations.click-to-start')}
+
+
+ {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].map(elem =>
)}
+
+ >
)}
diff --git a/frontend/src/pages/integrations/aws-parameter-store/create.tsx b/frontend/src/pages/integrations/aws-parameter-store/create.tsx
index d06ac0061e..4f84ff4764 100644
--- a/frontend/src/pages/integrations/aws-parameter-store/create.tsx
+++ b/frontend/src/pages/integrations/aws-parameter-store/create.tsx
@@ -56,6 +56,7 @@ export default function AWSParameterStoreCreateIntegrationPage() {
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? '');
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
+ const [secretPath, setSecretPath] = useState('/');
const [selectedAWSRegion, setSelectedAWSRegion] = useState('');
const [path, setPath] = useState('');
const [pathErrorText, setPathErrorText] = useState('');
@@ -69,9 +70,9 @@ export default function AWSParameterStoreCreateIntegrationPage() {
}
}, [workspace]);
- const isValidAWSParameterStorePath = (secretPath: string) => {
+ const isValidAWSParameterStorePath = (awsStorePath: string) => {
const pattern = /^\/([\w-]+\/)*[\w-]+\/$/;
- return pattern.test(secretPath) && secretPath.length <= 2048;
+ return pattern.test(awsStorePath) && awsStorePath.length <= 2048;
};
const handleButtonClick = async () => {
@@ -101,7 +102,8 @@ export default function AWSParameterStoreCreateIntegrationPage() {
targetServiceId: null,
owner: null,
path,
- region: selectedAWSRegion
+ region: selectedAWSRegion,
+ secretPath
});
setIsLoading(false);
@@ -133,6 +135,13 @@ export default function AWSParameterStoreCreateIntegrationPage() {
))}
+