mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-25 18:48:58 -06:00
refactor: Added input validation using zod (#790)
* added input validation using zod * changed console.log to console.error * fix formatting issues --------- Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
committed by
GitHub
parent
50d7c9666c
commit
4f4e95fecb
@@ -2,8 +2,11 @@
|
||||
import "server-only";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TActionClass, TActionClassInput, ZActionClassInput } from "@formbricks/types/v1/actionClasses";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { cache } from "react";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { TActionClass, TActionClassInput } from "@formbricks/types/v1/actionClasses";
|
||||
|
||||
const select = {
|
||||
id: true,
|
||||
@@ -16,7 +19,8 @@ const select = {
|
||||
environmentId: true,
|
||||
};
|
||||
|
||||
export const getActionClasses = async (environmentId: string): Promise<TActionClass[]> => {
|
||||
export const getActionClasses = cache(async (environmentId: string): Promise<TActionClass[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
let actionClasses = await prisma.eventClass.findMany({
|
||||
where: {
|
||||
@@ -32,12 +36,13 @@ export const getActionClasses = async (environmentId: string): Promise<TActionCl
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching actions for environment ${environmentId}`);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export const deleteActionClass = async (
|
||||
environmentId: string,
|
||||
actionClassId: string
|
||||
): Promise<TActionClass> => {
|
||||
validateInputs([environmentId, ZId], [actionClassId, ZId]);
|
||||
try {
|
||||
const result = await prisma.eventClass.delete({
|
||||
where: {
|
||||
@@ -59,6 +64,7 @@ export const createActionClass = async (
|
||||
environmentId: string,
|
||||
actionClass: TActionClassInput
|
||||
): Promise<TActionClass> => {
|
||||
validateInputs([environmentId, ZId], [actionClass, ZActionClassInput]);
|
||||
try {
|
||||
const result = await prisma.eventClass.create({
|
||||
data: {
|
||||
@@ -83,6 +89,7 @@ export const updateActionClass = async (
|
||||
actionClassId: string,
|
||||
inputActionClass: Partial<TActionClassInput>
|
||||
): Promise<TActionClass> => {
|
||||
validateInputs([environmentId, ZId], [actionClassId, ZId], [inputActionClass, ZActionClassInput.partial()]);
|
||||
try {
|
||||
const result = await prisma.eventClass.update({
|
||||
where: {
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import "server-only";
|
||||
|
||||
import z from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { DatabaseError } from "@formbricks/types/v1/errors";
|
||||
import { TAction } from "@formbricks/types/v1/actions";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache } from "react";
|
||||
import "server-only";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
|
||||
export const getActionsByEnvironmentId = cache(
|
||||
async (environmentId: string, limit?: number): Promise<TAction[]> => {
|
||||
validateInputs([environmentId, ZId], [limit, z.number().optional()]);
|
||||
try {
|
||||
const actionsPrisma = await prisma.event.findMany({
|
||||
where: {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import "server-only";
|
||||
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TActivityFeedItem } from "@formbricks/types/v1/activity";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { cache } from "react";
|
||||
|
||||
export const getActivityTimeline = async (personId: string): Promise<TActivityFeedItem[]> => {
|
||||
export const getActivityTimeline = cache(async (personId: string): Promise<TActivityFeedItem[]> => {
|
||||
validateInputs([personId, ZId]);
|
||||
const person = await prisma.person.findUnique({
|
||||
where: {
|
||||
id: personId,
|
||||
@@ -75,4 +81,4 @@ export const getActivityTimeline = async (personId: string): Promise<TActivityFe
|
||||
const unifiedList: TActivityFeedItem[] = [...unifiedAttributes, ...unifiedDisplays, ...unifiedEvents];
|
||||
|
||||
return unifiedList;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import "server-only";
|
||||
|
||||
import z from "zod";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TApiKey, TApiKeyCreateInput } from "@formbricks/types/v1/apiKeys";
|
||||
import { TApiKey, TApiKeyCreateInput, ZApiKeyCreateInput } from "@formbricks/types/v1/apiKeys";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { getHash } from "../crypto";
|
||||
import { createHash, randomBytes } from "crypto";
|
||||
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
|
||||
export const getApiKey = async (apiKey: string): Promise<TApiKey | null> => {
|
||||
validateInputs([apiKey, z.string()]);
|
||||
if (!apiKey) {
|
||||
throw new InvalidInputError("API key cannot be null or undefined.");
|
||||
}
|
||||
@@ -35,6 +38,7 @@ export const getApiKey = async (apiKey: string): Promise<TApiKey | null> => {
|
||||
};
|
||||
|
||||
export const getApiKeys = cache(async (environmentId: string): Promise<TApiKey[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const apiKeys = await prisma.apiKey.findMany({
|
||||
where: {
|
||||
@@ -54,6 +58,7 @@ export const getApiKeys = cache(async (environmentId: string): Promise<TApiKey[]
|
||||
export const hashApiKey = (key: string): string => createHash("sha256").update(key).digest("hex");
|
||||
|
||||
export async function createApiKey(environmentId: string, apiKeyData: TApiKeyCreateInput): Promise<TApiKey> {
|
||||
validateInputs([environmentId, ZId], [apiKeyData, ZApiKeyCreateInput]);
|
||||
try {
|
||||
const key = randomBytes(16).toString("hex");
|
||||
const hashedKey = hashApiKey(key);
|
||||
@@ -76,6 +81,7 @@ export async function createApiKey(environmentId: string, apiKeyData: TApiKeyCre
|
||||
}
|
||||
|
||||
export const getApiKeyFromKey = async (apiKey: string): Promise<TApiKey | null> => {
|
||||
validateInputs([apiKey, z.string()]);
|
||||
if (!apiKey) {
|
||||
throw new InvalidInputError("API key cannot be null or undefined.");
|
||||
}
|
||||
@@ -98,6 +104,7 @@ export const getApiKeyFromKey = async (apiKey: string): Promise<TApiKey | null>
|
||||
};
|
||||
|
||||
export const deleteApiKey = async (id: string): Promise<void> => {
|
||||
validateInputs([id, ZId]);
|
||||
try {
|
||||
await prisma.apiKey.delete({
|
||||
where: {
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
"use server";
|
||||
import "server-only";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import {
|
||||
TAttributeClass,
|
||||
TAttributeClassUpdateInput,
|
||||
ZAttributeClassUpdateInput,
|
||||
} from "@formbricks/types/v1/attributeClasses";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { DatabaseError } from "@formbricks/types/v1/errors";
|
||||
import { TAttributeClass } from "@formbricks/types/v1/attributeClasses";
|
||||
import { cache } from "react";
|
||||
|
||||
export const transformPrismaAttributeClass = (attributeClass: any): TAttributeClass | null => {
|
||||
@@ -18,6 +24,7 @@ export const transformPrismaAttributeClass = (attributeClass: any): TAttributeCl
|
||||
};
|
||||
|
||||
export const getAttributeClasses = cache(async (environmentId: string): Promise<TAttributeClass[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
let attributeClasses = await prisma.attributeClass.findMany({
|
||||
where: {
|
||||
@@ -39,8 +46,9 @@ export const getAttributeClasses = cache(async (environmentId: string): Promise<
|
||||
|
||||
export const updatetAttributeClass = async (
|
||||
attributeClassId: string,
|
||||
data: { description?: string; archived?: boolean }
|
||||
data: Partial<TAttributeClassUpdateInput>
|
||||
): Promise<TAttributeClass | null> => {
|
||||
validateInputs([attributeClassId, ZId], [data, ZAttributeClassUpdateInput.partial()]);
|
||||
try {
|
||||
let attributeClass = await prisma.attributeClass.update({
|
||||
where: {
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TDisplay, TDisplayInput, TDisplaysWithSurveyName } from "@formbricks/types/v1/displays";
|
||||
import {
|
||||
TDisplay,
|
||||
TDisplayInput,
|
||||
TDisplaysWithSurveyName,
|
||||
ZDisplayInput,
|
||||
} from "@formbricks/types/v1/displays";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { transformPrismaPerson } from "./person";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { cache } from "react";
|
||||
|
||||
const selectDisplay = {
|
||||
id: true,
|
||||
@@ -30,6 +38,7 @@ const selectDisplay = {
|
||||
};
|
||||
|
||||
export const createDisplay = async (displayInput: TDisplayInput): Promise<TDisplay> => {
|
||||
validateInputs([displayInput, ZDisplayInput]);
|
||||
try {
|
||||
const displayPrisma = await prisma.display.create({
|
||||
data: {
|
||||
@@ -67,6 +76,7 @@ export const createDisplay = async (displayInput: TDisplayInput): Promise<TDispl
|
||||
};
|
||||
|
||||
export const markDisplayResponded = async (displayId: string): Promise<TDisplay> => {
|
||||
validateInputs([displayId, ZId]);
|
||||
try {
|
||||
if (!displayId) throw new Error("Display ID is required");
|
||||
|
||||
@@ -99,51 +109,54 @@ export const markDisplayResponded = async (displayId: string): Promise<TDisplay>
|
||||
}
|
||||
};
|
||||
|
||||
export const getDisplaysOfPerson = async (personId: string): Promise<TDisplaysWithSurveyName[] | null> => {
|
||||
try {
|
||||
const displaysPrisma = await prisma.display.findMany({
|
||||
where: {
|
||||
personId: personId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
surveyId: true,
|
||||
survey: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
export const getDisplaysOfPerson = cache(
|
||||
async (personId: string): Promise<TDisplaysWithSurveyName[] | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const displaysPrisma = await prisma.display.findMany({
|
||||
where: {
|
||||
personId: personId,
|
||||
},
|
||||
status: true,
|
||||
},
|
||||
});
|
||||
select: {
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
surveyId: true,
|
||||
survey: {
|
||||
select: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
status: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!displaysPrisma) {
|
||||
throw new ResourceNotFoundError("Display from PersonId", personId);
|
||||
if (!displaysPrisma) {
|
||||
throw new ResourceNotFoundError("Display from PersonId", personId);
|
||||
}
|
||||
|
||||
let displays: TDisplaysWithSurveyName[] = [];
|
||||
|
||||
displaysPrisma.forEach((displayPrisma) => {
|
||||
const display: TDisplaysWithSurveyName = {
|
||||
id: displayPrisma.id,
|
||||
createdAt: displayPrisma.createdAt,
|
||||
updatedAt: displayPrisma.updatedAt,
|
||||
person: null,
|
||||
status: displayPrisma.status,
|
||||
surveyId: displayPrisma.surveyId,
|
||||
surveyName: displayPrisma.survey.name,
|
||||
};
|
||||
displays.push(display);
|
||||
});
|
||||
|
||||
return displays;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
let displays: TDisplaysWithSurveyName[] = [];
|
||||
|
||||
displaysPrisma.forEach((displayPrisma) => {
|
||||
const display: TDisplaysWithSurveyName = {
|
||||
id: displayPrisma.id,
|
||||
createdAt: displayPrisma.createdAt,
|
||||
updatedAt: displayPrisma.updatedAt,
|
||||
person: null,
|
||||
status: displayPrisma.status,
|
||||
surveyId: displayPrisma.surveyId,
|
||||
surveyName: displayPrisma.survey.name,
|
||||
};
|
||||
displays.push(display);
|
||||
});
|
||||
|
||||
return displays;
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError("Database operation failed");
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
@@ -2,12 +2,14 @@ import "server-only";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { z } from "zod";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { ZEnvironment, ZEnvironmentUpdateInput, ZId } from "@formbricks/types/v1/environment";
|
||||
import { DatabaseError, ResourceNotFoundError, ValidationError } from "@formbricks/types/v1/errors";
|
||||
import { ZEnvironment } from "@formbricks/types/v1/environment";
|
||||
import type { TEnvironment, TEnvironmentUpdateInput } from "@formbricks/types/v1/environment";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
|
||||
export const getEnvironment = cache(async (environmentId: string): Promise<TEnvironment> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
let environmentPrisma;
|
||||
try {
|
||||
environmentPrisma = await prisma.environment.findUnique({
|
||||
@@ -39,6 +41,7 @@ export const getEnvironment = cache(async (environmentId: string): Promise<TEnvi
|
||||
});
|
||||
|
||||
export const getEnvironments = cache(async (productId: string): Promise<TEnvironment[]> => {
|
||||
validateInputs([productId, ZId]);
|
||||
let productPrisma;
|
||||
try {
|
||||
productPrisma = await prisma.product.findFirst({
|
||||
@@ -80,6 +83,7 @@ export const updateEnvironment = async (
|
||||
environmentId: string,
|
||||
data: Partial<TEnvironmentUpdateInput>
|
||||
): Promise<TEnvironment> => {
|
||||
validateInputs([environmentId, ZId], [data, ZEnvironmentUpdateInput.partial()]);
|
||||
const newData = { ...data, updatedAt: new Date() };
|
||||
let updatedEnvironment;
|
||||
try {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/error
|
||||
import { TPerson } from "@formbricks/types/v1/people";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { getAttributeClassByName } from "./attributeClass";
|
||||
|
||||
export const selectPerson = {
|
||||
@@ -58,6 +60,7 @@ export const transformPrismaPerson = (person: TransformPersonInput): TPerson =>
|
||||
};
|
||||
|
||||
export const getPerson = cache(async (personId: string): Promise<TPerson | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const personPrisma = await prisma.person.findUnique({
|
||||
where: {
|
||||
@@ -83,6 +86,7 @@ export const getPerson = cache(async (personId: string): Promise<TPerson | null>
|
||||
});
|
||||
|
||||
export const getPeople = cache(async (environmentId: string): Promise<TPerson[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const personsPrisma = await prisma.person.findMany({
|
||||
where: {
|
||||
@@ -109,6 +113,7 @@ export const getPeople = cache(async (environmentId: string): Promise<TPerson[]>
|
||||
});
|
||||
|
||||
export const createPerson = async (environmentId: string): Promise<TPerson> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const personPrisma = await prisma.person.create({
|
||||
data: {
|
||||
@@ -134,6 +139,7 @@ export const createPerson = async (environmentId: string): Promise<TPerson> => {
|
||||
};
|
||||
|
||||
export const deletePerson = async (personId: string): Promise<void> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
await prisma.person.delete({
|
||||
where: {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import "server-only";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { z } from "zod";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { ZProduct, ZProductUpdateInput } from "@formbricks/types/v1/product";
|
||||
import { DatabaseError, ValidationError } from "@formbricks/types/v1/errors";
|
||||
import type { TProduct, TProductUpdateInput } from "@formbricks/types/v1/product";
|
||||
import { ZProduct } from "@formbricks/types/v1/product";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache } from "react";
|
||||
import "server-only";
|
||||
import { z } from "zod";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
|
||||
const selectProduct = {
|
||||
id: true,
|
||||
@@ -23,6 +25,7 @@ const selectProduct = {
|
||||
};
|
||||
|
||||
export const getProducts = cache(async (teamId: string): Promise<TProduct[]> => {
|
||||
validateInputs([teamId, ZId]);
|
||||
try {
|
||||
const products = await prisma.product.findMany({
|
||||
where: {
|
||||
@@ -71,6 +74,7 @@ export const updateProduct = async (
|
||||
productId: string,
|
||||
inputProduct: Partial<TProductUpdateInput>
|
||||
): Promise<TProduct> => {
|
||||
validateInputs([productId, ZId], [inputProduct, ZProductUpdateInput]);
|
||||
let updatedProduct;
|
||||
try {
|
||||
updatedProduct = await prisma.product.update({
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { TProfile } from "@formbricks/types/v1/profile";
|
||||
import { TProfile, ZProfileUpdateInput } from "@formbricks/types/v1/profile";
|
||||
import { deleteTeam } from "./team";
|
||||
import { MembershipRole } from "@prisma/client";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { TMembership, TMembershipRole, ZMembershipRole } from "@formbricks/types/v1/membership";
|
||||
import { TProfileUpdateInput } from "@formbricks/types/v1/profile";
|
||||
|
||||
const responseSelection = {
|
||||
id: true,
|
||||
@@ -14,13 +18,9 @@ const responseSelection = {
|
||||
updatedAt: true,
|
||||
};
|
||||
|
||||
interface Membership {
|
||||
role: MembershipRole;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
// function to retrive basic information about a user's profile
|
||||
export const getProfile = cache(async (userId: string): Promise<TProfile | null> => {
|
||||
validateInputs([userId, ZId]);
|
||||
try {
|
||||
const profile = await prisma.user.findUnique({
|
||||
where: {
|
||||
@@ -43,7 +43,8 @@ export const getProfile = cache(async (userId: string): Promise<TProfile | null>
|
||||
}
|
||||
});
|
||||
|
||||
const updateUserMembership = async (teamId: string, userId: string, role: MembershipRole) => {
|
||||
const updateUserMembership = async (teamId: string, userId: string, role: TMembershipRole) => {
|
||||
validateInputs([teamId, ZId], [userId, ZId], [role, ZMembershipRole]);
|
||||
await prisma.membership.update({
|
||||
where: {
|
||||
userId_teamId: {
|
||||
@@ -57,11 +58,12 @@ const updateUserMembership = async (teamId: string, userId: string, role: Member
|
||||
});
|
||||
};
|
||||
|
||||
const getAdminMemberships = (memberships: Membership[]) =>
|
||||
const getAdminMemberships = (memberships: TMembership[]) =>
|
||||
memberships.filter((membership) => membership.role === MembershipRole.admin);
|
||||
|
||||
// function to update a user's profile
|
||||
export const updateProfile = async (personId: string, data: Prisma.UserUpdateInput): Promise<TProfile> => {
|
||||
export const updateProfile = async (personId: string, data: TProfileUpdateInput): Promise<TProfile> => {
|
||||
validateInputs([personId, ZId], [data, ZProfileUpdateInput]);
|
||||
try {
|
||||
const updatedProfile = await prisma.user.update({
|
||||
where: {
|
||||
@@ -80,6 +82,7 @@ export const updateProfile = async (personId: string, data: Prisma.UserUpdateInp
|
||||
}
|
||||
};
|
||||
const deleteUser = async (userId: string) => {
|
||||
validateInputs([userId, ZId]);
|
||||
await prisma.user.delete({
|
||||
where: {
|
||||
id: userId,
|
||||
@@ -89,6 +92,7 @@ const deleteUser = async (userId: string) => {
|
||||
|
||||
// function to delete a user's profile including teams
|
||||
export const deleteProfile = async (personId: string): Promise<void> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const currentUserMemberships = await prisma.membership.findMany({
|
||||
where: {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { prisma } from "@formbricks/database";
|
||||
import {
|
||||
TResponse,
|
||||
TResponseInput,
|
||||
TResponseUpdateInput,
|
||||
ZResponseInput,
|
||||
ZResponseUpdateInput,
|
||||
} from "@formbricks/types/v1/responses";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
import { TResponse, TResponseInput, TResponseUpdateInput } from "@formbricks/types/v1/responses";
|
||||
import { TPerson } from "@formbricks/types/v1/people";
|
||||
import { TTag } from "@formbricks/types/v1/tags";
|
||||
import { Prisma } from "@prisma/client";
|
||||
@@ -8,6 +14,8 @@ import { cache } from "react";
|
||||
import "server-only";
|
||||
import { getPerson, transformPrismaPerson } from "./person";
|
||||
import { captureTelemetry } from "../telemetry";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
|
||||
const responseSelection = {
|
||||
id: true,
|
||||
@@ -65,6 +73,7 @@ const responseSelection = {
|
||||
};
|
||||
|
||||
export const getResponsesByPersonId = async (personId: string): Promise<Array<TResponse> | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const responsePrisma = await prisma.response.findMany({
|
||||
where: {
|
||||
@@ -98,6 +107,7 @@ export const getResponsesByPersonId = async (personId: string): Promise<Array<TR
|
||||
};
|
||||
|
||||
export const createResponse = async (responseInput: Partial<TResponseInput>): Promise<TResponse> => {
|
||||
validateInputs([responseInput, ZResponseInput.partial()]);
|
||||
captureTelemetry("response created");
|
||||
try {
|
||||
let person: TPerson | null = null;
|
||||
@@ -145,6 +155,7 @@ export const createResponse = async (responseInput: Partial<TResponseInput>): Pr
|
||||
};
|
||||
|
||||
export const getResponse = async (responseId: string): Promise<TResponse | null> => {
|
||||
validateInputs([responseId, ZId]);
|
||||
try {
|
||||
const responsePrisma = await prisma.response.findUnique({
|
||||
where: {
|
||||
@@ -174,10 +185,12 @@ export const getResponse = async (responseId: string): Promise<TResponse | null>
|
||||
};
|
||||
|
||||
export const preloadSurveyResponses = (surveyId: string) => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
void getSurveyResponses(surveyId);
|
||||
};
|
||||
|
||||
export const getSurveyResponses = cache(async (surveyId: string): Promise<TResponse[]> => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
try {
|
||||
const responsesPrisma = await prisma.response.findMany({
|
||||
where: {
|
||||
@@ -212,6 +225,7 @@ export const preloadEnvironmentResponses = (environmentId: string) => {
|
||||
};
|
||||
|
||||
export const getEnvironmentResponses = cache(async (environmentId: string): Promise<TResponse[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const responsesPrisma = await prisma.response.findMany({
|
||||
where: {
|
||||
@@ -247,6 +261,7 @@ export const updateResponse = async (
|
||||
responseId: string,
|
||||
responseInput: TResponseUpdateInput
|
||||
): Promise<TResponse> => {
|
||||
validateInputs([responseId, ZId], [responseInput, ZResponseUpdateInput]);
|
||||
try {
|
||||
const currentResponse = await getResponse(responseId);
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import { DatabaseError } from "@formbricks/types/v1/errors";
|
||||
import { TSession, TSessionWithActions } from "@formbricks/types/v1/sessions";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
|
||||
const select = {
|
||||
id: true,
|
||||
@@ -18,6 +20,7 @@ const select = {
|
||||
const oneHour = 1000 * 60 * 60;
|
||||
|
||||
export const getSession = async (sessionId: string): Promise<TSession | null> => {
|
||||
validateInputs([sessionId, ZId]);
|
||||
try {
|
||||
const session = await prisma.session.findUnique({
|
||||
where: {
|
||||
@@ -39,6 +42,7 @@ export const getSession = async (sessionId: string): Promise<TSession | null> =>
|
||||
export const getSessionWithActionsOfPerson = async (
|
||||
personId: string
|
||||
): Promise<TSessionWithActions[] | null> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const sessionsWithActionsForPerson = await prisma.session.findMany({
|
||||
where: {
|
||||
@@ -73,6 +77,7 @@ export const getSessionWithActionsOfPerson = async (
|
||||
};
|
||||
|
||||
export const getSessionCount = cache(async (personId: string): Promise<number> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const sessionCount = await prisma.session.count({
|
||||
where: {
|
||||
@@ -89,6 +94,7 @@ export const getSessionCount = cache(async (personId: string): Promise<number> =
|
||||
});
|
||||
|
||||
export const createSession = async (personId: string): Promise<TSession> => {
|
||||
validateInputs([personId, ZId]);
|
||||
try {
|
||||
const session = await prisma.session.create({
|
||||
data: {
|
||||
@@ -113,6 +119,7 @@ export const createSession = async (personId: string): Promise<TSession> => {
|
||||
};
|
||||
|
||||
export const extendSession = async (sessionId: string): Promise<TSession> => {
|
||||
validateInputs([sessionId, ZId]);
|
||||
try {
|
||||
const session = await prisma.session.update({
|
||||
where: {
|
||||
|
||||
@@ -6,6 +6,8 @@ import { cache } from "react";
|
||||
import "server-only";
|
||||
import { z } from "zod";
|
||||
import { captureTelemetry } from "../telemetry";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
|
||||
export const selectSurvey = {
|
||||
id: true,
|
||||
@@ -68,11 +70,13 @@ export const selectSurveyWithAnalytics = {
|
||||
};
|
||||
|
||||
export const preloadSurveyWithAnalytics = (surveyId: string) => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
void getSurveyWithAnalytics(surveyId);
|
||||
};
|
||||
|
||||
export const getSurveyWithAnalytics = cache(
|
||||
async (surveyId: string): Promise<TSurveyWithAnalytics | null> => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
let surveyPrisma;
|
||||
try {
|
||||
surveyPrisma = await prisma.survey.findUnique({
|
||||
@@ -125,6 +129,7 @@ export const getSurveyWithAnalytics = cache(
|
||||
);
|
||||
|
||||
export const getSurvey = cache(async (surveyId: string): Promise<TSurvey | null> => {
|
||||
validateInputs([surveyId, ZId]);
|
||||
let surveyPrisma;
|
||||
try {
|
||||
surveyPrisma = await prisma.survey.findUnique({
|
||||
@@ -162,6 +167,7 @@ export const getSurvey = cache(async (surveyId: string): Promise<TSurvey | null>
|
||||
});
|
||||
|
||||
export const getSurveys = cache(async (environmentId: string): Promise<TSurvey[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
let surveysPrisma;
|
||||
try {
|
||||
surveysPrisma = await prisma.survey.findMany({
|
||||
@@ -200,6 +206,7 @@ export const getSurveys = cache(async (environmentId: string): Promise<TSurvey[]
|
||||
|
||||
export const getSurveysWithAnalytics = cache(
|
||||
async (environmentId: string): Promise<TSurveyWithAnalytics[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
let surveysPrisma;
|
||||
try {
|
||||
surveysPrisma = await prisma.survey.findMany({
|
||||
@@ -246,6 +253,7 @@ export const getSurveysWithAnalytics = cache(
|
||||
);
|
||||
|
||||
export async function deleteSurvey(surveyId: string) {
|
||||
validateInputs([surveyId, ZId]);
|
||||
const deletedSurvey = await prisma.survey.delete({
|
||||
where: {
|
||||
id: surveyId,
|
||||
@@ -256,6 +264,7 @@ export async function deleteSurvey(surveyId: string) {
|
||||
}
|
||||
|
||||
export async function createSurvey(environmentId: string, surveyBody: any) {
|
||||
validateInputs([environmentId, ZId]);
|
||||
const survey = await prisma.survey.create({
|
||||
data: {
|
||||
...surveyBody,
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
populateEnvironment,
|
||||
updateEnvironmentArgs,
|
||||
} from "../utils/createDemoProductHelpers";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
|
||||
export const select = {
|
||||
id: true,
|
||||
@@ -56,6 +58,7 @@ export const getTeamsByUserId = cache(async (userId: string): Promise<TTeam[]> =
|
||||
});
|
||||
|
||||
export const getTeamByEnvironmentId = cache(async (environmentId: string): Promise<TTeam | null> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const team = await prisma.team.findFirst({
|
||||
where: {
|
||||
@@ -83,6 +86,7 @@ export const getTeamByEnvironmentId = cache(async (environmentId: string): Promi
|
||||
});
|
||||
|
||||
export const deleteTeam = async (teamId: string) => {
|
||||
validateInputs([teamId, ZId]);
|
||||
try {
|
||||
await prisma.team.delete({
|
||||
where: {
|
||||
@@ -99,6 +103,7 @@ export const deleteTeam = async (teamId: string) => {
|
||||
};
|
||||
|
||||
export const createDemoProduct = cache(async (teamId: string) => {
|
||||
validateInputs([teamId, ZId]);
|
||||
const productWithEnvironment = Prisma.validator<Prisma.ProductArgs>()({
|
||||
include: {
|
||||
environments: true,
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/v1/errors";
|
||||
|
||||
export const getTeamDetails = async (environmentId: string) => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
const environment = await prisma.environment.findUnique({
|
||||
where: {
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
"use server";
|
||||
import "server-only";
|
||||
|
||||
import { TWebhook, TWebhookInput } from "@formbricks/types/v1/webhooks";
|
||||
import { TWebhook, TWebhookInput, ZWebhookInput } from "@formbricks/types/v1/webhooks";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { cache } from "react";
|
||||
import { ResourceNotFoundError, DatabaseError, InvalidInputError } from "@formbricks/types/v1/errors";
|
||||
|
||||
export const getWebhooks = async (environmentId: string): Promise<TWebhook[]> => {
|
||||
export const getWebhooks = cache(async (environmentId: string): Promise<TWebhook[]> => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
try {
|
||||
return await prisma.webhook.findMany({
|
||||
where: {
|
||||
@@ -16,9 +20,10 @@ export const getWebhooks = async (environmentId: string): Promise<TWebhook[]> =>
|
||||
} catch (error) {
|
||||
throw new DatabaseError(`Database error when fetching webhooks for environment ${environmentId}`);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export const getWebhook = async (id: string): Promise<TWebhook | null> => {
|
||||
validateInputs([id, ZId]);
|
||||
const webhook = await prisma.webhook.findUnique({
|
||||
where: {
|
||||
id,
|
||||
@@ -31,6 +36,7 @@ export const createWebhook = async (
|
||||
environmentId: string,
|
||||
webhookInput: TWebhookInput
|
||||
): Promise<TWebhook> => {
|
||||
validateInputs([environmentId, ZId], [webhookInput, ZWebhookInput]);
|
||||
try {
|
||||
if (!webhookInput.url || !webhookInput.triggers) {
|
||||
throw new InvalidInputError("Missing URL or trigger in webhook input");
|
||||
@@ -61,6 +67,7 @@ export const updateWebhook = async (
|
||||
webhookId: string,
|
||||
webhookInput: Partial<TWebhookInput>
|
||||
): Promise<TWebhook> => {
|
||||
validateInputs([environmentId, ZId], [webhookId, ZId], [webhookInput, ZWebhookInput]);
|
||||
try {
|
||||
const result = await prisma.webhook.update({
|
||||
where: {
|
||||
@@ -82,6 +89,7 @@ export const updateWebhook = async (
|
||||
};
|
||||
|
||||
export const deleteWebhook = async (id: string): Promise<TWebhook> => {
|
||||
validateInputs([id, ZId]);
|
||||
try {
|
||||
return await prisma.webhook.delete({
|
||||
where: {
|
||||
|
||||
15
packages/lib/utils/validate.ts
Normal file
15
packages/lib/utils/validate.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import z from "zod";
|
||||
import { ValidationError } from "@formbricks/types/v1/errors";
|
||||
|
||||
type ValidationPair = [any, z.ZodSchema<any>];
|
||||
|
||||
export const validateInputs = (...pairs: ValidationPair[]): void => {
|
||||
for (const [value, schema] of pairs) {
|
||||
const inputValidation = schema.safeParse(value);
|
||||
|
||||
if (!inputValidation.success) {
|
||||
console.error(`Validation failed for ${schema}: ${inputValidation.error.message}`);
|
||||
throw new ValidationError("Validation failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3,4 +3,4 @@ export default {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,4 +11,10 @@ export const ZAttributeClass = z.object({
|
||||
archived: z.boolean(),
|
||||
});
|
||||
|
||||
export const ZAttributeClassUpdateInput = z.object({
|
||||
description: z.string(),
|
||||
archived: z.boolean(),
|
||||
});
|
||||
export type TAttributeClassUpdateInput = z.infer<typeof ZAttributeClassUpdateInput>;
|
||||
|
||||
export type TAttributeClass = z.infer<typeof ZAttributeClass>;
|
||||
|
||||
@@ -17,4 +17,6 @@ export const ZEnvironmentUpdateInput = z.object({
|
||||
widgetSetupCompleted: z.boolean(),
|
||||
});
|
||||
|
||||
export const ZId = z.string().cuid2();
|
||||
|
||||
export type TEnvironmentUpdateInput = z.infer<typeof ZEnvironmentUpdateInput>;
|
||||
|
||||
11
packages/types/v1/membership.ts
Normal file
11
packages/types/v1/membership.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const ZMembershipRole = z.enum(["owner", "admin", "editor", "developer", "viewer"]);
|
||||
|
||||
export const ZMembership = z.object({
|
||||
role: ZMembershipRole,
|
||||
userId: z.string(),
|
||||
});
|
||||
|
||||
export type TMembership = z.infer<typeof ZMembership>;
|
||||
export type TMembershipRole = z.infer<typeof ZMembershipRole>;
|
||||
Reference in New Issue
Block a user