mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-05 16:19:55 -06:00
chore: add caching to js actions endpoint (#822)
* fix: caching in actions endpoint * refactor --------- Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { responses } from "@/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/lib/api/validator";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { createAction } from "@formbricks/lib/services/actions";
|
||||
import { ZJsActionInput } from "@formbricks/types/v1/js";
|
||||
import { EventType } from "@prisma/client";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function OPTIONS(): Promise<NextResponse> {
|
||||
@@ -26,42 +25,11 @@ export async function POST(req: Request): Promise<NextResponse> {
|
||||
|
||||
const { environmentId, sessionId, name, properties } = inputValidation.data;
|
||||
|
||||
let eventType: EventType = EventType.code;
|
||||
if (name === "Exit Intent (Desktop)" || name === "50% Scroll") {
|
||||
eventType = EventType.automatic;
|
||||
}
|
||||
|
||||
await prisma.event.create({
|
||||
data: {
|
||||
properties,
|
||||
session: {
|
||||
connect: {
|
||||
id: sessionId,
|
||||
},
|
||||
},
|
||||
eventClass: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
name_environmentId: {
|
||||
name,
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
name,
|
||||
type: eventType,
|
||||
environment: {
|
||||
connect: {
|
||||
id: environmentId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
createAction({
|
||||
environmentId,
|
||||
sessionId,
|
||||
name,
|
||||
properties,
|
||||
});
|
||||
|
||||
return responses.successResponse({}, true);
|
||||
|
||||
@@ -4,7 +4,9 @@ import { withAccelerate } from "@prisma/extension-accelerate";
|
||||
const prismaClientSingleton = () => {
|
||||
return new PrismaClient({
|
||||
datasources: { db: { url: process.env.DATABASE_URL } },
|
||||
/* log: ["query", "info"], */
|
||||
...(process.env.DEBUG === "1" && {
|
||||
log: ["query", "info"],
|
||||
}),
|
||||
}).$extends(withAccelerate());
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,14 @@ import { revalidateTag, unstable_cache } from "next/cache";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
|
||||
const halfHourInSeconds = 60 * 30;
|
||||
|
||||
export const getActionClassCacheTag = (name: string, environmentId: string): string =>
|
||||
`env-${environmentId}-actionClass-${name}`;
|
||||
const getActionClassCacheKey = (name: string, environmentId: string): string[] => [
|
||||
getActionClassCacheTag(name, environmentId),
|
||||
];
|
||||
|
||||
const getActionClassesCacheTag = (environmentId: string): string => `env-${environmentId}-actionClasses`;
|
||||
const getActionClassesCacheKey = (environmentId: string): string[] => [
|
||||
getActionClassesCacheTag(environmentId),
|
||||
@@ -52,7 +60,7 @@ export const getActionClassesCached = (environmentId: string) =>
|
||||
getActionClassesCacheKey(environmentId),
|
||||
{
|
||||
tags: getActionClassesCacheKey(environmentId),
|
||||
revalidate: 30 * 60, // 30 minutes
|
||||
revalidate: halfHourInSeconds,
|
||||
}
|
||||
)();
|
||||
|
||||
@@ -132,6 +140,7 @@ export const updateActionClass = async (
|
||||
});
|
||||
|
||||
// revalidate cache
|
||||
revalidateTag(getActionClassCacheTag(result.name, environmentId));
|
||||
revalidateTag(getActionClassesCacheTag(environmentId));
|
||||
|
||||
return result;
|
||||
@@ -139,3 +148,20 @@ export const updateActionClass = async (
|
||||
throw new DatabaseError(`Database error when updating an action for environment ${environmentId}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const getActionClassCached = async (name: string, environmentId: string) =>
|
||||
unstable_cache(
|
||||
async () => {
|
||||
return await prisma.eventClass.findFirst({
|
||||
where: {
|
||||
name,
|
||||
environmentId,
|
||||
},
|
||||
});
|
||||
},
|
||||
getActionClassCacheKey(name, environmentId),
|
||||
{
|
||||
tags: getActionClassCacheKey(name, environmentId),
|
||||
revalidate: halfHourInSeconds,
|
||||
}
|
||||
)();
|
||||
|
||||
@@ -8,6 +8,11 @@ import { ZId } from "@formbricks/types/v1/environment";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { TJsActionInput } from "@formbricks/types/v1/js";
|
||||
import { revalidateTag } from "next/cache";
|
||||
import { EventType } from "@prisma/client";
|
||||
import { getActionClassCacheTag, getActionClassCached } from "../services/actionClass";
|
||||
import { getSessionCached } from "../services/session";
|
||||
|
||||
export const getActionsByEnvironmentId = cache(
|
||||
async (environmentId: string, limit?: number): Promise<TAction[]> => {
|
||||
@@ -48,3 +53,80 @@ export const getActionsByEnvironmentId = cache(
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const createAction = async (data: TJsActionInput) => {
|
||||
const { environmentId, name, properties, sessionId } = data;
|
||||
|
||||
let eventType: EventType = EventType.code;
|
||||
if (name === "Exit Intent (Desktop)" || name === "50% Scroll") {
|
||||
eventType = EventType.automatic;
|
||||
}
|
||||
|
||||
const session = await getSessionCached(sessionId);
|
||||
|
||||
if (!session) {
|
||||
throw new Error("Session not found");
|
||||
}
|
||||
|
||||
const actionClass = await getActionClassCached(name, environmentId);
|
||||
|
||||
if (actionClass) {
|
||||
await prisma.event.create({
|
||||
data: {
|
||||
properties,
|
||||
sessionId: session.id,
|
||||
eventClassId: actionClass.id,
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if action class does not exist, create it and then create the action
|
||||
await prisma.$transaction([
|
||||
prisma.eventClass.create({
|
||||
data: {
|
||||
name,
|
||||
type: eventType,
|
||||
environmentId,
|
||||
},
|
||||
}),
|
||||
|
||||
prisma.event.create({
|
||||
data: {
|
||||
properties,
|
||||
session: {
|
||||
connect: {
|
||||
id: sessionId,
|
||||
},
|
||||
},
|
||||
eventClass: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
name_environmentId: {
|
||||
name,
|
||||
environmentId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
name,
|
||||
type: eventType,
|
||||
environment: {
|
||||
connect: {
|
||||
id: environmentId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
// revalidate cache
|
||||
revalidateTag(sessionId);
|
||||
revalidateTag(getActionClassCacheTag(name, environmentId));
|
||||
};
|
||||
|
||||
@@ -10,6 +10,10 @@ import { revalidateTag, unstable_cache } from "next/cache";
|
||||
import { cache } from "react";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
|
||||
const halfHourInSeconds = 60 * 30;
|
||||
|
||||
const getSessionCacheKey = (sessionId: string): string[] => [sessionId];
|
||||
|
||||
const select = {
|
||||
id: true,
|
||||
createdAt: true,
|
||||
@@ -45,10 +49,10 @@ export const getSessionCached = (sessionId: string) =>
|
||||
async () => {
|
||||
return await getSession(sessionId);
|
||||
},
|
||||
[sessionId],
|
||||
getSessionCacheKey(sessionId),
|
||||
{
|
||||
tags: [sessionId],
|
||||
revalidate: 30 * 60, // 30 minutes
|
||||
tags: getSessionCacheKey(sessionId),
|
||||
revalidate: halfHourInSeconds, // 30 minutes
|
||||
}
|
||||
)();
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"outputs": ["dist/**", ".next/**"],
|
||||
"env": [
|
||||
"CRON_SECRET",
|
||||
"DEBUG",
|
||||
"PRISMA_GENERATE_DATAPROXY",
|
||||
"GITHUB_ID",
|
||||
"GITHUB_SECRET",
|
||||
|
||||
Reference in New Issue
Block a user