Commit Graph
8 Commits
Author SHA1 Message Date
tamakyi b4c2a8452f 审计修复:后端(安全 / 数据一致性 / 性能)
audit-2026-09-21.md 里的后端条目落地。每条都配了回归守卫,并逐条做过变异验证
(注入原 bug → 确认按正确理由变红 → 还原 → 变绿)。

安全
- CSRF 只加同源 Origin/Referer 守卫(csrf.go),不动模板
- 会话 cookie 的 Secure 按请求判断;两步验证两闸(loginGuard + 验证码一次性)
- 跳转收口到 safeInternalPath;clientIP 只认 XFF 最后一段
- GPX 按属主圈定且查 RowsAffected;下载名走 downloadDisposition
- 上传口统一 MaxBytesReader;落盘后的失败路径清掉半成品

数据一致性
- 区域判定两端各过一遍再 AND(apiTrack 与 movementsWhere 对齐)
- Filter.Verdict 全域一致(下推到 visitsWhere);聚合刷新必须返回 error
- 附属数据改挂坐标键,Go 与 SQL 两套表达式逐位一致
- 脏备份的伪时间/越界坐标不再进领域模型;rows 循环统一 drainRows
- 合并与导出:writeRawRow 上抛错误、悬空引用清理、原生列补全

性能
- placeCols 相关子查询带 dataset_id 前导列;findSourceArchive 先按大小筛
- 批量勾选改 ANY($3::bigint[]);fillupCache 加上限;同一查询不再查两遍

另:新增「全新库」冒烟测试 —— 空库跑完 Migrate 再用应用侧读查询真打一遍,
迁移块排到依赖表之前这种错只有新库会炸,老库毫无症状。
2026-09-24 20:37:16 +08:00
tamakyi 693ee82ab8 修「填了显示名却说还没有中文名」,并给枚举拼错拦一道
用户报:把某行的导出值改成 MKPOICategoryCarRent、又手填了显示名「租车」,
保存成功、框里也回填了,页面却仍挂着「还没有中文名」。

两件事叠在一起:

1. 判断错了。Missing 只看内置表,没看有没有自定义覆盖 ——
   填过了还照样提示。改成 categoryLabelMissing(v) && 没有覆盖。
   补 TestCategoryLabelMissingRespectsOverride,并做变异验证(去掉那半个条件即报错)。

2. 值本身是拼错的。MKPOICategoryCarRent 不是真机的枚举,正确是
   MKPOICategoryCarRental(少了个 al)——站内看不出任何异常,但导回手机后
   那个地点显示不出类别。这正是加「显示名」时提醒过的风险,所以补一道防线:
   编辑页把内置枚举名渲染给脚本,往 poi_category 写值的两个入口
   (类别台账的「改名」、地点行内编辑的「自定义类别」)都过 okEnum(),
   不在已知枚举里就 confirm,还能猜「你是不是想写 MKPOICategoryCarRental」。
   中文值和已知枚举不打扰。

另外:这次用脚本往模板写 JS 时,\n 被多转义一层成了真换行,JS 单引号字符串
含裸换行 ⇒ 整块 <script> 语法错误、页面交互全哑,而模板解析测试照样通过。
新增 inline_script_test.go 扫所有内联脚本块(现在 31 个),字符串跨行就报错
并指出文件行号,已做变异验证。
2026-09-20 12:22:11 +08:00
tamakyi 82e63412c0 类别显示名可自己维护,顺手把两套对不上的中文名合成一套
你要改的那个「海滩」原来根本不在数据里——它是 Go 里硬编码的 map,所以改不动。
更糟的是这样的 map 有**两张**,而且对不上:

  MKPOICategoryFoodMarket   菜市场(编辑页)  /  食品市场(补全页、GPX)
  MKPOICategoryCafe         咖啡馆              /  咖啡厅
  MKPOICategoryFitnessCenter 健身房             /  健身中心
  外加 ATM / Atm 大小写各写一份、一条永远匹配不上的 MKPOICategoryMarina2

现在合成一张内置表(42 条,冲突处取编辑页那套),并在它上面加了一层站点覆盖:

- 新表 category_labels(value, label),站点级、不挂 dataset——值本身才是稳定键
- 端点 POST /admin/edit/category/label;传空 label = 删掉覆盖行、恢复内置默认名
- 类别台账那行改成两个带标签的输入框:
      导出值 [MKPOICategoryBeach]   ← 会跟着导出写回手机
      显示名 [               ]      ← 只影响站内显示,placeholder 写着默认值
  是 MKPOICategory 枚举但内置表里没有的,会提示「填一个中文名」,这就是你要的补全入口

**显示名绝不写进 places.poi_category**——中文跟着写进去就把导出数据污染了,
专门加了 TestCategoryLabelNeverTouchesValue 钉这条。显示名会贯穿到所有中文名的地方:
编辑页/照片页/补全页的下拉、补全页「在用」标签、访客地点页、GPX 的 desc。

