mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-29 10:46:12 -06:00
166 lines
4.4 KiB
TypeScript
166 lines
4.4 KiB
TypeScript
import "server-only";
|
|
import { Prisma } from "@prisma/client";
|
|
import { z } from "zod";
|
|
import { prisma } from "@formbricks/database";
|
|
import { PrismaErrorType } from "@formbricks/database/types/error";
|
|
import { logger } from "@formbricks/logger";
|
|
import { ZId, ZString } from "@formbricks/types/common";
|
|
import { DatabaseError, InvalidInputError, ValidationError } from "@formbricks/types/errors";
|
|
import { TProject, TProjectUpdateInput, ZProject, ZProjectUpdateInput } from "@formbricks/types/project";
|
|
import { createEnvironment } from "@/lib/environment/service";
|
|
import { validateInputs } from "@/lib/utils/validate";
|
|
import { deleteFilesByEnvironmentId } from "@/modules/storage/service";
|
|
|
|
const selectProject = {
|
|
id: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
name: true,
|
|
organizationId: true,
|
|
languages: true,
|
|
recontactDays: true,
|
|
linkSurveyBranding: true,
|
|
inAppSurveyBranding: true,
|
|
config: true,
|
|
placement: true,
|
|
clickOutsideClose: true,
|
|
darkOverlay: true,
|
|
environments: true,
|
|
styling: true,
|
|
logo: true,
|
|
};
|
|
|
|
export const updateProject = async (
|
|
projectId: string,
|
|
inputProject: TProjectUpdateInput
|
|
): Promise<TProject> => {
|
|
validateInputs([projectId, ZId], [inputProject, ZProjectUpdateInput]);
|
|
const { environments, ...data } = inputProject;
|
|
let updatedProject;
|
|
try {
|
|
updatedProject = await prisma.project.update({
|
|
where: {
|
|
id: projectId,
|
|
},
|
|
data: {
|
|
...data,
|
|
environments: {
|
|
connect: environments?.map((environment) => ({ id: environment.id })) ?? [],
|
|
},
|
|
},
|
|
select: selectProject,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
throw new DatabaseError(error.message);
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
try {
|
|
const project = ZProject.parse(updatedProject);
|
|
|
|
return project;
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
logger.error(error.errors, "Error updating project");
|
|
}
|
|
throw new ValidationError("Data validation of project failed");
|
|
}
|
|
};
|
|
|
|
export const createProject = async (
|
|
organizationId: string,
|
|
projectInput: Partial<TProjectUpdateInput>
|
|
): Promise<TProject> => {
|
|
validateInputs([organizationId, ZString], [projectInput, ZProjectUpdateInput.partial()]);
|
|
|
|
if (!projectInput.name) {
|
|
throw new ValidationError("Project Name is required");
|
|
}
|
|
|
|
const { environments, teamIds, ...data } = projectInput;
|
|
|
|
try {
|
|
let project = await prisma.project.create({
|
|
data: {
|
|
config: {
|
|
channel: null,
|
|
industry: null,
|
|
},
|
|
...data,
|
|
name: projectInput.name,
|
|
organizationId,
|
|
},
|
|
select: selectProject,
|
|
});
|
|
|
|
if (teamIds) {
|
|
await prisma.projectTeam.createMany({
|
|
data: teamIds.map((teamId) => ({
|
|
projectId: project.id,
|
|
teamId,
|
|
})),
|
|
});
|
|
}
|
|
|
|
const devEnvironment = await createEnvironment(project.id, {
|
|
type: "development",
|
|
});
|
|
|
|
const prodEnvironment = await createEnvironment(project.id, {
|
|
type: "production",
|
|
});
|
|
|
|
const updatedProject = await updateProject(project.id, {
|
|
environments: [devEnvironment, prodEnvironment],
|
|
});
|
|
|
|
return updatedProject;
|
|
} catch (error) {
|
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
if (error.code === PrismaErrorType.UniqueConstraintViolation) {
|
|
throw new InvalidInputError("A project with this name already exists in your organization");
|
|
}
|
|
throw new DatabaseError(error.message);
|
|
}
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const deleteProject = async (projectId: string): Promise<TProject> => {
|
|
try {
|
|
const project = await prisma.project.delete({
|
|
where: {
|
|
id: projectId,
|
|
},
|
|
select: selectProject,
|
|
});
|
|
|
|
if (project) {
|
|
// delete all files from storage related to this project
|
|
|
|
const s3FilesPromises = project.environments.map(async (environment) => {
|
|
return deleteFilesByEnvironmentId(environment.id);
|
|
});
|
|
|
|
const s3FilesResult = await Promise.all(s3FilesPromises);
|
|
|
|
for (const result of s3FilesResult) {
|
|
if (!result.ok) {
|
|
// fail silently because we don't want to throw an error if the files are not deleted
|
|
logger.error(result.error, "Error deleting S3 files");
|
|
}
|
|
}
|
|
}
|
|
|
|
return project;
|
|
} catch (error) {
|
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
throw new DatabaseError(error.message);
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
};
|