This commit is contained in:
Your Name
2026-07-25 00:09:24 +08:00
parent 9a315e1404
commit 9bae6b1d9b
16 changed files with 338 additions and 17 deletions
+3 -1
View File
@@ -35,7 +35,9 @@ func CredentialTypeMeta(ctype CredentialType) []CredentialField {
}
case CredentialCloudflare:
return []CredentialField{
{Key: "cf_api_token", Label: "API Token", Type: "password", Required: true},
{Key: "cf_api_token", Label: "API Token", Type: "password", Required: false},
{Key: "cf_email", Label: "Email (Global API Key 方式)", Type: "text", Required: false},
{Key: "cf_api_key", Label: "Global API Key", Type: "password", Required: false},
}
case CredentialDNSPod:
return []CredentialField{
+37 -6
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
@@ -67,6 +68,11 @@ func (h *CredentialHandler) CreateCredential(c *gin.Context) {
return
}
// Trim whitespace from all values
for k, v := range testData {
testData[k] = strings.TrimSpace(v)
}
// Validate fields for the provider type
fields := config.CredentialTypeMeta(req.Type)
for _, f := range fields {
@@ -78,10 +84,23 @@ func (h *CredentialHandler) CreateCredential(c *gin.Context) {
}
}
// Cloudflare: require either API Token or Email+Key
if req.Type == config.CredentialCloudflare {
hasToken := testData["cf_api_token"] != ""
hasEmailKey := testData["cf_email"] != "" && testData["cf_api_key"] != ""
if !hasToken && !hasEmailKey {
c.JSON(http.StatusBadRequest, gin.H{"error": "请填写 API Token,或者同时填写 Email 和 Global API Key"})
return
}
}
// Re-serialize trimmed data
trimmedData, _ := json.Marshal(testData)
cred := &config.Credential{
Name: req.Name,
Type: req.Type,
Data: req.Data,
Data: string(trimmedData),
}
if err := config.CredStore.Create(cred); err != nil {
@@ -122,16 +141,28 @@ func (h *CredentialHandler) UpdateCredential(c *gin.Context) {
existing.Type = req.Type
}
if req.Data != "" {
// If it's the masked placeholder, keep existing data
if req.Data == "********" {
existing.Data = existing.Data
} else {
// If it's the masked placeholder, keep existing data unchanged
if req.Data != "********" {
var testData map[string]string
if err := json.Unmarshal([]byte(req.Data), &testData); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "data must be valid JSON"})
return
}
existing.Data = req.Data
// Trim whitespace from all values
for k, v := range testData {
testData[k] = strings.TrimSpace(v)
}
// Cloudflare: require either API Token or Email+Key
if existing.Type == config.CredentialCloudflare {
hasToken := testData["cf_api_token"] != ""
hasEmailKey := testData["cf_email"] != "" && testData["cf_api_key"] != ""
if !hasToken && !hasEmailKey {
c.JSON(http.StatusBadRequest, gin.H{"error": "请填写 API Token,或者同时填写 Email 和 Global API Key"})
return
}
}
trimmedData, _ := json.Marshal(testData)
existing.Data = string(trimmedData)
}
}
+14 -6
View File
@@ -52,6 +52,8 @@ type DNSConfig struct {
// Cloudflare
CFAPIToken string `json:"cf_api_token,omitempty"`
CFEmail string `json:"cf_email,omitempty"`
CFAPIKey string `json:"cf_api_key,omitempty"`
// DNSPod
DNSPodID string `json:"dnspod_id,omitempty"`
@@ -84,8 +86,8 @@ func GetACMECertificate(cert *config.Certificate, cfg *config.Config) error {
// Use custom DNS timeout for better reliability
if err := client.Challenge.SetDNS01Provider(provider,
dns01.AddRecursiveNameservers(dns01.ParseNameservers([]string{"8.8.8.8:53", "1.1.1.1:53"})),
dns01.AddDNSTimeout(15*time.Second),
dns01.AddRecursiveNameservers(dns01.ParseNameservers([]string{"223.5.5.5:53", "119.29.29.29:53", "8.8.8.8:53"})),
dns01.AddDNSTimeout(120*time.Second),
); err != nil {
return fmt.Errorf("failed to set DNS-01 provider: %v", err)
}
@@ -155,8 +157,8 @@ func RenewCertificate(cert *config.Certificate, cfg *config.Config) error {
// Use custom DNS timeout for better reliability
if err := client.Challenge.SetDNS01Provider(provider,
dns01.AddRecursiveNameservers(dns01.ParseNameservers([]string{"8.8.8.8:53", "1.1.1.1:53"})),
dns01.AddDNSTimeout(15*time.Second),
dns01.AddRecursiveNameservers(dns01.ParseNameservers([]string{"223.5.5.5:53", "119.29.29.29:53", "8.8.8.8:53"})),
dns01.AddDNSTimeout(120*time.Second),
); err != nil {
return fmt.Errorf("failed to set DNS-01 provider: %v", err)
}
@@ -327,8 +329,14 @@ func getDNSProvider(cert *config.Certificate) (challenge.Provider, error) {
case "cloudflare":
cfg := cloudflareprov.NewDefaultConfig()
if dnsCfg.CFAPIToken != "" {
cfg.AuthToken = dnsCfg.CFAPIToken
token := strings.TrimSpace(dnsCfg.CFAPIToken)
if token != "" {
cfg.AuthToken = token
}
// Also support email + Global API Key auth
if dnsCfg.CFEmail != "" && dnsCfg.CFAPIKey != "" {
cfg.AuthEmail = strings.TrimSpace(dnsCfg.CFEmail)
cfg.AuthKey = strings.TrimSpace(dnsCfg.CFAPIKey)
}
provider, err := cloudflareprov.NewDNSProviderConfig(cfg)
if err != nil {