mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-14 13:53:29 +08:00
docs(zh-CN): sync Chinese docs with latest upstream changes (#304)
* docs(zh-CN): sync Chinese docs with latest upstream changes * update --------- Co-authored-by: neo <neo.dowithless@gmail.com>
This commit is contained in:
166
docs/zh-CN/examples/saas-nextjs-CLAUDE.md
Normal file
166
docs/zh-CN/examples/saas-nextjs-CLAUDE.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# SaaS 应用程序 — 项目 CLAUDE.md
|
||||
|
||||
> 一个 Next.js + Supabase + Stripe SaaS 应用程序的真实示例。
|
||||
> 将此复制到您的项目根目录,并根据您的技术栈进行自定义。
|
||||
|
||||
## 项目概览
|
||||
|
||||
**技术栈:** Next.js 15(App Router)、TypeScript、Supabase(身份验证 + 数据库)、Stripe(计费)、Tailwind CSS、Playwright(端到端测试)
|
||||
|
||||
**架构:** 默认使用服务器组件。仅在需要交互性时使用客户端组件。API 路由用于 Webhook,服务器操作用于数据变更。
|
||||
|
||||
## 关键规则
|
||||
|
||||
### 数据库
|
||||
|
||||
* 所有查询均使用启用 RLS 的 Supabase 客户端 — 绝不要绕过 RLS
|
||||
* 迁移在 `supabase/migrations/` 中 — 绝不要直接修改数据库
|
||||
* 使用带有明确列列表的 `select()`,而不是 `select('*')`
|
||||
* 所有面向用户的查询必须包含 `.limit()` 以防止返回无限制的结果
|
||||
|
||||
### 身份验证
|
||||
|
||||
* 在服务器组件中使用来自 `@supabase/ssr` 的 `createServerClient()`
|
||||
* 在客户端组件中使用来自 `@supabase/ssr` 的 `createBrowserClient()`
|
||||
* 受保护的路由检查 `getUser()` — 绝不要仅依赖 `getSession()` 进行身份验证
|
||||
* `middleware.ts` 中的中间件会在每个请求上刷新身份验证令牌
|
||||
|
||||
### 计费
|
||||
|
||||
* Stripe webhook 处理程序在 `app/api/webhooks/stripe/route.ts` 中
|
||||
* 绝不要信任客户端的定价数据 — 始终在服务器端从 Stripe 获取
|
||||
* 通过 `subscription_status` 列检查订阅状态,由 webhook 同步
|
||||
* 免费层用户:3 个项目,每天 100 次 API 调用
|
||||
|
||||
### 代码风格
|
||||
|
||||
* 代码或注释中不使用表情符号
|
||||
* 仅使用不可变模式 — 使用展开运算符,永不直接修改
|
||||
* 服务器组件:不使用 `'use client'` 指令,不使用 `useState`/`useEffect`
|
||||
* 客户端组件:`'use client'` 放在顶部,保持最小化 — 将逻辑提取到钩子中
|
||||
* 所有输入验证(API 路由、表单、环境变量)优先使用 Zod 模式
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
src/
|
||||
app/
|
||||
(auth)/ # Auth pages (login, signup, forgot-password)
|
||||
(dashboard)/ # Protected dashboard pages
|
||||
api/
|
||||
webhooks/ # Stripe, Supabase webhooks
|
||||
layout.tsx # Root layout with providers
|
||||
components/
|
||||
ui/ # Shadcn/ui components
|
||||
forms/ # Form components with validation
|
||||
dashboard/ # Dashboard-specific components
|
||||
hooks/ # Custom React hooks
|
||||
lib/
|
||||
supabase/ # Supabase client factories
|
||||
stripe/ # Stripe client and helpers
|
||||
utils.ts # General utilities
|
||||
types/ # Shared TypeScript types
|
||||
supabase/
|
||||
migrations/ # Database migrations
|
||||
seed.sql # Development seed data
|
||||
```
|
||||
|
||||
## 关键模式
|
||||
|
||||
### API 响应格式
|
||||
|
||||
```typescript
|
||||
type ApiResponse<T> =
|
||||
| { success: true; data: T }
|
||||
| { success: false; error: string; code?: string }
|
||||
```
|
||||
|
||||
### 服务器操作模式
|
||||
|
||||
```typescript
|
||||
'use server'
|
||||
|
||||
import { z } from 'zod'
|
||||
import { createServerClient } from '@/lib/supabase/server'
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string().min(1).max(100),
|
||||
})
|
||||
|
||||
export async function createProject(formData: FormData) {
|
||||
const parsed = schema.safeParse({ name: formData.get('name') })
|
||||
if (!parsed.success) {
|
||||
return { success: false, error: parsed.error.flatten() }
|
||||
}
|
||||
|
||||
const supabase = await createServerClient()
|
||||
const { data: { user } } = await supabase.auth.getUser()
|
||||
if (!user) return { success: false, error: 'Unauthorized' }
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('projects')
|
||||
.insert({ name: parsed.data.name, user_id: user.id })
|
||||
.select('id, name, created_at')
|
||||
.single()
|
||||
|
||||
if (error) return { success: false, error: 'Failed to create project' }
|
||||
return { success: true, data }
|
||||
}
|
||||
```
|
||||
|
||||
## 环境变量
|
||||
|
||||
```bash
|
||||
# Supabase
|
||||
NEXT_PUBLIC_SUPABASE_URL=
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=
|
||||
SUPABASE_SERVICE_ROLE_KEY= # Server-only, never expose to client
|
||||
|
||||
# Stripe
|
||||
STRIPE_SECRET_KEY=
|
||||
STRIPE_WEBHOOK_SECRET=
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
|
||||
|
||||
# App
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
```
|
||||
|
||||
## 测试策略
|
||||
|
||||
```bash
|
||||
/tdd # Unit + integration tests for new features
|
||||
/e2e # Playwright tests for auth flow, billing, dashboard
|
||||
/test-coverage # Verify 80%+ coverage
|
||||
```
|
||||
|
||||
### 关键的端到端测试流程
|
||||
|
||||
1. 注册 → 邮箱验证 → 创建第一个项目
|
||||
2. 登录 → 仪表盘 → CRUD 操作
|
||||
3. 升级计划 → Stripe 结账 → 订阅激活
|
||||
4. Webhook:订阅取消 → 降级到免费层
|
||||
|
||||
## ECC 工作流
|
||||
|
||||
```bash
|
||||
# Planning a feature
|
||||
/plan "Add team invitations with email notifications"
|
||||
|
||||
# Developing with TDD
|
||||
/tdd
|
||||
|
||||
# Before committing
|
||||
/code-review
|
||||
/security-scan
|
||||
|
||||
# Before release
|
||||
/e2e
|
||||
/test-coverage
|
||||
```
|
||||
|
||||
## Git 工作流
|
||||
|
||||
* `feat:` 新功能,`fix:` 错误修复,`refactor:` 代码变更
|
||||
* 从 `main` 创建功能分支,需要 PR
|
||||
* CI 运行:代码检查、类型检查、单元测试、端到端测试
|
||||
* 部署:在 PR 上部署到 Vercel 预览环境,在合并到 `main` 时部署到生产环境
|
||||
Reference in New Issue
Block a user