更新构建文件 & 添加项目提示文档

This commit is contained in:
2025-12-15 12:50:45 +08:00
parent 5db63273bc
commit d62367f37e
3 changed files with 152 additions and 16 deletions

View File

@@ -1,6 +1,10 @@
.idea/ .idea/
.vscode/ .vscode/
.zed/
./git
./github
node_modules/ node_modules/
.next/ .next/
.env .env

139
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,139 @@
# 蓝狐代理 (Lanhu Proxy) - AI 开发助手指南
## 项目概述
Next.js 15+ 代理服务平台,包含公开营销站点与用户管理后台。使用 Bun 运行时React 19 Server Componentsshadcn/ui 组件库。
**核心业务**: IP 代理购买、提取、白名单管理、实名认证、充值计费
## 架构模式
### 三层数据流架构
1. **Server Actions** (`src/actions/*.ts`) - 所有 API 调用统一入口
- 每个文件对应一个业务域 (auth, bill, ip, resource 等)
- 通过 `base.ts` 的三种调用模式访问后端:
- `callPublic()` - 无需认证的公开接口
- `callByDevice()` - 使用 CLIENT_ID/CLIENT_SECRET 的设备级认证
- `callByUser()` - 使用用户会话 token 的用户级认证
- 所有调用都用 `cache()` 包装以支持 React 缓存
- 返回统一的 `ApiResponse<T>` 类型
2. **Zustand Store** (`src/components/stores/*.tsx`) - 客户端状态管理
- 异步初始化模式: 通过 Promise 传递服务端数据到客户端
- `ProfileStoreProvider` 在根布局初始化用户资料
- 每个 store 导出: `createXxxStore()`, `XxxStoreProvider`, `useXxxStore()`
- 示例: `profile.tsx` 使用 `profile: Promise<User | null>` 实现服务端到客户端水合
3. **认证流程** - Token 刷新在 middleware 自动处理
- `src/proxy.ts` (middleware) 检测 `auth_token` 过期时自动调用 `refreshAuth()`
- 访问 `/admin` 路径无 token 时重定向到 `/login?redirect=`
- Cookies: `auth_token` (访问令牌), `auth_refresh` (刷新令牌)
### 路由组织
- **(home)** - 公开营销站点,包含产品介绍、文档中心、定制服务
- **(auth)** - 登录、实名认证、隐私协议、兑换码
- **admin** - 用户后台,包含仪表盘、账单、资源管理、白名单、提取功能
- **(api)/proxies** - API 端点路由
括号路由 `(group)` 不影响 URL 路径,仅用于组织布局层级。
### 验证码集成 (Cap.js)
- `src/lib/cap/index.ts` 自定义存储实现,使用 HMAC 签名的 Cookie 而非文件系统
- `getCap()` 返回配置好的 Cap 实例
- Challenge 和 token 都存储在 httpOnly cookies 中
- `sign()` / `verify()` 基于 CLIENT_SECRET 的签名验证
## 开发工作流
### 命令速查
```bash
bun install # 安装依赖
bun dev # 开发服务器 (0.0.0.0:3000 + Turbopack)
bun run build # 生产构建 (Turbopack)
bun start # 启动生产服务器
bun run lint # ESLint 自动修复
bun run check # TypeScript 类型检查
```
### 组件开发模式
**shadcn/ui 组件** (`src/components/ui/*.tsx`)
- 使用 `components.json` 配置New York 风格
- 通过 CLI 添加: `bunx shadcn@latest add <component>`
- 所有组件使用 `merge()` (twMerge 别名) 合并类名
**业务组件** (`src/components/composites/*`)
- 复杂表单、对话框、支付组件等放在此目录
- 标记 `'use client'` 的客户端组件
- 示例: `purchase/index.tsx` 使用 URL 查询参数控制 Tab 状态
**工具函数**
- `merge(...inputs)` - 合并 Tailwind 类名 (来自 `@/lib/utils`)
- `Children` 类型 - 标准 `{children: ReactNode}` 类型别名
### MDX 文档系统
- 文档源文件: `src/docs/*.mdx`
- 配置: `next.config.ts` 集成 `@next/mdx`, `remark-gfm`, `rehype-highlight`
- 页面扩展名: `.md`, `.mdx`, `.js`, `.jsx`, `.ts`, `.tsx`
- 布局: `src/app/(home)/docs/layout.tsx` 提供面包屑和侧边栏
## 项目特定约定
### 类型定义模式
```typescript
// 从 server action 推断请求/响应类型
type ExtraReq<T> = T extends (...args: infer P) => unknown ? P[0] : never
type ExtraResp<T> = Awaited<ReturnType<T>> extends ApiResponse<infer D> ? D : never
// 使用示例
type LoginRequest = ExtraReq<typeof login>
type UserProfile = ExtraResp<typeof getProfile>
```
### 错误处理
- Server actions 返回 `ApiResponse<T>`:
- 成功: `{success: true, data: T}`
- 失败: `{success: false, status: number, message: string}`
- 使用 `sonner` (Toaster) 显示用户提示
- 预定义错误: `UnauthorizedError` (401 场景)
### 样式规范
- 主色系: `primary-*`, `card`, `primary-muted`
- 响应式断点: 使用 Tailwind 默认 (`sm:`, `md:`, `lg:`)
- 布局: 首页使用 `bg-blue-50` 背景,后台使用 `bg-card` 和圆角遮罩 (见 `admin/layout.tsx`)
## 关键文件参考
- [src/actions/base.ts](src/actions/base.ts) - API 调用核心抽象
- [src/proxy.ts](src/proxy.ts) - 认证 middleware 和 token 刷新逻辑
- [src/components/stores/profile.tsx](src/components/stores/profile.tsx) - 异步 store 初始化模式
- [src/lib/cap/index.ts](src/lib/cap/index.ts) - Cap.js 自定义存储实现
- [src/app/layout.tsx](src/app/layout.tsx) - Store providers 和异步初始化
- [next.config.ts](next.config.ts) - MDX、React Compiler、Turbopack 配置
## 环境变量
需要配置:
- `NEXT_PUBLIC_API_BASE_URL` - 后端 API 地址
- `CLIENT_ID`, `CLIENT_SECRET` - OAuth2 设备认证凭据
## 部署
- 使用 `output: 'standalone'` 模式
- Dockerfile 多阶段构建 (Bun 1.2.19 Alpine)
- 生产端口: 3000
## 待改进事项 (来自 README)
- 考虑使用 SWR/React Query 替代当前的 server cache + zustand 混合方案
- 手机端优化: 购买和提取页面需一页展示全部内容
- IP 管理按长短效分页
- 后台导航结构重组 (当前过于分散)

