fix(web): 知识库图谱预览与保存修复(CSP 放行 S3 图片源 + imageUrl 表单注册)

- server.cjs: img-src 放行 http://100.83.103.1:7480,修复编辑页预览图被 CSP 拦截
- Knowledge.tsx: 新增隐藏 Form.Item name=imageUrl,修复 validateFields 提交时丢弃图片 URL
- 部署至 100.83.103.1:5174(备份 /home/pan/backups/web-20260812-1605)
- 补绑白僵病 image_url(UPDATE 1),更新故障排查/开发交接/变更记录
This commit is contained in:
weijuesen
2026-08-12 16:16:28 +08:00
parent 4f66a71c6c
commit fa01b1cb97
5 changed files with 111 additions and 1 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ const SECURITY_HEADERS = {
'X-Frame-Options': 'DENY',
'Referrer-Policy': 'no-referrer',
'Content-Security-Policy':
"default-src 'self'; img-src 'self' data: blob:; media-src 'self' blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'",
"default-src 'self'; img-src 'self' data: blob: http://100.83.103.1:7480; media-src 'self' blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'",
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
};
+4
View File
@@ -183,6 +183,10 @@ function DiseaseTab() {
)}
</Upload>
</Form.Item>
{/* imageUrl 通过隐藏字段注册到表单,保证 validateFields 提交时携带(修复:未注册字段会被丢弃) */}
<Form.Item name="imageUrl" hidden>
<Input />
</Form.Item>
<Form.Item label="病原" name="pathogen">
<Input.TextArea rows={2} />
</Form.Item>
+24
View File
@@ -1,5 +1,29 @@
# 变更记录
## 2026-08-12 Web 知识库图谱预览与保存修复(故障排查 #三十二)
### 变更背景
用户反馈 Web 知识库上传新症状图谱后编辑页预览图打不开、提交后图谱未保存。排查确认上传与存储正常(Ceph 对象存在、匿名 GET 200),问题在前端:CSP 拦截跨源预览 + 表单未注册 `imageUrl` 导致提交体缺失该字段。
### 变更内容
| 文件 | 变更 |
|------|------|
| `web/src/pages/Knowledge.tsx` | 新增隐藏 `Form.Item name="imageUrl"`,使 `validateFields()` 提交时携带图片 URL |
| `web/server.cjs` | CSP `img-src` 增加 `http://100.83.103.1:7480`(开发 S3 源) |
### 部署与验证
- 本地:`npm run lint`(通过)、`npm run build`(通过,新 bundle `index-CSVEb7gG.js`)。
- 部署:备份 `/home/pan/backups/web-20260812-1605/`(旧 dist、server.cjs、web.log),上传新产物并重启 node。
- 验证:`http://localhost:5174/` 200CSP 头含 S3 源;新 bundle 200`/api/v1/health` 200DB 白僵病 `image_url` 已补绑(`UPDATE 1`),图片匿名 GET 200。
### 回滚点
- 前端:恢复 `/home/pan/backups/web-20260812-1605/` 旧产物后重启 node。
- 数据:`/home/pan/backups/diseases-20260812-1605.dump`pg_restore),或执行反向 UPDATE 将 `image_url` 置空。
## 2026-08-12 知识库初始版开发与部署
### 变更背景
+27
View File
@@ -12,6 +12,33 @@
---
## 2026-08-12 补充:Web 知识库图谱预览与保存修复(故障排查 #三十二)
### 做了什么
- 修复 Web 知识库「蚕病百科」两个问题:编辑页图谱预览打不开(CSP 拦截跨源 S3 图片)、提交后 `imageUrl` 未保存(表单未注册该字段)。
- `web/src/pages/Knowledge.tsx`:「症状图谱图片」后新增隐藏 `Form.Item name="imageUrl"`,保证 `form.validateFields()` 提交时携带图片 URL。
- `web/server.cjs`CSP `img-src` 放行 `http://100.83.103.1:7480`(开发 S3 源)。
- 已部署并重启前端(新 bundle `index-CSVEb7gG.js`),并按用户确认直接改库把白僵病已上传的图谱 URL 补绑到 `diseases.image_url`
### 思路与决策
- 根因定位:日志显示上传 200、PATCH 200,但 `image_url` 为空 → 定位为前端表单未注册 `imageUrl`antd `validateFields()` 只返回已注册字段),以及 CSP 跨源拦截预览。
- 修复方式:用隐藏 `Form.Item` 注册字段(antd 标准做法),避免手工拼请求体;CSP 采用放行开发 S3 源,生产切云 OSS/COS 时再替换为 OSS/CDN 域名或改同源代理。
- 数据补绑:用户明确选择「直接改库」,回滚点为备份的 `diseases` 表 dump 与逐行 UPDATE 反向语句。
### 验证
- `web``npm run lint` 通过(仅既有无关警告)、`npm run build` 通过。
- 机制红绿验证:用 rc-field-form 验证 `validateFields()` 会丢弃未注册的 `imageUrl`,注册/合并后可携带。
- 部署验证:`http://localhost:5174/` 200CSP 头含 `http://100.83.103.1:7480`;新 bundle 200`/api/v1/health` 200bundle 中可见 `name:"imageUrl", hidden:!0`
- 数据验证:`UPDATE 1`;查询确认白僵病 `image_url` 已绑定;图片匿名 GET 200image/jpeg)。
### 回滚点
- 前端:`/home/pan/backups/web-20260812-1605/`(旧 dist、server.cjs、web.log)恢复后重启 node。
- 数据:`diseases` 表 dump `/home/pan/backups/diseases-20260812-1605.dump`;或执行 `UPDATE diseases SET image_url=NULL, updated_at=now() WHERE id='17b1cae4-21cc-4996-9db3-96f785aef800';`
## 2026-08-12 模块:知识库初始版(计划 #10)
### 做了什么
+55
View File
@@ -1023,3 +1023,58 @@ APP 使用 `react-native-vector-icons` 的 `MaterialCommunityIcons` 字体渲染
- `ScrollView` 的 `style` 设置容器样式(如 flex、背景色),`contentContainerStyle` 设置内容样式(如 padding)
- 仅修改 JS/TS 代码,Metro reload 即可生效,无需重新构建 APK
---
## 三十二、Web 知识库症状图谱预览打不开且保存不生效
**现象**:Web「知识库 → 蚕病百科」编辑病种时上传新的症状图谱:编辑弹窗里的预览图打不开;点击提交提示"保存成功",但重新打开/刷新后图谱仍未绑定(`image_url` 为空)。
**原因(两个独立问题,均为前端问题,后端与存储正常)**:
1. **预览图打不开:CSP 拦截跨源图片**
`web/server.cjs` 的 `Content-Security-Policy` 为 `img-src 'self' data: blob:`,只允许同源图片。上传成功后返回的图片 URL 是 `http://100.83.103.1:7480/silk-images/...`Ceph RGW,端口 7480 与页面 5174 不同源),浏览器直接拦截,图片本身可匿名访问(200 image/jpeg)。
2. **保存不生效:`imageUrl` 未注册到表单,提交时被丢弃**
`web/src/pages/Knowledge.tsx` 上传成功后执行 `form.setFieldValue('imageUrl', res.url)`,但「症状图谱图片」的 `Form.Item` 只有 label、没有 `name="imageUrl"`。antd 的 `form.validateFields()` 只返回已注册(有 `Form.Item name`)的字段,`imageUrl` 不在其中,因此 `PATCH /knowledge/diseases/:id` 的请求体里根本没有 `imageUrl`,后端收到 200 但 `image_url` 列不更新(`updated_at` 会变)。
**排查证据(开发服务器 100.83.103.1**
- `web/web.log``POST /api/v1/knowledge/images` → `PATCH /api/v1/knowledge/diseases/17b1cae4-21cc-4996-9db3-96f785aef800` → `GET /api/v1/knowledge/diseases`
- `server-go/server-go.log`(实际生效日志,非 `/home/pan/silk/server-go.log`):15:57:54 `POST images` 20015:58:03 `PATCH` 200
- Ceph `silk-images` 桶:存在 `knowledge/20260812/36a2bc56e466fb41.jpeg`286608 字节,image/jpeg),匿名 GET 200
- PostgreSQL:白僵病 `image_url` 为 NULL;全表无任何病种有 `image_url`
- 部署产物校验:服务器 `server.cjs` md5 与仓库一致(CSP 生效);新 bundle 中已含修复代码
**处理**
1. 前端修复(`web/src/pages/Knowledge.tsx`、`web/server.cjs`):
- 「症状图谱图片」后新增隐藏字段,使 `imageUrl` 注册进表单并被 `validateFields()` 返回:
```tsx
<Form.Item name="imageUrl" hidden>
<Input />
</Form.Item>
```
- CSP `img-src` 放行开发环境 S3 源:`img-src 'self' data: blob: http://100.83.103.1:7480`
2. 构建与部署(本地 `npm run lint`、`npm run build` 均通过):
- 备份旧产物:`/home/pan/backups/web-20260812-1605/`dist、server.cjs、web.log
- 上传新 `dist` 与 `server.cjs`,重启 node(旧 pid 3764542 → 新 pid 3803472
- 验证:`http://localhost:5174/` 200CSP 头含 `http://100.83.103.1:7480`;新 bundle `index-CSVEb7gG.js` 200`/api/v1/health` 200
3. 数据补绑(用户确认直接改库):
```sql
UPDATE diseases
SET image_url = 'http://100.83.103.1:7480/silk-images/knowledge/20260812/36a2bc56e466fb41.jpeg',
updated_at = now()
WHERE id = '17b1cae4-21cc-4996-9db3-96f785aef800';
```
结果:`UPDATE 1`,查询确认 `image_url` 已写入;图片匿名 GET 200。
**验证结果**:DB 行已绑定图谱 URL;CSP 已放行;部署产物包含隐藏字段注册;lint/build 通过(详见上文)。
**回滚点**
- 前端回滚:恢复 `/home/pan/backups/web-20260812-1605/` 下的旧 `dist`/`server.cjs` 并重启 node
- 数据回滚:`UPDATE diseases SET image_url = NULL, updated_at = now() WHERE id = '17b1cae4-21cc-4996-9db3-96f785aef800';` 或 `pg_restore` 恢复 `/home/pan/backups/diseases-20260812-1605.dump`
**注意事项**
- `/home/pan/silk` 不是 git 仓库,无法打 tag 回滚,只能依赖文件备份(本次已备份)
- 部署指南中的 `admin/admin123` 已无法登录(401 用户名或密码错误),本次 API 冒烟改用 DB 直接验证;如需 API 级冒烟请先确认当前 admin 密码
- 已知边界:编辑页若点「移除」图片再保存,`imageUrl` 会以 `undefined` 提交(键被省略),不会清空库里已有图片;如需「移除即清空」后续应显式传 `null`
- CSP 当前放行的是开发服务器 IP;生产切云 OSS/COS 时需把 OSS/CDN 域名加入 `img-src`,或改为同源代理