添加DNS接口

This commit is contained in:
lj124 2026-05-02 15:09:58 +08:00
parent a936352962
commit f4abc5cd88
8 changed files with 2022 additions and 0 deletions

873
dns.md Normal file
View File

@ -0,0 +1,873 @@
# DNS 管理 API 文档
## 概述
本文档描述了 DNS 管理系统的 API 接口,包括 DNS 服务器、DNS 区域和 DNS 记录的增删改查操作。
## 基础信息
- 基础路径: `/dns`
- 请求方法: POST
- 认证: 需要在请求头中携带认证 token
## 常量定义
### DNS 服务器状态
| 值 | 说明 |
|----|------|
| 0 | 已停止 |
| 1 | 运行中 |
| 2 | 错误 |
### DNS 记录类型
| 值 | 类型 | 说明 |
|----|------|------|
| 1 | A | IPv4 地址记录 |
| 28 | AAAA | IPv6 地址记录 |
| 5 | CNAME | 别名记录 |
| 15 | MX | 邮件交换记录 |
| 2 | NS | 名称服务器记录 |
| 6 | SOA | 起始授权记录 |
| 16 | TXT | 文本记录 |
| 33 | SRV | 服务位置记录 |
## DNS 服务器接口
### 1. 创建 DNS 服务器
**接口路径**: `/dns/server/create`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| name | string | 是 | DNS 服务器名称 |
| port | uint | 否 | 监听端口,默认 53 |
| listen_ip | string | 否 | 监听 IP默认 0.0.0.0 |
| upstream_dns | string | 否 | 上游 DNS 服务器,多个用逗号分隔 |
| enable_recursion | bool | 否 | 是否启用递归查询 |
| description | string | 否 | 描述 |
**请求示例**:
```json
{
"name": "我的 DNS 服务器",
"port": 53,
"listen_ip": "0.0.0.0",
"upstream_dns": "8.8.8.8,8.8.4.4",
"enable_recursion": true,
"description": "个人 DNS 服务器"
}
```
**响应示例**:
```json
{
"code": 0,
"message": "创建DNS服务器成功",
"data": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"user_id": 1,
"name": "我的 DNS 服务器",
"port": 53,
"listen_ip": "0.0.0.0",
"upstream_dns": "8.8.8.8,8.8.4.4",
"enable_recursion": true,
"status": 0,
"description": "个人 DNS 服务器"
}
}
```
### 2. 获取 DNS 服务器
**接口路径**: `/dns/server/get`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| server_id | uint | 否 | DNS 服务器 ID0 表示获取全部 |
| get_type | int | 否 | 获取类型0 获取自己的1 获取全部(管理员) |
**请求示例**:
```json
{
"server_id": 1,
"get_type": 0
}
```
**响应示例**:
```json
{
"code": 0,
"message": "获取DNS服务器成功",
"data": [
{
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"user_id": 1,
"name": "我的 DNS 服务器",
"port": 53,
"listen_ip": "0.0.0.0",
"upstream_dns": "8.8.8.8,8.8.4.4",
"enable_recursion": true,
"status": 0,
"description": "个人 DNS 服务器"
}
]
}
```
### 3. 更新 DNS 服务器
**接口路径**: `/dns/server/update`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| server_id | uint | 是 | DNS 服务器 ID |
| name | string | 否 | DNS 服务器名称 |
| port | uint | 否 | 监听端口 |
| listen_ip | string | 否 | 监听 IP |
| upstream_dns | string | 否 | 上游 DNS 服务器 |
| enable_recursion | bool | 否 | 是否启用递归查询 |
| status | uint | 否 | 状态0 停止1 运行中2 错误 |
| description | string | 否 | 描述 |
**请求示例**:
```json
{
"server_id": 1,
"name": "更新后的 DNS 服务器",
"status": 1
}
```
**响应示例**:
```json
{
"code": 0,
"message": "更新DNS服务器成功",
"data": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T01:00:00Z",
"DeletedAt": null,
"user_id": 1,
"name": "更新后的 DNS 服务器",
"port": 53,
"listen_ip": "0.0.0.0",
"upstream_dns": "8.8.8.8,8.8.4.4",
"enable_recursion": true,
"status": 1,
"description": "个人 DNS 服务器"
}
}
```
### 4. 删除 DNS 服务器
**接口路径**: `/dns/server/delete`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| server_id | uint | 否 | DNS 服务器 ID |
| del_type | uint | 是 | 删除类型0 删除单条1 删除所有 |
**请求示例**:
```json
{
"server_id": 1,
"del_type": 0
}
```
**响应示例**:
```json
{
"code": 0,
"message": "删除DNS服务器成功"
}
```
## DNS 区域接口
### 5. 创建 DNS 区域
**接口路径**: `/dns/zone/create`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| server_id | uint | 是 | 所属 DNS 服务器 ID |
| domain | string | 是 | 域名,如 example.com |
| soa_mname | string | 否 | SOA 记录的主域名服务器 |
| soa_rname | string | 否 | SOA 记录的管理员邮箱 |
| soa_serial | uint | 否 | SOA 记录的序列号,默认 1 |
| soa_refresh | uint | 否 | SOA 记录的刷新时间(秒),默认 86400 |
| soa_retry | uint | 否 | SOA 记录的重试时间(秒),默认 7200 |
| soa_expire | uint | 否 | SOA 记录的过期时间(秒),默认 3600000 |
| soa_minimum | uint | 否 | SOA 记录的最小 TTL默认 3600 |
| ttl | uint | 否 | 默认 TTL默认 3600 |
| description | string | 否 | 描述 |
**请求示例**:
```json
{
"server_id": 1,
"domain": "example.com",
"soa_mname": "ns1.example.com",
"soa_rname": "admin.example.com",
"description": "示例域名"
}
```
**响应示例**:
```json
{
"code": 0,
"message": "创建DNS区域成功",
"data": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"server_id": 1,
"domain": "example.com",
"soa_mname": "ns1.example.com",
"soa_rname": "admin.example.com",
"soa_serial": 1,
"soa_refresh": 86400,
"soa_retry": 7200,
"soa_expire": 3600000,
"soa_minimum": 3600,
"ttl": 3600,
"description": "示例域名"
}
}
```
### 6. 获取 DNS 区域
**接口路径**: `/dns/zone/get`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| zone_id | uint | 否 | Zone ID0 表示获取全部 |
| server_id | uint | 否 | DNS 服务器 ID用于过滤 |
| get_type | int | 否 | 获取类型0 获取自己的1 获取全部(管理员) |
**请求示例**:
```json
{
"zone_id": 1,
"get_type": 0
}
```
**响应示例**:
```json
{
"code": 0,
"message": "获取DNS区域成功",
"data": [
{
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"server_id": 1,
"domain": "example.com",
"soa_mname": "ns1.example.com",
"soa_rname": "admin.example.com",
"soa_serial": 1,
"soa_refresh": 86400,
"soa_retry": 7200,
"soa_expire": 3600000,
"soa_minimum": 3600,
"ttl": 3600,
"description": "示例域名"
}
]
}
```
### 7. 更新 DNS 区域
**接口路径**: `/dns/zone/update`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| zone_id | uint | 是 | Zone ID |
| domain | string | 否 | 域名 |
| soa_mname | string | 否 | SOA 记录的主域名服务器 |
| soa_rname | string | 否 | SOA 记录的管理员邮箱 |
| soa_serial | uint | 否 | SOA 记录的序列号 |
| soa_refresh | uint | 否 | SOA 记录的刷新时间(秒) |
| soa_retry | uint | 否 | SOA 记录的重试时间(秒) |
| soa_expire | uint | 否 | SOA 记录的过期时间(秒) |
| soa_minimum | uint | 否 | SOA 记录的最小 TTL |
| ttl | uint | 否 | 默认 TTL |
| description | string | 否 | 描述 |
**请求示例**:
```json
{
"zone_id": 1,
"description": "更新后的描述"
}
```
**响应示例**:
```json
{
"code": 0,
"message": "更新DNS区域成功",
"data": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T01:00:00Z",
"DeletedAt": null,
"server_id": 1,
"domain": "example.com",
"soa_mname": "ns1.example.com",
"soa_rname": "admin.example.com",
"soa_serial": 1,
"soa_refresh": 86400,
"soa_retry": 7200,
"soa_expire": 3600000,
"soa_minimum": 3600,
"ttl": 3600,
"description": "更新后的描述"
}
}
```
### 8. 删除 DNS 区域
**接口路径**: `/dns/zone/delete`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| zone_id | uint | 否 | Zone ID |
| del_type | uint | 是 | 删除类型0 删除单条1 删除所有 |
**请求示例**:
```json
{
"zone_id": 1,
"del_type": 0
}
```
**响应示例**:
```json
{
"code": 0,
"message": "删除DNS区域成功"
}
```
## DNS 记录接口
### 9. 创建 DNS 记录
**接口路径**: `/dns/record/create`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| zone_id | uint | 是 | 所属 Zone ID |
| name | string | 是 | 记录名称,如 www 或 @ |
| type | uint | 是 | 记录类型1=A, 28=AAAA, 5=CNAME, 15=MX 等 |
| value | string | 否 | 记录值 |
| ttl | uint | 否 | TTL如果为 0 则使用 Zone 的默认 TTL |
| priority | uint | 否 | 优先级(用于 MX、SRV 等记录) |
| weight | uint | 否 | 权重(用于 SRV 记录) |
| port | uint | 否 | 端口(用于 SRV 记录) |
| target | string | 否 | 目标(用于 SRV 记录) |
**请求示例 - A 记录**:
```json
{
"zone_id": 1,
"name": "www",
"type": 1,
"value": "192.168.1.1",
"ttl": 3600
}
```
**请求示例 - CNAME 记录**:
```json
{
"zone_id": 1,
"name": "mail",
"type": 5,
"value": "mailserver.example.com",
"ttl": 3600
}
```
**请求示例 - MX 记录**:
```json
{
"zone_id": 1,
"name": "@",
"type": 15,
"value": "mx1.example.com",
"ttl": 3600,
"priority": 10
}
```
**响应示例**:
```json
{
"code": 0,
"message": "创建DNS记录成功",
"data": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"zone_id": 1,
"name": "www",
"type": 1,
"value": "192.168.1.1",
"ttl": 3600,
"priority": 0,
"weight": 0,
"port": 0,
"target": ""
}
}
```
### 10. 获取 DNS 记录
**接口路径**: `/dns/record/get`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| record_id | uint | 否 | 记录 ID0 表示获取全部 |
| zone_id | uint | 否 | Zone ID用于过滤 |
| get_type | int | 否 | 获取类型0 获取自己的1 获取全部(管理员) |
**请求示例**:
```json
{
"record_id": 1,
"get_type": 0
}
```
**响应示例**:
```json
{
"code": 0,
"message": "获取DNS记录成功",
"data": [
{
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"zone_id": 1,
"name": "www",
"type": 1,
"value": "192.168.1.1",
"ttl": 3600,
"priority": 0,
"weight": 0,
"port": 0,
"target": ""
}
]
}
```
### 11. 更新 DNS 记录
**接口路径**: `/dns/record/update`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| record_id | uint | 是 | 记录 ID |
| name | string | 否 | 记录名称 |
| type | uint | 否 | 记录类型 |
| value | string | 否 | 记录值 |
| ttl | uint | 否 | TTL |
| priority | uint | 否 | 优先级 |
| weight | uint | 否 | 权重 |
| port | uint | 否 | 端口 |
| target | string | 否 | 目标 |
**请求示例**:
```json
{
"record_id": 1,
"value": "192.168.1.2"
}
```
**响应示例**:
```json
{
"code": 0,
"message": "更新DNS记录成功",
"data": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T01:00:00Z",
"DeletedAt": null,
"zone_id": 1,
"name": "www",
"type": 1,
"value": "192.168.1.2",
"ttl": 3600,
"priority": 0,
"weight": 0,
"port": 0,
"target": ""
}
}
```
### 12. 删除 DNS 记录
**接口路径**: `/dns/record/delete`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| record_id | uint | 否 | 记录 ID |
| del_type | uint | 是 | 删除类型0 删除单条1 删除所有 |
**请求示例**:
```json
{
"record_id": 1,
"del_type": 0
}
```
**响应示例**:
```json
{
"code": 0,
"message": "删除DNS记录成功"
}
```
## DNS 服务运行管理接口
### 13. 启动 DNS 服务器
**接口路径**: `/dns/server/start`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| server_id | uint | 是 | DNS 服务器 ID |
**请求示例**:
```json
{
"server_id": 1
}
```
**响应示例**:
```json
{
"code": 0,
"message": "启动DNS服务器成功",
"data": {
"server_id": 1,
"config": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"user_id": 1,
"name": "我的 DNS 服务器",
"port": 53,
"listen_ip": "0.0.0.0",
"upstream_dns": "8.8.8.8,8.8.4.4",
"enable_recursion": true,
"status": 1,
"description": "个人 DNS 服务器"
},
"running": true
}
}
```
### 14. 停止 DNS 服务器
**接口路径**: `/dns/server/stop`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| server_id | uint | 是 | DNS 服务器 ID |
**请求示例**:
```json
{
"server_id": 1
}
```
**响应示例**:
```json
{
"code": 0,
"message": "停止DNS服务器成功"
}
```
### 15. 重启 DNS 服务器
**接口路径**: `/dns/server/restart`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| server_id | uint | 是 | DNS 服务器 ID |
**请求示例**:
```json
{
"server_id": 1
}
```
**响应示例**:
```json
{
"code": 0,
"message": "重启DNS服务器成功",
"data": {
"server_id": 1,
"config": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"user_id": 1,
"name": "我的 DNS 服务器",
"port": 53,
"listen_ip": "0.0.0.0",
"upstream_dns": "8.8.8.8,8.8.4.4",
"enable_recursion": true,
"status": 1,
"description": "个人 DNS 服务器"
},
"running": true
}
}
```
### 16. 获取 DNS 服务器运行状态
**接口路径**: `/dns/server/status`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| server_id | uint | 是 | DNS 服务器 ID |
**请求示例**:
```json
{
"server_id": 1
}
```
**响应示例**:
```json
{
"code": 0,
"message": "获取DNS服务器状态成功",
"data": {
"server_id": 1,
"config": {
"ID": 1,
"CreatedAt": "2024-01-01T00:00:00Z",
"UpdatedAt": "2024-01-01T00:00:00Z",
"DeletedAt": null,
"user_id": 1,
"name": "我的 DNS 服务器",
"port": 53,
"listen_ip": "0.0.0.0",
"upstream_dns": "8.8.8.8,8.8.4.4",
"enable_recursion": true,
"status": 1,
"description": "个人 DNS 服务器"
},
"running": true
}
}
```
## 数据模型
### DNSServer
| 字段 | 类型 | 说明 |
|------|------|------|
| ID | uint | 主键 ID |
| CreatedAt | time.Time | 创建时间 |
| UpdatedAt | time.Time | 更新时间 |
| DeletedAt | *time.Time | 删除时间 |
| user_id | uint | 用户 ID |
| name | string | DNS 服务器名称 |
| port | uint | 监听端口 |
| listen_ip | string | 监听 IP |
| upstream_dns | string | 上游 DNS 服务器 |
| enable_recursion | bool | 是否启用递归查询 |
| status | uint | 状态0 停止1 运行中2 错误 |
| description | string | 描述 |
### DNSZone
| 字段 | 类型 | 说明 |
|------|------|------|
| ID | uint | 主键 ID |
| CreatedAt | time.Time | 创建时间 |
| UpdatedAt | time.Time | 更新时间 |
| DeletedAt | *time.Time | 删除时间 |
| server_id | uint | 所属 DNS 服务器 ID |
| domain | string | 域名 |
| soa_mname | string | SOA 记录的主域名服务器 |
| soa_rname | string | SOA 记录的管理员邮箱 |
| soa_serial | uint | SOA 记录的序列号 |
| soa_refresh | uint | SOA 记录的刷新时间(秒) |
| soa_retry | uint | SOA 记录的重试时间(秒) |
| soa_expire | uint | SOA 记录的过期时间(秒) |
| soa_minimum | uint | SOA 记录的最小 TTL |
| ttl | uint | 默认 TTL |
| description | string | 描述 |
### DNSRecord
| 字段 | 类型 | 说明 |
|------|------|------|
| ID | uint | 主键 ID |
| CreatedAt | time.Time | 创建时间 |
| UpdatedAt | time.Time | 更新时间 |
| DeletedAt | *time.Time | 删除时间 |
| zone_id | uint | 所属 Zone ID |
| name | string | 记录名称 |
| type | uint | 记录类型 |
| value | string | 记录值 |
| ttl | uint | TTL |
| priority | uint | 优先级 |
| weight | uint | 权重 |
| port | uint | 端口 |
| target | string | 目标 |
### DNSServiceInstance
| 字段 | 类型 | 说明 |
|------|------|------|
| server_id | uint | DNS 服务器 ID |
| config | DNSServer | DNS 服务器配置 |
| running | bool | 是否正在运行 |
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 0 | 成功 |
| 1 | 参数错误 |
| 2 | 系统错误 |
## 功能特性
### 基础功能
- 支持多 DNS 服务器实例管理
- 每个 DNS 服务器可以管理多个域名区域Zone
- 每个域名区域可以添加多条 DNS 记录
- 支持常见 DNS 记录类型A、AAAA、CNAME、MX、NS、TXT、SRV、SOA
- 完整的 SOA 记录配置
### 运行时管理
- 支持 DNS 服务的启动、停止、重启操作
- 同时支持 UDP 和 TCP 协议
- 配置修改后自动重启运行中的服务
- 实时获取服务运行状态
- 支持递归查询,可配置上游 DNS 服务器
### 权限说明
- 普通用户只能操作自己创建的 DNS 服务器、区域和记录
- 管理员可以操作所有 DNS 服务器、区域和记录
- 删除操作会级联删除相关的子资源(删除服务器会删除其下的区域,删除区域会删除其下的记录)
### 自动重启特性
当 DNS 服务器的配置端口、监听IP、上游DNS等被修改时如果服务正在运行系统会自动重启服务以应用新的配置。

