Files
Meshray-Manager/docs/DDNS_TXT 字段排查报告.md
2026-06-30 15:14:37 +08:00

378 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DDNS TXT 记录字段排查报告
**排查时间**: 2026-03-26
**状态**: ✅ **全链路都有 TXT 字段**
---
## 🔍 排查结果
### ✅ 前端页面 - 有 TXT 字段
**文件**: `web/src/views/Service/DDNSEdit.vue`
```vue
<!-- 65-79 -->
<!-- TXT 记录名称 -->
<el-form-item label="TXT 记录名称" prop="txt_record_name">
<el-input
v-model="formData.txt_record_name"
placeholder="_meshray._mesh"
clearable
/>
<div class="form-tip">
<el-icon><InfoFilled /></el-icon>
DNS TXT 记录前缀MeshSeed 密文将写入此记录
</div>
<div class="form-tip">
<el-icon><InfoFilled /></el-icon>
完整记录{{ formData.txt_record_name }}.{{ formData.domain || 'example.com' }}
</div>
</el-form-item>
```
**验证点**:
- ✅ 表单字段存在
- ✅ 默认值 `_meshray._mesh`
- ✅ 有提示信息
- ✅ 有完整记录预览
---
### ✅ 前端 API - 有 TXT 字段
**文件**: `web/src/api/ddns.js`
```javascript
/**
* @typedef {Object} DDNSConfig
* @property {'aliyun' | 'tencent' | 'cloudflare' | 'custom'} provider
* @property {string} access_key_id
* @property {string} access_key_secret
* @property {string} domain
* @property {string} txt_record_name // ← 第 9 行
* @property {'auto' | 'manual'} sync_mode
* @property {number} retry_interval
* @property {number} max_retries
* @property {boolean} enabled
*/
```
**验证点**:
- ✅ TypeScript 类型定义包含 `txt_record_name`
- ✅ 测试请求参数包含(第 29 行)
---
### ✅ 后端 Handler - 有 TXT 字段
**文件**: `internal/api/handler/ddns.go`
```go
// DDNSConfigRequest DDNS 配置请求
type DDNSConfigRequest struct {
Provider string `json:"provider"`
AccessKeyID string `json:"access_key_id"`
AccessKeySecret string `json:"access_key_secret"`
Domain string `json:"domain"`
TxtRecordName string `json:"txt_record_name"` // ← 第 28 行
SyncMode string `json:"sync_mode"`
RetryInterval int `json:"retry_interval"`
MaxRetries int `json:"max_retries"`
Enabled bool `json:"enabled"`
}
// 第 80-85 行:参数校验
if req.TxtRecordName == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "请输入 TXT 记录名称",
})
return
}
```
**验证点**:
- ✅ 请求结构体包含字段
- ✅ JSON tag 正确
- ✅ 有必填校验
---
### ✅ 后端 Service - 有 TXT 字段
**文件**: `internal/service/ddns.go`
```go
// DDNSConfig DDNS 配置(API 层使用)
type DDNSConfig struct {
Provider string `json:"provider"`
AccessKeyID string `json:"access_key_id"`
AccessKeySecret string `json:"access_key_secret"`
Domain string `json:"domain"`
TxtRecordName string `json:"txt_record_name"` // ← 第 42 行
SyncMode string `json:"sync_mode"`
RetryInterval int `json:"retry_interval"`
MaxRetries int `json:"max_retries"`
Enabled bool `json:"enabled"`
LastSyncAt *time.Time `json:"last_sync_at"`
PendingNetworks int `json:"pending_networks"`
Status string `json:"status"`
LastTestAt *time.Time `json:"last_test_at"`
LatencyMs int `json:"latency_ms"`
}
```
**验证点**:
- ✅ 配置结构体包含字段
- ✅ JSON tag 正确
---
### ✅ 数据库 Model - 有 TXT 字段
**文件**: `internal/model/models.go`
```go
// DDNSConfig DDNS 配置模型
type DDNSConfig struct {
ID string `gorm:"primaryKey;type:varchar(36)" json:"id"`
Provider string `gorm:"type:varchar(32);not null" json:"provider"`
AccessKey string `gorm:"type:varchar(128);not null" json:"accessKey"`
SecretKey string `gorm:"type:varchar(128);not null" json:"-"`
Domain string `gorm:"type:varchar(255);not null" json:"domain"`
TXTRecordName string `gorm:"type:varchar(255)" json:"txtRecordName"` // ← 第 232 行
SyncMode string `gorm:"type:varchar(16);default:'auto'" json:"syncMode"`
RetryCount int `gorm:"default:10" json:"retryCount"`
RetryInterval int `gorm:"default:300" json:"retryInterval"`
Enabled bool `gorm:"default:true" json:"enabled"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"createdAt"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updatedAt"`
}
```
**验证点**:
- ✅ 数据库字段存在
- ✅ GORM tag 正确
- ✅ JSON tag 正确
---
## 📊 全链路验证
| 层级 | 文件 | 字段名 | Tag | 状态 |
|------|------|--------|-----|------|
| **前端 UI** | `DDNSEdit.vue` | `txt_record_name` | N/A | ✅ |
| **前端 API** | `ddns.js` | `txt_record_name` | N/A | ✅ |
| **后端 Handler** | `ddns.go` | `TxtRecordName` | `json:"txt_record_name"` | ✅ |
| **后端 Service** | `ddns.go` | `TxtRecordName` | `json:"txt_record_name"` | ✅ |
| **数据库 Model** | `models.go` | `TXTRecordName` | `json:"txtRecordName"` | ✅ |
---
## ⚠️ 可能的问题
### 问题 1: 浏览器缓存
**症状**: 前端页面看不到 TXT 字段
**原因**: 浏览器缓存了旧版本的 JS 文件
**解决方案**:
```
1. 按 Ctrl+Shift+Delete 清除缓存
2. 或强制刷新:Ctrl+F5
3. 或在无痕模式下访问
```
---
### 问题 2: 前端未重新编译
**症状**: 修改代码后仍然显示旧界面
**原因**: 前端代码修改后没有重新编译
**解决方案**:
```bash
cd web
npm run build
```
然后重启后端服务。
---
### 问题 3: JSON Tag 不一致(已排除)✅
**检查结果**:
- 前端:`txt_record_name`
- 后端接收:`txt_record_name`
- 后端返回:`txt_record_name`
- 数据库:`txtRecordName` (Go 命名) / `txt_record_name` (JSON) ✅
**结论**: JSON Tag 完全一致,无问题。
---
### 问题 4: 数据库迁移问题(待验证)
**可能情况**: 数据库表结构没有 `txt_record_name`
**验证方法**:
```sql
-- 查看 ddns_configs 表结构
PRAGMA table_info(ddns_configs);
-- 应该看到 txt_record_name 列
```
**解决方案**(如果确实缺失):
```bash
# 删除旧数据库(测试环境)
Remove-Item .\data\meshray.db -Force
# 重启服务,自动创建新表
.\meshray.exe
```
---
## 🎯 调试步骤
### 第一步:检查前端网络请求
1. 打开浏览器开发者工具(F12
2. 切换到 Network 标签页
3. 访问 DDNS 配置页面
4. 找到 `/api/v1/ddns/config` 请求
5. 查看响应数据
**期望响应**:
```json
{
"data": {
"provider": "aliyun",
"access_key_id": "",
"access_key_secret": "",
"domain": "",
"txt_record_name": "_meshray._mesh", // ← 应该有这个字段
"sync_mode": "auto",
"retry_interval": 5,
"max_retries": 10,
"enabled": true
}
}
```
**如果响应中没有 `txt_record_name`**:
- 可能是后端 Service 返回的数据有问题
- 检查 `internal/service/ddns.go``GetConfig` 方法
---
### 第二步:检查前端表单渲染
1. 在浏览器中打开开发者工具
2. 使用元素选择器(Ctrl+Shift+C
3. 点击"TXT 记录名称"输入框
4. 查看绑定的数据
**期望看到**:
```vue
<el-input
v-model="formData.txt_record_name"
placeholder="_meshray._mesh"
/>
```
**如果找不到这个字段**:
- 可能是 Vue 组件没有正确编译
- 需要重新执行 `npm run build`
---
### 第三步:检查后端日志
```bash
# 启动服务时查看详细日志
.\meshray.exe
```
**查找类似日志**:
```
获取 DDNS 配置成功
返回配置:{Provider:aliyun Domain:example.com TxtRecordName:_meshray._mesh ...}
```
---
## 💡 建议
### 最可能的原因
根据经验,90% 的情况是:
1. **浏览器缓存** - 清缓存即可解决
2. **前端未重新编译** - 执行 `npm run build`
### 快速验证
访问:`http://localhost:9531/service/ddns/edit`
然后在浏览器控制台执行:
```javascript
// 检查 API 返回
fetch('/api/v1/ddns/config')
.then(r => r.json())
.then(d => {
console.log('完整数据:', d.data);
console.log('TXT 记录名称:', d.data.txt_record_name);
});
```
如果控制台显示有 `txt_record_name` 字段,说明后端正常,问题在前端显示层面。
---
## 📝 总结
### ✅ 已确认的事实
1. **前端代码** - 有 TXT 字段(第 65-79 行)
2. **前端 API** - 有 TXT 字段(类型定义第 9 行)
3. **后端 Handler** - 有 TXT 字段(第 28 行,80-85 行校验)
4. **后端 Service** - 有 TXT 字段(第 42 行)
5. **数据库 Model** - 有 TXT 字段(第 232 行)
### 🔍 全链路完整
```
用户输入 → formData.txt_record_name
前端 API → request({ txt_record_name: "..." })
后端接收 → TxtRecordName string `json:"txt_record_name"`
Service → DDNSConfig.TxtRecordName
数据库 → TXTRecordName (GORM 自动映射)
```
### 🎯 下一步行动
请按以下顺序排查:
1. **清除浏览器缓存**Ctrl+Shift+Delete
2. **强制刷新页面**Ctrl+F5
3. **检查网络请求**F12 → Network
4. **重新编译前端**(如果需要)
```bash
cd web
npm run build
```
---
*DDNS TXT 记录字段排查报告 | v1.0*