Skip to content

Commit 0bdd80f

Browse files
committed
feat: vercel route fix, env added
1 parent f441f28 commit 0bdd80f

File tree

15 files changed

+544
-38
lines changed

15 files changed

+544
-38
lines changed

WARP.md

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# WARP.md
2+
3+
This file provides guidance to WARP (warp.dev) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Databuddy is a comprehensive analytics and data management platform built as a monorepo using Turborepo, Bun, and modern web technologies. The platform provides real-time analytics, user tracking, and data visualization capabilities.
8+
9+
## Core Architecture
10+
11+
### Monorepo Structure
12+
13+
This is a Turborepo monorepo with the following key applications and packages:
14+
15+
**Applications (`apps/`):**
16+
- `dashboard/` - Next.js 15 frontend application (main analytics dashboard)
17+
- `api/` - Elysia.js backend API server with tRPC
18+
- `database/` - Database management UI (Next.js)
19+
- `basket/` - Data collection service
20+
- `docs/` - Documentation site
21+
22+
**Packages (`packages/`):**
23+
- `sdk/` - Analytics SDK for React/Vue (published package)
24+
- `db/` - Database layer with Drizzle ORM (PostgreSQL + ClickHouse)
25+
- `rpc/` - tRPC API definitions and procedures
26+
- `auth/` - Authentication system
27+
- `redis/` - Redis client and utilities
28+
- `validation/` - Zod schemas and validation
29+
- `email/` - Email templates and sending
30+
- `shared/` - Shared types and utilities
31+
- `env/` - Environment variable validation
32+
33+
### Technology Stack
34+
35+
- **Runtime**: Bun (required for all operations)
36+
- **Frontend**: Next.js 15 with React 19, TypeScript 5.8+
37+
- **Backend**: Elysia.js with tRPC
38+
- **Database**: PostgreSQL (via Drizzle ORM) + ClickHouse (analytics)
39+
- **Cache**: Redis
40+
- **Styling**: Tailwind CSS 4.x
41+
- **Icons**: Phosphor Icons (only, never Lucide)
42+
- **State**: Jotai + TanStack Query
43+
- **Forms**: React Hook Form + Zod v4
44+
- **Dates**: Dayjs (never date-fns)
45+
- **Linting**: Ultracite (Biome-based)
46+
47+
## Development Commands
48+
49+
### Essential Commands (Always use `bun`)
50+
51+
```bash
52+
# Install dependencies
53+
bun install
54+
55+
# Development (starts all apps)
56+
bun run dev
57+
58+
# Build all packages/apps
59+
bun run build
60+
61+
# Database operations
62+
bun run db:push # Push schema changes
63+
bun run db:studio # Open Drizzle Studio
64+
bun run db:migrate # Run migrations
65+
bun run db:seed <WEBSITE_ID> [DOMAIN] [EVENT_COUNT] # Seed test data
66+
67+
# SDK build (required before dev)
68+
bun run sdk:build
69+
70+
# Individual app development
71+
bun run dashboard:dev # Dashboard only
72+
73+
# Linting and formatting
74+
bun run lint # Lint with Ultracite
75+
bun run format # Format with Ultracite
76+
bun run check-types # TypeScript check
77+
78+
# Testing
79+
bun run test # Run tests with Vitest
80+
81+
# Email development
82+
bun run email:dev # Email templates server
83+
```
84+
85+
### Testing Commands
86+
87+
```bash
88+
# Run all tests
89+
bun run test
90+
91+
# Run specific test file
92+
bun test path/to/test.ts
93+
94+
# Watch mode
95+
bun test --watch
96+
```
97+
98+
## Critical Development Rules
99+
100+
### Package Manager
101+
- **ALWAYS use `bun`** - Never use npm, pnpm, or Node.js commands
102+
- Package manager is enforced via `.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc`
103+
104+
### Code Quality Standards
105+
- **Ultracite**: Strict linting/formatting (subsecond performance, AI-friendly)
106+
- **Type Safety**: No `any`, `unknown`, `never` types - use explicit types from `@shared/types`
107+
- **Zod v4**: Required everywhere (from `zod/v4`, not v3)
108+
- **Error Handling**: Never throw in server actions - use try/catch and return errors
109+
- **React**: Almost never use `useEffect` unless critical
110+
111+
### UI/UX Standards
112+
- **Icons**: Only Phosphor Icons (`@phosphor-icons/react`) - use `Icon` suffix
113+
- **Border Radius**: Always use `rounded` (never `rounded-xl`, `rounded-md`)
114+
- **Dates**: Dayjs only (never date-fns)
115+
- **State Management**: TanStack Query for data fetching (never SWR)
116+
- **Naming**: lower-case-kebab-case for files/components
117+
- **No Mock Data**: Never add placeholders or mock data
118+
119+
### Architecture Patterns
120+
- **Modularity**: Split components/utils for reusability and performance
121+
- **Complexity**: Favor less complex, fewer-line solutions
122+
- **Mobile First**: Always consider mobile responsiveness
123+
- **Error Boundaries**: Always implement properly
124+
- **Console Usage**: Use appropriate methods (`console.error`, `console.time`, `console.json`)
125+
126+
## Database Architecture
127+
128+
### Primary Database (PostgreSQL)
129+
- **ORM**: Drizzle ORM
130+
- **Migrations**: Located in `packages/db/`
131+
- **Studio**: Access via `bun run db:studio`
132+
133+
### Analytics Database (ClickHouse)
134+
- **Purpose**: High-performance analytics and event storage
135+
- **Setup**: `bun run clickhouse:init`
136+
- **Access**: Via `packages/db/clickhouse/`
137+
138+
### Redis Cache
139+
- **Package**: `@databuddy/redis`
140+
- **Usage**: Session storage, caching, rate limiting
141+
142+
## API Architecture
143+
144+
### tRPC Setup
145+
- **Definitions**: `packages/rpc/src/`
146+
- **Client**: Auto-generated TypeScript client
147+
- **Server**: Elysia.js integration in `apps/api/`
148+
149+
### Authentication
150+
- **Package**: `@databuddy/auth`
151+
- **Method**: Better Auth integration
152+
- **Providers**: Google, GitHub
153+
154+
## SDK Architecture
155+
156+
The `@databuddy/sdk` package provides analytics tracking for external applications:
157+
158+
- **Core**: Framework-agnostic analytics client
159+
- **React**: React-specific hooks and components
160+
- **Vue**: Vue 3 composables and components
161+
- **Build**: Uses Unbuild for multi-format output
162+
163+
## Environment Setup
164+
165+
### Required Services
166+
- PostgreSQL database
167+
- Redis server
168+
- ClickHouse (for analytics)
169+
- Cloudflare account (deployment)
170+
- Vercel account (deployment)
171+
172+
### Environment Files
173+
- Copy `.env.example` to `.env`
174+
- Configure database URLs, API keys, and service credentials
175+
176+
## Important Workspace Features
177+
178+
### Turborepo Configuration
179+
- **Caching**: Build artifacts cached for performance
180+
- **Dependencies**: Managed via workspace protocol
181+
- **Pipelines**: Defined in `turbo.json`
182+
183+
### Shared Dependencies (Catalog)
184+
Key shared versions defined in root `package.json`:
185+
- React 19.0.0
186+
- Next.js 15.3.4+
187+
- TypeScript 5.8.3+
188+
- Zod 3.25.76 (workspace packages use v4)
189+
- Tailwind CSS 4.1.4+
190+
191+
## Testing Strategy
192+
193+
- **Framework**: Vitest 3.x
194+
- **Location**: Tests alongside source files (`.test.ts` suffix)
195+
- **Coverage**: Available via `@vitest/coverage-v8`
196+
- **Example**: See `apps/basket/src/hooks/auth.test.ts`
197+
198+
## Deployment Architecture
199+
200+
- **Dashboard**: Vercel (Next.js app)
201+
- **API**: Deployed separately (Elysia.js)
202+
- **Database**: PostgreSQL + ClickHouse cluster
203+
- **CDN**: Cloudflare for static assets and edge functions
204+
205+
## Development Workflow
206+
207+
1. **Setup**: `bun install``bun run db:push``bun run sdk:build`
208+
2. **Development**: `bun run dev` (starts all services)
209+
3. **Database**: Use `bun run db:studio` for visual management
210+
4. **Testing**: `bun run test` for validation
211+
5. **Linting**: Automatic via Ultracite on save
212+
213+
## Key Files to Understand
214+
215+
- `turbo.json` - Monorepo build pipeline configuration
216+
- `packages/db/src/schema/` - Database schema definitions
217+
- `packages/rpc/src/` - API route definitions and types
218+
- `apps/dashboard/` - Main user interface
219+
- `.cursor/rules/` - Development standards and constraints
220+
221+
## Performance Considerations
222+
223+
- **Build Performance**: Turborepo caching + Bun's speed
224+
- **Runtime Performance**: React 19 + Next.js 15 optimizations
225+
- **Database Performance**: ClickHouse for analytics, PostgreSQL for transactional
226+
- **SDK Performance**: Minimal bundle size, tree-shakeable
227+
228+
## Security & Compliance
229+
230+
- **Authentication**: Better Auth with OAuth providers
231+
- **Data Protection**: GDPR compliant data handling
232+
- **API Security**: Rate limiting via Upstash Redis
233+
- **Environment**: Secure environment variable handling via `@databuddy/env`

