Skip to content

Commit d95ab53

Browse files
feat: use convex for analytics (#709)
* feat: use convex for analytics * fix * add range * fix * fx
1 parent bc9b450 commit d95ab53

File tree

23 files changed

+1329
-2129
lines changed

23 files changed

+1329
-2129
lines changed

.github/workflows/release.yaml

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,13 @@ jobs:
4949
if: steps.version.outputs.version != ''
5050
run: bun install --frozen-lockfile
5151
env:
52-
BTS_TELEMETRY: 1
53-
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
54-
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
52+
BTS_TELEMETRY: 0
5553

5654
- name: Build CLI
5755
if: steps.version.outputs.version != ''
5856
run: cd apps/cli && bun run build
5957
env:
60-
BTS_TELEMETRY: 1
61-
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
62-
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
58+
BTS_TELEMETRY: 0
6359

6460
- name: Create GitHub Release
6561
if: steps.version.outputs.version != ''
@@ -85,15 +81,11 @@ jobs:
8581
run: cd apps/cli && bun publish --access public
8682
env:
8783
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
88-
BTS_TELEMETRY: 1
89-
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
90-
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
84+
BTS_TELEMETRY: 0
9185

9286
- name: Publish create-bts alias to NPM
9387
if: steps.version.outputs.version != ''
9488
run: cd packages/create-bts && bun publish --access public
9589
env:
9690
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
97-
BTS_TELEMETRY: 1
98-
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
99-
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
91+
BTS_TELEMETRY: 0

apps/cli/src/utils/analytics.ts

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ import type { ProjectConfig } from "../types";
22
import { getLatestCLIVersion } from "./get-latest-cli-version";
33
import { isTelemetryEnabled } from "./telemetry";
44

5-
const POSTHOG_API_KEY = process.env.POSTHOG_API_KEY || "";
6-
const POSTHOG_HOST = process.env.POSTHOG_HOST;
5+
const CONVEX_INGEST_URL =
6+
"https://striped-seahorse-863.convex.site/api/analytics/ingest";
77

8-
function generateSessionId() {
9-
const rand = Math.random().toString(36).slice(2);
10-
const now = Date.now().toString(36);
11-
return `cli_${now}${rand}`;
8+
async function sendConvexEvent(payload: Record<string, unknown>) {
9+
if (!CONVEX_INGEST_URL) return;
10+
11+
try {
12+
await fetch(CONVEX_INGEST_URL, {
13+
method: "POST",
14+
headers: {
15+
"Content-Type": "application/json",
16+
},
17+
body: JSON.stringify(payload),
18+
});
19+
} catch (_error) {}
1220
}
1321

1422
export async function trackProjectCreation(
@@ -17,28 +25,16 @@ export async function trackProjectCreation(
1725
) {
1826
if (!isTelemetryEnabled() || disableAnalytics) return;
1927

20-
const sessionId = generateSessionId();
2128
// biome-ignore lint/correctness/noUnusedVariables: `projectName`, `projectDir`, and `relativePath` are not used in the event properties
2229
const { projectName, projectDir, relativePath, ...safeConfig } = config;
2330

24-
const payload = {
25-
api_key: POSTHOG_API_KEY,
26-
event: "project_created",
27-
properties: {
31+
try {
32+
await sendConvexEvent({
33+
event: "project_created",
2834
...safeConfig,
2935
cli_version: getLatestCLIVersion(),
3036
node_version: typeof process !== "undefined" ? process.version : "",
3137
platform: typeof process !== "undefined" ? process.platform : "",
32-
$ip: null,
33-
},
34-
distinct_id: sessionId,
35-
};
36-
37-
try {
38-
await fetch(`${POSTHOG_HOST}/capture`, {
39-
method: "POST",
40-
headers: { "Content-Type": "application/json" },
41-
body: JSON.stringify(payload),
4238
});
4339
} catch (_error) {}
4440
}

apps/web/src/app/(home)/_components/stats-section.tsx

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,47 @@ import {
1414
} from "lucide-react";
1515
import Link from "next/link";
1616

17-
export default function StatsSection({
18-
analyticsData,
19-
}: {
20-
analyticsData: {
21-
totalProjects: number;
22-
avgProjectsPerDay: string;
23-
lastUpdated: string | null;
24-
};
25-
}) {
26-
// no idea why there are no types
27-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
28-
const githubRepo = useQuery((api as any).stats.getGithubRepo, {
17+
type EventRow = {
18+
_creationTime: number;
19+
};
20+
21+
function computeStats(events: EventRow[] | undefined) {
22+
if (!events || events.length === 0) {
23+
return { totalProjects: 0, avgProjectsPerDay: "0", lastUpdated: null };
24+
}
25+
26+
const byDay = new Set<string>();
27+
let maxTime = 0;
28+
29+
for (const ev of events) {
30+
const day = new Date(ev._creationTime).toISOString().slice(0, 10);
31+
byDay.add(day);
32+
if (ev._creationTime > maxTime) maxTime = ev._creationTime;
33+
}
34+
35+
const totalProjects = events.length;
36+
const avgProjectsPerDay =
37+
byDay.size === 0 ? "0" : (totalProjects / byDay.size).toFixed(2);
38+
const lastUpdated = new Date(maxTime).toLocaleDateString("en-US", {
39+
month: "short",
40+
day: "numeric",
41+
year: "numeric",
42+
});
43+
44+
return { totalProjects, avgProjectsPerDay, lastUpdated };
45+
}
46+
47+
export default function StatsSection() {
48+
const events = useQuery(api.analytics.getAllEvents, { range: "30d" });
49+
const githubRepo = useQuery(api.stats.getGithubRepo, {
2950
name: "AmanVarshney01/create-better-t-stack",
3051
});
31-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
32-
const npmPackages = useQuery((api as any).stats.getNpmPackages, {
52+
const npmPackages = useQuery(api.stats.getNpmPackages, {
3353
names: ["create-better-t-stack"],
3454
});
3555

3656
const liveNpmDownloadCount = useNpmDownloadCounter(npmPackages);
57+
const analyticsData = computeStats(events as EventRow[] | undefined);
3758

3859
return (
3960
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
@@ -44,16 +65,19 @@ export default function StatsSection({
4465
<span className="font-semibold text-sm sm:text-base">
4566
CLI_ANALYTICS.JSON
4667
</span>
68+
<span className="rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground uppercase">
69+
Last 30 days
70+
</span>
4771
</div>
4872

4973
<div className="space-y-3">
5074
<div className="flex items-center justify-between">
5175
<span className="flex items-center gap-1 font-mono text-muted-foreground text-xs uppercase tracking-wide">
5276
<BarChart3 className="h-3 w-3" />
53-
Total Projects
77+
Total Projects (30d)
5478
</span>
5579
<NumberFlow
56-
value={analyticsData?.totalProjects || 0}
80+
value={analyticsData.totalProjects}
5781
className="font-bold font-mono text-lg text-primary tabular-nums"
5882
transformTiming={{
5983
duration: 1000,
@@ -71,7 +95,7 @@ export default function StatsSection({
7195
Avg/Day
7296
</span>
7397
<span className="font-mono text-foreground text-sm">
74-
{analyticsData?.avgProjectsPerDay || "—"}
98+
{analyticsData.avgProjectsPerDay}
7599
</span>
76100
</div>
77101

@@ -81,7 +105,7 @@ export default function StatsSection({
81105
Last Updated
82106
</span>
83107
<span className="truncate font-mono text-accent">
84-
{analyticsData?.lastUpdated ||
108+
{analyticsData.lastUpdated ||
85109
new Date().toLocaleDateString("en-US", {
86110
month: "short",
87111
day: "numeric",

apps/web/src/app/(home)/_components/testimonials.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ export default function Testimonials({
220220
</motion.div>
221221

222222
{!showAllTweets && (
223-
<div className="pointer-events-none absolute right-0 bottom-10 left-0 h-32 bg-gradient-to-t from-muted/20 via-muted/40 to-transparent" />
223+
<div className="pointer-events-none absolute right-0 bottom-10 left-0 h-32 bg-linear-to-t from-muted/20 via-muted/40 to-transparent" />
224224
)}
225225

226226
<div className="my-4">
@@ -280,7 +280,7 @@ export default function Testimonials({
280280
</motion.div>
281281

282282
{!showAllTweets && (
283-
<div className="pointer-events-none absolute right-0 bottom-10 left-0 h-32 bg-gradient-to-t from-muted/20 via-muted/40 to-transparent" />
283+
<div className="pointer-events-none absolute right-0 bottom-10 left-0 h-32 bg-linear-to-t from-muted/20 via-muted/40 to-transparent" />
284284
)}
285285

286286
<div className="my-4">
@@ -340,7 +340,7 @@ export default function Testimonials({
340340
</motion.div>
341341

342342
{!showAllTweets && (
343-
<div className="pointer-events-none absolute right-0 bottom-10 left-0 h-32 bg-gradient-to-t from-muted/20 via-muted/40 to-transparent" />
343+
<div className="pointer-events-none absolute right-0 bottom-10 left-0 h-32 bg-linear-to-t from-muted/20 via-muted/40 to-transparent" />
344344
)}
345345

346346
<div className="my-4">

apps/web/src/app/(home)/analytics/_components/addons-examples-charts.tsx

Lines changed: 0 additions & 114 deletions
This file was deleted.

0 commit comments

Comments
 (0)