Repository: 1Panel-dev/1Panel
Pull request #8782: feat: add .user.ini for php website
Author: login: zhengkunwang223
Labels: name: do-not-merge/release-note-label-needed
name: approved
name: dev-v2
Created: 2025-05-22T07:08:31Z | Merged: 2025-05-22T07:17:59Z



Diff:
diff --git a/agent/app/service/runtime_utils.go b/agent/app/service/runtime_utils.go
index 0c283938fc84..fdaadf224a8e 100644
--- a/agent/app/service/runtime_utils.go
+++ b/agent/app/service/runtime_utils.go
@@ -188,6 +188,7 @@ func SyncRuntimesStatus(runtimes []model.Runtime) error {
 			case "restarting":
 				runtimes[index].Status = constant.StatusRestarting
 			}
+			_ = runtimeRepo.Save(&runtimes[index])
 			delete(runtimeContainer, contain.Names[0])
 		}
 	}
diff --git a/agent/app/service/website.go b/agent/app/service/website.go
index d7b8817baf16..cc0cabaa1ab5 100644
--- a/agent/app/service/website.go
+++ b/agent/app/service/website.go
@@ -422,6 +422,15 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
 		if err = createWafConfig(website, domains); err != nil {
 			return err
 		}
+		if create.Type == constant.Runtime {
+			runtime, err = runtimeRepo.GetFirst(context.Background(), repo.WithByID(create.RuntimeID))
+			if err != nil {
+				return err
+			}
+			if runtime.Type == constant.RuntimePHP && runtime.Resource == constant.ResourceAppstore {
+				createPHPConfig(website)
+			}
+		}
 		tx, ctx := helper.GetTxAndContext()
 		defer tx.Rollback()
 		if err = websiteRepo.Create(ctx, website); err != nil {
diff --git a/agent/app/service/website_utils.go b/agent/app/service/website_utils.go
index 42425c17dfaa..38ee4b5794cc 100644
--- a/agent/app/service/website_utils.go
+++ b/agent/app/service/website_utils.go
@@ -349,6 +349,13 @@ func createAllWebsitesWAFConfig(websites []model.Website) error {
 	return nil
 }
 
+func createPHPConfig(website *model.Website) {
+	fileOp := files.NewFileOp()
+	userIniPath := path.Join(GetSitePath(*website, SiteIndexDir), ".user.ini")
+	_ = fileOp.CreateFile(userIniPath)
+	_ = fileOp.SaveFile(userIniPath, fmt.Sprintf("open_basedir=/www/sites/%s/index", website.Alias), 0644)
+}
+
 func createWafConfig(website *model.Website, domains []model.WebsiteDomain) error {
 	nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
 	if err != nil {
diff --git a/agent/utils/ssl/acme.go b/agent/utils/ssl/acme.go
index 9e0b05ea4960..8adb04e4b86a 100644
--- a/agent/utils/ssl/acme.go
+++ b/agent/utils/ssl/acme.go
@@ -46,6 +46,23 @@ const (
 	KeyRSA4096 = certcrypto.RSA4096
 )
 
+type AcmeUser struct {
+	Email        string
+	Registration *registration.Resource
+	Key          crypto.PrivateKey
+}
+
+func (u *AcmeUser) GetEmail() string {
+	return u.Email
+}
+
+func (u *AcmeUser) GetRegistration() *registration.Resource {
+	return u.Registration
+}
+func (u *AcmeUser) GetPrivateKey() crypto.PrivateKey {
+	return u.Key
+}
+
 func GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) {
 	var (
 		marshal []byte
diff --git a/agent/utils/ssl/client.go b/agent/utils/ssl/client.go
index 28b5d52144fc..f50b261a00f8 100644
--- a/agent/utils/ssl/client.go
+++ b/agent/utils/ssl/client.go
@@ -2,25 +2,7 @@ package ssl
 
 import (
 	"crypto"
-	"encoding/json"
 	"github.com/1Panel-dev/1Panel/agent/app/dto"
-	"github.com/go-acme/lego/v4/providers/dns/alidns"
-	"github.com/go-acme/lego/v4/providers/dns/clouddns"
-	"github.com/go-acme/lego/v4/providers/dns/cloudflare"
-	"github.com/go-acme/lego/v4/providers/dns/cloudns"
-	"github.com/go-acme/lego/v4/providers/dns/dnspod"
-	"github.com/go-acme/lego/v4/providers/dns/freemyip"
-	"github.com/go-acme/lego/v4/providers/dns/godaddy"
-	"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
-	"github.com/go-acme/lego/v4/providers/dns/namecheap"
-	"github.com/go-acme/lego/v4/providers/dns/namedotcom"
-	"github.com/go-acme/lego/v4/providers/dns/namesilo"
-	"github.com/go-acme/lego/v4/providers/dns/rainyun"
-	"github.com/go-acme/lego/v4/providers/dns/spaceship"
-	"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
-	"github.com/go-acme/lego/v4/providers/dns/vercel"
-	"github.com/go-acme/lego/v4/providers/dns/volcengine"
-	"github.com/go-acme/lego/v4/providers/dns/westcn"
 	"os"
 	"strings"
 	"time"
@@ -33,39 +15,15 @@ import (
 	"github.com/go-acme/lego/v4/challenge/dns01"
 	"github.com/go-acme/lego/v4/lego"
 	"github.com/go-acme/lego/v4/providers/http/webroot"
-	"github.com/go-acme/lego/v4/registration"
 	"github.com/pkg/errors"
 )
 
-type AcmeUser struct {
-	Email        string
-	Registration *registration.Resource
-	Key          crypto.PrivateKey
-}
-
-func (u *AcmeUser) GetEmail() string {
-	return u.Email
-}
-
-func (u *AcmeUser) GetRegistration() *registration.Resource {
-	return u.Registration
-}
-func (u *AcmeUser) GetPrivateKey() crypto.PrivateKey {
-	return u.Key
-}
-
 type AcmeClientOption func(*AcmeClientOptions)
 
 type AcmeClientOptions struct {
 	SystemProxy *dto.SystemProxy
 }
 
-func WithSystemProxy(proxy *dto.SystemProxy) AcmeClientOption {
-	return func(opts *AcmeClientOptions) {
-		opts.SystemProxy = proxy
-	}
-}
-
 type AcmeClient struct {
 	Config   *lego.Config
 	Client   *lego.Client
@@ -85,205 +43,6 @@ func NewAcmeClient(acmeAccount *model.WebsiteAcmeAccount, systemProxy *dto.Syste
 	return client, nil
 }
 
-type DnsType string
-
-const (
-	DnsPod       DnsType = "DnsPod"
-	AliYun       DnsType = "AliYun"
-	CloudFlare   DnsType = "CloudFlare"
-	CloudDns     DnsType = "CloudDns"
-	NameSilo     DnsType = "NameSilo"
-	NameCheap    DnsType = "NameCheap"
-	NameCom      DnsType = "NameCom"
-	Godaddy      DnsType = "Godaddy"
-	TencentCloud DnsType = "TencentCloud"
-	RainYun      DnsType = "RainYun"
-	Volcengine   DnsType = "Volcengine"
-	HuaweiCloud  DnsType = "HuaweiCloud"
-	FreeMyIP     DnsType = "FreeMyIP"
-	Vercel       DnsType = "Vercel"
-	Spaceship    DnsType = "Spaceship"
-	WestCN       DnsType = "WestCN"
-	ClouDNS      DnsType = "ClouDNS"
-)
-
-type DNSParam struct {
-	ID           string `json:"id"`
-	Token        string `json:"token"`
-	AccessKey    string `json:"accessKey"`
-	SecretKey    string `json:"secretKey"`
-	Email        string `json:"email"`
-	APIkey       string `json:"apiKey"`
-	APIUser      string `json:"apiUser"`
-	APISecret    string `json:"apiSecret"`
-	SecretID     string `json:"secretID"`
-	ClientID     string `json:"clientID"`
-	Password     string `json:"password"`
-	Region       string `json:"region"`
-	Username     string `json:"username"`
-	AuthID       string `json:"authID"`
-	SubAuthID    string `json:"subAuthID"`
-	AuthPassword string `json:"authPassword"`
-}
-
-var (
-	propagationTimeout = 30 * time.Minute
-	pollingInterval    = 10 * time.Second
-	ttl                = 3600
-	dnsTimeOut         = 30 * time.Minute
-	manualDnsTimeout   = 10 * time.Minute
-)
-
-func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, error) {
-	var (
-		param DNSParam
-		p     challenge.Provider
-		err   error
-	)
-	if err := json.Unmarshal([]byte(params), &param); err != nil {
-		return nil, err
-	}
-	switch dnsType {
-	case DnsPod:
-		dnsPodConfig := dnspod.NewDefaultConfig()
-		dnsPodConfig.LoginToken = param.ID + "," + param.Token
-		dnsPodConfig.PropagationTimeout = propagationTimeout
-		dnsPodConfig.PollingInterval = pollingInterval
-		dnsPodConfig.TTL = ttl
-		p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
-	case AliYun:
-		alidnsConfig := alidns.NewDefaultConfig()
-		alidnsConfig.SecretKey = param.SecretKey
-		alidnsConfig.APIKey = param.AccessKey
-		alidnsConfig.PropagationTimeout = propagationTimeout
-		alidnsConfig.PollingInterval = pollingInterval
-		alidnsConfig.TTL = ttl
-		p, err = alidns.NewDNSProviderConfig(alidnsConfig)
-	case CloudFlare:
-		cloudflareConfig := cloudflare.NewDefaultConfig()
-		cloudflareConfig.AuthEmail = param.Email
-		cloudflareConfig.AuthToken = param.APIkey
-		cloudflareConfig.PropagationTimeout = propagationTimeout
-		cloudflareConfig.PollingInterval = pollingInterval
-		cloudflareConfig.TTL = ttl
-		p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
-	case CloudDns:
-		clouddnsConfig := clouddns.NewDefaultConfig()
-		clouddnsConfig.ClientID = param.ClientID
-		clouddnsConfig.Email = param.Email
-		clouddnsConfig.Password = param.Password
-		clouddnsConfig.PropagationTimeout = propagationTimeout
-		clouddnsConfig.PollingInterval = pollingInterval
-		clouddnsConfig.TTL = ttl
-		p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
-	case NameCheap:
-		namecheapConfig := namecheap.NewDefaultConfig()
-		namecheapConfig.APIKey = param.APIkey
-		namecheapConfig.APIUser = param.APIUser
-		namecheapConfig.PropagationTimeout = propagationTimeout
-		namecheapConfig.PollingInterval = pollingInterval
-		namecheapConfig.TTL = ttl
-		p, err = namecheap.NewDNSProviderConfig(namecheapConfig)
-	case NameSilo:
-		nameSiloConfig := namesilo.NewDefaultConfig()
-		nameSiloConfig.APIKey = param.APIkey
-		nameSiloConfig.PropagationTimeout = propagationTimeout
-		nameSiloConfig.PollingInterval = pollingInterval
-		nameSiloConfig.TTL = ttl
-		p, err = namesilo.NewDNSProviderConfig(nameSiloConfig)
-	case Godaddy:
-		godaddyConfig := godaddy.NewDefaultConfig()
-		godaddyConfig.APIKey = param.APIkey
-		godaddyConfig.APISecret = param.APISecret
-		godaddyConfig.PropagationTimeout = propagationTimeout
-		godaddyConfig.PollingInterval = pollingInterval
-		godaddyConfig.TTL = ttl
-		p, err = godaddy.NewDNSProviderConfig(godaddyConfig)
-	case NameCom:
-		nameComConfig := namedotcom.NewDefaultConfig()
-		nameComConfig.APIToken = param.Token
-		nameComConfig.Username = param.APIUser
-		nameComConfig.PropagationTimeout = propagationTimeout
-		nameComConfig.PollingInterval = pollingInterval
-		nameComConfig.TTL = ttl
-		p, err = namedotcom.NewDNSProviderConfig(nameComConfig)
-	case TencentCloud:
-		tencentCloudConfig := tencentcloud.NewDefaultConfig()
-		tencentCloudConfig.SecretID = param.SecretID
-		tencentCloudConfig.SecretKey = param.SecretKey
-		tencentCloudConfig.PropagationTimeout = propagationTimeout
-		tencentCloudConfig.PollingInterval = pollingInterval
-		tencentCloudConfig.TTL = ttl
-		p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig)
-	case RainYun:
-		rainyunConfig := rainyun.NewDefaultConfig()
-		rainyunConfig.APIKey = param.APIkey
-		rainyunConfig.PropagationTimeout = propagationTimeout
-		rainyunConfig.PollingInterval = pollingInterval
-		rainyunConfig.TTL = ttl
-		p, err = rainyun.NewDNSProviderConfig(rainyunConfig)
-	case Volcengine:
-		volcConfig := volcengine.NewDefaultConfig()
-		volcConfig.SecretKey = param.SecretKey
-		volcConfig.AccessKey = param.AccessKey
-		volcConfig.PropagationTimeout = propagationTimeout
-		volcConfig.PollingInterval = pollingInterval
-		volcConfig.TTL = ttl
-		p, err = volcengine.NewDNSProviderConfig(volcConfig)
-	case HuaweiCloud:
-		huaweiCloudConfig := huaweicloud.NewDefaultConfig()
-		huaweiCloudConfig.AccessKeyID = param.AccessKey
-		huaweiCloudConfig.SecretAccessKey = param.SecretKey
-		huaweiCloudConfig.Region = param.Region
-		huaweiCloudConfig.PropagationTimeout = propagationTimeout
-		huaweiCloudConfig.PollingInterval = pollingInterval
-		huaweiCloudConfig.TTL = int32(ttl)
-		p, err = huaweicloud.NewDNSProviderConfig(huaweiCloudConfig)
-	case FreeMyIP:
-		freeMyIpConfig := freemyip.NewDefaultConfig()
-		freeMyIpConfig.Token = param.Token
-		freeMyIpConfig.PropagationTimeout = propagationTimeout
-		freeMyIpConfig.PollingInterval = pollingInterval
-		p, err = freemyip.NewDNSProviderConfig(freeMyIpConfig)
-	case Vercel:
-		vercelConfig := vercel.NewDefaultConfig()
-		vercelConfig.AuthToken = param.Token
-		vercelConfig.PropagationTimeout = propagationTimeout
-		vercelConfig.PollingInterval = pollingInterval
-		p, err = vercel.NewDNSProviderConfig(vercelConfig)
-	case Spaceship:
-		spaceshipConfig := spaceship.NewDefaultConfig()
-		spaceshipConfig.APIKey = param.APIkey
-		spaceshipConfig.APISecret = param.APISecret
-		spaceshipConfig.PropagationTimeout = propagationTimeout
-		spaceshipConfig.PollingInterval = pollingInterval
-		spaceshipConfig.TTL = ttl
-		p, err = spaceship.NewDNSProviderConfig(spaceshipConfig)
-	case WestCN:
-		westcnConfig := westcn.NewDefaultConfig()
-		westcnConfig.Username = param.Username
-		westcnConfig.Password = param.Password
-		westcnConfig.PropagationTimeout = propagationTimeout
-		westcnConfig.PollingInterval = pollingInterval
-		westcnConfig.TTL = ttl
-		p, err = westcn.NewDNSProviderConfig(westcnConfig)
-
-	case ClouDNS:
-		cloudnsConfig := cloudns.NewDefaultConfig()
-		cloudnsConfig.AuthID = param.AuthID
-		cloudnsConfig.SubAuthID = param.SubAuthID
-		cloudnsConfig.AuthPassword = param.AuthPassword
-		cloudnsConfig.PropagationTimeout = propagationTimeout
-		cloudnsConfig.PollingInterval = pollingInterval
-		cloudnsConfig.TTL = ttl
-		p, err = cloudns.NewDNSProviderConfig(cloudnsConfig)
-	}
-	if err != nil {
-		return nil, err
-	}
-	return p, nil
-}
-
 func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.WebsiteSSL) error {
 	p, err := getDNSProviderConfig(dnsType, params)
 	if err != nil {
@@ -313,7 +72,7 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, websiteSSL model.Web
 
 func (c *AcmeClient) UseManualDns(websiteSSL model.WebsiteSSL) error {
 	p, err := NewCustomDNSProviderManual(&ManualConfig{
-		PropagationTimeout: propagationTimeout,
+		PropagationTimeout: 20 * time.Minute,
 		PollingInterval:    pollingInterval,
 		TTL:                ttl,
 	})
@@ -381,36 +140,6 @@ type Resolve struct {
 	Err   string
 }
 
-type ManualConfig struct {
-	TTL                int
-	PropagationTimeout time.Duration
-	PollingInterval    time.Duration
-}
-
-type CustomManualDnsProvider struct {
-	config *ManualConfig
-}
-
-func NewCustomDNSProviderManual(config *ManualConfig) (*CustomManualDnsProvider, error) {
-	return &CustomManualDnsProvider{config}, nil
-}
-
-func (p *CustomManualDnsProvider) Present(domain, token, keyAuth string) error {
-	return nil
-}
-
-func (p *CustomManualDnsProvider) CleanUp(domain, token, keyAuth string) error {
-	return nil
-}
-
-func (p *CustomManualDnsProvider) Sequential() time.Duration {
-	return manualDnsTimeout
-}
-
-func (p *CustomManualDnsProvider) Timeout() (timeout, interval time.Duration) {
-	return p.config.PropagationTimeout, p.config.PollingInterval
-}
-
 func (c *AcmeClient) GetDNSResolve(domains []string) (map[string]Resolve, error) {
 	core, err := api.New(c.Config.HTTPClient, c.Config.UserAgent, c.Config.CADirURL, c.User.Registration.URI, c.User.Key)
 	if err != nil {
@@ -462,6 +191,7 @@ func (c *AcmeClient) GetDNSResolve(domains []string) (map[string]Resolve, error)
 		if strings.HasPrefix(domain, "*.") && strings.Contains(fqdn, "*.") {
 			fqdn = strings.Replace(fqdn, "*.", "", 1)
 		}
+		_, _ = dns01.FindZoneByFqdn(challengeInfo.EffectiveFQDN)
 		resolves[domain] = Resolve{
 			Key:   fqdn,
 			Value: challengeInfo.Value,
diff --git a/agent/utils/ssl/custom_manual_provider.go b/agent/utils/ssl/custom_manual_provider.go
new file mode 100644
index 000000000000..1b0d25292b76
--- /dev/null
+++ b/agent/utils/ssl/custom_manual_provider.go
@@ -0,0 +1,33 @@
+package ssl
+
+import "time"
+
+type ManualConfig struct {
+	TTL                int
+	PropagationTimeout time.Duration
+	PollingInterval    time.Duration
+}
+
+type CustomManualDnsProvider struct {
+	config *ManualConfig
+}
+
+func NewCustomDNSProviderManual(config *ManualConfig) (*CustomManualDnsProvider, error) {
+	return &CustomManualDnsProvider{config}, nil
+}
+
+func (p *CustomManualDnsProvider) Present(domain, token, keyAuth string) error {
+	return nil
+}
+
+func (p *CustomManualDnsProvider) CleanUp(domain, token, keyAuth string) error {
+	return nil
+}
+
+func (p *CustomManualDnsProvider) Sequential() time.Duration {
+	return manualDnsTimeout
+}
+
+func (p *CustomManualDnsProvider) Timeout() (timeout, interval time.Duration) {
+	return p.config.PropagationTimeout, p.config.PollingInterval
+}
diff --git a/agent/utils/ssl/dns_provider.go b/agent/utils/ssl/dns_provider.go
new file mode 100644
index 000000000000..cacd0bc56386
--- /dev/null
+++ b/agent/utils/ssl/dns_provider.go
@@ -0,0 +1,223 @@
+package ssl
+
+import (
+	"encoding/json"
+	"github.com/go-acme/lego/v4/challenge"
+	"github.com/go-acme/lego/v4/providers/dns/alidns"
+	"github.com/go-acme/lego/v4/providers/dns/clouddns"
+	"github.com/go-acme/lego/v4/providers/dns/cloudflare"
+	"github.com/go-acme/lego/v4/providers/dns/cloudns"
+	"github.com/go-acme/lego/v4/providers/dns/dnspod"
+	"github.com/go-acme/lego/v4/providers/dns/freemyip"
+	"github.com/go-acme/lego/v4/providers/dns/godaddy"
+	"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
+	"github.com/go-acme/lego/v4/providers/dns/namecheap"
+	"github.com/go-acme/lego/v4/providers/dns/namedotcom"
+	"github.com/go-acme/lego/v4/providers/dns/namesilo"
+	"github.com/go-acme/lego/v4/providers/dns/rainyun"
+	"github.com/go-acme/lego/v4/providers/dns/spaceship"
+	"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
+	"github.com/go-acme/lego/v4/providers/dns/vercel"
+	"github.com/go-acme/lego/v4/providers/dns/volcengine"
+	"github.com/go-acme/lego/v4/providers/dns/westcn"
+	"time"
+)
+
+type DnsType string
+
+const (
+	DnsPod       DnsType = "DnsPod"
+	AliYun       DnsType = "AliYun"
+	CloudFlare   DnsType = "CloudFlare"
+	CloudDns     DnsType = "CloudDns"
+	NameSilo     DnsType = "NameSilo"
+	NameCheap    DnsType = "NameCheap"
+	NameCom      DnsType = "NameCom"
+	Godaddy      DnsType = "Godaddy"
+	TencentCloud DnsType = "TencentCloud"
+	RainYun      DnsType = "RainYun"
+	Volcengine   DnsType = "Volcengine"
+	HuaweiCloud  DnsType = "HuaweiCloud"
+	FreeMyIP     DnsType = "FreeMyIP"
+	Vercel       DnsType = "Vercel"
+	Spaceship    DnsType = "Spaceship"
+	WestCN       DnsType = "WestCN"
+	ClouDNS      DnsType = "ClouDNS"
+)
+
+type DNSParam struct {
+	ID           string `json:"id"`
+	Token        string `json:"token"`
+	AccessKey    string `json:"accessKey"`
+	SecretKey    string `json:"secretKey"`
+	Email        string `json:"email"`
+	APIkey       string `json:"apiKey"`
+	APIUser      string `json:"apiUser"`
+	APISecret    string `json:"apiSecret"`
+	SecretID     string `json:"secretID"`
+	ClientID     string `json:"clientID"`
+	Password     string `json:"password"`
+	Region       string `json:"region"`
+	Username     string `json:"username"`
+	AuthID       string `json:"authID"`
+	SubAuthID    string `json:"subAuthID"`
+	AuthPassword string `json:"authPassword"`
+}
+
+var (
+	propagationTimeout = 30 * time.Minute
+	pollingInterval    = 10 * time.Second
+	ttl                = 3600
+	dnsTimeOut         = 30 * time.Minute
+	manualDnsTimeout   = 10 * time.Minute
+)
+
+func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, error) {
+	var (
+		param DNSParam
+		p     challenge.Provider
+		err   error
+	)
+	if err := json.Unmarshal([]byte(params), &param); err != nil {
+		return nil, err
+	}
+	switch dnsType {
+	case DnsPod:
+		dnsPodConfig := dnspod.NewDefaultConfig()
+		dnsPodConfig.LoginToken = param.ID + "," + param.Token
+		dnsPodConfig.PropagationTimeout = propagationTimeout
+		dnsPodConfig.PollingInterval = pollingInterval
+		dnsPodConfig.TTL = ttl
+		p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
+	case AliYun:
+		alidnsConfig := alidns.NewDefaultConfig()
+		alidnsConfig.SecretKey = param.SecretKey
+		alidnsConfig.APIKey = param.AccessKey
+		alidnsConfig.PropagationTimeout = propagationTimeout
+		alidnsConfig.PollingInterval = pollingInterval
+		alidnsConfig.TTL = ttl
+		p, err = alidns.NewDNSProviderConfig(alidnsConfig)
+	case CloudFlare:
+		cloudflareConfig := cloudflare.NewDefaultConfig()
+		cloudflareConfig.AuthEmail = param.Email
+		cloudflareConfig.AuthToken = param.APIkey
+		cloudflareConfig.PropagationTimeout = propagationTimeout
+		cloudflareConfig.PollingInterval = pollingInterval
+		cloudflareConfig.TTL = ttl
+		p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
+	case CloudDns:
+		clouddnsConfig := clouddns.NewDefaultConfig()
+		clouddnsConfig.ClientID = param.ClientID
+		clouddnsConfig.Email = param.Email
+		clouddnsConfig.Password = param.Password
+		clouddnsConfig.PropagationTimeout = propagationTimeout
+		clouddnsConfig.PollingInterval = pollingInterval
+		clouddnsConfig.TTL = ttl
+		p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
+	case NameCheap:
+		namecheapConfig := namecheap.NewDefaultConfig()
+		namecheapConfig.APIKey = param.APIkey
+		namecheapConfig.APIUser = param.APIUser
+		namecheapConfig.PropagationTimeout = propagationTimeout
+		namecheapConfig.PollingInterval = pollingInterval
+		namecheapConfig.TTL = ttl
+		p, err = namecheap.NewDNSProviderConfig(namecheapConfig)
+	case NameSilo:
+		nameSiloConfig := namesilo.NewDefaultConfig()
+		nameSiloConfig.APIKey = param.APIkey
+		nameSiloConfig.PropagationTimeout = propagationTimeout
+		nameSiloConfig.PollingInterval = pollingInterval
+		nameSiloConfig.TTL = ttl
+		p, err = namesilo.NewDNSProviderConfig(nameSiloConfig)
+	case Godaddy:
+		godaddyConfig := godaddy.NewDefaultConfig()
+		godaddyConfig.APIKey = param.APIkey
+		godaddyConfig.APISecret = param.APISecret
+		godaddyConfig.PropagationTimeout = propagationTimeout
+		godaddyConfig.PollingInterval = pollingInterval
+		godaddyConfig.TTL = ttl
+		p, err = godaddy.NewDNSProviderConfig(godaddyConfig)
+	case NameCom:
+		nameComConfig := namedotcom.NewDefaultConfig()
+		nameComConfig.APIToken = param.Token
+		nameComConfig.Username = param.APIUser
+		nameComConfig.PropagationTimeout = propagationTimeout
+		nameComConfig.PollingInterval = pollingInterval
+		nameComConfig.TTL = ttl
+		p, err = namedotcom.NewDNSProviderConfig(nameComConfig)
+	case TencentCloud:
+		tencentCloudConfig := tencentcloud.NewDefaultConfig()
+		tencentCloudConfig.SecretID = param.SecretID
+		tencentCloudConfig.SecretKey = param.SecretKey
+		tencentCloudConfig.PropagationTimeout = propagationTimeout
+		tencentCloudConfig.PollingInterval = pollingInterval
+		tencentCloudConfig.TTL = ttl
+		p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig)
+	case RainYun:
+		rainyunConfig := rainyun.NewDefaultConfig()
+		rainyunConfig.APIKey = param.APIkey
+		rainyunConfig.PropagationTimeout = propagationTimeout
+		rainyunConfig.PollingInterval = pollingInterval
+		rainyunConfig.TTL = ttl
+		p, err = rainyun.NewDNSProviderConfig(rainyunConfig)
+	case Volcengine:
+		volcConfig := volcengine.NewDefaultConfig()
+		volcConfig.SecretKey = param.SecretKey
+		volcConfig.AccessKey = param.AccessKey
+		volcConfig.PropagationTimeout = propagationTimeout
+		volcConfig.PollingInterval = pollingInterval
+		volcConfig.TTL = ttl
+		p, err = volcengine.NewDNSProviderConfig(volcConfig)
+	case HuaweiCloud:
+		huaweiCloudConfig := huaweicloud.NewDefaultConfig()
+		huaweiCloudConfig.AccessKeyID = param.AccessKey
+		huaweiCloudConfig.SecretAccessKey = param.SecretKey
+		huaweiCloudConfig.Region = param.Region
+		huaweiCloudConfig.PropagationTimeout = propagationTimeout
+		huaweiCloudConfig.PollingInterval = pollingInterval
+		huaweiCloudConfig.TTL = int32(ttl)
+		p, err = huaweicloud.NewDNSProviderConfig(huaweiCloudConfig)
+	case FreeMyIP:
+		freeMyIpConfig := freemyip.NewDefaultConfig()
+		freeMyIpConfig.Token = param.Token
+		freeMyIpConfig.PropagationTimeout = propagationTimeout
+		freeMyIpConfig.PollingInterval = pollingInterval
+		p, err = freemyip.NewDNSProviderConfig(freeMyIpConfig)
+	case Vercel:
+		vercelConfig := vercel.NewDefaultConfig()
+		vercelConfig.AuthToken = param.Token
+		vercelConfig.PropagationTimeout = propagationTimeout
+		vercelConfig.PollingInterval = pollingInterval
+		p, err = vercel.NewDNSProviderConfig(vercelConfig)
+	case Spaceship:
+		spaceshipConfig := spaceship.NewDefaultConfig()
+		spaceshipConfig.APIKey = param.APIkey
+		spaceshipConfig.APISecret = param.APISecret
+		spaceshipConfig.PropagationTimeout = propagationTimeout
+		spaceshipConfig.PollingInterval = pollingInterval
+		spaceshipConfig.TTL = ttl
+		p, err = spaceship.NewDNSProviderConfig(spaceshipConfig)
+	case WestCN:
+		westcnConfig := westcn.NewDefaultConfig()
+		westcnConfig.Username = param.Username
+		westcnConfig.Password = param.Password
+		westcnConfig.PropagationTimeout = propagationTimeout
+		westcnConfig.PollingInterval = pollingInterval
+		westcnConfig.TTL = ttl
+		p, err = westcn.NewDNSProviderConfig(westcnConfig)
+
+	case ClouDNS:
+		cloudnsConfig := cloudns.NewDefaultConfig()
+		cloudnsConfig.AuthID = param.AuthID
+		cloudnsConfig.SubAuthID = param.SubAuthID
+		cloudnsConfig.AuthPassword = param.AuthPassword
+		cloudnsConfig.PropagationTimeout = propagationTimeout
+		cloudnsConfig.PollingInterval = pollingInterval
+		cloudnsConfig.TTL = ttl
+		p, err = cloudns.NewDNSProviderConfig(cloudnsConfig)
+	}
+	if err != nil {
+		return nil, err
+	}
+	return p, nil
+}
diff --git a/frontend/src/components/log/task/index.vue b/frontend/src/components/log/task/index.vue
index ceb3368bc545..4137e6f67cd1 100644
--- a/frontend/src/components/log/task/index.vue
+++ b/frontend/src/components/log/task/index.vue
@@ -73,6 +73,7 @@ const handleClose = () => {
     em('close', true);
     open.value = false;
     bus.emit('refreshTask', true);
+    bus.emit('refreshApp', true);
 };
 
 defineExpose({ openWithResourceID, openWithTaskID });
diff --git a/frontend/src/views/app-store/apps/index.vue b/frontend/src/views/app-store/apps/index.vue
index 1d38c2ecb771..db62d9dc6f77 100644
--- a/frontend/src/views/app-store/apps/index.vue
+++ b/frontend/src/views/app-store/apps/index.vue
@@ -282,7 +282,7 @@ const searchByName = () => {
 };
 
 onMounted(async () => {
-    bus.on('refreshTask', () => {
+    bus.on('refreshApp', () => {
         search(req);
     });
     if (router.currentRoute.value.query.install) {


Reviews:
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T07:09:07Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T07:09:17Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T07:09:22Z
user: login: wanghe-fit2cloud
body: /lgtm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-22T07:16:39Z

Review comments:
diff_hunk: @@ -0,0 +1,223 @@
+package ssl
+
+import (
+	"encoding/json"
+	"github.com/go-acme/lego/v4/challenge"
+	"github.com/go-acme/lego/v4/providers/dns/alidns"
+	"github.com/go-acme/lego/v4/providers/dns/clouddns"
+	"github.com/go-acme/lego/v4/providers/dns/cloudflare"
+	"github.com/go-acme/lego/v4/providers/dns/cloudns"
+	"github.com/go-acme/lego/v4/providers/dns/dnspod"
+	"github.com/go-acme/lego/v4/providers/dns/freemyip"
+	"github.com/go-acme/lego/v4/providers/dns/godaddy"
+	"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
+	"github.com/go-acme/lego/v4/providers/dns/namecheap"
+	"github.com/go-acme/lego/v4/providers/dns/namedotcom"
+	"github.com/go-acme/lego/v4/providers/dns/namesilo"
+	"github.com/go-acme/lego/v4/providers/dns/rainyun"
+	"github.com/go-acme/lego/v4/providers/dns/spaceship"
+	"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
+	"github.com/go-acme/lego/v4/providers/dns/vercel"
+	"github.com/go-acme/lego/v4/providers/dns/volcengine"
+	"github.com/go-acme/lego/v4/providers/dns/westcn"
+	"time"
+)
+
+type DnsType string
+
+const (
+	DnsPod       DnsType = "DnsPod"
+	AliYun       DnsType = "AliYun"
+	CloudFlare   DnsType = "CloudFlare"
+	CloudDns     DnsType = "CloudDns"
+	NameSilo     DnsType = "NameSilo"
+	NameCheap    DnsType = "NameCheap"
+	NameCom      DnsType = "NameCom"
+	Godaddy      DnsType = "Godaddy"
+	TencentCloud DnsType = "TencentCloud"
+	RainYun      DnsType = "RainYun"
+	Volcengine   DnsType = "Volcengine"
+	HuaweiCloud  DnsType = "HuaweiCloud"
+	FreeMyIP     DnsType = "FreeMyIP"
+	Vercel       DnsType = "Vercel"
+	Spaceship    DnsType = "Spaceship"
+	WestCN       DnsType = "WestCN"
+	ClouDNS      DnsType = "ClouDNS"
+)
+
+type DNSParam struct {
+	ID           string `json:"id"`
+	Token        string `json:"token"`
+	AccessKey    string `json:"accessKey"`
+	SecretKey    string `json:"secretKey"`
+	Email        string `json:"email"`
+	APIkey       string `json:"apiKey"`
+	APIUser      string `json:"apiUser"`
+	APISecret    string `json:"apiSecret"`
+	SecretID     string `json:"secretID"`
+	ClientID     string `json:"clientID"`
+	Password     string `json:"password"`
+	Region       string `json:"region"`
+	Username     string `json:"username"`
+	AuthID       string `json:"authID"`
+	SubAuthID    string `json:"subAuthID"`
+	AuthPassword string `json:"authPassword"`
+}
+
+var (
+	propagationTimeout = 30 * time.Minute
+	pollingInterval    = 10 * time.Second
+	ttl                = 3600
+	dnsTimeOut         = 30 * time.Minute
+	manualDnsTimeout   = 10 * time.Minute
+)
+
+func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, error) {
+	var (
+		param DNSParam
+		p     challenge.Provider
+		err   error
+	)
+	if err := json.Unmarshal([]byte(params), &param); err != nil {
+		return nil, err
+	}
+	switch dnsType {
+	case DnsPod:
+		dnsPodConfig := dnspod.NewDefaultConfig()
+		dnsPodConfig.LoginToken = param.ID + "," + param.Token
+		dnsPodConfig.PropagationTimeout = propagationTimeout
+		dnsPodConfig.PollingInterval = pollingInterval
+		dnsPodConfig.TTL = ttl
+		p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
+	case AliYun:
+		alidnsConfig := alidns.NewDefaultConfig()
+		alidnsConfig.SecretKey = param.SecretKey
+		alidnsConfig.APIKey = param.AccessKey
+		alidnsConfig.PropagationTimeout = propagationTimeout
+		alidnsConfig.PollingInterval = pollingInterval
+		alidnsConfig.TTL = ttl
+		p, err = alidns.NewDNSProviderConfig(alidnsConfig)
+	case CloudFlare:
+		cloudflareConfig := cloudflare.NewDefaultConfig()
+		cloudflareConfig.AuthEmail = param.Email
+		cloudflareConfig.AuthToken = param.APIkey
+		cloudflareConfig.PropagationTimeout = propagationTimeout
+		cloudflareConfig.PollingInterval = pollingInterval
+		cloudflareConfig.TTL = ttl
+		p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
+	case CloudDns:
+		clouddnsConfig := clouddns.NewDefaultConfig()
+		clouddnsConfig.ClientID = param.ClientID
+		clouddnsConfig.Email = param.Email
+		clouddnsConfig.Password = param.Password
+		clouddnsConfig.PropagationTimeout = propagationTimeout
+		clouddnsConfig.PollingInterval = pollingInterval
+		clouddnsConfig.TTL = ttl
+		p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
+	case NameCheap:
+		namecheapConfig := namecheap.NewDefaultConfig()
+		namecheapConfig.APIKey = param.APIkey
+		namecheapConfig.APIUser = param.APIUser
+		namecheapConfig.PropagationTimeout = propagationTimeout
+		namecheapConfig.PollingInterval = pollingInterval
+		namecheapConfig.TTL = ttl
+		p, err = namecheap.NewDNSProviderConfig(namecheapConfig)
+	case NameSilo:
+		nameSiloConfig := namesilo.NewDefaultConfig()
+		nameSiloConfig.APIKey = param.APIkey
+		nameSiloConfig.PropagationTimeout = propagationTimeout
+		nameSiloConfig.PollingInterval = pollingInterval
+		nameSiloConfig.TTL = ttl
+		p, err = namesilo.NewDNSProviderConfig(nameSiloConfig)
+	case Godaddy:
+		godaddyConfig := godaddy.NewDefaultConfig()
+		godaddyConfig.APIKey = param.APIkey
+		godaddyConfig.APISecret = param.APISecret
+		godaddyConfig.PropagationTimeout = propagationTimeout
+		godaddyConfig.PollingInterval = pollingInterval
+		godaddyConfig.TTL = ttl
+		p, err = godaddy.NewDNSProviderConfig(godaddyConfig)
+	case NameCom:
+		nameComConfig := namedotcom.NewDefaultConfig()
+		nameComConfig.APIToken = param.Token
+		nameComConfig.Username = param.APIUser
+		nameComConfig.PropagationTimeout = propagationTimeout
+		nameComConfig.PollingInterval = pollingInterval
+		nameComConfig.TTL = ttl
+		p, err = namedotcom.NewDNSProviderConfig(nameComConfig)
+	case TencentCloud:
+		tencentCloudConfig := tencentcloud.NewDefaultConfig()
+		tencentCloudConfig.SecretID = param.SecretID
+		tencentCloudConfig.SecretKey = param.SecretKey
+		tencentCloudConfig.PropagationTimeout = propagationTimeout
+		tencentCloudConfig.PollingInterval = pollingInterval
+		tencentCloudConfig.TTL = ttl
+		p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig)
+	case RainYun:
+		rainyunConfig := rainyun.NewDefaultConfig()
+		rainyunConfig.APIKey = param.APIkey
+		rainyunConfig.PropagationTimeout = propagationTimeout
+		rainyunConfig.PollingInterval = pollingInterval
+		rainyunConfig.TTL = ttl
+		p, err = rainyun.NewDNSProviderConfig(rainyunConfig)
+	case Volcengine:
+		volcConfig := volcengine.NewDefaultConfig()
+		volcConfig.SecretKey = param.SecretKey
+		volcConfig.AccessKey = param.AccessKey
+		volcConfig.PropagationTimeout = propagationTimeout
+		volcConfig.PollingInterval = pollingInterval
+		volcConfig.TTL = ttl
+		p, err = volcengine.NewDNSProviderConfig(volcConfig)
+	case HuaweiCloud:
+		huaweiCloudConfig := huaweicloud.NewDefaultConfig()
+		huaweiCloudConfig.AccessKeyID = param.AccessKey
+		huaweiCloudConfig.SecretAccessKey = param.SecretKey
+		huaweiCloudConfig.Region = param.Region
+		huaweiCloudConfig.PropagationTimeout = propagationTimeout
+		huaweiCloudConfig.PollingInterval = pollingInterval
+		huaweiCloudConfig.TTL = int32(ttl)
+		p, err = huaweicloud.NewDNSProviderConfig(huaweiCloudConfig)
+	case FreeMyIP:
+		freeMyIpConfig := freemyip.NewDefaultConfig()
+		freeMyIpConfig.Token = param.Token
+		freeMyIpConfig.PropagationTimeout = propagationTimeout
+		freeMyIpConfig.PollingInterval = pollingInterval
+		p, err = freemyip.NewDNSProviderConfig(freeMyIpConfig)
+	case Vercel:
+		vercelConfig := vercel.NewDefaultConfig()
+		vercelConfig.AuthToken = param.Token
+		vercelConfig.PropagationTimeout = propagationTimeout
+		vercelConfig.PollingInterval = pollingInterval
+		p, err = vercel.NewDNSProviderConfig(vercelConfig)
+	case Spaceship:
+		spaceshipConfig := spaceship.NewDefaultConfig()
+		spaceshipConfig.APIKey = param.APIkey
+		spaceshipConfig.APISecret = param.APISecret
+		spaceshipConfig.PropagationTimeout = propagationTimeout
+		spaceshipConfig.PollingInterval = pollingInterval
+		spaceshipConfig.TTL = ttl
+		p, err = spaceship.NewDNSProviderConfig(spaceshipConfig)
+	case WestCN:
+		westcnConfig := westcn.NewDefaultConfig()
+		westcnConfig.Username = param.Username
+		westcnConfig.Password = param.Password
+		westcnConfig.PropagationTimeout = propagationTimeout
+		westcnConfig.PollingInterval = pollingInterval
+		westcnConfig.TTL = ttl
+		p, err = westcn.NewDNSProviderConfig(westcnConfig)
+
+	case ClouDNS:
+		cloudnsConfig := cloudns.NewDefaultConfig()
+		cloudnsConfig.AuthID = param.AuthID
+		cloudnsConfig.SubAuthID = param.SubAuthID
+		cloudnsConfig.AuthPassword = param.AuthPassword
+		cloudnsConfig.PropagationTimeout = propagationTimeout
+		cloudnsConfig.PollingInterval = pollingInterval
+		cloudnsConfig.TTL = ttl
+		p, err = cloudns.NewDNSProviderConfig(cloudnsConfig)
+	}
+	if err != nil {
+		return nil, err
+	}
+	return p, nil
+}
path: agent/utils/ssl/dns_provider.go
user: login: wanghe-fit2cloud
body: ### Code Differences and Issues:

There are several concerns and issues with the provided Go code snippet:

1. **Imports**:
   - The imports section includes multiple package groups (`alidns`, `clouddns`, etc.). This may cause import conflicts if only a few of these packages are used in this specific file.

2. **Constants**:
   - There is no clear reason to define constants like `propagationTimeout`, `pollingInterval`, and `ttl` at the top level. If they're intended for reuse across functions, consider defining them inside local variables within each function that uses them.

3. **Error Handling**:
   - In the main loop where DNS provider configurations are being set up, there's no error handling when initializing providers after unmarshaling parameters. It would be better to handle any errors during configuration initialization explicitly.

4. **Functionality**:
   - Using separate constants (like `manualDnsTimeout`) for timeout settings doesn't seem necessary given how these timeouts relate to others (e.g., `dnsTimeOut`). Consider consolidating these into a single variable structure where appropriate.

5. **Code Duplication**:
   - Some parts of the conditional statements are repeated for different DNS types. Extract common logic to reusable functions or methods to reduce redundancy.

6. **Security Practice**:
   - Hardcoding sensitive information such as API keys and tokens in clear text (especially passwords) can pose security risks. Use environment variables or secure vaults instead.

7. **Documentation**:
   - Lack of comments describing responsibilities and business logic makes it challenging for other developers to understand and maintain the codebase.

8. **Variable Naming**:
   - Variable names like `param` might not be descriptive enough. Refactor to use more meaningful identifiers.

### Optimization Suggestions:

- **Use Environment Variables**: Read credentials from environment variables rather than hardcoding them.
  ```go
  apiKey := os.Getenv("API_KEY")
  // Continue setting up config with apiKey...
  ```

- **Consolidate Timeout Values**: Group all timeout values under a single type or interface to manage centrally.
  ```go
  type ConfigTimeout struct {
    PropagationTimeout time.Duration
    PollingInterval     time.Duration
    TTL                 int
  }

  func newConfigTimeout() ConfigTimeout {
    return ConfigTimeout{
      propagationTimeout,
      pollingInterval,
      ttl,
    }
  }
  ```
  
- **Extract Functions**: Separate setup logic into smaller functions for clarity and maintainability.
  ```go
  func createDNSProviderFromParams(config *ConfigTimeout, id string, token string) challenge.Provider {
    var (
      param DNSParam
      p     challenge.Provider
      err   error
    )
    
    // Unmarshal or validate params here
    
    switch id {
    case DnsPod:
      dnsPodConfig := dnspod.NewDefaultConfig()
      dnsPodConfig.LoginToken = param.ID + "," + token
      dnsPodConfig.ConfigTimeout = config
      p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
    case ... // similarly create structs for other IDs
    default:
      log.Fatalf("Unsupported DNS Provider ID: %v", id)
    }
    
    if err != nil {
      log.Fatalf("Failed to initialize DNS provider: %v", err)
    }
    
    return p
  }
  ```

### Conclusion:

While the initial intent seems solid, various improvements are needed, including refactoring, adding error checks, and best practices for security and readability. These changes will enhance the robustness and maintainability of the code base.
created_at: 2025-05-22T07:09:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,33 @@
+package ssl
+
+import "time"
+
+type ManualConfig struct {
+	TTL                int
+	PropagationTimeout time.Duration
+	PollingInterval    time.Duration
+}
+
+type CustomManualDnsProvider struct {
+	config *ManualConfig
+}
+
+func NewCustomDNSProviderManual(config *ManualConfig) (*CustomManualDnsProvider, error) {
+	return &CustomManualDnsProvider{config}, nil
+}
+
+func (p *CustomManualDnsProvider) Present(domain, token, keyAuth string) error {
+	return nil
+}
+
+func (p *CustomManualDnsProvider) CleanUp(domain, token, keyAuth string) error {
+	return nil
+}
+
+func (p *CustomManualDnsProvider) Sequential() time.Duration {
+	return manualDnsTimeout
+}
+
+func (p *CustomManualDnsProvider) Timeout() (timeout, interval time.Duration) {
+	return p.config.PropagationTimeout, p.config.PollingInterval
+}
path: agent/utils/ssl/custom_manual_provider.go
user: login: wanghe-fit2cloud
body: The provided code for a custom DNS provider using SSL/TLS settings has few minor issues that can be addressed:

1. **Variable Initialization**: The variable `manualDnsTimeout` is used in the `Sequential()` method but not defined anywhere else in the file. This needs to be initialized properly or removed if it's intended to be set elsewhere.

2. **Unused Import**: The import statement `import "sync"` is present at the top of the file, but there are no usage of `sync` components inside the package. This could potentially indicate an unused dependency or typo.

3. **Error Handling**: The `NewCustomDNSProviderManual` function returns only one error value (`error`). If the actual implementation were more complex, additional error handling could have been introduced around the creation of the `CustomManualDnsProvider` instance.

4. **Documentation**: There is no docstring describing the purposes of these functions or types, which can help anyone reading the code understand its functionality better.

Overall, the code is functional as-is but with slight improvements needed for clarity and robustness. The main areas to address would involve fixing any undefined variables and removing unnecessary imports.
created_at: 2025-05-22T07:09:17Z
author_association: MEMBER
diff_hunk: @@ -46,6 +46,23 @@ const (
 	KeyRSA4096 = certcrypto.RSA4096
 )
 
+type AcmeUser struct {
+	Email        string
+	Registration *registration.Resource
+	Key          crypto.PrivateKey
+}
+
+func (u *AcmeUser) GetEmail() string {
+	return u.Email
+}
+
+func (u *AcmeUser) GetRegistration() *registration.Resource {
+	return u.Registration
+}
+func (u *AcmeUser) GetPrivateKey() crypto.PrivateKey {
+	return u.Key
+}
+
 func GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) {
 	var (
 		marshal []byte
path: agent/utils/ssl/acme.go
user: login: wanghe-fit2cloud
body: No apparent differences were found in the provided code snippet that indicate any irregularities, potential issues, or optimization suggestions at this time. The additions of the `AcmeUser` struct and related methods do not appear to have introduced errors unless there are additional constraints on their usage elsewhere in the system. However, without further context about how these changes are being used and what specific performance or functionality benefits they might bring, I cannot make more informed evaluations.
created_at: 2025-05-22T07:09:22Z
author_association: MEMBER

PR comments:
user: login: f2c-ci-robot[bot]
created_at: 2025-05-22T07:08:35Z
body: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-05-22T07:10:07Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=1Panel-dev_1Panel&pullRequest=8782) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=1Panel-dev_1Panel&pullRequest=8782&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=1Panel-dev_1Panel&pullRequest=8782&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=1Panel-dev_1Panel&pullRequest=8782&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=1Panel-dev_1Panel&pullRequest=8782&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=1Panel-dev_1Panel&pullRequest=8782&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=1Panel-dev_1Panel&pullRequest=8782)


author_association: NONE
user: login: wanghe-fit2cloud
created_at: 2025-05-22T07:16:47Z
body: /approve
author_association: MEMBER
user: login: f2c-ci-robot[bot]
created_at: 2025-05-22T07:16:54Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/1Panel-dev/1Panel/pull/8782#issuecomment-2900170040" title="Approved">wanghe-fit2cloud</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=1Panel-dev%2F1Panel).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/1Panel-dev/1Panel/blob/dev-v2/OWNERS)~~ [wanghe-fit2cloud]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE