- 移除 vercel.json 中不存在的 Secret 引用 (@database_url) - 在 CLAUDE.md 中添加 Neon 数据库配置说明 - 环境变量应直接在 Vercel Dashboard 中设置 Co-Authored-By: Claude <noreply@anthropic.com>
156 lines
7.7 KiB
Markdown
156 lines
7.7 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Development Commands
|
|
|
|
### Build & Run
|
|
```bash
|
|
pnpm dev # Start development server (Next.js 15)
|
|
pnpm build # Build for production
|
|
pnpm start # Start production server
|
|
pnpm lint # Run ESLint
|
|
```
|
|
|
|
### Testing
|
|
```bash
|
|
pnpm test # Run Vitest unit tests
|
|
pnpm test:e2e # Run Playwright E2E tests
|
|
```
|
|
|
|
### Database
|
|
```bash
|
|
pnpm prisma migrate dev # Run database migrations
|
|
pnpm prisma migrate dev --name init # Create initial migration
|
|
pnpm prisma db seed # Seed database (uses ts-node)
|
|
pnpm prisma studio # Open Prisma Studio for database inspection
|
|
```
|
|
|
|
## Architecture Overview
|
|
|
|
This is a **Next.js 15 multilingual AI project navigation website** using the App Router architecture with the following key components:
|
|
|
|
### i18n Architecture (next-intl)
|
|
- **Locales**: `zh` (default) and `en`
|
|
- **Route pattern**: `/{locale}/path` (always prefixed with locale)
|
|
- **Middleware**: `src/middleware.ts` handles locale detection and routing
|
|
- **i18n config**: `src/i18n/request.ts` loads locale messages from `src/messages/{locale}.json`
|
|
- **Messages**: Translation files at `src/messages/zh.json` and `src/messages/en.json`
|
|
|
|
### App Router Structure
|
|
```
|
|
src/app/
|
|
├── [locale]/ # Locale-scoped routes
|
|
│ ├── page.tsx # Home page
|
|
│ ├── projects/ # Projects listing and details
|
|
│ │ ├── page.tsx # Projects list
|
|
│ │ └── [id]/ # Individual project details (slug-based)
|
|
│ └── layout.tsx # Locale layout (header, footer)
|
|
├── api/ # API routes (no locale prefix)
|
|
│ └── webhook/projects/route.ts # Webhook for project ingestion
|
|
└── layout.tsx # Root layout
|
|
```
|
|
|
|
### Database (Prisma + PostgreSQL)
|
|
- **Provider**: Neon (serverless PostgreSQL)
|
|
- **Schema**: `prisma/schema.prisma` defines models: `Project`, `Tag`, `ExternalLink`, `ProjectTag`
|
|
- **Enums**: `ProjectStatus` (ACTIVE/ARCHIVED), `LinkType` (WEBSITE/GITHUB/HUGGINGFACE/PAPER)
|
|
- **Client singleton**: `src/lib/prisma.ts` exports Prisma client instance
|
|
- **Multilingual fields**: Most models have `name`/`nameEn`, `description`/`descriptionEn`, `content`/`contentEn` pairs
|
|
- **Cascade deletions**: `ExternalLink` and `ProjectTag` use `onDelete: Cascade` - deleting a project automatically cleans up its links and tag connections
|
|
- **Key constraints**:
|
|
- `Project.slug`: Unique
|
|
- `Tag.name`: Unique (tag names are globally unique)
|
|
- `Tag.slug`: Unique
|
|
- `ExternalLink`: `@@unique([projectId, url])` (each project can't have duplicate URLs)
|
|
- **Indexes**: `idx_project_status_createdAt`, `idx_project_slug`, `idx_tag_slug`, `idx_link_projectId`, `idx_link_type`, `idx_link_type_url` (composite for efficient URL-based deduplication)
|
|
|
|
#### Neon Database Setup
|
|
- **Dashboard**: https://console.neon.tech
|
|
- **Connection String Format**: `postgres://[user]:[password]@[host]/[database]?sslmode=require`
|
|
- **Vercel Integration**: Set `DATABASE_URL` environment variable in Vercel Dashboard (do NOT use `vercel.json` env references)
|
|
- **Free Tier**: 0.5GB storage, 300 hours compute/month
|
|
- **Run Migrations**: After deployment, run `pnpm prisma db push` or use Neon's SQL Editor to create tables
|
|
|
|
### Webhook Deduplication Strategy
|
|
The webhook at `src/app/api/webhook/projects/route.ts` implements a **multi-level deduplication** strategy to prevent duplicate projects:
|
|
1. **GitHub URL exact match** (highest priority) - via `ExternalLink` table using `idx_link_type_url` index
|
|
2. **Website URL exact match** - via `ExternalLink` table using `idx_link_type_url` index
|
|
3. **slug match** (fallback) - via `Project.slug` field
|
|
|
|
When updating an existing project, the webhook:
|
|
- Updates all project fields (name, description, content, status, source)
|
|
- Replaces all tags (deletes old `ProjectTag` connections via `ProjectTag` table, creates new ones)
|
|
- Replaces all links (deletes old `ExternalLink` entries, creates new ones)
|
|
|
|
**Tag handling special case**: Due to `Tag.name` unique constraint, tag upsert follows:
|
|
1. First try to find existing tag by name
|
|
2. If not found, try upsert by slug
|
|
3. If slug conflicts, use the existing tag with that slug
|
|
|
|
### Data Fetching (Server-Side)
|
|
- **Location**: `src/hooks/useProjects.ts` (server functions, not React hooks)
|
|
- **Functions**: `getProjects()`, `getProjectBySlug()`, `getAllTags()`, `getTagsWithProjectCounts()`
|
|
- **Usage**: Directly called in Server Components and route handlers
|
|
- **Query transformation**: `getProjects()` and `getProjectBySlug()` flatten the `ProjectTag` junction table structure to return tags directly
|
|
- **ISR**: Project detail pages use `export const revalidate = 300` (5 minutes) at `src/app/[locale]/projects/[id]/page.tsx`
|
|
|
|
### Validation (Zod)
|
|
- **Schemas**: `src/lib/validations.ts` defines all Zod schemas
|
|
- `ProjectInputSchema`: Validates incoming project data (1-10 tags, 1-10 links required)
|
|
- `WebhookPayloadSchema`: Validates webhook requests with API key (1-100 projects per request)
|
|
- `ProjectQuerySchema`: Validates query parameters (search, tags, status, page, limit)
|
|
|
|
### Styling (Tailwind CSS)
|
|
- **Neo-brutalism design**: Sharp corners (0px radius), bold borders (4px shadows), hard edges
|
|
- **Theme colors**:
|
|
- Primary: Gold (#FFD700)
|
|
- Secondary: Orange (#ff6f00)
|
|
- Background light: #F5F2EB, dark: #121212
|
|
- Surface light: #FFFFFF, dark: #1E1E1E
|
|
- **Typography**: Space Mono (headings), Inter (body)
|
|
- **Dark mode**: Class-based with `dark:` prefix
|
|
- **Config**: `tailwind.config.ts` extends theme with custom colors, shadows, and animations
|
|
|
|
### Content Rendering
|
|
- **Markdown**: Project content fields support Markdown via `react-markdown`
|
|
- **Plugins**: `rehype-raw`, `rehype-sanitize`, `rehype-shiki`, `remark-gfm`
|
|
- **Usage**: `ProjectDetail` component renders `content`/`contentEn` as Markdown
|
|
|
|
### UI Components
|
|
- **Radix UI primitives**: `@radix-ui/react-slot`, `@radix-ui/react-navigation-menu`, `@radix-ui/react-dropdown-menu`, `@radix-ui/react-separator`
|
|
- **Lucide React icons**: Used throughout the app (package imports optimized via `experimental.optimizePackageImports`)
|
|
- **Custom components**: `src/components/` organized by domain
|
|
- `layout/`: Header, Footer, AnnouncementBar
|
|
- `locale/`: LocaleSwitcher
|
|
- `project/`: ProjectCard, ProjectList, ProjectDetail, ProjectSidebar, RelatedProjects, TagCloud, ExternalLinkCard, ShareButtons, MarkdownContent, GitHubBadges, GitHubTextStatsCard
|
|
- `search/`: SearchBar
|
|
- `ui/`: Base UI components (buttons, cards, etc.)
|
|
|
|
### Next.js Configuration
|
|
- **next.config.js**:
|
|
- `next-intl` plugin wrapper for i18n
|
|
- Image domains: localhost, *.anthropic.com
|
|
- Lucide-react package import optimization
|
|
- **tsconfig.json**: ES2022 target, strict mode enabled
|
|
- **Testing**: Vitest for unit tests, Playwright for E2E tests (configured but not extensively used yet)
|
|
|
|
## MCP Servers Usage (按需使用)
|
|
|
|
1. **context7**: 不确定 API 用法时查阅最新文档
|
|
2. **chrome-devtools-mcp**: 查看页面效果、调试 UI 修复 BUG
|
|
3. **web-search-prime**: 默认联网搜索工具
|
|
4. **vision-mcp-server**: 图片/视频理解
|
|
|
|
## Git Commits
|
|
|
|
提交信息主要使用中文,使用描述性的提交格式。
|
|
|
|
## Development Workflow
|
|
|
|
- Before viewing the page, check if there's already a project running at localhost:3000. If yes, access it directly; if no, then run `pnpm dev`
|
|
- The dev server runs with hot-reload enabled for fast iteration
|
|
- The middleware handles locale detection and routing automatically - no manual locale configuration needed
|
|
- When adding new translations, update both `src/messages/zh.json` and `src/messages/en.json`
|
|
- Database changes require running `pnpm prisma migrate dev` to update the schema
|