feat: group events by environmentId in posthog (#2036)

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
Shubham Palriwala
2024-02-12 15:41:05 +05:30
committed by GitHub
parent 07f28d0971
commit 14b162aabc
19 changed files with 36 additions and 993 deletions

View File

@@ -8,14 +8,21 @@ import { env } from "@formbricks/lib/env.mjs";
const posthogEnabled = env.NEXT_PUBLIC_POSTHOG_API_KEY && env.NEXT_PUBLIC_POSTHOG_API_HOST;
export default function PosthogIdentify({ session }: { session: Session }) {
export default function PosthogIdentify({
session,
environmentId,
}: {
session: Session;
environmentId: string;
}) {
const posthog = usePostHog();
useEffect(() => {
if (posthogEnabled && session.user && posthog) {
posthog.identify(session.user.id, { name: session.user.name, email: session.user.email });
posthog.group("environment", environmentId, { name: environmentId });
}
}, [session, posthog]);
}, [session, environmentId, posthog]);
return null;
}

View File

@@ -10,6 +10,7 @@ import { AuthorizationError } from "@formbricks/types/errors";
import ToasterClient from "@formbricks/ui/ToasterClient";
import FormbricksClient from "../../components/FormbricksClient";
import PosthogIdentify from "./components/PosthogIdentify";
export default async function EnvironmentLayout({ children, params }) {
const session = await getServerSession(authOptions);
@@ -24,6 +25,7 @@ export default async function EnvironmentLayout({ children, params }) {
return (
<>
<ResponseFilterProvider>
<PosthogIdentify session={session} environmentId={params.environmentId} />
<FormbricksClient session={session} />
<ToasterClient />
<EnvironmentsNavbar

View File

@@ -1,19 +1,13 @@
import FormbricksClient from "@/app/(app)/components/FormbricksClient";
import { getServerSession } from "next-auth";
// import { redirect } from "next/navigation";
import { Suspense } from "react";
import { authOptions } from "@formbricks/lib/authOptions";
import { NoMobileOverlay } from "@formbricks/ui/NoMobileOverlay";
import { PHProvider, PostHogPageview } from "@formbricks/ui/PostHogClient";
import PosthogIdentify from "./components/PosthogIdentify";
export default async function AppLayout({ children }) {
const session = await getServerSession(authOptions);
// if (!session) {
// return redirect(`/auth/login`);
// }
return (
<>
@@ -23,13 +17,7 @@ export default async function AppLayout({ children }) {
</Suspense>
<PHProvider>
<>
{session ? (
<>
<PosthogIdentify session={session} />
<FormbricksClient session={session} />
</>
) : null}
{session ? <FormbricksClient session={session} /> : null}
{children}
</>
</PHProvider>

View File

@@ -3,9 +3,8 @@ import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createDisplayLegacy } from "@formbricks/lib/display/service";
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer";
import { getSurvey } from "@formbricks/lib/survey/service";
import { getTeamDetails } from "@formbricks/lib/teamDetail/service";
import { TDisplay, ZDisplayLegacyCreateInput } from "@formbricks/types/displays";
import { InvalidInputError } from "@formbricks/types/errors";
@@ -45,9 +44,6 @@ export async function POST(request: Request): Promise<NextResponse> {
}
}
// find teamId & teamOwnerId from environmentId
const teamDetails = await getTeamDetails(survey.environmentId);
// create display
let display: TDisplay;
try {
@@ -65,13 +61,9 @@ export async function POST(request: Request): Promise<NextResponse> {
}
}
if (teamDetails?.teamOwnerId) {
await capturePosthogEvent(teamDetails.teamOwnerId, "display created", teamDetails.teamId, {
surveyId,
});
} else {
console.warn("Posthog capture not possible. No team owner found");
}
await capturePosthogEnvironmentEvent(survey.environmentId, "display created", {
surveyId,
});
return responses.successResponse({ id: display.id }, true);
}

View File

@@ -5,10 +5,9 @@ import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { UAParser } from "ua-parser-js";
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer";
import { createResponseLegacy } from "@formbricks/lib/response/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { getTeamDetails } from "@formbricks/lib/teamDetail/service";
import { InvalidInputError } from "@formbricks/types/errors";
import { TResponse, ZResponseLegacyInput } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys";
@@ -54,8 +53,6 @@ export async function POST(request: Request): Promise<NextResponse> {
}
}
const teamDetails = await getTeamDetails(survey.environmentId);
let response: TResponse;
try {
const meta = {
@@ -104,14 +101,10 @@ export async function POST(request: Request): Promise<NextResponse> {
});
}
if (teamDetails?.teamOwnerId) {
await capturePosthogEvent(teamDetails.teamOwnerId, "response created", teamDetails.teamId, {
surveyId: response.surveyId,
surveyType: survey.type,
});
} else {
console.warn("Posthog capture not possible. No team owner found");
}
await capturePosthogEnvironmentEvent(survey.environmentId, "response created", {
surveyId: response.surveyId,
surveyType: survey.type,
});
return responses.successResponse({ id: response.id }, true);
}

View File

@@ -3,8 +3,7 @@ import { transformErrorToDetails } from "@/app/lib/api/validator";
import { NextResponse } from "next/server";
import { createDisplay } from "@formbricks/lib/display/service";
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
import { getTeamDetails } from "@formbricks/lib/teamDetail/service";
import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer";
import { ZDisplayCreateInput } from "@formbricks/types/displays";
import { InvalidInputError } from "@formbricks/types/errors";
@@ -33,8 +32,6 @@ export async function POST(request: Request, context: Context): Promise<NextResp
);
}
// find teamId & teamOwnerId from environmentId
const teamDetails = await getTeamDetails(inputValidation.data.environmentId);
let response = {};
// create display
@@ -50,11 +47,7 @@ export async function POST(request: Request, context: Context): Promise<NextResp
}
}
if (teamDetails?.teamOwnerId) {
await capturePosthogEvent(teamDetails.teamOwnerId, "display created", teamDetails.teamId);
} else {
console.warn("Posthog capture not possible. No team owner found");
}
await capturePosthogEnvironmentEvent(inputValidation.data.environmentId, "display created");
return responses.successResponse(response, true);
}

