<add>domain support

This commit is contained in:
2025-09-12 15:30:07 +00:00
parent b6fed87d61
commit 6c291c0121
14 changed files with 654 additions and 534 deletions

View File

@@ -151,6 +151,12 @@ func routers(r *gin.Engine, frontendDist fs.FS) {
auth.PATCH("/setting", adminHandler(updateConfig))
auth.GET("/domains", commonHandler(GetDomainList))
auth.POST("/domains", commonHandler(AddDomain))
auth.POST("/domains/:id/verify", commonHandler(VerifyDomain))
auth.PUT("/domains/:id", commonHandler(UpdateDomain))
auth.DELETE("/domains/:id", commonHandler(DeleteDomain))
r.NoRoute(fallbackToFrontend(frontendDist))
}

View File

@@ -0,0 +1,120 @@
// cmd/dashboard/controller/domain.go
package controller
import (
"encoding/json"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/nezhahq/nezha/model"
"github.com/nezhahq/nezha/service/singleton"
)
// DomainAPIResponse 是用于API返回的结构体可以包含一些后端计算好的字段
type DomainAPIResponse struct {
model.Domain
ExpiresInDays *int `json:"expires_in_days"` // 剩余天数使用指针以区分0和null
}
// UpdateDomain 更新域名
func UpdateDomain(c *gin.Context) (any, error) {
domainID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
return nil, newGormError("无效的域名ID")
}
var req model.DomainUpdateRequest // 使用新的请求体
if err := c.ShouldBindJSON(&req); err != nil {
return nil, newGormError("无效的请求: %s", err.Error())
}
return singleton.UpdateDomain(domainID, req)
}
func GetDomainList(c *gin.Context) (any, error) {
scope := c.DefaultQuery("scope", "admin")
domains, err := singleton.GetDomains(scope)
if err != nil {
return nil, err
}
var response []DomainAPIResponse
for _, d := range domains {
apiDomain := DomainAPIResponse{
Domain: d,
}
if d.BillingData != nil {
var billing model.BillingDataMod
if json.Unmarshal(d.BillingData, &billing) == nil && billing.EndDate != "" {
if endDate, err := time.Parse(time.RFC3339, billing.EndDate); err == nil {
daysLeft := int(time.Until(endDate).Hours() / 24)
apiDomain.ExpiresInDays = &daysLeft
}
}
}
response = append(response, apiDomain)
}
return response, nil
}
func AddDomain(c *gin.Context) (any, error) {
var req model.DomainAPIRequest // 使用 model/domain.go 中定义的请求体
if err := c.ShouldBindJSON(&req); err != nil {
return nil, newGormError("无效的请求: %s", err.Error())
}
return singleton.AddDomain(req.Domain)
}
func VerifyDomain(c *gin.Context) (any, error) {
domainID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
return nil, newGormError("无效的域名ID")
}
success, err := singleton.VerifyDomain(domainID)
if err != nil {
return nil, newGormError("验证过程中发生错误: %s", err.Error())
}
var message string
if success {
message = "验证成功,域名状态已更新"
} else {
message = "验证失败,未找到匹配的 TXT 记录"
}
// 对于这种包含非数据字段如message的特殊成功响应
// 我们可以直接返回一个 mapcommonHandler 会将其包装
return gin.H{
"success": success,
"message": message,
}, nil
}
func DeleteDomain(c *gin.Context) (any, error) {
domainID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
return nil, nil // ID无效直接返回成功不做任何事
}
if err := singleton.DeleteDomain(domainID); err != nil {
return nil, err
}
// 对于DELETE成功通常不返回data返回nil即可
return nil, nil
}
func UpdateDomainInfo(c *gin.Context) (any, error) {
domainID, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
return nil, newGormError("无效的域名ID")
}
var req model.DomainUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
return nil, newGormError("无效的请求: %s", err.Error())
}
return singleton.UpdateDomain(domainID, req)
}