Skip to content

Latest commit

 

History

History
237 lines (163 loc) · 17.1 KB

File metadata and controls

237 lines (163 loc) · 17.1 KB

RoadbookMaker 代码库架构概览(给 Agent / 开发者)

本文件用于帮助快速理解仓库结构与运行方式,便于开发、重构、测试与排障。

1. 项目概览

RoadbookMaker 是一个基于网页的地图标记与行程规划工具:

  • 前端static/ 下的纯原生 JavaScript + Leaflet,直接在浏览器运行,无构建步骤。核心文件:
    • static/index.html:入口 HTML。
    • static/style.css:样式定义(含 CSS 变量实现的明亮/暗色模式)。
    • static/script.jsRoadbookApp 主入口与类骨架,保留全局配置、constructorinit()bindEvents()、分享导入/移动端/调试等主流程。
    • static/app_*.js:按职责拆分的前端模块,通过 RoadbookApp.prototype 扩展主类。当前包含 app_utils.jsapp_theme.jsapp_history.jsapp_search.jsapp_ai.jsapp_date_filter.jsapp_date_notes.jsapp_sidebar.jsapp_detail_panels.jsapp_tooltips.jsapp_map.jsapp_io.js
    • static/help_tour.js:新手引导(罩子高亮 + tooltip + 自动演示点/线/日程编辑)。
    • static/online_mode.js:在线模式 API 交互。
    • static/ai_assistant.js:AI 助手功能,实现自然语言驱动的地图操作。
    • static/debug.js:调试模块,提供应用状态快照、日志捕获与环境信息查看(见第 10 节)。
    • static/html_export.js:负责生成包含完整数据与交互逻辑的独立 HTML/TXT 导出文件。
  • 后端backend/ 下的 Go + Gin API 服务(入口 backend/cmd/roadbook-api/main.go),提供:
    • 在线模式(登录/JWT、计划 CRUD、分享读取)
    • 地图搜索聚合与代理(/api/cnmap/search/api/tianmap/search
    • AI 助手代理(/api/v1/ai/*),负责与大语言模型交互
    • trafficpos 等辅助 API(/api/trafficpos
  • 部署:Nginx 提供静态资源,并反向代理 /api/ 到后端(见 nginx.prod.conf:25);Docker 镜像把前端 + 后端打包在一起(见 DockerfileDockerfile.localdocker-entrypoint.sh)。

在线模式数据持久化是“文件系统仓库”:计划以 data/<id>.json 形式写入磁盘(见 backend/internal/plan/repository.go:16)。

2. 构建与常用命令

前端

  • 纯静态资源:直接打开 static/index.html 或由 Nginx 提供。
  • 前端对后端基址的判定:
    • localhost/127.0.0.1/file:// 时使用 http://127.0.0.1:5436(见 static/script.js:5
    • 否则使用当前域名(见 static/script.js:8

后端

  • 生成后端配置(交互式):./scripts/generate_config.sh(写入 backend/configs/config.json,见 scripts/generate_config.sh:23
  • 管理 Agent 软链接:./scripts/link_agents.sh(用于同步 .agent 配置到 .coco/.gemini 等目录,见 scripts/link_agents.sh
  • 编译后端:./backend/scripts/build.sh(产物:backend/roadbook-api,见 backend/scripts/build.sh:34
  • 启动后端:在 backend/ 目录执行 ./roadbook-api(README 与 backend/cmd/roadbook-api/main.go:41

Docker

  • 从项目根目录构建本地镜像:./build.sh(会先在 backend/go builddocker build -f./Dockerfile.local,见 build.sh:16build.sh:21
  • 镜像运行示例:docker run -d -p 5215:80 roadbook(见 build.sh:26

容器启动逻辑:docker-entrypoint.sh 会启动 Nginx,并在 /app 下启动后端二进制(见 docker-entrypoint.sh:4docker-entrypoint.sh:7)。

3. 代码风格与约定(基于仓库现状)

Go(backend/

  • 模块:module github.com/chenxuan520/roadmap/backendgo 1.18(见 backend/go.mod:1backend/go.mod:3)。
  • 框架与库:Gin(HTTP)、golang-jwt/jwt/v4(JWT)、google/uuid(计划ID)、golang.org/x/time/rate(限流)(见 backend/go.mod:6-backend/go.mod:9)。
  • 路由组织:在 backend/internal/server/server.go 内组装 Gin Engine;/api/v1 下是认证/计划接口,/api 下保留搜索与其它公共接口(见 backend/internal/server/server.go:65backend/internal/server/server.go:97)。

前端(static/

  • 无框架:主入口是 static/script.js,核心业务已拆到 static/app_*.jsstatic/online_mode.js 负责在线模式/云端保存/登录,static/ai_assistant.js 负责 AI 助手。
  • 前端模块约定:script.js 必须先于 app_*.js 加载,由 script.js 定义 RoadbookApp,再由各模块扩展 RoadbookApp.prototype
  • 若修改 index.html 脚本顺序,必须保证 window.app = new RoadbookApp() 执行前,所有 app_*.js 已经完成注册。
  • 新手引导:static/help_tour.js(罩子高亮 + 步骤引导;入口在帮助面板 内)
  • 调试逻辑独立于 static/debug.js,通过 DebugModule 类实现,仅在需要时被调用。
  • 在线 token 存储:localStorage 的 key 为 online_token(见 static/online_mode.js:6)。
  • 在线模式快捷键:在在线模式且已登录时,Ctrl/Cmd+S 触发 saveToCloud() 云端保存(见 static/online_mode.js:41)。

维护规则

  • 命名约定:不要使用下划线 _ 开头命名“私有”函数、变量或绑定的事件处理器(推荐使用 boundHandler 形式),保持简洁。
  • 注释约定:修改代码时,必须保留原有的关键注释(特别是数据结构定义、业务逻辑说明等),除非该逻辑已被完全移除。
  • 语法检查:每次修改代码后,必须自行检查语法,确保代码可编译/可运行。
    • Go 后端:需要检查 unused imports 等编译错误。
    • 前端 JavaScript:必须使用 node -c <filename>.js 命令检查语法。
  • 变量作用域检查:在重构代码(特别是移动代码块)时,必须严格检查变量的作用域(Block Scope)。
    • 错误案例:将定义在 try 块内部的 const 变量(如 assistantMsg)引用的代码移动到 try...finally 块外部,导致 ReferenceError。
    • 避免方法:移动代码前,先确认所引用的变量在目标位置是否可见。如果需要在块外使用,请将变量声明提升到块外(使用 let)。

本仓库未提供统一格式化/校验工具配置文件(例如 ESLint/Prettier/Go fmt hook),修改时建议保持周边代码的现有缩进与命名风格。

4. 测试

Go 单元测试

  • 计划仓库测试:backend/internal/plan/repository_test.go(覆盖 Save/FindAll/Delete 等;测试会把 dataDir 指向临时目录,见 backend/internal/plan/repository_test.go:14-backend/internal/plan/repository_test.go:34)。
  • 限流器惰性清理测试:backend/internal/middleware/middleware_test.go(覆盖 TTL/清理间隔/阈值等,见 backend/internal/middleware/middleware_test.go:22)。

运行方式(在 backend/ 下):go test ./...

后端集成测试脚本

backend/scripts/backend_test.sh 会:

  • 生成临时 backend/configs/config.json(包含 jwtSecret 与用户 salted SHA256 hash)
  • 编译并启动后端,然后用 curl/jq 走一组 API(含登录、计划 CRUD、分享)

该脚本依赖:opensslsha256sumjqcurl(见 backend/scripts/backend_test.sh:47backend/scripts/backend_test.sh:102backend/scripts/backend_test.sh:115)。

前端端到端测试(Playwright)

前端 E2E 测试位于 test/,通过一个轻量静态服务器加载 static/index.html 做 UI 交互验证(测试用例会 mock 搜索等外部请求,避免依赖后端)。

  • 本地运行(不下载浏览器内核,复用系统 Chrome):在仓库根目录执行 cd test && npm run setup:local && npm run test:local
  • CI 运行(runner 上安装 Chromium 后 headless 跑):cd test && npm run setup:ci && npm run test:ci(见 /.github/workflows/ci_frontend.yml:1

更多说明见 test/README.md

5. 安全与数据保护

配置与密钥

  • 后端必须配置 jwtSecret 且用户列表不能为空(见 backend/internal/config/config.go:42-backend/internal/config/config.go:50)。
  • scripts/generate_config.sh 会用 openssl rand 生成 jwtSecret,并对密码做 salted SHA256(见 scripts/generate_config.sh:71scripts/generate_config.sh:80)。

认证与访问控制

  • JWT 认证通过 Authorization: Bearer <token> 传递(见 backend/internal/middleware/middleware.go:90-backend/internal/middleware/middleware.go:112)。所有 /api/v1 下(除登录和分享外)的接口,包括计划管理和 AI 助手,都需要认证。
  • /api/v1/login 有 IP 限流中间件(见 backend/internal/server/server.go:69backend/internal/middleware/middleware.go:69)。
  • 计划文件仓库包含路径遍历防御:对 idfilepath.Base(id) == id 校验(见 backend/internal/plan/repository.go:54backend/internal/plan/repository.go:76backend/internal/plan/repository.go:148)。

公开接口提醒

  • 分享接口无需认证:GET /api/v1/share/plans/:id(见 backend/internal/server/server.go:71-backend/internal/server/server.go:75)。
  • Nginx 仅将 /api/ 反代到后端(见 nginx.prod.conf:25),前端静态文件直接由 Nginx 读取。

浏览器本地数据

  • 离线数据保存在浏览器 localStorage(例如路书数据与在线 token)。共享电脑使用时需注意清理。

6. 配置与环境

后端配置文件

  • 默认读取路径:运行目录下的 configs/config.json(见 backend/internal/config/config.go:26)。
  • 生成位置:项目根目录脚本写入 backend/configs/config.json(见 scripts/generate_config.sh:23scripts/generate_config.sh:114)。
  • 字段:portallowed_originsallow_null_origin_for_devjwtSecretuserssearchai(见 backend/internal/config/config.go:15-backend/internal/config/config.go:23)。
  • AI 配置 (ai 字段):
    • enabled: 是否启用 AI 助手。
    • base_url: AI 服务提供商的 Base URL(兼容 OpenAI 格式)。
    • key: API Key。
    • model: 模型名称。
  • 搜索配置 (search 字段):
    • providers: 目前支持 gaode(高德地图),需配置 key

CORS

Gin 内置了一个“允许来源列表 + 可选 null origin”逻辑:

  • allowed_origins 命中时回写 Access-Control-Allow-Origin 等头
  • allow_null_origin_for_dev=trueOrigin: null,允许用于 file:// 本地开发测试

backend/internal/server/server.go:19-backend/internal/server/server.go:41

数据目录

  • 后端计划文件目录是相对路径 data/(见 backend/internal/plan/repository.go:17)。
  • Docker 镜像会创建 /app/data 并将后端工作目录设置为 /app(见 Dockerfile:35docker-entrypoint.sh:7),保证相对路径落在容器内可写目录。

7. 双后端(Go / Cloudflare Worker)对齐规则

cloudflare/ 目录包含一个完整的 Cloudflare Worker 实现,可作为 Go 后端(backend/)的 Serverless 替代方案

  • 功能范围:覆盖 Go 后端核心能力(JWT 认证、计划 CRUD、操作式地图编辑 API、搜索代理、KV 数据存储),并支持 Cloudflare Workers AI。
  • 部署与使用:详情见 cloudflare/README.md
  • 切换方式:前端通过双击标题配置 BaseURL,可切换到 Worker 后端。
  • 对齐要求(重要)
    • cloudflare/worker.js 必须与 backend/ 保持严格的功能对齐。
    • 任何对 Go 后端 API 的修改(路径、请求参数、返回 JSON 结构、字段命名、错误码),都必须同步修改 cloudflare/worker.js
    • 本地 Agent 或外部自动化编辑地图时,优先使用 POST /api/v1/plans/:id/map/actions,不要让 Agent 用 GET 后手工改 content 再 PUT 的方式重写整包数据。
    • KV 存储的 JSON 结构尽量与 Go 后端文件存储兼容(CamelCase 字段名),方便潜在迁移。
    • TrafficPos 经纬度顺序(机场:lat,lon;火车站:lon,lat)已对齐;修改时必须保持一致。

8. Agent/协作行为准则(CRITICAL)

  • Git 写操作:除非用户明确要求,否则严禁自动执行 git add / git commit / git push 等。
  • 推送后流水线监控(强制):一旦按用户明确要求执行 git push,必须立即监控该 push 触发的所有 GitHub Actions。使用 gh run list/gh run view --log-failed 定位失败 job,修复并重新推送,直到该 commit 或修复 commit 对应的相关流水线全部成功。不得在未确认流水线状态的情况下报告“完成”。
  • Plan Mode 限制:严禁使用 ExitPlanMode 工具;按用户指令直接执行。
  • 重置/回滚限制(重要):任何 reset / checkout / restore / “还原文件”等操作,只允许回滚 我本次会话里明确修改过的文件;涉及到非我修改的文件,除非用户明确点名要求,否则一律禁止重置。

AI 错误信息规范(必须遵守)

  • 给 AI 的失败信息必须包含可定位上下文,避免只返回“为空/失败”等不可操作描述。
  • 至少包含:action 名称 + 关键参数(例如 date)+ 关键内容截断预览(例如 notePreview)+(如适用)失败分支原因(如“日期不在行程中/ID 无效/参数缺失”)。
  • 示例:更新日期备注失败: 参数缺失(date="2025-01-01", note=undefined) / Action Failed: update_date_note - date not in itinerary. date=2025-01-01, notePreview=...

9. 变更与测试规则(前端优先)

配置文件保护(重要)

  • 除非用户明确要求,否则禁止修改 backend/configs/config.json(该文件常被用作本地测试配置,误改会影响开发者环境)。

前端变更必须同步更新 E2E(强制)

  • 任何前端行为变更(按钮/弹窗/交互流程/快捷键/删除-撤销语义/DOM id 或 class/文案影响选择器等)必须同步:
    • 新增或更新 Playwright E2E 用例(test/tests/*.spec.mjs),并确保选择器稳定(优先使用 id)。
    • 若变更影响调试模式、导入导出、在线模式等关键路径,必须补充对应覆盖用例。

运行测试的约束(避免全量耗时)

  • 默认只运行新增/修改的那几个用例文件,不要一次性跑全量(除非用户明确要求)。
  • 推荐命令:cd test && npm run test:local -- tests/<your-new>.spec.mjs
  • 修改前端 JS 后必须做语法检查:for f in static/script.js static/app_*.js static/html_export.js; do node -c "$f"; done

代码组织规则

  • 代码组织: static/script.js 现在主要承担主入口与类骨架职责。新增前端逻辑时,优先放入最相关的现有 static/app_*.js 模块;只有在职责边界明确且现有模块都不合适时,才新增新的模块文件。
  • 入口约束: static/script.js 顶部仍承载 apiBaseUrl 与版本号初始化,且 GitHub Pages 工作流会向该文件首行注入 window.API_BASE_URL;不要把这段入口逻辑移走。
  • 加载约束: 若新增新的 static/app_*.js 文件,必须同步更新 static/index.htmlstatic/sw.js.github/workflows/ci_frontend.yml

10. 调试模式 (Debug Mode)

项目内置调试模式,由 static/debug.js 实现,方便查看应用内部状态并导出现场信息。

  • 激活方式:URL 添加查询参数 ?debug=true(例如:http://localhost:5215/?debug=true)。
  • 入口:地图右下角控件区(帮助按钮 旁边)出现 🐞 按钮。
  • 核心功能 (DebugModule):
    • 环境嗅探:收集 UA、屏幕尺寸、时区、网络状态等。
    • 状态快照:完整抓取 app.markersapp.connectionslocalStorage 等数据。
    • 日志捕获:Hook consolewindow.onerror,记录最近 200 条日志与 50 条错误(仅在 debug 模式下生效)。
    • 存储分析:统计 localStorage 各 key 占用大小,帮助排查配额问题。
  • 交互:调试窗口支持关键词过滤、JSON 导出下载、一键复制全部信息。

11. PWA 与离线能力

RoadbookMaker 实现了 Progressive Web App (PWA) 支持,提供类原生应用体验与离线访问能力。

核心文件

  • static/manifest.json:Web App Manifest,定义应用名称、图标、主题色及显示模式(Standalone)。
  • static/sw.js:Service Worker 脚本。导航请求走 Network First,静态资源走 Stale-While-Revalidate
    • 预缓存列表:index.html, style.css, script.js 及所有核心 JS 模块。
    • 运行时缓存:同源静态资源与 unpkg CDN 资源按需写入运行时缓存,不再因为外部 CDN 预缓存失败而让安装整体失败。
    • 更新机制:缓存名由资源清单 + 手动 schema 版本共同生成,activate 时仅清理旧的 roadbook-* 缓存。

交互逻辑

  • 安装提示:在 static/script.js 中监听 beforeinstallprompt 事件,拦截默认提示,为自定义安装引导预留接口(当前实现为拦截后保存事件,等待后续扩展 UI 触发)。
  • 更新检测:每次页面加载时会自动注册/更新 Service Worker。