2024-10-21 14:30:50 +08:00
|
|
|
package controller
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2024-10-21 16:22:30 +08:00
|
|
|
"golang.org/x/net/idna"
|
|
|
|
|
2024-10-21 14:30:50 +08:00
|
|
|
"github.com/naiba/nezha/model"
|
|
|
|
"github.com/naiba/nezha/service/singleton"
|
|
|
|
)
|
|
|
|
|
2024-10-21 16:22:30 +08:00
|
|
|
// Add DDNS profile
|
|
|
|
// @Summary Add DDNS profile
|
2024-10-21 14:30:50 +08:00
|
|
|
// @Security BearerAuth
|
|
|
|
// @Schemes
|
2024-10-21 16:22:30 +08:00
|
|
|
// @Description Add DDNS profile
|
2024-10-21 14:30:50 +08:00
|
|
|
// @Tags auth required
|
|
|
|
// @Accept json
|
|
|
|
// @param request body model.DDNSForm true "DDNS Request"
|
|
|
|
// @Produce json
|
2024-10-23 17:56:51 +08:00
|
|
|
// @Success 200 {object} model.CommonResponse[uint64]
|
2024-10-21 14:30:50 +08:00
|
|
|
// @Router /ddns [post]
|
2024-10-23 17:56:51 +08:00
|
|
|
func createDDNS(c *gin.Context) (uint64, error) {
|
2024-10-21 14:30:50 +08:00
|
|
|
var df model.DDNSForm
|
|
|
|
var p model.DDNSProfile
|
|
|
|
|
|
|
|
if err := c.ShouldBindJSON(&df); err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return 0, err
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if df.MaxRetries < 1 || df.MaxRetries > 10 {
|
2024-10-23 17:56:51 +08:00
|
|
|
return 0, errors.New("重试次数必须为大于 1 且不超过 10 的整数")
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
p.Name = df.Name
|
2024-10-23 21:55:12 +08:00
|
|
|
enableIPv4 := df.EnableIPv4
|
|
|
|
enableIPv6 := df.EnableIPv6
|
2024-10-21 14:30:50 +08:00
|
|
|
p.EnableIPv4 = &enableIPv4
|
|
|
|
p.EnableIPv6 = &enableIPv6
|
|
|
|
p.MaxRetries = df.MaxRetries
|
|
|
|
p.Provider = df.Provider
|
|
|
|
p.DomainsRaw = df.DomainsRaw
|
|
|
|
p.Domains = strings.Split(p.DomainsRaw, ",")
|
|
|
|
p.AccessID = df.AccessID
|
|
|
|
p.AccessSecret = df.AccessSecret
|
|
|
|
p.WebhookURL = df.WebhookURL
|
|
|
|
p.WebhookMethod = df.WebhookMethod
|
|
|
|
p.WebhookRequestType = df.WebhookRequestType
|
|
|
|
p.WebhookRequestBody = df.WebhookRequestBody
|
|
|
|
p.WebhookHeaders = df.WebhookHeaders
|
|
|
|
|
|
|
|
for n, domain := range p.Domains {
|
|
|
|
// IDN to ASCII
|
|
|
|
domainValid, domainErr := idna.Lookup.ToASCII(domain)
|
|
|
|
if domainErr != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return 0, fmt.Errorf("域名 %s 解析错误: %v", domain, domainErr)
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
p.Domains[n] = domainValid
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := singleton.DB.Create(&p).Error; err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return 0, newGormError("%v", err)
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
singleton.OnDDNSUpdate()
|
2024-10-23 17:56:51 +08:00
|
|
|
|
|
|
|
return p.ID, nil
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
2024-10-21 16:22:30 +08:00
|
|
|
// Edit DDNS profile
|
|
|
|
// @Summary Edit DDNS profile
|
2024-10-21 14:30:50 +08:00
|
|
|
// @Security BearerAuth
|
|
|
|
// @Schemes
|
2024-10-21 16:22:30 +08:00
|
|
|
// @Description Edit DDNS profile
|
2024-10-21 14:30:50 +08:00
|
|
|
// @Tags auth required
|
|
|
|
// @Accept json
|
2024-10-21 23:00:51 +08:00
|
|
|
// @param id path string true "Profile ID"
|
2024-10-21 14:30:50 +08:00
|
|
|
// @param request body model.DDNSForm true "DDNS Request"
|
|
|
|
// @Produce json
|
|
|
|
// @Success 200 {object} model.CommonResponse[any]
|
|
|
|
// @Router /ddns/{id} [patch]
|
2024-10-23 17:56:51 +08:00
|
|
|
func updateDDNS(c *gin.Context) (any, error) {
|
2024-10-21 14:30:50 +08:00
|
|
|
idStr := c.Param("id")
|
2024-10-21 23:00:51 +08:00
|
|
|
|
2024-10-21 14:30:50 +08:00
|
|
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
|
|
|
if err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, err
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
2024-10-21 23:00:51 +08:00
|
|
|
var df model.DDNSForm
|
2024-10-21 14:30:50 +08:00
|
|
|
if err := c.ShouldBindJSON(&df); err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, err
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if df.MaxRetries < 1 || df.MaxRetries > 10 {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, errors.New("重试次数必须为大于 1 且不超过 10 的整数")
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
2024-10-21 23:00:51 +08:00
|
|
|
var p model.DDNSProfile
|
|
|
|
if err = singleton.DB.First(&p, id).Error; err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, fmt.Errorf("profile id %d does not exist", id)
|
2024-10-21 23:00:51 +08:00
|
|
|
}
|
|
|
|
|
2024-10-21 14:30:50 +08:00
|
|
|
p.Name = df.Name
|
|
|
|
p.ID = id
|
2024-10-23 21:55:12 +08:00
|
|
|
enableIPv4 := df.EnableIPv4
|
|
|
|
enableIPv6 := df.EnableIPv6
|
2024-10-21 14:30:50 +08:00
|
|
|
p.EnableIPv4 = &enableIPv4
|
|
|
|
p.EnableIPv6 = &enableIPv6
|
|
|
|
p.MaxRetries = df.MaxRetries
|
|
|
|
p.Provider = df.Provider
|
|
|
|
p.DomainsRaw = df.DomainsRaw
|
|
|
|
p.Domains = strings.Split(p.DomainsRaw, ",")
|
|
|
|
p.AccessID = df.AccessID
|
|
|
|
p.AccessSecret = df.AccessSecret
|
|
|
|
p.WebhookURL = df.WebhookURL
|
|
|
|
p.WebhookMethod = df.WebhookMethod
|
|
|
|
p.WebhookRequestType = df.WebhookRequestType
|
|
|
|
p.WebhookRequestBody = df.WebhookRequestBody
|
|
|
|
p.WebhookHeaders = df.WebhookHeaders
|
|
|
|
|
|
|
|
for n, domain := range p.Domains {
|
|
|
|
// IDN to ASCII
|
|
|
|
domainValid, domainErr := idna.Lookup.ToASCII(domain)
|
|
|
|
if domainErr != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, fmt.Errorf("域名 %s 解析错误: %v", domain, domainErr)
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
p.Domains[n] = domainValid
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = singleton.DB.Save(&p).Error; err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, newGormError("%v", err)
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
singleton.OnDDNSUpdate()
|
2024-10-23 17:56:51 +08:00
|
|
|
|
|
|
|
return nil, nil
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Batch delete DDNS configurations
|
|
|
|
// @Summary Batch delete DDNS configurations
|
|
|
|
// @Security BearerAuth
|
|
|
|
// @Schemes
|
|
|
|
// @Description Batch delete DDNS configurations
|
|
|
|
// @Tags auth required
|
|
|
|
// @Accept json
|
|
|
|
// @param request body []uint64 true "id list"
|
|
|
|
// @Produce json
|
|
|
|
// @Success 200 {object} model.CommonResponse[any]
|
|
|
|
// @Router /batch-delete/ddns [post]
|
2024-10-23 17:56:51 +08:00
|
|
|
func batchDeleteDDNS(c *gin.Context) (any, error) {
|
2024-10-21 14:30:50 +08:00
|
|
|
var ddnsConfigs []uint64
|
|
|
|
|
|
|
|
if err := c.ShouldBindJSON(&ddnsConfigs); err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, err
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := singleton.DB.Unscoped().Delete(&model.DDNSProfile{}, "id in (?)", ddnsConfigs).Error; err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, newGormError("%v", err)
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
singleton.OnDDNSUpdate()
|
2024-10-23 17:56:51 +08:00
|
|
|
|
|
|
|
return nil, nil
|
2024-10-21 14:30:50 +08:00
|
|
|
}
|
|
|
|
|
2024-10-21 16:22:30 +08:00
|
|
|
// List DDNS Profiles
|
|
|
|
// @Summary List DDNS profiles
|
|
|
|
// @Schemes
|
|
|
|
// @Description List DDNS profiles
|
|
|
|
// @Security BearerAuth
|
|
|
|
// @Tags auth required
|
|
|
|
// @param id query string false "Profile ID"
|
|
|
|
// @Produce json
|
|
|
|
// @Success 200 {object} model.CommonResponse[[]model.DDNSProfile]
|
|
|
|
// @Router /ddns [get]
|
2024-10-23 17:56:51 +08:00
|
|
|
func listDDNS(c *gin.Context) ([]model.DDNSProfile, error) {
|
2024-10-21 16:22:30 +08:00
|
|
|
var idList []uint64
|
|
|
|
idQuery := c.Query("id")
|
|
|
|
|
|
|
|
if idQuery != "" {
|
|
|
|
idListStr := strings.Split(idQuery, ",")
|
|
|
|
idList = make([]uint64, 0, len(idListStr))
|
|
|
|
for _, v := range idListStr {
|
|
|
|
id, err := strconv.ParseUint(v, 10, 64)
|
|
|
|
if err != nil {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, err
|
2024-10-21 16:22:30 +08:00
|
|
|
}
|
|
|
|
idList = append(idList, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var ddnsProfiles []model.DDNSProfile
|
|
|
|
|
|
|
|
singleton.DDNSCacheLock.RLock()
|
|
|
|
if len(idList) > 0 {
|
|
|
|
ddnsProfiles = make([]model.DDNSProfile, 0, len(idList))
|
|
|
|
for _, id := range idList {
|
|
|
|
if profile, ok := singleton.DDNSCache[id]; ok {
|
|
|
|
ddnsProfiles = append(ddnsProfiles, *profile)
|
|
|
|
} else {
|
2024-10-23 17:56:51 +08:00
|
|
|
return nil, fmt.Errorf("profile id %d not found", id)
|
2024-10-21 16:22:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ddnsProfiles = make([]model.DDNSProfile, 0, len(singleton.DDNSCache))
|
|
|
|
for _, profile := range singleton.DDNSCache {
|
|
|
|
ddnsProfiles = append(ddnsProfiles, *profile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
singleton.DDNSCacheLock.RUnlock()
|
2024-10-23 17:56:51 +08:00
|
|
|
return ddnsProfiles, nil
|
2024-10-21 16:22:30 +08:00
|
|
|
}
|
2024-10-22 00:04:17 +08:00
|
|
|
|
|
|
|
// List DDNS Providers
|
|
|
|
// @Summary List DDNS providers
|
|
|
|
// @Schemes
|
|
|
|
// @Description List DDNS providers
|
|
|
|
// @Security BearerAuth
|
|
|
|
// @Tags auth required
|
|
|
|
// @Produce json
|
|
|
|
// @Success 200 {object} model.CommonResponse[[]string]
|
|
|
|
// @Router /ddns/providers [get]
|
2024-10-23 17:56:51 +08:00
|
|
|
func listProviders(c *gin.Context) ([]string, error) {
|
|
|
|
return model.ProviderList, nil
|
2024-10-22 00:04:17 +08:00
|
|
|
}
|