396 lines
13 KiB
Go
396 lines
13 KiB
Go
package controller
|
||
|
||
/*
|
||
* @Description Web控制层
|
||
* @Author www.odboy.cn
|
||
* @Date 20241108
|
||
*/
|
||
import (
|
||
"fmt"
|
||
"github.com/gin-gonic/gin"
|
||
"kenaito-dns/cache"
|
||
"kenaito-dns/constant"
|
||
"kenaito-dns/dao"
|
||
"kenaito-dns/domain"
|
||
"kenaito-dns/util"
|
||
"net/http"
|
||
"strings"
|
||
)
|
||
|
||
func InitRestFunc(r *gin.Engine) {
|
||
// 健康检查
|
||
r.GET("/health", func(c *gin.Context) {
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"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记录
|
||
r.POST("/create", func(c *gin.Context) {
|
||
var jsonObj domain.CreateResolveRecord
|
||
err := c.ShouldBindJSON(&jsonObj)
|
||
newRecord, isErr := validRequestBody(c, err, jsonObj.Name, jsonObj.Type, jsonObj.Ttl, jsonObj.Value, false)
|
||
if isErr {
|
||
return
|
||
}
|
||
if dao.IsResolveRecordExist(newRecord) {
|
||
c.JSON(http.StatusBadRequest, gin.H{
|
||
"message": "记录 " + newRecord.Name + " " + newRecord.RecordType + " " + newRecord.Value + " 已存在",
|
||
})
|
||
return
|
||
}
|
||
newRecord.Ttl = jsonObj.Ttl
|
||
executeResult, err, oldVersion, newVersion := dao.BackupResolveRecord(newRecord)
|
||
if !executeResult {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("备份"+newRecord.RecordType+"记录失败, %v", err)})
|
||
return
|
||
}
|
||
executeResult, _ = dao.SaveResolveRecord(newRecord)
|
||
if !executeResult {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("添加"+newRecord.RecordType+"记录失败, %v", err)})
|
||
return
|
||
}
|
||
cache.ReloadCache()
|
||
body := make(map[string]interface{})
|
||
body["oldVersion"] = oldVersion
|
||
body["newVersion"] = newVersion
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 0,
|
||
"message": "添加" + newRecord.RecordType + "记录成功",
|
||
"data": body,
|
||
})
|
||
return
|
||
})
|
||
// 删除RR记录
|
||
r.POST("/remove", func(c *gin.Context) {
|
||
var jsonObj domain.RemoveResolveRecord
|
||
err := c.ShouldBindJSON(&jsonObj)
|
||
newRecord, isErr := validRequestBody(c, err, jsonObj.Name, jsonObj.Type, 0, jsonObj.Value, true)
|
||
if isErr {
|
||
return
|
||
}
|
||
if !dao.IsResolveRecordExist(newRecord) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "记录 " + newRecord.Name + " " + newRecord.RecordType + " " + newRecord.Value + " 不存在"})
|
||
return
|
||
}
|
||
executeResult, err, oldVersion, newVersion := dao.BackupResolveRecord(newRecord)
|
||
if !executeResult {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("备份"+newRecord.RecordType+"记录失败, %v", err)})
|
||
return
|
||
}
|
||
executeResult, err = dao.RemoveResolveRecord(newRecord)
|
||
if !executeResult {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("删除"+newRecord.RecordType+"记录失败, %v", err)})
|
||
return
|
||
}
|
||
cache.ReloadCache()
|
||
body := make(map[string]interface{})
|
||
body["oldVersion"] = oldVersion
|
||
body["newVersion"] = newVersion
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 0,
|
||
"message": "删除" + newRecord.RecordType + "记录成功",
|
||
"data": body,
|
||
})
|
||
return
|
||
})
|
||
// 修改RR记录
|
||
r.POST("/modify", func(c *gin.Context) {
|
||
var jsonObj domain.ModifyResolveRecord
|
||
err := c.ShouldBindJSON(&jsonObj)
|
||
newRecord, isErr := validModifyRequestBody(c, err, jsonObj.Name, jsonObj.Type, jsonObj.Ttl, jsonObj.Value)
|
||
if isErr {
|
||
return
|
||
}
|
||
if jsonObj.Id == 0 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数ID(id)必填"})
|
||
return
|
||
}
|
||
if !dao.IsResolveRecordExistById(jsonObj.Id) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("记录 id=%d 不存在", jsonObj.Id)})
|
||
return
|
||
}
|
||
if dao.IsUpdResolveRecordExist(jsonObj.Id, newRecord) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "记录 " + jsonObj.Name + " " + jsonObj.Type + " " + jsonObj.Value + " 已存在"})
|
||
return
|
||
}
|
||
localOldRecord := dao.FindResolveRecordById(jsonObj.Id)
|
||
executeResult, err, oldVersion, newVersion := dao.BackupResolveRecord(newRecord)
|
||
if !executeResult {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("备份"+newRecord.RecordType+"记录失败, %v", err)})
|
||
return
|
||
}
|
||
localNewRecord := dao.FindOneResolveRecord(localOldRecord, newVersion)
|
||
if localNewRecord == nil || localNewRecord.Id == 0 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("查询待更新" + newRecord.RecordType + "记录失败")})
|
||
return
|
||
}
|
||
if localNewRecord.Ttl == 0 {
|
||
localNewRecord.Ttl = 10
|
||
}
|
||
updRecord := new(dao.ResolveRecord)
|
||
updRecord.Name = newRecord.Name
|
||
updRecord.RecordType = newRecord.RecordType
|
||
updRecord.Ttl = newRecord.Ttl
|
||
updRecord.Value = newRecord.Value
|
||
updRecord.CreateTime = localNewRecord.CreateTime
|
||
executeResult, err = dao.ModifyResolveRecordById(localNewRecord.Id, updRecord)
|
||
if !executeResult {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("更新"+newRecord.RecordType+"记录失败, %v", err)})
|
||
return
|
||
}
|
||
cache.ReloadCache()
|
||
body := make(map[string]interface{})
|
||
body["oldVersion"] = oldVersion
|
||
body["newVersion"] = newVersion
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 0,
|
||
"message": "更新" + newRecord.RecordType + "记录成功",
|
||
"data": body,
|
||
})
|
||
return
|
||
})
|
||
// 分页查询RR记录
|
||
r.POST("/queryPage", func(c *gin.Context) {
|
||
var jsonObj domain.QueryPageArgs
|
||
err := c.ShouldBindJSON(&jsonObj)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||
return
|
||
}
|
||
records := dao.FindResolveRecordPage(jsonObj.Page, jsonObj.PageSize, &jsonObj)
|
||
count := dao.CountResolveRecordPage(jsonObj.Page, jsonObj.PageSize, &jsonObj)
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 0,
|
||
"message": "分页查询RR记录成功",
|
||
"data": records,
|
||
"count": count,
|
||
})
|
||
return
|
||
})
|
||
// 根据id查询RR记录明细
|
||
r.POST("/queryById", func(c *gin.Context) {
|
||
var jsonObj domain.QueryByIdArgs
|
||
err := c.ShouldBindJSON(&jsonObj)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||
return
|
||
}
|
||
records := dao.FindResolveRecordById(jsonObj.Id)
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 0,
|
||
"message": "根据id查询RR记录明细成功",
|
||
"data": records,
|
||
})
|
||
return
|
||
})
|
||
// 分页查询变更历史记录
|
||
r.POST("/queryVersionPage", func(c *gin.Context) {
|
||
var jsonObj domain.QueryPageArgs
|
||
err := c.ShouldBindJSON(&jsonObj)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||
return
|
||
}
|
||
records := dao.FindResolveVersionPage(jsonObj.Page, jsonObj.PageSize)
|
||
count := dao.CountResolveVersionPage(jsonObj.Page, jsonObj.PageSize)
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 0,
|
||
"message": "分页查询变更历史记录成功",
|
||
"data": records,
|
||
"count": count,
|
||
})
|
||
return
|
||
})
|
||
// 回滚到某一版本
|
||
r.POST("/rollback", func(c *gin.Context) {
|
||
var jsonObj domain.RollbackVersionArgs
|
||
err := c.ShouldBindJSON(&jsonObj)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||
return
|
||
}
|
||
resolveVersion := dao.FindResolveVersionByVersion(jsonObj.Version)
|
||
if resolveVersion == nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("版本号 %d 不存在, 回滚失败", jsonObj.Version)})
|
||
return
|
||
}
|
||
err = dao.ModifyResolveVersion(jsonObj.Version)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("回滚失败, %v", err)})
|
||
return
|
||
}
|
||
cache.ReloadCache()
|
||
body := make(map[string]interface{})
|
||
body["currentVersion"] = jsonObj.Version
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 0,
|
||
"message": "回滚成功",
|
||
"data": body,
|
||
})
|
||
return
|
||
})
|
||
}
|
||
func validRequestBody(c *gin.Context, err error, name string, recordType string, ttl int, value string, isDelete bool) (*dao.ResolveRecord, bool) {
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||
return nil, true
|
||
}
|
||
if util.IsBlank(name) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数主机记录(name)必填, 例如: www.odboy.cn 或 odboy.cn"})
|
||
return nil, true
|
||
}
|
||
if util.IsBlank(recordType) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录类型(type)必填, 目前支持 A、AAAA 记录"})
|
||
return nil, true
|
||
}
|
||
if util.IsBlank(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)必填"})
|
||
return nil, true
|
||
}
|
||
if !isDelete {
|
||
if ttl < 10 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数缓存有效时间(ttl)有误,必须大于等于10"})
|
||
return nil, true
|
||
}
|
||
}
|
||
if !util.IsValidName(name) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数主机记录(name)有误,无效的主机记录"})
|
||
return nil, true
|
||
}
|
||
switch recordType {
|
||
case constant.R_A:
|
||
if !util.IsIPv4(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,无效的IPv4地址"})
|
||
return nil, true
|
||
}
|
||
case constant.R_AAAA:
|
||
if !util.IsIPv6(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,无效的IPv6地址"})
|
||
return nil, true
|
||
}
|
||
case constant.R_CNAME:
|
||
if !util.IsValidDomain(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,无效的主机记录"})
|
||
return nil, true
|
||
}
|
||
case constant.R_MX:
|
||
if !util.IsValidDomain(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,无效的主机记录"})
|
||
return nil, true
|
||
}
|
||
case constant.R_TXT:
|
||
if len(value) > 512 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,长度必须 <= 512"})
|
||
return nil, true
|
||
}
|
||
default:
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(type)有误,不支持的记录类型: " + recordType})
|
||
return nil, true
|
||
}
|
||
newRecord := new(dao.ResolveRecord)
|
||
newRecord.Name = strings.TrimSpace(name)
|
||
newRecord.RecordType = strings.TrimSpace(recordType)
|
||
newRecord.Value = strings.TrimSpace(value)
|
||
return newRecord, false
|
||
}
|
||
func validModifyRequestBody(c *gin.Context, err error, name string, recordType string, ttl int, value string) (*dao.ResolveRecord, bool) {
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("校验失败, %v", err)})
|
||
return nil, true
|
||
}
|
||
if ttl != 0 && ttl < 10 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数缓存有效时间(ttl)有误,必须大于等于10"})
|
||
return nil, true
|
||
}
|
||
if !util.IsValidName(name) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数主机记录(name)有误,无效的主机记录"})
|
||
return nil, true
|
||
}
|
||
switch recordType {
|
||
case constant.R_A:
|
||
if !util.IsIPv4(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,无效的IPv4地址"})
|
||
return nil, true
|
||
}
|
||
case constant.R_AAAA:
|
||
if !util.IsIPv6(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,无效的IPv6地址"})
|
||
return nil, true
|
||
}
|
||
case constant.R_CNAME:
|
||
if !util.IsValidDomain(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,无效的主机记录"})
|
||
return nil, true
|
||
}
|
||
case constant.R_MX:
|
||
if !util.IsValidDomain(value) {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,无效的主机记录"})
|
||
return nil, true
|
||
}
|
||
case constant.R_TXT:
|
||
if len(value) > 512 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(value)有误,长度必须 <= 512"})
|
||
return nil, true
|
||
}
|
||
default:
|
||
c.JSON(http.StatusBadRequest, gin.H{"message": "参数记录值(type)有误,不支持的记录类型: " + recordType})
|
||
return nil, true
|
||
}
|
||
newRecord := new(dao.ResolveRecord)
|
||
newRecord.Name = strings.TrimSpace(name)
|
||
newRecord.RecordType = strings.TrimSpace(recordType)
|
||
newRecord.Value = strings.TrimSpace(value)
|
||
return newRecord, false
|
||
}
|