From b43797a0fbcb564ea59e83eb2ed344d1e6a6f7d9 Mon Sep 17 00:00:00 2001 From: Signaliks Date: Sat, 24 May 2025 21:32:34 +0800 Subject: [PATCH 01/24] Update models.go --- internal/pkg/sdk3rd/netlify/models.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/pkg/sdk3rd/netlify/models.go b/internal/pkg/sdk3rd/netlify/models.go index 196ee6cc..3ff2d216 100644 --- a/internal/pkg/sdk3rd/netlify/models.go +++ b/internal/pkg/sdk3rd/netlify/models.go @@ -26,8 +26,8 @@ func (r *baseResponse) GetMessage() string { type ProvisionSiteTLSCertificateParams struct { Certificate string `json:"certificate"` - CACertificates string `json:"key"` - Key string `json:"ca_certificates"` + CACertificates string `json:"ca_certificates"` + Key string `json:"key"` } type ProvisionSiteTLSCertificateResponse struct { From 71c093c042dc008928711a13f43e92bb13a3d309 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Sun, 25 May 2025 21:54:39 +0800 Subject: [PATCH 02/24] refactor: clean code --- internal/applicant/providers.go | 2 +- internal/deployer/providers.go | 28 ++-- internal/domain/access.go | 22 +-- .../aliyun-esa/internal/lego.go | 10 +- .../lego-providers/powerdns/powerdns.go | 6 +- .../1panel-console/1panel_console.go | 6 +- .../1panel-console/1panel_console_test.go | 10 +- .../providers/1panel-site/1panel_site.go | 8 +- .../providers/1panel-site/1panel_site_test.go | 10 +- .../baotapanel-console/baotapanel_console.go | 6 +- .../baotapanel_console_test.go | 10 +- .../baotapanel-site/baotapanel_site.go | 6 +- .../baotapanel-site/baotapanel_site_test.go | 10 +- .../baotawaf-console/baotawaf_console.go | 6 +- .../baotawaf-console/baotawaf_console_test.go | 10 +- .../providers/baotawaf-site/baotawaf_site.go | 6 +- .../baotawaf-site/baotawaf_site_test.go | 10 +- .../core/deployer/providers/cdnfly/cdnfly.go | 6 +- .../deployer/providers/cdnfly/cdnfly_test.go | 10 +- .../deployer/providers/flexcdn/flexcdn.go | 6 +- .../providers/flexcdn/flexcdn_test.go | 10 +- .../core/deployer/providers/goedge/goedge.go | 6 +- .../deployer/providers/goedge/goedge_test.go | 10 +- .../core/deployer/providers/lecdn/lecdn.go | 6 +- .../deployer/providers/lecdn/lecdn_test.go | 10 +- .../deployer/providers/proxmoxve/proxmoxve.go | 6 +- .../providers/proxmoxve/proxmoxve_test.go | 10 +- .../ratpanel-console/ratpanel_console.go | 6 +- .../ratpanel-console/ratpanel_console_test.go | 10 +- .../providers/ratpanel-site/ratpanel_site.go | 6 +- .../ratpanel-site/ratpanel_site_test.go | 10 +- .../deployer/providers/safeline/safeline.go | 6 +- .../providers/safeline/safeline_test.go | 10 +- .../providers/mattermost/mattermost.go | 8 +- .../providers/1panel-ssl/1panel_ssl.go | 6 +- .../providers/1panel-ssl/1panel_ssl_test.go | 10 +- migrations/1748178000_upgrade.go | 60 ++++++++ .../access/AccessForm1PanelConfig.tsx | 14 +- .../access/AccessFormBaotaPanelConfig.tsx | 14 +- .../access/AccessFormBaotaWAFConfig.tsx | 14 +- .../access/AccessFormCdnflyConfig.tsx | 14 +- .../access/AccessFormFlexCDNConfig.tsx | 14 +- .../access/AccessFormGoEdgeConfig.tsx | 14 +- .../access/AccessFormLeCDNConfig.tsx | 14 +- .../access/AccessFormPowerDNSConfig.tsx | 14 +- .../access/AccessFormProxmoxVEConfig.tsx | 14 +- .../access/AccessFormRatPanelConfig.tsx | 14 +- .../access/AccessFormSafeLineConfig.tsx | 14 +- .../access/AccessFormWebhookConfig.tsx | 6 +- .../components/workflow/node/_SharedNode.tsx | 132 +++++++++++------- ui/src/domain/access.ts | 22 +-- ui/src/i18n/locales/en/nls.access.json | 83 ++++------- ui/src/i18n/locales/zh/nls.access.json | 83 ++++------- ui/src/i18n/locales/zh/nls.settings.json | 4 +- 54 files changed, 445 insertions(+), 417 deletions(-) create mode 100644 migrations/1748178000_upgrade.go diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index de47ae18..3e4de205 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -476,7 +476,7 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi } applicant, err := pPowerDNS.NewChallengeProvider(&pPowerDNS.ChallengeProviderConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, DnsPropagationTimeout: options.DnsPropagationTimeout, diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index d1d72408..e8dbaf1a 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -119,7 +119,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer switch options.Provider { case domain.DeploymentProviderType1PanelConsole: deployer, err := p1PanelConsole.NewDeployer(&p1PanelConsole.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiVersion: access.ApiVersion, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, @@ -129,7 +129,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderType1PanelSite: deployer, err := p1PanelSite.NewDeployer(&p1PanelSite.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiVersion: access.ApiVersion, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, @@ -454,7 +454,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer switch options.Provider { case domain.DeploymentProviderTypeBaotaPanelConsole: deployer, err := pBaotaPanelConsole.NewDeployer(&pBaotaPanelConsole.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, AutoRestart: maputil.GetBool(options.ProviderServiceConfig, "autoRestart"), @@ -463,7 +463,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeBaotaPanelSite: deployer, err := pBaotaPanelSite.NewDeployer(&pBaotaPanelSite.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, SiteType: maputil.GetOrDefaultString(options.ProviderServiceConfig, "siteType", "other"), @@ -487,7 +487,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer switch options.Provider { case domain.DeploymentProviderTypeBaotaWAFConsole: deployer, err := pBaotaWAFConsole.NewDeployer(&pBaotaWAFConsole.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, }) @@ -495,7 +495,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeBaotaWAFSite: deployer, err := pBaotaWAFSite.NewDeployer(&pBaotaWAFSite.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, AllowInsecureConnections: access.AllowInsecureConnections, SiteName: maputil.GetString(options.ProviderServiceConfig, "siteName"), @@ -565,7 +565,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pCdnfly.NewDeployer(&pCdnfly.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiKey: access.ApiKey, ApiSecret: access.ApiSecret, AllowInsecureConnections: access.AllowInsecureConnections, @@ -614,7 +614,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pFlexCDN.NewDeployer(&pFlexCDN.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiRole: access.ApiRole, AccessKeyId: access.AccessKeyId, AccessKey: access.AccessKey, @@ -654,7 +654,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pGoEdge.NewDeployer(&pGoEdge.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiRole: access.ApiRole, AccessKeyId: access.AccessKeyId, AccessKey: access.AccessKey, @@ -773,7 +773,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pLeCDN.NewDeployer(&pLeCDN.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiVersion: access.ApiVersion, ApiRole: access.ApiRole, Username: access.Username, @@ -845,7 +845,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pProxmoxVE.NewDeployer(&pProxmoxVE.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiToken: access.ApiToken, ApiTokenSecret: access.ApiTokenSecret, AllowInsecureConnections: access.AllowInsecureConnections, @@ -916,7 +916,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer switch options.Provider { case domain.DeploymentProviderTypeRatPanelConsole: deployer, err := pRatPanelConsole.NewDeployer(&pRatPanelConsole.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, AccessTokenId: access.AccessTokenId, AccessToken: access.AccessToken, AllowInsecureConnections: access.AllowInsecureConnections, @@ -925,7 +925,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer case domain.DeploymentProviderTypeRatPanelSite: deployer, err := pRatPanelSite.NewDeployer(&pRatPanelSite.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, AccessTokenId: access.AccessTokenId, AccessToken: access.AccessToken, AllowInsecureConnections: access.AllowInsecureConnections, @@ -946,7 +946,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } deployer, err := pSafeLine.NewDeployer(&pSafeLine.DeployerConfig{ - ApiUrl: access.ApiUrl, + ServerUrl: access.ServerUrl, ApiToken: access.ApiToken, AllowInsecureConnections: access.AllowInsecureConnections, ResourceType: pSafeLine.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")), diff --git a/internal/domain/access.go b/internal/domain/access.go index 13975392..7e1549f1 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -16,7 +16,7 @@ type Access struct { } type AccessConfigFor1Panel struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiVersion string `json:"apiVersion"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` @@ -62,13 +62,13 @@ type AccessConfigForBaishan struct { } type AccessConfigForBaotaPanel struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type AccessConfigForBaotaWAF struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } @@ -87,7 +87,7 @@ type AccessConfigForCacheFly struct { } type AccessConfigForCdnfly struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` ApiSecret string `json:"apiSecret"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` @@ -147,7 +147,7 @@ type AccessConfigForEmail struct { } type AccessConfigForFlexCDN struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiRole string `json:"apiRole"` AccessKeyId string `json:"accessKeyId"` AccessKey string `json:"accessKey"` @@ -169,7 +169,7 @@ type AccessConfigForGoDaddy struct { } type AccessConfigForGoEdge struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiRole string `json:"apiRole"` AccessKeyId string `json:"accessKeyId"` AccessKey string `json:"accessKey"` @@ -200,7 +200,7 @@ type AccessConfigForLarkBot struct { } type AccessConfigForLeCDN struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiVersion string `json:"apiVersion"` ApiRole string `json:"apiRole"` Username string `json:"username"` @@ -249,13 +249,13 @@ type AccessConfigForPorkbun struct { } type AccessConfigForPowerDNS struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type AccessConfigForProxmoxVE struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiToken string `json:"apiToken"` ApiTokenSecret string `json:"apiTokenSecret,omitempty"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` @@ -271,14 +271,14 @@ type AccessConfigForRainYun struct { } type AccessConfigForRatPanel struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` AccessTokenId int32 `json:"accessTokenId"` AccessToken string `json:"accessToken"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type AccessConfigForSafeLine struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiToken string `json:"apiToken"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal/lego.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal/lego.go index 51d4e7c4..5a576af1 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal/lego.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/aliyun-esa/internal/lego.go @@ -102,9 +102,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err) } - siteId, err := d.getSiteId(strings.TrimRight(authZone, ".")) + siteName := strings.TrimRight(authZone, ".") + siteId, err := d.getSiteId(siteName) if err != nil { - return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err) + return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err) } if err := d.addOrUpdateDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, "."), info.Value); err != nil { @@ -122,9 +123,10 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("alicloud-esa: could not find zone for domain %q: %w", domain, err) } - siteId, err := d.getSiteId(strings.TrimRight(authZone, ".")) + siteName := strings.TrimRight(authZone, ".") + siteId, err := d.getSiteId(siteName) if err != nil { - return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", authZone, err) + return fmt.Errorf("alicloud-esa: could not find site for zone %q: %w", siteName, err) } if err := d.removeDNSRecord(siteId, strings.TrimRight(info.EffectiveFQDN, ".")); err != nil { diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go index 7630633c..b34516d4 100644 --- a/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/powerdns/powerdns.go @@ -11,7 +11,7 @@ import ( ) type ChallengeProviderConfig struct { - ApiUrl string `json:"apiUrl"` + ServerUrl string `json:"serverUrl"` ApiKey string `json:"apiKey"` AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` @@ -23,9 +23,9 @@ func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, panic("config is nil") } - host, _ := url.Parse(config.ApiUrl) + serverUrl, _ := url.Parse(config.ServerUrl) providerConfig := pdns.NewDefaultConfig() - providerConfig.Host = host + providerConfig.Host = serverUrl providerConfig.APIKey = config.ApiKey if config.AllowInsecureConnections { providerConfig.HTTPClient.Transport = &http.Transport{ diff --git a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go index e81b264f..0f7efed1 100644 --- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go +++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 1Panel 地址。 - ApiUrl string `json:"apiUrl"` + // 1Panel 服务地址。 + ServerUrl string `json:"serverUrl"` // 1Panel 版本。 // 可取值 "v1"、"v2"。 ApiVersion string `json:"apiVersion"` @@ -39,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go b/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go index 88bf961a..0feae021 100644 --- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go +++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiVersion string fApiKey string ) @@ -24,7 +24,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -35,7 +35,7 @@ Shell command to run this test: go test -v ./1panel_console_test.go -args \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_DEPLOYER_1PANELCONSOLE_SERVERURL="http://127.0.0.1:20410" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIVERSION="v1" \ --CERTIMATE_DEPLOYER_1PANELCONSOLE_APIKEY="your-api-key" */ @@ -47,13 +47,13 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiVersion: fApiVersion, ApiKey: fApiKey, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go index 690e5242..a546c1bb 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go @@ -16,8 +16,8 @@ import ( ) type DeployerConfig struct { - // 1Panel 地址。 - ApiUrl string `json:"apiUrl"` + // 1Panel 服务地址。 + ServerUrl string `json:"serverUrl"` // 1Panel 版本。 // 可取值 "v1"、"v2"。 ApiVersion string `json:"apiVersion"` @@ -49,13 +49,13 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ - ApiUrl: config.ApiUrl, + ServerUrl: config.ServerUrl, ApiVersion: config.ApiVersion, ApiKey: config.ApiKey, }) diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go index 1d5bafef..91b1ebb0 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiVersion string fApiKey string fWebsiteId int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./1panel_site_test.go -args \ --CERTIMATE_DEPLOYER_1PANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_1PANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_1PANELSITE_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_DEPLOYER_1PANELSITE_SERVERURL="http://127.0.0.1:20410" \ --CERTIMATE_DEPLOYER_1PANELSITE_APIVERSION="v1" \ --CERTIMATE_DEPLOYER_1PANELSITE_APIKEY="your-api-key" \ --CERTIMATE_DEPLOYER_1PANELSITE_WEBSITEID="your-website-id" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("WEBSITEID: %v", fWebsiteId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiVersion: fApiVersion, ApiKey: fApiKey, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go index b1a57a81..b5deb005 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 宝塔面板地址。 - ApiUrl string `json:"apiUrl"` + // 宝塔面板服务地址。 + ServerUrl string `json:"serverUrl"` // 宝塔面板接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -36,7 +36,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go index 5f3845e4..2fd4cc5b 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string ) @@ -23,7 +23,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -33,7 +33,7 @@ Shell command to run this test: go test -v ./baotapanel_console_test.go -args \ --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_BAOTAPANELCONSOLE_APIKEY="your-api-key" */ func TestDeploy(t *testing.T) { @@ -44,12 +44,12 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, AllowInsecureConnections: true, AutoRestart: true, diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go index 4d481f7f..f9936e2d 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go @@ -14,8 +14,8 @@ import ( ) type DeployerConfig struct { - // 宝塔面板地址。 - ApiUrl string `json:"apiUrl"` + // 宝塔面板服务地址。 + ServerUrl string `json:"serverUrl"` // 宝塔面板接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -41,7 +41,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go index 5fece978..9e4659ea 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string fSiteType string fSiteName string @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.StringVar(&fSiteType, argsPrefix+"SITETYPE", "", "") flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./baotapanel_site_test.go -args \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_BAOTAPANELSITE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_BAOTAPANELSITE_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_APIKEY="your-api-key" \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_SITETYPE="php" \ --CERTIMATE_DEPLOYER_BAOTAPANELSITE_SITENAME="your-site-name" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("SITETYPE: %v", fSiteType), fmt.Sprintf("SITENAME: %v", fSiteName), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, AllowInsecureConnections: true, SiteType: fSiteType, diff --git a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go index 811b350b..bf8a3892 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go +++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 堡塔云 WAF 地址。 - ApiUrl string `json:"apiUrl"` + // 堡塔云 WAF 服务地址。 + ServerUrl string `json:"serverUrl"` // 堡塔云 WAF 接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -34,7 +34,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go index ba6ddd26..b3804fb5 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go +++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string fSiteName string fSitePort int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -35,7 +35,7 @@ Shell command to run this test: go test -v ./baotawaf_console_test.go -args \ --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_BAOTAWAFCONSOLE_APIKEY="your-api-key" */ func TestDeploy(t *testing.T) { @@ -46,12 +46,12 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, AllowInsecureConnections: true, }) diff --git a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go index ed05937a..faaf5681 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go +++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go @@ -14,8 +14,8 @@ import ( ) type DeployerConfig struct { - // 堡塔云 WAF 地址。 - ApiUrl string `json:"apiUrl"` + // 堡塔云 WAF 服务地址。 + ServerUrl string `json:"serverUrl"` // 堡塔云 WAF 接口密钥。 ApiKey string `json:"apiKey"` // 是否允许不安全的连接。 @@ -40,7 +40,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go index 4e1ffe34..6bead4b5 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go +++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string fSiteName string fSitePort int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") flag.Int64Var(&fSitePort, argsPrefix+"SITEPORT", 0, "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./baotawaf_site_test.go -args \ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_BAOTAWAFSITE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_BAOTAWAFSITE_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_APIKEY="your-api-key" \ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_SITENAME="your-site-name"\ --CERTIMATE_DEPLOYER_BAOTAWAFSITE_SITEPORT=443 @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("SITENAME: %v", fSiteName), fmt.Sprintf("SITEPORT: %v", fSitePort), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, AllowInsecureConnections: true, SiteName: fSiteName, diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go index 909caf3e..6b7c3ec7 100644 --- a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go @@ -15,8 +15,8 @@ import ( ) type DeployerConfig struct { - // Cdnfly 地址。 - ApiUrl string `json:"apiUrl"` + // Cdnfly 服务地址。 + ServerUrl string `json:"serverUrl"` // Cdnfly 用户端 API Key。 ApiKey string `json:"apiKey"` // Cdnfly 用户端 API Secret。 @@ -46,7 +46,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiKey, config.ApiSecret, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.ApiSecret, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go index 26486721..73128183 100644 --- a/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiKey string fApiSecret string fCertificateId string @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") flag.StringVar(&fApiSecret, argsPrefix+"APISECRET", "", "") flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./cdnfly_test.go -args \ --CERTIMATE_DEPLOYER_CDNFLY_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_CDNFLY_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_CDNFLY_APIURL="http://127.0.0.1:88" \ + --CERTIMATE_DEPLOYER_CDNFLY_SERVERURL="http://127.0.0.1:88" \ --CERTIMATE_DEPLOYER_CDNFLY_APIKEY="your-api-key" \ --CERTIMATE_DEPLOYER_CDNFLY_APISECRET="your-api-secret" \ --CERTIMATE_DEPLOYER_CDNFLY_CERTIFICATEID="your-cert-id" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIKEY: %v", fApiKey), fmt.Sprintf("APISECRET: %v", fApiSecret), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiKey: fApiKey, ApiSecret: fApiSecret, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go index a12ed164..7925a72a 100644 --- a/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go +++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go @@ -16,8 +16,8 @@ import ( ) type DeployerConfig struct { - // FlexCDN URL。 - ApiUrl string `json:"apiUrl"` + // FlexCDN 服务地址。 + ServerUrl string `json:"serverUrl"` // FlexCDN 用户角色。 // 可取值 "user"、"admin"。 ApiRole string `json:"apiRole"` @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go index b9b8de07..6725140a 100644 --- a/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go +++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fAccessKeyId string fAccessKey string fCertificateId int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./flexcdn_test.go -args \ --CERTIMATE_DEPLOYER_FLEXCDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_FLEXCDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_FLEXCDN_APIURL="http://127.0.0.1:7788" \ + --CERTIMATE_DEPLOYER_FLEXCDN_SERVERURL="http://127.0.0.1:7788" \ --CERTIMATE_DEPLOYER_FLEXCDN_ACCESSKEYID="your-access-key-id" \ --CERTIMATE_DEPLOYER_FLEXCDN_ACCESSKEY="your-access-key" \ --CERTIMATE_DEPLOYER_FLEXCDN_CERTIFICATEID="your-cerficiate-id" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), fmt.Sprintf("ACCESSKEY: %v", fAccessKey), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiRole: "user", AccessKeyId: fAccessKeyId, AccessKey: fAccessKey, diff --git a/internal/pkg/core/deployer/providers/goedge/goedge.go b/internal/pkg/core/deployer/providers/goedge/goedge.go index ecae774e..5b3e2dc9 100644 --- a/internal/pkg/core/deployer/providers/goedge/goedge.go +++ b/internal/pkg/core/deployer/providers/goedge/goedge.go @@ -16,8 +16,8 @@ import ( ) type DeployerConfig struct { - // GoEdge URL。 - ApiUrl string `json:"apiUrl"` + // GoEdge 服务地址。 + ServerUrl string `json:"serverUrl"` // GoEdge 用户角色。 // 可取值 "user"、"admin"。 ApiRole string `json:"apiRole"` @@ -47,7 +47,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiRole, config.AccessKeyId, config.AccessKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/goedge/goedge_test.go b/internal/pkg/core/deployer/providers/goedge/goedge_test.go index d10f931c..ae03db1d 100644 --- a/internal/pkg/core/deployer/providers/goedge/goedge_test.go +++ b/internal/pkg/core/deployer/providers/goedge/goedge_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fAccessKeyId string fAccessKey string fCertificateId int64 @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") flag.StringVar(&fAccessKey, argsPrefix+"ACCESSKEY", "", "") flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./goedge_test.go -args \ --CERTIMATE_DEPLOYER_GOEDGE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_GOEDGE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_GOEDGE_APIURL="http://127.0.0.1:7788" \ + --CERTIMATE_DEPLOYER_GOEDGE_SERVERURL="http://127.0.0.1:7788" \ --CERTIMATE_DEPLOYER_GOEDGE_ACCESSKEYID="your-access-key-id" \ --CERTIMATE_DEPLOYER_GOEDGE_ACCESSKEY="your-access-key" \ --CERTIMATE_DEPLOYER_GOEDGE_CERTIFICATEID="your-cerficiate-id" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId), fmt.Sprintf("ACCESSKEY: %v", fAccessKey), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiRole: "user", AccessKeyId: fAccessKeyId, AccessKey: fAccessKey, diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn.go b/internal/pkg/core/deployer/providers/lecdn/lecdn.go index 1ad88dcf..15c5dba9 100644 --- a/internal/pkg/core/deployer/providers/lecdn/lecdn.go +++ b/internal/pkg/core/deployer/providers/lecdn/lecdn.go @@ -15,8 +15,8 @@ import ( ) type DeployerConfig struct { - // LeCDN URL。 - ApiUrl string `json:"apiUrl"` + // LeCDN 服务地址。 + ServerUrl string `json:"serverUrl"` // LeCDN 版本。 // 可取值 "v3"。 ApiVersion string `json:"apiVersion"` @@ -59,7 +59,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiRole, config.Username, config.Password, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiVersion, config.ApiRole, config.Username, config.Password, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go b/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go index cbaa4523..fda880c4 100644 --- a/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go +++ b/internal/pkg/core/deployer/providers/lecdn/lecdn_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiVersion string fUsername string fPassword string @@ -26,7 +26,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v3", "") flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "") flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "") @@ -39,7 +39,7 @@ Shell command to run this test: go test -v ./lecdn_test.go -args \ --CERTIMATE_DEPLOYER_LECDN_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_LECDN_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_LECDN_APIURL="http://127.0.0.1:5090" \ + --CERTIMATE_DEPLOYER_LECDN_SERVERURL="http://127.0.0.1:5090" \ --CERTIMATE_DEPLOYER_LECDN_USERNAME="your-username" \ --CERTIMATE_DEPLOYER_LECDN_PASSWORD="your-password" \ --CERTIMATE_DEPLOYER_LECDN_CERTIFICATEID="your-cerficiate-id" @@ -52,7 +52,7 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("USERNAME: %v", fUsername), fmt.Sprintf("PASSWORD: %v", fPassword), @@ -60,7 +60,7 @@ func TestDeploy(t *testing.T) { }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiVersion: fApiVersion, ApiRole: "user", Username: fUsername, diff --git a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go index d2e92460..455ccf6d 100644 --- a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go +++ b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go @@ -16,8 +16,8 @@ import ( ) type DeployerConfig struct { - // Proxmox VE 地址。 - ApiUrl string `json:"apiUrl"` + // Proxmox VE 服务地址。 + ServerUrl string `json:"serverUrl"` // Proxmox VE API Token。 ApiToken string `json:"apiToken"` // Proxmox VE API Token Secret。 @@ -43,7 +43,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiToken, config.ApiTokenSecret, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiToken, config.ApiTokenSecret, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go index 6251bd75..8ae02f3b 100644 --- a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go +++ b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiToken string fApiTokenSecret string fNodeName string @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") flag.StringVar(&fApiTokenSecret, argsPrefix+"APITOKENSECRET", "", "") flag.StringVar(&fNodeName, argsPrefix+"NODENAME", "", "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./proxmoxve_test.go -args \ --CERTIMATE_DEPLOYER_PROXMOXVE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_PROXMOXVE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_PROXMOXVE_APIURL="http://127.0.0.1:8006" \ + --CERTIMATE_DEPLOYER_PROXMOXVE_SERVERURL="http://127.0.0.1:8006" \ --CERTIMATE_DEPLOYER_PROXMOXVE_APITOKEN="your-api-token" \ --CERTIMATE_DEPLOYER_PROXMOXVE_APITOKENSECRET="your-api-token-secret" \ --CERTIMATE_DEPLOYER_PROXMOXVE_NODENAME="your-cluster-node-name" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APITOKEN: %v", fApiToken), fmt.Sprintf("APITOKENSECRET: %v", fApiTokenSecret), fmt.Sprintf("NODENAME: %v", fNodeName), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiToken: fApiToken, ApiTokenSecret: fApiTokenSecret, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go index 51faf4f2..a6c560ae 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go +++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 耗子面板地址。 - ApiUrl string `json:"apiUrl"` + // 耗子面板服务地址。 + ServerUrl string `json:"serverUrl"` // 耗子面板访问令牌 ID。 AccessTokenId int32 `json:"accessTokenId"` // 耗子面板访问令牌。 @@ -36,7 +36,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.AccessTokenId, config.AccessToken, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.AccessTokenId, config.AccessToken, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go index 3f3193b3..3366b06c 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go +++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fAccessTokenId int64 fAccessToken string ) @@ -24,7 +24,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "") flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "") } @@ -35,7 +35,7 @@ Shell command to run this test: go test -v ./ratpanel_console_test.go -args \ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_RATPANELCONSOLE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_RATPANELCONSOLE_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKENID="your-access-token-id" \ --CERTIMATE_DEPLOYER_RATPANELCONSOLE_ACCESSTOKEN="your-access-token" */ @@ -47,13 +47,13 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("ACCESSTOKENID: %v", fAccessTokenId), fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, AccessTokenId: int32(fAccessTokenId), AccessToken: fAccessToken, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go index b4e283be..a296c95e 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go +++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 耗子面板地址。 - ApiUrl string `json:"apiUrl"` + // 耗子面板服务地址。 + ServerUrl string `json:"serverUrl"` // 耗子面板访问令牌 ID。 AccessTokenId int32 `json:"accessTokenId"` // 耗子面板访问令牌。 @@ -38,7 +38,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.AccessTokenId, config.AccessToken, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.AccessTokenId, config.AccessToken, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go index 658175fb..cd84b3ea 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go +++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fAccessTokenId int64 fAccessToken string fSiteName string @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.Int64Var(&fAccessTokenId, argsPrefix+"ACCESSTOKENID", 0, "") flag.StringVar(&fAccessToken, argsPrefix+"ACCESSTOKEN", "", "") flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") @@ -37,7 +37,7 @@ Shell command to run this test: go test -v ./ratpanel_site_test.go -args \ --CERTIMATE_DEPLOYER_RATPANELSITE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_RATPANELSITE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_RATPANELSITE_APIURL="http://127.0.0.1:8888" \ + --CERTIMATE_DEPLOYER_RATPANELSITE_SERVERURL="http://127.0.0.1:8888" \ --CERTIMATE_DEPLOYER_RATPANELSITE_ACCESSTOKENID="your-access-token-id" \ --CERTIMATE_DEPLOYER_RATPANELSITE_ACCESSTOKEN="your-access-token" \ --CERTIMATE_DEPLOYER_RATPANELSITE_SITENAME="your-site-name" @@ -50,14 +50,14 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("ACCESSTOKENID: %v", fAccessTokenId), fmt.Sprintf("ACCESSTOKEN: %v", fAccessToken), fmt.Sprintf("SITENAME: %v", fSiteName), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, AccessTokenId: int32(fAccessTokenId), AccessToken: fAccessToken, AllowInsecureConnections: true, diff --git a/internal/pkg/core/deployer/providers/safeline/safeline.go b/internal/pkg/core/deployer/providers/safeline/safeline.go index f737fda9..0abee743 100644 --- a/internal/pkg/core/deployer/providers/safeline/safeline.go +++ b/internal/pkg/core/deployer/providers/safeline/safeline.go @@ -13,8 +13,8 @@ import ( ) type DeployerConfig struct { - // 雷池 URL。 - ApiUrl string `json:"apiUrl"` + // 雷池服务地址。 + ServerUrl string `json:"serverUrl"` // 雷池 API Token。 ApiToken string `json:"apiToken"` // 是否允许不安全的连接。 @@ -39,7 +39,7 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiToken, config.AllowInsecureConnections) + client, err := createSdkClient(config.ServerUrl, config.ApiToken, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/deployer/providers/safeline/safeline_test.go b/internal/pkg/core/deployer/providers/safeline/safeline_test.go index 67fe6755..9730473c 100644 --- a/internal/pkg/core/deployer/providers/safeline/safeline_test.go +++ b/internal/pkg/core/deployer/providers/safeline/safeline_test.go @@ -14,7 +14,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiToken string fCertificateId int64 ) @@ -24,7 +24,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") flag.Int64Var(&fCertificateId, argsPrefix+"CERTIFICATEID", 0, "") } @@ -35,7 +35,7 @@ Shell command to run this test: go test -v ./safeline_test.go -args \ --CERTIMATE_DEPLOYER_SAFELINE_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_DEPLOYER_SAFELINE_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_DEPLOYER_SAFELINE_APIURL="http://127.0.0.1:9443" \ + --CERTIMATE_DEPLOYER_SAFELINE_SERVERURL="http://127.0.0.1:9443" \ --CERTIMATE_DEPLOYER_SAFELINE_APITOKEN="your-api-token" \ --CERTIMATE_DEPLOYER_SAFELINE_CERTIFICATEID="your-cerficiate-id" */ @@ -47,13 +47,13 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APITOKEN: %v", fApiToken), fmt.Sprintf("CERTIFICATEID: %v", fCertificateId), }, "\n")) deployer, err := provider.NewDeployer(&provider.DeployerConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiToken: fApiToken, AllowInsecureConnections: true, ResourceType: provider.RESOURCE_TYPE_CERTIFICATE, diff --git a/internal/pkg/core/notifier/providers/mattermost/mattermost.go b/internal/pkg/core/notifier/providers/mattermost/mattermost.go index a9b2f4d6..8e4fb24d 100644 --- a/internal/pkg/core/notifier/providers/mattermost/mattermost.go +++ b/internal/pkg/core/notifier/providers/mattermost/mattermost.go @@ -12,13 +12,13 @@ import ( ) type NotifierConfig struct { - // 服务地址。 + // Mattermost 服务地址。 ServerUrl string `json:"serverUrl"` - // 用户名。 + // Mattermost 用户名。 Username string `json:"username"` - // 密码。 + // Mattermost 密码。 Password string `json:"password"` - // 频道 ID。 + // Mattermost 频道 ID。 ChannelId string `json:"channelId"` } diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go index 63900125..387944e3 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go @@ -14,8 +14,8 @@ import ( ) type UploaderConfig struct { - // 1Panel 地址。 - ApiUrl string `json:"apiUrl"` + // 1Panel 服务地址。 + ServerUrl string `json:"serverUrl"` // 1Panel 版本。 ApiVersion string `json:"apiVersion"` // 1Panel 接口密钥。 @@ -35,7 +35,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey) + client, err := createSdkClient(config.ServerUrl, config.ApiVersion, config.ApiKey) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go index cfb250be..d0af7c12 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl_test.go @@ -15,7 +15,7 @@ import ( var ( fInputCertPath string fInputKeyPath string - fApiUrl string + fServerUrl string fApiVersion string fApiKey string ) @@ -25,7 +25,7 @@ func init() { flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") - flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "") flag.StringVar(&fApiVersion, argsPrefix+"APIVERSION", "v1", "") flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") } @@ -36,7 +36,7 @@ Shell command to run this test: go test -v ./1panel_ssl_test.go -args \ --CERTIMATE_UPLOADER_1PANELSSL_INPUTCERTPATH="/path/to/your-input-cert.pem" \ --CERTIMATE_UPLOADER_1PANELSSL_INPUTKEYPATH="/path/to/your-input-key.pem" \ - --CERTIMATE_UPLOADER_1PANELSSL_APIURL="http://127.0.0.1:20410" \ + --CERTIMATE_UPLOADER_1PANELSSL_SERVERURL="http://127.0.0.1:20410" \ --CERTIMATE_UPLOADER_1PANELSSL_APIVERSION="v1" \ --CERTIMATE_UPLOADER_1PANELSSL_APIKEY="your-api-key" */ @@ -48,13 +48,13 @@ func TestDeploy(t *testing.T) { "args:", fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), - fmt.Sprintf("APIURL: %v", fApiUrl), + fmt.Sprintf("SERVERURL: %v", fServerUrl), fmt.Sprintf("APIVERSION: %v", fApiVersion), fmt.Sprintf("APIKEY: %v", fApiKey), }, "\n")) uploader, err := provider.NewUploader(&provider.UploaderConfig{ - ApiUrl: fApiUrl, + ServerUrl: fServerUrl, ApiVersion: fApiVersion, ApiKey: fApiKey, }) diff --git a/migrations/1748178000_upgrade.go b/migrations/1748178000_upgrade.go new file mode 100644 index 00000000..719ed64f --- /dev/null +++ b/migrations/1748178000_upgrade.go @@ -0,0 +1,60 @@ +package migrations + +import ( + "slices" + + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + // migrate data + { + accesses, err := app.FindAllRecords("access") + if err != nil { + return err + } + + providersToUpdate := []string{ + "1panel", + "baotapanel", + "baotawaf", + "cdnfly", + "flexcdn", + "goedge", + "lecdn", + "powerdns", + "proxmoxve", + "ratpanel", + "safeline", + } + for _, access := range accesses { + changed := false + + if slices.Contains(providersToUpdate, access.GetString("provider")) { + config := make(map[string]any) + if err := access.UnmarshalJSONField("config", &config); err != nil { + return err + } + + config["serverUrl"] = config["apiUrl"] + delete(config, "apiUrl") + access.Set("config", config) + changed = true + } + + if changed { + err = app.Save(access) + if err != nil { + return err + } + } + } + } + + return nil + }, func(app core.App) error { + return nil + }) +} diff --git a/ui/src/components/access/AccessForm1PanelConfig.tsx b/ui/src/components/access/AccessForm1PanelConfig.tsx index 29481f15..56792a02 100644 --- a/ui/src/components/access/AccessForm1PanelConfig.tsx +++ b/ui/src/components/access/AccessForm1PanelConfig.tsx @@ -17,7 +17,7 @@ export type AccessForm1PanelConfigProps = { const initFormModel = (): AccessForm1PanelConfigFieldValues => { return { - apiUrl: "http://:20410/", + serverUrl: "http://:20410/", apiVersion: "v1", apiKey: "", }; @@ -27,7 +27,7 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiVersion: z.string().nonempty(t("access.form.1panel_api_version.placeholder")), apiKey: z .string() @@ -51,8 +51,8 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal name={formName} onValuesChange={handleFormChange} > - - + + @@ -68,10 +68,10 @@ const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialVal - + diff --git a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx index dd355b5e..d9cc2752 100644 --- a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx +++ b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormBaotaPanelConfigProps = { const initFormModel = (): AccessFormBaotaPanelConfigFieldValues => { return { - apiUrl: "http://:8888/", + serverUrl: "http://:8888/", apiKey: "", }; }; @@ -26,7 +26,7 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiKey: z.string().nonempty(t("access.form.baotapanel_api_key.placeholder")).trim(), allowInsecureConnections: z.boolean().nullish(), }); @@ -45,8 +45,8 @@ const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initia name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormBaotaWAFConfig.tsx b/ui/src/components/access/AccessFormBaotaWAFConfig.tsx index e87ed596..edf18642 100644 --- a/ui/src/components/access/AccessFormBaotaWAFConfig.tsx +++ b/ui/src/components/access/AccessFormBaotaWAFConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormBaotaWAFConfigProps = { const initFormModel = (): AccessFormBaotaWAFConfigFieldValues => { return { - apiUrl: "http://:8379/", + serverUrl: "http://:8379/", apiKey: "", }; }; @@ -26,7 +26,7 @@ const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialV const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiKey: z.string().nonempty(t("access.form.baotawaf_api_key.placeholder")).trim(), allowInsecureConnections: z.boolean().nullish(), }); @@ -45,8 +45,8 @@ const AccessFormBaotaWAFConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormCdnflyConfig.tsx b/ui/src/components/access/AccessFormCdnflyConfig.tsx index e95e230b..10422c3b 100644 --- a/ui/src/components/access/AccessFormCdnflyConfig.tsx +++ b/ui/src/components/access/AccessFormCdnflyConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormCdnflyConfigProps = { const initFormModel = (): AccessFormCdnflyConfigFieldValues => { return { - apiUrl: "http://:88/", + serverUrl: "http://:88/", apiKey: "", apiSecret: "", }; @@ -27,7 +27,7 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiKey: z .string() .min(1, t("access.form.cdnfly_api_key.placeholder")) @@ -55,8 +55,8 @@ const AccessFormCdnflyConfig = ({ form: formInst, formName, disabled, initialVal name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormFlexCDNConfig.tsx b/ui/src/components/access/AccessFormFlexCDNConfig.tsx index 6ca020bf..71aa9607 100644 --- a/ui/src/components/access/AccessFormFlexCDNConfig.tsx +++ b/ui/src/components/access/AccessFormFlexCDNConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormFlexCDNConfigProps = { const initFormModel = (): AccessFormFlexCDNConfigFieldValues => { return { - apiUrl: "http://:8000/", + serverUrl: "http://:8000/", apiRole: "user", accessKeyId: "", accessKey: "", @@ -28,7 +28,7 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), role: z.union([z.literal("user"), z.literal("admin")], { message: t("access.form.flexcdn_api_role.placeholder"), }), @@ -51,8 +51,8 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa name={formName} onValuesChange={handleFormChange} > - - + + @@ -77,10 +77,10 @@ const AccessFormFlexCDNConfig = ({ form: formInst, formName, disabled, initialVa - + diff --git a/ui/src/components/access/AccessFormGoEdgeConfig.tsx b/ui/src/components/access/AccessFormGoEdgeConfig.tsx index 9c03f2be..641e2276 100644 --- a/ui/src/components/access/AccessFormGoEdgeConfig.tsx +++ b/ui/src/components/access/AccessFormGoEdgeConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormGoEdgeConfigProps = { const initFormModel = (): AccessFormGoEdgeConfigFieldValues => { return { - apiUrl: "http://:7788/", + serverUrl: "http://:7788/", apiRole: "user", accessKeyId: "", accessKey: "", @@ -28,7 +28,7 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), role: z.union([z.literal("user"), z.literal("admin")], { message: t("access.form.goedge_api_role.placeholder"), }), @@ -51,8 +51,8 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal name={formName} onValuesChange={handleFormChange} > - - + + @@ -77,10 +77,10 @@ const AccessFormGoEdgeConfig = ({ form: formInst, formName, disabled, initialVal - + diff --git a/ui/src/components/access/AccessFormLeCDNConfig.tsx b/ui/src/components/access/AccessFormLeCDNConfig.tsx index 4af5a639..282afa8d 100644 --- a/ui/src/components/access/AccessFormLeCDNConfig.tsx +++ b/ui/src/components/access/AccessFormLeCDNConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormLeCDNConfigProps = { const initFormModel = (): AccessFormLeCDNConfigFieldValues => { return { - apiUrl: "http://:5090/", + serverUrl: "http://:5090/", apiVersion: "v3", apiRole: "user", username: "", @@ -29,7 +29,7 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), role: z.union([z.literal("client"), z.literal("master")], { message: t("access.form.lecdn_api_role.placeholder"), }), @@ -52,8 +52,8 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu name={formName} onValuesChange={handleFormChange} > - - + + @@ -72,10 +72,10 @@ const AccessFormLeCDNConfig = ({ form: formInst, formName, disabled, initialValu - + diff --git a/ui/src/components/access/AccessFormPowerDNSConfig.tsx b/ui/src/components/access/AccessFormPowerDNSConfig.tsx index b2bfb081..33447cc8 100644 --- a/ui/src/components/access/AccessFormPowerDNSConfig.tsx +++ b/ui/src/components/access/AccessFormPowerDNSConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormPowerDNSConfigProps = { const initFormModel = (): AccessFormPowerDNSConfigFieldValues => { return { - apiUrl: "http://:8082/", + serverUrl: "http://:8082/", apiKey: "", }; }; @@ -26,7 +26,7 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiKey: z .string() .min(1, t("access.form.powerdns_api_key.placeholder")) @@ -49,8 +49,8 @@ const AccessFormPowerDNSConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx index d0a66745..29280b1c 100644 --- a/ui/src/components/access/AccessFormProxmoxVEConfig.tsx +++ b/ui/src/components/access/AccessFormProxmoxVEConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormProxmoxVEConfigProps = { const initFormModel = (): AccessFormProxmoxVEConfigFieldValues => { return { - apiUrl: "http://:8006/", + serverUrl: "http://:8006/", apiToken: "", }; }; @@ -26,7 +26,7 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiToken: z.string().nonempty(t("access.form.proxmoxve_api_token.placeholder")).trim(), apiTokenSecret: z.string().nullish(), allowInsecureConnections: z.boolean().nullish(), @@ -46,8 +46,8 @@ const AccessFormProxmoxVEConfig = ({ form: formInst, formName, disabled, initial name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormRatPanelConfig.tsx b/ui/src/components/access/AccessFormRatPanelConfig.tsx index ca3d2182..04a71cd1 100644 --- a/ui/src/components/access/AccessFormRatPanelConfig.tsx +++ b/ui/src/components/access/AccessFormRatPanelConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormRatPanelConfigProps = { const initFormModel = (): AccessFormRatPanelConfigFieldValues => { return { - apiUrl: "http://:8888/", + serverUrl: "http://:8888/", accessTokenId: 1, accessToken: "", }; @@ -27,7 +27,7 @@ const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialV const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), accessTokenId: z.preprocess((v) => Number(v), z.number().positive(t("access.form.ratpanel_access_token_id.placeholder"))), accessToken: z.string().nonempty(t("access.form.ratpanel_access_token.placeholder")).trim(), allowInsecureConnections: z.boolean().nullish(), @@ -47,8 +47,8 @@ const AccessFormRatPanelConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormSafeLineConfig.tsx b/ui/src/components/access/AccessFormSafeLineConfig.tsx index 2fde089d..c698d066 100644 --- a/ui/src/components/access/AccessFormSafeLineConfig.tsx +++ b/ui/src/components/access/AccessFormSafeLineConfig.tsx @@ -17,7 +17,7 @@ export type AccessFormSafeLineConfigProps = { const initFormModel = (): AccessFormSafeLineConfigFieldValues => { return { - apiUrl: "http://:9443/", + serverUrl: "http://:9443/", apiToken: "", }; }; @@ -26,7 +26,7 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV const { t } = useTranslation(); const formSchema = z.object({ - apiUrl: z.string().url(t("common.errmsg.url_invalid")), + serverUrl: z.string().url(t("common.errmsg.url_invalid")), apiToken: z .string() .min(1, t("access.form.safeline_api_token.placeholder")) @@ -49,8 +49,8 @@ const AccessFormSafeLineConfig = ({ form: formInst, formName, disabled, initialV name={formName} onValuesChange={handleFormChange} > - - + + - + diff --git a/ui/src/components/access/AccessFormWebhookConfig.tsx b/ui/src/components/access/AccessFormWebhookConfig.tsx index 6e6ec87a..d79f848d 100644 --- a/ui/src/components/access/AccessFormWebhookConfig.tsx +++ b/ui/src/components/access/AccessFormWebhookConfig.tsx @@ -362,10 +362,10 @@ const AccessFormWebhookConfig = ({ form: formInst, formName, disabled, initialVa - + diff --git a/ui/src/components/workflow/node/_SharedNode.tsx b/ui/src/components/workflow/node/_SharedNode.tsx index 510a0154..7bec1e90 100644 --- a/ui/src/components/workflow/node/_SharedNode.tsx +++ b/ui/src/components/workflow/node/_SharedNode.tsx @@ -1,13 +1,14 @@ -import { memo, useRef } from "react"; +import { memo, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { CloseCircleOutlined as CloseCircleOutlinedIcon, EllipsisOutlined as EllipsisOutlinedIcon, FormOutlined as FormOutlinedIcon, + MergeOutlined as MergeOutlinedIcon, MoreOutlined as MoreOutlinedIcon, } from "@ant-design/icons"; import { useControllableValue } from "ahooks"; -import { Button, Card, Drawer, Dropdown, Input, type InputRef, Modal, Popover, Space } from "antd"; +import { Button, Card, Drawer, Dropdown, Input, type InputRef, type MenuProps, Modal, Popover, Space } from "antd"; import { produce } from "immer"; import { isEqual } from "radash"; @@ -59,12 +60,13 @@ const SharedNodeTitle = ({ className, style, node, disabled }: SharedNodeTitlePr type SharedNodeMenuProps = SharedNodeProps & { branchId?: string; branchIndex?: number; + menus?: Array<"rename" | "duplicate" | "remove">; trigger: React.ReactNode; afterUpdate?: () => void; afterDelete?: () => void; }; -const isBranchingNode = (node: WorkflowNode) => { +const isNodeBranchLike = (node: WorkflowNode) => { return ( node.type === WorkflowNodeType.Branch || node.type === WorkflowNodeType.Condition || @@ -74,7 +76,11 @@ const isBranchingNode = (node: WorkflowNode) => { ); }; -const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterUpdate, afterDelete }: SharedNodeMenuProps) => { +const isNodeReadOnly = (node: WorkflowNode) => { + return node.type === WorkflowNodeType.Start || node.type === WorkflowNodeType.End; +}; + +const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, afterUpdate, afterDelete }: SharedNodeMenuProps) => { const { t } = useTranslation(); const { updateNode, removeNode, removeBranch } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeNode", "removeBranch"])); @@ -101,7 +107,7 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU }; const handleDeleteClick = async () => { - if (isBranchingNode(node)) { + if (isNodeBranchLike(node)) { await removeBranch(branchId!, branchIndex!); } else { await removeNode(node.id); @@ -110,56 +116,82 @@ const SharedNodeMenu = ({ trigger, node, disabled, branchId, branchIndex, afterU afterDelete?.(); }; + const menuItems = useMemo(() => { + let temp = [ + { + key: "rename", + disabled: disabled, + label: isNodeBranchLike(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"), + icon: , + onClick: () => { + nameRef.current = node.name; + + const dialog = modalApi.confirm({ + title: isNodeBranchLike(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"), + content: ( +
+ (nameRef.current = e.target.value)} + onPressEnter={async () => { + await handleRenameConfirm(); + dialog.destroy(); + }} + /> +
+ ), + icon: null, + okText: t("common.button.save"), + onOk: handleRenameConfirm, + }); + setTimeout(() => nameInputRef.current?.focus(), 1); + }, + }, + // { + // key: "duplicate", + // disabled: disabled || isNodeReadOnly(node), + // label: isNodeBranchLike(node) ? t("workflow_node.action.duplicate_branch") : t("workflow_node.action.duplicate_node"), + // icon: , + // }, + { + type: "divider", + }, + { + key: "remove", + disabled: disabled || isNodeReadOnly(node), + label: isNodeBranchLike(node) ? t("workflow_node.action.remove_branch") : t("workflow_node.action.remove_node"), + icon: , + danger: true, + onClick: handleDeleteClick, + }, + ] satisfies MenuProps["items"]; + + if (menus) { + temp = temp.filter((item) => item.type === "divider" || menus.includes(item.key as "rename" | "remove")); + temp = temp.filter((item, index, array) => { + if (item.type !== "divider") return true; + return index === 0 || array[index - 1].type !== "divider"; + }); + if (temp[0]?.type === "divider") { + temp.shift(); + } + if (temp[temp.length - 1]?.type === "divider") { + temp.pop(); + } + } + + return temp; + }, [disabled, node]); + return ( <> {ModelContextHolder} , - onClick: () => { - nameRef.current = node.name; - - const dialog = modalApi.confirm({ - title: isBranchingNode(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"), - content: ( -
- (nameRef.current = e.target.value)} - onPressEnter={async () => { - await handleRenameConfirm(); - dialog.destroy(); - }} - /> -
- ), - icon: null, - okText: t("common.button.save"), - onOk: handleRenameConfirm, - }); - setTimeout(() => nameInputRef.current?.focus(), 1); - }, - }, - { - type: "divider", - }, - { - key: "remove", - disabled: disabled || node.type === WorkflowNodeType.Start, - label: isBranchingNode(node) ? t("workflow_node.action.remove_branch") : t("workflow_node.action.remove_node"), - icon: , - danger: true, - onClick: handleDeleteClick, - }, - ], + items: menuItems, }} trigger={["click"]} > @@ -264,7 +296,6 @@ const SharedNodeConfigDrawer = ({ const { promise, resolve, reject } = Promise.withResolvers(); if (changed) { - console.log(oldValues, newValues); modalApi.confirm({ title: t("common.text.operation_confirm"), content: t("workflow_node.unsaved_changes.confirm"), @@ -288,6 +319,7 @@ const SharedNodeConfigDrawer = ({ destroyOnHidden extra={ } type="text" />} diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index f60f39c8..3765310a 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -73,7 +73,7 @@ export interface AccessModel extends BaseModel { // #region AccessConfig export type AccessConfigFor1Panel = { - apiUrl: string; + serverUrl: string; apiVersion: string; apiKey: string; allowInsecureConnections?: boolean; @@ -119,13 +119,13 @@ export type AccessConfigForBaishan = { }; export type AccessConfigForBaotaPanel = { - apiUrl: string; + serverUrl: string; apiKey: string; allowInsecureConnections?: boolean; }; export type AccessConfigForBaotaWAF = { - apiUrl: string; + serverUrl: string; apiKey: string; allowInsecureConnections?: boolean; }; @@ -144,7 +144,7 @@ export type AccessConfigForCacheFly = { }; export type AccessConfigForCdnfly = { - apiUrl: string; + serverUrl: string; apiKey: string; apiSecret: string; allowInsecureConnections?: boolean; @@ -204,7 +204,7 @@ export type AccessConfigForEmail = { }; export type AccessConfigForFlexCDN = { - apiUrl: string; + serverUrl: string; apiRole: string; accessKeyId: string; accessKey: string; @@ -226,7 +226,7 @@ export type AccessConfigForGoDaddy = { }; export type AccessConfigForGoEdge = { - apiUrl: string; + serverUrl: string; apiRole: string; accessKeyId: string; accessKey: string; @@ -257,7 +257,7 @@ export type AccessConfigForLarkBot = { }; export type AccessConfigForLeCDN = { - apiUrl: string; + serverUrl: string; apiVersion: string; apiRole: string; username: string; @@ -306,13 +306,13 @@ export type AccessConfigForPorkbun = { }; export type AccessConfigForPowerDNS = { - apiUrl: string; + serverUrl: string; apiKey: string; allowInsecureConnections?: boolean; }; export type AccessConfigForProxmoxVE = { - apiUrl: string; + serverUrl: string; apiToken: string; apiTokenSecret?: string; allowInsecureConnections?: boolean; @@ -328,14 +328,14 @@ export type AccessConfigForRainYun = { }; export type AccessConfigForRatPanel = { - apiUrl: string; + serverUrl: string; accessTokenId: number; accessToken: string; allowInsecureConnections?: boolean; }; export type AccessConfigForSafeLine = { - apiUrl: string; + serverUrl: string; apiToken: string; allowInsecureConnections?: boolean; }; diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 41b81636..c2e52c6a 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -34,16 +34,16 @@ "access.form.certificate_authority.placeholder": "Please select a certificate authority", "access.form.notification_channel.label": "Notification channel", "access.form.notification_channel.placeholder": "Please select a notification channel", - "access.form.1panel_api_url.label": "1Panel URL", - "access.form.1panel_api_url.placeholder": "Please enter 1Panel URL", + "access.form.1panel_server_url.label": "1Panel server URL", + "access.form.1panel_server_url.placeholder": "Please enter 1Panel server URL", "access.form.1panel_api_version.label": "1Panel version", "access.form.1panel_api_version.placeholder": "Please select 1Panel version", "access.form.1panel_api_key.label": "1Panel API key", "access.form.1panel_api_key.placeholder": "Please enter 1Panel API key", "access.form.1panel_api_key.tooltip": "For more information, see https://docs.1panel.pro/dev_manual/api_manual/", - "access.form.1panel_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.1panel_allow_insecure_conns.switch.on": "Allow", - "access.form.1panel_allow_insecure_conns.switch.off": "Disallow", + "access.form.common_allow_insecure_conns.label": "Insecure SSL/TLS connections", + "access.form.common_allow_insecure_conns.switch.on": "Allow", + "access.form.common_allow_insecure_conns.switch.off": "Disallow", "access.form.acmeca_endpoint.label": "Endpoint", "access.form.acmeca_endpoint.placeholder": "Please enter endpoint", "access.form.acmeca_endpoint.tooltip": "For more information, see https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1", @@ -104,22 +104,16 @@ "access.form.upyun_password.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", "access.form.baishan_api_token.label": "Baishan Cloud API token", "access.form.baishan_api_token.placeholder": "Please enter Baishan Cloud API token", - "access.form.baotapanel_api_url.label": "aaPanel URL", - "access.form.baotapanel_api_url.placeholder": "Please enter aaPanel URL", + "access.form.baotapanel_server_url.label": "aaPanel server URL", + "access.form.baotapanel_server_url.placeholder": "Please enter aaPanel server URL", "access.form.baotapanel_api_key.label": "aaPanel API key", "access.form.baotapanel_api_key.placeholder": "Please enter aaPanel API key", "access.form.baotapanel_api_key.tooltip": "For more information, see https://www.bt.cn/bbs/thread-20376-1-1.html", - "access.form.baotapanel_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.baotapanel_allow_insecure_conns.switch.on": "Allow", - "access.form.baotapanel_allow_insecure_conns.switch.off": "Disallow", - "access.form.baotawaf_api_url.label": "aaWAF URL", - "access.form.baotawaf_api_url.placeholder": "Please enter aaWAF URL", + "access.form.baotawaf_server_url.label": "aaWAF server URL", + "access.form.baotawaf_server_url.placeholder": "Please enter aaWAF server URL", "access.form.baotawaf_api_key.label": "aaWAF API key", "access.form.baotawaf_api_key.placeholder": "Please enter aaWAF API key", "access.form.baotawaf_api_key.tooltip": "For more information, see https://github.com/aaPanel/aaWAF/blob/main/API.md", - "access.form.baotawaf_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.baotawaf_allow_insecure_conns.switch.on": "Allow", - "access.form.baotawaf_allow_insecure_conns.switch.off": "Disallow", "access.form.byteplus_access_key.label": "BytePlus AccessKey", "access.form.byteplus_access_key.placeholder": "Please enter BytePlus AccessKey", "access.form.byteplus_access_key.tooltip": "For more information, see https://docs.byteplus.com/en/docs/byteplus-platform/docs-managing-keys", @@ -129,17 +123,14 @@ "access.form.cachefly_api_token.label": "CacheFly API token", "access.form.cachefly_api_token.placeholder": "Please enter CacheFly API token", "access.form.cachefly_api_token.tooltip": "For more information, see https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228", - "access.form.cdnfly_api_url.label": "Cdnfly URL", - "access.form.cdnfly_api_url.placeholder": "Please enter Cdnfly URL", + "access.form.cdnfly_server_url.label": "Cdnfly server URL", + "access.form.cdnfly_server_url.placeholder": "Please enter Cdnfly server URL", "access.form.cdnfly_api_key.label": "Cdnfly user API key", "access.form.cdnfly_api_key.placeholder": "Please enter Cdnfly user API key", "access.form.cdnfly_api_key.tooltip": "For more information, see https://doc.cdnfly.cn/shiyongjieshao.html", "access.form.cdnfly_api_secret.label": "Cdnfly user API secret", "access.form.cdnfly_api_secret.placeholder": "Please enter Cdnfly user API secret", "access.form.cdnfly_api_secret.tooltip": "For more information, see https://doc.cdnfly.cn/shiyongjieshao.html", - "access.form.cdnfly_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.cdnfly_allow_insecure_conns.switch.on": "Allow", - "access.form.cdnfly_allow_insecure_conns.switch.off": "Disallow", "access.form.cloudflare_dns_api_token.label": "Cloudflare DNS API token", "access.form.cloudflare_dns_api_token.placeholder": "Please enter Cloudflare DNS API token", "access.form.cloudflare_dns_api_token.tooltip": "For more information, see https://developers.cloudflare.com/fundamentals/api/get-started/create-token/", @@ -201,8 +192,8 @@ "access.form.email_default_sender_address.placeholder": "Please enter default sender email address", "access.form.email_default_receiver_address.label": "Default receiver email address (Optional)", "access.form.email_default_receiver_address.placeholder": "Please enter default receiver email address", - "access.form.flexcdn_api_url.label": "FlexCDN URL", - "access.form.flexcdn_api_url.placeholder": "Please enter FlexCDN URL", + "access.form.flexcdn_server_url.label": "FlexCDN server URL", + "access.form.flexcdn_server_url.placeholder": "Please enter FlexCDN server URL", "access.form.flexcdn_api_role.label": "FlexCDN user role", "access.form.flexcdn_api_role.placeholder": "Please select FlexCDN user role", "access.form.flexcdn_api_role.option.user.label": "Platform user", @@ -213,9 +204,6 @@ "access.form.flexcdn_access_key.label": "FlexCDN AccessKey", "access.form.flexcdn_access_key.placeholder": "Please enter FlexCDN AccessKey", "access.form.flexcdn_access_key.tooltip": "For more information, see https://flexcdn.cn/docs/api/auth", - "access.form.flexcdn_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.flexcdn_allow_insecure_conns.switch.on": "Allow", - "access.form.flexcdn_allow_insecure_conns.switch.off": "Disallow", "access.form.gcore_api_token.label": "Gcore API token", "access.form.gcore_api_token.placeholder": "Please enter Gcore API token", "access.form.gcore_api_token.tooltip": "For more information, see https://api.gcore.com/docs/iam#section/Authentication", @@ -231,8 +219,8 @@ "access.form.godaddy_api_secret.label": "GoDaddy API secret", "access.form.godaddy_api_secret.placeholder": "Please enter GoDaddy API secret", "access.form.godaddy_api_secret.tooltip": "For more information, see https://developer.godaddy.com/", - "access.form.goedge_api_url.label": "GoEdge URL", - "access.form.goedge_api_url.placeholder": "Please enter GoEdge URL", + "access.form.goedge_server_url.label": "GoEdge server URL", + "access.form.goedge_server_url.placeholder": "Please enter GoEdge server URL", "access.form.goedge_api_role.label": "GoEdge user role", "access.form.goedge_api_role.placeholder": "Please select GoEdge user role", "access.form.goedge_api_role.option.user.label": "Platform user", @@ -243,9 +231,6 @@ "access.form.goedge_access_key.label": "GoEdge AccessKey", "access.form.goedge_access_key.placeholder": "Please enter GoEdge AccessKey", "access.form.goedge_access_key.tooltip": "For more information, see https://goedge.cloud/docs/API/Auth.md", - "access.form.goedge_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.goedge_allow_insecure_conns.switch.on": "Allow", - "access.form.goedge_allow_insecure_conns.switch.off": "Disallow", "access.form.googletrustservices_eab_kid.label": "ACME EAB KID", "access.form.googletrustservices_eab_kid.placeholder": "Please enter ACME EAB KID", "access.form.googletrustservices_eab_kid.tooltip": "For more information, see https://cloud.google.com/certificate-manager/docs/public-ca-tutorial", @@ -270,8 +255,8 @@ "access.form.larkbot_webhook_url.label": "Lark bot Webhook URL", "access.form.larkbot_webhook_url.placeholder": "Please enter Lark bot Webhook URL", "access.form.larkbot_webhook_url.tooltip": "For more information, see https://www.feishu.cn/hc/en-US/articles/807992406756", - "access.form.lecdn_api_url.label": "LeCDN URL", - "access.form.lecdn_api_url.placeholder": "Please enter LeCDN URL", + "access.form.lecdn_server_url.label": "LeCDN server URL", + "access.form.lecdn_server_url.placeholder": "Please enter LeCDN server URL", "access.form.lecdn_api_version.label": "LeCDN version", "access.form.lecdn_api_version.placeholder": "Please select LeCDN version", "access.form.lecdn_api_role.label": "LeCDN user role", @@ -282,9 +267,6 @@ "access.form.lecdn_username.placeholder": "Please enter LeCDN username", "access.form.lecdn_password.label": "LeCDN password", "access.form.lecdn_password.placeholder": "Please enter GoEdge password", - "access.form.lecdn_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.lecdn_allow_insecure_conns.switch.on": "Allow", - "access.form.lecdn_allow_insecure_conns.switch.off": "Disallow", "access.form.mattermost_server_url.label": "Mattermost server URL", "access.form.mattermost_server_url.placeholder": "Please enter Mattermost server URL", "access.form.mattermost_username.label": "Mattermost username", @@ -330,25 +312,19 @@ "access.form.porkbun_secret_api_key.label": "Porkbun secret API key", "access.form.porkbun_secret_api_key.placeholder": "Please enter Porkbun secret API key", "access.form.porkbun_secret_api_key.tooltip": "For more information, see https://porkbun.com/api/json/v3/documentation", - "access.form.powerdns_api_url.label": "PowerDNS URL", - "access.form.powerdns_api_url.placeholder": "Please enter PowerDNS URL", + "access.form.powerdns_server_url.label": "PowerDNS server URL", + "access.form.powerdns_server_url.placeholder": "Please enter PowerDNS server URL", "access.form.powerdns_api_key.label": "PowerDNS API key", "access.form.powerdns_api_key.placeholder": "Please enter PowerDNS API key", "access.form.powerdns_api_key.tooltip": "For more information, see https://doc.powerdns.com/authoritative/http-api/index.html#enabling-the-api", - "access.form.powerdns_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.powerdns_allow_insecure_conns.switch.on": "Allow", - "access.form.powerdns_allow_insecure_conns.switch.off": "Disallow", - "access.form.proxmoxve_api_url.label": "Proxmox VE URL", - "access.form.proxmoxve_api_url.placeholder": "Please enter Proxmox VE URL", + "access.form.proxmoxve_server_url.label": "Proxmox VE server URL", + "access.form.proxmoxve_server_url.placeholder": "Please enter Proxmox VE server URL", "access.form.proxmoxve_api_token.label": "Proxmox VE API token", "access.form.proxmoxve_api_token.placeholder": "Please enter Proxmox VE API token", "access.form.proxmoxve_api_token.tooltip": "For more information, see https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens", "access.form.proxmoxve_api_token_secret.label": "Proxmox VE API token secret (Optional)", "access.form.proxmoxve_api_token_secret.placeholder": "Please enter Proxmox VE API token secret", "access.form.proxmoxve_api_token_secret.tooltip": "For more information, see https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens", - "access.form.proxmoxve_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.proxmoxve_allow_insecure_conns.switch.on": "Allow", - "access.form.proxmoxve_allow_insecure_conns.switch.off": "Disallow", "access.form.qiniu_access_key.label": "Qiniu AccessKey", "access.form.qiniu_access_key.placeholder": "Please enter Qiniu AccessKey", "access.form.qiniu_access_key.tooltip": "For more information, see https://portal.qiniu.com/", @@ -358,25 +334,19 @@ "access.form.rainyun_api_key.label": "Rain Yun API key", "access.form.rainyun_api_key.placeholder": "Please enter Rain Yun API key", "access.form.rainyun_api_key.tooltip": "For more information, see https://app.rainyun.com/account/settings/api-key", - "access.form.ratpanel_api_url.label": "RatPanel URL", - "access.form.ratpanel_api_url.placeholder": "Please enter RatPanel URL", + "access.form.ratpanel_server_url.label": "RatPanel server URL", + "access.form.ratpanel_server_url.placeholder": "Please enter RatPanel server URL", "access.form.ratpanel_access_token_id.label": "RatPanel access token ID", "access.form.ratpanel_access_token_id.placeholder": "Please enter RatPanel access token ID", "access.form.ratpanel_access_token_id.tooltip": "For more information, see https://ratpanel.github.io/advanced/api.html", "access.form.ratpanel_access_token.label": "RatPanel access token", "access.form.ratpanel_access_token.placeholder": "Please enter RatPanel access token", "access.form.ratpanel_access_token.tooltip": "For more information, see https://ratpanel.github.io/advanced/api.html", - "access.form.ratpanel_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.ratpanel_allow_insecure_conns.switch.on": "Allow", - "access.form.ratpanel_allow_insecure_conns.switch.off": "Disallow", - "access.form.safeline_api_url.label": "SafeLine URL", - "access.form.safeline_api_url.placeholder": "Please enter SafeLine URL", + "access.form.safeline_server_url.label": "SafeLine server URL", + "access.form.safeline_server_url.placeholder": "Please enter SafeLine server URL", "access.form.safeline_api_token.label": "SafeLine API token", "access.form.safeline_api_token.placeholder": "Please enter SafeLine API token", "access.form.safeline_api_token.tooltip": "For more information, see https://docs.waf.chaitin.com/en/reference/articles/openapi", - "access.form.safeline_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.safeline_allow_insecure_conns.switch.on": "Allow", - "access.form.safeline_allow_insecure_conns.switch.off": "Disallow", "access.form.ssh_host.label": "Server host", "access.form.ssh_host.placeholder": "Please enter server host", "access.form.ssh_port.label": "Server port", @@ -466,9 +436,6 @@ "access.form.webhook_preset_data.option.pushplus.label": "PushPlus", "access.form.webhook_preset_data.option.serverchan.label": "ServerChan", "access.form.webhook_preset_data.option.common.label": "General template", - "access.form.webhook_allow_insecure_conns.label": "Insecure SSL/TLS connections", - "access.form.webhook_allow_insecure_conns.switch.on": "Allow", - "access.form.webhook_allow_insecure_conns.switch.off": "Disallow", "access.form.wecombot_webhook_url.label": "WeCom bot Webhook URL", "access.form.wecombot_webhook_url.placeholder": "Please enter WeCom bot Webhook URL", "access.form.wecombot_webhook_url.tooltip": "For more information, see https://open.work.weixin.qq.com/help2/pc/18401", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index a5e89438..fdae1705 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -34,16 +34,16 @@ "access.form.certificate_authority.placeholder": "请选择证书颁发机构", "access.form.notification_channel.label": "通知渠道", "access.form.notification_channel.placeholder": "请选择通知渠道", - "access.form.1panel_api_url.label": "1Panel URL", - "access.form.1panel_api_url.placeholder": "请输入 1Panel URL", + "access.form.common_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", + "access.form.common_allow_insecure_conns.switch.on": "允许", + "access.form.common_allow_insecure_conns.switch.off": "不允许", + "access.form.1panel_server_url.label": "1Panel 服务地址", + "access.form.1panel_server_url.placeholder": "请输入 1Panel 服务地址", "access.form.1panel_api_version.label": "1Panel 版本", "access.form.1panel_api_version.placeholder": "请选择 1Panel 版本", "access.form.1panel_api_key.label": "1Panel 接口密钥", "access.form.1panel_api_key.placeholder": "请输入 1Panel 接口密钥", "access.form.1panel_api_key.tooltip": "这是什么?请参阅 https://1panel.cn/docs/dev_manual/api_manual/", - "access.form.1panel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.1panel_allow_insecure_conns.switch.on": "允许", - "access.form.1panel_allow_insecure_conns.switch.off": "不允许", "access.form.acmeca_endpoint.label": "服务端点", "access.form.acmeca_endpoint.placeholder": "请输入服务端点", "access.form.acmeca_endpoint.tooltip": "这是什么?请参阅 https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1", @@ -95,22 +95,16 @@ "access.form.baiducloud_secret_access_key.tooltip": "这是什么?请参阅 https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p", "access.form.baishan_api_token.label": "白山云 API Token", "access.form.baishan_api_token.placeholder": "请输入白山云 API Token", - "access.form.baotapanel_api_url.label": "宝塔面板 URL", - "access.form.baotapanel_api_url.placeholder": "请输入宝塔面板 URL", + "access.form.baotapanel_server_url.label": "宝塔面板服务地址", + "access.form.baotapanel_server_url.placeholder": "请输入宝塔面板服务地址", "access.form.baotapanel_api_key.label": "宝塔面板接口密钥", "access.form.baotapanel_api_key.placeholder": "请输入宝塔面板接口密钥", "access.form.baotapanel_api_key.tooltip": "这是什么?请参阅 https://www.bt.cn/bbs/thread-113890-1-1.html", - "access.form.baotapanel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.baotapanel_allow_insecure_conns.switch.on": "允许", - "access.form.baotapanel_allow_insecure_conns.switch.off": "不允许", - "access.form.baotawaf_api_url.label": "堡塔云 WAF URL", - "access.form.baotawaf_api_url.placeholder": "请输入堡塔云 WAF URL", + "access.form.baotawaf_server_url.label": "堡塔云 WAF 服务地址", + "access.form.baotawaf_server_url.placeholder": "请输入堡塔云 WAF 服务地址", "access.form.baotawaf_api_key.label": "堡塔云 WAF 接口密钥", "access.form.baotawaf_api_key.placeholder": "请输入 堡塔云 WAF 接口密钥", "access.form.baotawaf_api_key.tooltip": "这是什么?请参阅 https://github.com/aaPanel/aaWAF/blob/main/API.md", - "access.form.baotawaf_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.baotawaf_allow_insecure_conns.switch.on": "允许", - "access.form.baotawaf_allow_insecure_conns.switch.off": "不允许", "access.form.bunny_api_key.label": "Bunny API Key", "access.form.bunny_api_key.placeholder": "请输入 Bunny API Key", "access.form.bunny_api_key.tooltip": "这是什么?请参阅 https://docs.bunny.net/reference/bunnynet-api-overview", @@ -123,17 +117,14 @@ "access.form.cachefly_api_token.label": "CacheFly API Token", "access.form.cachefly_api_token.placeholder": "请输入 CacheFly API Token", "access.form.cachefly_api_token.tooltip": "这是什么?请参阅 https://kb.cachefly.com/kb/guide/en/generating-tokens-and-keys-Oll9Irt5TI/Steps/2460228", - "access.form.cdnfly_api_url.label": "Cdnfly URL", - "access.form.cdnfly_api_url.placeholder": "请输入 Cdnfly URL", + "access.form.cdnfly_server_url.label": "Cdnfly 服务地址", + "access.form.cdnfly_server_url.placeholder": "请输入 Cdnfly 服务地址", "access.form.cdnfly_api_key.label": "Cdnfly 用户端 API Key", "access.form.cdnfly_api_key.placeholder": "请输入 Cdnfly 用户端 API Key", "access.form.cdnfly_api_key.tooltip": "这是什么?请参阅 https://doc.cdnfly.cn/shiyongjieshao.html", "access.form.cdnfly_api_secret.label": "Cdnfly 用户端 API Secret", "access.form.cdnfly_api_secret.placeholder": "请输入 Cdnfly 用户端 API Secret", "access.form.cdnfly_api_secret.tooltip": "这是什么?请参阅 https://doc.cdnfly.cn/shiyongjieshao.html", - "access.form.cdnfly_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.cdnfly_allow_insecure_conns.switch.on": "允许", - "access.form.cdnfly_allow_insecure_conns.switch.off": "不允许", "access.form.cloudflare_dns_api_token.label": "Cloudflare DNS API 令牌", "access.form.cloudflare_dns_api_token.placeholder": "请输入 Cloudflare DNS API 令牌", "access.form.cloudflare_dns_api_token.tooltip": "这是什么?请参阅 https://developers.cloudflare.com/fundamentals/api/get-started/create-token/", @@ -195,8 +186,8 @@ "access.form.email_default_sender_address.placeholder": "请输入默认的发送邮箱地址", "access.form.email_default_receiver_address.label": "默认的接收邮箱地址(可选)", "access.form.email_default_receiver_address.placeholder": "请输入默认的接收邮箱地址", - "access.form.flexcdn_api_url.label": "FlexCDN URL", - "access.form.flexcdn_api_url.placeholder": "请输入 FlexCDN URL", + "access.form.flexcdn_server_url.label": "FlexCDN 服务地址", + "access.form.flexcdn_server_url.placeholder": "请输入 FlexCDN 服务地址", "access.form.flexcdn_api_role.label": "FlexCDN 用户角色", "access.form.flexcdn_api_role.placeholder": "请选择 FlexCDN 用户角色", "access.form.flexcdn_api_role.option.user.label": "平台用户", @@ -207,9 +198,6 @@ "access.form.flexcdn_access_key.label": "FlexCDN AccessKey", "access.form.flexcdn_access_key.placeholder": "请输入 FlexCDN AccessKey", "access.form.flexcdn_access_key.tooltip": "这是什么?请参阅 https://flexcdn.cn/docs/api/auth", - "access.form.flexcdn_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.flexcdn_allow_insecure_conns.switch.on": "允许", - "access.form.flexcdn_allow_insecure_conns.switch.off": "不允许", "access.form.gcore_api_token.label": "Gcore API Token", "access.form.gcore_api_token.placeholder": "请输入 Gcore API Token", "access.form.gcore_api_token.tooltip": "这是什么?请参阅 https://api.gcore.com/docs/iam#section/Authentication", @@ -225,8 +213,8 @@ "access.form.godaddy_api_secret.label": "GoDaddy API Secret", "access.form.godaddy_api_secret.placeholder": "请输入 GoDaddy API Secret", "access.form.godaddy_api_secret.tooltip": "这是什么?请参阅 https://developer.godaddy.com/", - "access.form.goedge_api_url.label": "GoEdge URL", - "access.form.goedge_api_url.placeholder": "请输入 GoEdge URL", + "access.form.goedge_server_url.label": "GoEdge 服务地址", + "access.form.goedge_server_url.placeholder": "请输入 GoEdge 服务地址", "access.form.goedge_api_role.label": "GoEdge 用户角色", "access.form.goedge_api_role.placeholder": "请选择 GoEdge 用户角色", "access.form.goedge_api_role.option.user.label": "平台用户", @@ -237,9 +225,6 @@ "access.form.goedge_access_key.label": "GoEdge AccessKey", "access.form.goedge_access_key.placeholder": "请输入 GoEdge AccessKey", "access.form.goedge_access_key.tooltip": "这是什么?请参阅 https://goedge.cloud/docs/API/Auth.md", - "access.form.goedge_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.goedge_allow_insecure_conns.switch.on": "允许", - "access.form.goedge_allow_insecure_conns.switch.off": "不允许", "access.form.googletrustservices_eab_kid.label": "ACME EAB KID", "access.form.googletrustservices_eab_kid.placeholder": "请输入 ACME EAB KID", "access.form.googletrustservices_eab_kid.tooltip": "这是什么?请参阅 https://cloud.google.com/certificate-manager/docs/public-ca-tutorial", @@ -264,8 +249,8 @@ "access.form.larkbot_webhook_url.label": "飞书群机器人 Webhook 地址", "access.form.larkbot_webhook_url.placeholder": "请输入飞书群机器人 Webhook 地址", "access.form.larkbot_webhook_url.tooltip": "这是什么?请参阅 https://www.feishu.cn/hc/zh-CN/articles/807992406756", - "access.form.lecdn_api_url.label": "LeCDN URL", - "access.form.lecdn_api_url.placeholder": "请输入 LeCDN URL", + "access.form.lecdn_server_url.label": "LeCDN 服务地址", + "access.form.lecdn_server_url.placeholder": "请输入 LeCDN 服务地址", "access.form.lecdn_api_version.label": "LeCDN 版本", "access.form.lecdn_api_version.placeholder": "请选择 LeCDN 版本", "access.form.lecdn_api_role.label": "LeCDN 用户角色", @@ -276,9 +261,6 @@ "access.form.lecdn_username.placeholder": "请输入 LeCDN 用户名", "access.form.lecdn_password.label": "LeCDN 用户密码", "access.form.lecdn_password.placeholder": "请输入 LeCDN 用户密码", - "access.form.lecdn_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.lecdn_allow_insecure_conns.switch.on": "允许", - "access.form.lecdn_allow_insecure_conns.switch.off": "不允许", "access.form.mattermost_server_url.label": "Mattermost 服务地址", "access.form.mattermost_server_url.placeholder": "请输入 Mattermost 服务地址", "access.form.mattermost_username.label": "Mattermost 用户名", @@ -324,25 +306,19 @@ "access.form.porkbun_secret_api_key.label": "Porkbun Secret API Key", "access.form.porkbun_secret_api_key.placeholder": "请输入 Porkbun Secret API Key", "access.form.porkbun_secret_api_key.tooltip": "这是什么?请参阅 https://porkbun.com/api/json/v3/documentation", - "access.form.powerdns_api_url.label": "PowerDNS URL", - "access.form.powerdns_api_url.placeholder": "请输入 PowerDNS URL", + "access.form.powerdns_server_url.label": "PowerDNS 服务地址", + "access.form.powerdns_server_url.placeholder": "请输入 PowerDNS 服务地址", "access.form.powerdns_api_key.label": "PowerDNS API Key", "access.form.powerdns_api_key.placeholder": "请输入 PowerDNS API Key", "access.form.powerdns_api_key.tooltip": "这是什么?请参阅 https://doc.powerdns.com/authoritative/http-api/index.html#enabling-the-api", - "access.form.powerdns_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.powerdns_allow_insecure_conns.switch.on": "允许", - "access.form.powerdns_allow_insecure_conns.switch.off": "不允许", - "access.form.proxmoxve_api_url.label": "Proxmox VE URL", - "access.form.proxmoxve_api_url.placeholder": "请输入 Proxmox VE URL", + "access.form.proxmoxve_server_url.label": "Proxmox VE 服务地址", + "access.form.proxmoxve_server_url.placeholder": "请输入 Proxmox VE 服务地址", "access.form.proxmoxve_api_token.label": "Proxmox VE API Token", "access.form.proxmoxve_api_token.placeholder": "请输入 Proxmox VE API Token", "access.form.proxmoxve_api_token.tooltip": "这是什么?请参阅 https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens", "access.form.proxmoxve_api_token_secret.label": "Proxmox VE API Token Secret(可选)", "access.form.proxmoxve_api_token_secret.placeholder": "请输入 Proxmox VE API Token Secret", "access.form.proxmoxve_api_token_secret.tooltip": "这是什么?请参阅 https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens", - "access.form.proxmoxve_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.proxmoxve_allow_insecure_conns.switch.on": "允许", - "access.form.proxmoxve_allow_insecure_conns.switch.off": "不允许", "access.form.qiniu_access_key.label": "七牛云 AccessKey", "access.form.qiniu_access_key.placeholder": "请输入七牛云 AccessKey", "access.form.qiniu_access_key.tooltip": "这是什么?请参阅 https://portal.qiniu.com/", @@ -352,25 +328,19 @@ "access.form.rainyun_api_key.label": "雨云 API 密钥", "access.form.rainyun_api_key.placeholder": "请输入雨云 API 密钥", "access.form.rainyun_api_key.tooltip": "这是什么?请参阅 https://app.rainyun.com/account/settings/api-key", - "access.form.ratpanel_api_url.label": "耗子面板 URL", - "access.form.ratpanel_api_url.placeholder": "请输入耗子面板 URL", + "access.form.ratpanel_server_url.label": "耗子面板服务地址", + "access.form.ratpanel_server_url.placeholder": "请输入耗子面板服务地址", "access.form.ratpanel_access_token_id.label": "耗子面板 AccessToken ID", "access.form.ratpanel_access_token_id.placeholder": "请输入耗子面板 AccessToken ID", "access.form.ratpanel_access_token_id.tooltip": "这是什么?请参阅 https://ratpanel.github.io/advanced/api.html", "access.form.ratpanel_access_token.label": "耗子面板 AccessToken", "access.form.ratpanel_access_token.placeholder": "请输入耗子面板 AccessToken", "access.form.ratpanel_access_token.tooltip": "这是什么?请参阅 https://ratpanel.github.io/advanced/api.html", - "access.form.ratpanel_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.ratpanel_allow_insecure_conns.switch.on": "允许", - "access.form.ratpanel_allow_insecure_conns.switch.off": "不允许", - "access.form.safeline_api_url.label": "雷池 URL", - "access.form.safeline_api_url.placeholder": "请输入雷池 URL", + "access.form.safeline_server_url.label": "雷池服务地址", + "access.form.safeline_server_url.placeholder": "请输入雷池服务地址", "access.form.safeline_api_token.label": "雷池 API Token", "access.form.safeline_api_token.placeholder": "请输入雷池 API Token", "access.form.safeline_api_token.tooltip": "这是什么?请参阅 https://docs.waf-ce.chaitin.cn/zh/更多技术文档/OPENAPI", - "access.form.safeline_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.safeline_allow_insecure_conns.switch.on": "允许", - "access.form.safeline_allow_insecure_conns.switch.off": "不允许", "access.form.ssh_host.label": "服务器地址", "access.form.ssh_host.placeholder": "请输入服务器地址", "access.form.ssh_port.label": "服务器端口", @@ -466,9 +436,6 @@ "access.form.webhook_preset_data.option.pushplus.label": "PushPlus 推送加", "access.form.webhook_preset_data.option.serverchan.label": "Server 酱", "access.form.webhook_preset_data.option.common.label": "通用模板", - "access.form.webhook_allow_insecure_conns.label": "忽略 SSL/TLS 证书错误", - "access.form.webhook_allow_insecure_conns.switch.on": "允许", - "access.form.webhook_allow_insecure_conns.switch.off": "不允许", "access.form.wecombot_webhook_url.label": "企业微信群机器人 Webhook 地址", "access.form.wecombot_webhook_url.placeholder": "请输入企业微信群机器人 Webhook 地址", "access.form.wecombot_webhook_url.tooltip": "这是什么?请参阅 https://open.work.weixin.qq.com/help2/pc/18401", diff --git a/ui/src/i18n/locales/zh/nls.settings.json b/ui/src/i18n/locales/zh/nls.settings.json index cde8ec0f..b3da01aa 100644 --- a/ui/src/i18n/locales/zh/nls.settings.json +++ b/ui/src/i18n/locales/zh/nls.settings.json @@ -28,8 +28,8 @@ "settings.notification.channel.switch.off": "停用", "settings.notification.push_test.button": "推送测试消息", "settings.notification.push_test.pushed": "已推送", - "settings.notification.channel.form.bark_server_url.label": "服务器地址", - "settings.notification.channel.form.bark_server_url.placeholder": "请输入服务器地址", + "settings.notification.channel.form.bark_server_url.label": "服务地址", + "settings.notification.channel.form.bark_server_url.placeholder": "请输入服务地址", "settings.notification.channel.form.bark_server_url.tooltip": "这是什么?请参阅 https://bark.day.app/

为空时,将使用 Bark 默认服务器。", "settings.notification.channel.form.bark_device_key.label": "设备密钥", "settings.notification.channel.form.bark_device_key.placeholder": "请输入设备密钥", From b798b824dbdea7d4dac581c7b976927429fcf8eb Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Sun, 25 May 2025 23:05:08 +0800 Subject: [PATCH 03/24] refactor(ui): MultipleSplitValueInput --- ui/src/components/MultipleSplitValueInput.tsx | 108 ++++++++++++ .../workflow/node/ApplyNodeConfigForm.tsx | 152 ++--------------- ...loyNodeConfigFormAliyunCASDeployConfig.tsx | 161 ++---------------- ...ployNodeConfigFormBaotaPanelSiteConfig.tsx | 84 ++------- ...eConfigFormTencentCloudSSLDeployConfig.tsx | 85 ++------- .../DeployNodeConfigFormWangsuCDNConfig.tsx | 85 ++------- .../i18n/locales/en/nls.workflow.nodes.json | 2 + .../i18n/locales/zh/nls.workflow.nodes.json | 2 + 8 files changed, 178 insertions(+), 501 deletions(-) create mode 100644 ui/src/components/MultipleSplitValueInput.tsx diff --git a/ui/src/components/MultipleSplitValueInput.tsx b/ui/src/components/MultipleSplitValueInput.tsx new file mode 100644 index 00000000..5f94ec7d --- /dev/null +++ b/ui/src/components/MultipleSplitValueInput.tsx @@ -0,0 +1,108 @@ +import { type ChangeEvent, useEffect } from "react"; +import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; +import { nanoid } from "@ant-design/pro-components"; +import { useControllableValue } from "ahooks"; +import { Button, Form, Input, type InputProps, Space } from "antd"; + +import { useAntdForm } from "@/hooks"; +import ModalForm from "./ModalForm"; +import MultipleInput from "./MultipleInput"; + +type SplitOptions = { + removeEmpty?: boolean; + trim?: boolean; +}; + +export type MultipleSplitValueInputProps = Omit & { + defaultValue?: string; + delimiter?: string; + maxCount?: number; + minCount?: number; + modalTitle?: string; + modalWidth?: number; + placeholderInModal?: string; + showSortButton?: boolean; + splitOptions?: SplitOptions; + value?: string[]; + onChange?: (value: string) => void; +}; + +const DEFAULT_DELIMITER = ";"; + +const MultipleSplitValueInput = ({ + className, + style, + delimiter = DEFAULT_DELIMITER, + disabled, + maxCount, + minCount, + modalTitle, + modalWidth = 480, + placeholder, + placeholderInModal, + showSortButton = true, + splitOptions = {}, + onClear, + ...props +}: MultipleSplitValueInputProps) => { + const [value, setValue] = useControllableValue(props, { + valuePropName: "value", + defaultValuePropName: "defaultValue", + trigger: "onChange", + }); + + const { form: formInst, formProps } = useAntdForm({ + name: "componentMultipleSplitValueInput_" + nanoid(), + initialValues: { value: value?.split(delimiter) }, + onSubmit: (values) => { + const temp = values.value ?? []; + if (splitOptions.trim) { + temp.map((e) => e.trim()); + } + if (splitOptions.removeEmpty) { + temp.filter((e) => !!e); + } + + setValue(temp.join(delimiter)); + }, + }); + + useEffect(() => { + formInst.setFieldValue("value", value?.split(delimiter)); + }, [delimiter, value]); + + const handleChange = (e: ChangeEvent) => { + setValue(e.target.value); + }; + + const handleClear = () => { + setValue(""); + onClear?.(); + }; + + return ( + + + + + + } + validateTrigger="onSubmit" + width={modalWidth} + > + + + + + + ); +}; + +export default MultipleSplitValueInput; diff --git a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx index 22cb57d5..7faa148e 100644 --- a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx @@ -1,12 +1,7 @@ import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; -import { - FormOutlined as FormOutlinedIcon, - PlusOutlined as PlusOutlinedIcon, - QuestionCircleOutlined as QuestionCircleOutlinedIcon, - RightOutlined as RightOutlinedIcon, -} from "@ant-design/icons"; +import { PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon, RightOutlined as RightOutlinedIcon } from "@ant-design/icons"; import { useControllableValue } from "ahooks"; import { AutoComplete, @@ -19,7 +14,6 @@ import { Input, InputNumber, Select, - Space, Switch, Tooltip, Typography, @@ -29,8 +23,7 @@ import { z } from "zod"; import AccessEditModal from "@/components/access/AccessEditModal"; import AccessSelect from "@/components/access/AccessSelect"; -import ModalForm from "@/components/ModalForm"; -import MultipleInput from "@/components/MultipleInput"; +import MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; import ACMEDns01ProviderSelect from "@/components/provider/ACMEDns01ProviderSelect"; import CAProviderSelect from "@/components/provider/CAProviderSelect"; import Show from "@/components/Show"; @@ -152,11 +145,9 @@ const ApplyNodeConfigForm = forwardRef("domains", formInst); const fieldProvider = Form.useWatch("provider", { form: formInst, preserve: true }); const fieldProviderAccessId = Form.useWatch("providerAccessId", formInst); const fieldCAProvider = Form.useWatch("caProvider", formInst); - const fieldNameservers = Form.useWatch("nameservers", formInst); const [showProvider, setShowProvider] = useState(false); useEffect(() => { @@ -294,25 +285,17 @@ const ApplyNodeConfigForm = forwardRef
} > - - - - - - - - } - onChange={(v) => { - formInst.setFieldValue("domains", v); - }} - /> - + } > - - - { - formInst.setFieldValue("nameservers", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("nameservers", undefined); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("nameservers", value); - }} - /> - + void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - domains: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || validDomainName(e.trim(), { allowWildcard: true })); - }, t("common.errmsg.domain_invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeApplyConfigFormDomainsModalInput", - initialValues: { domains: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.domains - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - -const NameserversModalInput = memo(({ trigger, value, onChange }: { trigger?: React.ReactNode; value?: string; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - nameservers: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || validIPv4Address(e) || validIPv6Address(e) || validDomainName(e)); - }, t("common.errmsg.domain_invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeApplyConfigFormNameserversModalInput", - initialValues: { nameservers: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.nameservers - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default memo(ApplyNodeConfigForm); diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx index 4e3db0db..980e89fe 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormAliyunCASDeployConfig.tsx @@ -1,13 +1,9 @@ -import { memo } from "react"; import { useTranslation } from "react-i18next"; -import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; -import { Alert, Button, Form, type FormInstance, Input, Space } from "antd"; +import { Alert, Form, type FormInstance, Input } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; -import ModalForm from "@/components/ModalForm"; -import MultipleInput from "@/components/MultipleInput"; -import { useAntdForm } from "@/hooks"; +import MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; type DeployNodeConfigFormAliyunCASDeployConfigFieldValues = Nullish<{ region: string; @@ -61,9 +57,6 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({ }); const formRule = createSchemaFieldRule(formSchema); - const fieldResourceIds = Form.useWatch("resourceIds", formInst); - const fieldContactIds = Form.useWatch("contactIds", formInst); - const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); }; @@ -87,69 +80,31 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({ } > - - - { - formInst.setFieldValue("resourceIds", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("resourceIds", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("resourceIds", value); - }} - /> - + } > - - - { - formInst.setFieldValue("contactIds", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("contactIds", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("contactIds", value); - }} - /> - + @@ -159,84 +114,4 @@ const DeployNodeConfigFormAliyunCASDeployConfig = ({ ); }; -const ResourceIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - resourceIds: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || /^[1-9]\d*$/.test(e)); - }, t("workflow_node.deploy.form.aliyun_cas_deploy_resource_ids.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormAliyunCASResourceIdsModalInput", - initialValues: { resourceIds: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.resourceIds - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - -const ContactIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - contactIds: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || /^[1-9]\d*$/.test(e)); - }, t("workflow_node.deploy.form.aliyun_cas_deploy_contact_ids.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormAliyunCASDeploymentJobContactIdsModalInput", - initialValues: { contactIds: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.contactIds - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default DeployNodeConfigFormAliyunCASDeployConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx index acc9d187..54507cdb 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx @@ -1,14 +1,10 @@ -import { memo } from "react"; import { useTranslation } from "react-i18next"; -import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; -import { Button, Form, type FormInstance, Input, Select, Space } from "antd"; +import { Form, type FormInstance, Input, Select } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; -import ModalForm from "@/components/ModalForm"; -import MultipleInput from "@/components/MultipleInput"; +import MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; import Show from "@/components/Show"; -import { useAntdForm } from "@/hooks"; type DeployNodeConfigFormBaotaPanelSiteConfigFieldValues = Nullish<{ siteType: string; @@ -71,7 +67,6 @@ const DeployNodeConfigFormBaotaPanelSiteConfig = ({ const formRule = createSchemaFieldRule(formSchema); const fieldSiteType = Form.useWatch("siteType", formInst); - const fieldSiteNames = Form.useWatch("siteNames", formInst); const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); @@ -110,80 +105,21 @@ const DeployNodeConfigFormBaotaPanelSiteConfig = ({ } > - - - { - formInst.setFieldValue("siteNames", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("siteNames", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("siteNames", value); - }} - /> - + ); }; -const SiteNamesModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - siteNames: z.array(z.string()).refine((v) => { - return v.every((e) => !!e?.trim()); - }, t("workflow_node.deploy.form.baotapanel_site_names.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormBaotaPanelSiteNamesModalInput", - initialValues: { siteNames: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.siteNames - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default DeployNodeConfigFormBaotaPanelSiteConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx index 2a4e7d12..4fcb4e76 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormTencentCloudSSLDeployConfig.tsx @@ -1,13 +1,9 @@ -import { memo } from "react"; import { useTranslation } from "react-i18next"; -import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; -import { Alert, AutoComplete, Button, Form, type FormInstance, Input, Space } from "antd"; +import { Alert, AutoComplete, Form, type FormInstance, Input } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; -import ModalForm from "@/components/ModalForm"; -import MultipleInput from "@/components/MultipleInput"; -import { useAntdForm } from "@/hooks"; +import MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; type DeployNodeConfigFormTencentCloudSSLDeployConfigFieldValues = Nullish<{ region: string; @@ -56,8 +52,6 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({ }); const formRule = createSchemaFieldRule(formSchema); - const fieldResourceIds = Form.useWatch("resourceIds", formInst); - const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); }; @@ -94,36 +88,17 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({ } > - - - { - formInst.setFieldValue("resourceIds", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("resourceIds", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("resourceIds", value); - }} - /> - + @@ -133,44 +108,4 @@ const DeployNodeConfigFormTencentCloudSSLDeployConfig = ({ ); }; -const ResourceIdsModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - resourceIds: z.array(z.string()).refine((v) => { - return v.every((e) => !e?.trim() || /^[A-Za-z0-9*._-|]+$/.test(e)); - }, t("workflow_node.deploy.form.tencentcloud_ssl_deploy_resource_ids.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormTencentCloudSSLDeployResourceIdsModalInput", - initialValues: { resourceIds: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.resourceIds - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default DeployNodeConfigFormTencentCloudSSLDeployConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx index 57d0d381..d64e6eba 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormWangsuCDNConfig.tsx @@ -1,13 +1,9 @@ -import { memo } from "react"; import { useTranslation } from "react-i18next"; -import { FormOutlined as FormOutlinedIcon } from "@ant-design/icons"; -import { Button, Form, type FormInstance, Input, Space } from "antd"; +import { Form, type FormInstance } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; -import ModalForm from "@/components/ModalForm"; -import MultipleInput from "@/components/MultipleInput"; -import { useAntdForm } from "@/hooks"; +import MultipleSplitValueInput from "@/components/MultipleSplitValueInput"; import { validDomainName } from "@/utils/validators"; type DeployNodeConfigFormWangsuCDNConfigFieldValues = Nullish<{ @@ -52,8 +48,6 @@ const DeployNodeConfigFormWangsuCDNConfig = ({ }); const formRule = createSchemaFieldRule(formSchema); - const fieldDomains = Form.useWatch("domains", formInst); - const handleFormChange = (_: unknown, values: z.infer) => { onValuesChange?.(values); }; @@ -68,79 +62,20 @@ const DeployNodeConfigFormWangsuCDNConfig = ({ onValuesChange={handleFormChange} > } > - - - { - formInst.setFieldValue("domains", e.target.value); - }} - onClear={() => { - formInst.setFieldValue("domains", ""); - }} - /> - - - - - } - onChange={(value) => { - formInst.setFieldValue("domains", value); - }} - /> - + ); }; -const SiteNamesModalInput = memo(({ value, trigger, onChange }: { value?: string; trigger?: React.ReactNode; onChange?: (value: string) => void }) => { - const { t } = useTranslation(); - - const formSchema = z.object({ - domains: z.array(z.string()).refine((v) => { - return v.every((e) => validDomainName(e)); - }, t("workflow_node.deploy.form.wangsu_cdn_domains.errmsg.invalid")), - }); - const formRule = createSchemaFieldRule(formSchema); - const { form: formInst, formProps } = useAntdForm({ - name: "workflowNodeDeployConfigFormWangsuCDNNamesModalInput", - initialValues: { domains: value?.split(MULTIPLE_INPUT_DELIMITER) }, - onSubmit: (values) => { - onChange?.( - values.domains - .map((e) => e.trim()) - .filter((e) => !!e) - .join(MULTIPLE_INPUT_DELIMITER) - ); - }, - }); - - return ( - - - - - - ); -}); - export default DeployNodeConfigFormWangsuCDNConfig; diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 80237287..ca60aedb 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -1,9 +1,11 @@ { "workflow_node.action.configure_node": "Configure node", "workflow_node.action.add_node": "Add node", + "workflow_node.action.duplicate_node": "Duplicate node", "workflow_node.action.rename_node": "Rename node", "workflow_node.action.remove_node": "Delete node", "workflow_node.action.add_branch": "Add branch", + "workflow_node.action.duplicate_branch": "Duplicate branch", "workflow_node.action.rename_branch": "Rename branch", "workflow_node.action.remove_branch": "Delete branch", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index faf40816..3da2c838 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -1,9 +1,11 @@ { "workflow_node.action.configure_node": "配置节点", "workflow_node.branch.add_node": "添加节点", + "workflow_node.action.duplicate_node": "复制节点", "workflow_node.action.rename_node": "重命名", "workflow_node.action.remove_node": "删除节点", "workflow_node.action.add_branch": "添加并行分支", + "workflow_node.action.duplicate_branch": "复制分支", "workflow_node.action.rename_branch": "重命名", "workflow_node.action.remove_branch": "删除分支", From 0909671be65fdf23025280fdef507b49ca3e0d6c Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Sun, 25 May 2025 23:32:41 +0800 Subject: [PATCH 04/24] refactor: clean code --- .../providers/1panel-console/1panel_console.go | 8 ++++---- .../core/deployer/providers/1panel-site/1panel_site.go | 8 ++++---- .../providers/baotapanel-console/baotapanel_console.go | 8 ++++---- .../providers/baotapanel-site/baotapanel_site.go | 8 ++++---- .../providers/baotawaf-console/baotawaf_console.go | 8 ++++---- .../deployer/providers/baotawaf-site/baotawaf_site.go | 8 ++++---- internal/pkg/core/deployer/providers/cdnfly/cdnfly.go | 8 ++++---- .../pkg/core/deployer/providers/flexcdn/flexcdn.go | 8 ++++---- internal/pkg/core/deployer/providers/goedge/goedge.go | 8 ++++---- internal/pkg/core/deployer/providers/lecdn/lecdn.go | 10 +++++----- .../pkg/core/deployer/providers/proxmoxve/proxmoxve.go | 8 ++++---- .../providers/ratpanel-console/ratpanel_console.go | 8 ++++---- .../deployer/providers/ratpanel-site/ratpanel_site.go | 8 ++++---- .../pkg/core/deployer/providers/safeline/safeline.go | 8 ++++---- .../core/uploader/providers/1panel-ssl/1panel_ssl.go | 8 ++++---- internal/pkg/sdk3rd/1panel/client.go | 4 ++-- internal/pkg/sdk3rd/btpanel/client.go | 4 ++-- internal/pkg/sdk3rd/btwaf/client.go | 4 ++-- internal/pkg/sdk3rd/cdnfly/client.go | 4 ++-- internal/pkg/sdk3rd/flexcdn/client.go | 4 ++-- internal/pkg/sdk3rd/goedge/client.go | 4 ++-- internal/pkg/sdk3rd/lecdn/v3/client/client.go | 4 ++-- internal/pkg/sdk3rd/lecdn/v3/master/client.go | 4 ++-- internal/pkg/sdk3rd/ratpanel/client.go | 4 ++-- internal/pkg/sdk3rd/safeline/client.go | 4 ++-- ui/src/components/workflow/node/_SharedNode.tsx | 7 ------- ui/src/i18n/locales/en/nls.workflow.nodes.json | 2 -- ui/src/i18n/locales/zh/nls.workflow.nodes.json | 2 -- 28 files changed, 81 insertions(+), 92 deletions(-) diff --git a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go index 0f7efed1..cdeb8af5 100644 --- a/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go +++ b/internal/pkg/core/deployer/providers/1panel-console/1panel_console.go @@ -82,9 +82,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid 1panel api url") +func createSdkClient(serverUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid 1panel server url") } if apiVersion == "" { @@ -95,7 +95,7 @@ func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*on return nil, errors.New("invalid 1panel api key") } - client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) + client := onepanelsdk.NewClient(serverUrl, apiVersion, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go index a546c1bb..30414689 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go @@ -177,9 +177,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid 1panel api url") +func createSdkClient(serverUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid 1panel server url") } if apiVersion == "" { @@ -190,7 +190,7 @@ func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*on return nil, errors.New("invalid 1panel api key") } - client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) + client := onepanelsdk.NewClient(serverUrl, apiVersion, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go index b5deb005..5709f82d 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go +++ b/internal/pkg/core/deployer/providers/baotapanel-console/baotapanel_console.go @@ -82,16 +82,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid baota api url") +func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid baota server url") } if apiKey == "" { return nil, errors.New("invalid baota api key") } - client := btsdk.NewClient(apiUrl, apiKey) + client := btsdk.NewClient(serverUrl, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go index f9936e2d..d6ee1533 100644 --- a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel_site.go @@ -124,16 +124,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid baota api url") +func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid baota server url") } if apiKey == "" { return nil, errors.New("invalid baota api key") } - client := btsdk.NewClient(apiUrl, apiKey) + client := btsdk.NewClient(serverUrl, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go index bf8a3892..482ca8e4 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go +++ b/internal/pkg/core/deployer/providers/baotawaf-console/baotawaf_console.go @@ -70,16 +70,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid baota api url") +func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid baota server url") } if apiKey == "" { return nil, errors.New("invalid baota api key") } - client := btsdk.NewClient(apiUrl, apiKey) + client := btsdk.NewClient(serverUrl, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go index faaf5681..435f7a69 100644 --- a/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go +++ b/internal/pkg/core/deployer/providers/baotawaf-site/baotawaf_site.go @@ -133,16 +133,16 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid baota api url") +func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*btsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid baota server url") } if apiKey == "" { return nil, errors.New("invalid baota api key") } - client := btsdk.NewClient(apiUrl, apiKey) + client := btsdk.NewClient(serverUrl, apiKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go index 6b7c3ec7..1ced8caf 100644 --- a/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go +++ b/internal/pkg/core/deployer/providers/cdnfly/cdnfly.go @@ -160,9 +160,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiKey, apiSecret string, skipTlsVerify bool) (*cfsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid cachefly api url") +func createSdkClient(serverUrl, apiKey, apiSecret string, skipTlsVerify bool) (*cfsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid cachefly server url") } if apiKey == "" { @@ -173,7 +173,7 @@ func createSdkClient(apiUrl, apiKey, apiSecret string, skipTlsVerify bool) (*cfs return nil, errors.New("invalid cachefly api secret") } - client := cfsdk.NewClient(apiUrl, apiKey, apiSecret) + client := cfsdk.NewClient(serverUrl, apiKey, apiSecret) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go index 7925a72a..8b692e90 100644 --- a/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go +++ b/internal/pkg/core/deployer/providers/flexcdn/flexcdn.go @@ -119,9 +119,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*flexcdnsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid flexcdn api url") +func createSdkClient(serverUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*flexcdnsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid flexcdn server url") } if apiRole != "user" && apiRole != "admin" { @@ -136,7 +136,7 @@ func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVeri return nil, errors.New("invalid flexcdn access key") } - client := flexcdnsdk.NewClient(apiUrl, apiRole, accessKeyId, accessKey) + client := flexcdnsdk.NewClient(serverUrl, apiRole, accessKeyId, accessKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/goedge/goedge.go b/internal/pkg/core/deployer/providers/goedge/goedge.go index 5b3e2dc9..25caeb01 100644 --- a/internal/pkg/core/deployer/providers/goedge/goedge.go +++ b/internal/pkg/core/deployer/providers/goedge/goedge.go @@ -119,9 +119,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*goedgesdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid goedge api url") +func createSdkClient(serverUrl, apiRole, accessKeyId, accessKey string, skipTlsVerify bool) (*goedgesdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid goedge server url") } if apiRole != "user" && apiRole != "admin" { @@ -136,7 +136,7 @@ func createSdkClient(apiUrl, apiRole, accessKeyId, accessKey string, skipTlsVeri return nil, errors.New("invalid goedge access key") } - client := goedgesdk.NewClient(apiUrl, apiRole, accessKeyId, accessKey) + client := goedgesdk.NewClient(serverUrl, apiRole, accessKeyId, accessKey) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/lecdn/lecdn.go b/internal/pkg/core/deployer/providers/lecdn/lecdn.go index 15c5dba9..c85f6558 100644 --- a/internal/pkg/core/deployer/providers/lecdn/lecdn.go +++ b/internal/pkg/core/deployer/providers/lecdn/lecdn.go @@ -141,9 +141,9 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiVersion, apiRole, username, password string, skipTlsVerify bool) (interface{}, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid lecdn api url") +func createSdkClient(serverUrl, apiVersion, apiRole, username, password string, skipTlsVerify bool) (interface{}, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid lecdn server url") } if username == "" { @@ -156,7 +156,7 @@ func createSdkClient(apiUrl, apiVersion, apiRole, username, password string, ski if apiVersion == apiVersionV3 && apiRole == apiRoleClient { // v3 版客户端 - client := leclientsdkv3.NewClient(apiUrl, username, password) + client := leclientsdkv3.NewClient(serverUrl, username, password) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } @@ -164,7 +164,7 @@ func createSdkClient(apiUrl, apiVersion, apiRole, username, password string, ski return client, nil } else if apiVersion == apiVersionV3 && apiRole == apiRoleMaster { // v3 版主控端 - client := lemastersdkv3.NewClient(apiUrl, username, password) + client := lemastersdkv3.NewClient(serverUrl, username, password) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go index 455ccf6d..349c3a16 100644 --- a/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go +++ b/internal/pkg/core/deployer/providers/proxmoxve/proxmoxve.go @@ -91,9 +91,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl, apiToken, apiTokenSecret string, skipTlsVerify bool) (*proxmox.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid pve api url") +func createSdkClient(serverUrl, apiToken, apiTokenSecret string, skipTlsVerify bool) (*proxmox.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid pve server url") } if apiToken == "" { @@ -112,7 +112,7 @@ func createSdkClient(apiUrl, apiToken, apiTokenSecret string, skipTlsVerify bool } } client := proxmox.NewClient( - strings.TrimRight(apiUrl, "/")+"/api2/json", + strings.TrimRight(serverUrl, "/")+"/api2/json", proxmox.WithHTTPClient(httpClient), proxmox.WithAPIToken(apiToken, apiTokenSecret), ) diff --git a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go index a6c560ae..651ae0ac 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go +++ b/internal/pkg/core/deployer/providers/ratpanel-console/ratpanel_console.go @@ -72,9 +72,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid ratpanel api url") +func createSdkClient(serverUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid ratpanel server url") } if accessTokenId == 0 { @@ -85,7 +85,7 @@ func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, ski return nil, errors.New("invalid ratpanel access token") } - client := rpsdk.NewClient(apiUrl, accessTokenId, accessToken) + client := rpsdk.NewClient(serverUrl, accessTokenId, accessToken) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go index a296c95e..8d605b3d 100644 --- a/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go +++ b/internal/pkg/core/deployer/providers/ratpanel-site/ratpanel_site.go @@ -79,9 +79,9 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE return &deployer.DeployResult{}, nil } -func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid ratpanel api url") +func createSdkClient(serverUrl string, accessTokenId int32, accessToken string, skipTlsVerify bool) (*rpsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid ratpanel server url") } if accessTokenId == 0 { @@ -92,7 +92,7 @@ func createSdkClient(apiUrl string, accessTokenId int32, accessToken string, ski return nil, errors.New("invalid ratpanel access token") } - client := rpsdk.NewClient(apiUrl, accessTokenId, accessToken) + client := rpsdk.NewClient(serverUrl, accessTokenId, accessToken) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/deployer/providers/safeline/safeline.go b/internal/pkg/core/deployer/providers/safeline/safeline.go index 0abee743..f1b6b039 100644 --- a/internal/pkg/core/deployer/providers/safeline/safeline.go +++ b/internal/pkg/core/deployer/providers/safeline/safeline.go @@ -98,16 +98,16 @@ func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM stri return nil } -func createSdkClient(apiUrl, apiToken string, skipTlsVerify bool) (*safelinesdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid safeline api url") +func createSdkClient(serverUrl, apiToken string, skipTlsVerify bool) (*safelinesdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid safeline server url") } if apiToken == "" { return nil, errors.New("invalid safeline api token") } - client := safelinesdk.NewClient(apiUrl, apiToken) + client := safelinesdk.NewClient(serverUrl, apiToken) if skipTlsVerify { client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) } diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go index 387944e3..c0283ccd 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go @@ -132,9 +132,9 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string, return nil, nil } -func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, error) { - if _, err := url.Parse(apiUrl); err != nil { - return nil, errors.New("invalid 1panel api url") +func createSdkClient(serverUrl, apiVersion, apiKey string) (*onepanelsdk.Client, error) { + if _, err := url.Parse(serverUrl); err != nil { + return nil, errors.New("invalid 1panel server url") } if apiVersion == "" { @@ -145,6 +145,6 @@ func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, er return nil, errors.New("invalid 1panel api key") } - client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) + client := onepanelsdk.NewClient(serverUrl, apiVersion, apiKey) return client, nil } diff --git a/internal/pkg/sdk3rd/1panel/client.go b/internal/pkg/sdk3rd/1panel/client.go index 003203d3..3fe549a0 100644 --- a/internal/pkg/sdk3rd/1panel/client.go +++ b/internal/pkg/sdk3rd/1panel/client.go @@ -19,13 +19,13 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiVersion, apiKey string) *Client { +func NewClient(serverUrl, apiVersion, apiKey string) *Client { if apiVersion == "" { apiVersion = "v1" } client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/") + "/api/" + apiVersion). + SetBaseURL(strings.TrimRight(serverUrl, "/") + "/api/" + apiVersion). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { timestamp := fmt.Sprintf("%d", time.Now().Unix()) tokenMd5 := md5.Sum([]byte("1panel" + apiKey + timestamp)) diff --git a/internal/pkg/sdk3rd/btpanel/client.go b/internal/pkg/sdk3rd/btpanel/client.go index 1da625da..aafee04f 100644 --- a/internal/pkg/sdk3rd/btpanel/client.go +++ b/internal/pkg/sdk3rd/btpanel/client.go @@ -19,9 +19,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiKey string) *Client { +func NewClient(serverUrl, apiKey string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")) + SetBaseURL(strings.TrimRight(serverUrl, "/")) return &Client{ apiKey: apiKey, diff --git a/internal/pkg/sdk3rd/btwaf/client.go b/internal/pkg/sdk3rd/btwaf/client.go index 5ae545cc..083db0c1 100644 --- a/internal/pkg/sdk3rd/btwaf/client.go +++ b/internal/pkg/sdk3rd/btwaf/client.go @@ -17,9 +17,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiKey string) *Client { +func NewClient(serverUrl, apiKey string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/") + "/api"). + SetBaseURL(strings.TrimRight(serverUrl, "/") + "/api"). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { timestamp := fmt.Sprintf("%d", time.Now().Unix()) keyMd5 := md5.Sum([]byte(apiKey)) diff --git a/internal/pkg/sdk3rd/cdnfly/client.go b/internal/pkg/sdk3rd/cdnfly/client.go index c8753ed5..2dabf6fd 100644 --- a/internal/pkg/sdk3rd/cdnfly/client.go +++ b/internal/pkg/sdk3rd/cdnfly/client.go @@ -15,9 +15,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiKey, apiSecret string) *Client { +func NewClient(serverUrl, apiKey, apiSecret string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")). + SetBaseURL(strings.TrimRight(serverUrl, "/")). SetHeader("api-key", apiKey). SetHeader("api-secret", apiSecret) diff --git a/internal/pkg/sdk3rd/flexcdn/client.go b/internal/pkg/sdk3rd/flexcdn/client.go index beae469a..b478ffac 100644 --- a/internal/pkg/sdk3rd/flexcdn/client.go +++ b/internal/pkg/sdk3rd/flexcdn/client.go @@ -24,14 +24,14 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiRole, accessKeyId, accessKey string) *Client { +func NewClient(serverUrl, apiRole, accessKeyId, accessKey string) *Client { client := &Client{ apiRole: apiRole, accessKeyId: accessKeyId, accessKey: accessKey, } client.client = resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")). + SetBaseURL(strings.TrimRight(serverUrl, "/")). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { if client.accessToken != "" { req.Header.Set("X-Cloud-Access-Token", client.accessToken) diff --git a/internal/pkg/sdk3rd/goedge/client.go b/internal/pkg/sdk3rd/goedge/client.go index 3dc961e3..3cd4900a 100644 --- a/internal/pkg/sdk3rd/goedge/client.go +++ b/internal/pkg/sdk3rd/goedge/client.go @@ -24,14 +24,14 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiRole, accessKeyId, accessKey string) *Client { +func NewClient(serverUrl, apiRole, accessKeyId, accessKey string) *Client { client := &Client{ apiRole: apiRole, accessKeyId: accessKeyId, accessKey: accessKey, } client.client = resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")). + SetBaseURL(strings.TrimRight(serverUrl, "/")). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { if client.accessToken != "" { req.Header.Set("X-Edge-Access-Token", client.accessToken) diff --git a/internal/pkg/sdk3rd/lecdn/v3/client/client.go b/internal/pkg/sdk3rd/lecdn/v3/client/client.go index ad3a752e..3fa822eb 100644 --- a/internal/pkg/sdk3rd/lecdn/v3/client/client.go +++ b/internal/pkg/sdk3rd/lecdn/v3/client/client.go @@ -22,13 +22,13 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, username, password string) *Client { +func NewClient(serverUrl, username, password string) *Client { client := &Client{ username: username, password: password, } client.client = resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/") + "/prod-api"). + SetBaseURL(strings.TrimRight(serverUrl, "/") + "/prod-api"). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { if client.accessToken != "" { req.Header.Set("Authorization", "Bearer "+client.accessToken) diff --git a/internal/pkg/sdk3rd/lecdn/v3/master/client.go b/internal/pkg/sdk3rd/lecdn/v3/master/client.go index 2da0c0c4..ee1abaca 100644 --- a/internal/pkg/sdk3rd/lecdn/v3/master/client.go +++ b/internal/pkg/sdk3rd/lecdn/v3/master/client.go @@ -22,13 +22,13 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, username, password string) *Client { +func NewClient(serverUrl, username, password string) *Client { client := &Client{ username: username, password: password, } client.client = resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/") + "/prod-api"). + SetBaseURL(strings.TrimRight(serverUrl, "/") + "/prod-api"). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { if client.accessToken != "" { req.Header.Set("Authorization", "Bearer "+client.accessToken) diff --git a/internal/pkg/sdk3rd/ratpanel/client.go b/internal/pkg/sdk3rd/ratpanel/client.go index 47202a04..f1d20359 100644 --- a/internal/pkg/sdk3rd/ratpanel/client.go +++ b/internal/pkg/sdk3rd/ratpanel/client.go @@ -20,9 +20,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost string, accessTokenId int32, accessToken string) *Client { +func NewClient(serverUrl string, accessTokenId int32, accessToken string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")+"/api"). + SetBaseURL(strings.TrimRight(serverUrl, "/")+"/api"). SetHeader("Accept", "application/json"). SetHeader("Content-Type", "application/json"). SetPreRequestHook(func(c *resty.Client, req *http.Request) error { diff --git a/internal/pkg/sdk3rd/safeline/client.go b/internal/pkg/sdk3rd/safeline/client.go index efcd3bd6..93d884a3 100644 --- a/internal/pkg/sdk3rd/safeline/client.go +++ b/internal/pkg/sdk3rd/safeline/client.go @@ -14,9 +14,9 @@ type Client struct { client *resty.Client } -func NewClient(apiHost, apiToken string) *Client { +func NewClient(serverUrl, apiToken string) *Client { client := resty.New(). - SetBaseURL(strings.TrimRight(apiHost, "/")). + SetBaseURL(strings.TrimRight(serverUrl, "/")). SetHeader("X-SLCE-API-TOKEN", apiToken) return &Client{ diff --git a/ui/src/components/workflow/node/_SharedNode.tsx b/ui/src/components/workflow/node/_SharedNode.tsx index 7bec1e90..44c894ef 100644 --- a/ui/src/components/workflow/node/_SharedNode.tsx +++ b/ui/src/components/workflow/node/_SharedNode.tsx @@ -4,7 +4,6 @@ import { CloseCircleOutlined as CloseCircleOutlinedIcon, EllipsisOutlined as EllipsisOutlinedIcon, FormOutlined as FormOutlinedIcon, - MergeOutlined as MergeOutlinedIcon, MoreOutlined as MoreOutlinedIcon, } from "@ant-design/icons"; import { useControllableValue } from "ahooks"; @@ -149,12 +148,6 @@ const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, setTimeout(() => nameInputRef.current?.focus(), 1); }, }, - // { - // key: "duplicate", - // disabled: disabled || isNodeReadOnly(node), - // label: isNodeBranchLike(node) ? t("workflow_node.action.duplicate_branch") : t("workflow_node.action.duplicate_node"), - // icon: , - // }, { type: "divider", }, diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index ca60aedb..80237287 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -1,11 +1,9 @@ { "workflow_node.action.configure_node": "Configure node", "workflow_node.action.add_node": "Add node", - "workflow_node.action.duplicate_node": "Duplicate node", "workflow_node.action.rename_node": "Rename node", "workflow_node.action.remove_node": "Delete node", "workflow_node.action.add_branch": "Add branch", - "workflow_node.action.duplicate_branch": "Duplicate branch", "workflow_node.action.rename_branch": "Rename branch", "workflow_node.action.remove_branch": "Delete branch", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 3da2c838..faf40816 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -1,11 +1,9 @@ { "workflow_node.action.configure_node": "配置节点", "workflow_node.branch.add_node": "添加节点", - "workflow_node.action.duplicate_node": "复制节点", "workflow_node.action.rename_node": "重命名", "workflow_node.action.remove_node": "删除节点", "workflow_node.action.add_branch": "添加并行分支", - "workflow_node.action.duplicate_branch": "复制分支", "workflow_node.action.rename_branch": "重命名", "workflow_node.action.remove_branch": "删除分支", From 7cd036f41ec8bd8a17ffbd5cbb8567a577c71c3d Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 09:26:11 +0800 Subject: [PATCH 05/24] fix: #724 --- .../deployer/providers/1panel-site/1panel_site.go | 7 ++++--- .../core/uploader/providers/1panel-ssl/1panel_ssl.go | 11 +++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go index 690e5242..3a92bb7c 100644 --- a/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go +++ b/internal/pkg/core/deployer/providers/1panel-site/1panel_site.go @@ -55,9 +55,10 @@ func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { } uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ - ApiUrl: config.ApiUrl, - ApiVersion: config.ApiVersion, - ApiKey: config.ApiKey, + ApiUrl: config.ApiUrl, + ApiVersion: config.ApiVersion, + ApiKey: config.ApiKey, + AllowInsecureConnections: config.AllowInsecureConnections, }) if err != nil { return nil, fmt.Errorf("failed to create ssl uploader: %w", err) diff --git a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go index 63900125..2a28c82c 100644 --- a/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go +++ b/internal/pkg/core/uploader/providers/1panel-ssl/1panel_ssl.go @@ -2,6 +2,7 @@ package onepanelssl import ( "context" + "crypto/tls" "errors" "fmt" "log/slog" @@ -20,6 +21,8 @@ type UploaderConfig struct { ApiVersion string `json:"apiVersion"` // 1Panel 接口密钥。 ApiKey string `json:"apiKey"` + // 是否允许不安全的连接。 + AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } type UploaderProvider struct { @@ -35,7 +38,7 @@ func NewUploader(config *UploaderConfig) (*UploaderProvider, error) { panic("config is nil") } - client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey) + client, err := createSdkClient(config.ApiUrl, config.ApiVersion, config.ApiKey, config.AllowInsecureConnections) if err != nil { return nil, fmt.Errorf("failed to create sdk client: %w", err) } @@ -132,7 +135,7 @@ func (u *UploaderProvider) getCertIfExists(ctx context.Context, certPEM string, return nil, nil } -func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, error) { +func createSdkClient(apiUrl, apiVersion, apiKey string, skipTlsVerify bool) (*onepanelsdk.Client, error) { if _, err := url.Parse(apiUrl); err != nil { return nil, errors.New("invalid 1panel api url") } @@ -146,5 +149,9 @@ func createSdkClient(apiUrl, apiVersion, apiKey string) (*onepanelsdk.Client, er } client := onepanelsdk.NewClient(apiUrl, apiVersion, apiKey) + if skipTlsVerify { + client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}) + } + return client, nil } From 11ff80ab28beb088db079bd77904726d8877326e Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 09:27:33 +0800 Subject: [PATCH 06/24] fix: #723 --- ui/src/i18n/locales/en/nls.workflow.nodes.json | 6 +++--- ui/src/i18n/locales/zh/nls.workflow.nodes.json | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 80237287..e0602370 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -109,7 +109,7 @@ "workflow_node.deploy.form.certificate.placeholder": "Please select certificate", "workflow_node.deploy.form.certificate.tooltip": "The certificate to be deployed comes from the previous nodes of application or upload.", "workflow_node.deploy.form.params_config.label": "Parameter settings", - "workflow_node.deploy.form.1panel_console_auto_restart.label": "Auto restart after deployment", + "workflow_node.deploy.form.1panel_console_auto_restart.label": "Auto restart 1Panel after deployment", "workflow_node.deploy.form.1panel_site_resource_type.label": "Resource type", "workflow_node.deploy.form.1panel_site_resource_type.placeholder": "Please select resource type", "workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "Website", @@ -331,7 +331,7 @@ "workflow_node.deploy.form.baishan_cdn_certificate_id.label": "Baishan Cloud CDN certificate ID (Optional)", "workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder": "Please enter Baishan Cloud CDN certificate ID", "workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip": "For more information, see https://cdnx.console.baishan.com/#/cdn/cert", - "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "Auto restart after deployment", + "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "Auto restart aaPanel after deployment", "workflow_node.deploy.form.baotapanel_site_type.label": "aaPanel site type", "workflow_node.deploy.form.baotapanel_site_type.placeholder": "Please select aaPanel site type", "workflow_node.deploy.form.baotapanel_site_type.option.php.label": "PHP sites", @@ -530,7 +530,7 @@ "workflow_node.deploy.form.netlify_site_id.tooltip": "For more information, see https://docs.netlify.com/api/get-started/#get-site", "workflow_node.deploy.form.proxmoxve_node_name.label": "Proxmox VE cluster node name", "workflow_node.deploy.form.proxmoxve_node_name.placeholder": "Please enter Proxmox VE cluster node name", - "workflow_node.deploy.form.proxmoxve_auto_restart.label": "Auto restart after deployment", + "workflow_node.deploy.form.proxmoxve_auto_restart.label": "Auto restart Proxmox VE after deployment", "workflow_node.deploy.form.qiniu_cdn_domain.label": "Qiniu CDN domain", "workflow_node.deploy.form.qiniu_cdn_domain.placeholder": "Please enter Qiniu CDN domain name", "workflow_node.deploy.form.qiniu_cdn_domain.tooltip": "For more information, see https://portal.qiniu.com/cdn", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index faf40816..2dbcb3ca 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -108,7 +108,7 @@ "workflow_node.deploy.form.certificate.placeholder": "请选择待部署证书", "workflow_node.deploy.form.certificate.tooltip": "待部署证书来自之前的申请或上传节点。如果选项为空请先确保前序节点配置正确。", "workflow_node.deploy.form.params_config.label": "参数设置", - "workflow_node.deploy.form.1panel_console_auto_restart.label": "部署后自动重启宝塔面板服务", + "workflow_node.deploy.form.1panel_console_auto_restart.label": "部署后自动重启 1Panel 服务", "workflow_node.deploy.form.1panel_site_resource_type.label": "证书部署方式", "workflow_node.deploy.form.1panel_site_resource_type.placeholder": "请选择证书部署方式", "workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "替换指定网站的证书", @@ -330,7 +330,7 @@ "workflow_node.deploy.form.baishan_cdn_certificate_id.label": "白山云 CDN 原证书 ID(可选)", "workflow_node.deploy.form.baishan_cdn_certificate_id.placeholder": "请输入白山云 CDN 原证书 ID", "workflow_node.deploy.form.baishan_cdn_certificate_id.tooltip": "这是什么?请参阅 https://cdnx.console.baishan.com/#/cdn/cert

不填写时,将上传新证书;否则,将替换原证书。", - "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "部署后自动重启 1Panel 服务", + "workflow_node.deploy.form.baotapanel_console_auto_restart.label": "部署后自动重启宝塔面板服务", "workflow_node.deploy.form.baotapanel_site_type.label": "宝塔面板网站类型", "workflow_node.deploy.form.baotapanel_site_type.placeholder": "请选择宝塔面板网站类型", "workflow_node.deploy.form.baotapanel_site_type.option.php.label": "PHP", From 970a1f0f7929bfb9e1f5de63cacc24b4610de012 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 10:17:25 +0800 Subject: [PATCH 07/24] fix: i18n error in AccessEditModal --- ui/src/components/access/AccessEditModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/components/access/AccessEditModal.tsx b/ui/src/components/access/AccessEditModal.tsx index 8d2d5204..a20d50b9 100644 --- a/ui/src/components/access/AccessEditModal.tsx +++ b/ui/src/components/access/AccessEditModal.tsx @@ -100,6 +100,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, .. }} afterClose={() => setOpen(false)} cancelButtonProps={{ disabled: formPending }} + cancelText={t("common.button.cancel")} closable confirmLoading={formPending} destroyOnHidden From 46b4ff73c9a64147276ef8a67c39b20d697e2f5a Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 11:29:04 +0800 Subject: [PATCH 08/24] fix: ari double renewal error --- internal/applicant/applicant.go | 17 ++++---- internal/applicant/providers.go | 4 +- internal/domain/certificate.go | 1 + internal/repository/certificate.go | 21 ++++++++++ .../workflow/node-processor/apply_node.go | 11 +++++- internal/workflow/node-processor/processor.go | 2 + .../workflow/node-processor/upload_node.go | 2 +- migrations/1748228400_upgrade.go | 39 +++++++++++++++++++ 8 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 migrations/1748228400_upgrade.go diff --git a/internal/applicant/applicant.go b/internal/applicant/applicant.go index e6a04bcd..f1200094 100644 --- a/internal/applicant/applicant.go +++ b/internal/applicant/applicant.go @@ -26,13 +26,14 @@ import ( ) type ApplyResult struct { + CSR string FullChainCertificate string IssuerCertificate string PrivateKey string ACMEAccountUrl string ACMECertUrl string ACMECertStableUrl string - CSR string + ARIReplaced bool } type Applicant interface { @@ -109,7 +110,7 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err certRepo := repository.NewCertificateRepository() lastCertificate, _ := certRepo.GetByWorkflowNodeId(context.Background(), config.Node.Id) - if lastCertificate != nil { + if lastCertificate != nil && !lastCertificate.ACMERenewed { newCertSan := slices.Clone(options.Domains) oldCertSan := strings.Split(lastCertificate.SubjectAltNames, ";") slices.Sort(newCertSan) @@ -119,8 +120,8 @@ func NewWithWorkflowNode(config ApplicantWithWorkflowNodeConfig) (Applicant, err lastCertX509, _ := certcrypto.ParsePEMCertificate([]byte(lastCertificate.Certificate)) if lastCertX509 != nil { replacedARICertId, _ := certificate.MakeARICertID(lastCertX509) - options.ReplacedARIAcct = lastCertificate.ACMEAccountUrl - options.ReplacedARICert = replacedARICertId + options.ARIReplaceAcct = lastCertificate.ACMEAccountUrl + options.ARIReplaceCert = replacedARICertId } } } @@ -235,22 +236,24 @@ func applyUseLego(legoProvider challenge.Provider, options *applicantProviderOpt Domains: options.Domains, Bundle: true, } - if options.ReplacedARIAcct == user.Registration.URI { - certRequest.ReplacesCertID = options.ReplacedARICert + if options.ARIReplaceAcct == user.Registration.URI { + certRequest.ReplacesCertID = options.ARIReplaceCert } + certResource, err := client.Certificate.Obtain(certRequest) if err != nil { return nil, err } return &ApplyResult{ + CSR: strings.TrimSpace(string(certResource.CSR)), FullChainCertificate: strings.TrimSpace(string(certResource.Certificate)), IssuerCertificate: strings.TrimSpace(string(certResource.IssuerCertificate)), PrivateKey: strings.TrimSpace(string(certResource.PrivateKey)), ACMEAccountUrl: user.Registration.URI, ACMECertUrl: certResource.CertURL, ACMECertStableUrl: certResource.CertStableURL, - CSR: strings.TrimSpace(string(certResource.CSR)), + ARIReplaced: certRequest.ReplacesCertID != "", }, nil } diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index de47ae18..175531ae 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -57,8 +57,8 @@ type applicantProviderOptions struct { DnsPropagationTimeout int32 DnsTTL int32 DisableFollowCNAME bool - ReplacedARIAcct string - ReplacedARICert string + ARIReplaceAcct string + ARIReplaceCert string } func createApplicantProvider(options *applicantProviderOptions) (challenge.Provider, error) { diff --git a/internal/domain/certificate.go b/internal/domain/certificate.go index 710ce268..b2b48fcd 100644 --- a/internal/domain/certificate.go +++ b/internal/domain/certificate.go @@ -28,6 +28,7 @@ type Certificate struct { ACMEAccountUrl string `json:"acmeAccountUrl" db:"acmeAccountUrl"` ACMECertUrl string `json:"acmeCertUrl" db:"acmeCertUrl"` ACMECertStableUrl string `json:"acmeCertStableUrl" db:"acmeCertStableUrl"` + ACMERenewed bool `json:"acmeRenewed" db:"acmeRenewed"` WorkflowId string `json:"workflowId" db:"workflowId"` WorkflowNodeId string `json:"workflowNodeId" db:"workflowNodeId"` WorkflowRunId string `json:"workflowRunId" db:"workflowRunId"` diff --git a/internal/repository/certificate.go b/internal/repository/certificate.go index 95bfd713..290d5f9f 100644 --- a/internal/repository/certificate.go +++ b/internal/repository/certificate.go @@ -77,6 +77,25 @@ func (r *CertificateRepository) GetByWorkflowNodeId(ctx context.Context, workflo return r.castRecordToModel(records[0]) } +func (r *CertificateRepository) GetByWorkflowRunId(ctx context.Context, workflowRunId string) (*domain.Certificate, error) { + records, err := app.GetApp().FindRecordsByFilter( + domain.CollectionNameCertificate, + "workflowRunId={:workflowRunId} && deleted=null", + "-created", + 1, 0, + dbx.Params{"workflowRunId": workflowRunId}, + ) + if err != nil { + return nil, err + } + + if len(records) == 0 { + return nil, domain.ErrRecordNotFound + } + + return r.castRecordToModel(records[0]) +} + func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Certificate) (*domain.Certificate, error) { collection, err := app.GetApp().FindCollectionByNameOrId(domain.CollectionNameCertificate) if err != nil { @@ -109,6 +128,7 @@ func (r *CertificateRepository) Save(ctx context.Context, certificate *domain.Ce record.Set("acmeAccountUrl", certificate.ACMEAccountUrl) record.Set("acmeCertUrl", certificate.ACMECertUrl) record.Set("acmeCertStableUrl", certificate.ACMECertStableUrl) + record.Set("acmeRenewed", certificate.ACMERenewed) record.Set("workflowId", certificate.WorkflowId) record.Set("workflowRunId", certificate.WorkflowRunId) record.Set("workflowNodeId", certificate.WorkflowNodeId) @@ -170,6 +190,7 @@ func (r *CertificateRepository) castRecordToModel(record *core.Record) (*domain. ACMEAccountUrl: record.GetString("acmeAccountUrl"), ACMECertUrl: record.GetString("acmeCertUrl"), ACMECertStableUrl: record.GetString("acmeCertStableUrl"), + ACMERenewed: record.GetBool("acmeRenewed"), WorkflowId: record.GetString("workflowId"), WorkflowRunId: record.GetString("workflowRunId"), WorkflowNodeId: record.GetString("workflowNodeId"), diff --git a/internal/workflow/node-processor/apply_node.go b/internal/workflow/node-processor/apply_node.go index ff8c573d..d38ece89 100644 --- a/internal/workflow/node-processor/apply_node.go +++ b/internal/workflow/node-processor/apply_node.go @@ -96,6 +96,15 @@ func (n *applyNode) Process(ctx context.Context) error { return err } + // 保存 ARI 记录 + if applyResult.ARIReplaced { + lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId) + if lastCertificate != nil { + lastCertificate.ACMERenewed = true + n.certRepo.Save(ctx, lastCertificate) + } + } + n.logger.Info("apply completed") return nil @@ -134,7 +143,7 @@ func (n *applyNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workflo return false, "the configuration item 'KeyAlgorithm' changed" } - lastCertificate, _ := n.certRepo.GetByWorkflowNodeId(ctx, n.node.Id) + lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId) if lastCertificate != nil { renewalInterval := time.Duration(currentNodeConfig.SkipBeforeExpiryDays) * time.Hour * 24 expirationTime := time.Until(lastCertificate.ExpireAt) diff --git a/internal/workflow/node-processor/processor.go b/internal/workflow/node-processor/processor.go index 4523b13a..f98aebae 100644 --- a/internal/workflow/node-processor/processor.go +++ b/internal/workflow/node-processor/processor.go @@ -34,6 +34,8 @@ func (n *nodeProcessor) SetLogger(logger *slog.Logger) { type certificateRepository interface { GetByWorkflowNodeId(ctx context.Context, workflowNodeId string) (*domain.Certificate, error) + GetByWorkflowRunId(ctx context.Context, workflowRunId string) (*domain.Certificate, error) + Save(ctx context.Context, certificate *domain.Certificate) (*domain.Certificate, error) } type workflowOutputRepository interface { diff --git a/internal/workflow/node-processor/upload_node.go b/internal/workflow/node-processor/upload_node.go index 2da19eed..a1878a41 100644 --- a/internal/workflow/node-processor/upload_node.go +++ b/internal/workflow/node-processor/upload_node.go @@ -83,7 +83,7 @@ func (n *uploadNode) checkCanSkip(ctx context.Context, lastOutput *domain.Workfl return false, "the configuration item 'PrivateKey' changed" } - lastCertificate, _ := n.certRepo.GetByWorkflowNodeId(ctx, n.node.Id) + lastCertificate, _ := n.certRepo.GetByWorkflowRunId(ctx, lastOutput.RunId) if lastCertificate != nil { return true, "the certificate has already been uploaded" } diff --git a/migrations/1748228400_upgrade.go b/migrations/1748228400_upgrade.go new file mode 100644 index 00000000..b6f954d2 --- /dev/null +++ b/migrations/1748228400_upgrade.go @@ -0,0 +1,39 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + // update collection `certificate` + { + collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") + if err != nil { + return err + } + + // add field + if err := collection.Fields.AddMarshaledJSONAt(14, []byte(`{ + "hidden": false, + "id": "bool810050391", + "name": "acmeRenewed", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + } + + return nil + }, func(app core.App) error { + return nil + }) +} From 4c13a3e86ad54d29d8ae7bd272bad36edb387005 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 13:19:15 +0800 Subject: [PATCH 09/24] feat: new acme dns-01 provider: hetzner --- internal/applicant/providers.go | 16 ++++++ internal/domain/access.go | 4 ++ internal/domain/provider.go | 2 + .../lego-providers/hetzner/hetzner.go | 36 ++++++++++++ ui/public/imgs/providers/hetzner.svg | 1 + ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormHetznerConfig.tsx | 57 +++++++++++++++++++ ui/src/domain/access.ts | 5 ++ ui/src/domain/provider.ts | 4 ++ ui/src/i18n/locales/en/nls.access.json | 3 + ui/src/i18n/locales/en/nls.provider.json | 1 + ui/src/i18n/locales/zh/nls.access.json | 3 + ui/src/i18n/locales/zh/nls.provider.json | 1 + 13 files changed, 136 insertions(+) create mode 100644 internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner/hetzner.go create mode 100644 ui/public/imgs/providers/hetzner.svg create mode 100644 ui/src/components/access/AccessFormHetznerConfig.tsx diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index 81cf8156..f967aef9 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -22,6 +22,7 @@ import ( pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore" pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname" pGoDaddy "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/godaddy" + pHetzner "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner" pHuaweiCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/huaweicloud" pJDCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/jdcloud" pNamecheap "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/namecheap" @@ -324,6 +325,21 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi return applicant, err } + case domain.ACMEDns01ProviderTypeHetzner: + { + access := domain.AccessConfigForHetzner{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pHetzner.NewChallengeProvider(&pHetzner.ChallengeProviderConfig{ + ApiToken: access.ApiToken, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ACMEDns01ProviderTypeHuaweiCloud, domain.ACMEDns01ProviderTypeHuaweiCloudDNS: { access := domain.AccessConfigForHuaweiCloud{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 7e1549f1..30088bdf 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -181,6 +181,10 @@ type AccessConfigForGoogleTrustServices struct { EabHmacKey string `json:"eabHmacKey"` } +type AccessConfigForHetzner struct { + ApiToken string `json:"apiToken"` +} + type AccessConfigForHuaweiCloud struct { AccessKeyId string `json:"accessKeyId"` SecretAccessKey string `json:"secretAccessKey"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index db0de59d..4552553d 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -44,6 +44,7 @@ const ( AccessProviderTypeGoDaddy = AccessProviderType("godaddy") AccessProviderTypeGoEdge = AccessProviderType("goedge") AccessProviderTypeGoogleTrustServices = AccessProviderType("googletrustservices") + AccessProviderTypeHetzner = AccessProviderType("hetzner") AccessProviderTypeHuaweiCloud = AccessProviderType("huaweicloud") AccessProviderTypeJDCloud = AccessProviderType("jdcloud") AccessProviderTypeKubernetes = AccessProviderType("k8s") @@ -131,6 +132,7 @@ const ( ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore) ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname) ACMEDns01ProviderTypeGoDaddy = ACMEDns01ProviderType(AccessProviderTypeGoDaddy) + ACMEDns01ProviderTypeHetzner = ACMEDns01ProviderType(AccessProviderTypeHetzner) ACMEDns01ProviderTypeHuaweiCloud = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeHuaweiCloudDNS] ACMEDns01ProviderTypeHuaweiCloudDNS = ACMEDns01ProviderType(AccessProviderTypeHuaweiCloud + "-dns") ACMEDns01ProviderTypeJDCloud = ACMEDns01ProviderType(AccessProviderTypeJDCloud) // 兼容旧值,等同于 [ACMEDns01ProviderTypeJDCloudDNS] diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner/hetzner.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner/hetzner.go new file mode 100644 index 00000000..c202cc78 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/hetzner/hetzner.go @@ -0,0 +1,36 @@ +package namedotcom + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/hetzner" +) + +type ChallengeProviderConfig struct { + ApiToken string `json:"apiToken"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := hetzner.NewDefaultConfig() + providerConfig.APIKey = config.ApiToken + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := hetzner.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/hetzner.svg b/ui/public/imgs/providers/hetzner.svg new file mode 100644 index 00000000..670cdda8 --- /dev/null +++ b/ui/public/imgs/providers/hetzner.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index fdf4f93f..6f4d5086 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -41,6 +41,7 @@ import AccessFormGnameConfig from "./AccessFormGnameConfig"; import AccessFormGoDaddyConfig from "./AccessFormGoDaddyConfig"; import AccessFormGoEdgeConfig from "./AccessFormGoEdgeConfig"; import AccessFormGoogleTrustServicesConfig from "./AccessFormGoogleTrustServicesConfig"; +import AccessFormHetznerConfig from "./AccessFormHetznerConfig"; import AccessFormHuaweiCloudConfig from "./AccessFormHuaweiCloudConfig"; import AccessFormJDCloudConfig from "./AccessFormJDCloudConfig"; import AccessFormKubernetesConfig from "./AccessFormKubernetesConfig"; @@ -239,6 +240,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.GOOGLETRUSTSERVICES: return ; + case ACCESS_PROVIDERS.HETZNER: + return ; case ACCESS_PROVIDERS.HUAWEICLOUD: return ; case ACCESS_PROVIDERS.JDCLOUD: diff --git a/ui/src/components/access/AccessFormHetznerConfig.tsx b/ui/src/components/access/AccessFormHetznerConfig.tsx new file mode 100644 index 00000000..12bf21b2 --- /dev/null +++ b/ui/src/components/access/AccessFormHetznerConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForHetzner } from "@/domain/access"; + +type AccessFormHetznerConfigFieldValues = Nullish; + +export type AccessFormHetznerConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormHetznerConfigFieldValues; + onValuesChange?: (values: AccessFormHetznerConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormHetznerConfigFieldValues => { + return { + apiToken: "", + }; +}; + +const AccessFormHetznerConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormHetznerConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiToken: z.string().nonempty(t("access.form.hetzner_api_token.placeholder")).trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormHetznerConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 3765310a..1576fd67 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -36,6 +36,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForGoDaddy | AccessConfigForGoEdge | AccessConfigForGoogleTrustServices + | AccessConfigForHetzner | AccessConfigForHuaweiCloud | AccessConfigForJDCloud | AccessConfigForKubernetes @@ -238,6 +239,10 @@ export type AccessConfigForGoogleTrustServices = { eabHmacKey: string; }; +export type AccessConfigForHetzner = { + apiToken: string; +}; + export type AccessConfigForHuaweiCloud = { accessKeyId: string; secretAccessKey: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 15542455..712530d7 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -35,6 +35,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ GODADDY: "godaddy", GOEDGE: "goedge", GOOGLETRUSTSERVICES: "googletrustservices", + HETZNER: "hetzner", HUAWEICLOUD: "huaweicloud", JDCLOUD: "jdcloud", KUBERNETES: "k8s", @@ -142,6 +143,7 @@ export const accessProvidersMap: Maphttps://cloud.google.com/certificate-manager/docs/public-ca-tutorial", + "access.form.hetzner_api_token.label": "Hetzner API token", + "access.form.hetzner_api_token.placeholder": "Please enter Hetzner API token", + "access.form.hetzner_api_token.tooltip": "For more information, see https://docs.hetzner.com/cloud/api/getting-started/generating-api-token", "access.form.huaweicloud_access_key_id.label": "Huawei Cloud AccessKeyId", "access.form.huaweicloud_access_key_id.placeholder": "Please enter Huawei Cloud AccessKeyId", "access.form.huaweicloud_access_key_id.tooltip": "For more information, see https://support.huaweicloud.com/intl/en-us/usermanual-ca/ca_01_0003.html", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index a3bbfecc..e9122149 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -74,6 +74,7 @@ "provider.godaddy": "GoDaddy", "provider.goedge": "GoEdge", "provider.googletrustservices": "Google Trust Services", + "provider.hetzner": "Hetzner", "provider.huaweicloud": "Huawei Cloud", "provider.huaweicloud.cdn": "Huawei Cloud - CDN (Content Delivery Network)", "provider.huaweicloud.dns": "Huawei Cloud - DNS (Domain Name Service)", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index fdae1705..e2cacc1f 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -231,6 +231,9 @@ "access.form.googletrustservices_eab_hmac_key.label": "ACME EAB HMAC Key", "access.form.googletrustservices_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC Key", "access.form.googletrustservices_eab_hmac_key.tooltip": "这是什么?请参阅 https://cloud.google.com/certificate-manager/docs/public-ca-tutorial", + "access.form.hetzner_api_token.label": "Hetzner API Token", + "access.form.hetzner_api_token.placeholder": "请输入 Hetzner API Token", + "access.form.hetzner_api_token.tooltip": "这是什么?请参阅 https://docs.hetzner.com/cloud/api/getting-started/generating-api-token", "access.form.huaweicloud_access_key_id.label": "华为云 AccessKeyId", "access.form.huaweicloud_access_key_id.placeholder": "请输入华为云 AccessKeyId", "access.form.huaweicloud_access_key_id.tooltip": "这是什么?请参阅 https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index b9fb8777..a6b94779 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -74,6 +74,7 @@ "provider.godaddy": "GoDaddy", "provider.goedge": "GoEdge", "provider.googletrustservices": "Google Trust Services", + "provider.hetzner": "Hetzner", "provider.huaweicloud": "华为云", "provider.huaweicloud.cdn": "华为云 - 内容分发网络 CDN", "provider.huaweicloud.dns": "华为云 - 云解析 DNS", From 40f4488009db60b768f35026a9dffcbb615c1b0a Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 13:29:48 +0800 Subject: [PATCH 10/24] feat: new acme dns-01 provider: digitalocean --- internal/applicant/providers.go | 16 ++++++ internal/domain/access.go | 4 ++ internal/domain/provider.go | 2 + .../digitalocean/digitalocean.go | 36 ++++++++++++ ui/public/imgs/providers/digitalocean.svg | 1 + ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormDigitalOceanConfig.tsx | 57 +++++++++++++++++++ ui/src/domain/access.ts | 4 ++ ui/src/domain/provider.ts | 4 ++ ui/src/i18n/locales/en/nls.access.json | 3 + ui/src/i18n/locales/en/nls.provider.json | 1 + ui/src/i18n/locales/zh/nls.access.json | 3 + ui/src/i18n/locales/zh/nls.provider.json | 1 + 13 files changed, 135 insertions(+) create mode 100644 internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean/digitalocean.go create mode 100644 ui/public/imgs/providers/digitalocean.svg create mode 100644 ui/src/components/access/AccessFormDigitalOceanConfig.tsx diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index f967aef9..e35aee3e 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -17,6 +17,7 @@ import ( pClouDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cloudns" pCMCCCloud "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/cmcccloud" pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec" + pDigitalOcean "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean" pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla" pDynv6 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6" pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore" @@ -247,6 +248,21 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi return applicant, err } + case domain.ACMEDns01ProviderTypeDigitalOcean: + { + access := domain.AccessConfigForDigitalOcean{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pDigitalOcean.NewChallengeProvider(&pDigitalOcean.ChallengeProviderConfig{ + AccessToken: access.AccessToken, + DnsPropagationTimeout: options.DnsPropagationTimeout, + DnsTTL: options.DnsTTL, + }) + return applicant, err + } + case domain.ACMEDns01ProviderTypeDNSLA: { access := domain.AccessConfigForDNSLA{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 30088bdf..c3530a4f 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -112,6 +112,10 @@ type AccessConfigForDeSEC struct { Token string `json:"token"` } +type AccessConfigForDigitalOcean struct { + AccessToken string `json:"accessToken"` +} + type AccessConfigForDingTalkBot struct { WebhookUrl string `json:"webhookUrl"` Secret string `json:"secret"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 4552553d..52a71e64 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -31,6 +31,7 @@ const ( AccessProviderTypeCTCCCloud = AccessProviderType("ctcccloud") // 天翼云(预留) AccessProviderTypeCUCCCloud = AccessProviderType("cucccloud") // 联通云(预留) AccessProviderTypeDeSEC = AccessProviderType("desec") + AccessProviderTypeDigitalOcean = AccessProviderType("digitalocean") AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot") AccessProviderTypeDNSLA = AccessProviderType("dnsla") AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") @@ -127,6 +128,7 @@ const ( ACMEDns01ProviderTypeClouDNS = ACMEDns01ProviderType(AccessProviderTypeClouDNS) ACMEDns01ProviderTypeCMCCCloud = ACMEDns01ProviderType(AccessProviderTypeCMCCCloud) ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC) + ACMEDns01ProviderTypeDigitalOcean = ACMEDns01ProviderType(AccessProviderTypeDigitalOcean) ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA) ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6) ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore) diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean/digitalocean.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean/digitalocean.go new file mode 100644 index 00000000..0e3cb358 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean/digitalocean.go @@ -0,0 +1,36 @@ +package namedotcom + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/digitalocean" +) + +type ChallengeProviderConfig struct { + AccessToken string `json:"accessToken"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` + DnsTTL int32 `json:"dnsTTL,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := digitalocean.NewDefaultConfig() + providerConfig.AuthToken = config.AccessToken + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + if config.DnsTTL != 0 { + providerConfig.TTL = int(config.DnsTTL) + } + + provider, err := digitalocean.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/digitalocean.svg b/ui/public/imgs/providers/digitalocean.svg new file mode 100644 index 00000000..94b90bf9 --- /dev/null +++ b/ui/public/imgs/providers/digitalocean.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index 6f4d5086..4fd547e8 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -29,6 +29,7 @@ import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig"; import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig"; import AccessFormDeSECConfig from "./AccessFormDeSECConfig"; +import AccessFormDigitalOceanConfig from "./AccessFormDigitalOceanConfig"; import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig"; import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; @@ -216,6 +217,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.DESEC: return ; + case ACCESS_PROVIDERS.DIGITALOCEAN: + return ; case ACCESS_PROVIDERS.DINGTALKBOT: return ; case ACCESS_PROVIDERS.DNSLA: diff --git a/ui/src/components/access/AccessFormDigitalOceanConfig.tsx b/ui/src/components/access/AccessFormDigitalOceanConfig.tsx new file mode 100644 index 00000000..f4aafc4f --- /dev/null +++ b/ui/src/components/access/AccessFormDigitalOceanConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForDigitalOcean } from "@/domain/access"; + +type AccessFormDigitalOceanConfigFieldValues = Nullish; + +export type AccessFormDigitalOceanConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormDigitalOceanConfigFieldValues; + onValuesChange?: (values: AccessFormDigitalOceanConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormDigitalOceanConfigFieldValues => { + return { + accessToken: "", + }; +}; + +const AccessFormDigitalOceanConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDigitalOceanConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + accessToken: z.string().nonempty(t("access.form.digitalocean_access_token.placeholder")).trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormDigitalOceanConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 1576fd67..32a1b128 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -170,6 +170,10 @@ export type AccessConfigForDeSEC = { token: string; }; +export type AccessConfigForDigitalOcean = { + accessToken: string; +}; + export type AccessConfigForDingTalkBot = { webhookUrl: string; secret?: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 712530d7..6d1ad303 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -23,6 +23,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ CLOUDNS: "cloudns", CMCCCLOUD: "cmcccloud", DESEC: "desec", + DIGITALOCEAN: "digitalocean", DINGTALKBOT: "dingtalkbot", DNSLA: "dnsla", DOGECLOUD: "dogecloud", @@ -139,6 +140,7 @@ export const accessProvidersMap: Maphttps://desec.readthedocs.io/en/latest/auth/tokens.html", + "access.form.digitalocean_access_token.label": "DigitalOcean access token", + "access.form.digitalocean_access_token.placeholder": "Please enter DigitalOcean access token", + "access.form.digitalocean_access_token.tooltip": "For more information, see https://docs.digitalocean.com/reference/api/create-personal-access-token/", "access.form.dingtalkbot_webhook_url.label": "DingTalk bot Webhook URL", "access.form.dingtalkbot_webhook_url.placeholder": "Please enter DingTalk bot Webhook URL", "access.form.dingtalkbot_webhook_url.tooltip": "For more information, see https://open.dingtalk.com/document/orgapp/obtain-the-webhook-address-of-a-custom-robot", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index e9122149..c623bb27 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -58,6 +58,7 @@ "provider.ctcccloud": "China Telecom Cloud (State Cloud)", "provider.cucccloud": "China Unicom Cloud", "provider.desec": "deSEC", + "provider.digitalocean": "DigitalOcean", "provider.dingtalkbot": "DingTalk Bot", "provider.dnsla": "DNS.LA", "provider.dogecloud": "Doge Cloud", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index e2cacc1f..f3a39ec2 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -146,6 +146,9 @@ "access.form.desec_token.label": "deSEC Token", "access.form.desec_token.placeholder": "请输入 deSEC Token", "access.form.desec_token.tooltip": "这是什么?请参阅 https://desec.readthedocs.io/en/latest/auth/tokens.html", + "access.form.digitalocean_access_token.label": "DigitalOcean Access Token", + "access.form.digitalocean_access_token.placeholder": "请输入 DigitalOcean Access Token", + "access.form.digitalocean_access_token.tooltip": "这是什么?请参阅 https://docs.digitalocean.com/reference/api/create-personal-access-token/", "access.form.dingtalkbot_webhook_url.label": "钉钉群机器人 Webhook 地址", "access.form.dingtalkbot_webhook_url.placeholder": "请输入钉钉群机器人 Webhook 地址", "access.form.dingtalkbot_webhook_url.tooltip": "这是什么?请参阅 https://open.dingtalk.com/document/orgapp/obtain-the-webhook-address-of-a-custom-robot", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index a6b94779..a68a1a09 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -58,6 +58,7 @@ "provider.ctcccloud": "联通云", "provider.cucccloud": "天翼云", "provider.desec": "deSEC", + "provider.digitalocean": "DigitalOcean", "provider.dingtalkbot": "钉钉群机器人", "provider.dnsla": "DNS.LA", "provider.dogecloud": "多吉云", From cd9dac776551db29ee8e3968e8dadcf5a0cd67fa Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 13:59:00 +0800 Subject: [PATCH 11/24] feat: new acme dns-01 provider: duckdns --- internal/applicant/providers.go | 15 +++++ internal/domain/access.go | 4 ++ internal/domain/provider.go | 2 + .../lego-providers/duckdns/duckdns.go | 32 ++++++++++ ui/public/imgs/providers/duckdns.png | Bin 0 -> 6821 bytes ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormDuckDNSConfig.tsx | 57 ++++++++++++++++++ ui/src/domain/access.ts | 6 ++ ui/src/domain/provider.ts | 4 ++ ui/src/i18n/locales/en/nls.access.json | 3 + ui/src/i18n/locales/en/nls.provider.json | 1 + ui/src/i18n/locales/zh/nls.access.json | 3 + ui/src/i18n/locales/zh/nls.provider.json | 1 + 13 files changed, 131 insertions(+) create mode 100644 internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns/duckdns.go create mode 100644 ui/public/imgs/providers/duckdns.png create mode 100644 ui/src/components/access/AccessFormDuckDNSConfig.tsx diff --git a/internal/applicant/providers.go b/internal/applicant/providers.go index e35aee3e..98561daf 100644 --- a/internal/applicant/providers.go +++ b/internal/applicant/providers.go @@ -19,6 +19,7 @@ import ( pDeSEC "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/desec" pDigitalOcean "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/digitalocean" pDNSLA "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dnsla" + pDuckDNS "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns" pDynv6 "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/dynv6" pGcore "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gcore" pGname "github.com/usual2970/certimate/internal/pkg/core/applicant/acme-dns-01/lego-providers/gname" @@ -279,6 +280,20 @@ func createApplicantProvider(options *applicantProviderOptions) (challenge.Provi return applicant, err } + case domain.ACMEDns01ProviderTypeDuckDNS: + { + access := domain.AccessConfigForDuckDNS{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + applicant, err := pDuckDNS.NewChallengeProvider(&pDuckDNS.ChallengeProviderConfig{ + Token: access.Token, + DnsPropagationTimeout: options.DnsPropagationTimeout, + }) + return applicant, err + } + case domain.ACMEDns01ProviderTypeDynv6: { access := domain.AccessConfigForDynv6{} diff --git a/internal/domain/access.go b/internal/domain/access.go index c3530a4f..dac85e31 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -131,6 +131,10 @@ type AccessConfigForDogeCloud struct { SecretKey string `json:"secretKey"` } +type AccessConfigForDuckDNS struct { + Token string `json:"token"` +} + type AccessConfigForDynv6 struct { HttpToken string `json:"httpToken"` } diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 52a71e64..14a107b6 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -35,6 +35,7 @@ const ( AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot") AccessProviderTypeDNSLA = AccessProviderType("dnsla") AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") + AccessProviderTypeDuckDNS = AccessProviderType("duckdns") AccessProviderTypeDynv6 = AccessProviderType("dynv6") AccessProviderTypeEdgio = AccessProviderType("edgio") AccessProviderTypeEmail = AccessProviderType("email") @@ -130,6 +131,7 @@ const ( ACMEDns01ProviderTypeDeSEC = ACMEDns01ProviderType(AccessProviderTypeDeSEC) ACMEDns01ProviderTypeDigitalOcean = ACMEDns01ProviderType(AccessProviderTypeDigitalOcean) ACMEDns01ProviderTypeDNSLA = ACMEDns01ProviderType(AccessProviderTypeDNSLA) + ACMEDns01ProviderTypeDuckDNS = ACMEDns01ProviderType(AccessProviderTypeDuckDNS) ACMEDns01ProviderTypeDynv6 = ACMEDns01ProviderType(AccessProviderTypeDynv6) ACMEDns01ProviderTypeGcore = ACMEDns01ProviderType(AccessProviderTypeGcore) ACMEDns01ProviderTypeGname = ACMEDns01ProviderType(AccessProviderTypeGname) diff --git a/internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns/duckdns.go b/internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns/duckdns.go new file mode 100644 index 00000000..6cc823d0 --- /dev/null +++ b/internal/pkg/core/applicant/acme-dns-01/lego-providers/duckdns/duckdns.go @@ -0,0 +1,32 @@ +package namedotcom + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/providers/dns/duckdns" +) + +type ChallengeProviderConfig struct { + Token string `json:"token"` + DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` +} + +func NewChallengeProvider(config *ChallengeProviderConfig) (challenge.Provider, error) { + if config == nil { + panic("config is nil") + } + + providerConfig := duckdns.NewDefaultConfig() + providerConfig.Token = config.Token + if config.DnsPropagationTimeout != 0 { + providerConfig.PropagationTimeout = time.Duration(config.DnsPropagationTimeout) * time.Second + } + + provider, err := duckdns.NewDNSProviderConfig(providerConfig) + if err != nil { + return nil, err + } + + return provider, nil +} diff --git a/ui/public/imgs/providers/duckdns.png b/ui/public/imgs/providers/duckdns.png new file mode 100644 index 0000000000000000000000000000000000000000..c80f4a9ed84ecf4545da1088eb305892504ef8e3 GIT binary patch literal 6821 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 className, return ; case ACCESS_PROVIDERS.DOGECLOUD: return ; + case ACCESS_PROVIDERS.DUCKDNS: + return ; case ACCESS_PROVIDERS.DYNV6: return ; case ACCESS_PROVIDERS.EDGIO: diff --git a/ui/src/components/access/AccessFormDuckDNSConfig.tsx b/ui/src/components/access/AccessFormDuckDNSConfig.tsx new file mode 100644 index 00000000..969f78f8 --- /dev/null +++ b/ui/src/components/access/AccessFormDuckDNSConfig.tsx @@ -0,0 +1,57 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForDuckDNS } from "@/domain/access"; + +type AccessFormDuckDNSConfigFieldValues = Nullish; + +export type AccessFormDuckDNSConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormDuckDNSConfigFieldValues; + onValuesChange?: (values: AccessFormDuckDNSConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormDuckDNSConfigFieldValues => { + return { + token: "", + }; +}; + +const AccessFormDuckDNSConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDuckDNSConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + token: z.string().nonempty(t("access.form.duckdns_token.placeholder")).trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default AccessFormDuckDNSConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 32a1b128..9e2d4f70 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -24,9 +24,11 @@ export interface AccessModel extends BaseModel { | AccessConfigForClouDNS | AccessConfigForCMCCCloud | AccessConfigForDeSEC + | AccessConfigForDigitalOcean | AccessConfigForDingTalkBot | AccessConfigForDNSLA | AccessConfigForDogeCloud + | AccessConfigForDuckDNS | AccessConfigForDynv6 | AccessConfigForEdgio | AccessConfigForEmail @@ -189,6 +191,10 @@ export type AccessConfigForDogeCloud = { secretKey: string; }; +export type AccessConfigForDuckDNS = { + token: string; +}; + export type AccessConfigForDynv6 = { httpToken: string; }; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 6d1ad303..6576aa94 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -27,6 +27,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ DINGTALKBOT: "dingtalkbot", DNSLA: "dnsla", DOGECLOUD: "dogecloud", + DUCKDNS: "duckdns", DYNV6: "dynv6", EDGIO: "edgio", EMAIL: "email", @@ -142,6 +143,7 @@ export const accessProvidersMap: Maphttps://console.dogecloud.com/", + "access.form.duckdns_token.label": "DuckDNS token", + "access.form.duckdns_token.placeholder": "Please enter DuckDNS token", + "access.form.duckdns_token.tooltip": "For more information, see https://www.duckdns.org/spec.jsp", "access.form.dynv6_http_token.label": "dynv6 HTTP token", "access.form.dynv6_http_token.placeholder": "Please enter dynv6 HTTP token", "access.form.dynv6_http_token.tooltip": "For more information, see https://dynv6.com/keys", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index c623bb27..80cb65cf 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -63,6 +63,7 @@ "provider.dnsla": "DNS.LA", "provider.dogecloud": "Doge Cloud", "provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)", + "provider.duckdns": "Duck DNS", "provider.dynv6": "dynv6", "provider.edgio": "Edgio", "provider.edgio.applications": "Edgio - Applications", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index f3a39ec2..1f538ac5 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -167,6 +167,9 @@ "access.form.dogecloud_secret_key.label": "多吉云 SecretKey", "access.form.dogecloud_secret_key.placeholder": "请输入多吉云 SecretKey", "access.form.dogecloud_secret_key.tooltip": "这是什么?请参阅 https://console.dogecloud.com/", + "access.form.duckdns_token.label": "DuckDNS Token", + "access.form.duckdns_token.placeholder": "请输入 DuckDNS Token", + "access.form.duckdns_token.tooltip": "这是什么?请参阅 https://www.duckdns.org/spec.jsp", "access.form.dynv6_http_token.label": "dynv6 HTTP Token", "access.form.dynv6_http_token.placeholder": "请输入 dynv6 HTTP Token", "access.form.dynv6_http_token.tooltip": "这是什么?请参阅 https://dynv6.com/keys", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index a68a1a09..8de1ccf5 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -63,6 +63,7 @@ "provider.dnsla": "DNS.LA", "provider.dogecloud": "多吉云", "provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN", + "provider.duckdns": "Duck DNS", "provider.dynv6": "dynv6", "provider.edgio": "Edgio", "provider.edgio.applications": "Edgio - Applications", From 8e23b14bf37612aaa4f82255f1ce0b7cd2724fd6 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 15:55:09 +0800 Subject: [PATCH 12/24] feat: new notification provider: discord bot --- internal/domain/access.go | 5 ++ internal/domain/provider.go | 2 + internal/notify/providers.go | 14 ++++ .../providers/discordbot/discordbot.go | 67 ++++++++++++++++++ .../providers/discordbot/discordbot_test.go | 64 +++++++++++++++++ .../providers/telegrambot/telegrambot_test.go | 8 +-- ui/public/imgs/providers/discord.svg | 1 + ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormDiscordBotConfig.tsx | 70 +++++++++++++++++++ .../access/AccessFormTelegramBotConfig.tsx | 18 ++--- .../workflow/node/NotifyNodeConfigForm.tsx | 3 + .../NotifyNodeConfigFormDiscordBotConfig.tsx | 61 ++++++++++++++++ .../NotifyNodeConfigFormTelegramBotConfig.tsx | 8 +-- ui/src/domain/access.ts | 6 ++ ui/src/domain/provider.ts | 8 ++- ui/src/i18n/locales/en/nls.access.json | 20 ++++-- ui/src/i18n/locales/en/nls.provider.json | 1 + .../i18n/locales/en/nls.workflow.nodes.json | 11 +-- ui/src/i18n/locales/zh/nls.access.json | 20 ++++-- ui/src/i18n/locales/zh/nls.provider.json | 3 +- .../i18n/locales/zh/nls.workflow.nodes.json | 9 ++- 21 files changed, 361 insertions(+), 41 deletions(-) create mode 100644 internal/pkg/core/notifier/providers/discordbot/discordbot.go create mode 100644 internal/pkg/core/notifier/providers/discordbot/discordbot_test.go create mode 100644 ui/public/imgs/providers/discord.svg create mode 100644 ui/src/components/access/AccessFormDiscordBotConfig.tsx create mode 100644 ui/src/components/workflow/node/NotifyNodeConfigFormDiscordBotConfig.tsx diff --git a/internal/domain/access.go b/internal/domain/access.go index dac85e31..4e002ecf 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -121,6 +121,11 @@ type AccessConfigForDingTalkBot struct { Secret string `json:"secret"` } +type AccessConfigForDiscordBot struct { + BotToken string `json:"botToken"` + DefaultChannelId string `json:"defaultChannelId,omitempty"` +} + type AccessConfigForDNSLA struct { ApiId string `json:"apiId"` ApiSecret string `json:"apiSecret"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 14a107b6..8f5c959a 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -33,6 +33,7 @@ const ( AccessProviderTypeDeSEC = AccessProviderType("desec") AccessProviderTypeDigitalOcean = AccessProviderType("digitalocean") AccessProviderTypeDingTalkBot = AccessProviderType("dingtalkbot") + AccessProviderTypeDiscordBot = AccessProviderType("discordbot") AccessProviderTypeDNSLA = AccessProviderType("dnsla") AccessProviderTypeDogeCloud = AccessProviderType("dogecloud") AccessProviderTypeDuckDNS = AccessProviderType("duckdns") @@ -269,6 +270,7 @@ type NotificationProviderType string */ const ( NotificationProviderTypeDingTalkBot = NotificationProviderType(AccessProviderTypeDingTalkBot) + NotificationProviderTypeDiscordBot = NotificationProviderType(AccessProviderTypeDiscordBot) NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail) NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot) NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost) diff --git a/internal/notify/providers.go b/internal/notify/providers.go index 3a8c575d..23f5bb43 100644 --- a/internal/notify/providers.go +++ b/internal/notify/providers.go @@ -7,6 +7,7 @@ import ( "github.com/usual2970/certimate/internal/domain" "github.com/usual2970/certimate/internal/pkg/core/notifier" pDingTalkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/dingtalkbot" + pDiscordBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/discordbot" pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email" pLarkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot" pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost" @@ -42,6 +43,19 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier }) } + case domain.NotificationProviderTypeDiscordBot: + { + access := domain.AccessConfigForDiscordBot{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + return pDiscordBot.NewNotifier(&pDiscordBot.NotifierConfig{ + BotToken: access.BotToken, + ChannelId: maputil.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.DefaultChannelId), + }) + } + case domain.NotificationProviderTypeEmail: { access := domain.AccessConfigForEmail{} diff --git a/internal/pkg/core/notifier/providers/discordbot/discordbot.go b/internal/pkg/core/notifier/providers/discordbot/discordbot.go new file mode 100644 index 00000000..dbffaba7 --- /dev/null +++ b/internal/pkg/core/notifier/providers/discordbot/discordbot.go @@ -0,0 +1,67 @@ +package discordbot + +import ( + "context" + "fmt" + "log/slog" + + "github.com/go-resty/resty/v2" + + "github.com/usual2970/certimate/internal/pkg/core/notifier" +) + +type NotifierConfig struct { + // Discord Bot API Token。 + BotToken string `json:"botToken"` + // Discord Channel ID。 + ChannelId string `json:"channelId"` +} + +type NotifierProvider struct { + config *NotifierConfig + logger *slog.Logger + httpClient *resty.Client +} + +var _ notifier.Notifier = (*NotifierProvider)(nil) + +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { + if config == nil { + panic("config is nil") + } + + client := resty.New() + + return &NotifierProvider{ + config: config, + logger: slog.Default(), + httpClient: client, + }, nil +} + +func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { + if logger == nil { + n.logger = slog.Default() + } else { + n.logger = logger + } + return n +} + +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + // REF: https://discord.com/developers/docs/resources/message#create-message + req := n.httpClient.R(). + SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Bot "+n.config.BotToken). + SetBody(map[string]any{ + "content": subject + "\n" + message, + }) + resp, err := req.Post(fmt.Sprintf("https://discord.com/api/v9/channels/%s/messages", n.config.ChannelId)) + if err != nil { + return nil, fmt.Errorf("discord api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("discord api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return ¬ifier.NotifyResult{}, nil +} diff --git a/internal/pkg/core/notifier/providers/discordbot/discordbot_test.go b/internal/pkg/core/notifier/providers/discordbot/discordbot_test.go new file mode 100644 index 00000000..42edf95e --- /dev/null +++ b/internal/pkg/core/notifier/providers/discordbot/discordbot_test.go @@ -0,0 +1,64 @@ +package discordbot_test + +import ( + "context" + "flag" + "fmt" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/discordbot" +) + +const ( + mockSubject = "test_subject" + mockMessage = "test_message" +) + +var ( + fApiToken string + fChannelId string +) + +func init() { + argsPrefix := "CERTIMATE_NOTIFIER_DISCORDBOT_" + + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./discordbot_test.go -args \ + --CERTIMATE_NOTIFIER_DISCORDBOT_APITOKEN="your-bot-token" \ + --CERTIMATE_NOTIFIER_DISCORDBOT_CHANNELID="your-channel-id" +*/ +func TestNotify(t *testing.T) { + flag.Parse() + + t.Run("Notify", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("APITOKEN: %v", fApiToken), + fmt.Sprintf("CHANNELID: %v", fChannelId), + }, "\n")) + + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ + BotToken: fApiToken, + ChannelId: fChannelId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + res, err := notifier.Notify(context.Background(), mockSubject, mockMessage) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go b/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go index 3a207384..8dc18b95 100644 --- a/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go +++ b/internal/pkg/core/notifier/providers/telegrambot/telegrambot_test.go @@ -17,14 +17,14 @@ const ( var ( fApiToken string - fChartId int64 + fChatId int64 ) func init() { argsPrefix := "CERTIMATE_NOTIFIER_TELEGRAMBOT_" flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") - flag.Int64Var(&fChartId, argsPrefix+"CHATID", 0, "") + flag.Int64Var(&fChatId, argsPrefix+"CHATID", 0, "") } /* @@ -41,12 +41,12 @@ func TestNotify(t *testing.T) { t.Log(strings.Join([]string{ "args:", fmt.Sprintf("APITOKEN: %v", fApiToken), - fmt.Sprintf("CHATID: %v", fChartId), + fmt.Sprintf("CHATID: %v", fChatId), }, "\n")) notifier, err := provider.NewNotifier(&provider.NotifierConfig{ BotToken: fApiToken, - ChatId: fChartId, + ChatId: fChatId, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/ui/public/imgs/providers/discord.svg b/ui/public/imgs/providers/discord.svg new file mode 100644 index 00000000..04f8f5e9 --- /dev/null +++ b/ui/public/imgs/providers/discord.svg @@ -0,0 +1 @@ + diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index baea8ed8..2a28aaac 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -31,6 +31,7 @@ import AccessFormCMCCCloudConfig from "./AccessFormCMCCCloudConfig"; import AccessFormDeSECConfig from "./AccessFormDeSECConfig"; import AccessFormDigitalOceanConfig from "./AccessFormDigitalOceanConfig"; import AccessFormDingTalkBotConfig from "./AccessFormDingTalkBotConfig"; +import AccessFormDiscordBotConfig from "./AccessFormDiscordBotConfig"; import AccessFormDNSLAConfig from "./AccessFormDNSLAConfig"; import AccessFormDogeCloudConfig from "./AccessFormDogeCloudConfig"; import AccessFormDuckDNSConfig from "./AccessFormDuckDNSConfig"; @@ -222,6 +223,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.DINGTALKBOT: return ; + case ACCESS_PROVIDERS.DISCORDBOT: + return ; case ACCESS_PROVIDERS.DNSLA: return ; case ACCESS_PROVIDERS.DOGECLOUD: diff --git a/ui/src/components/access/AccessFormDiscordBotConfig.tsx b/ui/src/components/access/AccessFormDiscordBotConfig.tsx new file mode 100644 index 00000000..5f844ccc --- /dev/null +++ b/ui/src/components/access/AccessFormDiscordBotConfig.tsx @@ -0,0 +1,70 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForDiscordBot } from "@/domain/access"; + +type AccessFormDiscordBotConfigFieldValues = Nullish; + +export type AccessFormDiscordBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormDiscordBotConfigFieldValues; + onValuesChange?: (values: AccessFormDiscordBotConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormDiscordBotConfigFieldValues => { + return { + botToken: "", + }; +}; + +const AccessFormDiscordBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormDiscordBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + botToken: z + .string({ message: t("access.form.discordbot_token.placeholder") }) + .min(1, t("access.form.discordbot_token.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })), + defaultChannelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormDiscordBotConfig; diff --git a/ui/src/components/access/AccessFormTelegramBotConfig.tsx b/ui/src/components/access/AccessFormTelegramBotConfig.tsx index 3181c71d..a347610f 100644 --- a/ui/src/components/access/AccessFormTelegramBotConfig.tsx +++ b/ui/src/components/access/AccessFormTelegramBotConfig.tsx @@ -26,8 +26,8 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi const formSchema = z.object({ botToken: z - .string({ message: t("access.form.telegram_bot_token.placeholder") }) - .min(1, t("access.form.telegram_bot_token.placeholder")) + .string({ message: t("access.form.telegrambot_token.placeholder") }) + .min(1, t("access.form.telegrambot_token.placeholder")) .max(256, t("common.errmsg.string_max", { max: 256 })), defaultChatId: z .preprocess( @@ -38,7 +38,7 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi .refine((v) => { if (v == null || v + "" === "") return true; return !Number.isNaN(+v!) && +v! !== 0; - }, t("access.form.telegram_bot_default_chat_id.placeholder")) + }, t("access.form.telegrambot_default_chat_id.placeholder")) ) .nullish(), }); @@ -59,20 +59,20 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi > } + tooltip={} > - + } + tooltip={} > - + ); diff --git a/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx b/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx index d303c2ba..44a4a604 100644 --- a/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx @@ -17,6 +17,7 @@ import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks import { useAccessesStore } from "@/stores/access"; import { useNotifyChannelsStore } from "@/stores/notify"; +import NotifyNodeConfigFormDiscordBotConfig from "./NotifyNodeConfigFormDiscordBotConfig"; import NotifyNodeConfigFormEmailConfig from "./NotifyNodeConfigFormEmailConfig"; import NotifyNodeConfigFormMattermostConfig from "./NotifyNodeConfigFormMattermostConfig"; import NotifyNodeConfigFormTelegramBotConfig from "./NotifyNodeConfigFormTelegramBotConfig"; @@ -110,6 +111,8 @@ const NotifyNodeConfigForm = forwardRef; case NOTIFICATION_PROVIDERS.EMAIL: return ; case NOTIFICATION_PROVIDERS.MATTERMOST: diff --git a/ui/src/components/workflow/node/NotifyNodeConfigFormDiscordBotConfig.tsx b/ui/src/components/workflow/node/NotifyNodeConfigFormDiscordBotConfig.tsx new file mode 100644 index 00000000..200f2684 --- /dev/null +++ b/ui/src/components/workflow/node/NotifyNodeConfigFormDiscordBotConfig.tsx @@ -0,0 +1,61 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type NotifyNodeConfigFormDiscordBotConfigFieldValues = Nullish<{ + channelId?: string; +}>; + +export type NotifyNodeConfigFormDiscordBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: NotifyNodeConfigFormDiscordBotConfigFieldValues; + onValuesChange?: (values: NotifyNodeConfigFormDiscordBotConfigFieldValues) => void; +}; + +const initFormModel = (): NotifyNodeConfigFormDiscordBotConfigFieldValues => { + return {}; +}; + +const NotifyNodeConfigFormDiscordBotConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: NotifyNodeConfigFormDiscordBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + channelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default NotifyNodeConfigFormDiscordBotConfig; diff --git a/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx b/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx index a40142ee..29eaa807 100644 --- a/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx +++ b/ui/src/components/workflow/node/NotifyNodeConfigFormTelegramBotConfig.tsx @@ -38,7 +38,7 @@ const NotifyNodeConfigFormTelegramBotConfig = ({ .refine((v) => { if (v == null || v + "" === "") return true; return !Number.isNaN(+v!) && +v! !== 0; - }, t("workflow_node.notify.form.telegram_bot_chat_id.placeholder")) + }, t("workflow_node.notify.form.telegrambot_chat_id.placeholder")) ) .nullish(), }); @@ -59,11 +59,11 @@ const NotifyNodeConfigFormTelegramBotConfig = ({ > } + tooltip={} > - + ); diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 9e2d4f70..9e953963 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -26,6 +26,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForDeSEC | AccessConfigForDigitalOcean | AccessConfigForDingTalkBot + | AccessConfigForDiscordBot | AccessConfigForDNSLA | AccessConfigForDogeCloud | AccessConfigForDuckDNS @@ -181,6 +182,11 @@ export type AccessConfigForDingTalkBot = { secret?: string; }; +export type AccessConfigForDiscordBot = { + botToken: string; + defaultChannelId?: string; +}; + export type AccessConfigForDNSLA = { apiId: string; apiSecret: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 6576aa94..806c8283 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -25,6 +25,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ DESEC: "desec", DIGITALOCEAN: "digitalocean", DINGTALKBOT: "dingtalkbot", + DISCORDBOT: "discordbot", DNSLA: "dnsla", DOGECLOUD: "dogecloud", DUCKDNS: "duckdns", @@ -172,8 +173,9 @@ export const accessProvidersMap: Map [ e[0] as string, { @@ -588,6 +590,7 @@ export const deploymentProvidersMap: Map [ type, { diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 31772bf5..fe437f87 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -161,6 +161,12 @@ "access.form.dingtalkbot_secret.label": "DingTalk bot secret", "access.form.dingtalkbot_secret.placeholder": "Please enter DingTalk bot secret", "access.form.dingtalkbot_secret.tooltip": "For more information, see https://open.dingtalk.com/document/orgapp/customize-robot-security-settings", + "access.form.discordbot_token.label": "Discord bot token", + "access.form.discordbot_token.placeholder": "Please enter Discord bot token", + "access.form.discordbot_token.tooltip": "How to get it? Please refer to https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token", + "access.form.discordbot_default_channel_id.label": "Default Discord channel ID (Optional)", + "access.form.discordbot_default_channel_id.placeholder": "Please enter default Discord channel ID", + "access.form.discordbot_default_channel_id.tooltip": "For more information, see https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", "access.form.dnsla_api_id.label": "DNS.LA API ID", "access.form.dnsla_api_id.placeholder": "Please enter DNS.LA API ID", "access.form.dnsla_api_id.tooltip": "For more information, see https://www.dns.la/docs/ApiDoc", @@ -284,7 +290,7 @@ "access.form.mattermost_password.placeholder": "Please enter Mattermost password", "access.form.mattermost_default_channel_id.label": "Default Mattermost channel ID (Optional)", "access.form.mattermost_default_channel_id.placeholder": "Please enter default Mattermost channel ID", - "access.form.mattermost_default_channel_id.tooltip": "How to get the channel ID? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details.” You can directly see the channel ID on the pop-up page.", + "access.form.mattermost_default_channel_id.tooltip": "How to get it? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details.” You can directly see the channel ID on the pop-up page.", "access.form.namecheap_username.label": "Namecheap username", "access.form.namecheap_username.placeholder": "Please enter Namecheap username", "access.form.namecheap_username.tooltip": "For more information, see https://www.namecheap.com/support/api/intro/", @@ -380,12 +386,12 @@ "access.form.sslcom_eab_hmac_key.label": "ACME EAB HMAC key", "access.form.sslcom_eab_hmac_key.placeholder": "Please enter ACME EAB HMAC key", "access.form.sslcom_eab_hmac_key.tooltip": "For more information, see https://www.ssl.com/how-to/generate-acme-credentials-for-reseller-customers/", - "access.form.telegram_bot_token.label": "Telegram bot token", - "access.form.telegram_bot_token.placeholder": "Please enter Telegram bot token", - "access.form.telegram_bot_token.tooltip": "How to get the bot token? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", - "access.form.telegram_bot_default_chat_id.label": "Default Telegram chat ID (Optional)", - "access.form.telegram_bot_default_chat_id.placeholder": "Please enter default Telegram chat ID", - "access.form.telegram_bot_default_chat_id.tooltip": "How to get the chat ID? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", + "access.form.telegrambot_token.label": "Telegram bot token", + "access.form.telegrambot_token.placeholder": "Please enter Telegram bot token", + "access.form.telegrambot_token.tooltip": "How to get it? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", + "access.form.telegrambot_default_chat_id.label": "Default Telegram chat ID (Optional)", + "access.form.telegrambot_default_chat_id.placeholder": "Please enter default Telegram chat ID", + "access.form.telegrambot_default_chat_id.tooltip": "How to get it? Please refer to https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", "access.form.tencentcloud_secret_id.label": "Tencent Cloud SecretId", "access.form.tencentcloud_secret_id.placeholder": "Please enter Tencent Cloud SecretId", "access.form.tencentcloud_secret_id.tooltip": "For more information, see https://cloud.tencent.com/document/product/598/40488?lang=en", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index 80cb65cf..b34c92a7 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -60,6 +60,7 @@ "provider.desec": "deSEC", "provider.digitalocean": "DigitalOcean", "provider.dingtalkbot": "DingTalk Bot", + "provider.discordbot": "Discord Bot", "provider.dnsla": "DNS.LA", "provider.dogecloud": "Doge Cloud", "provider.dogecloud.cdn": "Doge Cloud - CDN (Content Delivery Network)", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index e0602370..172d707b 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -815,6 +815,9 @@ "workflow_node.notify.form.provider_access.placeholder": "Please select an authorization of notification provider", "workflow_node.notify.form.provider_access.button": "Create", "workflow_node.notify.form.params_config.label": "Parameter settings", + "workflow_node.notify.form.discordbot_channel_id.label": "Discord channel ID (Optional)", + "workflow_node.notify.form.discordbot_channel_id.placeholder": "Please enter Discord channel ID to override the default value", + "workflow_node.notify.form.discordbot_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.", "workflow_node.notify.form.email_sender_address.label": "Sender email address (Optional)", "workflow_node.notify.form.email_sender_address.placeholder": "Please enter sender email address to override the default value", "workflow_node.notify.form.email_sender_address.tooltip": "Leave it blank to use the default sender email address provided by the authorization.", @@ -822,11 +825,11 @@ "workflow_node.notify.form.email_receiver_address.placeholder": "Please enter receiver email address to override the default value", "workflow_node.notify.form.email_receiver_address.tooltip": "Leave it blank to use the default receiver email address provided by the selected authorization.", "workflow_node.notify.form.mattermost_channel_id.label": "Mattermost channel ID (Optional)", - "workflow_node.notify.form.mattermost_channel_id.placeholder": "Please enter Mattermost channel ID to override the default value", + "workflow_node.notify.form.mattermost_channel_id.placeholder": "Please enter Mattermost channel ID to override the default value", "workflow_node.notify.form.mattermost_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.", - "workflow_node.notify.form.telegram_bot_chat_id.label": "Telegram chat ID (Optional)", - "workflow_node.notify.form.telegram_bot_chat_id.placeholder": "Please enter Telegram chat ID to override the default value", - "workflow_node.notify.form.telegram_bot_chat_id.tooltip": "Leave it blank to use the default chat ID provided by the selected authorization.", + "workflow_node.notify.form.telegrambot_chat_id.label": "Telegram chat ID (Optional)", + "workflow_node.notify.form.telegrambot_chat_id.placeholder": "Please enter Telegram chat ID to override the default value", + "workflow_node.notify.form.telegrambot_chat_id.tooltip": "Leave it blank to use the default chat ID provided by the selected authorization.", "workflow_node.notify.form.webhook_data.label": "Webhook data (Optional)", "workflow_node.notify.form.webhook_data.placeholder": "Please enter Webhook data to override the default value", "workflow_node.notify.form.webhook_data.tooltip": "Leave it blank to use the default Webhook data provided by the authorization.", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 1f538ac5..21ad79c5 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -155,6 +155,12 @@ "access.form.dingtalkbot_secret.label": "钉钉群机器人加签密钥", "access.form.dingtalkbot_secret.placeholder": "请输入钉钉群机器人加签密钥", "access.form.dingtalkbot_secret.tooltip": "这是什么?请参阅 https://open.dingtalk.com/document/orgapp/customize-robot-security-settings", + "access.form.discordbot_token.label": "Discord 机器人 API Token", + "access.form.discordbot_token.placeholder": "请输入 Discord 机器人 API Token", + "access.form.discordbot_token.tooltip": "如何获取此参数?请参阅 https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token", + "access.form.discordbot_default_channel_id.label": "默认的 Discord 频道 ID(可选)", + "access.form.discordbot_default_channel_id.placeholder": "请输入默认的 Discord 频道 ID", + "access.form.discordbot_default_channel_id.tooltip": "这是什么?请参阅 https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", "access.form.dnsla_api_id.label": "DNS.LA API ID", "access.form.dnsla_api_id.placeholder": "请输入 DNS.LA API ID", "access.form.dnsla_api_id.tooltip": "这是什么?请参阅 https://www.dns.la/docs/ApiDoc", @@ -278,7 +284,7 @@ "access.form.mattermost_password.placeholder": "请输入 Mattermost 密码", "access.form.mattermost_default_channel_id.label": "默认的 Mattermost 频道 ID(可选)", "access.form.mattermost_default_channel_id.placeholder": "请输入默认的 Mattermost 频道 ID", - "access.form.mattermost_default_channel_id.tooltip": "如何获取频道 ID?从左侧边栏中选择目标频道,点击顶部的频道名称,选择“频道详情”,即可在弹出页面中直接看到频道 ID。", + "access.form.mattermost_default_channel_id.tooltip": "如何获取此参数?从左侧边栏中选择目标频道,点击顶部的频道名称,选择“频道详情”,即可在弹出页面中直接看到频道 ID。", "access.form.namecheap_username.label": "Namecheap 用户名", "access.form.namecheap_username.placeholder": "请输入 Namecheap 用户名", "access.form.namecheap_username.tooltip": "这是什么?请参阅 https://www.namecheap.com/support/api/intro/", @@ -374,12 +380,12 @@ "access.form.sslcom_eab_hmac_key.label": "ACME EAB HMAC key", "access.form.sslcom_eab_hmac_key.placeholder": "请输入 ACME EAB HMAC key", "access.form.sslcom_eab_hmac_key.tooltip": "这是什么?请参阅 https://www.ssl.com/how-to/generate-acme-credentials-for-reseller-customers/", - "access.form.telegram_bot_token.label": "Telegram 群机器人 API Token", - "access.form.telegram_bot_token.placeholder": "请输入 Telegram 群机器人 API Token", - "access.form.telegram_bot_token.tooltip": "如何获取机器人 API Token?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", - "access.form.telegram_bot_default_chat_id.label": "默认的 Telegram 会话 ID(可选)", - "access.form.telegram_bot_default_chat_id.placeholder": "请输入默认的 Telegram 会话 ID", - "access.form.telegram_bot_default_chat_id.tooltip": "如何获取会话 ID?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", + "access.form.telegrambot_token.label": "Telegram 机器人 API Token", + "access.form.telegrambot_token.placeholder": "请输入 Telegram 机器人 API Token", + "access.form.telegrambot_token.tooltip": "如何获取此参数?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", + "access.form.telegrambot_default_chat_id.label": "默认的 Telegram 会话 ID(可选)", + "access.form.telegrambot_default_chat_id.placeholder": "请输入默认的 Telegram 会话 ID", + "access.form.telegrambot_default_chat_id.tooltip": "如何获取此参数?请参阅 https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a", "access.form.tencentcloud_secret_id.label": "腾讯云 SecretId", "access.form.tencentcloud_secret_id.placeholder": "请输入腾讯云 SecretId", "access.form.tencentcloud_secret_id.tooltip": "这是什么?请参阅 https://cloud.tencent.com/document/product/598/40488", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index 8de1ccf5..dd12aa27 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -60,6 +60,7 @@ "provider.desec": "deSEC", "provider.digitalocean": "DigitalOcean", "provider.dingtalkbot": "钉钉群机器人", + "provider.discordbot": "Discord 机器人", "provider.dnsla": "DNS.LA", "provider.dogecloud": "多吉云", "provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN", @@ -119,7 +120,7 @@ "provider.safeline": "雷池", "provider.ssh": "SSH 部署", "provider.sslcom": "SSL.com", - "provider.telegrambot": "Telegram 群机器人", + "provider.telegrambot": "Telegram 机器人", "provider.tencentcloud": "腾讯云", "provider.tencentcloud.cdn": "腾讯云 - 内容分发网络 CDN", "provider.tencentcloud.clb": "腾讯云 - 负载均衡 CLB", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 2dbcb3ca..b6d27dc7 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -814,6 +814,9 @@ "workflow_node.notify.form.provider_access.placeholder": "请选择通知渠道授权", "workflow_node.notify.form.provider_access.button": "新建", "workflow_node.notify.form.params_config.label": "参数设置", + "workflow_node.notify.form.discordbot_channel_id.label": "Discord 频道 ID(可选)", + "workflow_node.notify.form.discordbot_channel_id.placeholder": "请输入 Discord 频道 ID 以覆盖默认值", + "workflow_node.notify.form.discordbot_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。", "workflow_node.notify.form.email_sender_address.label": "发送邮箱地址(可选)", "workflow_node.notify.form.email_sender_address.placeholder": "请输入发送邮箱地址以覆盖默认值", "workflow_node.notify.form.email_sender_address.tooltip": "不填写时,将使用所选通知渠道授权的默认发送邮箱地址。", @@ -823,9 +826,9 @@ "workflow_node.notify.form.mattermost_channel_id.label": "Mattermost 频道 ID(可选)", "workflow_node.notify.form.mattermost_channel_id.placeholder": "请输入 Mattermost 频道 ID 以覆盖默认值", "workflow_node.notify.form.mattermost_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。", - "workflow_node.notify.form.telegram_bot_chat_id.label": "Telegram 会话 ID(可选)", - "workflow_node.notify.form.telegram_bot_chat_id.placeholder": "请输入 Telegram 会话 ID 以覆盖默认值", - "workflow_node.notify.form.telegram_bot_chat_id.tooltip": "不填写时,将使用所选通知渠道授权的默认会话 ID。", + "workflow_node.notify.form.telegrambot_chat_id.label": "Telegram 会话 ID(可选)", + "workflow_node.notify.form.telegrambot_chat_id.placeholder": "请输入 Telegram 会话 ID 以覆盖默认值", + "workflow_node.notify.form.telegrambot_chat_id.tooltip": "不填写时,将使用所选通知渠道授权的默认会话 ID。", "workflow_node.notify.form.webhook_data.label": "Webhook 回调数据(可选)", "workflow_node.notify.form.webhook_data.placeholder": "请输入 Webhook 回调数据以覆盖默认值", "workflow_node.notify.form.webhook_data.tooltip": "不填写时,将使用所选部署目标授权的默认 Webhook 回调数据。", From e82a59289b2537ee3ee31c24a1823c9e5bb4033e Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 16:41:16 +0800 Subject: [PATCH 13/24] feat: new notification provider: slack bot --- internal/domain/access.go | 5 ++ internal/domain/provider.go | 2 + internal/notify/providers.go | 14 ++++ .../pkg/core/notifier/providers/bark/bark.go | 1 + .../providers/discordbot/discordbot.go | 1 + .../core/notifier/providers/gotify/gotify.go | 1 + .../providers/mattermost/mattermost.go | 2 + .../notifier/providers/pushover/pushover.go | 1 + .../notifier/providers/pushplus/pushplus.go | 1 + .../providers/serverchan/serverchan.go | 1 + .../notifier/providers/slackbot/slackbot.go | 70 ++++++++++++++++++ .../providers/slackbot/slackbot_test.go | 64 +++++++++++++++++ .../providers/telegrambot/telegrambot.go | 1 + .../notifier/providers/webhook/webhook.go | 2 +- .../notifier/providers/wecombot/wecombot.go | 1 + ui/public/imgs/providers/slack.svg | 1 + ui/src/components/access/AccessForm.tsx | 3 + .../access/AccessFormDiscordBotConfig.tsx | 3 +- .../access/AccessFormSlackBotConfig.tsx | 71 +++++++++++++++++++ .../access/AccessFormTelegramBotConfig.tsx | 3 +- .../workflow/node/NotifyNodeConfigForm.tsx | 3 + .../NotifyNodeConfigFormSlackBotConfig.tsx | 55 ++++++++++++++ ui/src/domain/access.ts | 6 ++ ui/src/domain/provider.ts | 4 ++ ui/src/i18n/locales/en/nls.access.json | 8 ++- ui/src/i18n/locales/en/nls.provider.json | 7 +- .../i18n/locales/en/nls.workflow.nodes.json | 3 + ui/src/i18n/locales/zh/nls.access.json | 8 ++- ui/src/i18n/locales/zh/nls.provider.json | 7 +- .../i18n/locales/zh/nls.workflow.nodes.json | 3 + 30 files changed, 341 insertions(+), 11 deletions(-) create mode 100644 internal/pkg/core/notifier/providers/slackbot/slackbot.go create mode 100644 internal/pkg/core/notifier/providers/slackbot/slackbot_test.go create mode 100644 ui/public/imgs/providers/slack.svg create mode 100644 ui/src/components/access/AccessFormSlackBotConfig.tsx create mode 100644 ui/src/components/workflow/node/NotifyNodeConfigFormSlackBotConfig.tsx diff --git a/internal/domain/access.go b/internal/domain/access.go index 4e002ecf..482f753a 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -300,6 +300,11 @@ type AccessConfigForSafeLine struct { AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"` } +type AccessConfigForSlackBot struct { + BotToken string `json:"botToken"` + DefaultChannelId string `json:"defaultChannelId,omitempty"` +} + type AccessConfigForSSH struct { Host string `json:"host"` Port int32 `json:"port"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 8f5c959a..c8cb37d5 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -71,6 +71,7 @@ const ( AccessProviderTypeRainYun = AccessProviderType("rainyun") AccessProviderTypeRatPanel = AccessProviderType("ratpanel") AccessProviderTypeSafeLine = AccessProviderType("safeline") + AccessProviderTypeSlackBot = AccessProviderType("slackbot") AccessProviderTypeSSH = AccessProviderType("ssh") AccessProviderTypeSSLCOM = AccessProviderType("sslcom") AccessProviderTypeTelegramBot = AccessProviderType("telegrambot") @@ -274,6 +275,7 @@ const ( NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail) NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot) NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost) + NotificationProviderTypeSlackBot = NotificationProviderType(AccessProviderTypeSlackBot) NotificationProviderTypeTelegramBot = NotificationProviderType(AccessProviderTypeTelegramBot) NotificationProviderTypeWebhook = NotificationProviderType(AccessProviderTypeWebhook) NotificationProviderTypeWeComBot = NotificationProviderType(AccessProviderTypeWeComBot) diff --git a/internal/notify/providers.go b/internal/notify/providers.go index 23f5bb43..7dc63465 100644 --- a/internal/notify/providers.go +++ b/internal/notify/providers.go @@ -11,6 +11,7 @@ import ( pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email" pLarkBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/larkbot" pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost" + pSlackBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/slackbot" pTelegramBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegrambot" pWebhook "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/webhook" pWeComBot "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/wecombot" @@ -101,6 +102,19 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier }) } + case domain.NotificationProviderTypeSlackBot: + { + access := domain.AccessConfigForSlackBot{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + return pSlackBot.NewNotifier(&pSlackBot.NotifierConfig{ + BotToken: access.BotToken, + ChannelId: maputil.GetOrDefaultString(options.ProviderServiceConfig, "channelId", access.DefaultChannelId), + }) + } + case domain.NotificationProviderTypeTelegramBot: { access := domain.AccessConfigForTelegramBot{} diff --git a/internal/pkg/core/notifier/providers/bark/bark.go b/internal/pkg/core/notifier/providers/bark/bark.go index 97ece0be..ec0d44f3 100644 --- a/internal/pkg/core/notifier/providers/bark/bark.go +++ b/internal/pkg/core/notifier/providers/bark/bark.go @@ -58,6 +58,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // REF: https://bark.day.app/#/tutorial req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "title": subject, diff --git a/internal/pkg/core/notifier/providers/discordbot/discordbot.go b/internal/pkg/core/notifier/providers/discordbot/discordbot.go index dbffaba7..3ed0cab7 100644 --- a/internal/pkg/core/notifier/providers/discordbot/discordbot.go +++ b/internal/pkg/core/notifier/providers/discordbot/discordbot.go @@ -51,6 +51,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://discord.com/developers/docs/resources/message#create-message req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetHeader("Authorization", "Bot "+n.config.BotToken). SetBody(map[string]any{ diff --git a/internal/pkg/core/notifier/providers/gotify/gotify.go b/internal/pkg/core/notifier/providers/gotify/gotify.go index 81dcb8ad..c82cd5a5 100644 --- a/internal/pkg/core/notifier/providers/gotify/gotify.go +++ b/internal/pkg/core/notifier/providers/gotify/gotify.go @@ -56,6 +56,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // REF: https://gotify.net/api-docs#/message/createMessage req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetHeader("Authorization", "Bearer "+n.config.Token). SetBody(map[string]any{ diff --git a/internal/pkg/core/notifier/providers/mattermost/mattermost.go b/internal/pkg/core/notifier/providers/mattermost/mattermost.go index 8e4fb24d..81283f7c 100644 --- a/internal/pkg/core/notifier/providers/mattermost/mattermost.go +++ b/internal/pkg/core/notifier/providers/mattermost/mattermost.go @@ -58,6 +58,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // REF: https://developers.mattermost.com/api-documentation/#/operations/Login loginReq := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "login_id": n.config.Username, @@ -74,6 +75,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // REF: https://developers.mattermost.com/api-documentation/#/operations/CreatePost postReq := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetHeader("Authorization", "Bearer "+loginResp.Header().Get("Token")). SetBody(map[string]any{ diff --git a/internal/pkg/core/notifier/providers/pushover/pushover.go b/internal/pkg/core/notifier/providers/pushover/pushover.go index b7f74bba..827a45d6 100644 --- a/internal/pkg/core/notifier/providers/pushover/pushover.go +++ b/internal/pkg/core/notifier/providers/pushover/pushover.go @@ -51,6 +51,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://pushover.net/api req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "title": subject, diff --git a/internal/pkg/core/notifier/providers/pushplus/pushplus.go b/internal/pkg/core/notifier/providers/pushplus/pushplus.go index 834f9683..79a27d49 100644 --- a/internal/pkg/core/notifier/providers/pushplus/pushplus.go +++ b/internal/pkg/core/notifier/providers/pushplus/pushplus.go @@ -50,6 +50,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://pushplus.plus/doc/guide/api.html#%E4%B8%80%E3%80%81%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3 req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "title": subject, diff --git a/internal/pkg/core/notifier/providers/serverchan/serverchan.go b/internal/pkg/core/notifier/providers/serverchan/serverchan.go index d74b2fcc..d1897ab4 100644 --- a/internal/pkg/core/notifier/providers/serverchan/serverchan.go +++ b/internal/pkg/core/notifier/providers/serverchan/serverchan.go @@ -49,6 +49,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://sct.ftqq.com/ req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "text": subject, diff --git a/internal/pkg/core/notifier/providers/slackbot/slackbot.go b/internal/pkg/core/notifier/providers/slackbot/slackbot.go new file mode 100644 index 00000000..7b16ad25 --- /dev/null +++ b/internal/pkg/core/notifier/providers/slackbot/slackbot.go @@ -0,0 +1,70 @@ +package discordbot + +import ( + "context" + "fmt" + "log/slog" + + "github.com/go-resty/resty/v2" + + "github.com/usual2970/certimate/internal/pkg/core/notifier" +) + +type NotifierConfig struct { + // Slack Bot API Token。 + BotToken string `json:"botToken"` + // Slack Channel ID。 + ChannelId string `json:"channelId"` +} + +type NotifierProvider struct { + config *NotifierConfig + logger *slog.Logger + httpClient *resty.Client +} + +var _ notifier.Notifier = (*NotifierProvider)(nil) + +func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) { + if config == nil { + panic("config is nil") + } + + client := resty.New() + + return &NotifierProvider{ + config: config, + logger: slog.Default(), + httpClient: client, + }, nil +} + +func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { + if logger == nil { + n.logger = slog.Default() + } else { + n.logger = logger + } + return n +} + +func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { + // REF: https://docs.slack.dev/messaging/sending-and-scheduling-messages#publishing + req := n.httpClient.R(). + SetContext(ctx). + SetHeader("Content-Type", "application/json"). + SetHeader("Authorization", "Bearer "+n.config.BotToken). + SetBody(map[string]any{ + "token": n.config.BotToken, + "channel": n.config.ChannelId, + "text": subject + "\n" + message, + }) + resp, err := req.Post("https://slack.com/api/chat.postMessage") + if err != nil { + return nil, fmt.Errorf("slack api error: failed to send request: %w", err) + } else if resp.IsError() { + return nil, fmt.Errorf("slack api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return ¬ifier.NotifyResult{}, nil +} diff --git a/internal/pkg/core/notifier/providers/slackbot/slackbot_test.go b/internal/pkg/core/notifier/providers/slackbot/slackbot_test.go new file mode 100644 index 00000000..356ef71f --- /dev/null +++ b/internal/pkg/core/notifier/providers/slackbot/slackbot_test.go @@ -0,0 +1,64 @@ +package discordbot_test + +import ( + "context" + "flag" + "fmt" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/slackbot" +) + +const ( + mockSubject = "test_subject" + mockMessage = "test_message" +) + +var ( + fApiToken string + fChannelId string +) + +func init() { + argsPrefix := "CERTIMATE_NOTIFIER_SLACKBOT_" + + flag.StringVar(&fApiToken, argsPrefix+"APITOKEN", "", "") + flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", 0, "") +} + +/* +Shell command to run this test: + + go test -v ./slackbot_test.go -args \ + --CERTIMATE_NOTIFIER_SLACKBOT_APITOKEN="your-bot-token" \ + --CERTIMATE_NOTIFIER_SLACKBOT_CHANNELID="your-channel-id" +*/ +func TestNotify(t *testing.T) { + flag.Parse() + + t.Run("Notify", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("APITOKEN: %v", fApiToken), + fmt.Sprintf("CHANNELID: %v", fChannelId), + }, "\n")) + + notifier, err := provider.NewNotifier(&provider.NotifierConfig{ + BotToken: fApiToken, + ChannelId: fChannelId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + res, err := notifier.Notify(context.Background(), mockSubject, mockMessage) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go b/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go index 99b86a38..39e1f705 100644 --- a/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go +++ b/internal/pkg/core/notifier/providers/telegrambot/telegrambot.go @@ -51,6 +51,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://core.telegram.org/bots/api#sendmessage req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "chat_id": n.config.ChatId, diff --git a/internal/pkg/core/notifier/providers/webhook/webhook.go b/internal/pkg/core/notifier/providers/webhook/webhook.go index 5f62f170..8850ea73 100644 --- a/internal/pkg/core/notifier/providers/webhook/webhook.go +++ b/internal/pkg/core/notifier/providers/webhook/webhook.go @@ -139,7 +139,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s // 生成请求 // 其中 GET 请求需转换为查询参数 - req := n.httpClient.R().SetHeaderMultiValues(webhookHeaders) + req := n.httpClient.R().SetContext(ctx).SetHeaderMultiValues(webhookHeaders) req.URL = webhookUrl.String() req.Method = webhookMethod if webhookMethod == http.MethodGet { diff --git a/internal/pkg/core/notifier/providers/wecombot/wecombot.go b/internal/pkg/core/notifier/providers/wecombot/wecombot.go index 36c179d4..8f51a70a 100644 --- a/internal/pkg/core/notifier/providers/wecombot/wecombot.go +++ b/internal/pkg/core/notifier/providers/wecombot/wecombot.go @@ -49,6 +49,7 @@ func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier { func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) { // REF: https://developer.work.weixin.qq.com/document/path/91770 req := n.httpClient.R(). + SetContext(ctx). SetHeader("Content-Type", "application/json"). SetBody(map[string]any{ "msgtype": "text", diff --git a/ui/public/imgs/providers/slack.svg b/ui/public/imgs/providers/slack.svg new file mode 100644 index 00000000..100af3d4 --- /dev/null +++ b/ui/public/imgs/providers/slack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index 2a28aaac..c8c03290 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -64,6 +64,7 @@ import AccessFormQiniuConfig from "./AccessFormQiniuConfig"; import AccessFormRainYunConfig from "./AccessFormRainYunConfig"; import AccessFormRatPanelConfig from "./AccessFormRatPanelConfig"; import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig"; +import AccessFormSlackBotConfig from "./AccessFormSlackBotConfig"; import AccessFormSSHConfig from "./AccessFormSSHConfig"; import AccessFormSSLComConfig from "./AccessFormSSLComConfig"; import AccessFormTelegramBotConfig from "./AccessFormTelegramBotConfig"; @@ -289,6 +290,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.SAFELINE: return ; + case ACCESS_PROVIDERS.SLACKBOT: + return ; case ACCESS_PROVIDERS.SSH: return ; case ACCESS_PROVIDERS.TELEGRAMBOT: diff --git a/ui/src/components/access/AccessFormDiscordBotConfig.tsx b/ui/src/components/access/AccessFormDiscordBotConfig.tsx index 5f844ccc..16848686 100644 --- a/ui/src/components/access/AccessFormDiscordBotConfig.tsx +++ b/ui/src/components/access/AccessFormDiscordBotConfig.tsx @@ -28,7 +28,8 @@ const AccessFormDiscordBotConfig = ({ form: formInst, formName, disabled, initia botToken: z .string({ message: t("access.form.discordbot_token.placeholder") }) .min(1, t("access.form.discordbot_token.placeholder")) - .max(256, t("common.errmsg.string_max", { max: 256 })), + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), defaultChannelId: z.string().nullish(), }); const formRule = createSchemaFieldRule(formSchema); diff --git a/ui/src/components/access/AccessFormSlackBotConfig.tsx b/ui/src/components/access/AccessFormSlackBotConfig.tsx new file mode 100644 index 00000000..3bea5f58 --- /dev/null +++ b/ui/src/components/access/AccessFormSlackBotConfig.tsx @@ -0,0 +1,71 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForSlackBot } from "@/domain/access"; + +type AccessFormSlackBotConfigFieldValues = Nullish; + +export type AccessFormSlackBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormSlackBotConfigFieldValues; + onValuesChange?: (values: AccessFormSlackBotConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormSlackBotConfigFieldValues => { + return { + botToken: "", + }; +}; + +const AccessFormSlackBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormSlackBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + botToken: z + .string({ message: t("access.form.slackbot_token.placeholder") }) + .min(1, t("access.form.slackbot_token.placeholder")) + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), + defaultChannelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormSlackBotConfig; diff --git a/ui/src/components/access/AccessFormTelegramBotConfig.tsx b/ui/src/components/access/AccessFormTelegramBotConfig.tsx index a347610f..82747694 100644 --- a/ui/src/components/access/AccessFormTelegramBotConfig.tsx +++ b/ui/src/components/access/AccessFormTelegramBotConfig.tsx @@ -28,7 +28,8 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi botToken: z .string({ message: t("access.form.telegrambot_token.placeholder") }) .min(1, t("access.form.telegrambot_token.placeholder")) - .max(256, t("common.errmsg.string_max", { max: 256 })), + .max(256, t("common.errmsg.string_max", { max: 256 })) + .trim(), defaultChatId: z .preprocess( (v) => (v == null || v === "" ? undefined : Number(v)), diff --git a/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx b/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx index 44a4a604..3c612df8 100644 --- a/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/NotifyNodeConfigForm.tsx @@ -20,6 +20,7 @@ import { useNotifyChannelsStore } from "@/stores/notify"; import NotifyNodeConfigFormDiscordBotConfig from "./NotifyNodeConfigFormDiscordBotConfig"; import NotifyNodeConfigFormEmailConfig from "./NotifyNodeConfigFormEmailConfig"; import NotifyNodeConfigFormMattermostConfig from "./NotifyNodeConfigFormMattermostConfig"; +import NotifyNodeConfigFormSlackBotConfig from "./NotifyNodeConfigFormSlackBotConfig"; import NotifyNodeConfigFormTelegramBotConfig from "./NotifyNodeConfigFormTelegramBotConfig"; import NotifyNodeConfigFormWebhookConfig from "./NotifyNodeConfigFormWebhookConfig"; @@ -117,6 +118,8 @@ const NotifyNodeConfigForm = forwardRef; case NOTIFICATION_PROVIDERS.MATTERMOST: return ; + case NOTIFICATION_PROVIDERS.SLACKBOT: + return ; case NOTIFICATION_PROVIDERS.TELEGRAMBOT: return ; case NOTIFICATION_PROVIDERS.WEBHOOK: diff --git a/ui/src/components/workflow/node/NotifyNodeConfigFormSlackBotConfig.tsx b/ui/src/components/workflow/node/NotifyNodeConfigFormSlackBotConfig.tsx new file mode 100644 index 00000000..5c304060 --- /dev/null +++ b/ui/src/components/workflow/node/NotifyNodeConfigFormSlackBotConfig.tsx @@ -0,0 +1,55 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type NotifyNodeConfigFormSlackBotConfigFieldValues = Nullish<{ + channelId?: string; +}>; + +export type NotifyNodeConfigFormSlackBotConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: NotifyNodeConfigFormSlackBotConfigFieldValues; + onValuesChange?: (values: NotifyNodeConfigFormSlackBotConfigFieldValues) => void; +}; + +const initFormModel = (): NotifyNodeConfigFormSlackBotConfigFieldValues => { + return {}; +}; + +const NotifyNodeConfigFormSlackBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: NotifyNodeConfigFormSlackBotConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + channelId: z.string().nullish(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default NotifyNodeConfigFormSlackBotConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 9e953963..3582b071 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -58,6 +58,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForRainYun | AccessConfigForRatPanel | AccessConfigForSafeLine + | AccessConfigForSlackBot | AccessConfigForSSH | AccessConfigForSSLCom | AccessConfigForTelegramBot @@ -361,6 +362,11 @@ export type AccessConfigForSafeLine = { allowInsecureConnections?: boolean; }; +export type AccessConfigForSlackBot = { + botToken: string; + defaultChannelId?: string; +}; + export type AccessConfigForSSH = { host: string; port: number; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 806c8283..2d288a3c 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -61,6 +61,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ RAINYUN: "rainyun", RATPANEL: "ratpanel", SAFELINE: "safeline", + SLACKBOT: "slackbot", SSH: "ssh", SSLCOM: "sslcom", TELEGRAMBOT: "telegrambot", @@ -174,6 +175,7 @@ export const accessProvidersMap: Map [ @@ -594,6 +596,7 @@ export const NOTIFICATION_PROVIDERS = Object.freeze({ EMAIL: `${ACCESS_PROVIDERS.EMAIL}`, LARKBOT: `${ACCESS_PROVIDERS.LARKBOT}`, MATTERMOST: `${ACCESS_PROVIDERS.MATTERMOST}`, + SLACKBOT: `${ACCESS_PROVIDERS.SLACKBOT}`, TELEGRAMBOT: `${ACCESS_PROVIDERS.TELEGRAMBOT}`, WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`, WECOMBOT: `${ACCESS_PROVIDERS.WECOMBOT}`, @@ -620,6 +623,7 @@ export const notificationProvidersMap: Map [ diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index fe437f87..98717976 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -163,7 +163,7 @@ "access.form.dingtalkbot_secret.tooltip": "For more information, see https://open.dingtalk.com/document/orgapp/customize-robot-security-settings", "access.form.discordbot_token.label": "Discord bot token", "access.form.discordbot_token.placeholder": "Please enter Discord bot token", - "access.form.discordbot_token.tooltip": "How to get it? Please refer to https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token", + "access.form.discordbot_token.tooltip": "For more information, see https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token", "access.form.discordbot_default_channel_id.label": "Default Discord channel ID (Optional)", "access.form.discordbot_default_channel_id.placeholder": "Please enter default Discord channel ID", "access.form.discordbot_default_channel_id.tooltip": "For more information, see https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", @@ -362,6 +362,12 @@ "access.form.safeline_api_token.label": "SafeLine API token", "access.form.safeline_api_token.placeholder": "Please enter SafeLine API token", "access.form.safeline_api_token.tooltip": "For more information, see https://docs.waf.chaitin.com/en/reference/articles/openapi", + "access.form.slackbot_token.label": "Slack bot token", + "access.form.slackbot_token.placeholder": "Please enter Slack bot token", + "access.form.slackbot_token.tooltip": "For more information, see https://docs.slack.dev/authentication/tokens#bot", + "access.form.slackbot_default_channel_id.label": "Default Slack channel ID (Optional)", + "access.form.slackbot_default_channel_id.placeholder": "Please enter default Slack channel ID", + "access.form.slackbot_default_channel_id.tooltip": "How to get it? Please refer to https://www.youtube.com/watch?v=Uz5Yi5C2pwQ", "access.form.ssh_host.label": "Server host", "access.form.ssh_host.placeholder": "Please enter server host", "access.form.ssh_port.label": "Server port", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index b34c92a7..85966786 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -68,7 +68,7 @@ "provider.dynv6": "dynv6", "provider.edgio": "Edgio", "provider.edgio.applications": "Edgio - Applications", - "provider.email": "Email", + "provider.email": "Email (SMTP)", "provider.fastly": "Fastly", "provider.flexcdn": "FlexCDN", "provider.gcore": "Gcore", @@ -96,7 +96,7 @@ "provider.lecdn": "LeCDN", "provider.letsencrypt": "Let's Encrypt", "provider.letsencryptstaging": "Let's Encrypt Staging Environment", - "provider.local": "Local deployment", + "provider.local": "Local host", "provider.mattermost": "Mattermost", "provider.namecheap": "Namecheap", "provider.namedotcom": "Name.com", @@ -118,7 +118,8 @@ "provider.ratpanel.console": "RatPanel - Console", "provider.ratpanel.site": "RatPanel - Website", "provider.safeline": "SafeLine", - "provider.ssh": "SSH deployment", + "provider.slackbot": "Slack Bot", + "provider.ssh": "Remote host (SSH)", "provider.sslcom": "SSL.com", "provider.telegrambot": "Telegram Bot", "provider.tencentcloud": "Tencent Cloud", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 172d707b..92989ac2 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -827,6 +827,9 @@ "workflow_node.notify.form.mattermost_channel_id.label": "Mattermost channel ID (Optional)", "workflow_node.notify.form.mattermost_channel_id.placeholder": "Please enter Mattermost channel ID to override the default value", "workflow_node.notify.form.mattermost_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.", + "workflow_node.notify.form.slackbot_channel_id.label": "Slack channel ID (Optional)", + "workflow_node.notify.form.slackbot_channel_id.placeholder": "Please enter Slack channel ID to override the default value", + "workflow_node.notify.form.slackbot_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.", "workflow_node.notify.form.telegrambot_chat_id.label": "Telegram chat ID (Optional)", "workflow_node.notify.form.telegrambot_chat_id.placeholder": "Please enter Telegram chat ID to override the default value", "workflow_node.notify.form.telegrambot_chat_id.tooltip": "Leave it blank to use the default chat ID provided by the selected authorization.", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 21ad79c5..7e184d55 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -157,7 +157,7 @@ "access.form.dingtalkbot_secret.tooltip": "这是什么?请参阅 https://open.dingtalk.com/document/orgapp/customize-robot-security-settings", "access.form.discordbot_token.label": "Discord 机器人 API Token", "access.form.discordbot_token.placeholder": "请输入 Discord 机器人 API Token", - "access.form.discordbot_token.tooltip": "如何获取此参数?请参阅 https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token", + "access.form.discordbot_token.tooltip": "这是什么?请参阅 https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token", "access.form.discordbot_default_channel_id.label": "默认的 Discord 频道 ID(可选)", "access.form.discordbot_default_channel_id.placeholder": "请输入默认的 Discord 频道 ID", "access.form.discordbot_default_channel_id.tooltip": "这是什么?请参阅 https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID", @@ -356,6 +356,12 @@ "access.form.safeline_api_token.label": "雷池 API Token", "access.form.safeline_api_token.placeholder": "请输入雷池 API Token", "access.form.safeline_api_token.tooltip": "这是什么?请参阅 https://docs.waf-ce.chaitin.cn/zh/更多技术文档/OPENAPI", + "access.form.slackbot_token.label": "Slack 机器人 Token", + "access.form.slackbot_token.placeholder": "请输入 Slack 机器人 Token", + "access.form.slackbot_token.tooltip": "这是什么?请参阅 https://docs.slack.dev/authentication/tokens#bot", + "access.form.slackbot_default_channel_id.label": "默认的 Slack 频道 ID(可选)", + "access.form.slackbot_default_channel_id.placeholder": "请输入默认的 Slack 频道 ID", + "access.form.slackbot_default_channel_id.tooltip": "如何获取此参数?请参阅 https://www.youtube.com/watch?v=Uz5Yi5C2pwQ", "access.form.ssh_host.label": "服务器地址", "access.form.ssh_host.placeholder": "请输入服务器地址", "access.form.ssh_port.label": "服务器端口", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index dd12aa27..c4e126e0 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -68,7 +68,7 @@ "provider.dynv6": "dynv6", "provider.edgio": "Edgio", "provider.edgio.applications": "Edgio - Applications", - "provider.email": "邮件", + "provider.email": "邮件(SMTP)", "provider.fastly": "Fastly", "provider.flexcdn": "FlexCDN", "provider.gcore": "Gcore", @@ -96,7 +96,7 @@ "provider.lecdn": "LeCDN", "provider.letsencrypt": "Let's Encrypt", "provider.letsencryptstaging": "Let's Encrypt 测试环境", - "provider.local": "本地部署", + "provider.local": "本地主机", "provider.mattermost": "Mattermost", "provider.namecheap": "Namecheap", "provider.namedotcom": "Name.com", @@ -118,7 +118,8 @@ "provider.ratpanel.console": "耗子面板 - 控制台", "provider.ratpanel.site": "耗子面板 - 网站", "provider.safeline": "雷池", - "provider.ssh": "SSH 部署", + "provider.slackbot": "Slack 机器人", + "provider.ssh": "远程主机(SSH)", "provider.sslcom": "SSL.com", "provider.telegrambot": "Telegram 机器人", "provider.tencentcloud": "腾讯云", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index b6d27dc7..87f0076b 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -826,6 +826,9 @@ "workflow_node.notify.form.mattermost_channel_id.label": "Mattermost 频道 ID(可选)", "workflow_node.notify.form.mattermost_channel_id.placeholder": "请输入 Mattermost 频道 ID 以覆盖默认值", "workflow_node.notify.form.mattermost_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。", + "workflow_node.notify.form.slackbot_channel_id.label": "Slack 频道 ID(可选)", + "workflow_node.notify.form.slackbot_channel_id.placeholder": "请输入 Slack 频道 ID 以覆盖默认值", + "workflow_node.notify.form.slackbot_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。", "workflow_node.notify.form.telegrambot_chat_id.label": "Telegram 会话 ID(可选)", "workflow_node.notify.form.telegrambot_chat_id.placeholder": "请输入 Telegram 会话 ID 以覆盖默认值", "workflow_node.notify.form.telegrambot_chat_id.tooltip": "不填写时,将使用所选通知渠道授权的默认会话 ID。", From b0eb71421f8e50fb7e6120454581e48fdf4719a7 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 16:55:15 +0800 Subject: [PATCH 14/24] chore(deps): upgrade go mod dependencies --- go.mod | 46 +++++++++++------------ go.sum | 117 +++++++++++++++++++++++++++++---------------------------- 2 files changed, 82 insertions(+), 81 deletions(-) diff --git a/go.mod b/go.mod index 37686d8c..0f12bd45 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.24.3 require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.3.1 github.com/Edgio/edgio-api v0.0.0-workspace github.com/G-Core/gcorelabscdn-go v1.0.31 @@ -14,10 +14,10 @@ require ( github.com/alibabacloud-go/apig-20240327/v3 v3.2.2 github.com/alibabacloud-go/cas-20200407/v3 v3.0.4 github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2 - github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3 + github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.4 github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.7 github.com/alibabacloud-go/ddoscoo-20200101/v4 v4.0.0 - github.com/alibabacloud-go/esa-20240910/v2 v2.32.0 + github.com/alibabacloud-go/esa-20240910/v2 v2.33.0 github.com/alibabacloud-go/fc-20230330/v4 v4.3.5 github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 github.com/alibabacloud-go/ga-20191120/v3 v3.1.8 @@ -26,18 +26,18 @@ require ( github.com/alibabacloud-go/slb-20140515/v4 v4.0.10 github.com/alibabacloud-go/tea v1.3.9 github.com/alibabacloud-go/vod-20170321/v4 v4.8.4 - github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2 + github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.3 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/aws/aws-sdk-go-v2/service/acm v1.32.0 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.46.1 - github.com/baidubce/bce-sdk-go v0.9.226 + github.com/baidubce/bce-sdk-go v0.9.228 github.com/blinkbean/dingtalk v1.1.3 github.com/byteplus-sdk/byteplus-sdk-golang v1.0.46 github.com/go-acme/lego/v4 v4.23.1 github.com/go-lark/lark v1.16.0 github.com/go-resty/resty/v2 v2.16.5 github.com/go-viper/mapstructure/v2 v2.2.1 - github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148 + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150 github.com/jdcloud-api/jdcloud-sdk-go v1.64.0 github.com/libdns/dynv6 v1.0.0 github.com/libdns/libdns v0.2.3 @@ -45,29 +45,29 @@ require ( github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 github.com/pkg/sftp v1.13.9 github.com/pocketbase/dbx v1.11.0 - github.com/pocketbase/pocketbase v0.28.0 + github.com/pocketbase/pocketbase v0.28.2 github.com/povsister/scp v0.0.0-20250504051308-e467f71ea63c github.com/qiniu/go-sdk/v7 v7.25.3 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1166 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1173 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162 - github.com/ucloud/ucloud-sdk-go v0.22.35 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1169 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1166 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1164 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1170 + github.com/ucloud/ucloud-sdk-go v0.22.41 github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12 - github.com/volcengine/volc-sdk-golang v1.0.207 - github.com/volcengine/volcengine-go-sdk v1.1.7 + github.com/volcengine/volc-sdk-golang v1.0.208 + github.com/volcengine/volcengine-go-sdk v1.1.8 gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 golang.org/x/crypto v0.38.0 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 - k8s.io/api v0.33.0 - k8s.io/apimachinery v0.33.0 - k8s.io/client-go v0.33.0 + k8s.io/api v0.33.1 + k8s.io/apimachinery v0.33.1 + k8s.io/client-go v0.33.1 software.sslmate.com/src/go-pkcs12 v0.5.0 ) @@ -211,10 +211,10 @@ require ( google.golang.org/protobuf v1.36.5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.62.1 // indirect + modernc.org/libc v1.65.7 // indirect modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.9.1 // indirect - modernc.org/sqlite v1.37.0 // indirect + modernc.org/memory v1.11.0 // indirect + modernc.org/sqlite v1.37.1 // indirect ) replace github.com/Edgio/edgio-api v0.0.0-workspace => ./internal/pkg/sdk3rd/edgio/edgio-api@v0.0.0-workspace diff --git a/go.sum b/go.sum index 5a64f77a..7bbf5848 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,8 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4 github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= @@ -101,8 +101,8 @@ github.com/alibabacloud-go/cas-20200407/v3 v3.0.4 h1:ngRlctbt135zoujwX0lXSv9m4h1 github.com/alibabacloud-go/cas-20200407/v3 v3.0.4/go.mod h1:6n9MZ9SH3HlSzfe2oKwjOqhJx3dxvW2gMDO+lq8t9U4= github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2 h1:+KJOPukTM+xMyiLOW5qBwYKG2df3Ar7coRsqc1juKO8= github.com/alibabacloud-go/cdn-20180510/v5 v5.2.2/go.mod h1:GnPiPL3HlzCi8SGiLiVgKrAFkP1vTtcF4yGtjsl4wfo= -github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3 h1:OTLn0ShbE0jJj+5Z+P76zeHsZYxZjO7YVThQoeaBM9M= -github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.3/go.mod h1:eUmD1G4BjEBOAHIeJrHJL7pyLGgXSRTPLjBmYY7uPEg= +github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.4 h1:SsyAoXM1R4J3I4xQdPW/rRW8cTo2KN440/4h/pGiwRQ= +github.com/alibabacloud-go/cloudapi-20160714/v5 v5.7.4/go.mod h1:eUmD1G4BjEBOAHIeJrHJL7pyLGgXSRTPLjBmYY7uPEg= github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY= github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE= @@ -132,8 +132,8 @@ github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/ql github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8= github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= -github.com/alibabacloud-go/esa-20240910/v2 v2.32.0 h1:eudSgNIkCg6huIu3HuF16BJG6+CA6bIuIddxpuPydpg= -github.com/alibabacloud-go/esa-20240910/v2 v2.32.0/go.mod h1:HZS5PmYJvcmH4vrJYuCvK3AnYzD9hLlO8CT0hgRyDXo= +github.com/alibabacloud-go/esa-20240910/v2 v2.33.0 h1:10IWxrMcF1W6/7BUJIJifrofduSG0wRFqDbRfIsR3Zw= +github.com/alibabacloud-go/esa-20240910/v2 v2.33.0/go.mod h1:HZS5PmYJvcmH4vrJYuCvK3AnYzD9hLlO8CT0hgRyDXo= github.com/alibabacloud-go/fc-20230330/v4 v4.3.5 h1:nDNjVzGwkQPbQnAuxAmxvS9x8QGLph8j0ptEdZDPGBA= github.com/alibabacloud-go/fc-20230330/v4 v4.3.5/go.mod h1:vEJimQ6E/e+m2z0/oXdeQWlFw/Pi/Ar6NKcMrSvcILE= github.com/alibabacloud-go/fc-open-20210406/v2 v2.0.12 h1:A3D8Mp6qf8DfR6Dt5MpS8aDVaWfS4N85T5CvGUvgrjM= @@ -190,8 +190,8 @@ github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzY github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alibabacloud-go/vod-20170321/v4 v4.8.4 h1:MYP2xfrcud8vlWljQ4lhemNgAgi9/AUAa450n8TUXZo= github.com/alibabacloud-go/vod-20170321/v4 v4.8.4/go.mod h1:5ocQ6hIc9tpGixD2iy099aOGwIgpzjT2le4Krd4aLn8= -github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2 h1:CmhJzCZ5RiSiWU6BV2XJUtIMD2LDo9FFfqlYGtx1aAw= -github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.2/go.mod h1:9itYSTzipL3NlvhvNYfTjFaapoZzG68nlu/KUdh9SpA= +github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.3 h1:25tmcJxIitrk55crBGssPlqRzmFcpGVW5TEFxdUvfg0= +github.com/alibabacloud-go/waf-openapi-20211001/v5 v5.1.3/go.mod h1:9itYSTzipL3NlvhvNYfTjFaapoZzG68nlu/KUdh9SpA= github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 h1:yUkCbrSM1cWtgBfRVKMQtdt22KhDvKY7g4V+92eG9wA= github.com/aliyun/alibaba-cloud-sdk-go v1.63.100/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= @@ -250,8 +250,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjK github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= -github.com/baidubce/bce-sdk-go v0.9.226 h1:VKEKcJC9P33yIfYJZr12Q/4Bvj18RFbgO8w8XOfU8AI= -github.com/baidubce/bce-sdk-go v0.9.226/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= +github.com/baidubce/bce-sdk-go v0.9.228 h1:XEY3/oAxXcsi7+3atib9fMI6YNE9sL5qo+WMZ+iqmNE= +github.com/baidubce/bce-sdk-go v0.9.228/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -545,8 +545,8 @@ github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148 h1:PdWSbniKnPhKe1B19KUHW/9ahYbFH2EY6Iq6sxOnomo= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.148/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150 h1:Ih+z79Ko1ClH4dlv7O1lyHRiVCjkb2NZYYk+1cSZbU8= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.150/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -737,8 +737,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pocketbase/dbx v1.11.0 h1:LpZezioMfT3K4tLrqA55wWFw1EtH1pM4tzSVa7kgszU= github.com/pocketbase/dbx v1.11.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs= -github.com/pocketbase/pocketbase v0.28.0 h1:dnMHSO0wuYpKs6oP3X5buw1lY9ptd8zy1fTjN+Ae+mA= -github.com/pocketbase/pocketbase v0.28.0/go.mod h1:WE6xMM4+pxKIVNl4B1mcOEZXlDvPGl7cZ64TW2iXHdI= +github.com/pocketbase/pocketbase v0.28.2 h1:b6cfUfr5d4whvUFGFhI8oHRzx/eB76GCUQGftqgv9lM= +github.com/pocketbase/pocketbase v0.28.2/go.mod h1:ElwIYS1b5xS9w0U7AK7tsm6FuC0lzw57H8p/118Cu7g= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/povsister/scp v0.0.0-20250504051308-e467f71ea63c h1:1+j5JHz9mUzYSp0scuF6hzvJP28EDBFe5eBJb0xnGk4= @@ -771,8 +771,8 @@ github.com/qiniu/x v1.10.5 h1:7V/CYWEmo9axJULvrJN6sMYh2FdY+esN5h8jwDkA4b0= github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -829,31 +829,32 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155 h1:ildxJtjnqiKZxWDVKHT/ncIknGDijtg60MuFELON8bY= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdn v1.0.1155/go.mod h1:iLASpooTdyXtx642E5Ws7cfWENsp4/uZ/78TFoln7OI= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161 h1:yGFg9/6j3NP10r9PfSWHfekuq4SwPyqblWnfISfKANo= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1161/go.mod h1:9MzQSEULYm5wHAKz8R3oQ8ovg4vWeLFzn0DmRWTc6zg= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1120/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1124/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1166 h1:cEoDsBt7vGh7YtfVHVmgXKQURZANBE8UKK/So84QUdU= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/clb v1.0.1166/go.mod h1:0o5Cfgdh+bAx7kpQ5a5wce/ZUiDvy4Md8NcbrLtk6i8= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1150/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1155/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1160/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1161/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162 h1:bscCBygP9JRl6iNabF+vmBOhY+xayFFGYV5Wa0NzH0A= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1162/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1164/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1166/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1169/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1170/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1172/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1173 h1:W5bzEWiJwiwRZR0/P1l78OYWUXYsXLjhBaQ64c+9+fk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1173/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150 h1:RQQYfZOFYlkxKR2+xp8el3+8xs9DhxBy+ajlHtapqtQ= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/live v1.0.1150/go.mod h1:zpfr6EBWy7ClASTGUgIy01Gn4R79UXf+2QGQeyR124A= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120 h1:z0t0lb5h1mZirXftO8MRg25COYZHx0ubQjSPhZT/LY0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1120/go.mod h1:IFZL44Keyl+MHrhpFwUaQmJvMDwGr+t+cUfFAC+74lU= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124 h1:LQKAlxFb0sYiE8ojK5h9+seuFzogoJtYnXmiRF+4F4Q= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124/go.mod h1:tYbK0FbHVG+78od7eZpzczE8qk0JWKO/osTQWuiJ3Fo= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162 h1:z/JF+JGi6bGf8vnK9ZeVXz+1Q3ih8nF6KjThxhtIrNc= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1162/go.mod h1:rsO8JCP+WQeLQ32wAB4oRRjsEz0O+kvCGDqc7Ze1jc4= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160 h1:aNVEDS1yQ7sLfXOOQ/bF3eljFjyvHoJ/J8qSC9mC9gw= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1160/go.mod h1:kf6NQmKK6sh1ACwh8iliBy7I/burd+AWusNz6zbDvLM= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162 h1:gnmuUaoFAShc9FKj3Omswu3n08bHM/sGsl8xjFAkFNs= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1162/go.mod h1:bu3KAFeoJ1xDGQp72h9Le3FqbOcCcdomOUig3OqgcE4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172 h1:6SUO0hTie3zxnUEMxmhnS1iRIXpAukSZV27Nrx4NwIk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/scf v1.0.1172/go.mod h1:tmN4zfu70SD0iee3qfpc09NRLel30zGoAuzIs4X0Kfs= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1169 h1:oBymtJEmKDnS2NIR0PDLd+xCGQ+7uMoEt7zEB5Q3x8U= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1169/go.mod h1:K27PNEgRJ602ESXUNnlRnCkf1+XYHI6RVP/ylIe/Aro= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1166 h1:+kIsoG2If/0y15PpZsXfT0QqTuwec9nMgo1JP8KQMkw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/teo v1.0.1166/go.mod h1:lGmBMXqe3vBg6Bi9h4mVpWLKd7jQIMRbndr8KNHBqSw= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1164 h1:caaIGWOs/JtWOK5ptVMEiiGgzU7Jf6UpMv+9IbIa4vs= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.1164/go.mod h1:SJI2mc77gDC7Tw1QoF/4d5SwLvz8HQFUecWtIXb+r/Q= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1170 h1:kcQCWuI9zOkZgL5CK66HNAJmSWCSJxRrDxXT+j02CeE= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/waf v1.0.1170/go.mod h1:vTukVfThbBIc4lOf4eq/q51eEk78oZUJd2lAoJBOJwI= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= @@ -862,18 +863,18 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ucloud/ucloud-sdk-go v0.22.35 h1:Q4CY3Ae5813jmNUrGdCjc8tlyleL5Lyl0APnpK5L4sk= -github.com/ucloud/ucloud-sdk-go v0.22.35/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw= +github.com/ucloud/ucloud-sdk-go v0.22.41 h1:JndTJhCx7A1wggZfVb4KMm7D0Wfvd/HkmQVfSNjClQA= +github.com/ucloud/ucloud-sdk-go v0.22.41/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12 h1:u9+32DXQIOFPG8oQ3xrjSAUSyAcaq5bqO4cEBom/6lA= github.com/volcengine/ve-tos-golang-sdk/v2 v2.7.12/go.mod h1:IrjK84IJJTuOZOTMv/P18Ydjy/x+ow7fF7q11jAxXLM= github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU= -github.com/volcengine/volc-sdk-golang v1.0.207 h1:1OJ/nC92dF1URRoyO1AHSghCob12NT1PAA/GoK8uU18= -github.com/volcengine/volc-sdk-golang v1.0.207/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= -github.com/volcengine/volcengine-go-sdk v1.1.7 h1:5ElF1inqX1QUKX8/XGk+HGpG+F01W+m73cLQH+0x50s= -github.com/volcengine/volcengine-go-sdk v1.1.7/go.mod h1:EyKoi6t6eZxoPNGr2GdFCZti2Skd7MO3eUzx7TtSvNo= +github.com/volcengine/volc-sdk-golang v1.0.208 h1:DyGUPjEKhWS08BkptfqenXTuUq+LKb7+gX/RBAtNl8w= +github.com/volcengine/volc-sdk-golang v1.0.208/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= +github.com/volcengine/volcengine-go-sdk v1.1.8 h1:/T2p7qeeLWWhGrhtB00b8VNlE32S266LcO+jqFUYwzY= +github.com/volcengine/volcengine-go-sdk v1.1.8/go.mod h1:EyKoi6t6eZxoPNGr2GdFCZti2Skd7MO3eUzx7TtSvNo= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1394,39 +1395,39 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= -k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= -k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= -k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= -k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= +k8s.io/api v0.33.1 h1:tA6Cf3bHnLIrUK4IqEgb2v++/GYUtqiu9sRVk3iBXyw= +k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw= +k8s.io/apimachinery v0.33.1 h1:mzqXWV8tW9Rw4VeW9rEkqvnxj59k1ezDUl20tFK/oM4= +k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.1 h1:ZZV/Ks2g92cyxWkRRnfUDsnhNn28eFpt26aGc8KbXF4= +k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic= -modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= -modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU= -modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw= +modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s= +modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= +modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE= modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= -modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8= +modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s= -modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo= +modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00= +modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g= -modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= -modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= +modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs= +modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= From 3bb88d9f93f058a1f812c3a0d532012378372a32 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 17:04:40 +0800 Subject: [PATCH 15/24] chore(deps): upgrade npm dependencies --- ui/package-lock.json | 226 +++++++++++++++++++++++-------------------- ui/package.json | 22 ++--- 2 files changed, 131 insertions(+), 117 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index 44a4d2b6..e1bbb340 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -17,27 +17,27 @@ "@uiw/codemirror-extensions-basic-setup": "^4.23.12", "@uiw/codemirror-theme-vscode": "^4.23.12", "@uiw/react-codemirror": "^4.23.12", - "ahooks": "^3.8.4", - "antd": "^5.25.1", + "ahooks": "^3.8.5", + "antd": "^5.25.3", "antd-zod": "^6.1.0", "clsx": "^2.1.1", "cron-parser": "^5.2.0", "file-saver": "^2.0.5", - "i18next": "^25.1.2", + "i18next": "^25.2.1", "i18next-browser-languagedetector": "^8.1.0", "immer": "^10.1.1", - "lucide-react": "^0.509.0", + "lucide-react": "^0.511.0", "nanoid": "^5.1.5", "pocketbase": "^0.26.0", "radash": "^12.1.0", "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", - "react-i18next": "^15.5.1", - "react-router-dom": "^7.6.0", + "react-i18next": "^15.5.2", + "react-router-dom": "^7.6.1", "tailwind-merge": "^2.6.0", - "zod": "^3.24.4", - "zustand": "^5.0.4" + "zod": "^3.25.28", + "zustand": "^5.0.5" }, "devDependencies": { "@types/file-saver": "^2.0.7", @@ -46,10 +46,10 @@ "@types/react": "^18.3.12", "@types/react-copy-to-clipboard": "^5.0.7", "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.32.0", - "@typescript-eslint/parser": "^8.32.0", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", "@vitejs/plugin-legacy": "^6.1.1", - "@vitejs/plugin-react": "^4.4.1", + "@vitejs/plugin-react": "^4.5.0", "autoprefixer": "^10.4.21", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.5", @@ -94,9 +94,9 @@ } }, "node_modules/@ant-design/colors": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-7.2.0.tgz", - "integrity": "sha512-bjTObSnZ9C/O8MB/B4OUtd/q9COomuJAR2SYfhxLyHvCKn4EKwCN3e+fWGMo7H5InAyV0wL17jdE9ALrdOW/6A==", + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-7.2.1.tgz", + "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==", "dependencies": { "@ant-design/fast-color": "^2.0.6" } @@ -2047,12 +2047,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", "engines": { "node": ">=6.9.0" } @@ -3225,6 +3222,12 @@ "react-dom": ">=18.0.0" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.9", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.9.tgz", + "integrity": "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==", + "dev": true + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.40.0", "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", @@ -3613,18 +3616,18 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.0.tgz", - "integrity": "sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/type-utils": "8.32.0", - "@typescript-eslint/utils": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, @@ -3641,16 +3644,25 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.32.0.tgz", - "integrity": "sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/typescript-estree": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4" }, "engines": { @@ -3666,13 +3678,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.32.0.tgz", - "integrity": "sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0" + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3683,13 +3695,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.32.0.tgz", - "integrity": "sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.32.0", - "@typescript-eslint/utils": "8.32.0", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3706,9 +3718,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.32.0.tgz", - "integrity": "sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3719,13 +3731,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.0.tgz", - "integrity": "sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3745,15 +3757,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.32.0.tgz", - "integrity": "sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/typescript-estree": "8.32.0" + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3768,12 +3780,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.32.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.0.tgz", - "integrity": "sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==", + "version": "8.32.1", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/types": "8.32.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3922,14 +3934,15 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.4.1", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", - "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz", + "integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==", "dev": true, "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@rolldown/pluginutils": "1.0.0-beta.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, @@ -3970,9 +3983,9 @@ } }, "node_modules/ahooks": { - "version": "3.8.4", - "resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.8.4.tgz", - "integrity": "sha512-39wDEw2ZHvypaT14EpMMk4AzosHWt0z9bulY0BeDsvc9PqJEV+Kjh/4TZfftSsotBMq52iYIOFPd3PR56e0ZJg==", + "version": "3.8.5", + "resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.8.5.tgz", + "integrity": "sha512-Y+MLoJpBXVdjsnnBjE5rOSPkQ4DK+8i5aPDzLJdIOsCpo/fiAeXcBY1Y7oWgtOK0TpOz0gFa/XcyO1UGdoqLcw==", "dependencies": { "@babel/runtime": "^7.21.0", "dayjs": "^1.9.1", @@ -3988,7 +4001,7 @@ "node": ">=8.0.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/ajv": { @@ -4017,11 +4030,11 @@ } }, "node_modules/antd": { - "version": "5.25.1", - "resolved": "https://registry.npmmirror.com/antd/-/antd-5.25.1.tgz", - "integrity": "sha512-4KC7KuPCjr0z3Vuw9DsF+ceqJaPLbuUI3lOX1sY8ix25ceamp+P8yxOmk3Y2JHCD2ZAhq+5IQ/DTJRN2adWYKQ==", + "version": "5.25.3", + "resolved": "https://registry.npmmirror.com/antd/-/antd-5.25.3.tgz", + "integrity": "sha512-tBBcAFRjmWM3sitxrL/FEbQL+MTQntYY5bGa5c1ZZZHXWCynkhS3Ch/gy25mGMUY1M/9Uw3pH029v/RGht1x3w==", "dependencies": { - "@ant-design/colors": "^7.2.0", + "@ant-design/colors": "^7.2.1", "@ant-design/cssinjs": "^1.23.0", "@ant-design/cssinjs-utils": "^1.1.3", "@ant-design/fast-color": "^2.0.6", @@ -4056,11 +4069,11 @@ "rc-rate": "~2.13.1", "rc-resize-observer": "^1.4.3", "rc-segmented": "~2.7.0", - "rc-select": "~14.16.7", + "rc-select": "~14.16.8", "rc-slider": "~11.1.8", "rc-steps": "~6.0.1", "rc-switch": "~4.1.0", - "rc-table": "~7.50.4", + "rc-table": "~7.50.5", "rc-tabs": "~15.6.1", "rc-textarea": "~1.10.0", "rc-tooltip": "~6.4.0", @@ -6081,9 +6094,9 @@ } }, "node_modules/i18next": { - "version": "25.1.2", - "resolved": "https://registry.npmmirror.com/i18next/-/i18next-25.1.2.tgz", - "integrity": "sha512-SP63m8LzdjkrAjruH7SCI3ndPSgjt4/wX7ouUUOzCW/eY+HzlIo19IQSfYA9X3qRiRP1SYtaTsg/Oz/PGsfD8w==", + "version": "25.2.1", + "resolved": "https://registry.npmmirror.com/i18next/-/i18next-25.2.1.tgz", + "integrity": "sha512-+UoXK5wh+VlE1Zy5p6MjcvctHXAhRwQKCxiJD8noKZzIXmnAX8gdHX5fLPA3MEVxEN4vbZkQFy8N0LyD9tUqPw==", "funding": [ { "type": "individual", @@ -6099,7 +6112,7 @@ } ], "dependencies": { - "@babel/runtime": "^7.26.10" + "@babel/runtime": "^7.27.1" }, "peerDependencies": { "typescript": "^5" @@ -6797,9 +6810,9 @@ } }, "node_modules/lucide-react": { - "version": "0.509.0", - "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.509.0.tgz", - "integrity": "sha512-xCJHn6Uh5qF6PGml25vveCTrHJZcqS1G1MVzWZK54ZQsOiCVJk4fwY3oyo5EXS2S+aqvTpWYIfJN+PesJ0quxg==", + "version": "0.511.0", + "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.511.0.tgz", + "integrity": "sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -7874,9 +7887,9 @@ } }, "node_modules/rc-select": { - "version": "14.16.7", - "resolved": "https://registry.npmmirror.com/rc-select/-/rc-select-14.16.7.tgz", - "integrity": "sha512-lT9kO5gFHQdJzu9a0btcOtNaJHkhenSl8H5mcpgXN9VIMXP59rnkpbdHmPrteixWs1D5zFOTyoTYX3b7joADIQ==", + "version": "14.16.8", + "resolved": "https://registry.npmmirror.com/rc-select/-/rc-select-14.16.8.tgz", + "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^2.1.1", @@ -7943,9 +7956,9 @@ } }, "node_modules/rc-table": { - "version": "7.50.4", - "resolved": "https://registry.npmmirror.com/rc-table/-/rc-table-7.50.4.tgz", - "integrity": "sha512-Y+YuncnQqoS5e7yHvfvlv8BmCvwDYDX/2VixTBEhkMDk9itS9aBINp4nhzXFKiBP/frG4w0pS9d9Rgisl0T1Bw==", + "version": "7.50.5", + "resolved": "https://registry.npmmirror.com/rc-table/-/rc-table-7.50.5.tgz", + "integrity": "sha512-FDZu8aolhSYd3v9KOc3lZOVAU77wmRRu44R0Wfb8Oj1dXRUsloFaXMSl6f7yuWZUxArJTli7k8TEOX2mvhDl4A==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/context": "^1.4.0", @@ -8135,9 +8148,9 @@ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" }, "node_modules/react-i18next": { - "version": "15.5.1", - "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.5.1.tgz", - "integrity": "sha512-C8RZ7N7H0L+flitiX6ASjq9p5puVJU1Z8VyL3OgM/QOMRf40BMZX+5TkpxzZVcTmOLPX5zlti4InEX5pFyiVeA==", + "version": "15.5.2", + "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-15.5.2.tgz", + "integrity": "sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A==", "dependencies": { "@babel/runtime": "^7.25.0", "html-parse-stringify": "^3.0.1" @@ -8179,9 +8192,9 @@ } }, "node_modules/react-router": { - "version": "7.6.0", - "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.6.0.tgz", - "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", + "version": "7.6.1", + "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.6.1.tgz", + "integrity": "sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==", "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" @@ -8200,11 +8213,11 @@ } }, "node_modules/react-router-dom": { - "version": "7.6.0", - "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.6.0.tgz", - "integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==", + "version": "7.6.1", + "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.6.1.tgz", + "integrity": "sha512-vxU7ei//UfPYQ3iZvHuO1D/5fX3/JOqhNTbRR+WjSBWxf9bIvpWK+ftjmdfJHzPOuMQKe2fiEdG+dZX6E8uUpA==", "dependencies": { - "react-router": "7.6.0" + "react-router": "7.6.1" }, "engines": { "node": ">=20.0.0" @@ -8286,7 +8299,8 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -9926,17 +9940,17 @@ } }, "node_modules/zod": { - "version": "3.24.4", - "resolved": "https://registry.npmmirror.com/zod/-/zod-3.24.4.tgz", - "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", + "version": "3.25.28", + "resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.28.tgz", + "integrity": "sha512-/nt/67WYKnr5by3YS7LroZJbtcCBurDKKPBPWWzaxvVCGuG/NOsiKkrjoOhI8mJ+SQUXEbUzeB3S+6XDUEEj7Q==", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zustand": { - "version": "5.0.4", - "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.4.tgz", - "integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==", + "version": "5.0.5", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.5.tgz", + "integrity": "sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==", "engines": { "node": ">=12.20.0" }, diff --git a/ui/package.json b/ui/package.json index bec7eda9..b8383a99 100644 --- a/ui/package.json +++ b/ui/package.json @@ -19,27 +19,27 @@ "@uiw/codemirror-extensions-basic-setup": "^4.23.12", "@uiw/codemirror-theme-vscode": "^4.23.12", "@uiw/react-codemirror": "^4.23.12", - "ahooks": "^3.8.4", - "antd": "^5.25.1", + "ahooks": "^3.8.5", + "antd": "^5.25.3", "antd-zod": "^6.1.0", "clsx": "^2.1.1", "cron-parser": "^5.2.0", "file-saver": "^2.0.5", - "i18next": "^25.1.2", + "i18next": "^25.2.1", "i18next-browser-languagedetector": "^8.1.0", "immer": "^10.1.1", - "lucide-react": "^0.509.0", + "lucide-react": "^0.511.0", "nanoid": "^5.1.5", "pocketbase": "^0.26.0", "radash": "^12.1.0", "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", - "react-i18next": "^15.5.1", - "react-router-dom": "^7.6.0", + "react-i18next": "^15.5.2", + "react-router-dom": "^7.6.1", "tailwind-merge": "^2.6.0", - "zod": "^3.24.4", - "zustand": "^5.0.4" + "zod": "^3.25.28", + "zustand": "^5.0.5" }, "devDependencies": { "@types/file-saver": "^2.0.7", @@ -48,10 +48,10 @@ "@types/react": "^18.3.12", "@types/react-copy-to-clipboard": "^5.0.7", "@types/react-dom": "^18.3.1", - "@typescript-eslint/eslint-plugin": "^8.32.0", - "@typescript-eslint/parser": "^8.32.0", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", "@vitejs/plugin-legacy": "^6.1.1", - "@vitejs/plugin-react": "^4.4.1", + "@vitejs/plugin-react": "^4.5.0", "autoprefixer": "^10.4.21", "eslint": "^8.57.0", "eslint-config-prettier": "^10.1.5", From bf1d03a30efda4f84e7915fe02b17129bbf54ddd Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 21:49:37 +0800 Subject: [PATCH 16/24] feat(migration): tracer --- migrations/1739462400_collections_snapshot.go | 15 +- migrations/1740050400_upgrade.go | 34 ++-- migrations/1742209200_upgrade.go | 14 ++ migrations/1742392800_upgrade.go | 144 ++++++++------- migrations/1742644800_upgrade.go | 12 ++ migrations/1743264000_upgrade.go | 11 +- migrations/1744192800_upgrade.go | 164 +++++++++-------- migrations/1744459000_upgrade.go | 166 ++++++++++-------- migrations/1745308800_upgrade.go | 161 +++++++++-------- migrations/1745726400_upgrade.go | 42 +++-- migrations/1747314000_upgrade.go | 24 ++- migrations/1747389600_upgrade.go | 26 ++- migrations/1748178000_upgrade.go | 32 ++-- migrations/1748228400_upgrade.go | 6 + migrations/tracer.go | 22 +++ 15 files changed, 518 insertions(+), 355 deletions(-) create mode 100644 migrations/tracer.go diff --git a/migrations/1739462400_collections_snapshot.go b/migrations/1739462400_collections_snapshot.go index 25564526..2f78e7b9 100644 --- a/migrations/1739462400_collections_snapshot.go +++ b/migrations/1739462400_collections_snapshot.go @@ -2,7 +2,6 @@ package migrations import ( x509 "crypto/x509" - "log/slog" "strings" "github.com/pocketbase/pocketbase/core" @@ -12,7 +11,8 @@ import ( func init() { m.Register(func(app core.App) error { - slog.Info("[CERTIMATE] migration: ready ...") + tracer := NewTracer("to v0.3") + tracer.Printf("go ...") // backup collection records collectionRecords := make([]*core.Record, 0) @@ -30,7 +30,7 @@ func init() { } collectionRecords = append(collectionRecords, records...) - slog.Info("[CERTIMATE] migration: collection '" + collection.Name + "' backed up") + tracer.Printf("collection '%s' backed up", collection.Name) if collection.Name == "access" { collection.Fields.RemoveByName("usage") @@ -107,7 +107,7 @@ func init() { { app.Delete(collection) - slog.Info("[CERTIMATE] migration: collection '" + collection.Name + "' truncated") + tracer.Printf("collection '%s' truncated", collection.Name) } } } @@ -1729,7 +1729,7 @@ func init() { return err } - slog.Info("[CERTIMATE] migration: collections imported") + tracer.Printf("collections imported") // restore records for _, record := range collectionRecords { @@ -1795,12 +1795,11 @@ func init() { return err } - slog.Info("[CERTIMATE] migration: collection '" + record.Collection().Name + "' record #" + record.Id + " updated") + tracer.Printf("record #%s in collection '%s' updated", record.Id, record.Collection().Name) } } - slog.Info("[CERTIMATE] migration: done") - + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1740050400_upgrade.go b/migrations/1740050400_upgrade.go index 93bc3f33..261169f8 100644 --- a/migrations/1740050400_upgrade.go +++ b/migrations/1740050400_upgrade.go @@ -7,14 +7,17 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1740050400") + tracer.Printf("go ...") + // update collection `certificate` { - certimateCollection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") + collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") if err != nil { return err } - if err := certimateCollection.Fields.AddMarshaledJSONAt(4, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{ "autogeneratePattern": "", "hidden": false, "id": "plmambpz", @@ -31,7 +34,7 @@ func init() { return err } - if err := certimateCollection.Fields.AddMarshaledJSONAt(5, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(5, []byte(`{ "autogeneratePattern": "", "hidden": false, "id": "49qvwxcg", @@ -48,7 +51,7 @@ func init() { return err } - if err := certimateCollection.Fields.AddMarshaledJSONAt(7, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(7, []byte(`{ "autogeneratePattern": "", "hidden": false, "id": "agt7n5bb", @@ -65,19 +68,21 @@ func init() { return err } - if err := app.Save(certimateCollection); err != nil { + if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `workflow` { - workflowCollection, err := app.FindCollectionByNameOrId("tovyif5ax6j62ur") + collection, err := app.FindCollectionByNameOrId("tovyif5ax6j62ur") if err != nil { return err } - if err := workflowCollection.Fields.AddMarshaledJSONAt(6, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(6, []byte(`{ "hidden": false, "id": "awlphkfe", "maxSize": 5000000, @@ -90,7 +95,7 @@ func init() { return err } - if err := workflowCollection.Fields.AddMarshaledJSONAt(7, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(7, []byte(`{ "hidden": false, "id": "g9ohkk5o", "maxSize": 5000000, @@ -103,19 +108,21 @@ func init() { return err } - if err := app.Save(workflowCollection); err != nil { + if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `workflow_output` { - workflowOutputCollection, err := app.FindCollectionByNameOrId("bqnxb95f2cooowp") + collection, err := app.FindCollectionByNameOrId("bqnxb95f2cooowp") if err != nil { return err } - if err := workflowOutputCollection.Fields.AddMarshaledJSONAt(4, []byte(`{ + if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{ "hidden": false, "id": "c2rm9omj", "maxSize": 5000000, @@ -128,11 +135,14 @@ func init() { return err } - if err := app.Save(workflowOutputCollection); err != nil { + if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1742209200_upgrade.go b/migrations/1742209200_upgrade.go index 5cda0c35..1bc0d482 100644 --- a/migrations/1742209200_upgrade.go +++ b/migrations/1742209200_upgrade.go @@ -11,6 +11,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1742209200") + tracer.Printf("go ...") + // create collection `workflow_logs` { jsonData := `{ @@ -167,6 +170,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' created", collection.Name) } // migrate data @@ -215,6 +220,8 @@ func init() { if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } @@ -243,6 +250,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // migrate data @@ -321,6 +330,8 @@ func init() { if err := app.Save(workflowRun); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", workflowRun.Id, workflowRun.Collection().Name) } } @@ -336,8 +347,11 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1742392800_upgrade.go b/migrations/1742392800_upgrade.go index a06bdc75..4416936a 100644 --- a/migrations/1742392800_upgrade.go +++ b/migrations/1742392800_upgrade.go @@ -7,74 +7,86 @@ import ( func init() { m.Register(func(app core.App) error { - collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") - if err != nil { - return err + tracer := NewTracer("(v0.3)1742392800") + tracer.Printf("go ...") + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "byteplus", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "huaweicloud", + "jdcloud", + "k8s", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "tencentcloud", + "ucloud", + "upyun", + "volcengine", + "webhook", + "westcn" + ] + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + + tracer.Printf("collection '%s' updated", collection.Name) } - // update field - if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ - "hidden": false, - "id": "hwy7m03o", - "maxSelect": 1, - "name": "provider", - "presentable": false, - "required": false, - "system": false, - "type": "select", - "values": [ - "1panel", - "acmehttpreq", - "akamai", - "aliyun", - "aws", - "azure", - "baiducloud", - "baishan", - "baotapanel", - "byteplus", - "cachefly", - "cdnfly", - "cloudflare", - "cloudns", - "cmcccloud", - "ctcccloud", - "cucccloud", - "dnsla", - "dogecloud", - "dynv6", - "edgio", - "fastly", - "gname", - "gcore", - "godaddy", - "goedge", - "huaweicloud", - "jdcloud", - "k8s", - "local", - "namecheap", - "namedotcom", - "namesilo", - "ns1", - "powerdns", - "qiniu", - "qingcloud", - "rainyun", - "safeline", - "ssh", - "tencentcloud", - "ucloud", - "upyun", - "volcengine", - "webhook", - "westcn" - ] - }`)); err != nil { - return err - } - - return app.Save(collection) + tracer.Printf("done") + return nil }, func(app core.App) error { return nil }) diff --git a/migrations/1742644800_upgrade.go b/migrations/1742644800_upgrade.go index b28634a1..228a70fc 100644 --- a/migrations/1742644800_upgrade.go +++ b/migrations/1742644800_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1742644800") + tracer.Printf("go ...") + // update collection `workflow_run` { collection, err := app.FindCollectionByNameOrId("qjp8lygssgwyqyz") @@ -35,6 +38,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `workflow_output` @@ -61,6 +66,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `workflow_logs` @@ -105,6 +112,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `access` @@ -182,8 +191,11 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1743264000_upgrade.go b/migrations/1743264000_upgrade.go index 75f98b30..22738be2 100644 --- a/migrations/1743264000_upgrade.go +++ b/migrations/1743264000_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1743264000") + tracer.Printf("go ...") + // update collection `settings` { collection, err := app.FindCollectionByNameOrId("dy6ccjb60spfy6p") @@ -52,6 +55,8 @@ func init() { if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } @@ -62,7 +67,6 @@ func init() { return err } - // update field if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ "hidden": false, "id": "hwy7m03o", @@ -136,6 +140,8 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // update collection `acme_accounts` @@ -163,9 +169,12 @@ func init() { if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1744192800_upgrade.go b/migrations/1744192800_upgrade.go index 83e83ee6..eb4c6d06 100644 --- a/migrations/1744192800_upgrade.go +++ b/migrations/1744192800_upgrade.go @@ -7,84 +7,96 @@ import ( func init() { m.Register(func(app core.App) error { - collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") - if err != nil { - return err + tracer := NewTracer("(v0.3)1744192800") + tracer.Printf("go ...") + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "byteplus", + "buypass", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "desec", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "googletrustservices", + "huaweicloud", + "jdcloud", + "k8s", + "letsencrypt", + "letsencryptstaging", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "porkbun", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "sslcom", + "tencentcloud", + "ucloud", + "upyun", + "vercel", + "volcengine", + "wangsu", + "webhook", + "westcn", + "zerossl" + ] + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return nil + } + + tracer.Printf("collection '%s' updated", collection.Name) } - // update field - if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ - "hidden": false, - "id": "hwy7m03o", - "maxSelect": 1, - "name": "provider", - "presentable": false, - "required": false, - "system": false, - "type": "select", - "values": [ - "1panel", - "acmehttpreq", - "akamai", - "aliyun", - "aws", - "azure", - "baiducloud", - "baishan", - "baotapanel", - "byteplus", - "buypass", - "cachefly", - "cdnfly", - "cloudflare", - "cloudns", - "cmcccloud", - "ctcccloud", - "cucccloud", - "desec", - "dnsla", - "dogecloud", - "dynv6", - "edgio", - "fastly", - "gname", - "gcore", - "godaddy", - "goedge", - "googletrustservices", - "huaweicloud", - "jdcloud", - "k8s", - "letsencrypt", - "letsencryptstaging", - "local", - "namecheap", - "namedotcom", - "namesilo", - "ns1", - "porkbun", - "powerdns", - "qiniu", - "qingcloud", - "rainyun", - "safeline", - "ssh", - "sslcom", - "tencentcloud", - "ucloud", - "upyun", - "vercel", - "volcengine", - "wangsu", - "webhook", - "westcn", - "zerossl" - ] - }`)); err != nil { - return err - } - - return app.Save(collection) + tracer.Printf("done") + return nil }, func(app core.App) error { return nil }) diff --git a/migrations/1744459000_upgrade.go b/migrations/1744459000_upgrade.go index 4b2bbba9..82ecbcf6 100644 --- a/migrations/1744459000_upgrade.go +++ b/migrations/1744459000_upgrade.go @@ -7,85 +7,97 @@ import ( func init() { m.Register(func(app core.App) error { - collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") - if err != nil { - return err + tracer := NewTracer("(v0.3)1744459000") + tracer.Printf("go ...") + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "hwy7m03o", + "maxSelect": 1, + "name": "provider", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "1panel", + "acmehttpreq", + "akamai", + "aliyun", + "aws", + "azure", + "baiducloud", + "baishan", + "baotapanel", + "bunny", + "byteplus", + "buypass", + "cachefly", + "cdnfly", + "cloudflare", + "cloudns", + "cmcccloud", + "ctcccloud", + "cucccloud", + "desec", + "dnsla", + "dogecloud", + "dynv6", + "edgio", + "fastly", + "gname", + "gcore", + "godaddy", + "goedge", + "googletrustservices", + "huaweicloud", + "jdcloud", + "k8s", + "letsencrypt", + "letsencryptstaging", + "local", + "namecheap", + "namedotcom", + "namesilo", + "ns1", + "porkbun", + "powerdns", + "qiniu", + "qingcloud", + "rainyun", + "safeline", + "ssh", + "sslcom", + "tencentcloud", + "ucloud", + "upyun", + "vercel", + "volcengine", + "wangsu", + "webhook", + "westcn", + "zerossl" + ] + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + + tracer.Printf("collection '%s' updated", collection.Name) } - // update field - if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ - "hidden": false, - "id": "hwy7m03o", - "maxSelect": 1, - "name": "provider", - "presentable": false, - "required": false, - "system": false, - "type": "select", - "values": [ - "1panel", - "acmehttpreq", - "akamai", - "aliyun", - "aws", - "azure", - "baiducloud", - "baishan", - "baotapanel", - "bunny", - "byteplus", - "buypass", - "cachefly", - "cdnfly", - "cloudflare", - "cloudns", - "cmcccloud", - "ctcccloud", - "cucccloud", - "desec", - "dnsla", - "dogecloud", - "dynv6", - "edgio", - "fastly", - "gname", - "gcore", - "godaddy", - "goedge", - "googletrustservices", - "huaweicloud", - "jdcloud", - "k8s", - "letsencrypt", - "letsencryptstaging", - "local", - "namecheap", - "namedotcom", - "namesilo", - "ns1", - "porkbun", - "powerdns", - "qiniu", - "qingcloud", - "rainyun", - "safeline", - "ssh", - "sslcom", - "tencentcloud", - "ucloud", - "upyun", - "vercel", - "volcengine", - "wangsu", - "webhook", - "westcn", - "zerossl" - ] - }`)); err != nil { - return err - } - - return app.Save(collection) + tracer.Printf("done") + return nil }, func(app core.App) error { return nil }) diff --git a/migrations/1745308800_upgrade.go b/migrations/1745308800_upgrade.go index 989664fb..68d025fd 100644 --- a/migrations/1745308800_upgrade.go +++ b/migrations/1745308800_upgrade.go @@ -9,84 +9,93 @@ import ( func init() { m.Register(func(app core.App) error { - collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") - if err != nil { - return err - } - - // add temp field `providerTmp` - if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{ - "autogeneratePattern": "", - "hidden": false, - "id": "text2024822322", - "max": 0, - "min": 0, - "name": "providerTmp", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }`)); err != nil { - return err - } - if err := app.Save(collection); err != nil { - return err - } - - // copy `provider` to `providerTmp` - if _, err := app.DB().NewQuery("UPDATE access SET providerTmp = provider").Execute(); err != nil { - return err - } - - // remove old field `provider` - collection.Fields.RemoveById("hwy7m03o") - if err := json.Unmarshal([]byte(`{ - "indexes": [ - "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)" - ] - }`), &collection); err != nil { - return err - } - if err := app.Save(collection); err != nil { - return err - } - - // rename field `providerTmp` to `provider` - if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ - "autogeneratePattern": "", - "hidden": false, - "id": "text2024822322", - "max": 0, - "min": 0, - "name": "provider", - "pattern": "", - "presentable": false, - "primaryKey": false, - "required": false, - "system": false, - "type": "text" - }`)); err != nil { - return err - } - if err := app.Save(collection); err != nil { - return err - } - - // rebuild indexes - if err := json.Unmarshal([]byte(`{ - "indexes": [ - "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)", - "CREATE INDEX `+"`"+`idx_frh0JT1Aqx`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`provider`+"`"+`)" - ] - }`), &collection); err != nil { - return err - } - if err := app.Save(collection); err != nil { - return err + tracer := NewTracer("(v0.3)1745308800") + tracer.Printf("go ...") + + // update collection `access` + { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // add temp field `providerTmp` + if err := collection.Fields.AddMarshaledJSONAt(3, []byte(`{ + "autogeneratePattern": "", + "hidden": false, + "id": "text2024822322", + "max": 0, + "min": 0, + "name": "providerTmp", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // copy `provider` to `providerTmp` + if _, err := app.DB().NewQuery("UPDATE access SET providerTmp = provider").Execute(); err != nil { + return err + } + + // remove old field `provider` + collection.Fields.RemoveById("hwy7m03o") + if err := json.Unmarshal([]byte(`{ + "indexes": [ + "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)" + ] + }`), &collection); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // rename field `providerTmp` to `provider` + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "autogeneratePattern": "", + "hidden": false, + "id": "text2024822322", + "max": 0, + "min": 0, + "name": "provider", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + // rebuild indexes + if err := json.Unmarshal([]byte(`{ + "indexes": [ + "CREATE INDEX `+"`"+`idx_wkoST0j`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`name`+"`"+`)", + "CREATE INDEX `+"`"+`idx_frh0JT1Aqx`+"`"+` ON `+"`"+`access`+"`"+` (`+"`"+`provider`+"`"+`)" + ] + }`), &collection); err != nil { + return err + } + if err := app.Save(collection); err != nil { + return err + } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1745726400_upgrade.go b/migrations/1745726400_upgrade.go index e4449f36..0513dac4 100644 --- a/migrations/1745726400_upgrade.go +++ b/migrations/1745726400_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1745726400") + tracer.Printf("go ...") + // update collection `access` { collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") @@ -34,53 +37,62 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // migrate data { - accesses, err := app.FindAllRecords("access") + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") if err != nil { return err } - for _, access := range accesses { + records, err := app.FindAllRecords(collection) + if err != nil { + return err + } + + for _, record := range records { changed := false - if access.GetString("provider") == "buypass" { - access.Set("reserve", "ca") + if record.GetString("provider") == "buypass" { + record.Set("reserve", "ca") changed = true - } else if access.GetString("provider") == "googletrustservices" { - access.Set("reserve", "ca") + } else if record.GetString("provider") == "googletrustservices" { + record.Set("reserve", "ca") changed = true - } else if access.GetString("provider") == "sslcom" { - access.Set("reserve", "ca") + } else if record.GetString("provider") == "sslcom" { + record.Set("reserve", "ca") changed = true - } else if access.GetString("provider") == "zerossl" { - access.Set("reserve", "ca") + } else if record.GetString("provider") == "zerossl" { + record.Set("reserve", "ca") changed = true } - if access.GetString("provider") == "webhook" { + if record.GetString("provider") == "webhook" { config := make(map[string]any) - if err := access.UnmarshalJSONField("config", &config); err != nil { + if err := record.UnmarshalJSONField("config", &config); err != nil { return err } config["method"] = "POST" config["headers"] = "Content-Type: application/json" - access.Set("config", config) + record.Set("config", config) changed = true } if changed { - err = app.Save(access) - if err != nil { + if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1747314000_upgrade.go b/migrations/1747314000_upgrade.go index 19a25bb2..2c3ecdc4 100644 --- a/migrations/1747314000_upgrade.go +++ b/migrations/1747314000_upgrade.go @@ -7,36 +7,46 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1747314000") + tracer.Printf("go ...") + // migrate data { - accesses, err := app.FindAllRecords("access") + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") if err != nil { return err } - for _, access := range accesses { + records, err := app.FindAllRecords(collection) + if err != nil { + return err + } + + for _, record := range records { changed := false - if access.GetString("provider") == "goedge" { + if record.GetString("provider") == "goedge" { config := make(map[string]any) - if err := access.UnmarshalJSONField("config", &config); err != nil { + if err := record.UnmarshalJSONField("config", &config); err != nil { return err } config["apiRole"] = "user" - access.Set("config", config) + record.Set("config", config) changed = true } if changed { - err = app.Save(access) - if err != nil { + if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1747389600_upgrade.go b/migrations/1747389600_upgrade.go index a145679a..fabc562c 100644 --- a/migrations/1747389600_upgrade.go +++ b/migrations/1747389600_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1747389600") + tracer.Printf("go ...") + // update collection `certificate` { collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") @@ -34,38 +37,47 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } // migrate data { - accesses, err := app.FindAllRecords("access") + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") if err != nil { return err } - for _, access := range accesses { + records, err := app.FindAllRecords(collection) + if err != nil { + return err + } + + for _, record := range records { changed := false - if access.GetString("provider") == "1panel" { + if record.GetString("provider") == "1panel" { config := make(map[string]any) - if err := access.UnmarshalJSONField("config", &config); err != nil { + if err := record.UnmarshalJSONField("config", &config); err != nil { return err } config["apiVersion"] = "v1" - access.Set("config", config) + record.Set("config", config) changed = true } if changed { - err = app.Save(access) - if err != nil { + if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1748178000_upgrade.go b/migrations/1748178000_upgrade.go index 719ed64f..44783448 100644 --- a/migrations/1748178000_upgrade.go +++ b/migrations/1748178000_upgrade.go @@ -9,9 +9,17 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1748178000") + tracer.Printf("go ...") + // migrate data { - accesses, err := app.FindAllRecords("access") + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + records, err := app.FindAllRecords(collection) if err != nil { return err } @@ -29,30 +37,34 @@ func init() { "ratpanel", "safeline", } - for _, access := range accesses { + for _, record := range records { changed := false - if slices.Contains(providersToUpdate, access.GetString("provider")) { + if slices.Contains(providersToUpdate, record.GetString("provider")) { config := make(map[string]any) - if err := access.UnmarshalJSONField("config", &config); err != nil { + if err := record.UnmarshalJSONField("config", &config); err != nil { return err } - config["serverUrl"] = config["apiUrl"] - delete(config, "apiUrl") - access.Set("config", config) - changed = true + if config["apiUrl"] != nil { + config["serverUrl"] = config["apiUrl"] + delete(config, "apiUrl") + record.Set("config", config) + changed = true + } } if changed { - err = app.Save(access) - if err != nil { + if err := app.Save(record); err != nil { return err } + + tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name) } } } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/1748228400_upgrade.go b/migrations/1748228400_upgrade.go index b6f954d2..afdfd850 100644 --- a/migrations/1748228400_upgrade.go +++ b/migrations/1748228400_upgrade.go @@ -7,6 +7,9 @@ import ( func init() { m.Register(func(app core.App) error { + tracer := NewTracer("(v0.3)1748228400") + tracer.Printf("go ...") + // update collection `certificate` { collection, err := app.FindCollectionByNameOrId("4szxr9x43tpj6np") @@ -30,8 +33,11 @@ func init() { if err := app.Save(collection); err != nil { return err } + + tracer.Printf("collection '%s' updated", collection.Name) } + tracer.Printf("done") return nil }, func(app core.App) error { return nil diff --git a/migrations/tracer.go b/migrations/tracer.go new file mode 100644 index 00000000..04f58e54 --- /dev/null +++ b/migrations/tracer.go @@ -0,0 +1,22 @@ +package migrations + +import ( + "fmt" + "log/slog" +) + +type Tracer struct { + logger *slog.Logger + flag string +} + +func NewTracer(flag string) *Tracer { + return &Tracer{ + logger: slog.Default(), + flag: flag, + } +} + +func (l *Tracer) Printf(format string, args ...any) { + l.logger.Info("[CERTIMATE] migration " + l.flag + ": " + fmt.Sprintf(format, args...)) +} From 533996352452ad0559a43da23468c73c54c3b7af Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 26 May 2025 23:02:57 +0800 Subject: [PATCH 17/24] feat(ui): improve i18n --- .../workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx | 6 +++++- .../workflow/node/DeployNodeConfigFormUpyunFileConfig.tsx | 6 +++++- ui/src/i18n/locales/en/nls.workflow.nodes.json | 2 ++ ui/src/i18n/locales/zh/nls.workflow.nodes.json | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx index e09f5266..c18d570c 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigFormUpyunCDNConfig.tsx @@ -1,5 +1,5 @@ import { useTranslation } from "react-i18next"; -import { Form, type FormInstance, Input } from "antd"; +import { Alert, Form, type FormInstance, Input } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { z } from "zod"; @@ -44,6 +44,10 @@ const DeployNodeConfigFormUpyunCDNConfig = ({ form: formInst, formName, disabled name={formName} onValuesChange={handleFormChange} > + + } /> + + + + } /> + + https://console.ucloud-global.com/ufile", + "workflow_node.deploy.form.upyun_cdn.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the UPYUN, please create a GitHub Issue.", "workflow_node.deploy.form.upyun_cdn_domain.label": "UPYUN CDN domain", "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "Please enter UPYUN CDN domain name", "workflow_node.deploy.form.upyun_cdn_domain.tooltip": "For more information, see https://console.upyun.com/services/cdn/", + "workflow_node.deploy.form.upyun_file.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the UPYUN, please create a GitHub Issue.", "workflow_node.deploy.form.upyun_file_domain.label": "UPYUN bucket domain", "workflow_node.deploy.form.upyun_file_domain.placeholder": "Please enter UPYUN bucket domain name", "workflow_node.deploy.form.upyun_file_domain.tooltip": "For more information, see https://console.upyun.com/services/file/", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 87f0076b..6f13984e 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -695,9 +695,11 @@ "workflow_node.deploy.form.ucloud_us3_domain.label": "优刻得 US3 自定义域名", "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "请输入优刻得 US3 自定义域名", "workflow_node.deploy.form.ucloud_us3_domain.tooltip": "这是什么?请参阅 https://console.ucloud.cn/ufile", + "workflow_node.deploy.form.upyun_cdn.guide": "小贴士:由于又拍云未提供相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇又拍云接口变更,请到 GitHub 发起 Issue 告知。", "workflow_node.deploy.form.upyun_cdn_domain.label": "又拍云 CDN 加速域名", "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "请输入又拍云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.upyun_cdn_domain.tooltip": "这是什么?请参阅 https://console.upyun.com/services/cdn/", + "workflow_node.deploy.form.upyun_file.guide": "小贴士:由于又拍云未提供相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇又拍云接口变更,请到 GitHub 发起 Issue 告知。", "workflow_node.deploy.form.upyun_file_domain.label": "又拍云云存储加速域名", "workflow_node.deploy.form.upyun_file_domain.placeholder": "请输入又拍云云存储加速域名", "workflow_node.deploy.form.upyun_file_domain.tooltip": "这是什么?请参阅 https://console.upyun.com/services/file/", From cfdd3c621fe87e35d1358e1b870f95d117c77e35 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 27 May 2025 04:29:51 +0800 Subject: [PATCH 18/24] feat: new deployment provider: unicloud webhost --- internal/deployer/providers.go | 18 ++ internal/domain/access.go | 5 + internal/domain/provider.go | 2 + .../unicloud-webhost/unicloud_webhost.go | 101 +++++++ .../unicloud-webhost/unicloud_webhost_test.go | 85 ++++++ internal/pkg/sdk3rd/dcloud/unicloud/api.go | 78 ++++++ internal/pkg/sdk3rd/dcloud/unicloud/client.go | 257 ++++++++++++++++++ internal/pkg/sdk3rd/dcloud/unicloud/models.go | 103 +++++++ ui/public/imgs/providers/unicloud.png | Bin 0 -> 17955 bytes ui/src/components/access/AccessForm.tsx | 7 +- .../access/AccessFormUniCloudConfig.tsx | 68 +++++ .../access/AccessFormUpyunConfig.tsx | 4 +- .../workflow/node/DeployNodeConfigForm.tsx | 3 + ...loyNodeConfigFormUniCloudWebHostConfig.tsx | 85 ++++++ ui/src/domain/access.ts | 6 + ui/src/domain/provider.ts | 26 +- ui/src/i18n/locales/en/nls.access.json | 16 +- ui/src/i18n/locales/en/nls.provider.json | 2 + .../i18n/locales/en/nls.workflow.nodes.json | 10 + ui/src/i18n/locales/zh/nls.access.json | 4 + ui/src/i18n/locales/zh/nls.provider.json | 2 + .../i18n/locales/zh/nls.workflow.nodes.json | 10 + 22 files changed, 871 insertions(+), 21 deletions(-) create mode 100644 internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost.go create mode 100644 internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost_test.go create mode 100644 internal/pkg/sdk3rd/dcloud/unicloud/api.go create mode 100644 internal/pkg/sdk3rd/dcloud/unicloud/client.go create mode 100644 internal/pkg/sdk3rd/dcloud/unicloud/models.go create mode 100644 ui/public/imgs/providers/unicloud.png create mode 100644 ui/src/components/access/AccessFormUniCloudConfig.tsx create mode 100644 ui/src/components/workflow/node/DeployNodeConfigFormUniCloudWebHostConfig.tsx diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index e8dbaf1a..7f9bae91 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -79,6 +79,7 @@ import ( pTencentCloudWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/tencentcloud-waf" pUCloudUCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-ucdn" pUCloudUS3 "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ucloud-us3" + pUniCloudWebHost "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/unicloud-webhost" pUpyunCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/upyun-cdn" pVolcEngineALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-alb" pVolcEngineCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/volcengine-cdn" @@ -1144,6 +1145,23 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer } } + case domain.DeploymentProviderTypeUniCloudWebHost: + { + access := domain.AccessConfigForUniCloud{} + if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + deployer, err := pUniCloudWebHost.NewDeployer(&pUniCloudWebHost.DeployerConfig{ + Username: access.Username, + Password: access.Password, + SpaceProvider: maputil.GetString(options.ProviderServiceConfig, "spaceProvider"), + SpaceId: maputil.GetString(options.ProviderServiceConfig, "spaceId"), + Domain: maputil.GetString(options.ProviderServiceConfig, "domain"), + }) + return deployer, err + } + case domain.DeploymentProviderTypeUpyunCDN, domain.DeploymentProviderTypeUpyunFile: { access := domain.AccessConfigForUpyun{} diff --git a/internal/domain/access.go b/internal/domain/access.go index 482f753a..e31bb1a0 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -343,6 +343,11 @@ type AccessConfigForUCloud struct { ProjectId string `json:"projectId,omitempty"` } +type AccessConfigForUniCloud struct { + Username string `json:"username"` + Password string `json:"password"` +} + type AccessConfigForUpyun struct { Username string `json:"username"` Password string `json:"password"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index c8cb37d5..55f8b2af 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -77,6 +77,7 @@ const ( AccessProviderTypeTelegramBot = AccessProviderType("telegrambot") AccessProviderTypeTencentCloud = AccessProviderType("tencentcloud") AccessProviderTypeUCloud = AccessProviderType("ucloud") + AccessProviderTypeUniCloud = AccessProviderType("unicloud") AccessProviderTypeUpyun = AccessProviderType("upyun") AccessProviderTypeVercel = AccessProviderType("vercel") AccessProviderTypeVolcEngine = AccessProviderType("volcengine") @@ -244,6 +245,7 @@ const ( DeploymentProviderTypeTencentCloudWAF = DeploymentProviderType(AccessProviderTypeTencentCloud + "-waf") DeploymentProviderTypeUCloudUCDN = DeploymentProviderType(AccessProviderTypeUCloud + "-ucdn") DeploymentProviderTypeUCloudUS3 = DeploymentProviderType(AccessProviderTypeUCloud + "-us3") + DeploymentProviderTypeUniCloudWebHost = DeploymentProviderType(AccessProviderTypeUniCloud + "-webhost") DeploymentProviderTypeUpyunCDN = DeploymentProviderType(AccessProviderTypeUpyun + "-cdn") DeploymentProviderTypeUpyunFile = DeploymentProviderType(AccessProviderTypeUpyun + "-file") DeploymentProviderTypeVolcEngineALB = DeploymentProviderType(AccessProviderTypeVolcEngine + "-alb") diff --git a/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost.go b/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost.go new file mode 100644 index 00000000..e24708bd --- /dev/null +++ b/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost.go @@ -0,0 +1,101 @@ +package unicloudwebhost + +import ( + "context" + "errors" + "fmt" + "log/slog" + "net/url" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + unisdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/dcloud/unicloud" +) + +type DeployerConfig struct { + // uniCloud 控制台账号。 + Username string `json:"username"` + // uniCloud 控制台密码。 + Password string `json:"password"` + // 服务空间提供商。 + // 可取值 "aliyun"、"tencent"。 + SpaceProvider string `json:"spaceProvider"` + // 服务空间 ID。 + SpaceId string `json:"spaceId"` + // 托管网站域名(不支持泛域名)。 + Domain string `json:"domain"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *unisdk.Client +} + +var _ deployer.Deployer = (*DeployerProvider)(nil) + +func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) { + if config == nil { + panic("config is nil") + } + + client, err := createSdkClient(config.Username, config.Password) + if err != nil { + return nil, fmt.Errorf("failed to create sdk client: %w", err) + } + + return &DeployerProvider{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer { + if logger == nil { + d.logger = slog.Default() + } else { + d.logger = logger + } + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) { + if d.config.SpaceProvider == "" { + return nil, errors.New("config `spaceProvider` is required") + } + if d.config.SpaceId == "" { + return nil, errors.New("config `spaceId` is required") + } + if d.config.Domain == "" { + return nil, errors.New("config `domain` is required") + } + + // 变更网站证书 + createDomainWithCertReq := &unisdk.CreateDomainWithCertRequest{ + Provider: d.config.SpaceProvider, + SpaceId: d.config.SpaceId, + Domain: d.config.Domain, + Cert: url.QueryEscape(certPEM), + Key: url.QueryEscape(privkeyPEM), + } + createDomainWithCertResp, err := d.sdkClient.CreateDomainWithCert(createDomainWithCertReq) + d.logger.Debug("sdk request 'unicloud.host.CreateDomainWithCert'", slog.Any("request", createDomainWithCertReq), slog.Any("response", createDomainWithCertResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'unicloud.host.CreateDomainWithCert': %w", err) + } + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(username, password string) (*unisdk.Client, error) { + if username == "" { + return nil, errors.New("invalid unicloud username") + } + + if password == "" { + return nil, errors.New("invalid unicloud password") + } + + client := unisdk.NewClient(username, password) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost_test.go b/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost_test.go new file mode 100644 index 00000000..1e47ba24 --- /dev/null +++ b/internal/pkg/core/deployer/providers/unicloud-webhost/unicloud_webhost_test.go @@ -0,0 +1,85 @@ +package unicloudwebhost_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/unicloud-webhost" +) + +var ( + fInputCertPath string + fInputKeyPath string + fUsername string + fPassword string + fSpaceProvider string + fSpaceId string + fDomain string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "") + flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "") + flag.StringVar(&fSpaceProvider, argsPrefix+"SPACEPROVIDER", "", "") + flag.StringVar(&fSpaceId, argsPrefix+"SPACEID", "", "") + flag.StringVar(&fDomain, argsPrefix+"DOMAIN", "", "") +} + +/* +Shell command to run this test: + + go test -v ./unicloud_webhost_test.go -args \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_USERNAME="your-username" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_PASSWORD="your-password" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_SPACEPROVIDER="aliyun/tencent" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_SPACEID="your-space-id" \ + --CERTIMATE_DEPLOYER_UNICLOUDWEBHOST_DOMAIN="example.com" +*/ +func TestDeploy(t *testing.T) { + flag.Parse() + + t.Run("Deploy", func(t *testing.T) { + t.Log(strings.Join([]string{ + "args:", + fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath), + fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath), + fmt.Sprintf("USERNAME: %v", fUsername), + fmt.Sprintf("PASSWORD: %v", fPassword), + fmt.Sprintf("SPACEPROVIDER: %v", fSpaceProvider), + fmt.Sprintf("SPACEID: %v", fSpaceId), + fmt.Sprintf("DOMAIN: %v", fDomain), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + Username: fUsername, + Password: fPassword, + SpaceProvider: fSpaceProvider, + SpaceId: fSpaceId, + Domain: fDomain, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + fInputCertData, _ := os.ReadFile(fInputCertPath) + fInputKeyData, _ := os.ReadFile(fInputKeyPath) + res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData)) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + t.Logf("ok: %v", res) + }) +} diff --git a/internal/pkg/sdk3rd/dcloud/unicloud/api.go b/internal/pkg/sdk3rd/dcloud/unicloud/api.go new file mode 100644 index 00000000..1cd90b15 --- /dev/null +++ b/internal/pkg/sdk3rd/dcloud/unicloud/api.go @@ -0,0 +1,78 @@ +package unicloud + +import ( + "fmt" + "net/http" + "regexp" + "time" +) + +func (c *Client) ensureServerlessJwtTokenExists() error { + c.serverlessJwtTokenMtx.Lock() + defer c.serverlessJwtTokenMtx.Unlock() + if c.serverlessJwtToken != "" && c.serverlessJwtTokenExp.After(time.Now()) { + return nil + } + + params := &loginParams{ + Password: c.password, + } + if regexp.MustCompile("^1\\d{10}$").MatchString(c.username) { + params.Mobile = c.username + } else if regexp.MustCompile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$").MatchString(c.username) { + params.Email = c.username + } else { + params.Username = c.username + } + + resp := &loginResponse{} + if err := c.invokeServerlessWithResult( + uniIdentityEndpoint, uniIdentityClientSecret, uniIdentityAppId, uniIdentitySpaceId, + "uni-id-co", "login", "", params, nil, + resp); err != nil { + return err + } else if resp.Data == nil || resp.Data.NewToken == nil || resp.Data.NewToken.Token == "" { + return fmt.Errorf("unicloud api error: received empty token") + } + + c.serverlessJwtToken = resp.Data.NewToken.Token + c.serverlessJwtTokenExp = time.UnixMilli(resp.Data.NewToken.TokenExpired) + + return nil +} + +func (c *Client) ensureApiUserTokenExists() error { + if err := c.ensureServerlessJwtTokenExists(); err != nil { + return err + } + + c.apiUserTokenMtx.Lock() + defer c.apiUserTokenMtx.Unlock() + if c.apiUserToken != "" { + return nil + } + + resp := &getUserTokenResponse{} + if err := c.invokeServerlessWithResult( + uniConsoleEndpoint, uniConsoleClientSecret, uniConsoleAppId, uniConsoleSpaceId, + "uni-cloud-kernel", "", "user/getUserToken", nil, map[string]any{"isLogin": true}, + resp); err != nil { + return err + } else if resp.Data == nil || resp.Data.Data == nil || resp.Data.Data.Data == nil || resp.Data.Data.Data.Token == "" { + return fmt.Errorf("unicloud api error: received empty user token") + } + + c.apiUserToken = resp.Data.Data.Data.Token + + return nil +} + +func (c *Client) CreateDomainWithCert(req *CreateDomainWithCertRequest) (*CreateDomainWithCertResponse, error) { + if err := c.ensureApiUserTokenExists(); err != nil { + return nil, err + } + + resp := &CreateDomainWithCertResponse{} + err := c.sendRequestWithResult(http.MethodPost, "/host/create-domain-with-cert", req, resp) + return resp, err +} diff --git a/internal/pkg/sdk3rd/dcloud/unicloud/client.go b/internal/pkg/sdk3rd/dcloud/unicloud/client.go new file mode 100644 index 00000000..1e0f3728 --- /dev/null +++ b/internal/pkg/sdk3rd/dcloud/unicloud/client.go @@ -0,0 +1,257 @@ +package unicloud + +import ( + "crypto/hmac" + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "runtime" + "sort" + "strings" + "sync" + "time" + + "github.com/go-resty/resty/v2" +) + +type Client struct { + username string + password string + + serverlessJwtToken string + serverlessJwtTokenExp time.Time + serverlessJwtTokenMtx sync.Mutex + + serverlessClient *resty.Client + + apiUserToken string + apiUserTokenMtx sync.Mutex + + apiClient *resty.Client +} + +const ( + uniIdentityEndpoint = "https://account.dcloud.net.cn/client" + uniIdentityClientSecret = "ba461799-fde8-429f-8cc4-4b6d306e2339" + uniIdentityAppId = "__UNI__uniid_server" + uniIdentitySpaceId = "uni-id-server" + uniConsoleEndpoint = "https://unicloud.dcloud.net.cn/client" + uniConsoleClientSecret = "4c1f7fbf-c732-42b0-ab10-4634a8bbe834" + uniConsoleAppId = "__UNI__unicloud_console" + uniConsoleSpaceId = "dc-6nfabcn6ada8d3dd" +) + +func NewClient(username, password string) *Client { + client := &Client{ + username: username, + password: password, + } + client.serverlessClient = resty.New() + client.apiClient = resty.New(). + SetBaseURL("https://unicloud-api.dcloud.net.cn/unicloud/api"). + SetPreRequestHook(func(c *resty.Client, req *http.Request) error { + if client.apiUserToken != "" { + req.Header.Set("Token", client.apiUserToken) + } + + return nil + }) + + return client +} + +func (c *Client) WithTimeout(timeout time.Duration) *Client { + c.serverlessClient.SetTimeout(timeout) + return c +} + +func (c *Client) generateSignature(params map[string]any, secret string) string { + keys := make([]string, 0, len(params)) + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + + canonicalStr := "" + for i, k := range keys { + if i > 0 { + canonicalStr += "&" + } + canonicalStr += k + "=" + fmt.Sprintf("%v", params[k]) + } + + mac := hmac.New(md5.New, []byte(secret)) + mac.Write([]byte(canonicalStr)) + sign := mac.Sum(nil) + signHex := hex.EncodeToString(sign) + + return signHex +} + +func (c *Client) buildServerlessClientInfo(appId string) (_clientInfo map[string]any, _err error) { + return map[string]any{ + "PLATFORM": "web", + "OS": strings.ToUpper(runtime.GOOS), + "APPID": appId, + "DEVICEID": "certimate", + "LOCALE": "zh-Hans", + "osName": runtime.GOOS, + "appId": appId, + "appName": "uniCloud", + "deviceId": "certimate", + "deviceType": "pc", + "uniPlatform": "web", + "uniCompilerVersion": "4.45", + "uniRuntimeVersion": "4.45", + }, nil +} + +func (c *Client) buildServerlessPayloadInfo(appId, spaceId, target, method, action string, params, data interface{}) (map[string]any, error) { + clientInfo, err := c.buildServerlessClientInfo(appId) + if err != nil { + return nil, err + } + + functionArgsParams := make([]any, 0) + if params != nil { + functionArgsParams = append(functionArgsParams, params) + } + + functionArgs := map[string]any{ + "clientInfo": clientInfo, + "uniIdToken": c.serverlessJwtToken, + } + if method != "" { + functionArgs["method"] = method + functionArgs["params"] = make([]any, 0) + } + if action != "" { + type _obj struct{} + functionArgs["action"] = action + functionArgs["data"] = &_obj{} + } + if params != nil { + functionArgs["params"] = []any{params} + } + if data != nil { + functionArgs["data"] = data + } + + jsonb, err := json.Marshal(map[string]any{ + "functionTarget": target, + "functionArgs": functionArgs, + }) + if err != nil { + return nil, err + } + + payload := map[string]any{ + "method": "serverless.function.runtime.invoke", + "params": string(jsonb), + "spaceId": spaceId, + "timestamp": time.Now().UnixMilli(), + } + + return payload, nil +} + +func (c *Client) invokeServerless(endpoint, clientSecret, appId, spaceId, target, method, action string, params, data interface{}) (*resty.Response, error) { + if endpoint == "" { + return nil, fmt.Errorf("unicloud api error: endpoint cannot be empty") + } + + payload, err := c.buildServerlessPayloadInfo(appId, spaceId, target, method, action, params, data) + if err != nil { + return nil, fmt.Errorf("unicloud api error: failed to build request: %w", err) + } + + clientInfo, _ := c.buildServerlessClientInfo(appId) + clientInfoJsonb, _ := json.Marshal(clientInfo) + + sign := c.generateSignature(payload, clientSecret) + + req := c.serverlessClient.R(). + SetHeader("Origin", "https://unicloud.dcloud.net.cn"). + SetHeader("Referer", "https://unicloud.dcloud.net.cn"). + SetHeader("Content-Type", "application/json"). + SetHeader("X-Client-Info", string(clientInfoJsonb)). + SetHeader("X-Client-Token", c.serverlessJwtToken). + SetHeader("X-Serverless-Sign", sign). + SetBody(payload) + resp, err := req.Post(endpoint) + if err != nil { + return resp, fmt.Errorf("unicloud api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("unicloud api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return resp, nil +} + +func (c *Client) invokeServerlessWithResult(endpoint, clientSecret, appId, spaceId, target, method, action string, params, data interface{}, result BaseResponse) error { + resp, err := c.invokeServerless(endpoint, clientSecret, appId, spaceId, target, method, action, params, data) + if err != nil { + if resp != nil { + json.Unmarshal(resp.Body(), &result) + } + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("unicloud api error: failed to unmarshal response: %w", err) + } else if success := result.GetSuccess(); !success { + return fmt.Errorf("unicloud api error: code='%s', message='%s'", result.GetErrorCode(), result.GetErrorMessage()) + } + + return nil +} + +func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) { + req := c.apiClient.R() + if strings.EqualFold(method, http.MethodGet) { + qs := make(map[string]string) + if params != nil { + temp := make(map[string]any) + jsonb, _ := json.Marshal(params) + json.Unmarshal(jsonb, &temp) + for k, v := range temp { + if v != nil { + qs[k] = fmt.Sprintf("%v", v) + } + } + } + + req = req.SetQueryParams(qs) + } else { + req = req.SetHeader("Content-Type", "application/json").SetBody(params) + } + + resp, err := req.Execute(method, path) + if err != nil { + return resp, fmt.Errorf("unicloud api error: failed to send request: %w", err) + } else if resp.IsError() { + return resp, fmt.Errorf("unicloud api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String()) + } + + return resp, nil +} + +func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result BaseResponse) error { + resp, err := c.sendRequest(method, path, params) + if err != nil { + if resp != nil { + json.Unmarshal(resp.Body(), &result) + } + return err + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return fmt.Errorf("unicloud api error: failed to unmarshal response: %w", err) + } else if retcode := result.GetReturnCode(); retcode != 0 { + return fmt.Errorf("unicloud api error: ret='%d', desc='%s'", retcode, result.GetReturnDesc()) + } + + return nil +} diff --git a/internal/pkg/sdk3rd/dcloud/unicloud/models.go b/internal/pkg/sdk3rd/dcloud/unicloud/models.go new file mode 100644 index 00000000..05b02db6 --- /dev/null +++ b/internal/pkg/sdk3rd/dcloud/unicloud/models.go @@ -0,0 +1,103 @@ +package unicloud + +type BaseResponse interface { + GetSuccess() bool + GetErrorCode() string + GetErrorMessage() string + + GetReturnCode() int32 + GetReturnDesc() string +} + +type baseResponse struct { + Success *bool `json:"success,omitempty"` + Header *map[string]string `json:"header,omitempty"` + Error *struct { + Code string `json:"code"` + Message string `json:"message"` + } `json:"error,omitempty"` + + ReturnCode *int32 `json:"ret,omitempty"` + ReturnDesc *string `json:"desc,omitempty"` +} + +func (r *baseResponse) GetReturnCode() int32 { + if r.ReturnCode != nil { + return *r.ReturnCode + } + return 0 +} + +func (r *baseResponse) GetReturnDesc() string { + if r.ReturnDesc != nil { + return *r.ReturnDesc + } + return "" +} + +func (r *baseResponse) GetSuccess() bool { + if r.Success != nil { + return *r.Success + } + return false +} + +func (r *baseResponse) GetErrorCode() string { + if r.Error != nil { + return r.Error.Code + } + return "" +} + +func (r *baseResponse) GetErrorMessage() string { + if r.Error != nil { + return r.Error.Message + } + return "" +} + +type loginParams struct { + Email string `json:"email,omitempty"` + Mobile string `json:"mobile,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password"` +} + +type loginResponse struct { + baseResponse + Data *struct { + Code int32 `json:"errCode"` + UID string `json:"uid"` + NewToken *struct { + Token string `json:"token"` + TokenExpired int64 `json:"tokenExpired"` + } `json:"newToken,omitempty"` + } `json:"data,omitempty"` +} + +type getUserTokenResponse struct { + baseResponse + Data *struct { + Code int32 `json:"code"` + Data *struct { + Result int32 `json:"ret"` + Description string `json:"desc"` + Data *struct { + Email string `json:"email"` + Token string `json:"token"` + } `json:"data,omitempty"` + } `json:"data,omitempty"` + } `json:"data,omitempty"` +} + +type CreateDomainWithCertRequest struct { + Provider string `json:"provider"` + SpaceId string `json:"spaceId"` + Domain string `json:"domain"` + Cert string `json:"cert"` + Key string `json:"key"` +} + +type CreateDomainWithCertResponse struct { + baseResponse +} diff --git a/ui/public/imgs/providers/unicloud.png b/ui/public/imgs/providers/unicloud.png new file mode 100644 index 0000000000000000000000000000000000000000..a6b0ca9b306c72ea9ffa6335616358ddb21a78bf GIT binary patch literal 17955 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 className, const formSchema = z.object({ name: z .string({ message: t("access.form.name.placeholder") }) + .trim() .min(1, t("access.form.name.placeholder")) - .max(64, t("common.errmsg.string_max", { max: 64 })) - .trim(), + .max(64, t("common.errmsg.string_max", { max: 64 })), provider: z.nativeEnum(ACCESS_PROVIDERS, { message: usage === "ca-only" @@ -302,6 +303,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.UCLOUD: return ; + case ACCESS_PROVIDERS.UNICLOUD: + return ; case ACCESS_PROVIDERS.UPYUN: return ; case ACCESS_PROVIDERS.VERCEL: diff --git a/ui/src/components/access/AccessFormUniCloudConfig.tsx b/ui/src/components/access/AccessFormUniCloudConfig.tsx new file mode 100644 index 00000000..d281f1fe --- /dev/null +++ b/ui/src/components/access/AccessFormUniCloudConfig.tsx @@ -0,0 +1,68 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { type AccessConfigForUniCloud } from "@/domain/access"; + +type AccessFormUniCloudConfigFieldValues = Nullish; + +export type AccessFormUniCloudConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormUniCloudConfigFieldValues; + onValuesChange?: (values: AccessFormUniCloudConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormUniCloudConfigFieldValues => { + return { + username: "", + password: "", + }; +}; + +const AccessFormUniCloudConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormUniCloudConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + username: z.string().trim().nonempty(t("access.form.unicloud_username.placeholder")), + password: z.string().trim().nonempty(t("access.form.unicloud_password.placeholder")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormUniCloudConfig; diff --git a/ui/src/components/access/AccessFormUpyunConfig.tsx b/ui/src/components/access/AccessFormUpyunConfig.tsx index 8cc06d97..665c50cf 100644 --- a/ui/src/components/access/AccessFormUpyunConfig.tsx +++ b/ui/src/components/access/AccessFormUpyunConfig.tsx @@ -33,9 +33,9 @@ const AccessFormUpyunConfig = ({ form: formInst, formName, disabled, initialValu .max(64, t("common.errmsg.string_max", { max: 64 })), password: z .string() + .trim() .min(1, t("access.form.upyun_password.placeholder")) - .max(64, t("common.errmsg.string_max", { max: 64 })) - .trim(), + .max(64, t("common.errmsg.string_max", { max: 64 })), }); const formRule = createSchemaFieldRule(formSchema); diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index 49ed12ec..0443327e 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -82,6 +82,7 @@ import DeployNodeConfigFormTencentCloudVODConfig from "./DeployNodeConfigFormTen import DeployNodeConfigFormTencentCloudWAFConfig from "./DeployNodeConfigFormTencentCloudWAFConfig"; import DeployNodeConfigFormUCloudUCDNConfig from "./DeployNodeConfigFormUCloudUCDNConfig.tsx"; import DeployNodeConfigFormUCloudUS3Config from "./DeployNodeConfigFormUCloudUS3Config.tsx"; +import DeployNodeConfigFormUniCloudWebHostConfig from "./DeployNodeConfigFormUniCloudWebHostConfig.tsx"; import DeployNodeConfigFormUpyunCDNConfig from "./DeployNodeConfigFormUpyunCDNConfig.tsx"; import DeployNodeConfigFormUpyunFileConfig from "./DeployNodeConfigFormUpyunFileConfig.tsx"; import DeployNodeConfigFormVolcEngineALBConfig from "./DeployNodeConfigFormVolcEngineALBConfig.tsx"; @@ -318,6 +319,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOYMENT_PROVIDERS.UCLOUD_US3: return ; + case DEPLOYMENT_PROVIDERS.UNICLOUD_WEBHOST: + return ; case DEPLOYMENT_PROVIDERS.UPYUN_CDN: return ; case DEPLOYMENT_PROVIDERS.UPYUN_FILE: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormUniCloudWebHostConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormUniCloudWebHostConfig.tsx new file mode 100644 index 00000000..b16a7a39 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormUniCloudWebHostConfig.tsx @@ -0,0 +1,85 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input, Select } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormUniCloudWebHostConfigFieldValues = Nullish<{ + spaceProvider: string; + spaceId: string; + domain: string; +}>; + +export type DeployNodeConfigFormUniCloudWebHostConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormUniCloudWebHostConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormUniCloudWebHostConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormUniCloudWebHostConfigFieldValues => { + return { + spaceProvider: "tencent", + spaceId: "", + domain: "", + }; +}; + +const DeployNodeConfigFormUniCloudWebHostConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormUniCloudWebHostConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + spaceProvider: z.string().trim().nonempty(t("workflow_node.deploy.form.unicloud_webhost_space_provider.placeholder")), + spaceId: z.string().trim().nonempty(t("workflow_node.deploy.form.unicloud_webhost_space_id.placeholder")), + domain: z.string().refine((v) => validDomainName(v), t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + + + +
+ ); +}; + +export default DeployNodeConfigFormUniCloudWebHostConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 3582b071..69979aac 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -64,6 +64,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForTelegramBot | AccessConfigForTencentCloud | AccessConfigForUCloud + | AccessConfigForUniCloud | AccessConfigForUpyun | AccessConfigForVercel | AccessConfigForVolcEngine @@ -397,6 +398,11 @@ export type AccessConfigForUCloud = { projectId?: string; }; +export type AccessConfigForUniCloud = { + username: string; + password: string; +}; + export type AccessConfigForUpyun = { username: string; password: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 2d288a3c..594674f1 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -67,6 +67,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ TELEGRAMBOT: "telegrambot", TENCENTCLOUD: "tencentcloud", UCLOUD: "ucloud", + UNICLOUD: "unicloud", UPYUN: "upyun", VERCEL: "vercel", VOLCENGINE: "volcengine", @@ -127,17 +128,18 @@ export const accessProvidersMap: Map [ type, diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index 98717976..bf453f1d 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -96,12 +96,6 @@ "access.form.bunny_api_key.label": "Bunny API key", "access.form.bunny_api_key.placeholder": "Please enter Bunny API key", "access.form.bunny_api_key.tooltip": "For more information, see https://docs.bunny.net/reference/bunnynet-api-overview", - "access.form.upyun_username.label": "UPYUN subaccount username", - "access.form.upyun_username.placeholder": "Please enter UPYUN subaccount username", - "access.form.upyun_username.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", - "access.form.upyun_password.label": "UPYUN subaccount password", - "access.form.upyun_password.placeholder": "Please enter UPYUN subaccount password", - "access.form.upyun_password.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", "access.form.baishan_api_token.label": "Baishan Cloud API token", "access.form.baishan_api_token.placeholder": "Please enter Baishan Cloud API token", "access.form.baotapanel_server_url.label": "aaPanel server URL", @@ -413,6 +407,16 @@ "access.form.ucloud_project_id.label": "UCloud project ID (Optional)", "access.form.ucloud_project_id.placeholder": "Please enter UCloud project ID", "access.form.ucloud_project_id.tooltip": "For more information, see https://console.ucloud-global.com/uaccount/iam/project_manage", + "access.form.unicloud_username.label": "uniCloud username", + "access.form.unicloud_username.placeholder": "Please enter uniCloud username", + "access.form.unicloud_password.label": "uniCloud password", + "access.form.unicloud_password.placeholder": "Please enter uniCloud password", + "access.form.upyun_username.label": "UPYUN subaccount username", + "access.form.upyun_username.placeholder": "Please enter UPYUN subaccount username", + "access.form.upyun_username.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", + "access.form.upyun_password.label": "UPYUN subaccount password", + "access.form.upyun_password.placeholder": "Please enter UPYUN subaccount password", + "access.form.upyun_password.tooltip": "For more information, see https://console.upyun.com/account/subaccount/", "access.form.vercel_api_access_token.label": "Vercel API access token", "access.form.vercel_api_access_token.placeholder": "Please enter Vercel API access token", "access.form.vercel_api_access_token.tooltip": "For more information, see https://vercel.com/guides/how-do-i-use-a-vercel-api-access-token", diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json index 85966786..9e59d9d0 100644 --- a/ui/src/i18n/locales/en/nls.provider.json +++ b/ui/src/i18n/locales/en/nls.provider.json @@ -138,6 +138,8 @@ "provider.ucloud": "UCloud", "provider.ucloud.ucdn": "UCloud - UCDN (UCloud Content Delivery Network)", "provider.ucloud.us3": "UCloud - US3 (UCloud Object-based Storage)", + "provider.unicloud": "uniCloud (DCloud)", + "provider.unicloud.webhost": "uniCloud (DCloud) - Web Host", "provider.upyun": "UPYUN", "provider.upyun.cdn": "UPYUN - CDN (Content Delivery Network)", "provider.upyun.file": "UPYUN - USS (Storage Service)", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index 02e7fe87..6de6eda8 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -696,6 +696,16 @@ "workflow_node.deploy.form.ucloud_us3_domain.label": "UCloud US3 domain", "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "Please enter UCloud US3 domain name", "workflow_node.deploy.form.ucloud_us3_domain.tooltip": "For more information, see https://console.ucloud-global.com/ufile", + "workflow_node.deploy.form.unicloud_webhost.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the uniCloud, please create a GitHub Issue.", + "workflow_node.deploy.form.unicloud_webhost_space_provider.label": "uniCloud space provider", + "workflow_node.deploy.form.unicloud_webhost_space_provider.placeholder": "Please select uniCloud space provider", + "workflow_node.deploy.form.unicloud_webhost_space_provider.option.aliyun.label": "Alibaba Cloud", + "workflow_node.deploy.form.unicloud_webhost_space_provider.option.tencent.label": "Tencent Cloud", + "workflow_node.deploy.form.unicloud_webhost_space_id.label": "uniCloud space ID", + "workflow_node.deploy.form.unicloud_webhost_space_id.placeholder": "uniCloud space ID", + "workflow_node.deploy.form.unicloud_webhost_space_id.tooltip": "For more information, see https://doc.dcloud.net.cn/uniCloud/concepts/space.html", + "workflow_node.deploy.form.unicloud_webhost_domain.label": "uniCloud Web host domain", + "workflow_node.deploy.form.unicloud_webhost_domain.placeholder": "uniCloud Web host domain", "workflow_node.deploy.form.upyun_cdn.guide": "Tips: This uses webpage simulator login and does not guarantee stability. If there are any changes to the UPYUN, please create a GitHub Issue.", "workflow_node.deploy.form.upyun_cdn_domain.label": "UPYUN CDN domain", "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "Please enter UPYUN CDN domain name", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 7e184d55..fb51668f 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -407,6 +407,10 @@ "access.form.ucloud_project_id.label": "优刻得项目 ID(可选)", "access.form.ucloud_project_id.placeholder": "请输入优刻得项目 ID", "access.form.ucloud_project_id.tooltip": "这是什么?请参阅 https://console.ucloud.cn/uaccount/iam/project_manage", + "access.form.unicloud_username.label": "uniCloud 控制台账号", + "access.form.unicloud_username.placeholder": "请输入 uniCloud 控制台账号", + "access.form.unicloud_password.label": "uniCloud 控制台密码", + "access.form.unicloud_password.placeholder": "请输入 uniCloud 控制台密码", "access.form.upyun_username.label": "又拍云子账号用户名", "access.form.upyun_username.placeholder": "请输入又拍云子账号用户名", "access.form.upyun_username.tooltip": "这是什么?请参阅 https://console.upyun.com/account/subaccount/

请关闭该账号的二次登录验证。", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index c4e126e0..27aa8d0e 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -138,6 +138,8 @@ "provider.ucloud": "优刻得", "provider.ucloud.ucdn": "优刻得 - 内容分发 UCDN", "provider.ucloud.us3": "优刻得 - 对象存储 US3", + "provider.unicloud": "uniCloud (DCloud)", + "provider.unicloud.webhost": "uniCloud (DCloud) - 前端网页托管", "provider.upyun": "又拍云", "provider.upyun.cdn": "又拍云 - 云分发 CDN", "provider.upyun.file": "又拍云 - 云存储 USS", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 6f13984e..6f8f09a6 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -695,6 +695,16 @@ "workflow_node.deploy.form.ucloud_us3_domain.label": "优刻得 US3 自定义域名", "workflow_node.deploy.form.ucloud_us3_domain.placeholder": "请输入优刻得 US3 自定义域名", "workflow_node.deploy.form.ucloud_us3_domain.tooltip": "这是什么?请参阅 https://console.ucloud.cn/ufile", + "workflow_node.deploy.form.unicloud_webhost.guide": "小贴士:由于 uniCloud 未提供相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇 uniCloud 接口变更,请到 GitHub 发起 Issue 告知。", + "workflow_node.deploy.form.unicloud_webhost_space_provider.label": "uniCloud 服务空间提供商", + "workflow_node.deploy.form.unicloud_webhost_space_provider.placeholder": "请选择 uniCloud 服务空间提供商", + "workflow_node.deploy.form.unicloud_webhost_space_provider.option.aliyun.label": "阿里云", + "workflow_node.deploy.form.unicloud_webhost_space_provider.option.tencent.label": "腾讯云", + "workflow_node.deploy.form.unicloud_webhost_space_id.label": "uniCloud 服务空间 ID", + "workflow_node.deploy.form.unicloud_webhost_space_id.placeholder": "请输入 uniCloud 服务空间 ID", + "workflow_node.deploy.form.unicloud_webhost_space_id.tooltip": "这是什么?请参阅 https://doc.dcloud.net.cn/uniCloud/concepts/space.html", + "workflow_node.deploy.form.unicloud_webhost_domain.label": "uniCloud 前端网页托管网站域名", + "workflow_node.deploy.form.unicloud_webhost_domain.placeholder": "请输入 uniCloud 前端网页托管网站域名", "workflow_node.deploy.form.upyun_cdn.guide": "小贴士:由于又拍云未提供相关 API,这里将使用网页模拟登录方式部署,但无法保证稳定性。如遇又拍云接口变更,请到 GitHub 发起 Issue 告知。", "workflow_node.deploy.form.upyun_cdn_domain.label": "又拍云 CDN 加速域名", "workflow_node.deploy.form.upyun_cdn_domain.placeholder": "请输入又拍云 CDN 加速域名(支持泛域名)", From 037305d8cd1186d4007eaa1996f8c9102acef080 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 27 May 2025 04:31:26 +0800 Subject: [PATCH 19/24] update README --- README.md | 2 +- README_EN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2ab2fb7b..bc2596af 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Certimate 旨在为用户提供一个安全、简便的 SSL 证书管理解决 - 支持单域名、多域名、泛域名证书,可选 RSA、ECC 签名算法; - 支持 PEM、PFX、JKS 等多种格式输出证书; - 支持 30+ 域名托管商(如阿里云、腾讯云、Cloudflare 等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-dns-providers)); -- 支持 80+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-hosting-providers)); +- 支持 90+ 部署目标(如 Kubernetes、CDN、WAF、负载均衡等,[点此查看完整清单](https://docs.certimate.me/docs/reference/providers#supported-hosting-providers)); - 支持邮件、钉钉、飞书、企业微信、Webhook 等多种通知渠道; - 支持 Let's Encrypt、Buypass、Google Trust Services、SSL.com、ZeroSSL 等多种 ACME 证书颁发机构; - 更多特性等待探索。 diff --git a/README_EN.md b/README_EN.md index f94020a2..67bab154 100644 --- a/README_EN.md +++ b/README_EN.md @@ -39,7 +39,7 @@ Certimate aims to provide users with a secure and user-friendly SSL certificate - Supports single-domain, multi-domain, wildcard certificates, with options for RSA or ECC. - Supports various certificate formats such as PEM, PFX, JKS. - Supports more than 30+ domain registrars (e.g., Alibaba Cloud, Tencent Cloud, Cloudflare, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-dns-providers)); -- Supports more than 80+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-hosting-providers)); +- Supports more than 90+ deployment targets (e.g., Kubernetes, CDN, WAF, load balancers, etc. [Check out this link](https://docs.certimate.me/en/docs/reference/providers#supported-hosting-providers)); - Supports multiple notification channels including email, DingTalk, Feishu, WeCom, Webhook, and more; - Supports multiple ACME CAs including Let's Encrypt, Buypass, Google Trust Services,SSL.com, ZeroSSL, and more; - More features waiting to be discovered. From d964b129b072c76a11da9be8f897fc5222c0884a Mon Sep 17 00:00:00 2001 From: RHQYZ Date: Tue, 27 May 2025 04:35:10 +0800 Subject: [PATCH 20/24] bump version to v0.3.14 --- ui/src/domain/version.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/domain/version.ts b/ui/src/domain/version.ts index 442a7506..b7741526 100644 --- a/ui/src/domain/version.ts +++ b/ui/src/domain/version.ts @@ -1 +1 @@ -export const version = "v0.3.13"; +export const version = "v0.3.14"; From 211f66dc0a9de5b2b9ff1fd6f6e2433a6365fcad Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 27 May 2025 04:43:44 +0800 Subject: [PATCH 21/24] fix: tsc build error --- ui/src/components/ModalForm.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ui/src/components/ModalForm.tsx b/ui/src/components/ModalForm.tsx index 8065d2f1..7fe54951 100644 --- a/ui/src/components/ModalForm.tsx +++ b/ui/src/components/ModalForm.tsx @@ -89,13 +89,6 @@ const ModalForm = = any>({ modalProps?.afterClose?.(); }, - onClose: async (e) => { - if (formPending) return; - - // 关闭 Modal 时 Promise.reject 阻止关闭 - await modalProps?.onClose?.(e as React.MouseEvent | React.KeyboardEvent); - setOpen(false); - }, }; const handleOkClick = async () => { From d8935337d68cc69ff2c02a1eed19f75ca1d15ca0 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 27 May 2025 04:59:38 +0800 Subject: [PATCH 22/24] build: fix goreleaser.yml --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 9315c6a7..270ffb42 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -31,7 +31,7 @@ builds: goarch: arm upx: - enable: true + - enable: true release: draft: true From a4f736e0f3fc044808a40981677741a40ad8c03c Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 27 May 2025 05:04:35 +0800 Subject: [PATCH 23/24] build: fix goreleaser.yml --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 270ffb42..d65550fd 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -31,7 +31,7 @@ builds: goarch: arm upx: - - enable: true + - enabled: true release: draft: true From 6c1e3bfcf65227688bceb585ab42f03a4193218c Mon Sep 17 00:00:00 2001 From: wood chen Date: Wed, 28 May 2025 18:51:12 +0800 Subject: [PATCH 24/24] refactor: remove unused copy node functionality from SharedNodeMenu --- .../components/workflow/node/_SharedNode.tsx | 50 ++----------------- 1 file changed, 3 insertions(+), 47 deletions(-) diff --git a/ui/src/components/workflow/node/_SharedNode.tsx b/ui/src/components/workflow/node/_SharedNode.tsx index 07cd4cdd..cd067e36 100644 --- a/ui/src/components/workflow/node/_SharedNode.tsx +++ b/ui/src/components/workflow/node/_SharedNode.tsx @@ -2,7 +2,6 @@ import { memo, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { CloseCircleOutlined as CloseCircleOutlinedIcon, - CopyOutlined as CopyOutlinedIcon, EllipsisOutlined as EllipsisOutlinedIcon, FormOutlined as FormOutlinedIcon, MoreOutlined as MoreOutlinedIcon, @@ -12,7 +11,7 @@ import { Button, Card, Drawer, Dropdown, Input, type InputRef, type MenuProps, M import { produce } from "immer"; import { isEqual } from "radash"; -import { type WorkflowNode, WorkflowNodeType, newNode } from "@/domain/workflow"; +import { type WorkflowNode, WorkflowNodeType } from "@/domain/workflow"; import { useZustandShallowSelector } from "@/hooks"; import { useWorkflowStore } from "@/stores/workflow"; @@ -83,7 +82,7 @@ const isNodeReadOnly = (node: WorkflowNode) => { const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, afterUpdate, afterDelete }: SharedNodeMenuProps) => { const { t } = useTranslation(); - const { updateNode, removeNode, removeBranch, addNode } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeNode", "removeBranch", "addNode"])); + const { updateNode, removeNode, removeBranch } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeNode", "removeBranch"])); const [modalApi, ModelContextHolder] = Modal.useModal(); @@ -106,49 +105,6 @@ const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, afterUpdate?.(); }; - const handleCopyNode = async () => { - try { - // Clone the node by creating a new node of the same type - const clonedNode = newNode(node.type); - // Copy over configurations - clonedNode.name = `${node.name} (副本)`; - - // Copy all configurable properties - if (node.config) { - clonedNode.config = JSON.parse(JSON.stringify(node.config)); - } - - if (node.inputs) { - clonedNode.inputs = JSON.parse(JSON.stringify(node.inputs)); - } - - if (node.outputs) { - clonedNode.outputs = JSON.parse(JSON.stringify(node.outputs)); - } - - if (node.validated !== undefined) { - clonedNode.validated = node.validated; - } - - // For branch nodes, we need to deep copy the branches structure - if (node.branches && node.type === WorkflowNodeType.Branch) { - // For branch nodes, we'll keep the structure but with new IDs - // This ensures we don't run into ID conflicts - clonedNode.branches = newNode(WorkflowNodeType.Branch).branches; - } else if (node.branches && node.type === WorkflowNodeType.ExecuteResultBranch) { - // For execute result branches, we'll keep the structure but with new IDs - clonedNode.branches = newNode(WorkflowNodeType.ExecuteResultBranch).branches; - } - - // Add the cloned node after the current node - await addNode(clonedNode, node.id); - - afterUpdate?.(); - } catch (err) { - console.error("Failed to copy node:", err); - } - }; - const handleDeleteClick = async () => { if (isNodeBranchLike(node)) { await removeBranch(branchId!, branchIndex!); @@ -394,4 +350,4 @@ export default { Menu: memo(SharedNodeMenu), Block: memo(SharedNodeBlock), ConfigDrawer: memo(SharedNodeConfigDrawer), -}; +}; \ No newline at end of file