Agent Portal — 03-28~30 数据重构与移动端优化¶
关键产品决策¶
- Dad 决策:移动端时间选择器改为 wheel picker
- 手机上时间选择器要改成右侧上下滑动的刻度表(wheel picker)
- 默认显示最新时间,且刻度居中;不要支持左右滑动
- 移动端要优化,避免双指放大
-
原因:传统时间选择器在移动端体验差,需要原生化的滚动体验
-
Dad 决策:服务器聊天与 bot 聊天逻辑分离
- 服务器聊天与 bot 聊天逻辑不同:引用服务器时,只需把上下文带着一起发给 Ottor direct
- 不要复用 bot 聊天的复杂链路
-
原因:服务器状态查询是运维需求,应该直接到负责运维的 bot,而不是走通用聊天路由
-
Dad 决策:Portal 数据源从 Supabase 改为 PostgreSQL 直连
- 废弃 Supabase 连接方式,统一改为直连 PostgreSQL
- 所有硬编码 Secret 提取到环境变量
- 原因:Portal 已是重度数据应用,Supabase 成为多余中间层,直连 PG 更可控
技术问题与根因分析¶
移动端时间选择器漂移问题¶
- 现象:刻度没有对上,选中项漂移
- 根因:使用了
paddingTop: calc(50% - 32px),但 CSS 的%padding 是基于宽度而不是高度计算 - 解决方案:
- 重写 wheel mechanics,去掉依赖 CSS 百分比 padding 的方案
- 放弃
scroll-snap,改为更可控的 JS-based snap - 阻止横向滑动,仅允许纵向滚动
- 结果:选中项可精确居中,默认定位最新项
服务器聊天报错问题¶
- 现象:选中服务器后点击"对话"报
invalid_body_param - 根因:
CommandBar使用primaryTarget.user_id = "server-proxy-ottor",但这不是真实的 Mattermostuser_id - 解决方案:服务器 target 的
user_id改为 Ottor 的真实 MM user_id - 关键原则:聊天目标必须使用真实的用户标识,不能使用虚构的代理标识
PWA 添加到桌面报错¶
- 现象:iOS 添加到桌面时报错
- 根因:
- 浏览器请求
/manifest.json等 PWA 文件 server.cjs(Express)作为 SPA fallback 时找不到这些静态文件- 解决方案:
- 增加
manifest.json - 确保
public/经 Vite build 复制到dist/ - 确认
express.static(STATIC_ROOT)在 catch-all 之前 - 结论:Express SPA 服务必须保证静态文件优先,fallback 最后
Supabase 接口兼容性问题¶
- 现象:
/pg/query401,而 REST API 正常 - 根因:Supabase 的
/pg/rest/v1/正常,但/pg/queryendpoint 返回 401 - Portal 特殊性:39 个
dbQuery调用,包含 JOIN、聚合、DDL,不适合改成 REST - 解决方案:
- 利用 Supabase RPC:
exec_sql(执行 DDL)、run_sql(返回结果) - 修复
run_sql对 DML 的支持,改造后支持 SELECT 与 DML
Bot 架构/实现决策¶
移动端组件架构¶
- 实现方案:
- 新增
MobileTimePicker/ 重写InlineTimeMachine - 加 safe-area、PWA meta、touch 优化
- 禁止 pinch-zoom
- 技术细节:
- iPhone 触感反馈问题:Safari iOS 不支持
navigator.vibrate(),Web 端没有官方 haptic API - 处理原则:不能伪造不存在的系统能力,只能通过视觉/滚动反馈模拟
Bot 归档功能设计¶
- 业务需求:支持归档 bot 项目,归档后灰色显示、收缩到底部、可展开查看
- 技术方案:纯前端实现,使用
localStorage存归档列表 - 选型理由:归档是用户视图偏好,不是核心业务事实,前端本地存储足够
数据库与前后端分离¶
- 前后端分离架构:
- API →
3002 - 静态前端 →
3013 - Bearer token middleware 增加到
app.use(express.json())后 - 前端 API 调用统一附带 token
- 选型理由:职责清晰,便于后续鉴权、SSE、静态资源缓存与独立重启
PostgreSQL 直连方案¶
- 架构升级:
- Node 侧:
server.cjs的dbQuery / dbExec改为直连 PG - Python 侧:新增
push/db.py - 新依赖:Node
pg、Pythonpsycopg2-binary - 迁移策略:引入数据库抽象层,优先直连 PG,保留 Supabase HTTP fallback
里程碑¶
- 移动端体验大幅提升:wheel picker 精确定位,PWA 支持,触控区域优化
- 前后端分离完成:Bearer token 中间件就绪,API 与静态资源分离部署
- 数据库架构升级:从 Supabase API 迁移到 PostgreSQL 直连
- 服务器聊天链路修复:运维指令可直接发给 Ottor,避免复杂路由
- 归档功能上线:bot 项目可归档管理,提升界面整洁度
- PWA 功能完善:支持添加到桌面,离线友好,移动端体验原生化
关键技术栈¶
- 前端:React + Vite + TypeScript
- 移动端:wheel picker + safe-area + touch optimization
- 数据库:PostgreSQL 直连 + Supabase fallback
- 部署:前后端分离 + Bearer token + Docker Compose
- PWA:manifest.json + offline support
重要经验教训¶
- CSS 百分比 padding 基于宽度计算,移动端布局需特别注意
- Express SPA 服务必须保证静态文件优先级高于 catch-all 路由
- 聊天目标必须使用真实用户标识,虚构代理标识会导致 API 失败
- PWA 功能需要完整的静态资源构建链支持
这一阶段确立了 Portal 的现代化移动端体验和稳定的数据库架构,为后续大规模数据处理和复杂交互奠定了基础。