Files
agent-park/CLAUDE.md
T
mzaxdandClaude 7955cabea8 fix: 修复 Next.js 15 async params 兼容性问题并更新文档
- 修复关键词词云页面和 API 路由中的 async params 处理
- 将 params 和 searchParams 正确声明为 Promise 类型并添加 await
- 在 CLAUDE.md 中添加 Next.js 15 Breaking Change 说明
- 添加 ESLint/Prettier 配置文档
- 添加 Next.js 15 页面创建指南
- 补充故障排查部分的相关错误处理说明

Co-Authored-By: Claude (glm-4.7) <noreply@anthropic.com>
2026-01-29 17:21:55 +08:00

23 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Development Commands

Build & Run

pnpm dev          # Start development server (Next.js 15)
pnpm build        # Build for production
pnpm start        # Start production server
pnpm lint         # Run ESLint

Testing

pnpm test         # Run Vitest unit tests
pnpm test:e2e     # Run Playwright E2E tests

Database

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, img.shields.io
    • Lucide-react package import optimization
  • tsconfig.json: ES2017 target, strict mode enabled, noUncheckedIndexedAccess enabled
  • Testing: Vitest for unit tests, Playwright for E2E tests (configured but not extensively used yet)

Next.js 15 Breaking Change: Async Params

Critical: Next.js 15 changed params and searchParams to be async (Promise type).

  • In page components: params: Promise<{ locale: string }> and searchParams: Promise<{ key?: string }>
  • In API routes: { params }: { params: Promise<{ id: string }> }
  • Always await these params before using them:
    // ✅ Correct
    const { locale } = await params;
    const resolvedSearchParams = await searchParams;
    const quarter = resolvedSearchParams.quarter || 'default';
    
    // ❌ Wrong (causes runtime errors in Next.js 15)
    const { locale } = params;  // params is a Promise, not an object
    const quarter = searchParams.quarter;
    

Project Discovery System

项目发现系统是自动化探索和收录 AI 项目的核心功能,采用双 Agent 协作架构实现上下文隔离:

架构组件

  1. 自定义 Agents (.claude/agents/):

    • content-explorer-agent: 项目内容探索专家,批量探索项目并生成结构化数据
      • 使用 agent-browser 子任务并行探索 GitHub 项目
      • 应用严格的内容质量标准(客观描述、避免营销术语、不写入动态数据)
      • 生成符合 ProjectInputSchema 的 JSON 数据
    • api-submitter-agent: API 提交专家,处理探索结果的提交和状态更新
      • 批量标记任务为 IN_PROGRESS
      • 提交探索数据到完成 API
      • 自动重试失败的提交(指数退避,最多3次)
  2. Claude Commands (.claude/commands/):

    • /discover-projects: 主命令,协调探索和提交流程
      • 参数解析(任务数量、批次大小)
      • 分批处理(默认每批3个任务)
      • Agent 调度和进度显示
      • 结果汇总和错误报告
  3. API Endpoints (src/app/api/discovery/):

    • POST /api/discovery/tasks: 创建新的探索任务(支持批量)
    • GET /api/discovery/tasks: 获取待处理任务列表(支持 status/limit/offset 过滤)
    • PATCH /api/discovery/tasks/{id}: 更新任务状态
    • POST /api/discovery/tasks/{id}/complete: 完成任务并提交项目数据
    • GET /api/webhook/check-duplicates: 检查项目是否已存在(URL 去重)
  4. Database Model:

    • ProjectDiscoveryTask: 任务追踪表
      • 状态: PENDING → IN_PROGRESS → COMPLETED/FAILED
      • 原始数据: sourceUrl, sourceType
      • 探索结果: explorationData (JSON), explorationSummary
      • 错误处理: errorMessage, retryCount, lastRetryAt
      • 索引: idx_task_status_created, idx_task_source_url, idx_task_project_id

数据流转

用户输入 URL → 创建 PENDING 任务 → /discover-projects 命令
    ↓
分批获取任务(每批3个)
    ↓
Content Explorer Agent (并行探索) → 探索结果 JSON
    ↓
API Submitter Agent (提交到生产环境 API)
    ↓
更新任务状态 → COMPLETED/FAILED

