mirror of
https://github.com/lukevella/rallly.git
synced 2025-04-28 17:56:37 +02:00
📈 Posthog package (#1431)
This commit is contained in:
parent
0fc7d0a0c8
commit
a5da319d82
43 changed files with 189 additions and 133 deletions
|
@ -22,6 +22,8 @@ const nextConfig = {
|
|||
"@rallly/icons",
|
||||
"@rallly/ui",
|
||||
"@rallly/tailwind-config",
|
||||
"@rallly/posthog",
|
||||
"@rallly/emails",
|
||||
],
|
||||
webpack(config) {
|
||||
config.module.rules.push({
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"@rallly/emails": "*",
|
||||
"@rallly/icons": "*",
|
||||
"@rallly/languages": "*",
|
||||
"@rallly/posthog": "*",
|
||||
"@rallly/tailwind-config": "*",
|
||||
"@rallly/ui": "*",
|
||||
"@sentry/nextjs": "*",
|
||||
|
@ -70,8 +71,6 @@
|
|||
"next-i18next": "^13.0.3",
|
||||
"php-serialize": "^4.1.1",
|
||||
"postcss": "^8.4.31",
|
||||
"posthog-js": "^1.154.0",
|
||||
"posthog-node": "^4.0.1",
|
||||
"react-big-calendar": "^1.8.1",
|
||||
"react-hook-form": "^7.42.1",
|
||||
"react-hook-form-persist": "^3.0.0",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"use client";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import type {
|
||||
DialogProps} from "@rallly/ui/dialog";
|
||||
|
@ -19,7 +20,6 @@ import { useForm } from "react-hook-form";
|
|||
import { Trans } from "@/components/trans";
|
||||
import { useTranslation } from "@/i18n/client";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
export function DeleteAccountDialog({
|
||||
email,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { useToast } from "@rallly/ui/hooks/use-toast";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
@ -10,7 +11,6 @@ import { useUser } from "@/components/user-provider";
|
|||
import { IfCloudHosted } from "@/contexts/environment";
|
||||
import { useTranslation } from "@/i18n/client";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
const allowedMimeTypes = z.enum(["image/jpeg", "image/png"]);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { cn } from "@rallly/ui";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { DialogTrigger } from "@rallly/ui/dialog";
|
||||
|
@ -26,7 +27,6 @@ import { Trans } from "@/components/trans";
|
|||
import { IfGuest, useUser } from "@/components/user-provider";
|
||||
import { IfFreeUser } from "@/contexts/plan";
|
||||
import type { IconComponent } from "@/types";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
function NavItem({
|
||||
href,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"use client";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@rallly/ui/alert";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { Input } from "@rallly/ui/input";
|
||||
|
@ -16,7 +17,6 @@ import { VerifyCode, verifyCode } from "@/components/auth/auth-forms";
|
|||
import { Spinner } from "@/components/spinner";
|
||||
import { isSelfHosted } from "@/utils/constants";
|
||||
import { validEmail } from "@/utils/form-validation";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
const allowGuestAccess = !isSelfHosted;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"use client";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import {
|
||||
Form,
|
||||
|
@ -15,7 +16,6 @@ import Link from "next/link";
|
|||
import { useParams, useSearchParams } from "next/navigation";
|
||||
import { signIn } from "next-auth/react";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { usePostHog } from "posthog-js/react";
|
||||
import React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"use client";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
@ -9,7 +10,6 @@ import { OptimizedAvatarImage } from "@/components/optimized-avatar-image";
|
|||
import { Skeleton } from "@/components/skeleton";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
type PageProps = { magicLink: string; email: string };
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import React from "react";
|
|||
|
||||
import { TimeZoneChangeDetector } from "@/app/[locale]/timezone-change-detector";
|
||||
import { Providers } from "@/app/providers";
|
||||
import { getServerSession } from "@/auth";
|
||||
import { SessionProvider } from "@/auth/session-provider";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
|
@ -21,21 +23,24 @@ export const viewport: Viewport = {
|
|||
userScalable: false,
|
||||
};
|
||||
|
||||
export default function Root({
|
||||
export default async function Root({
|
||||
children,
|
||||
params: { locale },
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}) {
|
||||
const session = await getServerSession();
|
||||
return (
|
||||
<html lang={locale} className={inter.className}>
|
||||
<body>
|
||||
<Toaster />
|
||||
<Providers>
|
||||
{children}
|
||||
<TimeZoneChangeDetector />
|
||||
</Providers>
|
||||
<SessionProvider session={session}>
|
||||
<Providers>
|
||||
{children}
|
||||
<TimeZoneChangeDetector />
|
||||
</Providers>
|
||||
</SessionProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"use client";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import type {
|
||||
DialogProps} from "@rallly/ui/dialog";
|
||||
|
@ -16,7 +17,6 @@ import { useRouter } from "next/navigation";
|
|||
import { DuplicateForm } from "@/app/[locale]/poll/[urlId]/duplicate-form";
|
||||
import { trpc } from "@/app/providers";
|
||||
import { Trans } from "@/components/trans";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
const formName = "duplicate-form";
|
||||
export function DuplicateDialog({
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
|
@ -8,7 +9,6 @@ import {
|
|||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@rallly/ui/dialog";
|
||||
import { usePostHog } from "posthog-js/react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { Trans } from "@/components/trans";
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
"use client";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import type { ButtonProps } from "@rallly/ui/button";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
export function LogoutButton({
|
||||
children,
|
||||
onClick,
|
||||
|
|
|
@ -4,7 +4,7 @@ import { randomid } from "@rallly/utils/nanoid";
|
|||
import languageParser from "accept-language-parser";
|
||||
import type { NextRequest, NextResponse } from "next/server";
|
||||
import type { JWT } from "next-auth/jwt";
|
||||
import { encode } from "next-auth/jwt";
|
||||
import { decode, encode } from "next-auth/jwt";
|
||||
|
||||
const supportedLocales = Object.keys(languages);
|
||||
|
||||
|
@ -61,10 +61,20 @@ export async function resetUser(req: NextRequest, res: NextResponse) {
|
|||
|
||||
export async function initGuest(req: NextRequest, res: NextResponse) {
|
||||
const { name } = getCookieSettings();
|
||||
|
||||
if (req.cookies.has(name)) {
|
||||
// already has a session token
|
||||
return;
|
||||
const token = req.cookies.get(name)?.value;
|
||||
if (token) {
|
||||
try {
|
||||
const jwt = await decode({
|
||||
token,
|
||||
secret: process.env.SECRET_PASSWORD,
|
||||
});
|
||||
if (jwt) {
|
||||
return jwt;
|
||||
}
|
||||
} catch (error) {
|
||||
// invalid token
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
const locale = await getLocaleFromHeader(req);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
"use client";
|
||||
import { PostHogProvider } from "@rallly/posthog/client";
|
||||
import { TooltipProvider } from "@rallly/ui/tooltip";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { createTRPCReact } from "@trpc/react-query";
|
||||
import { domMax, LazyMotion } from "framer-motion";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { UserProvider } from "@/components/user-provider";
|
||||
|
@ -32,13 +32,13 @@ export function Providers(props: { children: React.ReactNode }) {
|
|||
<QueryClientProvider client={queryClient}>
|
||||
<I18nProvider>
|
||||
<TooltipProvider>
|
||||
<SessionProvider>
|
||||
<PostHogProvider>
|
||||
<UserProvider>
|
||||
<ConnectedDayjsProvider>
|
||||
{props.children}
|
||||
</ConnectedDayjsProvider>
|
||||
</UserProvider>
|
||||
</SessionProvider>
|
||||
</PostHogProvider>
|
||||
</TooltipProvider>
|
||||
</I18nProvider>
|
||||
</QueryClientProvider>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { prisma } from "@rallly/database";
|
||||
import { posthog } from "@rallly/posthog/server";
|
||||
import { absoluteUrl } from "@rallly/utils/absolute-url";
|
||||
import { generateOtp, randomid } from "@rallly/utils/nanoid";
|
||||
import type {
|
||||
|
@ -16,15 +17,15 @@ import EmailProvider from "next-auth/providers/email";
|
|||
import GoogleProvider from "next-auth/providers/google";
|
||||
import type { Provider } from "next-auth/providers/index";
|
||||
|
||||
import { posthog } from "@/app/posthog";
|
||||
import { CustomPrismaAdapter } from "@/auth/custom-prisma-adapter";
|
||||
import { mergeGuestsIntoUser } from "@/auth/merge-user";
|
||||
import { env } from "@/env";
|
||||
import type { RegistrationTokenPayload } from "@/trpc/types";
|
||||
import { getEmailClient } from "@/utils/emails";
|
||||
import { getValueByPath } from "@/utils/get-value-by-path";
|
||||
import { decryptToken } from "@/utils/session";
|
||||
|
||||
import { CustomPrismaAdapter } from "./auth/custom-prisma-adapter";
|
||||
import { mergeGuestsIntoUser } from "./auth/merge-user";
|
||||
|
||||
const providers: Provider[] = [
|
||||
// When a user registers, we don't want to go through the email verification process
|
||||
// so this provider allows us exchange the registration token for a session token
|
||||
|
|
8
apps/web/src/auth/session-provider.tsx
Normal file
8
apps/web/src/auth/session-provider.tsx
Normal file
|
@ -0,0 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import type { SessionProviderProps } from "next-auth/react";
|
||||
import { SessionProvider as NextAuthSessionProvider } from "next-auth/react";
|
||||
|
||||
export function SessionProvider(props: SessionProviderProps) {
|
||||
return <NextAuthSessionProvider {...props} />;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
"use client";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import {
|
||||
Card,
|
||||
|
@ -19,7 +20,6 @@ import { Trans } from "@/components/trans";
|
|||
import { useUser } from "@/components/user-provider";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { setCookie } from "@/utils/cookies";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
import type { NewEventData} from "./forms";
|
||||
import { PollDetailsForm, PollOptionsForm } from "./forms";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"use client";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { cn } from "@rallly/ui";
|
||||
import { Badge } from "@rallly/ui/badge";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
|
@ -37,7 +38,6 @@ import { usePermissions } from "@/contexts/permissions";
|
|||
import { usePoll } from "@/contexts/poll";
|
||||
import { useRole } from "@/contexts/role";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
import { requiredString } from "../../utils/form-validation";
|
||||
import TruncatedLinkify from "../poll/truncated-linkify";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { cn } from "@rallly/ui";
|
||||
import {
|
||||
Card,
|
||||
|
@ -17,7 +18,6 @@ import { Trans } from "react-i18next";
|
|||
import { PayWallDialog } from "@/components/pay-wall-dialog";
|
||||
import { ProFeatureBadge } from "@/components/pro-feature-badge";
|
||||
import { usePlan } from "@/contexts/plan";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
export type PollSettingsFormData = {
|
||||
requireParticipantEmail: boolean;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
|
@ -40,7 +41,6 @@ import { useDeleteParticipantMutation } from "@/components/poll/mutations";
|
|||
import { Trans } from "@/components/trans";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { useFormValidation } from "@/utils/form-validation";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
export const ParticipantDropdown = ({
|
||||
participant,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { useDialog } from "@rallly/ui/dialog";
|
||||
import {
|
||||
|
@ -34,7 +35,6 @@ import { ProFeatureBadge } from "@/components/pro-feature-badge";
|
|||
import { Trans } from "@/components/trans";
|
||||
import { usePlan } from "@/contexts/plan";
|
||||
import { usePoll } from "@/contexts/poll";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
import { DeletePollDialog } from "./manage-poll/delete-poll-dialog";
|
||||
import { useCsvExporter } from "./manage-poll/use-csv-exporter";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
|
@ -11,7 +12,6 @@ import * as React from "react";
|
|||
|
||||
import { Trans } from "@/components/trans";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
export const DeletePollDialog: React.FunctionComponent<{
|
||||
open: boolean;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { usePostHog } from "@rallly/posthog/client";
|
||||
|
||||
import { usePoll } from "@/components/poll-context";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
import type { ParticipantForm } from "./types";
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import { Icon } from "@rallly/ui/icon";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
|
||||
|
@ -10,7 +11,6 @@ import { Skeleton } from "@/components/skeleton";
|
|||
import { Trans } from "@/components/trans";
|
||||
import { useUser } from "@/components/user-provider";
|
||||
import { trpc } from "@/trpc/client";
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
import { usePoll } from "../poll-context";
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import { Button } from "@rallly/ui/button";
|
||||
import Link from "next/link";
|
||||
import { Trans } from "next-i18next";
|
||||
import React from "react";
|
||||
|
||||
import { usePostHog } from "@/utils/posthog";
|
||||
|
||||
export const UpgradeButton = ({
|
||||
children,
|
||||
annual,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
"use client";
|
||||
import { usePostHog } from "@rallly/posthog/client";
|
||||
import type { Session } from "next-auth";
|
||||
import { useSession } from "next-auth/react";
|
||||
import React from "react";
|
||||
|
||||
import { Spinner } from "@/components/spinner";
|
||||
import { useSubscription } from "@/contexts/plan";
|
||||
import { PostHogProvider } from "@/contexts/posthog";
|
||||
import { PreferencesProvider } from "@/contexts/preferences";
|
||||
import { useTranslation } from "@/i18n/client";
|
||||
import { trpc } from "@/trpc/client";
|
||||
|
@ -60,6 +60,25 @@ export const UserProvider = (props: { children?: React.ReactNode }) => {
|
|||
const updatePreferences = trpc.user.updatePreferences.useMutation();
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const posthog = usePostHog();
|
||||
|
||||
const isGuest = !user?.email;
|
||||
const tier = isGuest ? "guest" : subscription?.active ? "pro" : "hobby";
|
||||
|
||||
React.useEffect(() => {
|
||||
if (user) {
|
||||
posthog?.identify(user.id, {
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
tier,
|
||||
timeZone: user.timeZone ?? null,
|
||||
image: user.image ?? null,
|
||||
locale: user.locale ?? i18n.language,
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [user?.id]);
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
|
@ -68,9 +87,6 @@ export const UserProvider = (props: { children?: React.ReactNode }) => {
|
|||
);
|
||||
}
|
||||
|
||||
const isGuest = !user.email;
|
||||
const tier = isGuest ? "guest" : subscription?.active ? "pro" : "hobby";
|
||||
|
||||
return (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
|
@ -109,7 +125,7 @@ export const UserProvider = (props: { children?: React.ReactNode }) => {
|
|||
await session.update(newPreferences);
|
||||
}}
|
||||
>
|
||||
<PostHogProvider>{props.children}</PostHogProvider>
|
||||
{props.children}
|
||||
</PreferencesProvider>
|
||||
</UserContext.Provider>
|
||||
);
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
"use client";
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider as Provider, usePostHog } from "posthog-js/react";
|
||||
import React from "react";
|
||||
import { useMount } from "react-use";
|
||||
|
||||
import { useUser } from "@/components/user-provider";
|
||||
import { env } from "@/env";
|
||||
|
||||
type PostHogProviderProps = React.PropsWithChildren;
|
||||
|
||||
if (typeof window !== "undefined" && env.NEXT_PUBLIC_POSTHOG_API_KEY) {
|
||||
posthog.init(env.NEXT_PUBLIC_POSTHOG_API_KEY, {
|
||||
debug: false,
|
||||
api_host: env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||
capture_pageview: false,
|
||||
capture_pageleave: true,
|
||||
disable_session_recording: true,
|
||||
enable_heatmaps: false,
|
||||
persistence: "memory",
|
||||
autocapture: false,
|
||||
opt_out_capturing_by_default: false,
|
||||
});
|
||||
}
|
||||
|
||||
function usePostHogPageView() {
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const posthog = usePostHog();
|
||||
const previousUrl = React.useRef<string | null>(null);
|
||||
React.useEffect(() => {
|
||||
// Track pageviews
|
||||
if (pathname && posthog) {
|
||||
let url = window.origin + pathname;
|
||||
if (searchParams?.toString()) {
|
||||
url = url + `?${searchParams.toString()}`;
|
||||
}
|
||||
if (previousUrl.current !== url) {
|
||||
posthog.capture("$pageview", {
|
||||
$current_url: url,
|
||||
});
|
||||
previousUrl.current = url;
|
||||
}
|
||||
}
|
||||
}, [pathname, searchParams, posthog]);
|
||||
}
|
||||
|
||||
export function PostHogProvider(props: PostHogProviderProps) {
|
||||
const { user } = useUser();
|
||||
|
||||
usePostHogPageView();
|
||||
|
||||
useMount(() => {
|
||||
if (user.email) {
|
||||
posthog.identify(user.id, {
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
tier: user.tier,
|
||||
timeZone: user.timeZone,
|
||||
locale: user.locale,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return <Provider client={posthog}>{props.children}</Provider>;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import languages from "@rallly/languages";
|
||||
import { withPostHog } from "@rallly/posthog/next/middleware";
|
||||
import { NextResponse } from "next/server";
|
||||
import withAuth from "next-auth/middleware";
|
||||
|
||||
|
@ -34,7 +35,11 @@ export const middleware = withAuth(
|
|||
|
||||
const res = NextResponse.rewrite(newUrl);
|
||||
|
||||
await initGuest(req, res);
|
||||
const jwt = await initGuest(req, res);
|
||||
|
||||
if (jwt?.sub) {
|
||||
await withPostHog(res, { distinctID: jwt.sub });
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { posthogApiHandler } from "@rallly/posthog/server";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { posthogApiHandler } from "@/app/posthog";
|
||||
import { AuthApiRoute } from "@/auth";
|
||||
import { composeApiHandlers } from "@/utils/next";
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import type { Stripe } from "@rallly/billing";
|
||||
import { stripe } from "@rallly/billing";
|
||||
import { prisma } from "@rallly/database";
|
||||
import { posthog, posthogApiHandler } from "@rallly/posthog/server";
|
||||
import * as Sentry from "@sentry/node";
|
||||
import { buffer } from "micro";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { z } from "zod";
|
||||
|
||||
import { posthog, posthogApiHandler } from "@/app/posthog";
|
||||
import { composeApiHandlers } from "@/utils/next";
|
||||
|
||||
export const config = {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { posthogApiHandler } from "@rallly/posthog/server";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { createNextApiHandler } from "@trpc/server/adapters/next";
|
||||
|
||||
import { posthogApiHandler } from "@/app/posthog";
|
||||
import { getServerSession } from "@/auth";
|
||||
import type { AppRouter} from "@/trpc/routers";
|
||||
import type { AppRouter } from "@/trpc/routers";
|
||||
import { appRouter } from "@/trpc/routers";
|
||||
import { getEmailClient } from "@/utils/emails";
|
||||
import { composeApiHandlers } from "@/utils/next";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { prisma } from "@rallly/database";
|
||||
import { posthog } from "@rallly/posthog/server";
|
||||
import { generateOtp } from "@rallly/utils/nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { posthog } from "@/app/posthog";
|
||||
import { isEmailBlocked } from "@/auth";
|
||||
import { createToken, decryptToken } from "@/utils/session";
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { PollStatus } from "@rallly/database";
|
||||
import { prisma } from "@rallly/database";
|
||||
import { posthog } from "@rallly/posthog/server";
|
||||
import { absoluteUrl, shortUrl } from "@rallly/utils/absolute-url";
|
||||
import { nanoid } from "@rallly/utils/nanoid";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
@ -7,7 +8,6 @@ import dayjs from "dayjs";
|
|||
import * as ics from "ics";
|
||||
import { z } from "zod";
|
||||
|
||||
import { posthog } from "@/app/posthog";
|
||||
import { getEmailClient } from "@/utils/emails";
|
||||
|
||||
import { getTimeZoneAbbreviation } from "../../utils/date";
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import { usePostHog as usePostHogHook } from "posthog-js/react";
|
||||
|
||||
export const usePostHog = () => {
|
||||
const posthog = usePostHogHook();
|
||||
return process.env.NEXT_PUBLIC_POSTHOG_API_KEY ? posthog : null;
|
||||
};
|
2
packages/posthog/.eslintrc.js
Normal file
2
packages/posthog/.eslintrc.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
/** @type {import("eslint").Linter.Config} */
|
||||
module.exports = require("@rallly/eslint-config/preset")(__dirname);
|
18
packages/posthog/package.json
Normal file
18
packages/posthog/package.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "@rallly/posthog",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"exports": {
|
||||
"./server": "./src/server/index.ts",
|
||||
"./client": "./src/client/index.ts",
|
||||
"./next/middleware": "./src/next/middleware.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"posthog-js": "^1.178.0",
|
||||
"posthog-node": "^4.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^14.2.13",
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
}
|
2
packages/posthog/src/client/index.ts
Normal file
2
packages/posthog/src/client/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { PostHogProvider } from "./provider";
|
||||
export { usePostHog } from "posthog-js/react";
|
36
packages/posthog/src/client/provider.tsx
Normal file
36
packages/posthog/src/client/provider.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
"use client";
|
||||
import Cookies from "js-cookie";
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider as Provider } from "posthog-js/react";
|
||||
import React from "react";
|
||||
|
||||
import { POSTHOG_BOOTSTAP_DATA_COOKIE_NAME } from "../constants";
|
||||
|
||||
if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
|
||||
let bootstrapData = {};
|
||||
try {
|
||||
const cookieData = Cookies.get(POSTHOG_BOOTSTAP_DATA_COOKIE_NAME);
|
||||
if (cookieData) {
|
||||
bootstrapData = JSON.parse(cookieData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Failed to parse PostHog bootstrap data:", error);
|
||||
}
|
||||
|
||||
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY, {
|
||||
debug: false,
|
||||
api_host: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||
capture_pageview: false,
|
||||
capture_pageleave: true,
|
||||
disable_session_recording: true,
|
||||
enable_heatmaps: false,
|
||||
persistence: "memory",
|
||||
bootstrap: bootstrapData,
|
||||
autocapture: false,
|
||||
opt_out_capturing_by_default: false,
|
||||
});
|
||||
}
|
||||
|
||||
export function PostHogProvider(props: { children?: React.ReactNode }) {
|
||||
return <Provider client={posthog}>{props.children}</Provider>;
|
||||
}
|
1
packages/posthog/src/constants.ts
Normal file
1
packages/posthog/src/constants.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const POSTHOG_BOOTSTAP_DATA_COOKIE_NAME = "posthog_bootstrap_data";
|
22
packages/posthog/src/next/middleware.ts
Normal file
22
packages/posthog/src/next/middleware.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { POSTHOG_BOOTSTAP_DATA_COOKIE_NAME } from "../constants";
|
||||
|
||||
const posthogApiKey = process.env.NEXT_PUBLIC_POSTHOG_API_KEY;
|
||||
|
||||
export async function withPostHog(
|
||||
res: NextResponse,
|
||||
bootstrapData: { distinctID?: string },
|
||||
) {
|
||||
if (!posthogApiKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
res.cookies.set({
|
||||
name: POSTHOG_BOOTSTAP_DATA_COOKIE_NAME,
|
||||
value: JSON.stringify(bootstrapData),
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: "lax",
|
||||
path: "/",
|
||||
});
|
||||
}
|
|
@ -1,13 +1,11 @@
|
|||
import { waitUntil } from "@vercel/functions";
|
||||
import { PostHog } from "posthog-node";
|
||||
|
||||
import { env } from "@/env";
|
||||
|
||||
function PostHogClient() {
|
||||
if (!env.NEXT_PUBLIC_POSTHOG_API_KEY) return null;
|
||||
if (!process.env.NEXT_PUBLIC_POSTHOG_API_KEY) return null;
|
||||
|
||||
const posthogClient = new PostHog(env.NEXT_PUBLIC_POSTHOG_API_KEY, {
|
||||
host: env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||
const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_API_KEY, {
|
||||
host: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||
flushAt: 1,
|
||||
flushInterval: 0,
|
||||
});
|
5
packages/posthog/tsconfig.json
Normal file
5
packages/posthog/tsconfig.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"extends": "@rallly/tsconfig/next.json",
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"],
|
||||
}
|
10
yarn.lock
10
yarn.lock
|
@ -12718,17 +12718,17 @@ postgres-range@^1.1.1:
|
|||
resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.4.tgz#a59c5f9520909bcec5e63e8cf913a92e4c952863"
|
||||
integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==
|
||||
|
||||
posthog-js@^1.154.0:
|
||||
version "1.181.0"
|
||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.181.0.tgz#b2119f6a27b27297dee9540bfcd33eddab06905c"
|
||||
integrity sha512-bI+J+f4E8x4JwbGtG6LReQv1Xvss01F6cs7UDlvffHySpVhNq4ptkNjV88B92IVEsrCtNYhy/TjFnGxk6RN0Qw==
|
||||
posthog-js@^1.178.0:
|
||||
version "1.178.0"
|
||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.178.0.tgz#80005798e6c67d4d6565a5648939a0f017b0879b"
|
||||
integrity sha512-ILD4flNh72d5dycc4ZouKORlaVr+pDzl5TlZr1JgP0NBAoduHjhE7XZYwk7zdYkry7u0qAIpfv2306zJCW2vGg==
|
||||
dependencies:
|
||||
core-js "^3.38.1"
|
||||
fflate "^0.4.8"
|
||||
preact "^10.19.3"
|
||||
web-vitals "^4.2.0"
|
||||
|
||||
posthog-node@^4.0.1:
|
||||
posthog-node@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-4.2.1.tgz#c9f077116bebd06dc65a3f9ae282d10db242c660"
|
||||
integrity sha512-l+fsjYEkTik3m/G0pE7gMr4qBJP84LhK779oQm6MBzhBGpd4By4qieTW+4FUAlNCyzQTynn3Nhsa50c0IELSxQ==
|
||||
|
|
Loading…
Add table
Reference in a new issue