diff --git a/web/Dockerfile b/web/Dockerfile index 3cfd1a0f3..46ec20325 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -61,6 +61,10 @@ ENV NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT} ARG NEXT_PUBLIC_CUSTOM_REFRESH_URL ENV NEXT_PUBLIC_CUSTOM_REFRESH_URL=${NEXT_PUBLIC_CUSTOM_REFRESH_URL} +ARG NEXT_PUBLIC_POSTHOG_KEY +ARG NEXT_PUBLIC_POSTHOG_HOST +ENV NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY} +ENV NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST} RUN npx next build @@ -122,6 +126,13 @@ ENV NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT} ARG NEXT_PUBLIC_CUSTOM_REFRESH_URL ENV NEXT_PUBLIC_CUSTOM_REFRESH_URL=${NEXT_PUBLIC_CUSTOM_REFRESH_URL} + +ARG NEXT_PUBLIC_POSTHOG_KEY +ARG NEXT_PUBLIC_POSTHOG_HOST +ENV NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY} +ENV NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST} + + # Note: Don't expose ports here, Compose will handle that for us if necessary. # If you want to run this without compose, specify the ports to # expose via cli diff --git a/web/package-lock.json b/web/package-lock.json index ffb68fc16..8d914d5b6 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -33,6 +33,7 @@ "next": "^14.2.3", "npm": "^10.8.0", "postcss": "^8.4.31", + "posthog-js": "^1.176.0", "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -4493,6 +4494,16 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, + "node_modules/core-js": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -5639,6 +5650,11 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -11136,6 +11152,26 @@ "node": ">=0.10.0" } }, + "node_modules/posthog-js": { + "version": "1.176.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.176.0.tgz", + "integrity": "sha512-T5XKNtRzp7q6CGb7Vc7wAI76rWap9fiuDUPxPsyPBPDkreKya91x9RIsSapAVFafwD1AEin1QMczCmt9Le9BWw==", + "dependencies": { + "core-js": "^3.38.1", + "fflate": "^0.4.8", + "preact": "^10.19.3", + "web-vitals": "^4.2.0" + } + }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -13192,6 +13228,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/web/package.json b/web/package.json index e7e2d4098..418eaf4f9 100644 --- a/web/package.json +++ b/web/package.json @@ -34,6 +34,7 @@ "next": "^14.2.3", "npm": "^10.8.0", "postcss": "^8.4.31", + "posthog-js": "^1.176.0", "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/web/src/app/PostHogPageView.tsx b/web/src/app/PostHogPageView.tsx new file mode 100644 index 000000000..e75c09731 --- /dev/null +++ b/web/src/app/PostHogPageView.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { usePathname, useSearchParams } from "next/navigation"; +import { useEffect } from "react"; +import { usePostHog } from "posthog-js/react"; + +export default function PostHogPageView(): null { + const pathname = usePathname(); + const searchParams = useSearchParams(); + const posthog = usePostHog(); + + useEffect(() => { + if (!posthog) { + return; + } + + // Track pageviews + if (pathname) { + let url = window.origin + pathname; + if (searchParams.toString()) { + url = url + `?${searchParams.toString()}`; + } + posthog.capture("$pageview", { + $current_url: url, + }); + } + }, [pathname, searchParams, posthog]); + + return null; +} diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index e6339ade9..e7e3c2c41 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -8,19 +8,21 @@ import { CUSTOM_ANALYTICS_ENABLED, SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED, } from "@/lib/constants"; -import { SettingsProvider } from "@/components/settings/SettingsProvider"; import { Metadata } from "next"; import { buildClientUrl } from "@/lib/utilsSS"; import { Inter } from "next/font/google"; -import Head from "next/head"; import { EnterpriseSettings, GatingType } from "./admin/settings/interfaces"; import { Card } from "@tremor/react"; import { HeaderTitle } from "@/components/header/HeaderTitle"; import { Logo } from "@/components/Logo"; -import { UserProvider } from "@/components/user/UserProvider"; -import { ProviderContextProvider } from "@/components/chat_search/ProviderContext"; import { fetchAssistantData } from "@/lib/chat/fetchAssistantdata"; import { AppProvider } from "@/components/context/AppProvider"; +import { PHProvider } from "./providers"; +import { default as dynamicImport } from "next/dynamic"; + +const PostHogPageView = dynamicImport(() => import("./PostHogPageView"), { + ssr: false, +}); const inter = Inter({ subsets: ["latin"], @@ -57,139 +59,124 @@ export default async function RootLayout({ }) { const combinedSettings = await fetchSettingsSS(); - const data = await fetchAssistantData(); - - const { assistants, hasAnyConnectors, hasImageCompatibleModel } = data; - const productGating = combinedSettings?.settings.product_gating ?? GatingType.NONE; - if (!combinedSettings) { - return ( - - - Settings Unavailable | Danswer - - -
-
- Danswer - -
- - -

Error

-

- Your Danswer instance was not configured properly and your - settings could not be loaded. This could be due to an admin - configuration issue or an incomplete setup. -

-

- If you're an admin, please check{" "} - - our docs - {" "} - to see how to configure Danswer properly. If you're a user, - please contact your admin to fix this error. -

-

- For additional support and guidance, you can reach out to our - community on{" "} - - Slack - - . -

-
-
- - - ); - } - if (productGating === GatingType.FULL) { - return ( - - - Access Restricted | Danswer - - -
-
- Danswer - -
- -

- Access Restricted -

-

- We regret to inform you that your access to Danswer has been - temporarily suspended due to a lapse in your subscription. -

-

- To reinstate your access and continue benefiting from - Danswer's powerful features, please update your payment - information. -

-

- If you're an admin, you can resolve this by visiting the - billing section. For other users, please reach out to your - administrator to address this matter. -

-
-
- - - ); - } - - return ( - - + const getPageContent = (content: React.ReactNode) => ( + + - - - {CUSTOM_ANALYTICS_ENABLED && combinedSettings.customAnalyticsScript && ( - -