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,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 (
<html lang="en" className={`${inter.variable} font-sans`}>
<Head>
<title>Settings Unavailable | Danswer</title>
</Head>
<body className="bg-background text-default">
<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>
<Logo height={40} width={40} />
</div>
<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.
</p>
<p className="mt-4">
If you&apos;re an admin, please check{" "}
<a
className="text-link"
href="https://docs.danswer.dev/introduction?utm_source=app&utm_medium=error_page&utm_campaign=config_error"
target="_blank"
rel="noopener noreferrer"
>
our docs
</a>{" "}
to see how to configure Danswer properly. If you&apos;re a user,
please contact your admin to fix this error.
</p>
<p className="mt-4">
For additional support and guidance, you can reach out to our
community on{" "}
<a
className="text-link"
href="https://danswer.ai?utm_source=app&utm_medium=error_page&utm_campaign=config_error"
target="_blank"
rel="noopener noreferrer"
>
Slack
</a>
.
</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">
<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>
<Logo height={40} width={40} />
</div>
<Card className="p-8 max-w-md">
<h1 className="text-2xl font-bold mb-4 text-error">
Access Restricted
</h1>
<p className="text-text-500 mb-4">
We regret to inform you that your access to Danswer has been
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.
</p>
<p className="text-text-500">
If you&apos;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.
</p>
</Card>
</div>
</body>
</html>
);
}
return (
<html lang="en">
<Head>
const getPageContent = (content: React.ReactNode) => (
<html lang="en" className={`${inter.variable} font-sans`}>
<head>
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, interactive-widget=resizes-content"
/>
</Head>
{CUSTOM_ANALYTICS_ENABLED && combinedSettings.customAnalyticsScript && (
<head>
<script
type="text/javascript"
dangerouslySetInnerHTML={{
__html: combinedSettings.customAnalyticsScript,
}}
/>
</head>
)}
{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 ${
// TODO: remove this once proper dark mode exists
process.env.THEME_IS_DARK?.toLowerCase() === "true" ? "dark" : ""
}`}
>
<AppProvider
settings={combinedSettings}
assistants={assistants}
hasAnyConnectors={hasAnyConnectors}
hasImageCompatibleModel={hasImageCompatibleModel}
>
{children}
</AppProvider>
<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>
<Logo height={40} width={40} />
</div>
<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.
</p>
<p className="mt-4">
If you&apos;re an admin, please check{" "}
<a
className="text-link"
href="https://docs.danswer.dev/introduction?utm_source=app&utm_medium=error_page&utm_campaign=config_error"
target="_blank"
rel="noopener noreferrer"
>
our docs
</a>{" "}
to see how to configure Danswer properly. If you&apos;re a user,
please contact your admin to fix this error.
</p>
<p className="mt-4">
For additional support and guidance, you can reach out to our
community on{" "}
<a
className="text-link"
href="https://danswer.ai?utm_source=app&utm_medium=error_page&utm_campaign=config_error"
target="_blank"
rel="noopener noreferrer"
>
Slack
</a>
.
</p>
</Card>
</div>
);
}
if (productGating === GatingType.FULL) {
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>
<Logo height={40} width={40} />
</div>
<Card className="p-8 max-w-md">
<h1 className="text-2xl font-bold mb-4 text-error">
Access Restricted
</h1>
<p className="text-text-500 mb-4">
We regret to inform you that your access to Danswer has been
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.
</p>
<p className="text-text-500">
If you&apos;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.
</p>
</Card>
</div>
);
}
const data = await fetchAssistantData();
const { assistants, hasAnyConnectors, hasImageCompatibleModel } = data;
return getPageContent(
<AppProvider
settings={combinedSettings}
assistants={assistants}
hasAnyConnectors={hasAnyConnectors}
hasImageCompatibleModel={hasImageCompatibleModel}
>
<PostHogPageView />
{children}
</AppProvider>
);
}

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