View File

@@ -1,7 +1,6 @@
import { unstable_cache } from "next/cache";
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
import { getTeamDetails } from "@formbricks/lib/teamDetail/service";
import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer";
export const sendFreeLimitReachedEventToPosthogBiWeekly = async (
environmentId: string,
@@ -9,12 +8,9 @@ export const sendFreeLimitReachedEventToPosthogBiWeekly = async (
): Promise<string> =>
unstable_cache(
async () => {
const teamDetails = await getTeamDetails(environmentId);
if (teamDetails?.teamOwnerId) {
await capturePosthogEvent(teamDetails.teamOwnerId, "free limit reached", teamDetails.teamId, {
plan,
});
}
await capturePosthogEnvironmentEvent(environmentId, "free limit reached", {
plan,
});
return "success";
},
[`posthog-${plan}-limitReached-${environmentId}`],

View File

@@ -6,10 +6,9 @@ import { NextResponse } from "next/server";
import { UAParser } from "ua-parser-js";
import { getPerson } from "@formbricks/lib/person/service";
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer";
import { createResponse } from "@formbricks/lib/response/service";
import { getSurvey } from "@formbricks/lib/survey/service";
import { getTeamDetails } from "@formbricks/lib/teamDetail/service";
import { ZId } from "@formbricks/types/environment";
import { InvalidInputError } from "@formbricks/types/errors";
import { TResponse, ZResponseInput } from "@formbricks/types/responses";
@@ -77,8 +76,6 @@ export async function POST(request: Request, context: Context): Promise<NextResp
);
}
const teamDetails = await getTeamDetails(survey.environmentId);
let response: TResponse;
try {
const meta = {
@@ -121,14 +118,10 @@ export async function POST(request: Request, context: Context): Promise<NextResp
});
}
if (teamDetails?.teamOwnerId) {
await capturePosthogEvent(teamDetails.teamOwnerId, "response created", teamDetails.teamId, {
surveyId: response.surveyId,
surveyType: survey.type,
});
} else {
console.warn("Posthog capture not possible. No team owner found");
}
await capturePosthogEnvironmentEvent(survey.environmentId, "response created", {
surveyId: response.surveyId,
surveyType: survey.type,
});
return responses.successResponse({ id: response.id }, true);
}

