Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: posthog #70

Merged
merged 6 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ bun.lockb
drizzle/
.prettierignore
.gitignore
*.env.example

.changeset/

Expand Down
8 changes: 8 additions & 0 deletions apps/docs/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ To obtain this key, head over to your unkey dashboard and create a new ratelimit

As for the namespace that you have created, you will need to put that as the value of the `UNKEY_NAMESPACE` environment variable.

#### Posthog (optional)

[Posthog](https://posthog.com) is the service of choice in OrbitKit for analysis, it is however optional as that if the `NEXT_PUBLIC_POSTHOG_HOST` or `NEXT_PUBLIC_POSTHOG_KEY` variables are not supplied for the web app or `PUBLIC_POSTHOG_KEY` and `PUBLIC_POSTHOG_HOST` for the marketing app, no analysis is applied.

To obtain this key, head over to your posthog dashboard, then in your settings, find `Project API Key`, that will be the value of the key environment variable.

As for the host use `app.posthog.com` or `eu.posthog.com` depending on your region.

#### OAuth

OrbitKit currently ships with two authentication providers, Google and Github. You will need to create an OAuth application on the respective platforms and input the values into the `.env.local` file.
Expand Down
2 changes: 2 additions & 0 deletions apps/marketing/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PUBLIC_POSTHOG_KEY=<ph_project_api_key>
PUBLIC_POSTHOG_HOST=<ph_client_api_host>
1 change: 1 addition & 0 deletions apps/marketing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@t3-oss/env-core": "^0.10.1",
"@total-typescript/ts-reset": "^0.5.1",
"astro": "^4.10.1",
"posthog-js": "^1.139.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"zod": "^3.23.8"
Expand Down
1 change: 1 addition & 0 deletions apps/marketing/src/components/posthog/PostHog.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script src="./posthog.ts"></script>
8 changes: 8 additions & 0 deletions apps/marketing/src/components/posthog/posthog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { env } from '@orbitkit/env/marketing';
import posthog from 'posthog-js';

if (env.PUBLIC_POSTHOG_KEY && env.PUBLIC_POSTHOG_HOST) {
posthog.init(env.PUBLIC_POSTHOG_KEY, {
api_host: env.PUBLIC_POSTHOG_HOST,
});
}
2 changes: 2 additions & 0 deletions apps/marketing/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
---
import { MyAvatar } from '@/components/avatar';
import BaseHead from '@/components/BaseHead.astro';
import PostHog from '@/components/posthog/PostHog.astro';
---

<!doctype html>
<html lang="en">
<head>
<BaseHead />
<PostHog />
</head>
<body>
<main class="container mx-auto py-6 px-6">
Expand Down
3 changes: 2 additions & 1 deletion apps/marketing/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"dependsOn": ["^build", "build"]
},
"build": {
"outputs": [".astro/**", "dist/**"]
"outputs": [".astro/**", "dist/**"],
"passThroughEnv": ["PUBLIC_POSTHOG_KEY", "PUBLIC_POSTHOG_HOST"]
}
}
}
4 changes: 4 additions & 0 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ DATABASE_URL=<postgres-db-url-from-neon-dashboard>
UPLOADTHING_SECRET=<uploadthing-secret>
UPLOADTHING_APP_ID=<uploadthing-app-id>

# Posthog (optional)
NEXT_PUBLIC_POSTHOG_HOST=<ph_client_api_host>
NEXT_PUBLIC_POSTHOG_KEY=<ph_project_api_key>

# Unkey (optional)
UNKEY_NAMESPACE=<unkey.dev-namespace>
UNKEY_ROOT_KEY=<unkey.dev-root-key>
Expand Down
2 changes: 2 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"geist": "^1.3.0",
"next": "14.2.3",
"next-themes": "^0.3.0",
"posthog-js": "^1.139.0",
"posthog-node": "^4.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"server-only": "^0.0.1",
Expand Down
28 changes: 21 additions & 7 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import './globals.css';
import { ThemeProvider } from 'next-themes';

import type { Metadata } from 'next';
import dynamic from 'next/dynamic';

import { NextSSRPlugin } from '@uploadthing/react/next-ssr-plugin';
import { GeistMono } from 'geist/font/mono';
Expand All @@ -15,6 +16,12 @@ import { TRPCReactProvider } from '@/lib/trpc/react';

import { fileRouter } from './api/uploadthing/core';

import { PostHogReactProvider } from '@/lib/posthog/react';

const PostHogPageView = dynamic(() => import('@/lib/posthog/view'), {
ssr: false,
});

export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
Expand All @@ -33,13 +40,20 @@ export default function RootLayout({
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body className={`${GeistSans.variable} ${GeistMono.variable}`}>
<NextSSRPlugin routerConfig={extractRouterConfig(fileRouter)} />
<ThemeProvider attribute="class" enableSystem disableTransitionOnChange>
<TRPCReactProvider>{children}</TRPCReactProvider>
<Toaster />
</ThemeProvider>
</body>
<PostHogReactProvider>
<body className={`${GeistSans.variable} ${GeistMono.variable}`}>
<PostHogPageView />
<NextSSRPlugin routerConfig={extractRouterConfig(fileRouter)} />
<ThemeProvider
attribute="class"
enableSystem
disableTransitionOnChange
>
<TRPCReactProvider>{children}</TRPCReactProvider>
<Toaster />
</ThemeProvider>
</body>
</PostHogReactProvider>
</html>
);
}
18 changes: 18 additions & 0 deletions apps/web/src/lib/posthog/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { env } from '@orbitkit/env/web';
import { PostHog } from 'posthog-node';

