danswer(Onyx)Web 前端开发指南:Next.js 本地开发、云端后端联调与 Playwright E2E 测试全解析
【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer
本文以 web/README.md 为骨架,结合 Onyx(原 danswer)仓库中的实际源码与配置,系统讲解
web/前端模块的本地开发环境搭建、如何用INTERNAL_URL与DEBUG_AUTH_COOKIE对接云端后端,以及基于 Playwright 的端到端测试体系。读完你既能在一分钟内启动本地开发服务器,也能配置一套安全的远程后端联调环境,并掌握本项目 Playwright 测试的组织规范与调试技巧。
一、web/前端模块概览
web/是 Onyx 项目的前端应用目录,基于Next.js 16.3.3与React 19.2.8构建,采用 App Router 架构,代码位于 web/src 下(app/、components/、lib/、sections/、views/等)。项目还通过 npm workspaces 托管了两个内部共享包(见 web/package.json):
@onyx-ai/opal(web/lib/opal):设计系统与 UI 组件库,负责侧边栏断点等视觉规范;@onyx-ai/shared(web/lib/shared):前后端共享的类型与工具。
从依赖清单(web/package.json)可以看出该前端的业务广度:@radix-ui/*系列提供无障碍原语组件,react-markdown+rehype-*+remark-*负责聊天消息的 Markdown/数学公式渲染与消毒,recharts承担使用量统计图表,@sentry/nextjs负责错误监控,swr与zustand分别管理服务端数据缓存和客户端状态。
启动整个栈并非只有前端一件事:聊天、搜索、文档索引等能力全部由backend/(FastAPI)提供,前端默认代理到本地 8080 端口(详见下文"后端地址的解析链路")。
二、环境准备:安装 bun 与依赖
Onyx 前端使用 bun 作为包管理器与运行时(这也与仓库根目录、web/下分别存在bun.lock的事实一致)。首先安装 bun(详见官方安装文档),然后在web/目录下安装依赖:
cd web bun install依赖安装的细节
- 仓库通过 npm workspaces(
workspaces: ["lib/opal", "lib/shared"])把内部包软链进node_modules,因此bun install会一并处理@onyx-ai/opal与@onyx-ai/shared两个本地包。 - 切换分支后如果
package.json发生变化,项目通过 pre-commit 钩子自动重装依赖(见根目录 CONTRIBUTING.md 中 Formatting and Linting 一节),通常无需手动干预。 - Playwright 相关依赖(
@playwright/test)同样在此步骤中就位,后续bunx playwright install才能解析到仓库锁定的版本。
三、启动开发服务器
安装完成后,在web/目录执行:
bun run dev该命令对应 web/package.json 中的next dev,启动后:
- 浏览器访问http://localhost:3000即可看到应用。
- 如果 3000 端口访问异常(例如被占用或代理冲突),README 建议设置
WEB_DOMAIN环境变量指向http://127.0.0.1:3000再访问:
WEB_DOMAIN=http://127.0.0.1:3000 bun run dev后端地址的解析链路
前端默认假定本地后端运行在8080端口。这个默认值在源码中有三处体现:
- web/src/lib/constants.ts:
export const INTERNAL_URL = process.env.INTERNAL_URL || "http://localhost:8080"; - web/src/lib/utilsSS.ts:服务端请求统一拼接为
${INTERNAL_URL}${path}; - web/next.config.js:
rewrites()把/api/docs、/openapi.json等路径代理到INTERNAL_URL(未设置时同样回退到http://localhost:8080)。
因此,bun run dev配合本地backend/(FastAPI,8080)即可组成完整可用的开发环境。理解这条链路是下一步对接云端后端的基础。
四、连接云端后端:.env.local与INTERNAL_URL
日常开发中你可能不想本地起一整套后端(含数据库、向量库等),而是让前端直连远程的后端环境(如 staging 或 production)。此时需要在web/目录下(与package.json同级)创建.env.local文件:
# 让本地开发服务器指向云端后端 INTERNAL_URL=https://st-dev.onyx.app/api # 用于对远程后端做认证的调试 Cookie # 开发模式下该 Cookie 会被自动注入到 API 请求中 # 获取方式: # 1. 打开 https://st-dev.onyx.app(或你的目标后端地址)并登录 # 2. 打开 DevTools(F12)→ Application → Cookies → [你的后端域名] # 3. 找到名为 "fastapiusersauth" 的 Cookie,复制其值 # 4. 粘贴到下面(不要加引号) # 注意:该 Cookie 会过期,需要定期刷新 DEBUG_AUTH_COOKIE=你的cookie值关键行为与注意事项
- 配置文件位置:
.env.local必须放在web/目录(与package.json同级),不要放在仓库根目录。 - 修改后必须重启:创建或修改
.env.local后,需要重启开发服务器(bun run dev)才能生效。 DEBUG_AUTH_COOKIE仅开发模式生效:只有NODE_ENV=development时才被注入,生产构建完全不受影响。- 未设置
INTERNAL_URL时:前端回退到本地后端http://127.0.0.1:8080(见上文源码)。 - 不会覆盖已有 Cookie:默认情况下该机制不会覆盖已存在的同名认证 Cookie,如果你之前登录过,可能需要先清除
localhost域下的 Cookie。 - 安全红线:
.env.local中存放的是有效会话凭证,务必保持机密,绝不能提交进版本控制(该文件已列入.gitignore)。
注入机制的源码实现
DEBUG_AUTH_COOKIE的注入逻辑位于 web/src/lib/users/svcSS.ts 的processCookies()函数:
- 收集当前请求携带的所有 Cookie,拼成
name=value; name=value字符串; - 当
process.env.DEBUG_AUTH_COOKIE && process.env.NODE_ENV === "development"时,检查字符串中是否已存在认证 Cookie(Cookie 名由SERVER_SIDE_ONLY__AUTH_COOKIE_NAME决定); - 若不存在,则把
DEBUG_AUTH_COOKIE以fastapiusersauth=<value>的形式追加进请求头。
Cookie 名称默认是fastapiusersauth(FastAPI-Users 标准名称),定义于 web/src/lib/constants.ts,并且可通过AUTH_COOKIE_NAME环境变量覆盖——这样在 localhost 不同端口并行开多个 worktree 时,各前端实例可以维护彼此独立的认证 Cookie,避免串号。getCurrentUserSS()(同一文件 web/src/lib/users/svcSS.ts)通过调用后端/me接口完成服务端会话校验,注入后的 Cookie 会随该请求一并发送。
五、Playwright E2E 测试:从入门到调试
5.1 警告:测试会重置应用状态
在web/下执行测试会把应用重置为干净状态(注册测试账号、清理/重建测试数据),如果你不希望动当前数据,就不要在本地随意运行整套测试。
5.2 安装 Playwright 浏览器
先确保已执行过bun install(这样bunx会解析到node_modules中仓库锁定的 Playwright 版本,而不是临时拉取最新版),然后安装浏览器:
bun install bunx playwright install5.3 运行测试
playwright脚本在 web/package.json 中展开为playwright test:
bun run playwright只跑单个测试文件:
bun run playwright landing-page.spec.ts本地调试时可以加交互式参数,直观看到每一步在浏览器中的执行情况:
bun run playwright --ui # UI 模式,可视化管理与单步调试 bun run playwright --headed # 有头模式,弹出浏览器窗口5.4 测试结果与截图输出
web/playwright.config.ts 将输出目录配置为:
web/output/playwright/测试运行过程中会自动截图并保存到web/output/screenshots/。跨 CI 运行对比截图可使用 ODS 工具:
ods screenshot-diff compare --project admin该命令属于仓库内 ODS(Onyx Developer Suite)工具集,详见 tools/ods/README.md 的 screenshot-diff 章节。
5.5 Playwright 配置要点
从 web/playwright.config.ts 可以看到本项目测试基础设施的关键设定:
| 配置项 | 取值 | 说明 |
|---|---|---|
| 单测超时 | 100 秒 | timeout: 100000 |
| 断言超时 | 15 秒 | 降低断言抖动(expect.timeout: 15000) |
| 截图容差 | maxDiffPixelRatio: 0.01、threshold: 0.2 | 容忍抗锯齿/亚像素渲染差异 |
| CI 重试 | 2 次 | 本地 0 次(retries: process.env.CI ? 2 : 0) |
| CI 并发 | 4 个 worker | 本地默认并发(可注释切换为串行workers: 1便于调试) |
| 测试范围 | tests/e2e/*.spec.ts | 用testMatch与 Jest 测试隔离 |
| 失败追踪 | trace: "retain-on-failure" | 失败时保留 Trace 供回放 |
| 基准地址 | BASE_URL环境变量覆盖,默认http://localhost:3000 | 对应use.baseURL |
项目还定义了三个测试项目(project):
- admin:默认全量测试(
grepInvert排除@exclusive、@lite标记),桌面 Chrome、1280×720 视口,复用admin_auth.json登录态; - exclusive:仅跑带
@exclusive标记的用例,串行、单 worker,适合独立慢速场景; - lite:针对 Onyx Lite 栈(
DISABLE_VECTOR_DB=true,无 Vespa/Redis)的用例,仅跑带@lite标记的测试。
5.6 Global Setup:测试前的自动准备
测试不是裸奔的。globalSetup(web/tests/e2e/global-setup.ts)会在测试套件运行前自动完成:
- 健康检查:轮询
BASE_URL直到返回 200(最长 60 秒,每 2 秒一次,15 秒后开始告警)。若超时会抛出明确错误并提示先用ods compose dev启动前后端; - 注册测试账号:通过 API
POST /api/auth/register幂等注册管理员(admin_user@example.com)、二号管理员(admin2_user@example.com)和 8 个 worker 用户(worker0..7@example.com,凭证定义见 web/tests/e2e/constants.ts)。第一个注册的用户自动成为管理员; - API 登录并保存登录态:用 Playwright 的轻量 request context 调
POST /api/auth/login,把 Cookie 保存为admin_auth.json、admin2_auth.json、workerN_auth.json等 storage state 文件——比真实浏览器登录更快、更安静; - 消除新手引导干扰:通过
PATCH /api/user/personalization设置显示名称,关掉首次登录时"Onyx 该怎么称呼你?"的弹窗,避免它遮挡聊天界面导致测试误判; - 提升权限:把二号管理员加入默认 Admin 用户组(
/api/manage/admin/user-group+add-users); - 准备公共 LLM Provider:复用 admin 会话确保存在默认的公开 LLM Provider(许多测试——文件上传、Agent 创建等——都依赖默认 LLM 已配置)。
从 web/tests/e2e 的目录结构可以看到测试覆盖范围:admin/(管理后台、安全加固、Token 限流、SCIM、OAuth 等)、agents/、auth/、chat/、connectors/、craft/、mcp/、onboarding/、settings/等,基本对应前端全部功能面。
六、E2E 测试编写规范(给测试作者)
仓库为 e2e 测试制定了明确的硬性规则,见 web/tests/e2e/README.md,核心两条:
6.1 强制使用 Page Object Model
所有定位器与交互逻辑必须封装在 Page Object 类中(放在tests/e2e/pages/,一个类一个文件,按界面命名如ChatPage、InputBar),复合页面用嵌套对象暴露方法(chatPage.inputBar.someMethod()),spec 只调用方法、绝不直接构造定位器:
// ✅ 正确 —— spec 调用 POM 方法 await chatPage.goto(); await chatPage.inputBar.type("hello"); await chatPage.inputBar.send(); await chatPage.expectHumanMessage("hello"); // ❌ 错误 —— spec 内写裸定位器 await page.goto("/app"); await page.locator('[contenteditable="true"]').fill("hello"); await page.keyboard.press("Enter"); await expect(page.locator(".message")).toContainText("hello");定位器优先级从高到低:data-testid/aria-label(getByTestId、getByLabel)→ 角色(getByRole)→ 文本/标签(getByText、getByLabel)→ CSS 选择器(最后手段)。
6.2 断言必须用自动重试的 matcher
Playwright 的expect(locator).*会轮询重试直到断言通过或超时;而locator.getAttribute()、page.evaluate()只读取一次 DOM 快照,在 React 异步更新场景下极易产生抖动(flaky)测试:
| 断言对象 | 应使用 | 不应使用 |
|---|---|---|
| 属性 | expect(locator).toHaveAttribute(name, value) | getAttribute()后expect(...) |
| class | toHaveClass(/regex/)/.not.toHaveClass(...) | page.evaluate手写判断 |
| 文本 | toHaveText(value)/toContainText(value) | textContent()后expect(...) |
| 数量 | toHaveCount(n) | count()后expect(...) |
| 可见性 | toBeVisible()/toBeHidden() | 手写isVisible()判断 |
| 值 | toHaveValue(value) | inputValue()后expect(...) |
getAttribute/evaluate等一次性读取仍可用于 spec 内部的控制流分支(例如按读取值决定后续动作),只是不能作为对异步状态的断言基础。这些规则连同更宏观的 Onyx 测试分层策略(单元 / 外部依赖单元 / 集成 / E2E),在 backend/AGENTS.md 与 backend/tests/README.md 中有更完整的说明。
七、生产构建与常用脚本速查
开发之外,web/package.json 还提供了完整的工程化脚本:
| 命令 | 作用 |
|---|---|
bun run dev | 启动开发服务器(next dev) |
bun run dev:profile | 开启NEXT_PUBLIC_ENABLE_STATS=true的带统计开发模式 |
bun run dev:clean | 清理.next缓存后启动开发服务器 |
bun run build | 生产构建(next build) |
bun run build:fast | 跳过类型检查的快速构建(SKIP_TYPE_CHECK=1) |
bun run start | 启动生产服务器(next start) |
bun run lint/lint:fix | 基于 oxlint 的代码检查与自动修复 |
bun run types:check | Next 类型生成 + TypeScript 严格类型检查(tsconfig.types.json) |
bun run format/format:check | 基于 oxfmt 的格式化与校验 |
bun run test系列 | Jest 单元测试(含 watch、coverage、CI 模式) |
bun run playwright | Playwright E2E 测试 |
bun run storybook | 组件文档(Storybook,6006 端口) |
值得注意的是 web/next.config.js 中的几处工程化决策:output: "standalone"支持独立部署镜像;turbopack.root显式固定到web/以避免仓库根目录多 lockfile 干扰;React Compiler 在next build时开启、next dev时关闭(可用ENABLE_REACT_COMPILER=1强制开启)——这些配置共同支撑了前端在开发、CI、生产三条链路上的稳定性。
八、总结
围绕 web/README.md,本篇梳理了 Onyx 前端开发的完整闭环:
- 本地开发:
bun install+bun run dev,默认对接本地 8080 后端(源码回退逻辑见 web/src/lib/constants.ts); - 远程联调:通过
web/.env.local中的INTERNAL_URL指向云端后端、DEBUG_AUTH_COOKIE注入fastapiusersauth会话(注入实现见 web/src/lib/users/svcSS.ts),并牢记 Cookie 会过期、需定期刷新且严禁入库; - 质量保障:Playwright E2E 测试体系(web/playwright.config.ts)配备 global-setup 自动造数、storage state 复用登录态、POM 分层与自动重试断言规范(web/tests/e2e/README.md),配合
--ui/--headed调试和 ODS 截图对比工具,构成从开发到 CI 的完整质量闭环。
掌握了这三层,你就可以在 Onyx 仓库中高效地进行前端开发、后端联调与回归测试,无论面向本地全栈环境还是云端后端。
【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考