apps/dashboard/app/api/integrations/vercel/callback/route.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { randomUUID } from 'node:crypto';
22
import { auth } from '@databuddy/auth';
33
import { account, and, db, eq } from '@databuddy/db';
4+
import { env } from '@databuddy/env/dashboard';
45
import { headers } from 'next/headers';
56
import { type NextRequest, NextResponse } from 'next/server';
67

@@ -37,14 +38,14 @@ export async function GET(request: NextRequest) {
3738
// If no session, redirect to auth pages with the callback URL
3839
if (!session?.user) {
3940
const callbackUrl = new URL(request.url);
40-
const completeIntegrationUrl = `${process.env.BETTER_AUTH_URL}${callbackUrl.pathname}${callbackUrl.search}`;
41+
const completeIntegrationUrl = `${env.BETTER_AUTH_URL}${callbackUrl.pathname}${callbackUrl.search}`;
4142

4243
return NextResponse.redirect(
43-
`${process.env.BETTER_AUTH_URL}/register?callback=${encodeURIComponent(completeIntegrationUrl)}`
44+
`${env.BETTER_AUTH_URL}/register?callback=${encodeURIComponent(completeIntegrationUrl)}`
4445
);
4546
}
4647

47-
const redirectUri = `${process.env.BETTER_AUTH_URL}/api/integrations/vercel/callback`;
48+
const redirectUri = `${env.BETTER_AUTH_URL}/api/integrations/vercel/callback`;
4849

4950
const tokenResponse = await fetch(
5051
'https://api.vercel.com/v2/oauth/access_token',
@@ -55,16 +56,16 @@ export async function GET(request: NextRequest) {
5556
},
5657
body: new URLSearchParams({
5758
code,
58-
client_id: process.env.VERCEL_CLIENT_ID as string,
59-
client_secret: process.env.VERCEL_CLIENT_SECRET as string,
59+
client_id: env.VERCEL_CLIENT_ID as string,
60+
client_secret: env.VERCEL_CLIENT_SECRET as string,
6061
redirect_uri: redirectUri,
6162
}),
6263
}
6364
);
6465

