Scert/backend/internal/httpapi/settings_api.go
xuans b8fd536dbd 权限整改:完成 DNS 账户项目归属与凭据使用隔离
为 DNS 配置和账户补充项目归属,接入 DNS 凭据使用与项目管理权限校验,并补充配置和接口回归测试。
2026-09-29 19:21:38 +08:00

554 lines
23 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package httpapi
import (
"context"
"errors"
"net/http"
"reflect"
"strings"
"time"
"github.com/gin-gonic/gin"
"git.pcux.cn/xuans/Scert/backend/internal/certificate"
"git.pcux.cn/xuans/Scert/backend/internal/config"
"git.pcux.cn/xuans/Scert/backend/internal/domain"
"git.pcux.cn/xuans/Scert/backend/internal/repository"
)
type settingsView struct {
Server settingsServerView `json:"server"`
Web settingsWebView `json:"web"`
Paths settingsPathsView `json:"paths"`
ACME settingsACMEView `json:"acme"`
DNS settingsDNSView `json:"dns"`
Notification settingsNotificationView `json:"notification"`
}
type settingsServerView struct {
ProxyHTTPSPort int `json:"proxy_https_port"`
Host string `json:"host"`
Port int `json:"port"`
HTTPSMode string `json:"https_mode"`
}
type settingsWebView struct {
Enabled bool `json:"enabled"`
Hostname string `json:"hostname"`
PublicIP string `json:"public_ip"`
}
type settingsPathsView struct {
Certificate string `json:"certificate"`
}
type settingsACMEView struct {
CA string `json:"ca"`
Environment string `json:"environment"`
AccountEmail string `json:"account_email"`
RenewalCheckIntervalDays int `json:"renewal_check_interval_days"`
RenewalNearExpiryDays int `json:"renewal_near_expiry_days"`
RenewalThresholdDays int `json:"renewal_threshold_days"`
RenewalUrgentThresholdDays int `json:"renewal_urgent_threshold_days"`
RenewalMaxRetries int `json:"renewal_max_retries"`
RenewalRetryIntervalMins int `json:"renewal_retry_interval_mins"`
PropagationTimeoutSecs int `json:"propagation_timeout_secs"`
PropagationPollingIntervalSecs int `json:"propagation_polling_interval_secs"`
}
type settingsDNSView struct {
ProjectID string `json:"project_id"`
Provider string `json:"provider"`
VerificationState string `json:"verification_state"`
CredentialConfigured bool `json:"credential_configured"`
}
type settingsNotificationView struct {
HTTPEnabled bool `json:"http_enabled"`
SMTPEnabled bool `json:"smtp_enabled"`
SMTPHost string `json:"smtp_host"`
SMTPPort int `json:"smtp_port"`
SMTPUsername string `json:"smtp_username"`
SMTPMode string `json:"smtp_mode"`
SMTPAllowPlaintextAuth bool `json:"smtp_allow_plaintext_auth"`
SMTPCredentialConfigured bool `json:"smtp_credential_configured"`
Provider string `json:"provider"`
Endpoint string `json:"endpoint"`
HTTPPath string `json:"http_path"`
HTTPLoginPath string `json:"http_login_path"`
HTTPAuthMode string `json:"http_auth_mode"`
HTTPCredentialConfigured bool `json:"http_credential_configured"`
EventMode string `json:"event_mode"`
IncludeDeploymentDetails bool `json:"include_deployment_details"`
IncludeCertificateSerial bool `json:"include_certificate_serial"`
IncludeManagementURL bool `json:"include_management_url"`
SenderName string `json:"sender_name"`
SenderEmail string `json:"sender_email"`
Recipients []string `json:"recipients"`
TimeoutSecs int `json:"timeout_secs"`
RetryCount int `json:"retry_count"`
RetryIntervalsSecs []int `json:"retry_intervals_secs"`
}
type settingsRequest struct {
Server *settingsServerRequest `json:"server"`
Web *settingsWebRequest `json:"web"`
Paths *settingsPathsRequest `json:"paths"`
ACME *settingsACMERequest `json:"acme"`
DNS *settingsDNSRequest `json:"dns"`
Notification *settingsNotificationRequest `json:"notification"`
}
type settingsServerRequest struct {
ProxyHTTPSPort *int `json:"proxy_https_port"`
Host *string `json:"host"`
Port *int `json:"port"`
HTTPSMode *string `json:"https_mode"`
}
type settingsWebRequest struct {
Enabled *bool `json:"enabled"`
Hostname *string `json:"hostname"`
PublicIP *string `json:"public_ip"`
}
type settingsPathsRequest struct {
Certificate *string `json:"certificate"`
}
type settingsACMERequest struct {
CA *string `json:"ca"`
Environment *string `json:"environment"`
AccountEmail *string `json:"account_email"`
RenewalCheckIntervalDays *int `json:"renewal_check_interval_days"`
RenewalNearExpiryDays *int `json:"renewal_near_expiry_days"`
RenewalThresholdDays *int `json:"renewal_threshold_days"`
RenewalUrgentThresholdDays *int `json:"renewal_urgent_threshold_days"`
RenewalMaxRetries *int `json:"renewal_max_retries"`
RenewalRetryIntervalMins *int `json:"renewal_retry_interval_mins"`
PropagationTimeoutSecs *int `json:"propagation_timeout_secs"`
PropagationPollingIntervalSecs *int `json:"propagation_polling_interval_secs"`
}
type settingsDNSRequest struct {
ProjectID *string `json:"project_id"`
Provider *string `json:"provider"`
CredentialRef *string `json:"credential_ref"`
VerificationToken *string `json:"verification_token"`
TestDomain *string `json:"test_domain"`
}
type settingsNotificationRequest struct {
HTTPEnabled *bool `json:"http_enabled"`
SMTPEnabled *bool `json:"smtp_enabled"`
SMTPHost *string `json:"smtp_host"`
SMTPPort *int `json:"smtp_port"`
SMTPUsername *string `json:"smtp_username"`
SMTPPassword *string `json:"smtp_password"`
SMTPMode *string `json:"smtp_mode"`
SMTPAllowPlaintextAuth *bool `json:"smtp_allow_plaintext_auth"`
HTTPCredentials map[string]string `json:"http_credentials"`
Provider *string `json:"provider"`
Endpoint *string `json:"endpoint"`
HTTPPath *string `json:"http_path"`
HTTPLoginPath *string `json:"http_login_path"`
HTTPAuthMode *string `json:"http_auth_mode"`
EventMode *string `json:"event_mode"`
IncludeDeploymentDetails *bool `json:"include_deployment_details"`
IncludeCertificateSerial *bool `json:"include_certificate_serial"`
IncludeManagementURL *bool `json:"include_management_url"`
SenderName *string `json:"sender_name"`
SenderEmail *string `json:"sender_email"`
Recipients *[]string `json:"recipients"`
TimeoutSecs *int `json:"timeout_secs"`
RetryCount *int `json:"retry_count"`
RetryIntervalsSecs *[]int `json:"retry_intervals_secs"`
}
func (s *Server) getSettings(c *gin.Context) {
s.configMu.RLock()
cfg := s.config
s.configMu.RUnlock()
view := settingsViewFromConfig(cfg)
c.JSON(http.StatusOK, view)
}
func (s *Server) updateSettings(c *gin.Context) {
session, authenticated := s.currentAuthSession(c)
if !authenticated {
s.writeError(c, http.StatusUnauthorized, "请先登录管理后台", nil)
return
}
if strings.TrimSpace(s.configPath) == "" {
s.writeError(c, http.StatusInternalServerError, "配置文件路径未配置", errors.New("配置文件路径为空"))
return
}
var request settingsRequest
if err := bindJSON(c, &request, jsonRequestBodyLimit); err != nil {
s.writeError(c, http.StatusBadRequest, "设置请求格式不正确", err)
return
}
if !s.tlsMu.TryLock() {
s.writeError(c, http.StatusConflict, "管理证书或配置正在更新,请稍后重试", nil)
return
}
defer s.tlsMu.Unlock()
s.configMu.Lock()
previous := s.config
updated := s.config
applySettingsRequest(&updated, request)
updated.Normalize()
if request.DNS != nil && request.DNS.ProjectID != nil && updated.DNS.ProjectID != previous.DNS.ProjectID {
if !s.authorizeDNSProjectAssignment(c, updated.DNS.ProjectID) {
s.configMu.Unlock()
return
}
}
createdCredentialRefs, err := s.saveSettingsNotificationCredentials(c.Request.Context(), &updated, request)
if err != nil {
s.rollbackSetupCredentialsWithLog(createdCredentialRefs)
s.configMu.Unlock()
s.writeError(c, http.StatusBadRequest, "保存通知凭据失败", err)
return
}
dnsCredentialRef, dnsVerificationToken, dnsTestDomain, err := s.applySettingsDNS(c.Request.Context(), &updated, previous, request.DNS)
if err != nil {
s.rollbackSetupCredentialsWithLog(createdCredentialRefs)
if dnsCredentialRef != "" && dnsCredentialRef != previous.DNS.CredentialRef {
s.rollbackSetupCredentialsWithLog([]string{dnsCredentialRef})
}
s.configMu.Unlock()
s.writeError(c, http.StatusBadRequest, "保存 DNS 设置失败", err)
return
}
if dnsCredentialRef != "" && dnsCredentialRef != previous.DNS.CredentialRef {
createdCredentialRefs = append(createdCredentialRefs, dnsCredentialRef)
}
// 只有实际更换凭据才记录新归属,修改其他设置不能认领历史账号。
if updated.DNS.CredentialRef == "" {
updated.DNS.OwnerUserID = ""
} else if updated.DNS.CredentialRef != previous.DNS.CredentialRef {
updated.DNS.OwnerUserID = session.UserID
}
if err := updated.Validate(); err != nil {
s.rollbackSetupCredentialsWithLog(createdCredentialRefs)
s.configMu.Unlock()
s.writeError(c, http.StatusBadRequest, "设置内容校验失败", err)
return
}
if err := updated.Save(s.configPath); err != nil {
s.rollbackSetupCredentialsWithLog(createdCredentialRefs)
s.configMu.Unlock()
s.writeError(c, http.StatusInternalServerError, "保存设置失败", err)
return
}
listenerRestartRequired := previous.Server.Host != updated.Server.Host ||
previous.Server.Port != updated.Server.Port ||
previous.Server.HTTPSMode != updated.Server.HTTPSMode
configRestartRequired := listenerRestartRequired ||
previous.Web.Enabled != updated.Web.Enabled ||
previous.Paths.Certificate != updated.Paths.Certificate ||
!reflect.DeepEqual(nonRenewalACMEConfig(previous.ACME), nonRenewalACMEConfig(updated.ACME)) ||
!reflect.DeepEqual(previous.DNS, updated.DNS) ||
!reflect.DeepEqual(previous.Notification, updated.Notification)
if s.updateRenewalPolicy != nil {
s.updateRenewalPolicy(renewalPolicyFromConfig(updated))
}
message := "设置已保存"
if listenerRestartRequired {
message = "设置已保存;服务监听地址、端口或 HTTPS 模式已变更,请重启 Scert 服务"
} else if configRestartRequired {
message = "设置已保存;Web 页面、证书、DNS 或通知运行配置已变更,请重启 Scert 服务"
}
s.config = updated
s.configMu.Unlock()
oldCredentialRefs := []string{
previous.DNS.CredentialRef,
previous.Notification.HTTPCredentialRef,
previous.Notification.SMTPCredentialRef,
}
newCredentialRefs := map[string]struct{}{
updated.DNS.CredentialRef: {},
updated.Notification.HTTPCredentialRef: {},
updated.Notification.SMTPCredentialRef: {},
}
var obsoleteCredentialRefs []string
for _, reference := range oldCredentialRefs {
reference = strings.TrimSpace(reference)
if reference == "" {
continue
}
if _, stillUsed := newCredentialRefs[reference]; !stillUsed {
obsoleteCredentialRefs = append(obsoleteCredentialRefs, reference)
}
}
if len(obsoleteCredentialRefs) > 0 {
if err := s.rollbackSetupCredentials(obsoleteCredentialRefs); err != nil && s.logger != nil {
s.logger.Warn("\u6e05\u7406\u5df2\u66ff\u6362\u7684\u654f\u611f\u51ed\u636e\u5931\u8d25", "error", err)
}
}
if dnsVerificationToken != "" && s.setupDNS != nil {
if err := s.setupDNS.Consume(dnsVerificationToken, updated.DNS.Provider, dnsCredentialRef, dnsTestDomain); err != nil && s.logger != nil {
s.logger.Warn("系统设置已保存,但清理 DNS 验证令牌失败", "error", err)
}
}
c.JSON(http.StatusOK, gin.H{
"settings": settingsViewFromConfig(updated),
"restart_required": configRestartRequired,
"message": message,
})
}
func (s *Server) authorizeDNSProjectAssignment(c *gin.Context, projectID string) bool {
authorizer, ok := s.projectMembers.(repository.ProjectAuthorizer)
if !ok {
authorizer, ok = s.projectDomains.(repository.ProjectAuthorizer)
}
if !ok {
s.writeError(c, http.StatusServiceUnavailable, "项目 DNS 权限服务尚未就绪", nil)
return false
}
if err := authorizer.AuthorizeProject(c.Request.Context(), c.GetString(authenticatedActorKey), projectID, domain.PermissionDNSManage); err != nil {
if errors.Is(err, repository.ErrProjectAccessDenied) {
s.writeError(c, http.StatusForbidden, "无权管理此项目的 DNS 账户", nil)
return false
}
s.writeError(c, http.StatusInternalServerError, "检查项目 DNS 管理权限失败", err)
return false
}
return true
}
func (s *Server) applySettingsDNS(ctx context.Context, cfg *config.Config, previous config.Config, request *settingsDNSRequest) (string, string, string, error) {
if cfg == nil || request == nil {
return "", "", "", nil
}
providerChanged := cfg.DNS.Provider != strings.TrimSpace(previous.DNS.Provider)
hasVerificationInput := request.CredentialRef != nil || request.VerificationToken != nil || request.TestDomain != nil
if !hasVerificationInput {
if providerChanged {
cfg.DNS.CredentialRef = ""
cfg.DNS.VerificationState = "pending"
}
return "", "", "", nil
}
if s.setupDNS == nil {
return "", "", "", errors.New("DNS 在线验证服务尚未初始化")
}
if request.CredentialRef == nil || request.VerificationToken == nil || request.TestDomain == nil {
return "", "", "", errors.New("DNS 验证参数不完整,请先完成在线测试")
}
credentialRef := strings.TrimSpace(*request.CredentialRef)
verificationToken := strings.TrimSpace(*request.VerificationToken)
testDomain := strings.TrimSpace(*request.TestDomain)
if credentialRef == "" || verificationToken == "" || testDomain == "" {
return credentialRef, verificationToken, testDomain, errors.New("DNS 验证参数不完整,请先完成在线测试")
}
if err := s.setupDNS.Check(verificationToken, cfg.DNS.Provider, credentialRef, testDomain); err != nil {
return credentialRef, verificationToken, testDomain, err
}
cfg.DNS.CredentialRef = credentialRef
cfg.DNS.VerificationState = "verified"
return credentialRef, verificationToken, testDomain, nil
}
func renewalPolicyFromConfig(cfg config.Config) certificate.RenewalPolicy {
return certificate.RenewalPolicy{
CheckInterval: time.Duration(cfg.ACME.RenewalCheckIntervalDays) * 24 * time.Hour,
NearExpiryThreshold: time.Duration(cfg.ACME.RenewalNearExpiryDays) * 24 * time.Hour,
RenewalThreshold: time.Duration(cfg.ACME.RenewalThresholdDays) * 24 * time.Hour,
UrgentThreshold: time.Duration(cfg.ACME.RenewalUrgentThresholdDays) * 24 * time.Hour,
MaxRetries: cfg.ACME.RenewalMaxRetries,
RetryInterval: time.Duration(cfg.ACME.RenewalRetryIntervalMins) * time.Minute,
}
}
func nonRenewalACMEConfig(cfg config.ACMEConfig) config.ACMEConfig {
cfg.RenewalCheckIntervalDays = 0
cfg.RenewalNearExpiryDays = 0
cfg.RenewalThresholdDays = 0
cfg.RenewalUrgentThresholdDays = 0
cfg.RenewalMaxRetries = 0
cfg.RenewalRetryIntervalMins = 0
return cfg
}
func settingsViewFromConfig(cfg config.Config) settingsView {
return settingsView{
Server: settingsServerView{Host: cfg.Server.Host, Port: cfg.Server.Port, HTTPSMode: cfg.Server.HTTPSMode, ProxyHTTPSPort: cfg.Server.ProxyHTTPSPort},
Web: settingsWebView{Enabled: cfg.Web.Enabled, Hostname: cfg.Web.Hostname, PublicIP: cfg.Web.PublicIP},
Paths: settingsPathsView{Certificate: cfg.Paths.Certificate},
ACME: settingsACMEView{
CA: cfg.ACME.CA, Environment: cfg.ACME.Environment, AccountEmail: cfg.ACME.AccountEmail,
RenewalCheckIntervalDays: cfg.ACME.RenewalCheckIntervalDays, RenewalNearExpiryDays: cfg.ACME.RenewalNearExpiryDays,
RenewalThresholdDays: cfg.ACME.RenewalThresholdDays, RenewalUrgentThresholdDays: cfg.ACME.RenewalUrgentThresholdDays,
RenewalMaxRetries: cfg.ACME.RenewalMaxRetries, RenewalRetryIntervalMins: cfg.ACME.RenewalRetryIntervalMins,
PropagationTimeoutSecs: cfg.ACME.PropagationTimeoutSecs, PropagationPollingIntervalSecs: cfg.ACME.PropagationPollingIntervalSecs,
},
DNS: settingsDNSView{
ProjectID: cfg.DNS.ProjectID,
Provider: cfg.DNS.Provider, VerificationState: cfg.DNS.VerificationState,
CredentialConfigured: strings.TrimSpace(cfg.DNS.CredentialRef) != "",
},
Notification: settingsNotificationView{
HTTPEnabled: cfg.Notification.HTTPEnabled, SMTPEnabled: cfg.Notification.SMTPEnabled, Provider: cfg.Notification.Provider,
SMTPHost: cfg.Notification.SMTPHost, SMTPPort: cfg.Notification.SMTPPort, SMTPUsername: cfg.Notification.SMTPUsername,
SMTPMode: cfg.Notification.SMTPMode, SMTPAllowPlaintextAuth: cfg.Notification.SMTPAllowPlaintextAuth,
SMTPCredentialConfigured: strings.TrimSpace(cfg.Notification.SMTPCredentialRef) != "",
Endpoint: cfg.Notification.Endpoint, HTTPPath: cfg.Notification.HTTPPath, HTTPLoginPath: cfg.Notification.HTTPLoginPath,
HTTPAuthMode: cfg.Notification.HTTPAuthMode, HTTPCredentialConfigured: strings.TrimSpace(cfg.Notification.HTTPCredentialRef) != "",
EventMode: cfg.Notification.EventMode, IncludeDeploymentDetails: cfg.Notification.IncludeDeploymentDetails,
IncludeCertificateSerial: cfg.Notification.IncludeCertificateSerial, IncludeManagementURL: cfg.Notification.IncludeManagementURL,
SenderName: cfg.Notification.SenderName, SenderEmail: cfg.Notification.SenderEmail,
Recipients: append([]string(nil), cfg.Notification.Recipients...), TimeoutSecs: cfg.Notification.TimeoutSecs,
RetryCount: cfg.Notification.RetryCount, RetryIntervalsSecs: append([]int(nil), cfg.Notification.RetryIntervalsSecs...),
},
}
}
func applySettingsRequest(cfg *config.Config, request settingsRequest) {
if cfg == nil {
return
}
if request.Server != nil {
if request.Server.Host != nil {
cfg.Server.Host = strings.TrimSpace(*request.Server.Host)
}
if request.Server.Port != nil {
cfg.Server.Port = *request.Server.Port
}
if request.Server.HTTPSMode != nil {
cfg.Server.HTTPSMode = strings.TrimSpace(*request.Server.HTTPSMode)
}
if request.Server.ProxyHTTPSPort != nil {
cfg.Server.ProxyHTTPSPort = *request.Server.ProxyHTTPSPort
}
}
if request.Web != nil {
if request.Web.Enabled != nil {
cfg.Web.Enabled = *request.Web.Enabled
}
if request.Web.Hostname != nil {
cfg.Web.Hostname = strings.TrimSpace(*request.Web.Hostname)
}
if request.Web.PublicIP != nil {
cfg.Web.PublicIP = strings.TrimSpace(*request.Web.PublicIP)
}
}
if request.Paths != nil && request.Paths.Certificate != nil {
cfg.Paths.Certificate = strings.TrimSpace(*request.Paths.Certificate)
}
if request.ACME != nil {
if request.ACME.CA != nil {
cfg.ACME.CA = strings.TrimSpace(*request.ACME.CA)
}
if request.ACME.Environment != nil {
cfg.ACME.Environment = strings.TrimSpace(*request.ACME.Environment)
}
if request.ACME.AccountEmail != nil {
cfg.ACME.AccountEmail = strings.TrimSpace(*request.ACME.AccountEmail)
}
if request.ACME.RenewalThresholdDays != nil {
cfg.ACME.RenewalThresholdDays = *request.ACME.RenewalThresholdDays
}
if request.ACME.RenewalCheckIntervalDays != nil {
cfg.ACME.RenewalCheckIntervalDays = *request.ACME.RenewalCheckIntervalDays
}
if request.ACME.RenewalNearExpiryDays != nil {
cfg.ACME.RenewalNearExpiryDays = *request.ACME.RenewalNearExpiryDays
}
if request.ACME.RenewalUrgentThresholdDays != nil {
cfg.ACME.RenewalUrgentThresholdDays = *request.ACME.RenewalUrgentThresholdDays
}
if request.ACME.RenewalMaxRetries != nil {
cfg.ACME.RenewalMaxRetries = *request.ACME.RenewalMaxRetries
}
if request.ACME.RenewalRetryIntervalMins != nil {
cfg.ACME.RenewalRetryIntervalMins = *request.ACME.RenewalRetryIntervalMins
}
if request.ACME.PropagationTimeoutSecs != nil {
cfg.ACME.PropagationTimeoutSecs = *request.ACME.PropagationTimeoutSecs
}
if request.ACME.PropagationPollingIntervalSecs != nil {
cfg.ACME.PropagationPollingIntervalSecs = *request.ACME.PropagationPollingIntervalSecs
}
}
if request.DNS != nil && request.DNS.Provider != nil {
cfg.DNS.Provider = strings.TrimSpace(*request.DNS.Provider)
}
if request.DNS != nil && request.DNS.ProjectID != nil {
cfg.DNS.ProjectID = strings.TrimSpace(*request.DNS.ProjectID)
}
if request.Notification != nil {
item := request.Notification
if item.HTTPEnabled != nil {
cfg.Notification.HTTPEnabled = *item.HTTPEnabled
}
if item.SMTPEnabled != nil {
cfg.Notification.SMTPEnabled = *item.SMTPEnabled
}
if item.SMTPHost != nil {
cfg.Notification.SMTPHost = strings.TrimSpace(*item.SMTPHost)
}
if item.SMTPPort != nil {
cfg.Notification.SMTPPort = *item.SMTPPort
}
if item.SMTPUsername != nil {
cfg.Notification.SMTPUsername = strings.TrimSpace(*item.SMTPUsername)
}
if item.SMTPMode != nil {
cfg.Notification.SMTPMode = strings.TrimSpace(*item.SMTPMode)
}
if item.SMTPAllowPlaintextAuth != nil {
cfg.Notification.SMTPAllowPlaintextAuth = *item.SMTPAllowPlaintextAuth
}
if item.Provider != nil {
cfg.Notification.Provider = strings.TrimSpace(*item.Provider)
}
if item.Endpoint != nil {
cfg.Notification.Endpoint = strings.TrimSpace(*item.Endpoint)
}
if item.HTTPPath != nil {
cfg.Notification.HTTPPath = strings.TrimSpace(*item.HTTPPath)
}
if item.HTTPLoginPath != nil {
cfg.Notification.HTTPLoginPath = strings.TrimSpace(*item.HTTPLoginPath)
}
if item.HTTPAuthMode != nil {
cfg.Notification.HTTPAuthMode = strings.TrimSpace(*item.HTTPAuthMode)
}
if item.EventMode != nil {
cfg.Notification.EventMode = strings.TrimSpace(*item.EventMode)
}
if item.IncludeDeploymentDetails != nil {
cfg.Notification.IncludeDeploymentDetails = *item.IncludeDeploymentDetails
}
if item.IncludeCertificateSerial != nil {
cfg.Notification.IncludeCertificateSerial = *item.IncludeCertificateSerial
}
if item.IncludeManagementURL != nil {
cfg.Notification.IncludeManagementURL = *item.IncludeManagementURL
}
if item.SenderName != nil {
cfg.Notification.SenderName = strings.TrimSpace(*item.SenderName)
}
if item.SenderEmail != nil {
cfg.Notification.SenderEmail = strings.TrimSpace(*item.SenderEmail)
}
if item.Recipients != nil {
cfg.Notification.Recipients = append([]string(nil), (*item.Recipients)...)
}
if item.TimeoutSecs != nil {
cfg.Notification.TimeoutSecs = *item.TimeoutSecs
}
if item.RetryCount != nil {
cfg.Notification.RetryCount = *item.RetryCount
}
if item.RetryIntervalsSecs != nil {
cfg.Notification.RetryIntervalsSecs = append([]int(nil), (*item.RetryIntervalsSecs)...)
}
}
}