17 KiB
Trade Message Center
本地开发要求
- Node.js
>=22.22.2 <23 - pnpm
11.7.0 - 可连接的 PostgreSQL 数据库
- Chrome 或 Chromium(用于加载和调试扩展)
安装依赖:
pnpm install
GitNexus 代码智能
GitNexus 会把代码解析为符号、调用关系和执行流程图,适合在改动前定位实现、评估影响范围,以及在提交前核对实际波及的模块。索引保存在本机的 .gitnexus/,不会提交到 Git。
安装与首次建索引
本仓库已经生成过 .gitnexus/run.cjs;日常使用不需要全局安装。新 clone 没有该文件时,在仓库根目录执行一次:
npx gitnexus@latest analyze
若本机的 npm 11 在安装原生依赖时失败,改用 pnpm:
pnpm --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter \
dlx gitnexus@latest analyze
也可以安装全局命令,之后上述本地 runner 会优先使用它:
npm install -g gitnexus
索引维护
在进行较大代码变更后、GitNexus 报告索引过期时,或需要重新生成 AI 指引文件时,执行:
node .gitnexus/run.cjs analyze
常用维护命令:
node .gitnexus/run.cjs status # 查看索引状态、版本和统计信息
node .gitnexus/run.cjs list # 列出本机已索引仓库
node .gitnexus/run.cjs clean # 删除当前仓库的本地索引;执行前会要求确认
clean 会移除 .gitnexus/,只在索引损坏或确定不再需要时使用;随后可用 analyze 重新生成。
在 AI 编程会话中使用
已配置 GitNexus MCP 的客户端(如本项目的 Codex/Claude 环境)可直接读取 gitnexus://repo/trade-message-center/context 确认索引是否可用。推荐按任务类型选择工具:
| 目标 | 工具 | 示例 |
|---|---|---|
| 理解功能或调用链 | query |
query({ query: "OneTalk 授权" }) |
| 查看某个符号的完整上下文 | context |
context({ name: "authorizeBinding" }) |
| 改动前评估调用方与风险 | impact |
impact({ target: "authorizeBinding", direction: "upstream" }) |
| 提交前确认当前 diff 的影响 | detect_changes |
detect_changes({ scope: "compare", base_ref: "main" }) |
| 跨文件重命名 | rename |
使用工具执行,不要文本替换 |
工作约定是:探索陌生逻辑时先用 query,修改函数、类或方法前先执行 impact;若结果为 HIGH 或 CRITICAL,先确认影响范围再继续。提交前执行 detect_changes,核对变更只覆盖预期的符号和执行流程。
环境变量与授权配置
环境变量文件统一放在仓库根目录,供 server、Chrome 扩展和数据库迁移共享:
.env.example:提交到仓库的变量模板,不放真实密钥。.env、.env.development:可选的基础或开发环境配置,不提交。.env.local:本机通用配置,不提交。
文件名统一使用 .env.example 和 .env.development;不要使用 .env.Example 或 .env.dev。
开发环境可以从模板开始:
cp .env.example .env.local
server 开发入口和迁移命令按以下顺序读取文件,后面的文件覆盖前面的同名变量:
.env → .env.local → .env.development
必填基础配置
| 变量 | 说明 |
|---|---|
HOST |
server 监听地址,例如 127.0.0.1。 |
PORT |
server 监听端口,必须是 1-65535 的整数,例如 7878。 |
DATABASE_URL |
Bright PostgreSQL 连接串;启动 server 和执行 migration 都需要。 |
OSS_BUCKET、OSS_ENDPOINT |
Chrome 扩展发布包所在的 OSS Bucket 与 HTTPS Endpoint。 |
OSS_ACCESS_KEY_ID、OSS_ACCESS_KEY_SECRET |
仅服务端使用的 OSS V1 临时下载链接签名凭据。 |
本地默认配置如下:
HOST=127.0.0.1
PORT=7878
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/trade_message_center
OneTalk 本地开发
pnpm dev 会以 NODE_ENV=development 启动 Bright server,并同时启动本地 Mind HTTP mock(默认 127.0.0.1:8787)。Bright 通过 MIND_AUTH_BASE_URL 访问 mock 的两个授权接口,不再需要 ONETALK_DEV_* 身份 fixture;mock 只模拟 Mind HTTP 授权结果,不是生产授权实现,也不会连接 Mind 数据库。
本地 .env.local 至少应将 MIND_AUTH_BASE_URL 指向 mock,并将 MIND_PAGE_ORIGIN 指向 Bright 页面地址:
MIND_AUTH_BASE_URL=http://127.0.0.1:8787
MIND_PAGE_ORIGIN=http://127.0.0.1:7878
ONETALK_PLUGIN_ORIGINS=chrome-extension://ogdbffjakeeidblabkeakakdecfbcmlf
apps/chrome-extension/.keys/extension-private-key.pem 是本地生成的扩展私钥,权限应保持为 600,不会提交到 Git;生成的 dist/manifest.json 内置从该私钥导出的固定公钥,因此从不同目录加载 apps/chrome-extension/dist/ 不会再导致扩展 ID 变化。当前固定 ID 是 ogdbffjakeeidblabkeakakdecfbcmlf。ONETALK_PLUGIN_ORIGINS 仍必须是这个扩展的精确 Origin,不能改成任意 Origin;MIND_PAGE_ORIGIN 是浏览器访问 Bright 的页面 Origin,不是 mock 的监听地址。
开发 server 会输出 [mind-auth][diagnostic],只包含 binding/session endpoint、授权 operation、request_started/allowed/rejected/request_failed、HTTP status 和稳定 code,可据此判断请求是否发出、Mind 是否返回以及最终拒绝原因;不包含 Cookie、binding、请求体或原始异常。扩展握手的 Origin 拒绝会出现在 [onetalk][diagnostic] 中。
Mind/Bright 生产认证边界
生产部署使用同一二级域名下的不同三级域名,例如 mind.example.com 与 bright.example.com。Mind 登录 Cookie 由 Mind 配置为覆盖两个三级域名的 Domain=.example.com、Path=/、Secure、HttpOnly Cookie;Mind 页面调用 Bright HTTP 时使用 credentials: "include",Bright 只允许精确 Mind Origin,并对页面 WS 和扩展 WS 分别执行 Origin allowlist。
Bright 不连接 Mind 数据库、不读取认证视图、不共享 Mind 登录密钥,也不签发 mind_page credential。Bright 只把页面请求中的 Cookie 转发给 Mind 的 Session 授权接口,并把插件的 channelAccountId + binding 转发给 Mind 的 binding 授权接口;Cookie 不进入 URL、WS payload、日志或持久化数据。
Mind HTTP 本地模拟
仓库提供一个无状态的本地 Mind HTTP mock,只实现上述两个授权接口。它默认监听 127.0.0.1:8787,默认 fixture 是账号 286995452、binding binding-1 和 Cookie mind_session=mock-valid;通过 MIND_MOCK_* 环境变量覆盖 fixture 后重启即可模拟 binding/version/cookie 切换。两个 endpoint 还可独立设置结果:MIND_MOCK_BINDING_RESULT=allow|scope_mismatch|binding_revoked|authorization_unavailable,MIND_MOCK_SESSION_RESULT=allow|auth_required|scope_mismatch|authorization_rejected|authorization_unavailable。
根目录执行 pnpm dev 时会先清理各 workspace 包的开发 dist,重新构建共享 contract,再自动启动 mock;也可以单独运行 pnpm dev:mock。
直接验证两个接口:
curl -sS http://127.0.0.1:8787/internal/bright/onetalk/authorize-binding \
-H 'content-type: application/json' \
--data '{"channelAccountId":"286995452","binding":"binding-1"}'
curl -sS http://127.0.0.1:8787/internal/bright/onetalk/authorize-session \
-H 'content-type: application/json' \
-H 'cookie: mind_session=mock-valid' \
--data '{"channelAccountId":"286995452"}'
根目录执行 pnpm dev 会自动启动 mock 和 Bright;也可以单独运行:
pnpm dev:mock
pnpm --filter @trade-message-center/server dev:mind-http 是仅供显式测试配置使用的 server 启动脚本;普通 pnpm dev 已直接使用 development-only 的 loopback Mind HTTP 配置。这个 mock 不模拟真实 Mind 的 Cookie Domain/SameSite/Secure、三级域 CORS/TLS、浏览器页面或 takeover 事务。
mind-http-mock 只参与本地 dev、类型检查和测试,不参与根 pnpm build,也不会被 Dockerfile.server 复制到生产镜像。
VITE_BRIGHT_WEBSOCKET_URL 用于 Chrome 扩展构建:
VITE_BRIGHT_WEBSOCKET_URL=ws://127.0.0.1:7878/ws/plugin
它会在扩展构建时注入,必须使用 ws:// 或 wss://。开发或生产构建未设置该变量时会直接失败,不再回退到代码内置地址。
该变量是四个扩展入口共用的构建配置,应放在根目录 .env.local,或通过命令行/CI 环境变量注入。
只有以 VITE_ 开头的变量会暴露给扩展代码,数据库连接串和 binding 等服务端敏感配置不得使用该前缀。
Chrome 扩展下载链接
GET /api/downloads/chrome-extension 返回当前 workspace 完整版本的临时 OSS 下载地址:
{
"version": "0.6.1",
"downloadUrl": "https://sinanpilot-bucket.oss-cn-hangzhou.aliyuncs.com/chrome-extension/0.6.1/trade-message-center-chrome-extension-0.6.1.zip?..."
}
版本由根 package.json 的 version 经 CI 注入为 TMC_PACKAGE_VERSION,接口和发布目录都不使用 v 前缀。OSS 签名参数由服务端签名器计算;访问密钥只保存在服务端运行时环境中。未配置 OSS 访问密钥时,接口明确返回 503 extension_download_unavailable,不影响本地其它 server 功能。
Workspace 版本
根目录 package.json 的 version 是 workspace 发布版本的唯一编辑点。根目录的 dev、dev:extension、build、typecheck、test 和 format:check 会自动先执行版本镜像同步,再检查一致性;修改根版本后可以直接运行这些命令,不会因为子包镜像尚未更新而中断。
如需手动执行或单独审计,可使用:
pnpm version:sync
pnpm version:check
也可以用一次命令完成“同步后检查”:
pnpm version:ensure
版本必须是 Chrome 扩展和 package metadata 都支持的三段数字版本,例如 0.1.0;不使用 prerelease、build metadata 或额外段。协议、配置、IndexedDB 和授权版本仍是独立概念,不随 workspace 版本修改。
启动 server 与数据库
首次启动或数据库结构有变化时,先执行迁移:
pnpm --filter @trade-message-center/server db:migrate
如需重置本地开发数据库,执行以下命令:
pnpm --filter @trade-message-center/server db:reset
该命令会删除开发库的 public 和 drizzle schema,重建 public schema,并重新执行全部 migration。它只接受 NODE_ENV=development 和 loopback 主机(localhost、127.0.0.1 或 ::1)的 DATABASE_URL;请确保目标是专用的本地开发库,命令不会保留其中的数据。
再从仓库根目录启动 server 和扩展开发构建:
pnpm dev
根命令会统一生成并注入 BUILD_HASH,同时启动 server 和 Popup、MAIN world、ISOLATED world、Service Worker 四个扩展 watch 构建。因此扩展开发不要直接运行底层 Vite 命令。
如果只需要重启 Bright server,使用:
pnpm dev:server
该命令直接启动 server watch,不调用 workspace 级 with-build-hash,也跳过 server predev 的 contract 构建;因此不会重新构建 Chrome 扩展,也不会改变当前扩展产物中的 BUILD_HASH。首次 checkout 或修改 onetalk-contract 后,请先执行 pnpm --filter @trade-message-center/onetalk-contract build。如果 server 需要本地 Mind HTTP mock,请另开终端运行 pnpm dev:mock。
检查 server 是否启动:
curl http://127.0.0.1:7878/health
预期返回:
{ "status": "ok" }
本地 Bright 联调页位于:
http://127.0.0.1:7878/harness
本地 harness 中的 OneTalk account 应与 MIND_MOCK_CHANNEL_ACCOUNT_ID(默认 286995452)一致;生产 Mind 页面不提交可信的 user/workspace header,而由 Cookie Session 授权结果派生。页面 WebSocket 使用 /ws/mind,插件使用 /ws/plugin。
加载和配置 Chrome 扩展
- 执行
pnpm dev,确认生成apps/chrome-extension/dist/。 - 打开
chrome://extensions,启用“开发者模式”,选择“加载已解压的扩展程序”,加载apps/chrome-extension/dist/。 - 打开 OneTalk 页面
https://onetalk.alibaba.com/,点击扩展图标打开 Popup;Popup 会从页面运行时读取登录人账号 ID(currentUserAccountId),不要填写 URL 的activeAccountId。 - 在 Binding 输入框填入
MIND_MOCK_BINDING(默认binding-1)对应的值后保存配置;Popup 首次运行会自动生成 Device ID。URL 中的activeAccountId仅是当前对话账号。 - Bright WebSocket URL 仅展示当前构建使用的地址,不在 Popup 中手工填写。配置保存或清除后,Service Worker 会动态建立、重建或关闭 Bright 连接和同步引擎。
扩展配置保存在 chrome.storage.local。Popup 不配置 mindUserId 或 workspaceId;保存 binding 时留空表示保持已有 binding,输入新值才会替换它。清除配置不会重置自动生成的 Device ID。
生产构建:
pnpm build
构建完成后仍可将 apps/chrome-extension/dist/ 作为未解压扩展加载。
生成并发布 Chrome 扩展
推送指向 main 历史的 Git tag 后,生成流程会在发布前检查中的同一次构建基础上打包扩展,并将版本包上传到 OSS:
oss://<OSS_BUCKET>/chrome-extension/<package-version>/trade-message-center-chrome-extension-<package-version>.zip
OSS Bucket、Endpoint 和 Region 已直接写在 .github/workflows/release_ci.yml 中。production Environment 只需要配置以下密钥:
- Secrets:
OSS_ACCESS_KEY_ID、OSS_ACCESS_KEY_SECRET。
上传账号只需要目标 Bucket 对应版本目录的写权限。Git tag 仍用于触发发布和定位源码,但不会作为扩展发布版本;版本目录和文件名使用根 package.json 的 version,均不添加 v 前缀。现有同版本覆盖语义保持不变。
Server 数据库迁移
server 使用 Drizzle 的 TypeScript schema 生成版本化 SQL migration。业务表定义放在 apps/server/src/database/schema/,迁移文件和元数据放在 apps/server/drizzle/,相关命令从仓库根目录执行:
pnpm --filter @trade-message-center/server db:generate
pnpm --filter @trade-message-center/server db:check
pnpm --filter @trade-message-center/server db:migrate
生产部署时,先以独立的一次性 job 执行 db:migrate,成功后再启动或滚动更新 server;应用启动不会自动修改数据库结构。已执行的 migration 不可修改,破坏性变更使用新的补偿 migration 和 expand-contract 流程。
测试与质量检查
常用检查:
pnpm format:check
pnpm typecheck
pnpm test
pnpm build
git diff --check
需要执行真实 PostgreSQL 集成测试时,将 TEST_DATABASE_URL 放在根目录 .env.local 中(或显式导出),运行专用测试命令:
pnpm --filter @trade-message-center/server test:integration
该命令会按 server 迁移命令相同的顺序加载根目录 .env、.env.local 和 .env.development。普通 pnpm test 会排除 *.integration.test.*,不会自动连接 PostgreSQL;未设置 TEST_DATABASE_URL 时,单独运行集成测试会明确标记为 skip,不代表真实数据库迁移和事务边界已经验证。
VSCode 开发环境
本项目统一使用 Oxfmt 格式化代码,缩进为 4 个空格。请在 VSCode 中安装 Oxc 插件 oxc.oxc-vscode,否则保存文件时可能使用其它 formatter,导致代码在提交钩子中再次变化。
安装方式:
- 打开 VSCode 扩展面板(macOS 快捷键:
⇧⌘X)。 - 搜索
Oxc,安装扩展oxc.oxc-vscode。 - 重新打开项目窗口,确认右下角或状态栏使用 Oxc formatter。
仓库已经在 .vscode/settings.json 中配置 Oxc 保存格式化,并通过 .oxfmtrc.json 固定 4 空格规则。项目本地依赖已经包含 Oxfmt,不需要单独全局安装。
如果暂时不安装插件,请关闭 VSCode 的 formatOnSave,需要格式化时在项目根目录执行:
pnpm format
pnpm format:check
不要同时启用 Prettier、Biome 或 VSCode 内置 TypeScript formatter。