6566
if (!tokenResponse.ok) {
6667
return NextResponse.redirect(
67-
`${process.env.BETTER_AUTH_URL}/auth/error?error=token_exchange_failed`
68+
`${env.BETTER_AUTH_URL}/auth/error?error=token_exchange_failed`
6869
);
6970
}
7071

@@ -78,7 +79,7 @@ export async function GET(request: NextRequest) {
7879

7980
if (!userResponse.ok) {
8081
return NextResponse.redirect(
81-
`${process.env.BETTER_AUTH_URL}/auth/error?error=user_fetch_failed`
82+
`${env.BETTER_AUTH_URL}/auth/error?error=user_fetch_failed`
8283
);
8384
}
8485

@@ -87,7 +88,7 @@ export async function GET(request: NextRequest) {
8788

8889
if (!(userInfo.email && userInfo.id)) {
8990
return NextResponse.redirect(
90-
`${process.env.BETTER_AUTH_URL}/auth/error?error=invalid_user_info`
91+
`${env.BETTER_AUTH_URL}/auth/error?error=invalid_user_info`
9192
);
9293
}
9394

@@ -111,7 +112,7 @@ export async function GET(request: NextRequest) {
111112
.set({
112113
accessToken: tokens.access_token,
113114
scope: scopeData,
114-
updatedAt: now,
115+
updatedAt: new Date(now),
115116
})
116117
.where(eq(account.id, existingAccount.id));
117118
} else {
@@ -122,19 +123,19 @@ export async function GET(request: NextRequest) {
122123
userId,
123124
accessToken: tokens.access_token,
124125
scope: scopeData,
125-
createdAt: now,
126-
updatedAt: now,
126+
createdAt: new Date(now),
127+
updatedAt: new Date(now),
127128
});
128129
}
129130

130131
const redirectUrl =
131-
next || `${process.env.BETTER_AUTH_URL}/dashboard?vercel_integrated=true`;
132+
next || `${env.BETTER_AUTH_URL}/dashboard?vercel_integrated=true`;
132133

133134
return NextResponse.redirect(redirectUrl);
134135
} catch (error) {
135136
console.error('Vercel OAuth callback error:', error);
136137
return NextResponse.redirect(
137-
`${process.env.BETTER_AUTH_URL}/auth/error?error=internal_error`
138+
`${env.BETTER_AUTH_URL}/auth/error?error=internal_error`
138139
);
139140
}
140141
}

