feat(schemas): add repository settings and user restore token fields
- Add allow_forking, allow_merge_commit, allow_rebase_merge, allow_squash_merge fields to repo schemas - Add delete_branch_on_merge field to repository models and schemas - Add has_issues, has_pull_requests, has_wiki, homepage fields to repo schemas - Add topics array field to repository schemas and models - Add restore_token_expires_at and restore_token_hash fields to user schemas - Remove UserAvatarResponse and UploadUserAvatarParams schemas completely - Update CreateRepoParams and UpdateRepoParams with new repository settings - Modify CreateTemplateParams and UpdateTemplateParams with notification template fields - Remove description from SetBranchProtectionParams schema - Delete App.css and auth.css files completely - Update App.tsx with routing migration notes
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
## Language
|
||||
Always respond in Chinese (中文). Use the user's language for all conversations and explanations. Code, commands, and technical terms can remain in English.
|
||||
|
||||
页面内容(UI 文案、标签、提示、错误信息)默认使用 **英语**。仅当用户明确要求时才做多语言。
|
||||
|
||||
# Project Guidelines — appwebks
|
||||
|
||||
## Tech Stack
|
||||
- **Runtime**: Bun (packageManager, scripts, lockfile)
|
||||
- **Framework**: React 19, TypeScript 6.0, Vite 8.0
|
||||
- **Styling**: Tailwind CSS 4.3 + clsx + tailwind-merge(CVA 变体模式)
|
||||
- **State**: @tanstack/react-query v5(服务端状态)+ URL search/path params(客户端状态)
|
||||
- **Routing**: react-router-dom v7
|
||||
- **HTTP**: ky(优先)+ axios
|
||||
- **Validation**: zod v4
|
||||
- **Icons**: lucide-react
|
||||
|
||||
## Project Structure
|
||||
```
|
||||
src/
|
||||
pages/ # 页面组件,每个 page 一个文件夹
|
||||
components/ # 共享组件,按功能域分文件夹
|
||||
hooks/ # 自定义 hooks(含 react-query hooks)
|
||||
client/ # 自动生成 API 代码(只读!)
|
||||
lib/ # 工具函数、常量
|
||||
types/ # 共享类型定义
|
||||
```
|
||||
|
||||
## Routing
|
||||
|
||||
使用 React Router v7 **Data Mode**:`createBrowserRouter` + Route Object 配置。
|
||||
|
||||
- 路由配置集中在 `src/routes.ts`,通过 `createBrowserRouter` 组装
|
||||
- 每个路由模块(Component + loader + action)为独立文件,放在 `src/pages/` 下
|
||||
- 路由文件不容纳 JSX,只 export RouteObject[]
|
||||
|
||||
```ts
|
||||
// src/routes.ts — 路由配置,只组装不渲染
|
||||
import { createBrowserRouter } from 'react-router';
|
||||
import { RepoListPage } from '@/pages/RepoList/RepoListPage';
|
||||
import { RepoDetailPage } from '@/pages/RepoDetail/RepoDetailPage';
|
||||
import { RootLayout } from '@/pages/RootLayout';
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
Component: RootLayout,
|
||||
children: [
|
||||
{ index: true, Component: RepoListPage },
|
||||
{ path: 'repo/:id', Component: RepoDetailPage },
|
||||
],
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
```tsx
|
||||
// src/pages/RepoList/RepoListPage.tsx — 路由模块
|
||||
export function RepoListPage() {
|
||||
const { data } = useRepoList();
|
||||
return <div>...</div>;
|
||||
}
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
### File Naming
|
||||
- React 组件文件:**PascalCase**(`Button.tsx`、`UserList.tsx`)
|
||||
- Hooks / utils / types 文件:**camelCase**(`useAuth.ts`、`formatDate.ts`)
|
||||
- 页面文件夹:**PascalCase**(`UserProfile/`、`RepoList/`)
|
||||
|
||||
### Component Conventions
|
||||
- 组件用 **function 声明**,不用 arrow function
|
||||
- Props 类型用 **interface**,写在组件文件顶部,不单独导出:
|
||||
```tsx
|
||||
interface ButtonProps {
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
variant?: 'primary' | 'secondary';
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Button({ size = 'md', variant = 'primary', children }: ButtonProps) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- 原生 HTML 元素 Props 用 `React.ComponentProps<'button'>` 派生
|
||||
- 单文件不超过 **200 行**,函数不超过 **50 行**
|
||||
- 复杂逻辑抽成 hooks
|
||||
|
||||
### Import Order
|
||||
Biome 的 `organizeImports` 会自动按以下顺序排列 import,组间空行分隔:
|
||||
1. React / React Router / react-query 等核心库
|
||||
2. 第三方 UI / 工具库(lucide-react、clsx、zod...)
|
||||
3. `@/` 别名导入
|
||||
4. 相对路径导入(`./`、`../`)
|
||||
|
||||
```tsx
|
||||
import { useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Search, Plus } from 'lucide-react';
|
||||
import { clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import { repoService } from '@/client/services/RepoService';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
|
||||
import { useRepoList } from './useRepoList';
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
### Tailwind + CVA
|
||||
用 `clsx` + `tailwind-merge` 管理组件变体,按 CVA 模式组织:
|
||||
|
||||
```tsx
|
||||
import { clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
function cn(...inputs: (string | undefined | false | null)[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
const variants = {
|
||||
size: {
|
||||
sm: 'px-2 py-1 text-sm',
|
||||
md: 'px-4 py-2 text-base',
|
||||
lg: 'px-6 py-3 text-lg',
|
||||
},
|
||||
variant: {
|
||||
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
||||
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
|
||||
},
|
||||
} as const;
|
||||
|
||||
interface ButtonProps {
|
||||
size?: keyof typeof variants.size;
|
||||
variant?: keyof typeof variants.variant;
|
||||
}
|
||||
|
||||
export function Button({ size = 'md', variant = 'primary', ... }: ButtonProps) {
|
||||
return <button className={cn(variants.size[size], variants.variant[variant])} ... />;
|
||||
}
|
||||
```
|
||||
|
||||
- **禁止新增任何 `.css` 文件**。项目只保留一个全局入口 CSS(`src/index.css`),仅用于:
|
||||
- `@import "tailwindcss"`
|
||||
- CSS 自定义属性(主题变量)
|
||||
- 全局 reset
|
||||
- 组件样式**必须**使用 Tailwind 原子类,不得引用局部 CSS 文件
|
||||
- 禁止 `@apply` 抽取类名
|
||||
- `cn()` 函数放在 `@/lib/cn.ts` 中统一导入
|
||||
|
||||
## State Management
|
||||
|
||||
### URL-Driven State
|
||||
- UI 状态(分页、筛选、排序、tab 切换)**必须**编码到 URL search/path params
|
||||
- 用 `useSearchParams()` 读写,组件内部 `useState` 最小化
|
||||
- 好处:可分享、可书签、浏览器前进后退自然工作
|
||||
|
||||
### Server State (react-query)
|
||||
- 所有 API 调用必须通过 react-query hooks(`useQuery` / `useMutation`)
|
||||
- 禁止在组件中直接调用 `src/client/` 的服务方法
|
||||
- Query key 约定:`['domain', 'action', ...params]`,如 `['repos', 'list', { page, q }]`
|
||||
|
||||
**注意**:TanStack Query v5 已移除 `useQuery` 上的 `onError` / `onSuccess` / `onSettled` 回调:
|
||||
- `useQuery` 错误处理:组件内检查 `error` 状态,或全局通过 `QueryClient.setDefaultOptions` 配置
|
||||
- `useMutation` 错误处理:`onError` 回调仍然可用
|
||||
|
||||
```tsx
|
||||
// useQuery — 错误在 error 字段上处理
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
const { data, error, isLoading } = useQuery({
|
||||
queryKey: ['repos', 'list', { page }],
|
||||
queryFn: () => repoService.listRepos({ page }),
|
||||
});
|
||||
|
||||
// useMutation — onError 仍然可用
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: (params) => repoService.createRepo(params),
|
||||
onError: (error) => toast.error(error.message),
|
||||
});
|
||||
```
|
||||
|
||||
## API Layer
|
||||
|
||||
### Generated Code(src/client/)
|
||||
- **视为只读**,禁止手动修改任何 `src/client/` 下的文件
|
||||
- 需要变更时修改 `openapi.json` 或 `genapi.js`,然后运行 `bun run genapi` 重新生成
|
||||
|
||||
### Custom Hooks 封装
|
||||
每个业务域在 `src/hooks/` 下创建对应的 react-query hook 文件:
|
||||
|
||||
```
|
||||
src/hooks/
|
||||
useRepoList.ts # useQuery: repo list + filters
|
||||
useRepoDetail.ts # useQuery: single repo
|
||||
useCreateRepo.ts # useMutation: create repo
|
||||
useBranchList.ts # useQuery: branch list
|
||||
```
|
||||
|
||||
hook 职责:
|
||||
- 组装 queryKey(如 `['repos', 'list', { page, q }]`)
|
||||
- 调用 `src/client/` 服务方法
|
||||
- 转换入参(筛选条件 → API 参数)、归一化错误
|
||||
- **默认返回 React Query 原始结果**:不隐藏 `isPending` / `isFetching` / `error` / `refetch` 等状态
|
||||
|
||||
```tsx
|
||||
// src/hooks/useRepoList.ts — 典型范式
|
||||
import { useQuery, keepPreviousData } from '@tanstack/react-query';
|
||||
import { repoService } from '@/client/services/RepoService';
|
||||
|
||||
export function useRepoList(filters: RepoFilters) {
|
||||
return useQuery({
|
||||
queryKey: ['repos', 'list', filters],
|
||||
queryFn: ({ signal }) => repoService.listRepos({ ...filters }, { signal }),
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
}
|
||||
// 组件侧直接解构:const { data, isPending, isFetching, error, refetch } = useRepoList(filters);
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
- 全局 **ErrorBoundary** 捕获 React 渲染错误
|
||||
- `useMutation` 的 API 错误通过 `onError` 回调统一分发 toast
|
||||
- `useQuery` 的 API 错误通过组件内判断 `error` 状态处理,或使用全局 `throwOnError` + ErrorBoundary
|
||||
- 禁止在组件中 try/catch API 调用后自己做 UI 错误展示
|
||||
|
||||
```tsx
|
||||
// 全局 QueryClient 配置(在 App 入口)
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
throwOnError: true, // Query 抛错 → ErrorBoundary 捕获
|
||||
},
|
||||
mutations: {
|
||||
onError: (error) => toast.error(error.message),
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Git
|
||||
|
||||
### Branching
|
||||
- **Trunk-based**:直接在 `main` 上做小而频繁的提交
|
||||
- 不做长期 feature 分支,未完成功能用 feature flag 或路由守卫控制
|
||||
- 需要时可以从 main 拉短期分支,但合入后立即删除
|
||||
|
||||
### Commits
|
||||
- **Conventional Commits**:`feat` / `fix` / `refactor` / `docs` / `test` / `chore`
|
||||
- 原子提交:一个 commit 只做一件事
|
||||
- 禁止 force push 到 main
|
||||
|
||||
## Formatter(Biome)
|
||||
项目使用 **Biome** 作为唯一的格式化 & lint 工具。配置见 `biome.json`。
|
||||
|
||||
### 格式化规则
|
||||
| 配置项 | 值 |
|
||||
|--------|-----|
|
||||
| 缩进 | 2 空格 |
|
||||
| 引号 | 单引号(JS/TS),双引号(JSX) |
|
||||
| 分号 | 总是 |
|
||||
| 尾逗号 | 总是 |
|
||||
| 行宽 | 100 |
|
||||
| 换行符 | LF |
|
||||
| 箭头函数括号 | 总是保留 |
|
||||
|
||||
### 命令
|
||||
```bash
|
||||
bun run format # 格式化 src/ 下所有文件
|
||||
bun run lint # 仅 lint 检查,不修改
|
||||
bun run check # 格式化 + lint,写入修复
|
||||
```
|
||||
|
||||
### Lint 规则
|
||||
- 启用 **recommended** 规则集
|
||||
- `noUnusedVariables`: error — 禁止未使用变量
|
||||
- `useHookAtTopLevel`: error — React hooks 必须在顶层调用
|
||||
- `noParameterAssign`: error — 禁止给参数重新赋值
|
||||
- `useConst`: error — 能用 const 就别用 let
|
||||
- `useTemplate`: warn — 字符串拼接改用模板字符串
|
||||
|
||||
## Quality Gate
|
||||
AI 助手完成代码修改后,**必须依次运行以下命令**确保通过:
|
||||
|
||||
```bash
|
||||
bun run check # Biome 格式化 + lint
|
||||
bun run build # TypeScript 类型检查 + Vite 构建
|
||||
```
|
||||
|
||||
任一步骤失败不允许提交。
|
||||
|
||||
## Testing
|
||||
当前阶段 **不强制写测试**,快速迭代优先。后续需要时优先补关键路径的单元测试(vitest)。
|
||||
|
||||
## Environment Variables
|
||||
- **禁止**在业务代码中直接读取 `import.meta.env.*`
|
||||
- 所有环境变量必须通过 `@/lib/env` 集中访问,统一提供默认值与类型安全
|
||||
|
||||
```ts
|
||||
// src/lib/env.ts
|
||||
export const env = {
|
||||
API_BASE_URL: import.meta.env.VITE_API_BASE_URL ?? '/api',
|
||||
DEV: import.meta.env.DEV,
|
||||
MODE: import.meta.env.MODE,
|
||||
} as const;
|
||||
```
|
||||
|
||||
## Import Discipline
|
||||
- **禁止**页面组件直接导入 `@/client/**`
|
||||
- 只有 `src/hooks/**` 或 `src/features/**/api/**` 可以导入 generated client
|
||||
- 页面通过自定义 hook 访问 API 数据,不接触底层 client
|
||||
|
||||
```
|
||||
✅ src/hooks/useRepoList.ts → import { RepoService } from '@/client/services/RepoService'
|
||||
✅ src/features/repos/api.ts → import { RepoService } from '@/client/services/RepoService'
|
||||
❌ src/pages/RepoList.tsx → import { RepoService } from '@/client/services/RepoService'
|
||||
```
|
||||
|
||||
## Safety
|
||||
- 禁止硬编码 secrets / API keys / tokens
|
||||
- 所有用户输入必须验证(zod schema)
|
||||
- 错误必须显式处理,禁止 silent failure
|
||||
- 禁止使用 `// ── xxxx ──────────` 分隔注释
|
||||
|
||||
## Reference
|
||||
- `hooks.txt` — 项目已安装依赖的完整 Hooks 清单
|
||||
- `biome.json` — Biome 格式化 / Lint 配置
|
||||
- `openapi.json` — API Schema(用于生成 `src/client/`)
|
||||
- `genapi.js` — API 代码生成脚本(`bun run genapi`)
|
||||
Reference in New Issue
Block a user