79
src/api/dns.js Normal file
View File

@ -0,0 +1,79 @@
import request from '@/utils/request.js'
// 转换数据为 URLSearchParams 格式
const toFormData = (data) => {
const params = new URLSearchParams();
for (let key in data) {
if (data[key] !== null && data[key] !== undefined && data[key] !== '') {
params.append(key, data[key]);
}
}
return params;
}
// DNS 服务器接口
export const createDNSServer = (data) => {
return request.post('/dns/server/create', toFormData(data))
}
export const getDNSServerList = (data) => {
return request.post('/dns/server/get', toFormData(data))
}
export const updateDNSServer = (data) => {
return request.post('/dns/server/update', toFormData(data))
}
export const deleteDNSServer = (data) => {
return request.post('/dns/server/delete', toFormData(data))
}
export const startDNSServer = (data) => {
return request.post('/dns/server/start', toFormData(data))
}
export const stopDNSServer = (data) => {
return request.post('/dns/server/stop', toFormData(data))
}
export const restartDNSServer = (data) => {
return request.post('/dns/server/restart', toFormData(data))
}
export const getDNSServerStatus = (data) => {
return request.post('/dns/server/status', toFormData(data))
}
// DNS 区域接口
export const createDNSZone = (data) => {
return request.post('/dns/zone/create', toFormData(data))
}
export const getDNSZoneList = (data) => {
return request.post('/dns/zone/get', toFormData(data))
}
export const updateDNSZone = (data) => {
return request.post('/dns/zone/update', toFormData(data))
}
export const deleteDNSZone = (data) => {
return request.post('/dns/zone/delete', toFormData(data))
}
// DNS 记录接口
export const createDNSRecord = (data) => {
return request.post('/dns/record/create', toFormData(data))
}
export const getDNSRecordList = (data) => {
return request.post('/dns/record/get', toFormData(data))
}
export const updateDNSRecord = (data) => {
return request.post('/dns/record/update', toFormData(data))
}
export const deleteDNSRecord = (data) => {
return request.post('/dns/record/delete', toFormData(data))
}

