378 lines
9.1 KiB
Markdown
378 lines
9.1 KiB
Markdown
# 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*
|