View File

@@ -1,6 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -1,42 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "@formbricks/database";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const environmentId = req.query.environmentId?.toString();
if (!environmentId) {
return res.status(400).json({ message: "Missing environmentId" });
}
const displayId = req.query.displayId?.toString();
if (!displayId) {
return res.status(400).json({ message: "Missing displayId" });
}
// CORS
if (req.method === "OPTIONS") {
res.status(200).end();
}
// POST
else if (req.method === "POST") {
// create new response
await prisma.display.update({
where: {
id: displayId,
},
data: {
status: "responded",
},
});
return res.status(201).end();
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}

View File

@@ -1,97 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "@formbricks/database";
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const environmentId = req.query.environmentId?.toString();
if (!environmentId) {
return res.status(400).json({ message: "Missing environmentId" });
}
// CORS
if (req.method === "OPTIONS") {
res.status(200).end();
}
// POST
else if (req.method === "POST") {
const { surveyId, personId } = req.body;
if (!surveyId) {
return res.status(400).json({ message: "Missing surveyId" });
}
// get teamId from environment
const environment = await prisma.environment.findUnique({
where: {
id: environmentId,
},
select: {
product: {
select: {
team: {
select: {
id: true,
memberships: {
select: {
userId: true,
role: true,
},
},
},
},
},
},
},
});
if (!environment) {
return res.status(404).json({ message: "Environment not found" });
}
const teamId = environment.product.team.id;
// find team owner
const teamOwnerId = environment.product.team.memberships.find((m) => m.role === "owner")?.userId;
const createBody: any = {
select: {
id: true,
},
data: {
survey: {
connect: {
id: surveyId,
},
},
},
};
if (personId) {
createBody.data.person = {
connect: {
id: personId,
},
};
}
// create new display
const displayData = await prisma.display.create(createBody);
if (teamOwnerId) {
await capturePosthogEvent(teamOwnerId, "display created", teamId, {
surveyId,
});
} else {
console.warn("Posthog capture not possible. No team owner found");
}
return res.json(displayData);
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}

View File

@@ -1,72 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "@formbricks/database";
import { TActionClassType } from "@formbricks/types/actionClasses";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const environmentId = req.query.environmentId?.toString();
if (!environmentId) {
return res.status(400).json({ message: "Missing environmentId" });
}
// CORS
if (req.method === "OPTIONS") {
res.status(200).end();
}
// POST
else if (req.method === "POST") {
const { personId, eventName, properties } = req.body;
if (!personId) {
return res.status(400).json({ message: "Missing personId" });
}
if (!eventName) {
return res.status(400).json({ message: "Missing eventName" });
}
let eventType: TActionClassType = "code";
if (eventName === "Exit Intent (Desktop)" || eventName === "50% Scroll") {
eventType = "automatic";
}
const eventData = await prisma.action.create({
data: {
properties,
person: {
connect: {
id: personId,
},
},
actionClass: {
connectOrCreate: {
where: {
name_environmentId: {
name: eventName,
environmentId,
},
},
create: {
name: eventName,
type: eventType,
environment: {
connect: {
id: environmentId,
},
},
},
},
},
},
select: {
id: true,
},
});
return res.json(eventData);
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}

View File

