diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..be5356bc --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,187 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Umami is a simple, fast, privacy-focused alternative to Google Analytics. It's built with Next.js 15 (App Router), TypeScript, and supports multiple database backends (PostgreSQL, ClickHouse, Kafka). + +## Development Commands + +### Package Manager +- Uses **pnpm** as the package manager (monorepo with `pnpm-workspace.yaml`) +- Install dependencies: `pnpm install` + +### Development +- Start development server: `pnpm dev` (runs on port 3001 with Turbo) +- Build application: `pnpm build` (runs multiple build steps including database setup) +- Start production server: `pnpm start` +- Build for Docker: `pnpm build-docker` + +### Database Operations +- Build Prisma client: `pnpm build-prisma-client` +- Update database schema: `pnpm update-db` +- Check database connection: `pnpm check-db` +- Seed test data: `pnpm seed-data` +- Change admin password: `pnpm change-password` + +### Code Quality +- Lint: `pnpm lint` (uses Biome) +- Format: `pnpm format` (uses Biome) +- Check (lint + format): `pnpm check` (uses Biome) +- Test: `pnpm test` (uses Jest) +- Cypress E2E tests: `pnpm cypress-run` or `pnpm cypress-open` + +### Tracker Script +- Build tracker script: `pnpm build-tracker` (rollup builds `/public/script.js`) +- Update tracker: `pnpm update-tracker` + +### Internationalization +- Generate language files: `pnpm generate-lang` +- Format language files: `pnpm format-lang` +- Compile language files: `pnpm compile-lang` + +## Architecture + +### Tech Stack +- **Frontend**: Next.js 15 (App Router), React 19, TypeScript, Tailwind CSS +- **Backend**: Next.js API routes, Prisma ORM, multiple database support +- **Database**: PostgreSQL (primary), ClickHouse (analytics), Kafka (event streaming) +- **State Management**: Zustand, React Query (TanStack) +- **Styling**: CSS Modules, Tailwind CSS, `@umami/react-zen` component library +- **Code Quality**: Biome (linting/formatting), TypeScript, Jest, Cypress + +### Key Directories +- `src/app/` - Next.js App Router pages and API routes + - `(collect)/` - Collection endpoints (pixels, links) + - `(main)/` - Main application UI routes + - `api/` - REST API endpoints +- `src/components/` - React components + - `common/` - Shared UI components + - `charts/` - Data visualization components + - `hooks/` - Custom React hooks +- `src/lib/` - Utility libraries and shared logic +- `src/tracker/` - Analytics tracker JavaScript (built to `/public/script.js`) +- `src/styles/` - Global CSS and design tokens +- `prisma/` - Database schema and migrations +- `db/` - Database-specific schemas (PostgreSQL, ClickHouse) +- `scripts/` - Build and utility scripts + +### Database Architecture +- **Primary Database**: PostgreSQL with Prisma ORM +- **Analytics Database**: ClickHouse for high-volume analytics data +- **Event Streaming**: Kafka for real-time event processing +- **Cache**: Redis for session and data caching +- Database type determined by `DATABASE_URL` environment variable +- Queries use `runQuery()` helper that routes to appropriate database + +### API Structure +- RESTful API built with Next.js Route Handlers +- Authentication via JWT tokens +- Main collection endpoint: `/api/send` (handles analytics events) +- Admin endpoints under `/api/admin/` +- User/team management under `/api/me/`, `/api/users/`, `/api/teams/` +- Website analytics under `/api/websites/[websiteId]/` + +### Tracker System +- JavaScript tracker at `/public/script.js` (built from `src/tracker/`) +- Configurable via data attributes on script tag +- Supports event tracking, page views, custom events +- Can be hosted externally via `TRACKER_SCRIPT_URL` env var +- Multiple script names supported via `TRACKER_SCRIPT_NAME` env var + +### Environment Configuration +Required environment variables: +- `DATABASE_URL` - PostgreSQL connection string +- `APP_SECRET` - JWT secret key + +Optional environment variables: +- `CLICKHOUSE_URL` - ClickHouse connection for analytics +- `KAFKA_URL` - Kafka connection for event streaming +- `REDIS_URL` - Redis connection for caching +- `CLOUD_MODE` - Enable cloud-specific features +- `COLLECT_API_ENDPOINT` - Custom endpoint for tracker +- `TRACKER_SCRIPT_NAME` - Alternative script names +- `DISABLE_UI` - Disable web interface (API-only mode) + +## Development Notes + +### Database Setup +1. Set `DATABASE_URL` in `.env` file +2. Run `pnpm build` to create database tables and admin user (admin/umami) +3. For ClickHouse analytics, also set `CLICKHOUSE_URL` + +### Testing +- Unit tests: `pnpm test` (Jest with `src/lib/__tests__/`) +- E2E tests: `pnpm cypress-run` (requires running application) +- Test database: Uses separate test database configuration + +### Code Style +- Uses Biome for formatting and linting (configured in `biome.json`) +- Line width: 100 characters +- Single quotes for strings +- Trailing commas in multiline objects/arrays +- TypeScript strict mode enabled + +### Build Process +The `pnpm build` command runs multiple sequential steps: +1. `check-env` - Validate environment variables +2. `build-db` - Build Prisma client and database schema +3. `check-db` - Verify database connection +4. `build-tracker` - Build analytics tracker script +5. `build-geo` - Build GeoIP database +6. `build-app` - Build Next.js application + +### Docker Deployment +- Docker image includes standalone Next.js output +- Uses multi-stage build for production optimization +- Includes all necessary dependencies +- Health check endpoint at `/api/heartbeat` + +### Internationalization +- Uses `react-intl` for translations +- Messages extracted from `src/components/messages.ts` +- Compiled to `public/intl/messages/` +- Default locale: `en-US`, configurable via `DEFAULT_LOCALE` env var + +## Common Tasks + +### Adding a New API Endpoint +1. Create file in `src/app/api/[path]/route.ts` +2. Use `parseRequest()` helper for request validation +3. Use `runQuery()` for database operations +4. Return appropriate HTTP responses using helpers from `src/lib/response.ts` + +### Adding a New Component +1. Create component in `src/components/` (organized by feature) +2. Use CSS Modules for styling (`*.module.css`) +3. Export TypeScript interfaces/types +4. Add to appropriate barrel file (`index.ts`) if needed + +### Database Schema Changes +1. Update `prisma/schema.prisma` +2. Create migration: `prisma migrate dev --name description` +3. Update Prisma client: `pnpm build-prisma-client` +4. Update TypeScript types as needed + +### Adding Tracker Features +1. Modify `src/tracker/index.js` +2. Rebuild tracker: `pnpm build-tracker` +3. Test with local development server + +## Troubleshooting + +### Database Connection Issues +- Verify `DATABASE_URL` format: `postgresql://user:pass@host:port/db` +- Check database is running and accessible +- Run `pnpm check-db` to test connection + +### Build Failures +- Ensure all environment variables are set +- Check Node.js version (requires 18.18+) +- Clear `.next` cache if build issues persist + +### Tracker Not Working +- Verify script is loaded: check browser console +- Check CORS headers if hosted on different domain +- Test with `/api/send` endpoint directly \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 1282fd86..4cc435f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ARG NODE_IMAGE_VERSION="22-alpine" # Install dependencies only when needed -FROM node:${NODE_IMAGE_VERSION} AS deps +FROM hub.diyla.com/node:${NODE_IMAGE_VERSION} AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat WORKDIR /app @@ -10,7 +10,7 @@ RUN npm install -g pnpm RUN pnpm install --frozen-lockfile # Rebuild the source code only when needed -FROM node:${NODE_IMAGE_VERSION} AS builder +FROM hub.diyla.com/node:${NODE_IMAGE_VERSION} AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . @@ -25,7 +25,7 @@ ENV DATABASE_URL="postgresql://user:pass@localhost:5432/dummy" RUN npm run build-docker # Production image, copy all the files and run next -FROM node:${NODE_IMAGE_VERSION} AS runner +FROM hub.diyla.com/node:${NODE_IMAGE_VERSION} AS runner WORKDIR /app ARG PRISMA_VERSION="6.19.0" diff --git a/docker-compose.yml b/docker-compose.yml index 348c294c..40719236 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,9 @@ --- services: umami: - image: ghcr.io/umami-software/umami:latest + build: + context: . + dockerfile: Dockerfile ports: - "3000:3000" environment: @@ -18,7 +20,7 @@ services: timeout: 5s retries: 5 db: - image: postgres:15-alpine + image: hub.diyla.com/postgres:15-alpine environment: POSTGRES_DB: umami POSTGRES_USER: umami