连带删掉两个绑在旧表上的模板函数 catName / poiname:模板里算不出显示名(要读站点覆盖),
改成在 Go 侧算好塞进数据(FillupRow.CatLabel、PlaceData.CategoryLabel)。
2026-09-20 10:54:51 +08:00
tamakyi da6fe54ab4 数据编辑页加「活动与类别」卡片:改名 / 删除 / 新增 / 合并
活动的管理能力其实后端早就有(create/update/delete 三个端点),但入口只藏在
「到访记录」卡片里——要先在下拉里选中某个活动,再点按钮弹 prompt,看不见
「哪些活动一条到访都没挂」(实测有 0 条的活动);类别则完全没有集中入口。

现在页首多了一张卡片(唯一默认展开的),两个表格都带用量:

- 活动:名称行内改、删除(有引用时二次确认并报条数)、新增,
  另有一条「把[A]并到[B]」——改名解决不了「两个名字指同一件事」,
  合并会把到访转过去、把 is_home/is_work 抄成目标的,再删源活动、清它的原始行留档。
- 类别:类别没有独立的表,值就长在 places.poi_category 上,所以「改名 / 清空」
  都是按值批量作用到地点;改成另一个已有值就是合并,不另做按钮。

新增端点:/admin/edit/category/rename、/admin/edit/category/clear、
/admin/edit/activity/merge。编辑页那条 DISTINCT poi_category 查询顺手改成
GROUP BY 带计数,一份数据两用。

顺带修一处会静默失效的写法:新表原来只写了 tb-stack,漏了配对的 tb,
而 CSS 全是 `.tw > table.tb.tb-stack …` ⇒ 一条堆叠规则都没命中,窄屏下表仍是
640px 底宽。横滑是在 .tw 里滚的、页面不溢出,肉眼很难发现,守卫测试也只查
「有没有 tb-stack」——已补上「有 tb-stack 必须有 tb」并做了变异验证。
2026-09-20 09:51:43 +08:00
tamakyi 30c502ce7b 备份与迷雾的差异分析、合并执行,附属数据改挂坐标键
备份(.rondbackup)与迷雾(.fwss)各加一层「先看差异再动手」:

- 迷雾:上传先出差异页,说清叠加能补多少、整体替换会丢多少,并在真实底图上
  用绿/灰/红标出会新增 / 不变 / 会丢的区域
- 备份:与当前数据集按 UUID 逐条比对(rond 每次导出都会重排 Z_PK,编号认不出人),
  新增合入、冲突逐条选、仅站内有逐条勾选删除,全在一个事务里执行;
  合入的行写回 entity_raw,保证再传同一份备份时差异归零
- 地点按坐标对齐:站点的「补全地址」会把地点改名,拿名称当键会让一整个地点的
  到访全变成假冲突
- 附属数据(备注/结论/访客票/图片)从 dataset_id+src_pk 换成坐标键,换数据集不再失联;
  访客票没有 dataset_id,回填按活跃数据集认领
- 修正 contentHash 拿 Z_PK 当指纹的隐患,以及两处「src_pk 跨快照稳定」的错误注释

导出格式(.rondbackup / .fwss)保持原样,往返测试通过。
2026-09-19 16:04:00 +08:00
tamakyi 86e420bd4e 新建地点的活动下拉可即时新增,地点行内编辑支持批量改该地点全部到访的活动
站长反馈「已有地点编辑和新建里只有类别没有活动能选」。实测澄清:新建地点卡一直有活动下拉,
只是形态与类别/照片页不一致(旁边常显一个新建框、下拉里没有「+ 新增活动…」);真正完全没有
活动字段的是地点列表行的「编辑」。

- 新建地点卡:活动下拉补「+ 新增活动…」哨兵,旁边的新建框改成选中哨兵才展开;
  删掉原来那对 #activity-add/#new-activity-name 监听——三处活动下拉现在共用同一套
  `visit-act-*` 委派处理器,重复监听会建出两条活动。
- 地点行内编辑:加「活动(改该地点全部到访)」下拉,默认「(不修改)」不预选当前值
  (否则每次改坐标保存都会重写历史到访的类型),并标出当前类型构成;选了真实活动时
  提交前 confirm 一次。
- 服务端(places 没有活动列,活动挂在到访上):editPlaceUpdate 解析 activity_id,空值=不修改,
  与地点行同一事务内 UPDATE visits 的 activity_id/is_home/is_work,flash 报改了几条;
  哨兵值/不存在的 id/别的数据集的活动一律拒。导出无需改动(ZVISIT.ZACTIVITY_ 本就是管理列)。
- 回归 internal/web/place_activity_test.go:批量改(含别的地点不受影响)、空值不修改、越权被拒、
  模板契约、以及导出包里那批到访的 ZACTIVITY_ 必须换成新活动的 rond 编号。
- 顺手:TestMovementCleanExport 建出 ROND_CLEAN_OUT 的父目录,别再把「目录不存在」报成导出失败。
2026-09-18 21:23:13 +08:00
tamakyi ad2531c113 修复图片上传分类无法新增、潜在movementWindow容差问题、404页面未生效问题 2026-09-18 17:06:22 +08:00
tamakyi b327bc1230 first release 2026-09-18 13:05:49 +08:00