View File

@ -69,6 +69,9 @@ import {
UserFilled,
OfficeBuilding,
Connection,
Setting as ServerIcon,
Link as ZoneIcon,
DocumentCopy as RecordIcon,
} from "@element-plus/icons-vue";
const router = useRouter();
@ -139,6 +142,24 @@ const menuList = ref([
icon: Connection,
show: true,
},
{
path: "/dnsServer",
title: "DNS服务器配置",
icon: ServerIcon,
show: true,
},
{
path: "/dnsZone",
title: "DNS区域管理",
icon: ZoneIcon,
show: true,
},
{
path: "/dnsRecord",
title: "DNS记录管理",
icon: RecordIcon,
show: true,
},
]);
const toggleCollapse = () => {

View File

@ -14,6 +14,9 @@ import Chat from "@/views/Chat.vue"
import Group from "@/views/Group.vue"
import File from "@/views/FileList.vue"
import Shell from "@/views/ShellList.vue"
import DNSServer from "@/views/DNSServer.vue"
import DNSZone from "@/views/DNSZone.vue"
import DNSRecord from "@/views/DNSRecord.vue"
import callback from "@/views/callback.vue";
import projectSelect from "@/views/project-select.vue";
@ -94,6 +97,21 @@ const routes = [
path: 'projectSelect',
name: 'projectSelect',
component: projectSelect
},
{
path: 'dnsServer',
name: 'DNSServer',
component: DNSServer
},
{
path: 'dnsZone',
name: 'DNSZone',
component: DNSZone
},
{
path: 'dnsRecord',
name: 'DNSRecord',
component: DNSRecord
}
]
},