apps/dashboard/env.ts

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1 @@
1-
import z from 'zod';
2-
3-
const envSchema = z.object({
4-
VERCEL_CLIENT_ID: z.string(),
5-
VERCEL_CLIENT_SECRET: z.string(),
6-
BETTER_AUTH_SECRET: z.string(),
7-
CLICKHOUSE_URL: z.string(),
8-
DATABASE_URL: z.string(),
9-
REDIS_URL: z.string(),
10-
AI_API_KEY: z.string(),
11-
BETTER_AUTH_URL: z.string(),
12-
AUTUMN_SECRET_KEY: z.string(),
13-
NODE_ENV: z.string().default('development'),
14-
GITHUB_CLIENT_ID: z.string(),
15-
GITHUB_CLIENT_SECRET: z.string(),
16-
GOOGLE_CLIENT_ID: z.string(),
17-
GOOGLE_CLIENT_SECRET: z.string(),
18-
RESEND_API_KEY: z.string(),
19-
NEXT_PUBLIC_API_URL: z.string(),
20-
});
21-
22-
const isDevelopment = process.env.NODE_ENV === 'development';
23-
const skipValidation = isDevelopment || process.env.SKIP_VALIDATION === 'true';
24-
25-
export const env = skipValidation ? process.env : envSchema.parse(process.env);
1+
export { env } from '@databuddy/env/dashboard';

apps/dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@ai-sdk/react": "^2.0.29",
14+
"@databuddy/env": "workspace:*",
1415
"@databuddy/sdk": "workspace:*",
1516
"@databuddy/validation": "workspace:*",
1617
"@hello-pangea/dnd": "^18.0.1",

0 commit comments

Comments
 (0)