diff --git a/.env.example b/.env.example index 6a81f72e2..d34b6e62c 100644 --- a/.env.example +++ b/.env.example @@ -116,3 +116,6 @@ E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_Password123" # [[REDIS]] NEXT_PRIVATE_REDIS_URL= NEXT_PRIVATE_REDIS_TOKEN= + +# [[Telemetry]] +DISABLE_TELEMETRY=true diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 0f3d1607f..3de3de4ae 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -4,12 +4,14 @@ import { Caveat, Inter } from 'next/font/google'; import { AxiomWebVitals } from 'next-axiom'; import { PublicEnvScript } from 'next-runtime-env'; +import { version } from 'package.json'; import { FeatureFlagProvider } from '@documenso/lib/client-only/providers/feature-flag'; import { LocaleProvider } from '@documenso/lib/client-only/providers/locale'; import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { getServerComponentAllFlags } from '@documenso/lib/server-only/feature-flags/get-server-component-feature-flag'; import { getLocale } from '@documenso/lib/server-only/headers/get-locale'; +import { sendInstanceInfo } from '@documenso/lib/server-only/telemetry/send-instance-info'; import { TrpcProvider } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; import { Toaster } from '@documenso/ui/primitives/toaster'; @@ -58,6 +60,13 @@ export default async function RootLayout({ children }: { children: React.ReactNo const locale = getLocale(); + void sendInstanceInfo({ + // TODO: Get actual uniqueId for each user + uniqueId: 1, + timestamp: new Date(), + version, + }); + return ( => { + const isProduction = process.env.NODE_ENV === 'production'; + const isTelemetryDisabled = process.env.DISABLE_TELEMETRY === 'true'; + + if (!isProduction || isTelemetryDisabled) { + return; + } + + const url = 'https://documenso-instances.fly.dev/ping'; + + try { + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify({ + uniqueId, + timestamp, + version, + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`Failed to record instance, failed with status code ${response.status}`); + } + + await response.json(); + } catch (error) { + console.error('Error posting instance information:', error); + } +}; diff --git a/packages/tsconfig/process-env.d.ts b/packages/tsconfig/process-env.d.ts index 13e3973db..80498f415 100644 --- a/packages/tsconfig/process-env.d.ts +++ b/packages/tsconfig/process-env.d.ts @@ -79,5 +79,7 @@ declare namespace NodeJS { DATABASE_URL?: string; POSTGRES_PRISMA_URL?: string; POSTGRES_URL_NON_POOLING?: string; + + DISABLE_TELEMETRY?: string; } } diff --git a/turbo.json b/turbo.json index 8f89e97a6..783e3ece9 100644 --- a/turbo.json +++ b/turbo.json @@ -2,13 +2,8 @@ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { - "dependsOn": [ - "^build" - ], - "outputs": [ - ".next/**", - "!.next/cache/**" - ] + "dependsOn": ["^build"], + "outputs": [".next/**", "!.next/cache/**"] }, "lint": { "cache": false @@ -24,9 +19,7 @@ "persistent": true }, "start": { - "dependsOn": [ - "^build" - ], + "dependsOn": ["^build"], "cache": false, "persistent": true }, @@ -34,15 +27,11 @@ "cache": false }, "test:e2e": { - "dependsOn": [ - "^build" - ], + "dependsOn": ["^build"], "cache": false } }, - "globalDependencies": [ - "**/.env.*local" - ], + "globalDependencies": ["**/.env.*local"], "globalEnv": [ "APP_VERSION", "NEXT_PRIVATE_ENCRYPTION_KEY", @@ -121,6 +110,7 @@ "GOOGLE_APPLICATION_CREDENTIALS", "E2E_TEST_AUTHENTICATE_USERNAME", "E2E_TEST_AUTHENTICATE_USER_EMAIL", - "E2E_TEST_AUTHENTICATE_USER_PASSWORD" + "E2E_TEST_AUTHENTICATE_USER_PASSWORD", + "DISABLE_TELEMETRY" ] -} \ No newline at end of file +}