generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Local-Development-and-Verification wiki_url: https://git.ilapage.cn/OPC/chorus/wiki/Local-Development-and-Verification.- wiki_revision: 6975232fead63ed60c480fb4bf7c0e9188a430d1 synchronized_at: 2026-08-21T15:02:26Z # 本地开发与验证 本页区分“现在可执行的文档流程”和“产品代码落地后可执行的 Go/前端/数据库流程”。代码不存在或本机版本不满足时必须如实记录,不把预期当作通过。 ## 环境要求 | 项 | 目标要求 | 检查命令 | |---|---|---| | Git | 近期版本 | `git --version` | | Python | Python 3 标准库 | `python --version` | | Go | **1.26.5** | `go version` | | MySQL | 8.0,支持 SKIP LOCKED | `mysql --version` | | golang-migrate | 可执行文件 | `migrate -version` | | Node | >=22,用于重建 portal/web 与 admin-ui 静态资源 | `node --version` | | pnpm | 9.15.1,由 packageManager/Corepack 固定 | `pnpm --version` | | Tailwind | 4.3.3,由 portal/web 锁文件固定 | `pnpm --dir portal/web exec tailwindcss --help` | 2026-08-20 已完成 MVP-0 环境基线:便携 Go 1.26.5、golang-migrate v4.19.1 和隔离 MySQL 8.4.8 已验证。Chorus 专用实例监听 `127.0.0.1:3308`,使用独立数据目录和测试账号;既有 `3307` 实例与 MySQL 5.7 不受影响。Node 22.22.1 可用;admin-ui 后续仍必须按 `packageManager` 使用 pnpm 9.15.1,不能直接采用全局 pnpm 11。 固定来源: - `D:\github\goadmin\go-admin` @ `f06540883b41d03782bb6b2c4150f298f328c6b6` - `D:\github\goadmin\go-admin-ui` @ `67d393d713877572fab0b897296a4c1d525fc81d` - `D:\github\goadmin\go-admin-doc` @ `424855aacf6905f3fde860c3331385cb25529a0d` 只从这些固定提交审查/导入,不直接在其工作区开发 chorus,也不让 chorus 构建依赖绝对路径。 主要环境变量: | 变量 | 用途 | |---|---| | `CHORUS_DSN` | Go 应用、种子和管理员 bootstrap 命令使用的 GORM/MySQL DSN | | `CHORUS_ADMIN_USERNAME` | 管理员 bootstrap 的目标账号;部署配置设为 `admin` | | `CHORUS_ADMIN_PASSWORD` | 管理员 bootstrap 密码;只在当前受保护进程环境中提供 | | `CHORUS_ADMIN_RESET_PASSWORD` | 仅设为 `1` 时允许 bootstrap 重置已有管理员密码 | | `CHORUS_MIGRATE_URL` | golang-migrate 专用 MySQL URL | | `CHORUS_SEED_USER_EMAIL` / `CHORUS_SEED_USER_PASSWORD` | 构造种子用户;仅通过环境注入 | | `CHORUS_SEED_PROVIDER_BASE_URL` | mock Provider 基础 URL;不得指向生产 | | `CHORUS_MASTER_KEY` / 对应 key ring 配置 | Provider Key AES-GCM 解密与轮换;管理端启动也必须提供 | | `CHORUS_ADMIN_ALLOW_CONNECTIVITY_PROBES` | 默认不设置或 `0`,管理端拒绝连通性探测;只有人工授权真实探测时才设为 `1` | | `CHORUS_ADMIN_CONNECTIVITY_COOLDOWN_SECONDS` | 探测已授权时的正整数冷却;未授权时不需要 | | `CHORUS_PROVIDER_HTTP_TIMEOUT_SECONDS` / `CHORUS_PROVIDER_MAX_RESPONSE_BYTES` | 探测已授权时的正整数安全 HTTP 限制 | | `CHORUS_SESSION_KEY` | portal 会话,必须与主密钥分离 | | `CHORUS_STORAGE_ROOT` | 受保护生成物目录 | | `CHORUS_ENV` | `development/test/production`,生产强制安全开关 | | `CHORUS_SESSION_TTL_MINUTES` | 会话有效分钟数;生产必填 | | `CHORUS_LOGIN_ATTEMPTS` / `CHORUS_LOGIN_WINDOW_SECONDS` | 单远端地址登录窗口限制;生产必填 | | `CHORUS_MAX_PROMPT_BYTES` | prompt UTF-8 字节上限;生产必填 | | `CHORUS_MAX_IMAGES` | 单任务图片数量上限;生产必填 | | `CHORUS_MAX_IMAGE_BYTES` / `CHORUS_MAX_UPLOAD_BYTES` | 单图/整次 multipart 字节上限;生产必填 | | `CHORUS_MAX_IMAGE_PIXELS` | 单图解码像素上限;生产必填 | | `CHORUS_HISTORY_LIMIT` | 单次历史查询上限;生产必填 | | `GITEA_TOKEN` | Wiki/工单操作,不影响产品服务 | ## 第一次运行 ### 1. 文档与工作区 ```powershell git status --short --branch python dev_scripts/harness.py check --strict python -m unittest discover -s tests -v $env:GITEA_URL = "https://git.ilapage.cn" python dev_scripts/harness.py sync --check ``` ### 2. 进入固定工具环境并连接隔离数据库 工具和凭据都位于仓库外。以下占位路径由维护者替换为自己的本机工具目录;环境文件不得提交,也不得把展开后的 DSN 输出到终端、日志、工单或 Wiki。 ```powershell $tools = "<本机工具目录>" $env:PATH = "$tools\go1.26.5\bin;$tools\migrate-v4.19.1;$env:PATH" . "$tools\chorus-test.env.ps1" go version migrate -version mysql --version ``` 当前已验证变量包括 `CHORUS_MYSQL_HOST`、`CHORUS_MYSQL_PORT`、`CHORUS_MYSQL_DATABASE`、`CHORUS_MYSQL_USER`、`CHORUS_MYSQL_PASSWORD`、`CHORUS_DB_DSN` 和 `CHORUS_MIGRATE_URL`。应用使用 GORM DSN,迁移工具使用单独验证过的 MySQL URL,不在两种格式之间临时拼接。 专用实例的可复制启动和停止方式如下;`my.ini` 必须绑定 `127.0.0.1`,并指定独立端口、数据目录、日志和 PID 文件。 ```powershell $mysqlHome = "" $instance = "" Start-Process -FilePath "$mysqlHome\bin\mysqld.exe" ` -ArgumentList "--defaults-file=$instance\my.ini", "--console" ` -WindowStyle Hidden $env:MYSQL_PWD = $env:CHORUS_MYSQL_ROOT_PASSWORD & "$mysqlHome\bin\mysqladmin.exe" --protocol=tcp ` --host=127.0.0.1 --port=3308 --user=root shutdown $env:MYSQL_PWD = $null ``` 能力探测使用构造查询,不创建业务表: ```powershell $env:MYSQL_PWD = $env:CHORUS_MYSQL_PASSWORD mysql --protocol=tcp --host=$env:CHORUS_MYSQL_HOST ` --port=$env:CHORUS_MYSQL_PORT --user=$env:CHORUS_MYSQL_USER ` --database=$env:CHORUS_MYSQL_DATABASE ` --execute="START TRANSACTION; SELECT id FROM (SELECT 1 AS id) AS capability_probe FOR UPDATE SKIP LOCKED; ROLLBACK;" $env:MYSQL_PWD = $null ``` #6 已提供首个迁移和幂等种子命令。只在明确的隔离库执行: ```powershell migrate -path migrations -database $env:CHORUS_MIGRATE_URL up $env:CHORUS_SEED_USER_EMAIL = "mvp0-test@chorus.invalid" $env:CHORUS_SEED_USER_PASSWORD = "<本次构造密码>" $env:CHORUS_SEED_PROVIDER_BASE_URL = "" go run ./cmd/chorus-seed go run ./cmd/chorus-seed migrate -path migrations -database $env:CHORUS_MIGRATE_URL down -all migrate -path migrations -database $env:CHORUS_MIGRATE_URL up ``` 两次种子执行后应仍是一个构造用户、一个 mock Provider、两个 ProviderModel 和两个 Prompt。`down -all` 会删除隔离库业务数据,只能用于明确可丢弃的测试库。`chorus_codegen` 只在 MVP-1 的 go-admin 结构研究/代码生成工单中另行创建,不提前扩大当前账号权限。 ### 管理员 bootstrap(#28) `go run ./cmd/chorus-admin-bootstrap` 只在 #20 的迁移及 `chorus_operator` 角色种子已完成后运行。它不执行迁移、AutoMigrate、seed,也不接受命令行参数。账号只创建一次;发现同名账号时会检查账号启用、未软删除且角色仍为 `chorus_operator`,不满足就失败关闭而不修改数据。 ```powershell $env:CHORUS_ADMIN_USERNAME = "admin" # 仅在当前受保护终端设置 CHORUS_ADMIN_PASSWORD;不得写入脚本、仓库、日志或文档。 go run ./cmd/chorus-admin-bootstrap ``` 第二次运行不会改变密码。只有经人工确认需要重置时,才在同一受保护终端临时设定 `CHORUS_ADMIN_RESET_PASSWORD=1` 后运行;完成后移除该变量。命令只输出创建、保持或显式重置的结果,不回显 DSN、密码或 bcrypt hash。 集成测试使用运行时生成的构造账号与密码,并要求显式的 `CHORUS_RUN_ADMIN_BOOTSTRAP_TESTS=1` 和指向隔离库的 `CHORUS_ADMIN_BOOTSTRAP_TEST_DSN`。测试结束会删除构造账号;不得把该变量指向开发共享库或生产库。 ### 启动管理端后端(#23) 先完成 migrations 和管理员 bootstrap。把 `admin/config/settings.example.yml` 复制到仓库外的受保护位置,并在其中设置独立 JWT secret 与数据库连接;示例文件不得填入真实值。当前终端安全注入 `CHORUS_MASTER_KEY` 后启动: ```powershell go -C admin run . server --config "<受保护 settings.yml 路径>" ``` 该命令只启动 go-admin 管理 API,不执行 migration、seed 或 AutoMigrate,也不注册代码生成、Swagger、WebSocket 或静态文件路由。管理 API 使用 `Authorization: Bearer `;不要在 URL 或 Cookie 放置 token。连通性探测默认返回 403 且不出站。只有经过人工批准的单次真实检查,才在本次受保护进程设置 `CHORUS_ADMIN_ALLOW_CONNECTIVITY_PROBES=1`,并同时提供正数 `CHORUS_ADMIN_CONNECTIVITY_COOLDOWN_SECONDS`、`CHORUS_PROVIDER_HTTP_TIMEOUT_SECONDS` 和 `CHORUS_PROVIDER_MAX_RESPONSE_BYTES`。 ### 3. go-admin schema-first 生成(MVP-1) 1. 先在 `chorus_codegen` 执行同一套版本化 SQL。 2. 启动仅限本机的 admin/admin-ui 开发实例。 3. 从数据库表列表导入业务表元数据,配置并预览生成结果。 4. 生成 Go/Vue 文件后检查输出路径、字段类型、敏感字段、权限和重复代码。 5. 不直接采用“生成迁移脚本”或 `/gen/todb` 的副作用;把菜单/API 配置整理成带 down 的 SQL。 6. 清空隔离库,从 migrations 重放并重新生成/构建。 7. 生产构建验证 dev-tools 路由不存在;仅隐藏菜单不算关闭。 go-admin AutoMigrate 不进入以上标准流程。如确需对照固定提交初始化结构,人工在额外可丢弃库运行一次,导出结构差异后销毁;禁止连接共享/生产库,也禁止把该命令放入应用启动、容器入口或部署脚本。 ### 4. 配置 mock 上游与种子用户(MVP-0) MVP-0 不开放注册。使用可重复、不会在日志输出密钥的种子命令建立测试用户、默认 prompt template、Provider 和 ProviderModel。默认先连接 mock HTTP 上游,覆盖文本、图片、429、5xx、超时、连接错误、400、401 和内容策略拒绝。 真实 Provider 的连通性检查只能由操作者明确触发一次低成本请求,并核对审计与冷却;不能作为自动测试或反复调试方式。 本地 mock 协议服务可单独启动,用于 curl 或协议调试。Windows 推荐通过受管启动脚本运行: ```bat scripts\chorus-dev.bat start-mock ``` 脚本默认读取仓库外的 `D:\OPC\chorus-tools\chorus-test.env.ps1`,也可把其他可信环境文件作为带引号的第二个参数传入。`start-mock` 只启动 mock 协议服务,不启动 worker,也不会放宽生产安全 Client 的 SSRF 规则。进程状态与日志保存在 `%LOCALAPPDATA%\Chorus\dev`。 前台调试仍可直接运行: ```powershell $env:CHORUS_MOCK_ADDR = "127.0.0.1:18080" go run ./cmd/chorus-mock-provider ``` 生产安全 Client 始终拒绝回环/私网 Provider URL。自动化端到端测试使用构造公共域名解析和受控 `DialContext` 把请求送到进程内 mock,不应为了让 portal 直连本机 mock 而放宽 SSRF 规则。 种子密码从 #11 起保存为 `bcrypt:v1:`。已有无版本构造哈希不会被登录兼容;使用同一构造邮箱和本次选择的密码重新运行 `go run ./cmd/chorus-seed`,会更新密码哈希而不新增用户。开发/测试未提供 session key 时进程随机生成临时 key,重启后会话失效;生产必须显式提供至少 32 字节且与 Provider 主密钥分离的 key。 ### 5. 启动 portal(MVP-0) Windows 本地开发优先使用仓库自带脚本: ```bat scripts\chorus-dev.bat start scripts\chorus-dev.bat status scripts\chorus-dev.bat restart scripts\chorus-dev.bat stop ``` `start` 会校验环境文件、本地 MySQL TCP 连接和监听端口,构建临时可执行文件,在后台启动 portal,并检查 `/login`。重复启动不会创建第二个 portal;`restart` 只重启 portal;`stop` 只停止由脚本记录且可执行文件路径匹配的 portal 和 mock 进程。脚本不会执行迁移、GORM AutoMigrate、seed 或创建测试账号。 脚本默认读取仓库外的 `D:\OPC\chorus-tools\chorus-test.env.ps1`;也可把其他可信环境文件作为带引号的第二个参数传入,例如 `scripts\chorus-dev.bat start "D:\tools\chorus local.env.ps1"`。日志、PID 状态和临时可执行文件位于 `%LOCALAPPDATA%\Chorus\dev`。 需要在当前终端观察输出时,仍可运行: ```powershell go run ./portal ``` 浏览器验证: 1. 用种子用户登录,不应出现公开注册链接; 2. 桌面双栏、375px 移动单列均可完成文本和图片提交; 3. 第一张图自动 primary,其余 reference,MVP-0 没有角色编辑/拖拽; 4. 提交后立即显示 queued/running,终态停止轮询; 5. 文本、图片、失败、网络轮询错误和认证过期都有明确界面; 6. 数据库中 `rendered_prompt`、`attempts`、latency 或失败 error 字段齐全; 7. 输出通过鉴权访问,不能猜 URL 读取其他用户文件。 ## 常用调试方式 | 症状 | 先看 | |---|---| | pending 不动 | worker 是否启动、取任务索引和数据库版本 | | running 租约过期 | lease_owner/token/until、上游超时、陈旧 worker CAS | | 成功但内容不对 | rendered_prompt 与 prompt template 版本 | | 400/401 尝试多家 | retryable 实现错误 | | 文件存在但页面 403/404 | 用户归属、原子落位、缩略图记录 | | 上游连不上 | SSRF 日志、DNS/IPv6、redirect、proxy 和 base_url | | 管理端生成异常 | 是否先迁移再导表,是否误把菜单脚本当业务 DDL | | 样式丢失 | portal Tailwind 产物或 admin-ui pnpm/Vue CLI 构建 | 不要在日志、SQL、工单或 Wiki 中贴 API Key、Cookie、真实用户输入和生产文件。 ## 测试策略 - 迁移:空 MySQL 8 隔离库逐个/全量 up-down-up,验证索引、外键、唯一约束和种子幂等。 - 队列:并发认领、租约过期重新认领、旧 token 最终写 0 行、优雅退出、attempt 原子性。从 #9 起该项必须连接迁移后的隔离 MySQL 8,mock SQL 不能替代。 ```powershell $env:CHORUS_TEST_DSN = $env:CHORUS_DSN go test -v -count=1 ./internal/core/queue/... go test -race -count=1 ./internal/core/queue/... $env:CHORUS_TEST_DSN = $null ``` Provider/worker 的 mock 协议、真实 MySQL 8 attempt/output 与优雅退出验证: ```powershell $env:CHORUS_TEST_DSN = $env:CHORUS_DSN go test -v -count=1 ./internal/core/provider/... ./internal/platform/http/... ./internal/platform/mockprovider/... ./portal/worker/... go test -race -count=1 ./... $env:CHORUS_TEST_DSN = $null ``` - Provider:全部 retryable 类别使用 mock,不消耗真实额度。 - Portal handler:真实 MySQL 8 覆盖登录/退出、session/CSRF 轮换、用户作用域幂等、pending 提交、上传边界、任务与文件跨用户授权、终态和 HTMX 401;Go 集成测试结束后清理本次构造数据。浏览器 E2E 使用固定的合成幂等任务,重复运行不持续新增记录。 ```powershell $env:CHORUS_TEST_DSN = $env:CHORUS_DSN go test -v -count=1 ./portal/handler/... ./portal/service/... ./portal/session/... ./portal/auth/... $env:CHORUS_TEST_DSN = $null ``` portal 静态资源和浏览器 E2E 使用固定锁文件。E2E 账号、密码和地址只通过本机环境变量提供,不写入命令示例、仓库或测试报告: ```powershell corepack pnpm --dir portal/web install --frozen-lockfile corepack pnpm --dir portal/web build $env:CHORUS_E2E_BASE_URL = "http://127.0.0.1:8080" # 在当前进程安全设置 CHORUS_E2E_EMAIL 与 CHORUS_E2E_PASSWORD corepack pnpm --dir portal/web test:e2e ``` - SSRF:IPv4/IPv6 私网、DNS rebinding、redirect 链、环境代理、结果 URL。 - 安全:密码/session/CSRF、登录节流、跨用户任务与文件、错误脱敏、密钥轮换。 - 浏览器:HTMX 动态状态、终态停止、认证过期、网络错误;375/768/1024/1440 无主区域横向滚动,并检查键盘、44px 触控目标和 reduced-motion。 - 供应链:Go/Node 依赖锁定、漏洞和许可证检查按实现工单确定命令。 ### 管理端后端集成测试(#23) 管理端测试只允许使用精确名称为 `chorus_test` 的可丢弃 MySQL 数据库;测试写入随机后缀的合成 Provider/Model/Route 数据并清理,不调用真实上游。运行: ```powershell $env:CHORUS_RUN_ADMIN_TESTS = "1" go -C admin test -count=1 ./... $env:CHORUS_RUN_ADMIN_TESTS = $null ``` 覆盖凭据加密、轮换和回退、普通更新不清除凭据、路由发布版本冲突、JWT/Casbin 路由边界、禁用探测不出站,以及探测冷却只预留一次。根模块的 migration up/down/up 回归仍需在可丢弃库执行:`go test -count=1 ./migrations -run TestMVP1MigrationsUpDownUpMySQL`。 ## 完成修改前 ```powershell git status --short --branch go build ./... go vet ./... go test ./... go -C admin build . go -C admin test ./... corepack pnpm --dir portal/web install --frozen-lockfile corepack pnpm --dir portal/web build corepack pnpm --dir portal/web test:e2e pnpm --dir admin-ui install --frozen-lockfile pnpm --dir admin-ui build:prod python dev_scripts/harness.py check --strict python -m unittest discover -s tests -v python dev_scripts/harness.py sync --check git diff --check ``` 只运行受当前范围影响且实际存在的产品命令;不存在或因工具版本不能执行的项写入工单。涉及迁移、安全、队列、权限或 UI 时,还必须执行上面的专项验证并记录结果。 ## MVP-0 集成验收记录(2026-08-21) 本次 #13 使用专用可丢弃库验证,不能把示例中的 down 或 fixture 指向现有开发库、共享库或生产库。结论如下: - MySQL 8.4.8:全量 `up → seed 两次 → down -all → up → seed 两次` 通过;种子稳定为 1 用户、1 Provider、2 ProviderModel、2 Prompt,down 后业务表为 0。 - Go:`go build ./...`、`go vet ./...`、`go test -race -count=1 -p=1 ./...` 通过;MySQL 测试覆盖幂等、跨用户、租约恢复、旧 token CAS、attempts、文本/图片 mock 成功和鉴权文件。 - 浏览器:`pnpm --dir portal/web test:e2e` 在 375/768/1024/1440 通过;1024 额外覆盖 pending、running、文本成功、图片成功、失败终态及终态移除轮询属性。 - 进程:真实内嵌 worker 从隔离库认领 6 条 pending 并形成终态;回环 Provider 被 SSRF 在连接前拒绝;两次前台 `Ctrl+C` 均干净退出。 内嵌 worker 的运行参数: | 变量 | 非生产默认 | 生产要求 | |---|---:|---| | `CHORUS_WORKER_LEASE_SECONDS` | 60 | 必须显式设置,且大于 HTTP 超时 + 5 秒 | | `CHORUS_WORKER_POLL_MILLISECONDS` | 250 | 必须显式设置 | | `CHORUS_PROVIDER_HTTP_TIMEOUT_SECONDS` | 45 | 必须显式设置 | | `CHORUS_PROVIDER_MAX_RESPONSE_BYTES` | 33554432 | 必须显式设置 | `CHORUS_TEST_DISABLE_WORKER=true` 仅供 `CHORUS_ENV=test` 的 Playwright fixture 使用。测试 helper 位于 `portal/web/e2e/fixture`,会按构造用户归属写入受控状态和测试图片;不得打包部署,也不得用于开发或生产数据。mock 上游的自动测试通过依赖注入连接本地 fixture,不允许把回环地址加入生产 SSRF 白名单。