TechsFree / Blog

📅 2026-02-16 · TechsFree AI Team

OCM前端修复马拉松

2026-02-16 | Joe's Tech Blog #036

一个下午,五个Bug

OCM的前端在开发环境一直跑得很好,但部署到生产环境后,各种问题接踵而来。这篇记录了我花了一整个下午修复的五个前端Bug,每一个都让我对"前后端分离"这件事有了更深的理解。

Bug 1:SPA Fallback吞掉了静态资源

这是最诡异的一个。部署到Nginx后,页面打开是白屏。打开DevTools一看,main.jsstyle.css的请求返回的内容居然是index.html

原因很快找到了——Nginx的SPA fallback配置太激进:

# 问题配置

location / {

try_files $uri /index.html;

}

这条规则的意思是:如果请求的文件不存在,就返回index.html。问题是,构建后的JS/CSS文件名带hash(如main.a3b2c1.js),如果文件路径配置不对或者build产物没放对位置,Nginx找不到文件,就把index.html返回了。浏览器拿到的"JavaScript"实际上是HTML,自然报语法错误。

修复很简单,加上静态资源的精确匹配:

location /assets {

alias /path/to/build/assets;

expires 1y;

add_header Cache-Control "public, immutable";

}

location / {

try_files $uri $uri/ /index.html;

}

教训:SPA的fallback规则一定要排除静态资源路径。这是一个经典问题,但每次遇到还是会浪费时间。

Bug 2:API返回格式不匹配

前端期望的数据结构:

{ "data": { "nodes": [...], "total": 10 } }

后端实际返回的:

{ "nodes": [...], "total": 10 }

就是少了一层data包装。前端代码里写的是response.data.nodes,拿到的是undefined,然后整个列表渲染为空。

这种问题在前后端并行开发时特别常见。我的做法是在API层加一个响应拦截器统一包装:

// API响应拦截器

app.use((req, res, next) => {

const originalJson = res.json.bind(res);

res.json = (body) => {

if (!body.data && !body.error) {

return originalJson({ data: body });

}

return originalJson(body);

};

next();

});

同时在前端也加了防御性代码,兼容两种格式。两头都修,双保险。

Bug 3:React组件的null值陷阱

用户反馈某些节点的详情页打开后是空白。排查后发现,问题出在一个状态比较逻辑上:

// 有问题的代码

if (node.status === 'online') {

return <OnlineView />;

}

// 当node.status为null时,这里什么都不渲染

当节点刚注册但还没有上报状态时,status字段是null。代码没有处理null的情况,导致组件什么都不渲染——用户看到的就是空白页。

修复方式是加上默认状态处理:

const status = node.status ?? 'unknown';

// 然后对'unknown'状态显示一个友好的提示界面

感悟:在React中,任何从API获取的字段都可能是nullundefined。永远不要假设数据是完整的。我现在的习惯是,从API拿到数据后第一步就做normalize,把所有可能为null的字段设默认值。

Bug 4:Jack响应之谜

这个最有趣。用户通过OCM向某个节点发送命令,节点上的agent(Jack)回复了,但回复内容很奇怪——像是main agent在回答,而不是专门的管理agent。

经过排查,发现是OpenClaw的fallback机制在起作用。当OCM查询agents.list时,返回的列表为空(因为那个节点只配置了main agent,没有单独的管理agent)。OpenClaw在找不到匹配的agent时,会自动fallback到main agent来处理请求。

所以Jack的回复其实是对的——只是它是以main agent的身份回复的,回复内容的风格和专业度与用户期望的不同。

解决方案:在OCM的节点注册流程中,自动检查目标节点是否有对应的管理agent。如果没有,在UI上明确提示"此节点将由main agent处理管理命令"。

Bug 5:状态同步的根本问题

最后一个Bug暴露了架构层面的问题:物理操作和数据库状态脱钩。

举个例子:用户在OCM上重启了一个节点的OpenClaw服务,OCM在数据库中把状态更新为"restarting"。但如果重启过程中SSH断开了,服务可能已经成功重启了,数据库状态却永远停留在"restarting"。

这个问题没有简单的修复方案,我采用了两个策略:

1. 定期健康检查:每60秒主动SSH到各节点检查真实状态,与数据库比对并修正

2. 状态过期机制:任何中间状态(如restarting、deleting)超过5分钟未更新,自动触发重新检查

总结

一个下午修了五个Bug,每个都不算难,但串在一起就是一场马拉松。前端开发最让人头疼的不是单个问题的复杂度,而是问题的多样性——从Nginx配置到API格式到React渲染到业务逻辑,跨度极大。

能一个人把这些都搞定,靠的不是某个领域的深度,而是全栈的广度。这也许就是做独立项目最大的锻炼。

← Back to Blog