初始化模版工程
This commit is contained in:
213
README.md
Normal file
213
README.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# vibe-next-template
|
||||
|
||||
Nova Agent 示例模板工程。[Nova](https://nova.betteryeah.com) 是一个创建 Agent 的 SaaS 产品,可以通过 Nova 创建能够操作沙箱、浏览器、文件系统的专业 Agent。本项目基于 Next.js 提供完整的 Agent 聊天前端,包含消息流、事件列表、会话管理与文件上传能力。
|
||||
|
||||
项目中 `.nova/config.json` 内置了一个示例 Agent,如需基于本模板开发自己的应用,请在 Nova 平台创建 Agent 并替换配置。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Next.js 16(App Router) / React 19 / TypeScript 5
|
||||
- Tailwind CSS 4 / Radix UI / Lucide React Icons
|
||||
- Zustand(状态管理)
|
||||
- 自定义 WebSocket 客户端(实时通信)
|
||||
- 自定义 HTTPClient(Fetch 封装,支持拦截器)
|
||||
- Drizzle ORM + PostgreSQL
|
||||
- Winston(日志,生产环境按天轮转)
|
||||
- Shiki(代码高亮)/ React Markdown + Remark GFM
|
||||
|
||||
## 快速开始
|
||||
|
||||
1. 安装依赖:`pnpm install`
|
||||
2. `.env`环境变量文件已经初始化完成了,所以设计环境变量的需要在这里修改
|
||||
3. (可选)修改 `.nova/config.json`,替换为你自己的 Agent
|
||||
4. 启动开发服务器:`pnpm dev`(端口 13000)
|
||||
|
||||
## Agent 配置
|
||||
|
||||
Agent 配置存放在 `.nova/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": [
|
||||
{
|
||||
"agent_id": "70da765f2d42490ca574d72dce4d24fe",
|
||||
"agent_name": "Nova示例Agent",
|
||||
"agent_description": "一个Nova的Agent示例,包含Agent具备的通用功能,文件操作、沙箱环境执行、浏览器操作等等。"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `agents` 数组中的第一个 Agent 作为默认使用的 Agent
|
||||
- 在 Nova 平台创建新 Agent 后,将 `agent_id`、`agent_name`、`agent_description` 替换为新 Agent 的信息
|
||||
- 配置通过 `app/api/nova-config.ts` 的 `getDefaultAgentId()` 在服务端读取,启动后缓存
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
.nova/
|
||||
config.json # Agent 配置(agent_id, agent_name, agent_description)
|
||||
|
||||
app/
|
||||
layout.tsx # 根布局(Geist Sans/Mono 字体)
|
||||
page.tsx # 主入口,初始化聊天连接
|
||||
globals.css # 全局样式
|
||||
api/ # Next.js API 路由(BFF 层,代理 Nova 平台 API)
|
||||
oapi-client.ts # 共享的 Nova OpenAPI HTTPClient 实例
|
||||
nova-config.ts # 读取 .nova/config.json,提供 getDefaultAgentId()
|
||||
info/route.ts # 返回客户端配置:agentId, conversationId, wssUrl, token
|
||||
chat/event/route.ts # 代理获取聊天事件历史
|
||||
chat/stop/route.ts # 停止当前任务
|
||||
conversation/route.ts # 会话列表
|
||||
file/upload/route.ts # 文件上传
|
||||
file/sign/route.ts # 文件签名 URL
|
||||
health/route.ts # 健康检查
|
||||
|
||||
components/
|
||||
nova-sdk/ # 核心聊天 SDK
|
||||
types.ts # 核心类型定义(ApiEvent, PlatformConfig, TaskArtifact 等)
|
||||
websocket.ts # WebSocket 客户端(自动重连、心跳、离线队列)
|
||||
store/
|
||||
useNovaStore.ts # Zustand 全局状态(events, artifacts, ws 状态, loading)
|
||||
context/
|
||||
context.ts # NovaContext 定义(HTTPClient + getArtifactUrl)
|
||||
Provider.tsx # Context Provider
|
||||
useNova.ts # useContext hook
|
||||
hooks/
|
||||
useNovaChatLogic.ts # 主编排 hook,组合所有子 hook
|
||||
useNovaEvents.ts # WebSocket + 历史事件获取
|
||||
useNovaService.ts # 从 platformConfig 创建 HTTPClient
|
||||
useMessageSender.ts # 通过 WebSocket 发送消息
|
||||
useEventProcessor.ts # ApiEvent → UI 消息转换
|
||||
useBuildConversationConnect.ts # 初始化:获取 agentId、conversationId
|
||||
useFileUploader.ts # 文件上传逻辑
|
||||
useAttachmentHandlers.ts # 附件点击处理
|
||||
useMessageScroll.ts # 消息列表自动滚动
|
||||
usePanelState.ts # 产物面板开关
|
||||
useSize.ts # 响应式尺寸
|
||||
nova-chat/ # 聊天主组件
|
||||
index.tsx # NovaChat(包裹 Provider、Header、MessageList、Input、TaskPanel)
|
||||
ChatHeader.tsx # 聊天头部
|
||||
ChatInputArea.tsx # 输入区域
|
||||
message-list/ # 消息列表
|
||||
index.tsx # MessageList
|
||||
MessageItem.tsx # 单条消息
|
||||
AttachmentItem.tsx # 文件附件
|
||||
ImageAttachmentItem.tsx # 图片附件
|
||||
ToolCallAction.tsx # 工具调用展示
|
||||
message-input/ # 消息输入
|
||||
index.tsx # 输入组件
|
||||
FilePreviewList.tsx # 文件预览列表
|
||||
task-panel/ # 产物预览面板
|
||||
index.tsx # TaskPanel(侧边栏,50% 宽度)
|
||||
ArtifactList.tsx # 产物列表
|
||||
ArtifactPreview.tsx # 产物预览
|
||||
Preview/ # 各类预览组件(Markdown、代码、PPT、工具调用)
|
||||
ui/ # 基础 UI 组件(Shadcn 风格)
|
||||
button.tsx, dialog.tsx, scroll-area.tsx, image.tsx, image-preview.tsx
|
||||
|
||||
http/
|
||||
index.ts # HTTPClient 类(Fetch 封装)
|
||||
http.ts # 底层 fetch 包装,支持 onRequest/onResponse/onError 钩子
|
||||
type.ts # HTTP 类型定义
|
||||
|
||||
db/
|
||||
index.ts # Drizzle ORM 连接(server-only,全局单例)
|
||||
|
||||
utils/
|
||||
logger.ts # Winston 日志(开发→终端,生产→按天文件轮转)
|
||||
cn.ts # clsx + tailwind-merge 工具函数
|
||||
```
|
||||
|
||||
## 数据流
|
||||
|
||||
```
|
||||
app/page.tsx
|
||||
│ useBuildConversationConnect() → GET /api/info
|
||||
│ 获取 agentId(来自 .nova/config.json), conversationId, platformConfig
|
||||
▼
|
||||
NovaChat 组件
|
||||
│ useNovaChatLogic() 编排所有子 hook
|
||||
├── useNovaEvents() → WebSocket 连接 + GET /api/chat/event 加载历史
|
||||
├── useMessageSender() → WebSocket 发送消息
|
||||
└── useEventProcessor()→ 事件转 UI 消息
|
||||
▼
|
||||
Zustand Store (useNovaStore)
|
||||
│ events[], artifacts[](从 events 自动提取), loading, ws 状态
|
||||
▼
|
||||
MessageList / TaskPanel 渲染
|
||||
```
|
||||
|
||||
## API 路由
|
||||
|
||||
| 路由 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/info` | GET | 返回客户端配置:agentId, conversationId, wssUrl, token |
|
||||
| `/api/chat/event` | GET | 代理 Nova `/chat/event_list`,获取事件历史 |
|
||||
| `/api/chat/stop` | POST | 停止当前任务 |
|
||||
| `/api/conversation` | GET | 会话列表 |
|
||||
| `/api/file/upload` | POST | 上传文件 |
|
||||
| `/api/file/sign` | POST | 获取文件签名 URL |
|
||||
| `/api/health` | GET | 健康检查 |
|
||||
|
||||
所有 API 路由通过 `app/api/oapi-client.ts` 中的共享 HTTPClient 代理到 Nova 平台,请求头自动注入 `Tenant-Id` 和 `Authorization`。Agent ID 从 `.nova/config.json` 读取,不再依赖环境变量。
|
||||
|
||||
## WebSocket 消息格式
|
||||
|
||||
- 心跳:`{ message_type: 'ping' }`
|
||||
- 聊天消息:`{ message_type: 'chat', conversation_id, content, ... }`
|
||||
- 切换会话:`{ message_type: 'switch_conversation', conversation_id }`
|
||||
|
||||
WebSocket 客户端内置自动重连(可配置次数/间隔)、心跳检测、网络状态监听、页面可见性恢复。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
pnpm install # 安装依赖
|
||||
pnpm dev # 启动开发服务器(端口 13000)
|
||||
pnpm build # 生产构建
|
||||
pnpm start # 生产运行(端口 13000)
|
||||
pnpm lint # ESLint 检查并自动修复
|
||||
pnpm db:generate # 生成 Drizzle 迁移
|
||||
pnpm db:migrate # 执行数据库迁移
|
||||
pnpm db:studio # 打开 Drizzle Studio GUI
|
||||
```
|
||||
|
||||
## 环境变量
|
||||
|
||||
参考 `.env.example`,在项目根目录创建 `.env.local`:
|
||||
|
||||
| 变量名 | 说明 |
|
||||
|--------|------|
|
||||
| `DATABASE_URL` | PostgreSQL 连接地址(Drizzle 使用) |
|
||||
| `LOG_DIR` | 生产环境日志目录(默认 `./logs`,按天切分) |
|
||||
| `NOVA_BASE_URL` | Nova OpenAPI 服务地址 |
|
||||
| `NOVA_TENANT_ID` | 租户 ID(请求头 `Tenant-Id`) |
|
||||
| `NOVA_ACCESS_KEY` | 鉴权密钥(请求头 `Authorization`) |
|
||||
|
||||
> Agent ID 已从环境变量迁移至 `.nova/config.json`,无需在 `.env.local` 中配置。
|
||||
|
||||
## 代码约定
|
||||
|
||||
- **路径别名**:`@/*` → `./*`(tsconfig paths)
|
||||
- **组件**:PascalCase 文件名,使用 `React.memo()` 优化
|
||||
- **Hooks**:camelCase,`use*` 前缀
|
||||
- **工具函数**:camelCase
|
||||
- **常量枚举**:UPPER_SNAKE_CASE(如 `EventType`, `TaskStatus`)
|
||||
- **样式**:Tailwind CSS 工具类,通过 `cn()` 函数(`utils/cn.ts`)合并类名
|
||||
- **回调稳定性**:使用 ahooks 的 `useMemoizedFn()` 或 `useCallback()`
|
||||
- **异步状态**:`queueMicrotask()` 延迟更新,避免 React 批量更新冲突
|
||||
- **Store 去重**:Zustand store 按 `event_id` 去重事件
|
||||
- **日志**:开发环境输出到终端,生产环境按天写入 `${LOG_DIR}/app-YYYY-MM-DD.log`
|
||||
- **数据库**:所有操作通过 Drizzle ORM,`import { db } from '@/db'`,仅限 server 端
|
||||
- **UI 错误提示**:使用中文(如 "Chat 不可用,请检查项目 .env 配置")
|
||||
- **请求头约定**:`X-Locale=zh`, `X-Region=CN`
|
||||
|
||||
## 注意事项
|
||||
|
||||
- `next.config.ts` 中 `reactStrictMode: false`
|
||||
- `.next/` 目录为构建产物,不要手动修改
|
||||
- 数据库连接使用全局单例模式,`db/index.ts` 标记了 `server-only`
|
||||
- 聊天初始化流程:先请求 `/api/info` 获取配置 → 再建立 WebSocket → 加载历史事件
|
||||
- 产物提取来源:`attachments`, `attachment_files`, `files`, `generated_files`,按文件 key 去重
|
||||
- 文件签名通过 `getArtifactUrl()` 懒加载,POST `/file/sign`,失败时 fallback 到原始路径
|
||||
Reference in New Issue
Block a user