472
src/views/DNSRecord.vue Normal file
View File

@ -0,0 +1,472 @@
<template>
<div class="dns-record-container">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>DNS 记录管理</span>
<el-button type="primary" size="small" @click="handleAdd">
<el-icon><Plus /></el-icon>
添加记录
</el-button>
</div>
</template>
<!-- 搜索筛选 -->
<el-form :inline="true" :model="searchForm" class="search-form">
<el-form-item label="DNS服务器">
<el-select v-model="searchForm.server_id" placeholder="请选择服务器" @change="handleServerChange">
<el-option
v-for="server in serverList"
:key="server.ID"
:label="server.name"
:value="server.ID"
/>
</el-select>
</el-form-item>
<el-form-item label="区域">
<el-select v-model="searchForm.zone_id" placeholder="请选择区域">
<el-option
v-for="zone in filteredZoneList"
:key="zone.ID"
:label="zone.domain"
:value="zone.ID"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="recordList" style="width: 100%">
<el-table-column prop="server_name" label="服务器" width="150" />
<el-table-column prop="zone_domain" label="区域" width="150" />
<el-table-column prop="name" label="记录名称" width="120" />
<el-table-column prop="type" label="记录类型" width="100">
<template #default="{ row }">
<el-tag :type="getRecordTypeColor(row.type)">
{{ getRecordTypeName(row.type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="value" label="记录值" show-overflow-tooltip />
<el-table-column prop="ttl" label="TTL" width="80">
<template #default="{ row }">
{{ row.ttl }}
</template>
</el-table-column>
<el-table-column prop="priority" label="优先级" width="80" v-if="showPriority">
<template #default="{ row }">
{{ row.priority || '-' }}
</template>
</el-table-column>
<el-table-column prop="weight" label="权重" width="80" v-if="showWeight">
<template #default="{ row }">
{{ row.weight || '-' }}
</template>
</el-table-column>
<el-table-column prop="port" label="端口" width="80" v-if="showPort">
<template #default="{ row }">
{{ row.port || '-' }}
</template>
</el-table-column>
<el-table-column prop="target" label="目标" width="150" v-if="showTarget">
<template #default="{ row }">
{{ row.target || '-' }}
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="handleEdit(row)">
编辑
</el-button>
<el-button link type="danger" size="small" @click="handleDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 新增/编辑对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px">
<el-form ref="recordFormRef" :model="recordForm" label-width="100px">
<el-form-item label="DNS服务器" prop="server_id">
<el-select v-model="formServerId" placeholder="请选择服务器" @change="handleFormServerChange">
<el-option
v-for="server in serverList"
:key="server.ID"
:label="server.name"
:value="server.ID"
/>
</el-select>
</el-form-item>
<el-form-item label="区域" prop="zone_id">
<el-select v-model="recordForm.zone_id" placeholder="请选择区域">
<el-option
v-for="zone in formZoneList"
:key="zone.ID"
:label="zone.domain"
:value="zone.ID"
/>
</el-select>
</el-form-item>
<el-form-item label="记录名称" prop="name">
<el-input v-model="recordForm.name" placeholder="请输入记录名称,如 www 或 @" />
</el-form-item>
<el-form-item label="记录类型" prop="type">
<el-select v-model="recordForm.type" placeholder="请选择记录类型">
<el-option
v-for="item in recordTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="记录值" prop="value">
<el-input v-model="recordForm.value" :type="recordForm.type === 16 ? 'textarea' : 'text'" :rows="recordForm.type === 16 ? 3 : 1" placeholder="请输入记录值" />
</el-form-item>
<el-form-item label="TTL" prop="ttl">
<el-input-number v-model="recordForm.ttl" :min="0" :max="31536000" style="width: 100%" />
</el-form-item>
<el-form-item label="优先级" prop="priority" v-if="showPriorityInput">
<el-input-number v-model="recordForm.priority" :min="0" :max="65535" style="width: 100%" />
</el-form-item>
<el-form-item label="权重" prop="weight" v-if="showWeightInput">
<el-input-number v-model="recordForm.weight" :min="0" :max="65535" style="width: 100%" />
</el-form-item>
<el-form-item label="端口" prop="port" v-if="showPortInput">
<el-input-number v-model="recordForm.port" :min="0" :max="65535" style="width: 100%" />
</el-form-item>
<el-form-item label="目标" prop="target" v-if="showTargetInput">
<el-input v-model="recordForm.target" placeholder="请输入目标" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import {
createDNSRecord,
getDNSRecordList,
updateDNSRecord,
deleteDNSRecord
} from '@/api/dns.js'
import { getDNSServerList } from '@/api/dns.js'
import { getDNSZoneList } from '@/api/dns.js'
const loading = ref(false)
const recordList = ref([])
const serverList = ref([])
const zoneList = ref([])
const dialogVisible = ref(false)
const dialogTitle = ref('')
const recordFormRef = ref()
//
const searchForm = reactive({
server_id: null,
zone_id: null
})
//
const recordForm = reactive({
record_id: null,
zone_id: null,
name: '',
type: 1,
value: '',
ttl: 0,
priority: 0,
weight: 0,
port: 0,
target: ''
})
// ID
const formServerId = ref(null)
//
const recordTypeOptions = [
{ value: 1, label: 'A' },
{ value: 28, label: 'AAAA' },
{ value: 5, label: 'CNAME' },
{ value: 15, label: 'MX' },
{ value: 2, label: 'NS' },
{ value: 6, label: 'SOA' },
{ value: 16, label: 'TXT' },
{ value: 33, label: 'SRV' }
]
//
const filteredZoneList = computed(() => {
if (!searchForm.server_id) {
return zoneList.value
}
return zoneList.value.filter(zone => zone.server_id === searchForm.server_id)
})
//
const formZoneList = computed(() => {
if (!formServerId.value) {
return []
}
return zoneList.value.filter(zone => zone.server_id === formServerId.value)
})
//
const showPriority = ref(false)
const showWeight = ref(false)
const showPort = ref(false)
const showTarget = ref(false)
const showPriorityInput = ref(false)
const showWeightInput = ref(false)
const showPortInput = ref(false)
const showTargetInput = ref(false)
//
watch(() => recordForm.type, (newType) => {
showPriorityInput.value = [15, 33].includes(newType) // MX, SRV
showWeightInput.value = [33].includes(newType) // SRV
showPortInput.value = [33].includes(newType) // SRV
showTargetInput.value = [33].includes(newType) // SRV
})
//
const fetchServerList = async () => {
try {
const res = await getDNSServerList({ server_id: 0, get_type: 0 })
if (res.code === 0) {
serverList.value = res.data
}
} catch (error) {
ElMessage.error('获取服务器列表失败')
}
}
//
const fetchZoneList = async () => {
try {
const res = await getDNSZoneList({ zone_id: 0, get_type: 0 })
if (res.code === 0) {
zoneList.value = res.data
}
} catch (error) {
ElMessage.error('获取区域列表失败')
}
}
//
const fetchRecordList = async () => {
loading.value = true
try {
const params = {
record_id: 0,
zone_id: searchForm.zone_id || 0,
get_type: 0
}
const res = await getDNSRecordList(params)
if (res.code === 0) {
//
const records = res.data.map(record => {
const zone = zoneList.value.find(z => z.ID === record.zone_id)
const server = serverList.value.find(s => s.ID === (zone ? zone.server_id : null))
return {
...record,
server_name: server ? server.name : '未知服务器',
zone_domain: zone ? zone.domain : '未知区域'
}
})
recordList.value = records
//
showPriority.value = records.some(record => [15, 33].includes(record.type))
showWeight.value = records.some(record => [33].includes(record.type))
showPort.value = records.some(record => [33].includes(record.type))
showTarget.value = records.some(record => [33].includes(record.type))
}
} catch (error) {
ElMessage.error('获取记录列表失败')
} finally {
loading.value = false
}
}
//
const handleServerChange = () => {
searchForm.zone_id = null
}
//
const handleSearch = () => {
fetchRecordList()
}
//
const handleAdd = () => {
dialogTitle.value = '添加 DNS 记录'
//
recordForm.record_id = null
recordForm.zone_id = null
recordForm.name = ''
recordForm.type = 1
recordForm.value = ''
recordForm.ttl = 0
recordForm.priority = 0
recordForm.weight = 0
recordForm.port = 0
recordForm.target = ''
formServerId.value = null
dialogVisible.value = true
}
//
const handleEdit = (row) => {
dialogTitle.value = '编辑 DNS 记录'
Object.assign(recordForm, row)
recordForm.record_id = row.ID
// ID
const zone = zoneList.value.find(z => z.ID === row.zone_id)
if (zone) {
formServerId.value = zone.server_id
}
dialogVisible.value = true
}
//
const handleFormServerChange = () => {
recordForm.zone_id = null
}
//
const handleSubmit = async () => {
await recordFormRef.value.validate()
try {
//
const submitData = {
zone_id: recordForm.zone_id,
name: recordForm.name,
type: recordForm.type,
value: recordForm.value,
ttl: recordForm.ttl,
priority: recordForm.priority,
weight: recordForm.weight,
port: recordForm.port,
target: recordForm.target
}
let res
if (recordForm.record_id) {
submitData.record_id = recordForm.record_id
res = await updateDNSRecord(submitData)
} else {
res = await createDNSRecord(submitData)
}
if (res.code === 0) {
ElMessage.success(recordForm.record_id ? '更新成功' : '创建成功')
dialogVisible.value = false
fetchRecordList()
}
} catch (error) {
ElMessage.error('操作失败')
}
}
//
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm(
'确定要删除该记录吗?',
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
const res = await deleteDNSRecord({ record_id: row.ID, del_type: 0 })
if (res.code === 0) {
ElMessage.success('删除成功')
fetchRecordList()
}
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('删除失败')
}
}
}
//
const getRecordTypeName = (type) => {
const typeMap = {
1: 'A',
28: 'AAAA',
5: 'CNAME',
15: 'MX',
2: 'NS',
6: 'SOA',
16: 'TXT',
33: 'SRV'
}
return typeMap[type] || '未知类型'
}
//
const getRecordTypeColor = (type) => {
const colorMap = {
1: 'primary',
28: 'success',
5: 'warning',
15: 'danger',
2: 'info',
6: 'info',
16: 'info',
33: 'info'
}
return colorMap[type] || 'info'
}
onMounted(() => {
Promise.all([fetchServerList(), fetchZoneList()]).then(() => {
fetchRecordList()
})
})
</script>
<style scoped>
.dns-record-container {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.search-form {
margin-bottom: 20px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
}
</style>

290
src/views/DNSServer.vue Normal file
View File

@ -0,0 +1,290 @@
<template>
<div class="dns-server-container">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>DNS 服务器配置</span>
<el-button type="primary" size="small" @click="handleAdd">
<el-icon><Plus /></el-icon>
添加服务器
</el-button>
</div>
</template>
<el-table v-loading="loading" :data="serverList" style="width: 100%">
<el-table-column prop="name" label="服务器名称" width="180" />
<el-table-column prop="listen_ip" label="监听IP" width="120" />
<el-table-column prop="port" label="端口" width="80" />
<el-table-column prop="upstream_dns" label="上游DNS" show-overflow-tooltip />
<el-table-column prop="enable_recursion" label="递归查询" width="100">
<template #default="{ row }">
<el-tag :type="row.enable_recursion ? 'success' : 'danger'">
{{ row.enable_recursion ? '开启' : '关闭' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)">
{{ getStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="description" label="描述" show-overflow-tooltip />
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="handleEdit(row)">
编辑
</el-button>
<el-button link type="success" size="small" @click="handleStart(row)" v-if="row.status === 0">
启动
</el-button>
<el-button link type="warning" size="small" @click="handleStop(row)" v-if="row.status === 1">
停止
</el-button>
<el-button link type="info" size="small" @click="handleRestart(row)" v-if="row.status === 1">
重启
</el-button>
<el-button link type="danger" size="small" @click="handleDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 新增/编辑对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px">
<el-form ref="serverFormRef" :model="serverForm" label-width="100px">
<el-form-item label="服务器名称" prop="name">
<el-input v-model="serverForm.name" placeholder="请输入服务器名称" />
</el-form-item>
<el-form-item label="监听IP" prop="listen_ip">
<el-input v-model="serverForm.listen_ip" placeholder="请输入监听IP" />
</el-form-item>
<el-form-item label="端口" prop="port">
<el-input-number v-model="serverForm.port" :min="1" :max="65535" style="width: 100%" />
</el-form-item>
<el-form-item label="上游DNS" prop="upstream_dns">
<el-input v-model="serverForm.upstream_dns" placeholder="多个DNS用逗号分隔" />
</el-form-item>
<el-form-item label="递归查询" prop="enable_recursion">
<el-switch v-model="serverForm.enable_recursion" />
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="serverForm.description" type="textarea" :rows="3" placeholder="请输入描述" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import {
createDNSServer,
getDNSServerList,
updateDNSServer,
deleteDNSServer,
startDNSServer,
stopDNSServer,
restartDNSServer
} from '@/api/dns.js'
const loading = ref(false)
const serverList = ref([])
const dialogVisible = ref(false)
const dialogTitle = ref('')
const serverFormRef = ref()
const serverForm = reactive({
server_id: null,
name: '',
listen_ip: '0.0.0.0',
port: 53,
upstream_dns: '8.8.8.8,8.8.4.4',
enable_recursion: true,
description: ''
})
//
const fetchServerList = async () => {
loading.value = true
try {
const res = await getDNSServerList({ server_id: 0, get_type: 0 })
if (res.code === 0) {
serverList.value = res.data
}
} catch (error) {
ElMessage.error('获取服务器列表失败')
} finally {
loading.value = false
}
}
//
const handleAdd = () => {
dialogTitle.value = '添加 DNS 服务器'
Object.keys(serverForm).forEach(key => {
if (key === 'listen_ip') serverForm[key] = '0.0.0.0'
else if (key === 'port') serverForm[key] = 53
else if (key === 'upstream_dns') serverForm[key] = '8.8.8.8,8.8.4.4'
else if (key === 'enable_recursion') serverForm[key] = true
else serverForm[key] = ''
})
dialogVisible.value = true
}
//
const handleEdit = (row) => {
dialogTitle.value = '编辑 DNS 服务器'
Object.assign(serverForm, row)
serverForm.server_id = row.ID
dialogVisible.value = true
}
//
const handleSubmit = async () => {
await serverFormRef.value.validate()
try {
//
const submitData = {
name: serverForm.name,
listen_ip: serverForm.listen_ip,
port: serverForm.port,
upstream_dns: serverForm.upstream_dns,
enable_recursion: serverForm.enable_recursion,
description: serverForm.description
}
let res
if (serverForm.server_id) {
submitData.server_id = serverForm.server_id
res = await updateDNSServer(submitData)
} else {
res = await createDNSServer(submitData)
}
if (res.code === 0) {
ElMessage.success(serverForm.server_id ? '更新成功' : '创建成功')
dialogVisible.value = false
fetchServerList()
}
} catch (error) {
ElMessage.error('操作失败')
}
}
//
const handleStart = async (row) => {
try {
const res = await startDNSServer({ server_id: row.ID })
if (res.code === 0) {
ElMessage.success('启动成功')
fetchServerList()
}
} catch (error) {
ElMessage.error('启动失败')
}
}
//
const handleStop = async (row) => {
try {
const res = await stopDNSServer({ server_id: row.ID })
if (res.code === 0) {
ElMessage.success('停止成功')
fetchServerList()
}
} catch (error) {
ElMessage.error('停止失败')
}
}
//
const handleRestart = async (row) => {
try {
const res = await restartDNSServer({ server_id: row.ID })
if (res.code === 0) {
ElMessage.success('重启成功')
fetchServerList()
}
} catch (error) {
ElMessage.error('重启失败')
}
}
//
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm(
'确定要删除该服务器吗?',
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
const res = await deleteDNSServer({ server_id: row.ID, del_type: 0 })
if (res.code === 0) {
ElMessage.success('删除成功')
fetchServerList()
}
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('删除失败')
}
}
}
//
const getStatusText = (status) => {
const statusMap = {
0: '已停止',
1: '运行中',
2: '错误'
}
return statusMap[status] || '未知'
}
//
const getStatusType = (status) => {
const typeMap = {
0: 'danger',
1: 'success',
2: 'warning'
}
return typeMap[status] || 'info'
}
onMounted(() => {
fetchServerList()
})
</script>
<style scoped>
.dns-server-container {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
}
</style>

251
src/views/DNSZone.vue Normal file
View File

@ -0,0 +1,251 @@
<template>
<div class="dns-zone-container">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>DNS 区域管理</span>
<el-button type="primary" size="small" @click="handleAdd">
<el-icon><Plus /></el-icon>
添加区域
</el-button>
</div>
</template>
<el-table v-loading="loading" :data="zoneList" style="width: 100%">
<el-table-column prop="domain" label="域名" width="200" />
<el-table-column prop="server_name" label="所属服务器" width="180" />
<el-table-column prop="soa_mname" label="主域名服务器" width="180" />
<el-table-column prop="soa_rname" label="管理员邮箱" width="180" />
<el-table-column prop="ttl" label="默认TTL" width="100">
<template #default="{ row }">
{{ row.ttl }}
</template>
</el-table-column>
<el-table-column prop="description" label="描述" show-overflow-tooltip />
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="handleEdit(row)">
编辑
</el-button>
<el-button link type="danger" size="small" @click="handleDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 新增/编辑对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px">
<el-form ref="zoneFormRef" :model="zoneForm" label-width="120px">
<el-form-item label="DNS服务器" prop="server_id">
<el-select v-model="zoneForm.server_id" placeholder="请选择DNS服务器" style="width: 100%">
<el-option
v-for="server in serverList"
:key="server.ID"
:label="server.name"
:value="server.ID"
/>
</el-select>
</el-form-item>
<el-form-item label="域名" prop="domain">
<el-input v-model="zoneForm.domain" placeholder="请输入域名,如 example.com" />
</el-form-item>
<el-form-item label="主域名服务器" prop="soa_mname">
<el-input v-model="zoneForm.soa_mname" placeholder="请输入主域名服务器" />
</el-form-item>
<el-form-item label="管理员邮箱" prop="soa_rname">
<el-input v-model="zoneForm.soa_rname" placeholder="请输入管理员邮箱" />
</el-form-item>
<el-form-item label="默认TTL" prop="ttl">
<el-input-number v-model="zoneForm.ttl" :min="1" :max="31536000" style="width: 100%" />
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="zoneForm.description" type="textarea" :rows="3" placeholder="请输入描述" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import {
createDNSZone,
getDNSZoneList,
updateDNSZone,
deleteDNSZone
} from '@/api/dns.js'
import { getDNSServerList } from '@/api/dns.js'
const loading = ref(false)
const zoneList = ref([])
const serverList = ref([])
const dialogVisible = ref(false)
const dialogTitle = ref('')
const zoneFormRef = ref()
const zoneForm = reactive({
zone_id: null,
server_id: null,
domain: '',
soa_mname: '',
soa_rname: '',
soa_serial: 1,
soa_refresh: 86400,
soa_retry: 7200,
soa_expire: 3600000,
soa_minimum: 3600,
ttl: 3600,
description: ''
})
//
const fetchServerList = async () => {
try {
const res = await getDNSServerList({ server_id: 0, get_type: 0 })
if (res.code === 0) {
serverList.value = res.data
}
} catch (error) {
ElMessage.error('获取服务器列表失败')
}
}
//
const fetchZoneList = async () => {
loading.value = true
try {
const res = await getDNSZoneList({ zone_id: 0, get_type: 0 })
if (res.code === 0) {
//
const zones = res.data.map(zone => {
const server = serverList.value.find(s => s.ID === zone.server_id)
return {
...zone,
server_name: server ? server.name : '未知服务器'
}
})
zoneList.value = zones
}
} catch (error) {
ElMessage.error('获取区域列表失败')
} finally {
loading.value = false
}
}
//
const handleAdd = () => {
dialogTitle.value = '添加 DNS 区域'
Object.keys(zoneForm).forEach(key => {
if (['soa_serial', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'ttl'].includes(key)) {
zoneForm[key] = key === 'ttl' ? 3600 : 1
} else {
zoneForm[key] = ''
}
})
dialogVisible.value = true
}
//
const handleEdit = (row) => {
dialogTitle.value = '编辑 DNS 区域'
Object.assign(zoneForm, row)
zoneForm.zone_id = row.ID
dialogVisible.value = true
}
//
const handleSubmit = async () => {
await zoneFormRef.value.validate()
try {
//
const submitData = {
domain: zoneForm.domain,
soa_mname: zoneForm.soa_mname,
soa_rname: zoneForm.soa_rname,
soa_serial: zoneForm.soa_serial,
soa_refresh: zoneForm.soa_refresh,
soa_retry: zoneForm.soa_retry,
soa_expire: zoneForm.soa_expire,
soa_minimum: zoneForm.soa_minimum,
ttl: zoneForm.ttl,
description: zoneForm.description
}
let res
if (zoneForm.zone_id) {
submitData.zone_id = zoneForm.zone_id
res = await updateDNSZone(submitData)
} else {
submitData.server_id = zoneForm.server_id
res = await createDNSZone(submitData)
}
if (res.code === 0) {
ElMessage.success(zoneForm.zone_id ? '更新成功' : '创建成功')
dialogVisible.value = false
fetchZoneList()
}
} catch (error) {
ElMessage.error('操作失败')
}
}
//
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm(
'确定要删除该区域吗?',
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
const res = await deleteDNSZone({ zone_id: row.ID, del_type: 0 })
if (res.code === 0) {
ElMessage.success('删除成功')
fetchZoneList()
}
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('删除失败')
}
}
}
onMounted(() => {
fetchServerList().then(() => {
fetchZoneList()
})
})
</script>
<style scoped>
.dns-zone-container {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -58,6 +58,24 @@
@click.prevent="handleMenuSelect('/projectSelect')"
>项目选择</el-button
>
<el-button
type="primary"
size="mini"
@click.prevent="handleMenuSelect('/dnsServer')"
>DNS服务器配置</el-button
>
<el-button
type="primary"
size="mini"
@click.prevent="handleMenuSelect('/dnsZone')"
>DNS区域管理</el-button
>
<el-button
type="primary"
size="mini"
@click.prevent="handleMenuSelect('/dnsRecord')"
>DNS记录管理</el-button
>
<el-button
type="primary"
size="mini"