View File

@@ -1,37 +1,30 @@
FROM node:22.14-alpine AS base FROM oven/bun:1.2.19-alpine AS base
# 运行时依赖
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add cairo pango giflib ttf-dejavu
# 依赖缓存阶段 # 依赖缓存阶段
FROM base AS deps FROM base AS deps
WORKDIR /app WORKDIR /app
RUN apk add build-base g++ cairo-dev pango-dev giflib-dev COPY package.json bun.lock ./
RUN bun config set registry https://registry.npmmirror.com
COPY package.json pnpm-lock.yaml* ./ RUN bun install --frozen-lockfile
RUN corepack enable pnpm
RUN pnpm config set registry https://registry.npmmirror.com
RUN pnpm install --frozen-lockfile
# 构建阶段 # 构建阶段
FROM base AS builder FROM base AS builder
WORKDIR /app WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY .. . COPY . .
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
RUN corepack enable pnpm RUN bun run build
RUN pnpm run build
# 生产阶段 # 生产阶段
FROM base AS runner FROM base AS runner
WORKDIR /app WORKDIR /app
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs RUN adduser --system --uid 1001 nextjs
@@ -47,4 +40,4 @@ EXPOSE 3000
ENV PORT=3000 ENV PORT=3000
ENV HOSTNAME="0.0.0.0" ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"] CMD ["bun", "server.js"]