diff --git a/backend/app/api/endpoints/search_endpoint.py b/backend/app/api/endpoints/search_endpoint.py index 734a5093..56d13183 100644 --- a/backend/app/api/endpoints/search_endpoint.py +++ b/backend/app/api/endpoints/search_endpoint.py @@ -29,7 +29,7 @@ summary="List all Search Results", description="List all Search Results", dependencies=[Depends(security.access_token_required)], - response_model=str, + response_model=dict[str, str] ) @version(1, 0) async def get_search_results( @@ -54,7 +54,7 @@ async def get_search_results( logger.info(f"get_search_results. result: {search_result}") - return JSONResponse(status_code=200, content=search_result) + return JSONResponse(status_code=200, content={"result": search_result}) @router.get( diff --git a/backend/app/config.py b/backend/app/config.py index 759e22b8..8d7013bd 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -127,6 +127,7 @@ # Sentry Configuration SENTRY_DSN: Secret = config("SENTRY_DSN", cast=Secret) +SENTRY_ENABLE_TRACING: bool = config("SENTRY_ENABLE_TRACING", cast=bool, default=False) # GROQ API Configuration diff --git a/backend/app/services/tracing.py b/backend/app/services/tracing.py index 675eb5dd..4736beae 100644 --- a/backend/app/services/tracing.py +++ b/backend/app/services/tracing.py @@ -5,12 +5,12 @@ from sentry_sdk.integrations.starlette import StarletteIntegration from sentry_sdk.integrations.fastapi import FastApiIntegration -from app.config import SENTRY_DSN +from app.config import SENTRY_DSN, SENTRY_ENABLE_TRACING def setup_tracing(): sentry_sdk.init( dsn=str(SENTRY_DSN), - enable_tracing=True, + enable_tracing=SENTRY_ENABLE_TRACING, integrations=[ AsyncioIntegration(), StarletteIntegration( diff --git a/frontend/README.md b/frontend/README.md index fc96b081..6f514803 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -10,6 +10,14 @@ Install `yarn` ## Getting Started +Create a `.env.local` file in the root of the project and add the following environment variables: + +```bash +NEXT_PUBLIC_POSTHOG_KEY= +NEXT_PUBLIC_POSTHOG_API_HOST= +NEXT_PUBLIC_POSTHOG_UI_HOST= +``` + Run the development server: ```bash diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index b11bca44..71101f37 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -8,6 +8,14 @@ const nextConfig = { source: '/api/:path*', destination: 'http://127.0.0.1:8000/:path*', }, + { + source: "/ingest/static/:path*", + destination: "https://us-assets.i.posthog.com/static/:path*", + }, + { + source: "/ingest/:path*", + destination: "https://us.i.posthog.com/:path*", + }, ] }; } diff --git a/frontend/package.json b/frontend/package.json index 84552552..3fe5bc23 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,7 @@ "nanoid": "^5.0.6", "next": "14.1.3", "next-axiom": "^1.1.1", + "posthog-js": "^1.116.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", diff --git a/frontend/src/app/PostHogPageView.tsx b/frontend/src/app/PostHogPageView.tsx new file mode 100644 index 00000000..7c76c038 --- /dev/null +++ b/frontend/src/app/PostHogPageView.tsx @@ -0,0 +1,28 @@ +'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 (pathname && posthog) { + let url = window.origin + pathname + if (searchParams.toString()) { + url = url + `?${searchParams.toString()}` + } + posthog.capture( + '$pageview', + { + '$current_url': url, + } + ) + } + }, [pathname, searchParams, posthog]) + + return null +} \ No newline at end of file diff --git a/frontend/src/app/_components/providers.tsx b/frontend/src/app/_components/providers.tsx index 14a53cca..6065f7c8 100644 --- a/frontend/src/app/_components/providers.tsx +++ b/frontend/src/app/_components/providers.tsx @@ -3,6 +3,27 @@ import { Analytics } from "@vercel/analytics/react" import { AxiomWebVitals } from "next-axiom" import { Toaster } from "react-hot-toast" +import posthog from "posthog-js" +import { PostHogProvider } from 'posthog-js/react' + +const posthog_enabled = process.env.NEXT_PUBLIC_POSTHOG_KEY != null + +if (typeof window !== 'undefined' && posthog_enabled) { + posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY || '', { + api_host: process.env.NEXT_PUBLIC_POSTHOG_API_HOST || '', + ui_host: process.env.NEXT_PUBLIC_POSTHOG_UI_HOST || '', + capture_pageview: false, + }) +} + +export function PosthogProvider({ + children, +}: { + children: React.ReactNode +}) { + return {children} +} + export function Providers() { return ( diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 339e7493..7ba4abe2 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -2,9 +2,10 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import Link from "next/link" import { cn } from "@/lib/utils" -import { Providers } from "./_components/providers" +import { Providers, PosthogProvider } from "./_components/providers" import Image from 'next/image'; import "./globals.css"; +import dynamic from 'next/dynamic' const BODY_PADDING = "px-4 sm:px-6" @@ -15,6 +16,10 @@ export const metadata: Metadata = { description: "Next generation healthcare search engine", }; +const PostHogPageView = dynamic(() => import('./PostHogPageView'), { + ssr: false, +}) + export default function RootLayout({ children, }: Readonly<{ @@ -22,32 +27,35 @@ export default function RootLayout({ }>) { return ( - -
- - + +
- Picture of the author - Curieo - -
-
- {children} -
- - + + + Picture of the author + Curieo + +
+
+ + {children} +
+ + + ) } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 1cdcf135..f00e8843 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1218,6 +1218,11 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fflate@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" + integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -2192,6 +2197,19 @@ postcss@^8, postcss@^8.4.23: picocolors "^1.0.0" source-map-js "^1.0.2" +posthog-js@^1.116.6: + version "1.116.6" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.116.6.tgz#9a5c9f49230a76642f4c44d93b96710f886c2880" + integrity sha512-rvt8HxzJD4c2B/xsUa4jle8ApdqljeBI2Qqjp4XJMohQf18DXRyM6b96H5/UMs8jxYuZG14Er0h/kEIWeU6Fmw== + dependencies: + fflate "^0.4.8" + preact "^10.19.3" + +preact@^10.19.3: + version "10.20.1" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.20.1.tgz#1bc598ab630d8612978f7533da45809a8298542b" + integrity sha512-JIFjgFg9B2qnOoGiYMVBtrcFxHqn+dNXbq76bVmcaHYJFYR4lW67AOcXgAYQQTDYXDOg/kTZrKPNCdRgJ2UJmw== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -2472,7 +2490,16 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2539,7 +2566,14 @@ string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==