/**
* This component is used to initialize posthog client.
* @returns posthog client.
*/
export default function PostHogClient() {
if (!env.NEXT_PUBLIC_POSTHOG_KEY || !env.NEXT_PUBLIC_POSTHOG_HOST) {
return;
}

return new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, {
host: env.NEXT_PUBLIC_POSTHOG_HOST,
flushAt: 1, // Sets how many capture calls we should flush the queue (in one batch).
flushInterval: 0, // Sets how many milliseconds we should wait before flushing the queue
});
}
29 changes: 29 additions & 0 deletions apps/web/src/lib/posthog/react.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client';
import { env } from '@orbitkit/env/web';
import posthog from 'posthog-js';
import { PostHogProvider } from 'posthog-js/react';
ahmedhesham6 marked this conversation as resolved.
Show resolved Hide resolved

if (
typeof window !== 'undefined' &&
env.NEXT_PUBLIC_POSTHOG_HOST &&
env.NEXT_PUBLIC_POSTHOG_KEY
) {
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: env.NEXT_PUBLIC_POSTHOG_HOST,
capture_pageview: false, // Disable automatic pageview capture, as we capture manually
});
}

/**
* This component is used to initialize the posthog on the client side.
* @param props The props to the component.
* @param props.children The children to render.
* @returns posthog provider wrapper around the children.
*/
export function PostHogReactProvider({
children,
}: {
children: React.ReactNode;
}) {
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}
29 changes: 29 additions & 0 deletions apps/web/src/lib/posthog/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client';

import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect } from 'react';
import { type PostHog, usePostHog } from 'posthog-js/react';

/**
* This component is used to capture page views in PostHog.
* @returns null
*/
export default function PostHogPageView(): null {
const pathname = usePathname();
const searchParams = useSearchParams();
const posthog = usePostHog() as PostHog | undefined;

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;
}
8 changes: 6 additions & 2 deletions apps/web/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"AUTH_GITHUB_SECRET",
"AUTH_GOOGLE_ID",
"AUTH_GOOGLE_SECRET",
"AUTH_GOOGLE_CODE_VERIFIER"
"AUTH_GOOGLE_CODE_VERIFIER",
"NEXT_PUBLIC_POSTHOG_HOST",
"NEXT_PUBLIC_POSTHOG_KEY"
]
},
"test:e2e": {
Expand All @@ -30,7 +32,9 @@
"AUTH_GITHUB_SECRET",
"AUTH_GOOGLE_ID",
"AUTH_GOOGLE_SECRET",
"AUTH_GOOGLE_CODE_VERIFIER"
"AUTH_GOOGLE_CODE_VERIFIER",
"NEXT_PUBLIC_POSTHOG_HOST",
"NEXT_PUBLIC_POSTHOG_KEY"
]
}
}
Expand Down
3 changes: 3 additions & 0 deletions cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ignorePaths:
- bun.lockb
- ./branch_out
- .tsbuildinfo
- '*.tsbuildinfo'
- .gitignore
- dist
- storybook-static
Expand Down Expand Up @@ -61,7 +62,9 @@ words:
- Ornella
- packagejson
- pacocoursey
- pageview
- peduarte
- posthog
- quickstart
- Ratelimit
- shadcn
Expand Down
6 changes: 5 additions & 1 deletion packages/env/src/marketing/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { createEnv } from '@t3-oss/env-core';
import { z } from 'zod';
import { sharedEnv } from '../shared';

export const env = createEnv({
extends: [sharedEnv],
server: {},
client: {},
client: {
PUBLIC_POSTHOG_KEY: z.string().optional(),
PUBLIC_POSTHOG_HOST: z.string().optional(),
},
clientPrefix: 'PUBLIC_',
runtimeEnv: import.meta.env,
skipValidation: !!import.meta.env.SKIP_ENV_VALIDATION,
Expand Down
9 changes: 8 additions & 1 deletion packages/env/src/web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@ export const env = createEnv({
AUTH_GOOGLE_SECRET: z.string().optional(),
AUTH_GOOGLE_CODE_VERIFIER: z.string().optional(),
},
client: {},
client: {
NEXT_PUBLIC_POSTHOG_KEY: z.string().optional(),
NEXT_PUBLIC_POSTHOG_HOST: z.string().optional(),
},
experimental__runtimeEnv: {
NODE_ENV: process.env.NODE_ENV,
// eslint-disable-next-line turbo/no-undeclared-env-vars
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can disable this rule entirely for this package in the eslint.config.js file of packages/env because it won't ever work as that the turbo plugin wouldn't be able to see the turbo.json files of the apps, where these env vars are passed through.

Add this to packages/env/eslint.config.js:

import { configs, defineConfig } from '@orbitkit/eslint';

export default defineConfig(...configs.base, {
  rules: {
    // We disable this rule in this package because turbo can't find the turbo.json files of the apps to know that we are actually passing through env vars there.
    'turbo/no-undeclared-env-vars': 'off',
  },
});

Then you can remove the eslint-disable-next-line comments from this file.

NEXT_PUBLIC_POSTHOG_KEY: process.env['NEXT_PUBLIC_POSTHOG_KEY'],
// eslint-disable-next-line turbo/no-undeclared-env-vars
NEXT_PUBLIC_POSTHOG_HOST: process.env['NEXT_PUBLIC_POSTHOG_HOST'],
},
emptyStringAsUndefined: true,
skipValidation: !!process.env['SKIP_ENV_VALIDATION'],
Expand Down
Loading