@@ -1,148 +0,0 @@
import { getSettings } from "@/app/lib/api/clientSettings";
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "@formbricks/database";
import { personCache } from "@formbricks/lib/person/cache";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const environmentId = req.query.environmentId?.toString();
if (!environmentId) {
return res.status(400).json({ message: "Missing environmentId" });
}
const personId = req.query.personId?.toString();
if (!personId) {
return res.status(400).json({ message: "Missing personId" });
}
// CORS
if (req.method === "OPTIONS") {
res.status(200).end();
}
// POST
else if (req.method === "POST") {
const { key, value } = req.body;
if (!key || !value) {
return res.status(400).json({ message: "Missing key or value" });
}
const currentPerson = await prisma.person.findUnique({
where: {
id: personId,
},
select: {
id: true,
environmentId: true,
attributes: {
select: {
id: true,
attributeClass: {
select: {
name: true,
},
},
},
},
},
});
if (!currentPerson) {
return res.status(400).json({ message: "Person not found" });
}
// find attribute class
let attributeClass = await prisma.attributeClass.findUnique({
where: {
name_environmentId: {
name: key,
environmentId,
},
},
select: {
id: true,
},
});
// create new attribute class if not found
if (attributeClass === null) {
attributeClass = await prisma.attributeClass.create({
data: {
name: key,
type: "code",
environment: {
connect: {
id: environmentId,
},
},
},
select: {
id: true,
},
});
}
// upsert attribute (update or create)
const attribute = await prisma.attribute.upsert({
where: {
attributeClassId_personId: {
attributeClassId: attributeClass.id,
personId,
},
},
update: {
value,
},
create: {
attributeClass: {
connect: {
id: attributeClass.id,
},
},
person: {
connect: {
id: personId,
},
},
value,
},
select: {
person: {
select: {
id: true,
environmentId: true,
attributes: {
select: {
id: true,
value: true,
attributeClass: {
select: {
id: true,
name: true,
},
},
},
},
},
},
},
});
const person = attribute.person;
personCache.revalidate({
id: person.id,
environmentId: person.environmentId,
});
const settings = await getSettings(environmentId, person.id);
// return updated person
return res.json({ person, settings });
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}

View File

@@ -1,169 +0,0 @@
import { sendToPipeline } from "@/app/lib/pipelines";
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "@formbricks/database";
import { INTERNAL_SECRET, WEBAPP_URL } from "@formbricks/lib/constants";
import { transformPrismaPerson } from "@formbricks/lib/person/service";
import { responseCache } from "@formbricks/lib/response/cache";
import { TPipelineInput } from "@formbricks/types/pipelines";
import { TResponse } from "@formbricks/types/responses";
import { TTag } from "@formbricks/types/tags";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const environmentId = req.query.environmentId?.toString();
if (!environmentId) {
return res.status(400).json({ message: "Missing environmentId" });
}
const responseId = req.query.responseId?.toString();
if (!responseId) {
return res.status(400).json({ message: "Missing responseId" });
}
// CORS
if (req.method === "OPTIONS") {
res.status(200).end();
}
// POST
else if (req.method === "PUT") {
const { response } = req.body;
const currentResponse = await prisma.response.findUnique({
where: {
id: responseId,
},
select: {
data: true,
},
});
if (!currentResponse) {
return res.status(400).json({ message: "Response not found" });
}
const newResponseData = {
...JSON.parse(JSON.stringify(currentResponse?.data)),
...response.data,
};
const responsePrisma = await prisma.response.update({
where: {
id: responseId,
},
data: {
...response,
data: newResponseData,
},
select: {
id: true,
createdAt: true,
updatedAt: true,
surveyId: true,
finished: true,
data: true,
ttc: true,
meta: true,
personAttributes: true,
singleUseId: true,
person: {
select: {
id: true,
userId: true,
environmentId: true,
createdAt: true,
updatedAt: true,
attributes: {
select: {
value: true,
attributeClass: {
select: {
name: true,
},
},
},
},
},
},
notes: {
select: {
id: true,
createdAt: true,
updatedAt: true,
text: true,
user: {
select: {
id: true,
name: true,
},
},
isResolved: true,
isEdited: true,
},
},
tags: {
select: {
tag: {
select: {
id: true,
createdAt: true,
updatedAt: true,
name: true,
environmentId: true,
},
},
},
},
},
});
// update response cache
responseCache.revalidate({
id: responseId,
surveyId: responsePrisma.surveyId,
environmentId,
});
const responseData: TResponse = {
...responsePrisma,
person: responsePrisma.person ? transformPrismaPerson(responsePrisma.person) : null,
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
};
// send response update to pipeline
// don't await to not block the response
fetch(`${WEBAPP_URL}/api/pipeline`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
internalSecret: INTERNAL_SECRET,
environmentId,
surveyId: responseData.surveyId,
event: "responseUpdated",
response: responseData,
} as TPipelineInput),
});
if (response.finished) {
// send response to pipeline
// don't await to not block the response
sendToPipeline({
environmentId,
surveyId: responseData.surveyId,
event: "responseFinished",
response: responseData,
});
}
return res.json({ message: "Response updated" });
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}

