From cde03b69974bf997c4bcfd20ba097faaecfcd872 Mon Sep 17 00:00:00 2001 From: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:49:25 +0530 Subject: [PATCH] fix: duplicate survey issue (#6774) --- .../summary/components/SurveyAnalysisCTA.tsx | 1 - apps/web/modules/survey/list/actions.ts | 34 +++++++++++++------ .../list/components/copy-survey-form.tsx | 1 - .../list/components/survey-dropdown-menu.tsx | 1 - 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyAnalysisCTA.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyAnalysisCTA.tsx index 62f691bdc4..a65e4307cb 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyAnalysisCTA.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyAnalysisCTA.tsx @@ -96,7 +96,6 @@ export const SurveyAnalysisCTA = ({ const duplicateSurveyAndRoute = async (surveyId: string) => { setLoading(true); const duplicatedSurveyResponse = await copySurveyToOtherEnvironmentAction({ - environmentId: environment.id, surveyId: surveyId, targetEnvironmentId: environment.id, }); diff --git a/apps/web/modules/survey/list/actions.ts b/apps/web/modules/survey/list/actions.ts index 74eddd7370..36b5a544bc 100644 --- a/apps/web/modules/survey/list/actions.ts +++ b/apps/web/modules/survey/list/actions.ts @@ -1,12 +1,13 @@ "use server"; import { z } from "zod"; -import { ResourceNotFoundError } from "@formbricks/types/errors"; +import { OperationNotAllowedError, ResourceNotFoundError } from "@formbricks/types/errors"; import { ZSurveyFilterCriteria } from "@formbricks/types/surveys/types"; import { authenticatedActionClient } from "@/lib/utils/action-client"; import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware"; import { AuthenticatedActionClientCtx } from "@/lib/utils/action-client/types/context"; import { + getEnvironmentIdFromSurveyId, getOrganizationIdFromEnvironmentId, getOrganizationIdFromSurveyId, getProjectIdFromEnvironmentId, @@ -50,7 +51,6 @@ export const getSurveyAction = authenticatedActionClient }); const ZCopySurveyToOtherEnvironmentAction = z.object({ - environmentId: z.string().cuid2(), surveyId: z.string().cuid2(), targetEnvironmentId: z.string().cuid2(), }); @@ -66,9 +66,10 @@ export const copySurveyToOtherEnvironmentAction = authenticatedActionClient parsedInput, }: { ctx: AuthenticatedActionClientCtx; - parsedInput: Record; + parsedInput: z.infer; }) => { - const sourceEnvironmentProjectId = await getProjectIdIfEnvironmentExists(parsedInput.environmentId); + const sourceEnvironmentId = await getEnvironmentIdFromSurveyId(parsedInput.surveyId); + const sourceEnvironmentProjectId = await getProjectIdIfEnvironmentExists(sourceEnvironmentId); const targetEnvironmentProjectId = await getProjectIdIfEnvironmentExists( parsedInput.targetEnvironmentId ); @@ -76,13 +77,25 @@ export const copySurveyToOtherEnvironmentAction = authenticatedActionClient if (!sourceEnvironmentProjectId || !targetEnvironmentProjectId) { throw new ResourceNotFoundError( "Environment", - sourceEnvironmentProjectId ? parsedInput.targetEnvironmentId : parsedInput.environmentId + sourceEnvironmentProjectId ? parsedInput.targetEnvironmentId : sourceEnvironmentId ); } + const sourceEnvironmentOrganizationId = await getOrganizationIdFromEnvironmentId(sourceEnvironmentId); + const targetEnvironmentOrganizationId = await getOrganizationIdFromEnvironmentId( + parsedInput.targetEnvironmentId + ); + + if (sourceEnvironmentOrganizationId !== targetEnvironmentOrganizationId) { + throw new OperationNotAllowedError( + "Source and target environments must be in the same organization" + ); + } + + // authorization check for source environment await checkAuthorizationUpdated({ userId: ctx.user.id, - organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.environmentId), + organizationId: sourceEnvironmentOrganizationId, access: [ { type: "organization", @@ -96,9 +109,10 @@ export const copySurveyToOtherEnvironmentAction = authenticatedActionClient ], }); + // authorization check for target environment await checkAuthorizationUpdated({ userId: ctx.user.id, - organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.environmentId), + organizationId: targetEnvironmentOrganizationId, access: [ { type: "organization", @@ -112,12 +126,10 @@ export const copySurveyToOtherEnvironmentAction = authenticatedActionClient ], }); - ctx.auditLoggingCtx.organizationId = await getOrganizationIdFromEnvironmentId( - parsedInput.environmentId - ); + ctx.auditLoggingCtx.organizationId = sourceEnvironmentOrganizationId; ctx.auditLoggingCtx.surveyId = parsedInput.surveyId; const result = await copySurveyToOtherEnvironment( - parsedInput.environmentId, + sourceEnvironmentId, parsedInput.surveyId, parsedInput.targetEnvironmentId, ctx.user.id diff --git a/apps/web/modules/survey/list/components/copy-survey-form.tsx b/apps/web/modules/survey/list/components/copy-survey-form.tsx index a4236b30bd..13cbc5f6e6 100644 --- a/apps/web/modules/survey/list/components/copy-survey-form.tsx +++ b/apps/web/modules/survey/list/components/copy-survey-form.tsx @@ -129,7 +129,6 @@ export const CopySurveyForm = ({ defaultProjects, survey, onCancel, setOpen }: C return { operation: copySurveyToOtherEnvironmentAction({ - environmentId: survey.environmentId, surveyId: survey.id, targetEnvironmentId: environmentId, }), diff --git a/apps/web/modules/survey/list/components/survey-dropdown-menu.tsx b/apps/web/modules/survey/list/components/survey-dropdown-menu.tsx index 440fac7f24..059ad98816 100644 --- a/apps/web/modules/survey/list/components/survey-dropdown-menu.tsx +++ b/apps/web/modules/survey/list/components/survey-dropdown-menu.tsx @@ -113,7 +113,6 @@ export const SurveyDropDownMenu = ({ setLoading(true); try { const duplicatedSurveyResponse = await copySurveyToOtherEnvironmentAction({ - environmentId, surveyId, targetEnvironmentId: environmentId, });