质量标准

  • 数据模板: .claude/schemas/project-content-template.md
  • 描述要求: 清晰说明功能、突出价值、避免营销术语、10-500字
  • 内容要求: 从 README 提取并重新组织、不机械翻译、符合中文表达习惯
  • 链接要求: 必须包含 GITHUB 链接、所有链接可访问
  • 标签要求: 1-10 个标签、技术/应用/状态分类
  • 动态数据处理: Star/Fork 数量等动态数据不写入内容,使用 GitHub Badge 显示

环境变量

  • WEBHOOK_API_KEY: 生产环境 API 密钥(必需,用于认证)

使用示例

# 处理默认10个任务(每批3个)
/discover-projects

# 处理指定数量的任务
/discover-projects 5

# 自定义批次大小
/discover-projects 9 --batch=2

# 处理所有待处理任务
/discover-projects all --batch=5

Keyword Cloud System (季度 AI 热点词云)

功能: 自动化采集 Google Trends 数据,展示季度 AI 热点词汇词云。

数据流: n8n 工作流 → AI 清洗 → 规则匹配 → PostgreSQL → Next.js 前端

数据库表

  • Quarter: 季度元数据(quarter, title, titleEn, subtitle, subtitleEn, displayOrder, isActive
  • Keyword: 关键词数据(word, trendScore, description, visualConfig
  • VisualStyleRule: 视觉样式规则配置(name, minScore, maxScore, color, size, border, rotation
  • KeywordCloudErrorLog: 错误日志(quarter, keyword, errorType, errorMessage

API 端点

  • GET /api/keyword-cloud/quarters: 获取季度列表(支持 isActive 过滤)
  • GET /api/keyword-cloud/keywords/[quarter]: 获取指定季度的关键词
  • GET /api/keyword-cloud/rules: 获取视觉样式规则配置
  • POST /api/keyword-cloud/keywords: 批量写入关键词(n8n 使用,需 API Key 认证)
  • GET /api/keyword-cloud/health: 健康检查端点(返回系统统计信息)

前端路由

  • /[locale]/keyword-cloud: 词云展示页面

前端组件

  • Location: src/app/[locale]/keyword-cloud/components/
  • CloudWord: 单个词汇组件(支持颜色、大小、边框、旋转、悬停弹出框)
  • QuarterNavigator: 季度导航组件(前后切换)
  • ProgressIndicator: 进度条组件(显示季度进度)
  • KeywordCloud: 主容器组件(集成所有子组件,客户端组件)

国际化支持

  • 消息键: src/messages/{locale}.json 中的 keywordCloud 命名空间
  • 支持的字段:
    • metaTitle/metaDescription: SEO 元数据
    • badge: 页面徽章文本
    • title/titleHighlight: 主标题(支持高亮)
    • subtitle: 副标题描述
    • loading/loadFailed/retry: 加载状态文本
    • hotKeyword: 热门词汇提示(支持 {word} 参数替换)
  • 导航菜单: navigation.keywordCloud 键控制导航栏显示
  • 添加新翻译: 更新 src/messages/zh.jsonsrc/messages/en.json 中的 keywordCloud 部分

数据访问层

  • Location: src/hooks/useKeywordCloud.ts (服务器端函数)
  • Functions:
    • getAllQuarters(): 获取所有季度列表
    • getQuarterByQuarter(): 获取单个季度详情(含关键词计数)
    • getKeywordsByQuarter(): 获取指定季度的所有关键词
    • getVisualStyleRules(): 获取视觉样式规则
    • upsertQuarter(): 创建或更新季度
    • createKeywords(): 批量创建关键词
    • logKeywordCloudError(): 记录错误日志

客户端 Hook

  • Location: src/hooks/useKeywordCloudClient.ts
  • Function: useKeywordCloud(quarter) - 响应式获取季度关键词数据

n8n 工作流

  • 配置文件: n8n-workflows/keyword-cloud-workflow.json
  • 文档: n8n-workflows/README.md
  • 流程:
    1. Schedule Trigger: 每季度末最后一天的 23:00 自动触发
    2. Calculate Quarter: 计算当前季度标识和时间范围
    3. Google Trends: 采集热门搜索词
    4. Extract Keywords: 提取关键词和热度分数
    5. Get Visual Rules: 获取视觉样式规则
    6. Match Visual Rules: 为关键词匹配视觉样式
    7. Send to API: 写入数据库

初始化数据

# 运行种子数据脚本(创建视觉规则和示例数据)
pnpm tsx scripts/seed-keyword-cloud.ts

测试 API

# 运行 API 测试脚本
set -a && source .env.local && set +a && npx tsx scripts/test-keyword-api.ts

环境变量

  • WEBHOOK_API_KEY: n8n 工作流使用的 API 密钥(必需)
  • n8n 环境变量(在 n8n 中设置):
    • API_URL: API 端点 URL(如 http://localhost:3000
    • API_KEY: 与 WEBHOOK_API_KEY 相同

MCP Servers Usage (按需使用)

  1. context7: 不确定 API 用法时查阅最新文档
  2. chrome-devtools-mcp: 查看页面效果、调试 UI 修复 BUG
  3. web-search-prime: 默认联网搜索工具
  4. vision-mcp-server: 图片/视频理解

Git Commits

Git 提交信息遵循约定式提交格式(详见上方 Code Quality & Standards → Git Commit Conventions)。

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
  • Always verify API responses with WEBHOOK_API_KEY when testing webhook or discovery endpoints
  • Use pnpm prisma studio to inspect database state during development

Common Development Patterns

Adding a New API Endpoint

  1. Create route file in src/app/api/ (e.g., src/app/api/your-endpoint/route.ts)
  2. Import validation schemas from @/lib/validations
  3. Use crypto.timingSafeEqual() for API key authentication (see webhook route:36-43)
  4. Implement partial success mode for batch operations (see webhook route:55-67)
  5. Return structured errors with NextResponse.json()

Adding New Database Fields

  1. Update prisma/schema.prisma with new fields
  2. Run pnpm prisma migrate dev --name your_migration_name
  3. Update Zod schemas in src/lib/validations.ts
  4. Update TypeScript types in src/hooks/useProjects.ts if needed
  5. Regenerate Prisma client: pnpm prisma generate

Creating Custom Agents

  1. Create agent file in .claude/agents/your-agent.md
  2. Define agent role, capabilities, and task instructions
  3. Reference existing agents (content-explorer-agent, api-submitter-agent) as templates
  4. Test via corresponding Claude Command in .claude/commands/

Working with Multilingual Content

  • All user-facing content should have both Chinese (name, description) and English (nameEn, descriptionEn) versions
  • Use src/messages/zh.json and src/messages/en.json for UI translations
  • For project data, prefer Chinese as primary language with English as optional
  • When creating content, avoid mechanical translation - write naturally for each locale

Creating New Pages (Next.js 15)

When adding new pages in the App Router, remember that params and searchParams are Promises:

// Page component structure
interface PageProps {
  params: Promise<{ locale: string; id?: string }>;
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}

export default async function MyPage({ params, searchParams }: PageProps) {
  // MUST await params and searchParams
  const { locale, id } = await params;
  const { filter, sort } = await searchParams;

  // Now you can use the values
  // ...
}

// For generateMetadata
export async function generateMetadata({
  params
}: {
  params: Promise<{ locale: string }>
}) {
  const { locale } = await params;
  // ...
}

For API routes with dynamic parameters:

export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  // ...
}

Code Quality & Standards

TypeScript Configuration

  • Strict mode enabled with additional safety flags: noUncheckedIndexedAccess, noImplicitReturns, noFallthroughCasesInSwitch
  • Path alias: @/* maps to ./src/*
  • Target: ES2017 for modern browser support

ESLint Configuration

  • Config: .eslintrc.json extends next/core-web-vitals and prettier
  • Console rules: no-console warns on console.log but allows console.warn and console.error
  • Prettier integration: eslint-config-prettier disables conflicting ESLint rules
  • Prettier config: .prettierrc.json for code formatting

Validation & Security

  • API Authentication: Webhook uses timing-safe comparison (crypto.timingSafeEqual) to prevent timing attacks
  • Input Validation: All API inputs use Zod schemas with detailed error messages
  • SQL Injection Prevention: Prisma ORM with parameterized queries
  • Data Sanitization: Markdown content sanitized with rehype-sanitize plugin

Error Handling Patterns

  • Webhook: Partial success mode - continues processing remaining projects even if individual projects fail
  • Database: Unique constraints use try-catch with fallback logic (e.g., tag slug conflicts in webhook)
  • Console: Use console.warn() for operational logs, console.error() for errors

Git Commit Conventions

  • Format: <type>: <description> (type in lowercase Chinese: feat/fix/refactor/chore)
  • Types: feat (新功能), fix (修复), refactor (重构), chore (杂项)
  • Examples:
    • feat: 新增项目发现任务系统
    • fix: 修复 ESLint 警告
    • refactor: 重构项目内容标准实现职责分离

Testing Strategy

Unit Tests (Vitest)

  • Location: Test files co-located with source code (e.g., *.test.ts)
  • Run: pnpm test for all tests, pnpm test <pattern> for specific tests
  • Configuration: Vitest with @testing-library/jest-dom matchers

E2E Tests (Playwright)

  • Location: tests/e2e/ or co-located with features
  • Run: pnpm test:e2e to execute all E2E tests
  • Usage: Focus on critical user journeys (project browsing, search, locale switching)

Performance Considerations

Database Optimization

  • Index Strategy: Composite indexes on frequently queried fields (status+createdAt, type+url)
  • N+1 Prevention: Batch queries for tags (see webhook route:94-98)
  • Connection Pooling: Prisma client singleton pattern (src/lib/prisma.ts)

Frontend Performance

  • ISR: Project detail pages revalidated every 5 minutes (revalidate = 300)
  • Package Optimization: Lucide-react imports optimized via experimental.optimizePackageImports
  • Image Domains: Pre-configured for localhost, *.anthropic.com, img.shields.io

API Rate Limiting

  • Webhook: Max 100 projects per request
  • Discovery tasks: Max 50 tasks per batch creation
  • All queries: Max 100 items per page (enforced via Zod schemas)

Troubleshooting

Common Issues

Database Connection Errors

  • Verify DATABASE_URL is set in .env.local
  • Check Neon dashboard for database status
  • Run pnpm prisma db push to sync schema if needed

Webhook Authentication Failures

  • Ensure WEBHOOK_API_KEY is set and matches (32+ characters)
  • Check that API key is sent in request body
  • Verify timing-safe comparison is used (never log raw API keys)

Missing Translations

  • All new UI text must be added to both src/messages/zh.json and src/messages/en.json
  • Missing keys will display as missing_key_name in the UI
  • Test both locales (switch via URL path /zh/ or /en/)

Build Errors After Schema Changes

  • Run pnpm prisma generate to regenerate Prisma client
  • Restart dev server after schema changes
  • Check for TypeScript errors in generated types

Next.js 15 Async Params Errors

  • If you see errors like params.locale is undefined or Cannot read properties of undefined, you likely forgot to await the params
  • Check that all page components properly await: const { locale } = await params;
  • Check that all API routes properly await: const { id } = await params;
  • See "Next.js Configuration → Next.js 15 Breaking Change" above for examples

Discovery Task Failures

  • Check src/app/api/discovery/lib/discovery-service.ts:16-80 for deduplication logic
  • Verify WEBHOOK_API_KEY is set for api-submitter-agent
  • Use pnpm prisma studio to inspect task status and error messages
  • Check browser console for agent-browser errors (requires chrome-devtools-mcp)

Debugging Tips

  1. Enable Prisma Query Logging:

    // In src/lib/prisma.ts
    export const prisma = new PrismaClient({
      log: ['query', 'error', 'warn'],
    })
    
  2. Check Database State:

    pnpm prisma studio
    # Opens at http://localhost:5555
    
  3. Test API Endpoints:

    # Create discovery task
    curl -X POST http://localhost:3000/api/discovery/tasks \
      -H "Content-Type: application/json" \
      -d '{"apiKey":"YOUR_KEY","tasks":[{"sourceUrl":"https://github.com/user/repo"}]}'
    
  4. Validate Request Payloads:

    • All schemas are in src/lib/validations.ts
    • Check schema errors in API response details field
    • Ensure all required fields are present

Environment Variables Checklist

For local development, ensure these are set in .env.local:

# Database (Neon PostgreSQL)
DATABASE_URL="postgres://[user]:[password]@[host]/[database]?sslmode=require"

# Webhook API Key (32+ characters)
WEBHOOK_API_KEY="your-secret-api-key-min-32-chars"

For production deployment (Vercel):

  • Set DATABASE_URL in Vercel Dashboard (Environment Variables)
  • Set WEBHOOK_API_KEY in Vercel Dashboard
  • Run pnpm prisma db push after first deployment to create tables
  • Do NOT use vercel.json for environment variables