View File

@@ -1,209 +0,0 @@
import { sendToPipeline } from "@/app/lib/pipelines";
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "@formbricks/database";
import { transformPrismaPerson } from "@formbricks/lib/person/service";
import { capturePosthogEvent } from "@formbricks/lib/posthogServer";
import { captureTelemetry } from "@formbricks/lib/telemetry";
import { TResponse } from "@formbricks/types/responses";
import { TTag } from "@formbricks/types/tags";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const environmentId = req.query.environmentId?.toString();
if (!environmentId) {
return res.status(400).json({ message: "Missing environmentId" });
}
// CORS
if (req.method === "OPTIONS") {
return res.status(200).end();
}
// POST
else if (req.method === "POST") {
const { surveyId, personId, response } = req.body;
if (!surveyId) {
return res.status(400).json({ message: "Missing surveyId" });
}
if (!response) {
return res.status(400).json({ message: "Missing data" });
}
// personId can be null, e.g. for link surveys
// check if survey exists
const survey = await prisma.survey.findUnique({
where: {
id: surveyId,
},
select: {
id: true,
type: true,
},
});
if (!survey) {
return res.status(404).json({ message: "Survey not found" });
}
// get teamId from environment
const environment = await prisma.environment.findUnique({
where: {
id: environmentId,
},
select: {
product: {
select: {
team: {
select: {
id: true,
memberships: {
select: {
userId: true,
role: true,
},
},
},
},
},
},
},
});
if (!environment) {
return res.status(404).json({ message: "Environment not found" });
}
const teamId = environment.product.team.id;
// find team owner
const teamOwnerId = environment.product.team.memberships.find((m) => m.role === "owner")?.userId;
const responseInput = {
survey: {
connect: {
id: surveyId,
},
},
...response,
};
if (personId) {
responseInput.data.person = {
connect: {
id: personId,
},
};
}
// create new response
const responsePrisma = await prisma.response.create({
data: {
...responseInput,
},
select: {
id: true,
createdAt: true,
updatedAt: true,
surveyId: true,
finished: true,
data: true,
ttc: true,
meta: true,
personAttributes: true,
singleUseId: true,
person: {
select: {
id: true,
userId: true,
environmentId: true,
createdAt: true,
updatedAt: true,
attributes: {
select: {
value: true,
attributeClass: {
select: {
name: true,
},
},
},
},
},
},
notes: {
select: {
id: true,
createdAt: true,
updatedAt: true,
text: true,
user: {
select: {
id: true,
name: true,
},
},
isResolved: true,
isEdited: true,
},
},
tags: {
select: {
tag: {
select: {
id: true,
createdAt: true,
updatedAt: true,
name: true,
environmentId: true,
},
},
},
},
},
});
const responseData: TResponse = {
...responsePrisma,
person: responsePrisma.person ? transformPrismaPerson(responsePrisma.person) : null,
tags: responsePrisma.tags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
};
// send response to pipeline
// don't await to not block the response
sendToPipeline({
environmentId,
surveyId,
event: "responseCreated",
response: responseData,
});
if (response.finished) {
// send response to pipeline
// don't await to not block the response
sendToPipeline({
environmentId,
surveyId,
event: "responseFinished",
response: responseData,
});
}
captureTelemetry("response created");
if (teamOwnerId) {
await capturePosthogEvent(teamOwnerId, "response created", teamId, {
surveyId,
surveyType: survey.type,
});
} else {
console.warn("Posthog capture not possible. No team owner found");
}
return res.json({ id: responseData.id });
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}

View File

