diff --git a/apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx b/apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx index d3a9946b71..6aaa9af924 100644 --- a/apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx +++ b/apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx @@ -38,6 +38,11 @@ export const useStartProject = () => { await apiUtils.project.createRequest.getPendingRequest.invalidate({ projectId: editorEngine.projectId }); }, }); + const { mutateAsync: trackProjectAccess } = api.project.trackAccess.useMutation({ + onSuccess: () => { + apiUtils.project.list.invalidate(); + }, + }); const [projectReadyState, setProjectReadyState] = useState({ canvas: false, conversations: false, @@ -54,6 +59,10 @@ export const useStartProject = () => { } }, [sandbox.session.isConnecting]); + useEffect(() => { + trackProjectAccess({ projectId: editorEngine.projectId }).catch(console.error); + }, [editorEngine.projectId, trackProjectAccess]); + useEffect(() => { if (tabState === 'reactivated') { sandbox.session.reconnect(editorEngine.projectId, user?.id); diff --git a/apps/web/client/src/server/api/routers/project/project.ts b/apps/web/client/src/server/api/routers/project/project.ts index a110db5bbf..0b71e10301 100644 --- a/apps/web/client/src/server/api/routers/project/project.ts +++ b/apps/web/client/src/server/api/routers/project/project.ts @@ -1,5 +1,6 @@ import { env } from '@/env'; import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc'; +import { TRPCError } from '@trpc/server'; import { trackEvent } from '@/utils/analytics/server'; import FirecrawlApp from '@mendable/firecrawl-js'; import { initModel } from '@onlook/ai'; @@ -422,4 +423,29 @@ export const projectRouter = createTRPCRouter({ return { success: true, tags: newTags }; }), + + trackAccess: protectedProcedure + .input(z.object({ projectId: z.string().uuid() })) + .mutation(async ({ ctx, input }) => { + // Check if user has access to this project + const membership = await ctx.db.query.userProjects.findFirst({ + where: and( + eq(userProjects.userId, ctx.user.id), + eq(userProjects.projectId, input.projectId), + ), + }); + + if (!membership) { + throw new TRPCError({ + code: 'FORBIDDEN', + message: 'You do not have access to this project' + }); + } + + await ctx.db.update(projects).set({ + updatedAt: new Date(), + }).where(eq(projects.id, input.projectId)); + + return { success: true }; + }), }); diff --git a/apps/web/client/src/server/api/routers/user/user.ts b/apps/web/client/src/server/api/routers/user/user.ts index 545faeeb3b..6a004bc0a2 100644 --- a/apps/web/client/src/server/api/routers/user/user.ts +++ b/apps/web/client/src/server/api/routers/user/user.ts @@ -22,7 +22,7 @@ export const userRouter = createTRPCRouter({ lastName: user.lastName ?? lastName, displayName: user.displayName ?? displayName, email: user.email ?? authUser.email, - avatarUrl: user.avatarUrl ?? authUser.user_metadata.avatarUrl, + avatarUrl: user.avatarUrl ?? authUser.user_metadata.avatar_url, }) : null; return userData; }), @@ -56,7 +56,7 @@ export const userRouter = createTRPCRouter({ lastName: input.lastName ?? lastName, displayName: input.displayName ?? displayName, email: input.email ?? authUser.email, - avatarUrl: input.avatarUrl ?? authUser.user_metadata.avatarUrl, + avatarUrl: input.avatarUrl ?? authUser.user_metadata.avatar_url, }; const [user] = await ctx.db