From b5825bade1a227ec255487f585704a73d423bbaa Mon Sep 17 00:00:00 2001
From: Tomas Francisco <4301103+tomasfrancisco@users.noreply.github.com>
Date: Thu, 29 Aug 2024 15:22:59 +0200
Subject: [PATCH] integrate posthog analytics
---
apps/dashboard/next.config.mjs | 17 +++++-
apps/dashboard/package.json | 4 +-
.../_actions/fetch-release-tokens.action.ts | 2 +-
.../auth/login/_actions/login-user.action.ts | 2 +-
apps/dashboard/src/app/layout.tsx | 19 ++++---
apps/dashboard/src/config.ts | 52 ++-----------------
apps/dashboard/src/env/client.ts | 18 +++++++
apps/dashboard/src/env/server.ts | 18 +++++++
apps/dashboard/src/lib/analytics/provider.tsx | 20 +++++++
apps/dashboard/src/lib/auth/middleware.ts | 6 +--
apps/dashboard/src/lib/figma/figma.ts | 7 +--
.../src/lib/middleware/figma/middleware.ts | 11 ++--
pnpm-lock.yaml | 30 +++++++++++
turbo.json | 6 ++-
14 files changed, 140 insertions(+), 72 deletions(-)
create mode 100644 apps/dashboard/src/env/client.ts
create mode 100644 apps/dashboard/src/env/server.ts
create mode 100644 apps/dashboard/src/lib/analytics/provider.tsx
diff --git a/apps/dashboard/next.config.mjs b/apps/dashboard/next.config.mjs
index dec0ce5..ee02835 100644
--- a/apps/dashboard/next.config.mjs
+++ b/apps/dashboard/next.config.mjs
@@ -2,7 +2,8 @@ import { fileURLToPath } from 'node:url';
import createJiti from 'jiti';
const jiti = createJiti(fileURLToPath(import.meta.url));
-jiti('./src/config');
+jiti('./src/env/client');
+jiti('./src/env/server');
/** @type {import('next').NextConfig} */
const nextConfig = {
@@ -16,6 +17,20 @@ const nextConfig = {
],
},
+ async rewrites() {
+ return [
+ {
+ source: '/_proxy/posthog/ingest/static/:path*',
+ destination: 'https://eu-assets.i.posthog.com/static/:path*',
+ },
+ {
+ source: '/_proxy/posthog/ingest/:path*',
+ destination: 'https://eu.i.posthog.com/:path*',
+ },
+ ];
+ },
+ skipTrailingSlashRedirect: true,
+
reactStrictMode: true,
/** Enables hot reloading for local packages without a build step */
diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json
index 77d2d3f..47efd32 100644
--- a/apps/dashboard/package.json
+++ b/apps/dashboard/package.json
@@ -23,6 +23,7 @@
"@octokit/core": "^6.1.2",
"@supabase/ssr": "^0.4.0",
"@supabase/supabase-js": "^2.45.0",
+ "@t3-oss/env-core": "^0.11.0",
"@t3-oss/env-nextjs": "^0.11.0",
"@tanstack/react-query": "^5.51.24",
"@trpc/react-query": "catalog:",
@@ -37,6 +38,7 @@
"memoize": "^10.0.0",
"next": "catalog:",
"postgres": "^3.4.4",
+ "posthog-js": "^1.160.0",
"rambda": "^9.2.1",
"react": "catalog:",
"react-diff-viewer": "^3.1.1",
@@ -51,9 +53,9 @@
"zod": "^3.23.8"
},
"devDependencies": {
- "@ds-project/services": "workspace:*",
"@ds-project/eslint": "workspace:*",
"@ds-project/prettier": "workspace:*",
+ "@ds-project/services": "workspace:*",
"@ds-project/typescript": "workspace:*",
"@next/env": "^14.2.5",
"@octokit/types": "^13.5.0",
diff --git a/apps/dashboard/src/app/app/tokens/_actions/fetch-release-tokens.action.ts b/apps/dashboard/src/app/app/tokens/_actions/fetch-release-tokens.action.ts
index 20b642c..1541b21 100644
--- a/apps/dashboard/src/app/app/tokens/_actions/fetch-release-tokens.action.ts
+++ b/apps/dashboard/src/app/app/tokens/_actions/fetch-release-tokens.action.ts
@@ -2,9 +2,9 @@
import type { DesignTokens } from 'style-dictionary/types';
import type { Octokit } from '@octokit/core';
-import { config } from '@/config';
import { api } from '@ds-project/api/rsc';
import { getInstallationOctokit } from '@ds-project/services/github';
+import { config } from '@/config';
async function searchFileSha({
octokit,
diff --git a/apps/dashboard/src/app/auth/login/_actions/login-user.action.ts b/apps/dashboard/src/app/auth/login/_actions/login-user.action.ts
index 0c2a774..b041885 100644
--- a/apps/dashboard/src/app/auth/login/_actions/login-user.action.ts
+++ b/apps/dashboard/src/app/auth/login/_actions/login-user.action.ts
@@ -1,9 +1,9 @@
'use server';
import { z } from 'zod';
-import { config } from '@/config';
import { createServerClient } from '@ds-project/auth/server';
import type { Database } from '@ds-project/database';
+import { config } from '@/config';
const schema = z.object({
email: z.string().email(),
diff --git a/apps/dashboard/src/app/layout.tsx b/apps/dashboard/src/app/layout.tsx
index feaded3..fa6cf85 100644
--- a/apps/dashboard/src/app/layout.tsx
+++ b/apps/dashboard/src/app/layout.tsx
@@ -2,6 +2,7 @@ import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import { cn } from '@/lib/css';
+import { AnalyticsProvider } from '@/lib/analytics/provider';
const inter = Inter({ subsets: ['latin'] });
@@ -17,14 +18,16 @@ export default function RootLayout({
}>) {
return (
-
- {children}
-
+
+
+ {children}
+
+
);
}
diff --git a/apps/dashboard/src/config.ts b/apps/dashboard/src/config.ts
index a6a43d9..7cea628 100644
--- a/apps/dashboard/src/config.ts
+++ b/apps/dashboard/src/config.ts
@@ -1,63 +1,19 @@
-import { createEnv } from '@t3-oss/env-nextjs';
-import { z } from 'zod';
-
-export const env = createEnv({
- server: {
- NODE_ENV: z.enum(['development', 'test', 'production']).optional(),
- GITHUB_APP_ID: z.string().min(1),
- GITHUB_APP_PRIVATE_KEY: z.string().min(1),
- GITHUB_APP_CLIENT_ID: z.string().min(1),
- GITHUB_APP_CLIENT_SECRET: z.string().min(1),
- FIGMA_APP_CLIENT_ID: z.string().min(1),
- FIGMA_APP_CLIENT_SECRET: z.string().min(1),
- POSTGRES_URL: z.string().min(1),
- },
- client: {
- NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
- NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
- NEXT_PUBLIC_VERCEL_ENV: z
- .enum(['development', 'preview', 'production'])
- .optional(),
- NEXT_PUBLIC_VERCEL_URL: z.string().min(1).optional(),
- },
- experimental__runtimeEnv: {
- NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
- NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
- NEXT_PUBLIC_VERCEL_ENV: process.env.NEXT_PUBLIC_VERCEL_ENV,
- NEXT_PUBLIC_VERCEL_URL: process.env.NEXT_PUBLIC_VERCEL_URL,
- },
-});
+import { clientEnv } from './env/client';
const pageUrl = (() => {
- switch (env.NEXT_PUBLIC_VERCEL_ENV) {
+ switch (clientEnv.VERCEL_ENV) {
case 'production':
return 'https://designsystemproject.pro';
case 'preview':
- return `https://${env.NEXT_PUBLIC_VERCEL_URL}`;
+ return `https://${clientEnv.VERCEL_URL}`;
default:
return 'http://localhost:3000';
}
})();
export const config = {
- environment: env.NODE_ENV,
pageUrl,
- supabaseUrl: env.NEXT_PUBLIC_SUPABASE_URL,
- supabaseAnonKey: env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
FIGMA_KEY: 'figma.key',
- github: {
- appId: env.GITHUB_APP_ID,
- appPrivateKey: Buffer.from(env.GITHUB_APP_PRIVATE_KEY, 'base64').toString(
- 'ascii'
- ),
- appClientId: env.GITHUB_APP_CLIENT_ID,
- appClientSecret: env.GITHUB_APP_CLIENT_SECRET,
- },
- databaseUrl: env.NEXT_PUBLIC_SUPABASE_URL,
- figma: {
- appClientId: env.FIGMA_APP_CLIENT_ID,
- appClientSecret: env.FIGMA_APP_CLIENT_SECRET,
- redirectUri: `${pageUrl}/integrations/providers/figma/callback`,
- },
+ figmaRedirectUri: `${pageUrl}/integrations/providers/figma/callback`,
gitTokensPath: 'packages/generator/tokens',
} as const;
diff --git a/apps/dashboard/src/env/client.ts b/apps/dashboard/src/env/client.ts
new file mode 100644
index 0000000..eb6e5c7
--- /dev/null
+++ b/apps/dashboard/src/env/client.ts
@@ -0,0 +1,18 @@
+/* eslint-disable no-restricted-properties */
+import { createEnv } from '@t3-oss/env-nextjs';
+import { z } from 'zod';
+import { vercel } from '@t3-oss/env-core/presets';
+
+export const clientEnv = createEnv({
+ extends: [vercel()],
+ client: {
+ NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
+ NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
+ NEXT_PUBLIC_POSTHOG_KEY: z.string().min(1),
+ },
+ runtimeEnv: {
+ NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
+ NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
+ NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY,
+ },
+});
diff --git a/apps/dashboard/src/env/server.ts b/apps/dashboard/src/env/server.ts
new file mode 100644
index 0000000..dfef3b9
--- /dev/null
+++ b/apps/dashboard/src/env/server.ts
@@ -0,0 +1,18 @@
+/* eslint-disable no-restricted-properties */
+import { createEnv } from '@t3-oss/env-nextjs';
+import { z } from 'zod';
+
+export const serverEnv = createEnv({
+ isServer: typeof window === 'undefined',
+ server: {
+ NODE_ENV: z.enum(['development', 'test', 'production']).optional(),
+ GITHUB_APP_ID: z.string().min(1),
+ GITHUB_APP_PRIVATE_KEY: z.string().min(1),
+ GITHUB_APP_CLIENT_ID: z.string().min(1),
+ GITHUB_APP_CLIENT_SECRET: z.string().min(1),
+ FIGMA_APP_CLIENT_ID: z.string().min(1),
+ FIGMA_APP_CLIENT_SECRET: z.string().min(1),
+ POSTGRES_URL: z.string().min(1),
+ },
+ experimental__runtimeEnv: process.env,
+});
diff --git a/apps/dashboard/src/lib/analytics/provider.tsx b/apps/dashboard/src/lib/analytics/provider.tsx
new file mode 100644
index 0000000..71f8282
--- /dev/null
+++ b/apps/dashboard/src/lib/analytics/provider.tsx
@@ -0,0 +1,20 @@
+'use client';
+
+import { clientEnv } from '@/env/client';
+import posthog from 'posthog-js';
+import { PostHogProvider } from 'posthog-js/react';
+
+if (typeof window !== 'undefined') {
+ posthog.init(clientEnv.NEXT_PUBLIC_POSTHOG_KEY, {
+ api_host: '/_proxy/posthog/ingest',
+ ui_host: 'https://eu.posthog.com',
+ person_profiles: 'identified_only', // or 'always' to create profiles for anonymous users as well
+ });
+}
+
+interface AnalyticsProviderProps {
+ children: React.ReactNode;
+}
+export function AnalyticsProvider({ children }: AnalyticsProviderProps) {
+ return {children};
+}
diff --git a/apps/dashboard/src/lib/auth/middleware.ts b/apps/dashboard/src/lib/auth/middleware.ts
index 3d26d07..b6f7d08 100644
--- a/apps/dashboard/src/lib/auth/middleware.ts
+++ b/apps/dashboard/src/lib/auth/middleware.ts
@@ -1,14 +1,14 @@
import { NextResponse } from 'next/server';
import type { MiddlewareFactory } from '../middleware';
-import { config } from '@/config';
import { createMiddlewareClient } from '@ds-project/auth/middleware';
+import { clientEnv } from '@/env/client';
export const authenticationMiddleware: MiddlewareFactory =
(middleware) =>
async (request, event, response = NextResponse.next({ request })) => {
const supabase = createMiddlewareClient(request, response, {
- supabaseAnonKey: config.supabaseAnonKey,
- supabaseUrl: config.supabaseUrl,
+ supabaseAnonKey: clientEnv.NEXT_PUBLIC_SUPABASE_ANON_KEY,
+ supabaseUrl: clientEnv.NEXT_PUBLIC_SUPABASE_URL,
});
const {
diff --git a/apps/dashboard/src/lib/figma/figma.ts b/apps/dashboard/src/lib/figma/figma.ts
index f9c4d34..2dee3a7 100644
--- a/apps/dashboard/src/lib/figma/figma.ts
+++ b/apps/dashboard/src/lib/figma/figma.ts
@@ -16,6 +16,7 @@ import {
import { database } from '@ds-project/database/client';
import { cache } from 'react';
import { api } from '@ds-project/api/rsc';
+import { serverEnv } from '@/env/server';
class Figma {
private apiUrl = 'https://api.figma.com';
@@ -47,7 +48,7 @@ class Figma {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
- body: `client_id=${encodeURIComponent(config.figma.appClientId)}&client_secret=${encodeURIComponent(config.figma.appClientSecret)}&redirect_uri=${encodeURIComponent(config.figma.redirectUri)}&code=${encodeURIComponent(code)}&grant_type=authorization_code`,
+ body: `client_id=${encodeURIComponent(serverEnv.FIGMA_APP_CLIENT_ID)}&client_secret=${encodeURIComponent(serverEnv.FIGMA_APP_CLIENT_SECRET)}&redirect_uri=${encodeURIComponent(config.figmaRedirectUri)}&code=${encodeURIComponent(code)}&grant_type=authorization_code`,
});
if (!result.ok) {
@@ -86,7 +87,7 @@ class Figma {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
- body: `client_id=${encodeURIComponent(config.figma.appClientId)}&client_secret=${encodeURIComponent(config.figma.appClientSecret)}&redirect_uri=${encodeURIComponent(config.figma.redirectUri)}&refresh_token=${encodeURIComponent(integration.data.refreshToken)}`,
+ body: `client_id=${encodeURIComponent(serverEnv.FIGMA_APP_CLIENT_ID)}&client_secret=${encodeURIComponent(serverEnv.FIGMA_APP_CLIENT_SECRET)}&redirect_uri=${encodeURIComponent(config.figmaRedirectUri)}&refresh_token=${encodeURIComponent(integration.data.refreshToken)}`,
});
if (!result.ok) {
@@ -241,7 +242,7 @@ class Figma {
}
)
) {
- return `${this.url}/oauth?client_id=${config.figma.appClientId}&redirect_uri=${config.figma.redirectUri}&scope=files:read,file_variables:read,file_variables:write&state=${state}&response_type=code`;
+ return `${this.url}/oauth?client_id=${serverEnv.FIGMA_APP_CLIENT_ID}&redirect_uri=${config.figmaRedirectUri}&scope=files:read,file_variables:read,file_variables:write&state=${state}&response_type=code`;
}
return null;
diff --git a/apps/dashboard/src/lib/middleware/figma/middleware.ts b/apps/dashboard/src/lib/middleware/figma/middleware.ts
index 1a8f457..e71b9e9 100644
--- a/apps/dashboard/src/lib/middleware/figma/middleware.ts
+++ b/apps/dashboard/src/lib/middleware/figma/middleware.ts
@@ -2,10 +2,11 @@ import 'server-only';
import { kv } from '@vercel/kv';
import { NextResponse } from 'next/server';
-import { config } from '@/config';
import type { KVCredentials, KVCredentialsRead } from '@/types/kv-types';
import type { MiddlewareFactory } from '../compose';
import { createMiddlewareClient } from '@ds-project/auth/middleware';
+import { clientEnv } from '@/env/client';
+import { config } from '@/config';
export const figmaMiddleware: MiddlewareFactory =
(middleware) =>
@@ -26,8 +27,8 @@ export const figmaMiddleware: MiddlewareFactory =
);
const supabase = createMiddlewareClient(request, response, {
- supabaseAnonKey: config.supabaseAnonKey,
- supabaseUrl: config.supabaseUrl,
+ supabaseAnonKey: clientEnv.NEXT_PUBLIC_SUPABASE_ANON_KEY,
+ supabaseUrl: clientEnv.NEXT_PUBLIC_SUPABASE_URL,
});
const {
data: { session },
@@ -83,8 +84,8 @@ export const figmaMiddleware: MiddlewareFactory =
console.log('🔐 Figma: Figma key detected. Finishing authentication...');
const supabase = createMiddlewareClient(request, response, {
- supabaseAnonKey: config.supabaseAnonKey,
- supabaseUrl: config.supabaseUrl,
+ supabaseAnonKey: clientEnv.NEXT_PUBLIC_SUPABASE_ANON_KEY,
+ supabaseUrl: clientEnv.NEXT_PUBLIC_SUPABASE_URL,
});
const {
data: { session },
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 53a3557..b6a7962 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -108,6 +108,9 @@ importers:
'@supabase/supabase-js':
specifier: ^2.45.0
version: 2.45.0
+ '@t3-oss/env-core':
+ specifier: ^0.11.0
+ version: 0.11.0(typescript@5.5.4)(zod@3.23.8)
'@t3-oss/env-nextjs':
specifier: ^0.11.0
version: 0.11.0(typescript@5.5.4)(zod@3.23.8)
@@ -150,6 +153,9 @@ importers:
postgres:
specifier: ^3.4.4
version: 3.4.4
+ posthog-js:
+ specifier: ^1.160.0
+ version: 1.160.0
rambda:
specifier: ^9.2.1
version: 9.2.1
@@ -4844,6 +4850,9 @@ packages:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
+ fflate@0.4.8:
+ resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
+
file-entry-cache@6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -6106,6 +6115,12 @@ packages:
resolution: {integrity: sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==}
engines: {node: '>=12'}
+ posthog-js@1.160.0:
+ resolution: {integrity: sha512-K/RRgmPYIpP69nnveCJfkclb8VU+R+jsgqlrKaLGsM5CtQM9g01WOzAiT3u36WLswi58JiFMXgJtECKQuoqTgQ==}
+
+ preact@10.23.2:
+ resolution: {integrity: sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==}
+
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -7208,6 +7223,9 @@ packages:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
+ web-vitals@4.2.3:
+ resolution: {integrity: sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==}
+
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -12011,6 +12029,8 @@ snapshots:
node-domexception: 1.0.0
web-streams-polyfill: 3.3.3
+ fflate@0.4.8: {}
+
file-entry-cache@6.0.1:
dependencies:
flat-cache: 3.2.0
@@ -13239,6 +13259,14 @@ snapshots:
postgres@3.4.4: {}
+ posthog-js@1.160.0:
+ dependencies:
+ fflate: 0.4.8
+ preact: 10.23.2
+ web-vitals: 4.2.3
+
+ preact@10.23.2: {}
+
prelude-ls@1.2.1: {}
prettier-plugin-packagejson@2.5.0(prettier@3.3.3):
@@ -14437,6 +14465,8 @@ snapshots:
web-streams-polyfill@3.3.3: {}
+ web-vitals@4.2.3: {}
+
webidl-conversions@3.0.1: {}
webpack-sources@3.2.3: {}
diff --git a/turbo.json b/turbo.json
index 965487b..1b8b2a9 100644
--- a/turbo.json
+++ b/turbo.json
@@ -8,7 +8,11 @@
"GITHUB_APP_CLIENT_SECRET",
"FIGMA_APP_CLIENT_ID",
"FIGMA_APP_CLIENT_SECRET",
- "POSTGRES_URL"
+ "POSTGRES_URL",
+ "NEXT_PUBLIC_SUPABASE_ANON_KEY",
+ "NEXT_PUBLIC_SUPABASE_URL",
+ "NEXT_PUBLIC_POSTHOG_KEY",
+ "NEXT_PUBLIC_POSTHOG_HOST"
],
"tasks": {
"topo": {