@@ -1,32 +0,0 @@
import { getSettings } from "@/app/lib/api/clientSettings";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const environmentId = req.query.environmentId?.toString();
if (!environmentId) {
return res.status(400).json({ message: "Missing environmentId" });
}
// CORS
if (req.method === "OPTIONS") {
res.status(200).end();
}
// GET
else if (req.method === "POST") {
const { personId } = req.body;
if (!personId) {
return res.status(400).json({ message: "Missing sessionId" });
}
const settings = await getSettings(environmentId, personId);
return res.json(settings);
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}

View File

@@ -1,78 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { prisma } from "@formbricks/database";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
const surveyId = req.query.surveyId?.toString();
if (!surveyId) {
return res.status(400).json({ message: "Missing surveyId" });
}
// CORS
if (req.method === "OPTIONS") {
res.status(200).end();
}
// GET
else if (req.method === "GET") {
// get survey
const survey = await prisma.survey.findFirst({
where: {
id: surveyId,
type: "link",
// status: "inProgress",
},
select: {
id: true,
questions: true,
thankYouCard: true,
environmentId: true,
status: true,
redirectUrl: true,
surveyClosedMessage: true,
},
});
// if survey does not exist, return 404
if (!survey) {
return res.status(404).json({ message: "Survey not found" });
}
// get brandColor from product using environmentId
const product = await prisma.product.findFirst({
where: {
environments: {
some: {
id: survey.environmentId,
},
},
},
select: {
brandColor: true,
linkSurveyBranding: true,
},
});
if (survey.status !== "inProgress") {
return res.status(403).json({
message: "Survey not running",
reason: survey.status,
brandColor: product?.brandColor,
formbricksSignature: product?.linkSurveyBranding,
surveyClosedMessage: survey?.surveyClosedMessage,
});
}
// if survey exists, return survey
return res.status(200).json({
...survey,
brandColor: product?.brandColor,
formbricksSignature: product?.linkSurveyBranding,
});
}
// Unknown HTTP Method
else {
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
}
}

View File

@@ -5,10 +5,9 @@ const enabled =
process.env.NEXT_PUBLIC_POSTHOG_API_HOST &&
process.env.NEXT_PUBLIC_POSTHOG_API_KEY;
export const capturePosthogEvent = async (
userId: string,
export const capturePosthogEnvironmentEvent = async (
environmentId: string,
eventName: string,
teamId?: string,
properties: any = {}
) => {
if (
@@ -23,12 +22,11 @@ export const capturePosthogEvent = async (
host: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
});
client.capture({
distinctId: environmentId,
event: eventName,
distinctId: userId,
groups: teamId ? { company: teamId } : {},
groups: { environment: environmentId },
properties,
});
await client.shutdownAsync();
} catch (error) {
console.error("error sending posthog event:", error);

View File

@@ -1,71 +0,0 @@
import "server-only";
import { Prisma } from "@prisma/client";
import { unstable_cache } from "next/cache";
import { prisma } from "@formbricks/database";
import { ZId } from "@formbricks/types/environment";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { SERVICES_REVALIDATION_INTERVAL } from "../constants";
import { teamCache } from "../team/cache";
import { validateInputs } from "../utils/validate";
export const getTeamDetails = async (
environmentId: string
): Promise<{ teamId: string; teamOwnerId: string | undefined }> =>
unstable_cache(
async () => {
validateInputs([environmentId, ZId]);
try {
const environment = await prisma.environment.findUnique({
where: {
id: environmentId,
},
select: {
product: {
select: {
team: {
select: {
id: true,
memberships: {
select: {
userId: true,
role: true,
},
},
},
},
},
},
},
});
if (!environment) {
throw new ResourceNotFoundError("Environment", environmentId);
}
const teamId: string = environment.product.team.id;
// find team owner
const teamOwnerId: string | undefined = environment.product.team.memberships.find(
(m) => m.role === "owner"
)?.userId;
return {
teamId: teamId,
teamOwnerId: teamOwnerId,
};
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
}
throw error;
}
},
[`getTeamDetails-${environmentId}`],
{
tags: [teamCache.tag.byEnvironmentId(environmentId)],
revalidate: SERVICES_REVALIDATION_INTERVAL,
}
)();