This commit is contained in:
pandeymangg
2026-03-10 19:45:56 +05:30
parent 72385e6351
commit a5a036ae05
25 changed files with 161 additions and 240 deletions
@@ -80,9 +80,24 @@ export const COLUMNS_ICON_MAP = {
const userAgentFields = ["browser", "os", "device"];
export const METADATA_FIELDS = ["action", "country", ...userAgentFields, "source", "url"];
export const getMetadataValue = (meta: TResponseMeta, label: string) => {
if (userAgentFields.includes(label)) {
return (meta.userAgent as Record<string, string> | undefined)?.[label];
export const getMetadataValue = (
meta: TResponseMeta,
label: (typeof METADATA_FIELDS)[number]
): string | undefined => {
switch (label) {
case "browser":
return meta.userAgent?.browser;
case "os":
return meta.userAgent?.os;
case "device":
return meta.userAgent?.device;
case "action":
return meta.action;
case "country":
return meta.country;
case "source":
return meta.source;
case "url":
return meta.url;
}
return (meta as Record<string, string | undefined>)[label];
};
@@ -14,11 +14,7 @@ import {
TResponseVariables,
ZResponseFilterCriteria,
} from "@formbricks/types/responses";
import {
TSurveyElement,
TSurveyElementChoice,
TSurveyElementTypeEnum,
} from "@formbricks/types/surveys/elements";
import { TSurveyElement, TSurveyElementTypeEnum } from "@formbricks/types/surveys/elements";
import {
TSurvey,
TSurveyElementSummaryAddress,
@@ -293,7 +289,10 @@ const checkForI18n = (
) => {
const element = elements.find((element) => element.id === id);
if (element?.type === "multipleChoiceMulti" || element?.type === "ranking") {
if (
element?.type === TSurveyElementTypeEnum.MultipleChoiceMulti ||
element?.type === TSurveyElementTypeEnum.Ranking
) {
// Initialize an array to hold the choice values
let choiceValues = [] as string[];
@@ -318,13 +317,9 @@ const checkForI18n = (
}
// Return the localized value of the choice fo multiSelect single element
if (element && "choices" in element) {
const choice = (element.choices as TSurveyElementChoice[])?.find(
(choice) => choice.label?.[languageCode] === responseData[id]
);
return choice && "label" in choice
? getLocalizedValue(choice.label, "default") || responseData[id]
: responseData[id];
if (element?.type === TSurveyElementTypeEnum.MultipleChoiceSingle) {
const choice = element.choices?.find((choice) => choice.label[languageCode] === responseData[id]);
return choice ? getLocalizedValue(choice.label, "default") || responseData[id] : responseData[id];
}
return responseData[id];
@@ -1,19 +1,12 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { ZDisplayCreateInput } from "@formbricks/types/displays";
import { ResourceNotFoundError } from "@formbricks/types/errors";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
import { createDisplay } from "./lib/display";
interface Context {
params: Promise<{
environmentId: string;
}>;
}
export const OPTIONS = async (): Promise<Response> => {
return responses.successResponse(
{},
@@ -25,7 +18,7 @@ export const OPTIONS = async (): Promise<Response> => {
};
export const POST = withV1ApiWrapper({
handler: async ({ req, props }: { req: NextRequest; props: Context }) => {
handler: async ({ req, props }: THandlerParams<{ params: Promise<{ environmentId: string }> }>) => {
const params = await props.params;
const jsonInput = await req.json();
const inputValidation = ZDisplayCreateInput.safeParse({
@@ -1,10 +1,9 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { ZEnvironmentId } from "@formbricks/types/environment";
import { ResourceNotFoundError } from "@formbricks/types/errors";
import { getEnvironmentState } from "@/app/api/v1/client/[environmentId]/environment/lib/environmentState";
import { responses } from "@/app/lib/api/response";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
export const OPTIONS = async (): Promise<Response> => {
return responses.successResponse(
@@ -19,13 +18,7 @@ export const OPTIONS = async (): Promise<Response> => {
};
export const GET = withV1ApiWrapper({
handler: async ({
req,
props,
}: {
req: NextRequest;
props: { params: Promise<{ environmentId: string }> };
}) => {
handler: async ({ req, props }: THandlerParams<{ params: Promise<{ environmentId: string }> }>) => {
const params = await props.params;
try {
@@ -1,4 +1,3 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TResponse, TResponseUpdateInput, ZResponseUpdateInput } from "@formbricks/types/responses";
@@ -6,7 +5,7 @@ import { TSurveyElement } from "@formbricks/types/surveys/elements";
import { TSurvey } from "@formbricks/types/surveys/types";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { sendToPipeline } from "@/app/lib/pipelines";
import { getResponse } from "@/lib/response/service";
import { getSurvey } from "@/lib/survey/service";
@@ -64,13 +63,7 @@ const validateResponse = (
};
export const PUT = withV1ApiWrapper({
handler: async ({
req,
props,
}: {
req: NextRequest;
props: { params: Promise<{ responseId: string }> };
}) => {
handler: async ({ req, props }: THandlerParams<{ params: Promise<{ responseId: string }> }>) => {
const params = await props.params;
const { responseId } = params;
@@ -196,7 +189,14 @@ export const PUT = withV1ApiWrapper({
response: responses.internalServerErrorResponse(error.message),
};
}
throw error;
logger.error(
{ error, url: req.url },
"Error in PUT /api/v1/client/[environmentId]/responses/[responseId]"
);
return {
response: responses.internalServerErrorResponse("Something went wrong"),
};
}
const { quotaFull, ...responseData } = updatedResponse;
@@ -1,5 +1,4 @@
import { headers } from "next/headers";
import { NextRequest } from "next/server";
import { UAParser } from "ua-parser-js";
import { logger } from "@formbricks/logger";
import { ZEnvironmentId } from "@formbricks/types/environment";
@@ -9,7 +8,7 @@ import { TResponseInput, ZResponseInput } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys/types";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { sendToPipeline } from "@/app/lib/pipelines";
import { getSurvey } from "@/lib/survey/service";
import { getClientIpFromHeaders } from "@/lib/utils/client-ip";
@@ -19,12 +18,6 @@ import { createQuotaFullObject } from "@/modules/ee/quotas/lib/helpers";
import { validateFileUploads } from "@/modules/storage/utils";
import { createResponseWithQuotaEvaluation } from "./lib/response";
interface Context {
params: Promise<{
environmentId: string;
}>;
}
export const OPTIONS = async (): Promise<Response> => {
return responses.successResponse(
{},
@@ -56,7 +49,7 @@ const validateResponse = (responseInputData: TResponseInput, survey: TSurvey) =>
};
export const POST = withV1ApiWrapper({
handler: async ({ req, props }: { req: NextRequest; props: Context }) => {
handler: async ({ req, props }: THandlerParams<{ params: Promise<{ environmentId: string }> }>) => {
const params = await props.params;
const requestHeaders = await headers();
let responseInput;
@@ -1,9 +1,8 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { TUploadPrivateFileRequest, ZUploadPrivateFileRequest } from "@formbricks/types/storage";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { MAX_FILE_UPLOAD_SIZES } from "@/lib/constants";
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
import { getSurvey } from "@/lib/survey/service";
@@ -12,12 +11,6 @@ import { getBiggerUploadFileSizePermission } from "@/modules/ee/license-check/li
import { getSignedUrlForUpload } from "@/modules/storage/service";
import { getErrorResponseFromStorageError } from "@/modules/storage/utils";
interface Context {
params: Promise<{
environmentId: string;
}>;
}
export const OPTIONS = async (): Promise<Response> => {
return responses.successResponse(
{},
@@ -34,7 +27,7 @@ export const OPTIONS = async (): Promise<Response> => {
// use this to let users upload files to a file upload question response for example
export const POST = withV1ApiWrapper({
handler: async ({ req, props }: { req: NextRequest; props: Context }) => {
handler: async ({ req, props }: THandlerParams<{ params: Promise<{ environmentId: string }> }>) => {
const params = await props.params;
const { environmentId } = params;
let jsonInput: TUploadPrivateFileRequest;
@@ -1,8 +1,7 @@
import { NextRequest } from "next/server";
import * as z from "zod";
import { logger } from "@formbricks/logger";
import { responses } from "@/app/lib/api/response";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { fetchAirtableAuthToken } from "@/lib/airtable/service";
import { AIRTABLE_CLIENT_ID, WEBAPP_URL } from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
@@ -21,7 +20,7 @@ const getEmail = async (token: string) => {
};
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
if (!authentication || !("user" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -1,14 +1,13 @@
import crypto from "crypto";
import { NextRequest } from "next/server";
import { responses } from "@/app/lib/api/response";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { AIRTABLE_CLIENT_ID, WEBAPP_URL } from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
const scope = `data.records:read data.records:write schema.bases:read schema.bases:write user.email:read`;
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
if (!authentication || !("user" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -1,14 +1,13 @@
import { NextRequest } from "next/server";
import * as z from "zod";
import { TIntegrationAirtable } from "@formbricks/types/integration/airtable";
import { responses } from "@/app/lib/api/response";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { getTables } from "@/lib/airtable/service";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { getIntegrationByType } from "@/lib/integration/service";
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
if (!authentication || !("user" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -1,7 +1,6 @@
import { NextRequest } from "next/server";
import { TIntegrationNotionConfigData, TIntegrationNotionInput } from "@formbricks/types/integration/notion";
import { responses } from "@/app/lib/api/response";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import {
ENCRYPTION_KEY,
NOTION_OAUTH_CLIENT_ID,
@@ -14,7 +13,7 @@ import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
if (!authentication || !("user" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -1,6 +1,5 @@
import { NextRequest } from "next/server";
import { responses } from "@/app/lib/api/response";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import {
NOTION_AUTH_URL,
NOTION_OAUTH_CLIENT_ID,
@@ -10,7 +9,7 @@ import {
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
if (!authentication || !("user" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -1,17 +1,16 @@
import { NextRequest } from "next/server";
import {
TIntegrationSlackConfig,
TIntegrationSlackConfigData,
TIntegrationSlackCredential,
} from "@formbricks/types/integration/slack";
import { responses } from "@/app/lib/api/response";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_REDIRECT_URI, WEBAPP_URL } from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
if (!authentication || !("user" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -1,11 +1,11 @@
import { NextRequest } from "next/server";
import { responses } from "@/app/lib/api/response";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { SLACK_AUTH_URL, SLACK_CLIENT_ID, SLACK_CLIENT_SECRET } from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
// session authentication
if (!authentication || !("user" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -1,11 +1,10 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { TActionClass, ZActionClassInput } from "@formbricks/types/action-classes";
import { TAuthenticationApiKey } from "@formbricks/types/auth";
import { handleErrorResponse } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { TApiAuditLog, TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { deleteActionClass, getActionClass, updateActionClass } from "@/lib/actionClass/service";
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
@@ -32,10 +31,7 @@ export const GET = withV1ApiWrapper({
handler: async ({
props,
authentication,
}: {
props: { params: Promise<{ actionClassId: string }> };
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ actionClassId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -66,12 +62,7 @@ export const PUT = withV1ApiWrapper({
props,
auditLog,
authentication,
}: {
req: NextRequest;
props: { params: Promise<{ actionClassId: string }> };
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ actionClassId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -85,7 +76,10 @@ export const PUT = withV1ApiWrapper({
response: responses.notFoundResponse("Action Class", params.actionClassId),
};
}
auditLog!.oldObject = actionClass;
if (auditLog) {
auditLog.oldObject = actionClass;
}
let actionClassUpdate;
try {
@@ -112,7 +106,10 @@ export const PUT = withV1ApiWrapper({
inputValidation.data
);
if (updatedActionClass) {
auditLog!.newObject = updatedActionClass;
if (auditLog) {
auditLog.newObject = updatedActionClass;
}
return {
response: responses.successResponse(updatedActionClass),
};
@@ -135,18 +132,16 @@ export const DELETE = withV1ApiWrapper({
props,
auditLog,
authentication,
}: {
props: { params: Promise<{ actionClassId: string }> };
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ actionClassId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
const params = await props.params;
auditLog!.targetId = params.actionClassId;
if (auditLog) {
auditLog.targetId = params.actionClassId;
}
try {
const actionClass = await fetchAndAuthorizeActionClass(authentication, params.actionClassId, "DELETE");
@@ -156,7 +151,9 @@ export const DELETE = withV1ApiWrapper({
};
}
auditLog!.oldObject = actionClass;
if (auditLog) {
auditLog.oldObject = actionClass;
}
const deletedActionClass = await deleteActionClass(params.actionClassId);
return {
@@ -1,16 +1,15 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { TActionClass, ZActionClassInput } from "@formbricks/types/action-classes";
import { DatabaseError } from "@formbricks/types/errors";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { TApiAuditLog, TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { createActionClass } from "@/lib/actionClass/service";
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
import { getActionClasses } from "./lib/action-classes";
export const GET = withV1ApiWrapper({
handler: async ({ authentication }: { authentication?: TApiV1Authentication }) => {
handler: async ({ authentication }) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -37,15 +36,7 @@ export const GET = withV1ApiWrapper({
});
export const POST = withV1ApiWrapper({
handler: async ({
req,
auditLog,
authentication,
}: {
req: NextRequest;
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
handler: async ({ req, auditLog, authentication }: THandlerParams) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -81,8 +72,10 @@ export const POST = withV1ApiWrapper({
}
const actionClass: TActionClass = await createActionClass(environmentId, inputValidation.data);
auditLog!.targetId = actionClass.id;
auditLog!.newObject = actionClass;
if (auditLog) {
auditLog.targetId = actionClass.id;
auditLog.newObject = actionClass;
}
return {
response: responses.successResponse(actionClass),
};
@@ -1,10 +1,9 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { ZResponseUpdateInput } from "@formbricks/types/responses";
import { handleErrorResponse } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { TApiAuditLog, TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { TApiV1Authentication, THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { sendToPipeline } from "@/app/lib/pipelines";
import { deleteResponse, getResponse } from "@/lib/response/service";
import { getSurvey } from "@/lib/survey/service";
@@ -40,13 +39,7 @@ async function fetchAndAuthorizeResponse(
}
export const GET = withV1ApiWrapper({
handler: async ({
props,
authentication,
}: {
props: { params: Promise<{ responseId: string }> };
authentication?: TApiV1Authentication;
}) => {
handler: async ({ props, authentication }: THandlerParams<{ params: Promise<{ responseId: string }> }>) => {
const params = await props.params;
try {
const result = await fetchAndAuthorizeResponse(params.responseId, authentication, "GET");
@@ -75,13 +68,11 @@ export const DELETE = withV1ApiWrapper({
props,
auditLog,
authentication,
}: {
props: { params: Promise<{ responseId: string }> };
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ responseId: string }> }>) => {
const params = await props.params;
auditLog!.targetId = params.responseId;
if (auditLog) {
auditLog.targetId = params.responseId;
}
try {
const result = await fetchAndAuthorizeResponse(params.responseId, authentication, "DELETE");
if (result.error) {
@@ -89,7 +80,9 @@ export const DELETE = withV1ApiWrapper({
response: result.error,
};
}
auditLog!.oldObject = result.response;
if (auditLog) {
auditLog.oldObject = result.response;
}
const deletedResponse = await deleteResponse(params.responseId);
return {
@@ -111,14 +104,11 @@ export const PUT = withV1ApiWrapper({
props,
auditLog,
authentication,
}: {
req: NextRequest;
props: { params: Promise<{ responseId: string }> };
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ responseId: string }> }>) => {
const params = await props.params;
auditLog!.targetId = params.responseId;
if (auditLog) {
auditLog.targetId = params.responseId;
}
try {
const result = await fetchAndAuthorizeResponse(params.responseId, authentication, "PUT");
if (result.error) {
@@ -126,7 +116,9 @@ export const PUT = withV1ApiWrapper({
response: result.error,
};
}
auditLog!.oldObject = result.response;
if (auditLog) {
auditLog.oldObject = result.response;
}
let responseUpdate;
try {
@@ -173,7 +165,9 @@ export const PUT = withV1ApiWrapper({
}
const updated = await updateResponseWithQuotaEvaluation(params.responseId, inputValidation.data);
auditLog!.newObject = updated;
if (auditLog) {
auditLog.newObject = updated;
}
sendToPipeline({
event: "responseUpdated",
@@ -1,10 +1,9 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { DatabaseError, InvalidInputError } from "@formbricks/types/errors";
import { TResponse, TResponseInput, ZResponseInput } from "@formbricks/types/responses";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { TApiAuditLog, TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { sendToPipeline } from "@/app/lib/pipelines";
import { getSurvey } from "@/lib/survey/service";
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
@@ -17,7 +16,7 @@ import {
} from "./lib/response";
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -111,15 +110,7 @@ const validateSurvey = async (responseInput: TResponseInput, environmentId: stri
};
export const POST = withV1ApiWrapper({
handler: async ({
req,
auditLog,
authentication,
}: {
req: NextRequest;
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
handler: async ({ req, auditLog, authentication }) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -178,8 +169,10 @@ export const POST = withV1ApiWrapper({
try {
const response = await createResponseWithQuotaEvaluation(responseInput);
auditLog!.targetId = response.id;
auditLog!.newObject = response;
if (auditLog) {
auditLog.targetId = response.id;
auditLog.newObject = response;
}
sendToPipeline({
event: "responseCreated",
@@ -1,10 +1,9 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { TUploadPublicFileRequest, ZUploadPublicFileRequest } from "@formbricks/types/storage";
import { checkAuth } from "@/app/api/v1/management/storage/lib/utils";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { rateLimitConfigs } from "@/modules/core/rate-limit/rate-limit-configs";
import { getSignedUrlForUpload } from "@/modules/storage/service";
import { getErrorResponseFromStorageError } from "@/modules/storage/utils";
@@ -15,7 +14,7 @@ import { getErrorResponseFromStorageError } from "@/modules/storage/utils";
// use this to get a signed url for uploading a public file for a specific resource, e.g. a survey's background image
export const POST = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
let storageInput: TUploadPublicFileRequest;
try {
@@ -1,4 +1,3 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { TAuthenticationApiKey } from "@formbricks/types/auth";
import { ZSurveyUpdateInput } from "@formbricks/types/surveys/types";
@@ -12,7 +11,7 @@ import {
validateSurveyInput,
} from "@/app/lib/api/survey-transformation";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { TApiAuditLog, TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
import { getSurvey, updateSurvey } from "@/lib/survey/service";
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
@@ -35,13 +34,7 @@ const fetchAndAuthorizeSurvey = async (
};
export const GET = withV1ApiWrapper({
handler: async ({
props,
authentication,
}: {
props: { params: Promise<{ surveyId: string }> };
authentication?: TApiV1Authentication;
}) => {
handler: async ({ props, authentication }: THandlerParams<{ params: Promise<{ surveyId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -89,17 +82,15 @@ export const DELETE = withV1ApiWrapper({
props,
auditLog,
authentication,
}: {
props: { params: Promise<{ surveyId: string }> };
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ surveyId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
const params = await props.params;
auditLog!.targetId = params.surveyId;
if (auditLog) {
auditLog.targetId = params.surveyId;
}
try {
const result = await fetchAndAuthorizeSurvey(params.surveyId, authentication, "DELETE");
if (result.error) {
@@ -107,7 +98,9 @@ export const DELETE = withV1ApiWrapper({
response: result.error,
};
}
auditLog!.oldObject = result.survey;
if (auditLog) {
auditLog.oldObject = result.survey;
}
const deletedSurvey = await deleteSurvey(params.surveyId);
return {
@@ -129,18 +122,15 @@ export const PUT = withV1ApiWrapper({
props,
auditLog,
authentication,
}: {
req: NextRequest;
props: { params: Promise<{ surveyId: string }> };
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ surveyId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
const params = await props.params;
auditLog!.targetId = params.surveyId;
if (auditLog) {
auditLog.targetId = params.surveyId;
}
try {
const result = await fetchAndAuthorizeSurvey(params.surveyId, authentication, "PUT");
if (result.error) {
@@ -148,7 +138,9 @@ export const PUT = withV1ApiWrapper({
response: result.error,
};
}
auditLog!.oldObject = result.survey;
if (auditLog) {
auditLog.oldObject = result.survey;
}
const organization = await getOrganizationByEnvironmentId(result.survey.environmentId);
if (!organization) {
@@ -207,7 +199,9 @@ export const PUT = withV1ApiWrapper({
try {
const updatedSurvey = await updateSurvey({ ...inputValidation.data, id: params.surveyId });
auditLog!.newObject = updatedSurvey;
if (auditLog) {
auditLog.newObject = updatedSurvey;
}
if (hasQuestions) {
const surveyWithQuestions = {
@@ -1,7 +1,6 @@
import { NextRequest } from "next/server";
import { handleErrorResponse } from "@/app/api/v1/auth";
import { responses } from "@/app/lib/api/response";
import { TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { getPublicDomain } from "@/lib/getPublicUrl";
import { getSurvey } from "@/lib/survey/service";
import { generateSurveySingleUseIds } from "@/lib/utils/single-use-surveys";
@@ -12,11 +11,7 @@ export const GET = withV1ApiWrapper({
req,
props,
authentication,
}: {
req: NextRequest;
props: { params: Promise<{ surveyId: string }> };
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ surveyId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -1,4 +1,3 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { DatabaseError } from "@formbricks/types/errors";
import { ZSurveyCreateInputWithEnvironmentId } from "@formbricks/types/surveys/types";
@@ -10,7 +9,7 @@ import {
validateSurveyInput,
} from "@/app/lib/api/survey-transformation";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { TApiAuditLog, TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
import { createSurvey } from "@/lib/survey/service";
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
@@ -18,7 +17,7 @@ import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
import { getSurveys } from "./lib/surveys";
export const GET = withV1ApiWrapper({
handler: async ({ req, authentication }: { req: NextRequest; authentication?: TApiV1Authentication }) => {
handler: async ({ req, authentication }) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -68,15 +67,7 @@ export const GET = withV1ApiWrapper({
});
export const POST = withV1ApiWrapper({
handler: async ({
req,
auditLog,
authentication,
}: {
req: NextRequest;
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
handler: async ({ req, auditLog, authentication }) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -143,8 +134,10 @@ export const POST = withV1ApiWrapper({
}
const survey = await createSurvey(environmentId, { ...surveyData, environmentId: undefined });
auditLog!.targetId = survey.id;
auditLog!.newObject = survey;
if (auditLog) {
auditLog.targetId = survey.id;
auditLog.newObject = survey;
}
if (hasQuestions) {
const surveyWithQuestions = {
@@ -1,18 +1,11 @@
import { NextRequest } from "next/server";
import { logger } from "@formbricks/logger";
import { deleteWebhook, getWebhook } from "@/app/api/v1/webhooks/[webhookId]/lib/webhook";
import { responses } from "@/app/lib/api/response";
import { TApiAuditLog, TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
export const GET = withV1ApiWrapper({
handler: async ({
props,
authentication,
}: {
props: { params: Promise<{ webhookId: string }> };
authentication?: TApiV1Authentication;
}) => {
handler: async ({ props, authentication }: THandlerParams<{ params: Promise<{ webhookId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -42,18 +35,15 @@ export const DELETE = withV1ApiWrapper({
props,
auditLog,
authentication,
}: {
req: NextRequest;
props: { params: Promise<{ webhookId: string }> };
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
}: THandlerParams<{ params: Promise<{ webhookId: string }> }>) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
const params = await props.params;
auditLog!.targetId = params.webhookId;
if (auditLog) {
auditLog.targetId = params.webhookId;
}
// check if webhook exists
const webhook = await getWebhook(params.webhookId);
@@ -68,7 +58,9 @@ export const DELETE = withV1ApiWrapper({
};
}
auditLog!.oldObject = webhook;
if (auditLog) {
auditLog.oldObject = webhook;
}
// delete webhook from database
try {
@@ -77,7 +69,9 @@ export const DELETE = withV1ApiWrapper({
response: responses.successResponse(deletedWebhook),
};
} catch (e) {
auditLog!.status = "failure";
if (auditLog) {
auditLog.status = "failure";
}
logger.error({ error: e, url: req.url }, "Error deleting webhook");
return {
response: responses.notFoundResponse("Webhook", params.webhookId),
+7 -14
View File
@@ -1,14 +1,13 @@
import { NextRequest } from "next/server";
import { DatabaseError, InvalidInputError } from "@formbricks/types/errors";
import { createWebhook, getWebhooks } from "@/app/api/v1/webhooks/lib/webhook";
import { ZWebhookInput } from "@/app/api/v1/webhooks/types/webhooks";
import { responses } from "@/app/lib/api/response";
import { transformErrorToDetails } from "@/app/lib/api/validator";
import { TApiAuditLog, TApiV1Authentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { THandlerParams, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
export const GET = withV1ApiWrapper({
handler: async ({ authentication }: { authentication?: TApiV1Authentication }) => {
handler: async ({ authentication }: THandlerParams) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -33,15 +32,7 @@ export const GET = withV1ApiWrapper({
});
export const POST = withV1ApiWrapper({
handler: async ({
req,
auditLog,
authentication,
}: {
req: NextRequest;
auditLog?: TApiAuditLog;
authentication?: TApiV1Authentication;
}) => {
handler: async ({ req, auditLog, authentication }: THandlerParams) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}
@@ -74,8 +65,10 @@ export const POST = withV1ApiWrapper({
try {
const webhook = await createWebhook(inputValidation.data);
auditLog!.targetId = webhook.id;
auditLog!.newObject = webhook;
if (auditLog) {
auditLog.targetId = webhook.id;
auditLog.newObject = webhook;
}
return {
response: responses.successResponse(webhook),
@@ -10,7 +10,7 @@ import { ZContactAttributeKeyCreateInput } from "./[contactAttributeKeyId]/types
import { createContactAttributeKey, getContactAttributeKeys } from "./lib/contact-attribute-keys";
export const GET = withV1ApiWrapper({
handler: async ({ authentication }: { authentication?: TApiV1Authentication }) => {
handler: async ({ authentication }) => {
if (!authentication || !("apiKeyId" in authentication)) {
return { response: responses.notAuthenticatedResponse() };
}