feat: 规范项目结构,对接前端控制台,支持启/停用
This commit is contained in:
parent
8de4f0cdb9
commit
287ce3636c
10
README.md
10
README.md
|
@ -13,10 +13,6 @@ Bind9不能直接支持API的方式添加解析记录, 通过脚本修改Bind
|
||||||
- gcc
|
- gcc
|
||||||
- go version >= 1.20
|
- go version >= 1.20
|
||||||
|
|
||||||
## 接口文档
|
|
||||||
|
|
||||||
[在线阅读](https://oss.odboy.cn/blog/files/onlinedoc/kenaito-dns/index.html)
|
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
- constant 常量
|
- constant 常量
|
||||||
|
@ -43,6 +39,9 @@ more than 7 hours
|
||||||
|
|
||||||
- 支持回滚 2024-11-08 [ok]
|
- 支持回滚 2024-11-08 [ok]
|
||||||
- 添加缓存 2024-11-09 [ok]
|
- 添加缓存 2024-11-09 [ok]
|
||||||
|
- 新增Web控制台 2024-11-11 [ok]
|
||||||
|
- 支持一键启/停用 2024-11-11 [ok]
|
||||||
|
- 支持一键回滚 2024-11-11 [ok]
|
||||||
|
|
||||||
## 运行配置
|
## 运行配置
|
||||||
|
|
||||||
|
@ -143,7 +142,8 @@ nslookup example.com 192.168.1.103
|
||||||
|
|
||||||
## 代码托管(以私人仓库Gitea为准)
|
## 代码托管(以私人仓库Gitea为准)
|
||||||
|
|
||||||
- Gitea: [https://gitea.odboy.cn/odboy/kenaito-dns](https://gitea.odboy.cn/odboy/kenaito-dns)
|
- Gitea后端: [https://gitea.odboy.cn/odboy/kenaito-dns](https://gitea.odboy.cn/odboy/kenaito-dns)
|
||||||
|
- Gitea前端: [https://gitea.odboy.cn/odboy/kenaito-dns-front](https://gitea.odboy.cn/odboy/kenaito-dns-front)
|
||||||
- Github: [https://github.com/odboy-tianjun/kenaito-dns](https://github.com/odboy-tianjun/kenaito-dns)
|
- Github: [https://github.com/odboy-tianjun/kenaito-dns](https://github.com/odboy-tianjun/kenaito-dns)
|
||||||
- Gitee(已关闭,单纯的不想放在gitee): [https://gitee.com/odboy/kenaito-dns](https://gitee.com/odboy/kenaito-dns)
|
- Gitee(已关闭,单纯的不想放在gitee): [https://gitee.com/odboy/kenaito-dns](https://gitee.com/odboy/kenaito-dns)
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ func ReloadCache() {
|
||||||
fmt.Println("[app] [info] " + time.Now().Format(config.AppTimeFormat) + " [Cache] Reload cache start")
|
fmt.Println("[app] [info] " + time.Now().Format(config.AppTimeFormat) + " [Cache] Reload cache start")
|
||||||
KeyResolveRecordMap.Range(cleanKeyCache)
|
KeyResolveRecordMap.Range(cleanKeyCache)
|
||||||
IdResolveRecordMap.Range(cleanIdCache)
|
IdResolveRecordMap.Range(cleanIdCache)
|
||||||
resolveRecords := dao.FindResolveRecordByVersion(dao.GetResolveVersion())
|
resolveRecords := dao.FindResolveRecordByVersion(dao.GetResolveVersion(), false)
|
||||||
for _, record := range resolveRecords {
|
for _, record := range resolveRecords {
|
||||||
// id -> resolveRecord
|
// id -> resolveRecord
|
||||||
IdResolveRecordMap.Store(record.Id, record)
|
IdResolveRecordMap.Store(record.Id, record)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AppVersion = "1.0.0"
|
AppVersion = "1.0.0"
|
||||||
AppTimeFormat = "2006/01/02 15:04:05.999999"
|
AppTimeFormat = "2006/01/02 15:04:05.999999"
|
||||||
|
DataTimeFormat = "2006-01-02 15:04:05"
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,9 +21,60 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
// 健康检查
|
// 健康检查
|
||||||
r.GET("/health", func(c *gin.Context) {
|
r.GET("/health", func(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"message": "pong",
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"data": "pong",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
// 测试解析状态
|
||||||
|
r.POST("/test", func(c *gin.Context) {
|
||||||
|
var jsonObj domain.TestArgs
|
||||||
|
err := c.ShouldBindJSON(&jsonObj)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := jsonObj.Name
|
||||||
|
valid := util.IsValidDomain(name)
|
||||||
|
if !valid {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"message": "域名解析失败"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
|
"message": "域名解析成功",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 启停记录
|
||||||
|
r.POST("/switch", func(c *gin.Context) {
|
||||||
|
var jsonObj domain.SwitchArgs
|
||||||
|
err := c.ShouldBindJSON(&jsonObj)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = dao.SwitchResolveRecord(jsonObj.Id, jsonObj.Enabled)
|
||||||
|
if err != nil {
|
||||||
|
if jsonObj.Enabled == 1 {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("启用失败, %v", err)})
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("停用失败, %v", err)})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cache.ReloadCache()
|
||||||
|
if jsonObj.Enabled == 1 {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
|
"message": "启用成功",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
|
"message": "停用成功",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
// 创建RR记录
|
// 创建RR记录
|
||||||
r.POST("/create", func(c *gin.Context) {
|
r.POST("/create", func(c *gin.Context) {
|
||||||
var jsonObj domain.CreateResolveRecord
|
var jsonObj domain.CreateResolveRecord
|
||||||
|
@ -33,7 +84,9 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if dao.IsResolveRecordExist(newRecord) {
|
if dao.IsResolveRecordExist(newRecord) {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"message": "记录 " + newRecord.Name + " " + newRecord.RecordType + " " + newRecord.Value + " 已存在"})
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"message": "记录 " + newRecord.Name + " " + newRecord.RecordType + " " + newRecord.Value + " 已存在",
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newRecord.Ttl = jsonObj.Ttl
|
newRecord.Ttl = jsonObj.Ttl
|
||||||
|
@ -52,8 +105,9 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
body["oldVersion"] = oldVersion
|
body["oldVersion"] = oldVersion
|
||||||
body["newVersion"] = newVersion
|
body["newVersion"] = newVersion
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
"message": "添加" + newRecord.RecordType + "记录成功",
|
"message": "添加" + newRecord.RecordType + "记录成功",
|
||||||
"body": body,
|
"data": body,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
@ -84,8 +138,9 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
body["oldVersion"] = oldVersion
|
body["oldVersion"] = oldVersion
|
||||||
body["newVersion"] = newVersion
|
body["newVersion"] = newVersion
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
"message": "删除" + newRecord.RecordType + "记录成功",
|
"message": "删除" + newRecord.RecordType + "记录成功",
|
||||||
"body": body,
|
"data": body,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
@ -128,6 +183,7 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
updRecord.RecordType = newRecord.RecordType
|
updRecord.RecordType = newRecord.RecordType
|
||||||
updRecord.Ttl = newRecord.Ttl
|
updRecord.Ttl = newRecord.Ttl
|
||||||
updRecord.Value = newRecord.Value
|
updRecord.Value = newRecord.Value
|
||||||
|
updRecord.CreateTime = localNewRecord.CreateTime
|
||||||
executeResult, err = dao.ModifyResolveRecordById(localNewRecord.Id, updRecord)
|
executeResult, err = dao.ModifyResolveRecordById(localNewRecord.Id, updRecord)
|
||||||
if !executeResult {
|
if !executeResult {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("更新"+newRecord.RecordType+"记录失败, %v", err)})
|
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("更新"+newRecord.RecordType+"记录失败, %v", err)})
|
||||||
|
@ -138,8 +194,9 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
body["oldVersion"] = oldVersion
|
body["oldVersion"] = oldVersion
|
||||||
body["newVersion"] = newVersion
|
body["newVersion"] = newVersion
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
"message": "更新" + newRecord.RecordType + "记录成功",
|
"message": "更新" + newRecord.RecordType + "记录成功",
|
||||||
"body": body,
|
"data": body,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
@ -152,7 +209,13 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
records := dao.FindResolveRecordPage(jsonObj.Page, jsonObj.PageSize, &jsonObj)
|
records := dao.FindResolveRecordPage(jsonObj.Page, jsonObj.PageSize, &jsonObj)
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "分页查询RR记录成功", "body": records})
|
count := dao.CountResolveRecordPage(jsonObj.Page, jsonObj.PageSize, &jsonObj)
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
|
"message": "分页查询RR记录成功",
|
||||||
|
"data": records,
|
||||||
|
"count": count,
|
||||||
|
})
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
// 根据id查询RR记录明细
|
// 根据id查询RR记录明细
|
||||||
|
@ -164,13 +227,21 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
records := dao.FindResolveRecordById(jsonObj.Id)
|
records := dao.FindResolveRecordById(jsonObj.Id)
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "根据id查询RR记录明细成功", "body": records})
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
|
"message": "根据id查询RR记录明细成功",
|
||||||
|
"data": records,
|
||||||
|
})
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
// 查询变更历史记录
|
// 查询变更历史记录
|
||||||
r.POST("/queryVersionList", func(c *gin.Context) {
|
r.POST("/queryVersionList", func(c *gin.Context) {
|
||||||
records := dao.FindResolveVersion()
|
records := dao.FindResolveVersion()
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "查询变更历史记录列表成功", "body": records})
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
|
"message": "查询变更历史记录列表成功",
|
||||||
|
"data": records,
|
||||||
|
})
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
// 回滚到某一版本
|
// 回滚到某一版本
|
||||||
|
@ -181,7 +252,7 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
versions := dao.FindResolveRecordByVersion(jsonObj.Version)
|
versions := dao.FindResolveRecordByVersion(jsonObj.Version, true)
|
||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("版本号 %d 不存在, 回滚失败", jsonObj.Version)})
|
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("版本号 %d 不存在, 回滚失败", jsonObj.Version)})
|
||||||
return
|
return
|
||||||
|
@ -195,8 +266,9 @@ func InitRestFunc(r *gin.Engine) {
|
||||||
body := make(map[string]interface{})
|
body := make(map[string]interface{})
|
||||||
body["currentVersion"] = jsonObj.Version
|
body["currentVersion"] = jsonObj.Version
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"code": 0,
|
||||||
"message": "回滚成功",
|
"message": "回滚成功",
|
||||||
"body": body,
|
"data": body,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cors 跨域中间件
|
||||||
|
func Cors() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
method := c.Request.Method // 请求方法
|
||||||
|
origin := c.Request.Header.Get("Origin") // 请求头部
|
||||||
|
var headerKeys []string // 声明请求头keys
|
||||||
|
for k, _ := range c.Request.Header {
|
||||||
|
headerKeys = append(headerKeys, k)
|
||||||
|
}
|
||||||
|
headerStr := strings.Join(headerKeys, ", ")
|
||||||
|
if headerStr != "" {
|
||||||
|
headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
|
||||||
|
} else {
|
||||||
|
headerStr = "access-control-allow-origin, access-control-allow-headers"
|
||||||
|
}
|
||||||
|
if origin != "" {
|
||||||
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
c.Header("Access-Control-Allow-Origin", "*") // 允许访问所有域
|
||||||
|
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") // 服务器支持的所有跨域请求的方法
|
||||||
|
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token, session, X_Requested_With, Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language, DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma") // 允许的头类型
|
||||||
|
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma, FooBar") // 允许跨域设置,可以返回其他子段
|
||||||
|
c.Header("Access-Control-Max-Age", "172800") // 缓存请求信息,单位为秒
|
||||||
|
c.Header("Access-Control-Allow-Credentials", "false") // 跨域请求是否需要带cookie信息,默认设置为true
|
||||||
|
c.Set("content-type", "application/json;charset=utf8") // 设置返回格式是json
|
||||||
|
}
|
||||||
|
// 放行所有OPTIONS方法
|
||||||
|
if method == "OPTIONS" {
|
||||||
|
c.JSON(http.StatusOK, "Options Request!")
|
||||||
|
}
|
||||||
|
// 处理请求
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,11 @@ package dao
|
||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"kenaito-dns/config"
|
||||||
"kenaito-dns/domain"
|
"kenaito-dns/domain"
|
||||||
"kenaito-dns/util"
|
"kenaito-dns/util"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResolveRecord struct {
|
type ResolveRecord struct {
|
||||||
|
@ -19,6 +21,9 @@ type ResolveRecord struct {
|
||||||
Ttl int `xorm:"not null integer 'ttl'" json:"ttl"`
|
Ttl int `xorm:"not null integer 'ttl'" json:"ttl"`
|
||||||
Value string `xorm:"not null text 'value'" json:"value"`
|
Value string `xorm:"not null text 'value'" json:"value"`
|
||||||
Version int `xorm:"not null integer 'version'" json:"version"`
|
Version int `xorm:"not null integer 'version'" json:"version"`
|
||||||
|
CreateTime string `xorm:"not null text 'create_time'" json:"createTime"`
|
||||||
|
UpdateTime string `xorm:"not null text 'update_time'" json:"updateTime"`
|
||||||
|
Enabled int `xorm:"not null integer 'enabled'" json:"enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ResolveRecord) TableName() string {
|
func (ResolveRecord) TableName() string {
|
||||||
|
@ -50,9 +55,14 @@ func FindOneResolveRecord(wrapper *ResolveRecord, version int) *ResolveRecord {
|
||||||
return &record
|
return &record
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindResolveRecordByVersion(version int) []ResolveRecord {
|
func FindResolveRecordByVersion(version int, isAll bool) []ResolveRecord {
|
||||||
var records []ResolveRecord
|
var records []ResolveRecord
|
||||||
err := Engine.Table("resolve_record").Where("`version` = ?", version).Find(&records)
|
session := Engine.Table("resolve_record")
|
||||||
|
session.Where("`version` = ?", version)
|
||||||
|
if !isAll {
|
||||||
|
session.Where("`enabled` = ?", 1)
|
||||||
|
}
|
||||||
|
err := session.Find(&records)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
@ -102,8 +112,44 @@ func FindResolveRecordPage(pageNo int, pageSize int, args *domain.QueryPageArgs)
|
||||||
}
|
}
|
||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
|
func CountResolveRecordPage(pageNo int, pageSize int, args *domain.QueryPageArgs) int {
|
||||||
|
// 每页显示5条记录
|
||||||
|
if pageSize <= 5 {
|
||||||
|
pageSize = 5
|
||||||
|
}
|
||||||
|
// 要查询的页码
|
||||||
|
if pageNo <= 0 {
|
||||||
|
pageNo = 1
|
||||||
|
}
|
||||||
|
// 计算跳过的记录数
|
||||||
|
offset := (pageNo - 1) * pageSize
|
||||||
|
session := Engine.Table("resolve_record").Where("")
|
||||||
|
if args != nil {
|
||||||
|
if !util.IsBlank(args.Name) {
|
||||||
|
qs := "%" + strings.TrimSpace(args.Name) + "%"
|
||||||
|
session.And("`name` LIKE ?", qs)
|
||||||
|
}
|
||||||
|
if !util.IsBlank(args.Type) {
|
||||||
|
qs := strings.TrimSpace(args.Type)
|
||||||
|
session.And("`record_type` = ?", qs)
|
||||||
|
}
|
||||||
|
if !util.IsBlank(args.Value) {
|
||||||
|
qs := strings.TrimSpace(args.Value)
|
||||||
|
session.And("`value` = ?", qs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.And("`version` = ?", GetResolveVersion())
|
||||||
|
count, err := session.Limit(pageSize, offset).Count()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
return int(count)
|
||||||
|
}
|
||||||
|
|
||||||
func SaveResolveRecord(wrapper *ResolveRecord) (bool, error) {
|
func SaveResolveRecord(wrapper *ResolveRecord) (bool, error) {
|
||||||
|
wrapper.CreateTime = time.Now().Format(config.DataTimeFormat)
|
||||||
|
wrapper.UpdateTime = time.Now().Format(config.DataTimeFormat)
|
||||||
|
wrapper.Enabled = 1
|
||||||
_, err := Engine.Table("resolve_record").Insert(wrapper)
|
_, err := Engine.Table("resolve_record").Insert(wrapper)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -116,7 +162,7 @@ func BackupResolveRecord(record *ResolveRecord) (bool, error, int, int) {
|
||||||
var backupRecords []*ResolveRecord
|
var backupRecords []*ResolveRecord
|
||||||
oldVersion := GetResolveVersion()
|
oldVersion := GetResolveVersion()
|
||||||
newVersion := GetResolveVersion() + 1
|
newVersion := GetResolveVersion() + 1
|
||||||
oldRecords := FindResolveRecordByVersion(oldVersion)
|
oldRecords := FindResolveRecordByVersion(oldVersion, true)
|
||||||
for _, oldRecord := range oldRecords {
|
for _, oldRecord := range oldRecords {
|
||||||
newRecord := new(ResolveRecord)
|
newRecord := new(ResolveRecord)
|
||||||
newRecord.Name = oldRecord.Name
|
newRecord.Name = oldRecord.Name
|
||||||
|
@ -124,6 +170,9 @@ func BackupResolveRecord(record *ResolveRecord) (bool, error, int, int) {
|
||||||
newRecord.Ttl = oldRecord.Ttl
|
newRecord.Ttl = oldRecord.Ttl
|
||||||
newRecord.Value = oldRecord.Value
|
newRecord.Value = oldRecord.Value
|
||||||
newRecord.Version = newVersion
|
newRecord.Version = newVersion
|
||||||
|
newRecord.CreateTime = oldRecord.CreateTime
|
||||||
|
newRecord.UpdateTime = oldRecord.UpdateTime
|
||||||
|
newRecord.Enabled = oldRecord.Enabled
|
||||||
backupRecords = append(backupRecords, newRecord)
|
backupRecords = append(backupRecords, newRecord)
|
||||||
}
|
}
|
||||||
record.Version = newVersion
|
record.Version = newVersion
|
||||||
|
@ -180,6 +229,21 @@ func IsUpdResolveRecordExist(id int, wrapper *ResolveRecord) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ModifyResolveRecordById(id int, updateRecord *ResolveRecord) (bool, error) {
|
func ModifyResolveRecordById(id int, updateRecord *ResolveRecord) (bool, error) {
|
||||||
|
updateRecord.UpdateTime = time.Now().Format(config.DataTimeFormat)
|
||||||
|
wrapper := new(ResolveRecord)
|
||||||
|
wrapper.Id = id
|
||||||
|
_, err := Engine.Table("resolve_record").Update(updateRecord, wrapper)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SwitchResolveRecord(id int, enabled int) (bool, error) {
|
||||||
|
var updateRecord ResolveRecord
|
||||||
|
updateRecord.UpdateTime = time.Now().Format(config.DataTimeFormat)
|
||||||
|
updateRecord.Enabled = enabled
|
||||||
wrapper := new(ResolveRecord)
|
wrapper := new(ResolveRecord)
|
||||||
wrapper.Id = id
|
wrapper.Id = id
|
||||||
_, err := Engine.Table("resolve_record").Update(updateRecord, wrapper)
|
_, err := Engine.Table("resolve_record").Update(updateRecord, wrapper)
|
||||||
|
|
BIN
dns.sqlite3
BIN
dns.sqlite3
Binary file not shown.
|
@ -34,6 +34,15 @@ type QueryPageArgs struct {
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TestArgs struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwitchArgs struct {
|
||||||
|
Id int `json:"id" binding:"required"`
|
||||||
|
Enabled int `json:"enabled" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type QueryByIdArgs struct {
|
type QueryByIdArgs struct {
|
||||||
Id int `json:"id" binding:"required"`
|
Id int `json:"id" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
2
main.go
2
main.go
|
@ -66,6 +66,8 @@ func initRestfulServer() {
|
||||||
param.ErrorMessage,
|
param.ErrorMessage,
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
// 允许使用跨域请求,全局中间件
|
||||||
|
router.Use(core.Cors())
|
||||||
// 使用 Recovery 中间件,处理任何出现的错误,并防止服务崩溃
|
// 使用 Recovery 中间件,处理任何出现的错误,并防止服务崩溃
|
||||||
router.Use(gin.Recovery())
|
router.Use(gin.Recovery())
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
|
|
|
@ -6,9 +6,14 @@ package util
|
||||||
* @Date 20241107
|
* @Date 20241107
|
||||||
*/
|
*/
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"kenaito-dns/config"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsBlank 检查字符串是否空
|
// IsBlank 检查字符串是否空
|
||||||
|
@ -38,9 +43,49 @@ func IsIPv6(ipAddr string) bool {
|
||||||
|
|
||||||
// IsValidDomain 判断域名是否正常解析
|
// IsValidDomain 判断域名是否正常解析
|
||||||
func IsValidDomain(domain string) bool {
|
func IsValidDomain(domain string) bool {
|
||||||
_, err := net.LookupHost(domain)
|
dnsServer := getLocalIP()
|
||||||
|
if dnsServer == "" {
|
||||||
|
dnsServer = "223.5.5.5"
|
||||||
|
}
|
||||||
|
dnsServer = dnsServer + ":53"
|
||||||
|
_, err := lookupHostWithDNS(domain, dnsServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookupHostWithDNS(host string, dnsServer string) ([]string, error) {
|
||||||
|
resolver := &net.Resolver{
|
||||||
|
PreferGo: true,
|
||||||
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
d := net.Dialer{}
|
||||||
|
conn, err := d.DialContext(ctx, network, dnsServer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[app] [error] "+time.Now().Format(config.AppTimeFormat)+" [DNSTool] 连接到 DNS 服务器失败: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ips, err := resolver.LookupHost(context.Background(), host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLocalIP() string {
|
||||||
|
addrList, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, addr := range addrList {
|
||||||
|
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
||||||
|
if ipNet.IP.To4() != nil {
|
||||||
|
return ipNet.IP.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue