add posthog + layout rework (#2926)

* add posthog + layout rework

* remove posthog node

* nit
This commit is contained in:
pablodanswer 2024-10-26 11:15:01 -07:00 committed by GitHub
parent 5e01d6befb
commit 9def9f0dba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 218 additions and 122 deletions

View File

@ -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

41
web/package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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;
}

View File

@ -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,20 +59,40 @@ 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 (
const getPageContent = (content: React.ReactNode) => (
<html lang="en" className={`${inter.variable} font-sans`}>
<Head>
<title>Settings Unavailable | Danswer</title>
</Head>
<body className="bg-background text-default">
<head>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, interactive-widget=resizes-content"
/>
{CUSTOM_ANALYTICS_ENABLED &&
combinedSettings?.customAnalyticsScript && (
<script
type="text/javascript"
dangerouslySetInnerHTML={{
__html: combinedSettings.customAnalyticsScript,
}}
/>
)}
</head>
<body className={`relative ${inter.variable} font-sans`}>
<div
className={`text-default min-h-screen bg-background ${
process.env.THEME_IS_DARK?.toLowerCase() === "true" ? "dark" : ""
}`}
>
<PHProvider>{content}</PHProvider>
</div>
</body>
</html>
);
if (!combinedSettings) {
return getPageContent(
<div className="flex flex-col items-center justify-center min-h-screen">
<div className="mb-2 flex items-center max-w-[175px]">
<HeaderTitle>Danswer</HeaderTitle>
@ -80,9 +102,9 @@ export default async function RootLayout({
<Card className="p-8 max-w-md">
<h1 className="text-2xl font-bold mb-4 text-error">Error</h1>
<p className="text-text-500">
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.
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.
</p>
<p className="mt-4">
If you&apos;re an admin, please check{" "}
@ -112,17 +134,10 @@ export default async function RootLayout({
</p>
</Card>
</div>
</body>
</html>
);
}
if (productGating === GatingType.FULL) {
return (
<html lang="en" className={`${inter.variable} font-sans`}>
<Head>
<title>Access Restricted | Danswer</title>
</Head>
<body className="bg-background text-default">
return getPageContent(
<div className="flex flex-col items-center justify-center min-h-screen">
<div className="mb-2 flex items-center max-w-[175px]">
<HeaderTitle>Danswer</HeaderTitle>
@ -137,9 +152,8 @@ export default async function RootLayout({
temporarily suspended due to a lapse in your subscription.
</p>
<p className="text-text-500 mb-4">
To reinstate your access and continue benefiting from
Danswer&apos;s powerful features, please update your payment
information.
To reinstate your access and continue benefiting from Danswer&apos;s
powerful features, please update your payment information.
</p>
<p className="text-text-500">
If you&apos;re an admin, you can resolve this by visiting the
@ -148,48 +162,21 @@ export default async function RootLayout({
</p>
</Card>
</div>
</body>
</html>
);
}
return (
<html lang="en">
<Head>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, interactive-widget=resizes-content"
/>
</Head>
const data = await fetchAssistantData();
const { assistants, hasAnyConnectors, hasImageCompatibleModel } = data;
{CUSTOM_ANALYTICS_ENABLED && combinedSettings.customAnalyticsScript && (
<head>
<script
type="text/javascript"
dangerouslySetInnerHTML={{
__html: combinedSettings.customAnalyticsScript,
}}
/>
</head>
)}
<body className={`relative ${inter.variable} font-sans`}>
<div
className={`text-default min-h-screen bg-background ${
// TODO: remove this once proper dark mode exists
process.env.THEME_IS_DARK?.toLowerCase() === "true" ? "dark" : ""
}`}
>
return getPageContent(
<AppProvider
settings={combinedSettings}
assistants={assistants}
hasAnyConnectors={hasAnyConnectors}
hasImageCompatibleModel={hasImageCompatibleModel}
>
<PostHogPageView />
{children}
</AppProvider>
</div>
</body>
</html>
);
}

26
web/src/app/providers.tsx Normal file
View File

@ -0,0 +1,26 @@
"use client";
import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
import { useEffect } from "react";
const isPostHogEnabled = !!(
process.env.NEXT_PUBLIC_POSTHOG_KEY && process.env.NEXT_PUBLIC_POSTHOG_HOST
);
export function PHProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
if (isPostHogEnabled) {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST!,
person_profiles: "identified_only",
capture_pageview: false,
});
}
}, []);
if (!isPostHogEnabled) {
return <>{children}</>;
}
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}