diff --git a/docs/documentation/platform/folder.mdx b/docs/documentation/platform/folder.mdx
index a3636a2ea..eab62549e 100644
--- a/docs/documentation/platform/folder.mdx
+++ b/docs/documentation/platform/folder.mdx
@@ -3,10 +3,10 @@ title: "Folders"
description: "Learn how to organize secrets with folders."
---
-Infisical Folders enable users to organize secrets using custom structures dependent on the intended use case (also known as **path-based secret storage**).
+Infisical Folders enable users to organize secrets using custom structures dependent on the intended use case (also known as **path-based secret storage**).
-It is great for organizing secrets around hierarchies with multiple services or types of secrets involved at large quantities.
-Infisical Folders can be infinitely nested to mirror your application architecture – whether it's microservices, monorepos,
+It is great for organizing secrets around hierarchies with multiple services or types of secrets involved at large quantities.
+Infisical Folders can be infinitely nested to mirror your application architecture – whether it's microservices, monorepos,
or any logical grouping that best suits your needs.
Consider the following structure for a microservice architecture:
@@ -22,7 +22,7 @@ Consider the following structure for a microservice architecture:
...
```
-In this example, we store environment variables for each microservice under each respective `/envars` folder.
+In this example, we store environment variables for each microservice under each respective `/envars` folder.
We also store user-specific secrets for micro-service 1 under `/service1/users`. With this folder structure in place, your applications only need to specify a path like `/microservice1/envars` to fetch secrets from there.
By extending this example, you can see how path-based secret storage provides a versatile approach to manage secrets for any architecture.
@@ -45,7 +45,27 @@ To delete a folder, hover over it and press the **X** button that appears on the
It's possible to compare the contents of folders across environments in the **Secrets Overview** page.
When you click on a folder, the table will display the items within it across environments.
-In the image below, you can see that the **Development** environment is the only one that contains items
+In the image below, you can see that the **Development** environment is the only one that contains items
in the `/users` folder, being other folders `/user-a`, `/user-b`, ... `/user-f`.
-
\ No newline at end of file
+
+
+### Replicating Folder Contents
+
+If you want to copy secrets or folders from one path to another, you can utilize the **Replicate Secrets** functionality located in the **Add Secret** dropdown.
+
+
+
+
+
+First, select the **Source Environment** and the **Source Root Path** you want to copy secrets *from*. In the example provided, we select `/dev-folder` as the source root path from the Development environment. This means any secrets within `/dev-folder` from Development will be replicated. By default, these secrets are copied into the *currently active* folder/path in your target environment (e.g., the root folder of your Staging environment in this scenario).
+
+As a final step, you can select the specific secrets you wish to copy and then click **Replicate Secrets**.
+
+
+
+The result shows two secrets successfully copied from the `/dev-folder` in the Development environment into the root folder of the Staging environment.
+
+
+ If you do not select a **Source Root Path**, the replication will consider the contents of the *entire root* of the **Source Environment** (e.g., the Development environment). In this example that would mean copying the `/dev-folder` itself rather than just its contents.
+
diff --git a/docs/images/platform/folder/replicate-secrets-modal.png b/docs/images/platform/folder/replicate-secrets-modal.png
new file mode 100644
index 000000000..eb9eeebf9
Binary files /dev/null and b/docs/images/platform/folder/replicate-secrets-modal.png differ
diff --git a/docs/images/platform/folder/replicate-secrets-result.png b/docs/images/platform/folder/replicate-secrets-result.png
new file mode 100644
index 000000000..473ad38d0
Binary files /dev/null and b/docs/images/platform/folder/replicate-secrets-result.png differ
diff --git a/docs/images/platform/folder/replicate-secrets.png b/docs/images/platform/folder/replicate-secrets.png
new file mode 100644
index 000000000..77e36d62c
Binary files /dev/null and b/docs/images/platform/folder/replicate-secrets.png differ
diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx
index d31c83512..e48a3c468 100644
--- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx
+++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ActionBar.tsx
@@ -380,7 +380,7 @@ export const ActionBar = ({
}
};
- // Replicate Folder Logic
+ // Replicate Secrets Logic
const createSecretCount = Object.keys(
(popUp.confirmUpload?.data as TSecOverwriteOpt)?.create || {}
).length;
@@ -439,17 +439,22 @@ export const ActionBar = ({
const processBatches = async () => {
await secretBatches.reduce(async (previous, batch) => {
await previous;
+ try {
+ const { secrets: batchSecrets } = await fetchDashboardProjectSecretsByKeys({
+ secretPath: normalizedPath,
+ environment,
+ projectId,
+ keys: batch
+ });
- const { secrets: batchSecrets } = await fetchDashboardProjectSecretsByKeys({
- secretPath: normalizedPath,
- environment,
- projectId,
- keys: batch
- });
-
- batchSecrets.forEach((secret) => {
- existingSecretLookup.add(`${normalizedPath}-${secret.secretKey}`);
- });
+ batchSecrets.forEach((secret) => {
+ existingSecretLookup.add(`${normalizedPath}-${secret.secretKey}`);
+ });
+ } catch (error) {
+ if (!(error instanceof AxiosError && error.response?.status === 404)) {
+ throw error;
+ }
+ }
}, Promise.resolve());
};
@@ -1050,7 +1055,7 @@ export const ActionBar = ({
className="h-10 text-left"
isFullWidth
>
- Replicate Folder
+ Replicate Secrets
)}
diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ReplicateFolderFromBoard/ReplicateFolderFromBoard.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ReplicateFolderFromBoard/ReplicateFolderFromBoard.tsx
index d0a9ed886..08fe0153c 100644
--- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ReplicateFolderFromBoard/ReplicateFolderFromBoard.tsx
+++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/ReplicateFolderFromBoard/ReplicateFolderFromBoard.tsx
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form";
-import { faClone } from "@fortawesome/free-solid-svg-icons";
+import { faArrowUpRightFromSquare, faBookOpen, faClone } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
@@ -226,8 +226,27 @@ export const ReplicateFolderFromBoard = ({
+ Replicate Secrets
+
+