mirror of https://github.com/usual2970/certimate
				
				
				
			feat: add 1panel deployer
							parent
							
								
									6ccbdeb89a
								
							
						
					
					
						commit
						29dda4ec66
					
				| 
						 | 
				
			
			@ -6,6 +6,8 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/usual2970/certimate/internal/domain"
 | 
			
		||||
	"github.com/usual2970/certimate/internal/pkg/core/deployer"
 | 
			
		||||
	p1PanelConsole "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/1panel-console"
 | 
			
		||||
	p1PanelSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/1panel-site"
 | 
			
		||||
	pAliyunALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-alb"
 | 
			
		||||
	pAliyunCASDeploy "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cas-deploy"
 | 
			
		||||
	pAliyunCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-cdn"
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +71,35 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) {
 | 
			
		|||
	  NOTICE: If you add new constant, please keep ASCII order.
 | 
			
		||||
	*/
 | 
			
		||||
	switch options.Provider {
 | 
			
		||||
	case domain.DeployProviderType1PanelConsole, domain.DeployProviderType1PanelSite:
 | 
			
		||||
		{
 | 
			
		||||
			access := domain.AccessConfigFor1Panel{}
 | 
			
		||||
			if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("failed to populate provider access config: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch options.Provider {
 | 
			
		||||
			case domain.DeployProviderType1PanelConsole:
 | 
			
		||||
				deployer, err := p1PanelConsole.NewDeployer(&p1PanelConsole.DeployerConfig{
 | 
			
		||||
					ApiUrl:      access.ApiUrl,
 | 
			
		||||
					ApiKey:      access.ApiKey,
 | 
			
		||||
					AutoRestart: maps.GetValueAsBool(options.ProviderDeployConfig, "autoRestart"),
 | 
			
		||||
				})
 | 
			
		||||
				return deployer, err
 | 
			
		||||
 | 
			
		||||
			case domain.DeployProviderType1PanelSite:
 | 
			
		||||
				deployer, err := p1PanelSite.NewDeployer(&p1PanelSite.DeployerConfig{
 | 
			
		||||
					ApiUrl:    access.ApiUrl,
 | 
			
		||||
					ApiKey:    access.ApiKey,
 | 
			
		||||
					WebsiteId: maps.GetValueAsInt64(options.ProviderDeployConfig, "websiteId"),
 | 
			
		||||
				})
 | 
			
		||||
				return deployer, err
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case domain.DeployProviderTypeAliyunALB, domain.DeployProviderTypeAliyunCASDeploy, domain.DeployProviderTypeAliyunCDN, domain.DeployProviderTypeAliyunCLB, domain.DeployProviderTypeAliyunDCDN, domain.DeployProviderTypeAliyunESA, domain.DeployProviderTypeAliyunLive, domain.DeployProviderTypeAliyunNLB, domain.DeployProviderTypeAliyunOSS, domain.DeployProviderTypeAliyunVOD, domain.DeployProviderTypeAliyunWAF:
 | 
			
		||||
		{
 | 
			
		||||
			access := domain.AccessConfigForAliyun{}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,11 @@ func (a *Access) UnmarshalConfigToMap() (map[string]any, error) {
 | 
			
		|||
	return config, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccessConfigFor1Panel struct {
 | 
			
		||||
	ApiUrl string `json:"apiUrl"`
 | 
			
		||||
	ApiKey string `json:"apiKey"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccessConfigForACMEHttpReq struct {
 | 
			
		||||
	Endpoint string `json:"endpoint"`
 | 
			
		||||
	Mode     string `json:"mode,omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ type AccessProviderType string
 | 
			
		|||
	NOTICE: If you add new constant, please keep ASCII order.
 | 
			
		||||
*/
 | 
			
		||||
const (
 | 
			
		||||
	AccessProviderType1Panel       = AccessProviderType("1panel") // 1Panel(预留)
 | 
			
		||||
	AccessProviderType1Panel       = AccessProviderType("1panel")
 | 
			
		||||
	AccessProviderTypeACMEHttpReq  = AccessProviderType("acmehttpreq")
 | 
			
		||||
	AccessProviderTypeAkamai       = AccessProviderType("akamai") // Akamai(预留)
 | 
			
		||||
	AccessProviderTypeAliyun       = AccessProviderType("aliyun")
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +108,8 @@ type DeployProviderType string
 | 
			
		|||
	NOTICE: If you add new constant, please keep ASCII order.
 | 
			
		||||
*/
 | 
			
		||||
const (
 | 
			
		||||
	DeployProviderType1PanelConsole         = DeployProviderType("1panel-console")
 | 
			
		||||
	DeployProviderType1PanelSite            = DeployProviderType("1panel-site")
 | 
			
		||||
	DeployProviderTypeAliyunALB             = DeployProviderType("aliyun-alb")
 | 
			
		||||
	DeployProviderTypeAliyunCASDeploy       = DeployProviderType("aliyun-casdeploy")
 | 
			
		||||
	DeployProviderTypeAliyunCDN             = DeployProviderType("aliyun-cdn")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
package onepanelconsole
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	xerrors "github.com/pkg/errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/usual2970/certimate/internal/pkg/core/deployer"
 | 
			
		||||
	"github.com/usual2970/certimate/internal/pkg/core/logger"
 | 
			
		||||
	opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DeployerConfig struct {
 | 
			
		||||
	// 1Panel 地址。
 | 
			
		||||
	ApiUrl string `json:"apiUrl"`
 | 
			
		||||
	// 1Panel 接口密钥。
 | 
			
		||||
	ApiKey string `json:"apiKey"`
 | 
			
		||||
	// 是否自动重启。
 | 
			
		||||
	AutoRestart bool `json:"autoRestart"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DeployerProvider struct {
 | 
			
		||||
	config    *DeployerConfig
 | 
			
		||||
	logger    logger.Logger
 | 
			
		||||
	sdkClient *opsdk.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
 | 
			
		||||
 | 
			
		||||
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
 | 
			
		||||
	if config == nil {
 | 
			
		||||
		panic("config is nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client, err := createSdkClient(config.ApiUrl, config.ApiKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to create sdk client")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &DeployerProvider{
 | 
			
		||||
		config:    config,
 | 
			
		||||
		logger:    logger.NewNilLogger(),
 | 
			
		||||
		sdkClient: client,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
 | 
			
		||||
	d.logger = logger
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
 | 
			
		||||
	// 设置面板 SSL 证书
 | 
			
		||||
	updateSystemSSLReq := &opsdk.UpdateSystemSSLRequest{
 | 
			
		||||
		Cert:    certPem,
 | 
			
		||||
		Key:     privkeyPem,
 | 
			
		||||
		SSL:     "enable",
 | 
			
		||||
		SSLType: "import-paste",
 | 
			
		||||
	}
 | 
			
		||||
	if d.config.AutoRestart {
 | 
			
		||||
		updateSystemSSLReq.AutoRestart = "true"
 | 
			
		||||
	} else {
 | 
			
		||||
		updateSystemSSLReq.AutoRestart = "false"
 | 
			
		||||
	}
 | 
			
		||||
	updateSystemSSLResp, err := d.sdkClient.UpdateSystemSSL(updateSystemSSLReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.UpdateSystemSSL'")
 | 
			
		||||
	} else {
 | 
			
		||||
		d.logger.Logt("已设置面板 SSL 证书", updateSystemSSLResp)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &deployer.DeployResult{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createSdkClient(apiUrl, apiKey string) (*opsdk.Client, error) {
 | 
			
		||||
	if _, err := url.Parse(apiUrl); err != nil {
 | 
			
		||||
		return nil, errors.New("invalid 1panel api url")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if apiKey == "" {
 | 
			
		||||
		return nil, errors.New("invalid 1panel api key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := opsdk.NewClient(apiUrl, apiKey)
 | 
			
		||||
	return client, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
package onepanelconsole_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/1panel-console"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	fInputCertPath string
 | 
			
		||||
	fInputKeyPath  string
 | 
			
		||||
	fApiUrl        string
 | 
			
		||||
	fApiKey        string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	argsPrefix := "CERTIMATE_DEPLOYER_1PANELCONSOLE_"
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
 | 
			
		||||
	flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
 | 
			
		||||
	flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
 | 
			
		||||
	flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
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_APIKEY="your-api-key"
 | 
			
		||||
*/
 | 
			
		||||
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("APIURL: %v", fApiUrl),
 | 
			
		||||
			fmt.Sprintf("APIKEY: %v", fApiKey),
 | 
			
		||||
		}, "\n"))
 | 
			
		||||
 | 
			
		||||
		deployer, err := provider.NewDeployer(&provider.DeployerConfig{
 | 
			
		||||
			ApiUrl:      fApiUrl,
 | 
			
		||||
			ApiKey:      fApiKey,
 | 
			
		||||
			AutoRestart: true,
 | 
			
		||||
		})
 | 
			
		||||
		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)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,120 @@
 | 
			
		|||
package onepanelsite
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	xerrors "github.com/pkg/errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/usual2970/certimate/internal/pkg/core/deployer"
 | 
			
		||||
	"github.com/usual2970/certimate/internal/pkg/core/logger"
 | 
			
		||||
	"github.com/usual2970/certimate/internal/pkg/core/uploader"
 | 
			
		||||
	uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/1panel-ssl"
 | 
			
		||||
	opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DeployerConfig struct {
 | 
			
		||||
	// 1Panel 地址。
 | 
			
		||||
	ApiUrl string `json:"apiUrl"`
 | 
			
		||||
	// 1Panel 接口密钥。
 | 
			
		||||
	ApiKey string `json:"apiKey"`
 | 
			
		||||
	// 网站 ID。
 | 
			
		||||
	WebsiteId int64 `json:"websiteId"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DeployerProvider struct {
 | 
			
		||||
	config      *DeployerConfig
 | 
			
		||||
	logger      logger.Logger
 | 
			
		||||
	sdkClient   *opsdk.Client
 | 
			
		||||
	sslUploader uploader.Uploader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ deployer.Deployer = (*DeployerProvider)(nil)
 | 
			
		||||
 | 
			
		||||
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
 | 
			
		||||
	if config == nil {
 | 
			
		||||
		panic("config is nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client, err := createSdkClient(config.ApiUrl, config.ApiKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to create sdk client")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
 | 
			
		||||
		ApiUrl: config.ApiUrl,
 | 
			
		||||
		ApiKey: config.ApiKey,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &DeployerProvider{
 | 
			
		||||
		config:      config,
 | 
			
		||||
		logger:      logger.NewNilLogger(),
 | 
			
		||||
		sdkClient:   client,
 | 
			
		||||
		sslUploader: uploader,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider {
 | 
			
		||||
	d.logger = logger
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
 | 
			
		||||
	// 获取网站 HTTPS 配置
 | 
			
		||||
	getHttpsConfReq := &opsdk.GetHttpsConfRequest{
 | 
			
		||||
		WebsiteID: d.config.WebsiteId,
 | 
			
		||||
	}
 | 
			
		||||
	getHttpsConfResp, err := d.sdkClient.GetHttpsConf(getHttpsConfReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.GetHttpsConf'")
 | 
			
		||||
	} else {
 | 
			
		||||
		d.logger.Logt("已获取网站 HTTPS 配置", getHttpsConfResp)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 上传证书到面板
 | 
			
		||||
	upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to upload certificate file")
 | 
			
		||||
	} else {
 | 
			
		||||
		d.logger.Logt("certificate file uploaded", upres)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 修改网站 HTTPS 配置
 | 
			
		||||
	certId, _ := strconv.ParseInt(upres.CertId, 10, 64)
 | 
			
		||||
	updateHttpsConfReq := &opsdk.UpdateHttpsConfRequest{
 | 
			
		||||
		WebsiteID:    d.config.WebsiteId,
 | 
			
		||||
		Type:         "existed",
 | 
			
		||||
		WebsiteSSLID: certId,
 | 
			
		||||
		Enable:       getHttpsConfResp.Data.Enable,
 | 
			
		||||
		HttpConfig:   getHttpsConfResp.Data.HttpConfig,
 | 
			
		||||
		SSLProtocol:  getHttpsConfResp.Data.SSLProtocol,
 | 
			
		||||
		Algorithm:    getHttpsConfResp.Data.Algorithm,
 | 
			
		||||
		Hsts:         getHttpsConfResp.Data.Hsts,
 | 
			
		||||
	}
 | 
			
		||||
	updateHttpsConfResp, err := d.sdkClient.UpdateHttpsConf(updateHttpsConfReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.UpdateHttpsConf'")
 | 
			
		||||
	} else {
 | 
			
		||||
		d.logger.Logt("已获取网站 HTTPS 配置", updateHttpsConfResp)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &deployer.DeployResult{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createSdkClient(apiUrl, apiKey string) (*opsdk.Client, error) {
 | 
			
		||||
	if _, err := url.Parse(apiUrl); err != nil {
 | 
			
		||||
		return nil, errors.New("invalid 1panel api url")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if apiKey == "" {
 | 
			
		||||
		return nil, errors.New("invalid 1panel api key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := opsdk.NewClient(apiUrl, apiKey)
 | 
			
		||||
	return client, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,75 @@
 | 
			
		|||
package onepanelsite_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/1panel-site"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	fInputCertPath string
 | 
			
		||||
	fInputKeyPath  string
 | 
			
		||||
	fApiUrl        string
 | 
			
		||||
	fApiKey        string
 | 
			
		||||
	fWebsiteId     int64
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	argsPrefix := "CERTIMATE_DEPLOYER_1PANELCONSOLE_"
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
 | 
			
		||||
	flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
 | 
			
		||||
	flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
 | 
			
		||||
	flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
 | 
			
		||||
	flag.Int64Var(&fWebsiteId, argsPrefix+"WEBSITEID", 0, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
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_APIKEY="your-api-key" \
 | 
			
		||||
	--CERTIMATE_DEPLOYER_1PANELCONSOLE_WEBSITEID="your-website-id"
 | 
			
		||||
*/
 | 
			
		||||
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("APIURL: %v", fApiUrl),
 | 
			
		||||
			fmt.Sprintf("APIKEY: %v", fApiKey),
 | 
			
		||||
			fmt.Sprintf("WEBSITEID: %v", fWebsiteId),
 | 
			
		||||
		}, "\n"))
 | 
			
		||||
 | 
			
		||||
		deployer, err := provider.NewDeployer(&provider.DeployerConfig{
 | 
			
		||||
			ApiUrl:    fApiUrl,
 | 
			
		||||
			ApiKey:    fApiKey,
 | 
			
		||||
			WebsiteId: fWebsiteId,
 | 
			
		||||
		})
 | 
			
		||||
		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)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -49,8 +49,9 @@ func TestDeploy(t *testing.T) {
 | 
			
		|||
		}, "\n"))
 | 
			
		||||
 | 
			
		||||
		deployer, err := provider.NewDeployer(&provider.DeployerConfig{
 | 
			
		||||
			ApiUrl: fApiUrl,
 | 
			
		||||
			ApiKey: fApiKey,
 | 
			
		||||
			ApiUrl:      fApiUrl,
 | 
			
		||||
			ApiKey:      fApiKey,
 | 
			
		||||
			AutoRestart: true,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("err: %+v", err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,125 @@
 | 
			
		|||
package onepanelssl
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	xerrors "github.com/pkg/errors"
 | 
			
		||||
 | 
			
		||||
	"github.com/usual2970/certimate/internal/pkg/core/uploader"
 | 
			
		||||
	opsdk "github.com/usual2970/certimate/internal/pkg/vendors/1panel-sdk"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UploaderConfig struct {
 | 
			
		||||
	// 1Panel 地址。
 | 
			
		||||
	ApiUrl string `json:"apiUrl"`
 | 
			
		||||
	// 1Panel 接口密钥。
 | 
			
		||||
	ApiKey string `json:"apiKey"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UploaderProvider struct {
 | 
			
		||||
	config    *UploaderConfig
 | 
			
		||||
	sdkClient *opsdk.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ uploader.Uploader = (*UploaderProvider)(nil)
 | 
			
		||||
 | 
			
		||||
func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
 | 
			
		||||
	if config == nil {
 | 
			
		||||
		panic("config is nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client, err := createSdkClient(config.ApiUrl, config.ApiKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to create sdk client")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &UploaderProvider{
 | 
			
		||||
		config:    config,
 | 
			
		||||
		sdkClient: client,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) {
 | 
			
		||||
	// 遍历证书列表,避免重复上传
 | 
			
		||||
	if res, err := u.getExistCert(ctx, certPem, privkeyPem); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	} else if res != nil {
 | 
			
		||||
		return res, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 生成新证书名(需符合 1Panel 命名规则)
 | 
			
		||||
	certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
 | 
			
		||||
 | 
			
		||||
	// 上传证书
 | 
			
		||||
	uploadWebsiteSSLReq := &opsdk.UploadWebsiteSSLRequest{
 | 
			
		||||
		Type:        "paste",
 | 
			
		||||
		Description: certName,
 | 
			
		||||
		Certificate: certPem,
 | 
			
		||||
		PrivateKey:  privkeyPem,
 | 
			
		||||
	}
 | 
			
		||||
	uploadWebsiteSSLResp, err := u.sdkClient.UploadWebsiteSSL(uploadWebsiteSSLReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.UploadWebsiteSSL'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 遍历证书列表,获取刚刚上传证书 ID
 | 
			
		||||
	if res, err := u.getExistCert(ctx, certPem, privkeyPem); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	} else if res == nil {
 | 
			
		||||
		return nil, fmt.Errorf("no ssl certificate found, may be upload failed (code: %d, message: %s)", uploadWebsiteSSLResp.GetCode(), uploadWebsiteSSLResp.GetMessage())
 | 
			
		||||
	} else {
 | 
			
		||||
		return res, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) {
 | 
			
		||||
	searchWebsiteSSLPageNumber := int32(1)
 | 
			
		||||
	searchWebsiteSSLPageSize := int32(100)
 | 
			
		||||
	for {
 | 
			
		||||
		searchWebsiteSSLReq := &opsdk.SearchWebsiteSSLRequest{
 | 
			
		||||
			Page:     searchWebsiteSSLPageNumber,
 | 
			
		||||
			PageSize: searchWebsiteSSLPageSize,
 | 
			
		||||
		}
 | 
			
		||||
		searchWebsiteSSLResp, err := u.sdkClient.SearchWebsiteSSL(searchWebsiteSSLReq)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Wrap(err, "failed to execute sdk request '1panel.SearchWebsiteSSL'")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, sslItem := range searchWebsiteSSLResp.Data.Items {
 | 
			
		||||
			if strings.TrimSpace(sslItem.PEM) == strings.TrimSpace(certPem) &&
 | 
			
		||||
				strings.TrimSpace(sslItem.PrivateKey) == strings.TrimSpace(privkeyPem) {
 | 
			
		||||
				// 如果已存在相同证书,直接返回已有的证书信息
 | 
			
		||||
				return &uploader.UploadResult{
 | 
			
		||||
					CertId:   fmt.Sprintf("%d", sslItem.ID),
 | 
			
		||||
					CertName: sslItem.Description,
 | 
			
		||||
				}, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(searchWebsiteSSLResp.Data.Items) < int(searchWebsiteSSLPageSize) {
 | 
			
		||||
			break
 | 
			
		||||
		} else {
 | 
			
		||||
			searchWebsiteSSLPageNumber++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createSdkClient(apiUrl, apiKey string) (*opsdk.Client, error) {
 | 
			
		||||
	if _, err := url.Parse(apiUrl); err != nil {
 | 
			
		||||
		return nil, errors.New("invalid 1panel api url")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if apiKey == "" {
 | 
			
		||||
		return nil, errors.New("invalid 1panel api key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := opsdk.NewClient(apiUrl, apiKey)
 | 
			
		||||
	return client, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
package onepanelssl_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/1panel-ssl"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	fInputCertPath string
 | 
			
		||||
	fInputKeyPath  string
 | 
			
		||||
	fApiUrl        string
 | 
			
		||||
	fApiKey        string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	argsPrefix := "CERTIMATE_UPLOADER_1PANELSSL_"
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
 | 
			
		||||
	flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
 | 
			
		||||
	flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "")
 | 
			
		||||
	flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
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_APIKEY="your-api-key"
 | 
			
		||||
*/
 | 
			
		||||
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("APIURL: %v", fApiUrl),
 | 
			
		||||
			fmt.Sprintf("APIKEY: %v", fApiKey),
 | 
			
		||||
		}, "\n"))
 | 
			
		||||
 | 
			
		||||
		uploader, err := provider.NewUploader(&provider.UploaderConfig{
 | 
			
		||||
			ApiUrl: fApiUrl,
 | 
			
		||||
			ApiKey: fApiKey,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("err: %+v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fInputCertData, _ := os.ReadFile(fInputCertPath)
 | 
			
		||||
		fInputKeyData, _ := os.ReadFile(fInputKeyPath)
 | 
			
		||||
		res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("err: %+v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sres, _ := json.Marshal(res)
 | 
			
		||||
		t.Logf("ok: %s", string(sres))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +76,13 @@ func (u *UploaderProvider) Upload(ctx context.Context, certPem string, privkeyPe
 | 
			
		|||
	uploadNormalCertificateResp, err := u.sdkClient.UploadNormalCertificate(uploadNormalCertificateReq)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if uploadNormalCertificateResp != nil && uploadNormalCertificateResp.GetRetCode() == 80035 {
 | 
			
		||||
			return u.getExistCert(ctx, certPem)
 | 
			
		||||
			if res, err := u.getExistCert(ctx, certPem); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			} else if res == nil {
 | 
			
		||||
				return nil, errors.New("no certificate found")
 | 
			
		||||
			} else {
 | 
			
		||||
				return res, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil, xerrors.Wrap(err, "failed to execute sdk request 'ussl.UploadNormalCertificate'")
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +211,7 @@ func (u *UploaderProvider) getExistCert(ctx context.Context, certPem string) (re
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errors.New("no certificate found")
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createSdkClient(privateKey, publicKey string) (*usdkSsl.USSLClient, error) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
package onepanelsdk
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (c *Client) UpdateSystemSSL(req *UpdateSystemSSLRequest) (*UpdateSystemSSLResponse, error) {
 | 
			
		||||
	resp := &UpdateSystemSSLResponse{}
 | 
			
		||||
	err := c.sendRequestWithResult(http.MethodPost, "/settings/ssl/update", req, resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return resp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) SearchWebsiteSSL(req *SearchWebsiteSSLRequest) (*SearchWebsiteSSLResponse, error) {
 | 
			
		||||
	resp := &SearchWebsiteSSLResponse{}
 | 
			
		||||
	err := c.sendRequestWithResult(http.MethodPost, "/websites/ssl/search", req, resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return resp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) UploadWebsiteSSL(req *UploadWebsiteSSLRequest) (*UploadWebsiteSSLResponse, error) {
 | 
			
		||||
	resp := &UploadWebsiteSSLResponse{}
 | 
			
		||||
	err := c.sendRequestWithResult(http.MethodPost, "/websites/ssl/upload", req, resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return resp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) GetHttpsConf(req *GetHttpsConfRequest) (*GetHttpsConfResponse, error) {
 | 
			
		||||
	resp := &GetHttpsConfResponse{}
 | 
			
		||||
	err := c.sendRequestWithResult(http.MethodGet, fmt.Sprintf("/websites/%d/https", req.WebsiteID), req, resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return resp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) UpdateHttpsConf(req *UpdateHttpsConfRequest) (*UpdateHttpsConfResponse, error) {
 | 
			
		||||
	resp := &UpdateHttpsConfResponse{}
 | 
			
		||||
	err := c.sendRequestWithResult(http.MethodPost, fmt.Sprintf("/websites/%d/https", req.WebsiteID), req, resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return resp, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
package onepanelsdk
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-resty/resty/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Client struct {
 | 
			
		||||
	apiHost string
 | 
			
		||||
	apiKey  string
 | 
			
		||||
 | 
			
		||||
	client *resty.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewClient(apiHost, apiKey string) *Client {
 | 
			
		||||
	client := resty.New()
 | 
			
		||||
 | 
			
		||||
	return &Client{
 | 
			
		||||
		apiHost: strings.TrimRight(apiHost, "/"),
 | 
			
		||||
		apiKey:  apiKey,
 | 
			
		||||
		client:  client,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) WithTimeout(timeout time.Duration) *Client {
 | 
			
		||||
	c.client.SetTimeout(timeout)
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) WithTlsConfig(config *tls.Config) *Client {
 | 
			
		||||
	c.client.SetTLSClientConfig(config)
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) generateToken(timestamp string) string {
 | 
			
		||||
	tokenMd5 := md5.Sum([]byte("1panel" + c.apiKey + timestamp))
 | 
			
		||||
	tokenMd5Hex := hex.EncodeToString(tokenMd5[:])
 | 
			
		||||
	return tokenMd5Hex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
 | 
			
		||||
	req := c.client.R()
 | 
			
		||||
	req.Method = method
 | 
			
		||||
	req.URL = c.apiHost + "/api/v1" + path
 | 
			
		||||
	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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timestamp := fmt.Sprintf("%d", time.Now().Unix())
 | 
			
		||||
	token := c.generateToken(timestamp)
 | 
			
		||||
	req.SetHeader("1Panel-Timestamp", timestamp)
 | 
			
		||||
	req.SetHeader("1Panel-Token", token)
 | 
			
		||||
 | 
			
		||||
	resp, err := req.Send()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("1panel api error: failed to send request: %w", err)
 | 
			
		||||
	} else if resp.IsError() {
 | 
			
		||||
		return nil, fmt.Errorf("1panel api error: unexpected status code: %d, %s", resp.StatusCode(), resp.Body())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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 {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := json.Unmarshal(resp.Body(), &result); err != nil {
 | 
			
		||||
		return fmt.Errorf("1panel api error: failed to parse response: %w", err)
 | 
			
		||||
	} else if errcode := result.GetCode(); errcode/100 != 2 {
 | 
			
		||||
		return fmt.Errorf("1panel api error: %d - %s", errcode, result.GetMessage())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
package onepanelsdk
 | 
			
		||||
 | 
			
		||||
type BaseResponse interface {
 | 
			
		||||
	GetCode() int32
 | 
			
		||||
	GetMessage() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type baseResponse struct {
 | 
			
		||||
	Code    int32  `json:"code"`
 | 
			
		||||
	Message string `json:"message"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *baseResponse) GetCode() int32 {
 | 
			
		||||
	return r.Code
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *baseResponse) GetMessage() string {
 | 
			
		||||
	return r.Message
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UpdateSystemSSLRequest struct {
 | 
			
		||||
	Cert        string `json:"cert"`
 | 
			
		||||
	Key         string `json:"key"`
 | 
			
		||||
	SSLType     string `json:"sslType"`
 | 
			
		||||
	SSL         string `json:"ssl"`
 | 
			
		||||
	SSLID       int64  `json:"sslID"`
 | 
			
		||||
	AutoRestart string `json:"autoRestart"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UpdateSystemSSLResponse struct {
 | 
			
		||||
	baseResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SearchWebsiteSSLRequest struct {
 | 
			
		||||
	Page     int32 `json:"page"`
 | 
			
		||||
	PageSize int32 `json:"pageSize"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SearchWebsiteSSLResponse struct {
 | 
			
		||||
	baseResponse
 | 
			
		||||
	Data struct {
 | 
			
		||||
		Items []*struct {
 | 
			
		||||
			ID          int64  `json:"id"`
 | 
			
		||||
			PEM         string `json:"pem"`
 | 
			
		||||
			PrivateKey  string `json:"privateKey"`
 | 
			
		||||
			Domains     string `json:"domains"`
 | 
			
		||||
			Description string `json:"description"`
 | 
			
		||||
			Status      string `json:"status"`
 | 
			
		||||
			UpdatedAt   string `json:"updatedAt"`
 | 
			
		||||
			CreatedAt   string `json:"createdAt"`
 | 
			
		||||
		} `json:"items"`
 | 
			
		||||
		Total int32 `json:"total"`
 | 
			
		||||
	} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UploadWebsiteSSLRequest struct {
 | 
			
		||||
	Type            string `json:"type"`
 | 
			
		||||
	SSLID           int64  `json:"sslID"`
 | 
			
		||||
	Certificate     string `json:"certificate"`
 | 
			
		||||
	CertificatePath string `json:"certificatePath"`
 | 
			
		||||
	PrivateKey      string `json:"privateKey"`
 | 
			
		||||
	PrivateKeyPath  string `json:"privateKeyPath"`
 | 
			
		||||
	Description     string `json:"description"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UploadWebsiteSSLResponse struct {
 | 
			
		||||
	baseResponse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GetHttpsConfRequest struct {
 | 
			
		||||
	WebsiteID int64 `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GetHttpsConfResponse struct {
 | 
			
		||||
	baseResponse
 | 
			
		||||
	Data struct {
 | 
			
		||||
		Enable      bool     `json:"enable"`
 | 
			
		||||
		HttpConfig  string   `json:"httpConfig"`
 | 
			
		||||
		SSLProtocol []string `json:"SSLProtocol"`
 | 
			
		||||
		Algorithm   string   `json:"algorithm"`
 | 
			
		||||
		Hsts        bool     `json:"hsts"`
 | 
			
		||||
	} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UpdateHttpsConfRequest struct {
 | 
			
		||||
	WebsiteID       int64    `json:"websiteId"`
 | 
			
		||||
	Enable          bool     `json:"enable"`
 | 
			
		||||
	Type            string   `json:"type"`
 | 
			
		||||
	WebsiteSSLID    int64    `json:"websiteSSLId"`
 | 
			
		||||
	PrivateKey      string   `json:"privateKey"`
 | 
			
		||||
	Certificate     string   `json:"certificate"`
 | 
			
		||||
	PrivateKeyPath  string   `json:"privateKeyPath"`
 | 
			
		||||
	CertificatePath string   `json:"certificatePath"`
 | 
			
		||||
	ImportType      string   `json:"importType"`
 | 
			
		||||
	HttpConfig      string   `json:"httpConfig"`
 | 
			
		||||
	SSLProtocol     []string `json:"SSLProtocol"`
 | 
			
		||||
	Algorithm       string   `json:"algorithm"`
 | 
			
		||||
	Hsts            bool     `json:"hsts"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UpdateHttpsConfResponse struct {
 | 
			
		||||
	baseResponse
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5156" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M69.530864 0h884.938272v1024H69.530864z" fill="#E0E0E0" fill-opacity="0"></path><path d="M79.560165 259.855802v503.572017l435.09465 250.73251 434.041152-250.73251-1.053498-502.518519-432.987654-250.73251-435.09465 249.679012z" fill="#005EFD"></path><path d="M93.255638 268.283786v486.716049l422.452675 241.251029 419.292181-242.304527v-485.662551l-421.399177-242.304527-420.345679 242.304527z" fill="#FFFFFF"></path><path d="M139.609547 294.621235l95.868313 56.888888 371.884774-218.074074-93.761317-53.728395-373.99177 214.913581z" fill="#005EFD"></path><path d="M139.609547 293.567737v435.09465l243.358025 139.061728 92.707819-55.835391-241.251029-140.115226 1.053498-320.263375-95.868313-57.942386zM791.269663 350.096329v321.780412l95.269925 55.732148V294.364181L645.749992 154.506008 551.527243 210.238156l239.74242 139.858173z" fill="#0854C1"></path><path d="M420.893498 886.470058l93.339918 56.052411 372.306172-214.692346-94.387094-57.110123L420.893498 886.470058zM385.074568 416.826996h49.88102v280.230453l93.3947 55.835391v-474.074075l-143.27572 81.119342v56.888889z" fill="#005EFD"></path><path d="M528.350288 752.89284l31.604938-16.855968v-440.362139l-31.604938-16.855968v474.074075z" fill="#0854C1"></path></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
						 | 
				
			
			@ -9,6 +9,7 @@ import { type AccessModel } from "@/domain/access";
 | 
			
		|||
import { ACCESS_PROVIDERS } from "@/domain/provider";
 | 
			
		||||
import { useAntdForm, useAntdFormName } from "@/hooks";
 | 
			
		||||
 | 
			
		||||
import AccessForm1PanelConfig from "./AccessForm1PanelConfig";
 | 
			
		||||
import AccessFormACMEHttpReqConfig from "./AccessFormACMEHttpReqConfig";
 | 
			
		||||
import AccessFormAliyunConfig from "./AccessFormAliyunConfig";
 | 
			
		||||
import AccessFormAWSConfig from "./AccessFormAWSConfig";
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +100,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
 | 
			
		|||
      NOTICE: If you add new child component, please keep ASCII order.
 | 
			
		||||
     */
 | 
			
		||||
    switch (fieldProvider) {
 | 
			
		||||
      case ACCESS_PROVIDERS["1PANEL"]:
 | 
			
		||||
        return <AccessForm1PanelConfig {...nestedFormProps} />;
 | 
			
		||||
      case ACCESS_PROVIDERS.ACMEHTTPREQ:
 | 
			
		||||
        return <AccessFormACMEHttpReqConfig {...nestedFormProps} />;
 | 
			
		||||
      case ACCESS_PROVIDERS.ALIYUN:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { Form, type FormInstance, Input } from "antd";
 | 
			
		||||
import { createSchemaFieldRule } from "antd-zod";
 | 
			
		||||
import { z } from "zod";
 | 
			
		||||
 | 
			
		||||
import { type AccessConfigFor1Panel } from "@/domain/access";
 | 
			
		||||
 | 
			
		||||
type AccessForm1PanelConfigFieldValues = Nullish<AccessConfigFor1Panel>;
 | 
			
		||||
 | 
			
		||||
export type AccessForm1PanelConfigProps = {
 | 
			
		||||
  form: FormInstance;
 | 
			
		||||
  formName: string;
 | 
			
		||||
  disabled?: boolean;
 | 
			
		||||
  initialValues?: AccessForm1PanelConfigFieldValues;
 | 
			
		||||
  onValuesChange?: (values: AccessForm1PanelConfigFieldValues) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const initFormModel = (): AccessForm1PanelConfigFieldValues => {
 | 
			
		||||
  return {
 | 
			
		||||
    apiUrl: "http://<your-host-addr>:20410/",
 | 
			
		||||
    apiKey: "",
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AccessForm1PanelConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessForm1PanelConfigProps) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const formSchema = z.object({
 | 
			
		||||
    apiUrl: z.string().url(t("common.errmsg.url_invalid")),
 | 
			
		||||
    apiKey: z
 | 
			
		||||
      .string()
 | 
			
		||||
      .min(1, t("access.form.1panel_api_key.placeholder"))
 | 
			
		||||
      .max(64, t("common.errmsg.string_max", { max: 64 }))
 | 
			
		||||
      .trim(),
 | 
			
		||||
  });
 | 
			
		||||
  const formRule = createSchemaFieldRule(formSchema);
 | 
			
		||||
 | 
			
		||||
  const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
 | 
			
		||||
    onValuesChange?.(values);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Form
 | 
			
		||||
      form={formInst}
 | 
			
		||||
      disabled={disabled}
 | 
			
		||||
      initialValues={initialValues ?? initFormModel()}
 | 
			
		||||
      layout="vertical"
 | 
			
		||||
      name={formName}
 | 
			
		||||
      onValuesChange={handleFormChange}
 | 
			
		||||
    >
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="apiUrl"
 | 
			
		||||
        label={t("access.form.1panel_api_url.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
        tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.1panel_api_url.tooltip") }}></span>}
 | 
			
		||||
      >
 | 
			
		||||
        <Input placeholder={t("access.form.1panel_api_url.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="apiKey"
 | 
			
		||||
        label={t("access.form.1panel_api_key.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
        tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.1panel_api_key.tooltip") }}></span>}
 | 
			
		||||
      >
 | 
			
		||||
        <Input.Password autoComplete="new-password" placeholder={t("access.form.1panel_api_key.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
    </Form>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default AccessForm1PanelConfig;
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ export type AccessFormBaotaPanelConfigProps = {
 | 
			
		|||
 | 
			
		||||
const initFormModel = (): AccessFormBaotaPanelConfigFieldValues => {
 | 
			
		||||
  return {
 | 
			
		||||
    apiUrl: "http://<your-ipaddr>:8888/",
 | 
			
		||||
    apiUrl: "http://<your-host-addr>:8888/",
 | 
			
		||||
    apiKey: "",
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ export type AccessFormCdnflyConfigProps = {
 | 
			
		|||
 | 
			
		||||
const initFormModel = (): AccessFormCdnflyConfigFieldValues => {
 | 
			
		||||
  return {
 | 
			
		||||
    apiUrl: "http://<your-ipaddr>:88/",
 | 
			
		||||
    apiUrl: "http://<your-host-addr>:88/",
 | 
			
		||||
    apiKey: "",
 | 
			
		||||
    apiSecret: "",
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ export type AccessFormPowerDNSConfigProps = {
 | 
			
		|||
 | 
			
		||||
const initFormModel = (): AccessFormPowerDNSConfigFieldValues => {
 | 
			
		||||
  return {
 | 
			
		||||
    apiUrl: "http://<your-ipaddr>:8082/",
 | 
			
		||||
    apiUrl: "http://<your-host-addr>:8082/",
 | 
			
		||||
    apiKey: "",
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ export type AccessFormSafeLineConfigProps = {
 | 
			
		|||
 | 
			
		||||
const initFormModel = (): AccessFormSafeLineConfigFieldValues => {
 | 
			
		||||
  return {
 | 
			
		||||
    apiUrl: "http://<your-ipaddr>:9443/",
 | 
			
		||||
    apiUrl: "http://<your-host-addr>:9443/",
 | 
			
		||||
    apiToken: "",
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,8 @@ import { type WorkflowNode, type WorkflowNodeConfigForDeploy } from "@/domain/wo
 | 
			
		|||
import { useAntdForm, useAntdFormName, useZustandShallowSelector } from "@/hooks";
 | 
			
		||||
import { useWorkflowStore } from "@/stores/workflow";
 | 
			
		||||
 | 
			
		||||
import DeployNodeConfigForm1PanelConsoleConfig from "./DeployNodeConfigForm1PanelConsoleConfig";
 | 
			
		||||
import DeployNodeConfigForm1PanelSiteConfig from "./DeployNodeConfigForm1PanelSiteConfig";
 | 
			
		||||
import DeployNodeConfigFormAliyunALBConfig from "./DeployNodeConfigFormAliyunALBConfig";
 | 
			
		||||
import DeployNodeConfigFormAliyunCASDeployConfig from "./DeployNodeConfigFormAliyunCASDeployConfig";
 | 
			
		||||
import DeployNodeConfigFormAliyunCDNConfig from "./DeployNodeConfigFormAliyunCDNConfig";
 | 
			
		||||
| 
						 | 
				
			
			@ -138,6 +140,10 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
 | 
			
		|||
        NOTICE: If you add new child component, please keep ASCII order.
 | 
			
		||||
       */
 | 
			
		||||
      switch (fieldProvider) {
 | 
			
		||||
        case DEPLOY_PROVIDERS["1PANEL_CONSOLE"]:
 | 
			
		||||
          return <DeployNodeConfigForm1PanelConsoleConfig {...nestedFormProps} />;
 | 
			
		||||
        case DEPLOY_PROVIDERS["1PANEL_SITE"]:
 | 
			
		||||
          return <DeployNodeConfigForm1PanelSiteConfig {...nestedFormProps} />;
 | 
			
		||||
        case DEPLOY_PROVIDERS.ALIYUN_ALB:
 | 
			
		||||
          return <DeployNodeConfigFormAliyunALBConfig {...nestedFormProps} />;
 | 
			
		||||
        case DEPLOY_PROVIDERS.ALIYUN_CAS_DEPLOY:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { Form, type FormInstance, Switch } from "antd";
 | 
			
		||||
import { createSchemaFieldRule } from "antd-zod";
 | 
			
		||||
import { z } from "zod";
 | 
			
		||||
 | 
			
		||||
type DeployNodeConfigForm1PanelConsoleConfigFieldValues = Nullish<{
 | 
			
		||||
  autoRestart?: boolean;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
export type DeployNodeConfigForm1PanelConsoleConfigProps = {
 | 
			
		||||
  form: FormInstance;
 | 
			
		||||
  formName: string;
 | 
			
		||||
  disabled?: boolean;
 | 
			
		||||
  initialValues?: DeployNodeConfigForm1PanelConsoleConfigFieldValues;
 | 
			
		||||
  onValuesChange?: (values: DeployNodeConfigForm1PanelConsoleConfigFieldValues) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const initFormModel = (): DeployNodeConfigForm1PanelConsoleConfigFieldValues => {
 | 
			
		||||
  return {};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DeployNodeConfigForm1PanelConsoleConfig = ({
 | 
			
		||||
  form: formInst,
 | 
			
		||||
  formName,
 | 
			
		||||
  disabled,
 | 
			
		||||
  initialValues,
 | 
			
		||||
  onValuesChange,
 | 
			
		||||
}: DeployNodeConfigForm1PanelConsoleConfigProps) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const formSchema = z.object({
 | 
			
		||||
    autoRestart: z.boolean().nullish(),
 | 
			
		||||
  });
 | 
			
		||||
  const formRule = createSchemaFieldRule(formSchema);
 | 
			
		||||
 | 
			
		||||
  const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
 | 
			
		||||
    onValuesChange?.(values);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Form
 | 
			
		||||
      form={formInst}
 | 
			
		||||
      disabled={disabled}
 | 
			
		||||
      initialValues={initialValues ?? initFormModel()}
 | 
			
		||||
      layout="vertical"
 | 
			
		||||
      name={formName}
 | 
			
		||||
      onValuesChange={handleFormChange}
 | 
			
		||||
    >
 | 
			
		||||
      <Form.Item name="autoRestart" label={t("workflow_node.deploy.form.1panel_console_auto_restart.label")} rules={[formRule]}>
 | 
			
		||||
        <Switch />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
    </Form>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default DeployNodeConfigForm1PanelConsoleConfig;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { Form, type FormInstance, Input } from "antd";
 | 
			
		||||
import { createSchemaFieldRule } from "antd-zod";
 | 
			
		||||
import { z } from "zod";
 | 
			
		||||
 | 
			
		||||
type DeployNodeConfigForm1PanelSiteConfigFieldValues = Nullish<{
 | 
			
		||||
  websiteId: string | number;
 | 
			
		||||
}>;
 | 
			
		||||
 | 
			
		||||
export type DeployNodeConfigForm1PanelSiteConfigProps = {
 | 
			
		||||
  form: FormInstance;
 | 
			
		||||
  formName: string;
 | 
			
		||||
  disabled?: boolean;
 | 
			
		||||
  initialValues?: DeployNodeConfigForm1PanelSiteConfigFieldValues;
 | 
			
		||||
  onValuesChange?: (values: DeployNodeConfigForm1PanelSiteConfigFieldValues) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const initFormModel = (): DeployNodeConfigForm1PanelSiteConfigFieldValues => {
 | 
			
		||||
  return {};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DeployNodeConfigForm1PanelSiteConfig = ({
 | 
			
		||||
  form: formInst,
 | 
			
		||||
  formName,
 | 
			
		||||
  disabled,
 | 
			
		||||
  initialValues,
 | 
			
		||||
  onValuesChange,
 | 
			
		||||
}: DeployNodeConfigForm1PanelSiteConfigProps) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const formSchema = z.object({
 | 
			
		||||
    websiteId: z.union([z.string(), z.number()]).refine((v) => {
 | 
			
		||||
      return /^\d+$/.test(v + "") && +v > 0;
 | 
			
		||||
    }, t("workflow_node.deploy.form.1panel_site_website_id.placeholder")),
 | 
			
		||||
  });
 | 
			
		||||
  const formRule = createSchemaFieldRule(formSchema);
 | 
			
		||||
 | 
			
		||||
  const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
 | 
			
		||||
    onValuesChange?.(values);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Form
 | 
			
		||||
      form={formInst}
 | 
			
		||||
      disabled={disabled}
 | 
			
		||||
      initialValues={initialValues ?? initFormModel()}
 | 
			
		||||
      layout="vertical"
 | 
			
		||||
      name={formName}
 | 
			
		||||
      onValuesChange={handleFormChange}
 | 
			
		||||
    >
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="websiteId"
 | 
			
		||||
        label={t("workflow_node.deploy.form.1panel_site_website_id.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
        tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.1panel_site_website_id.tooltip") }}></span>}
 | 
			
		||||
      >
 | 
			
		||||
        <Input type="number" placeholder={t("workflow_node.deploy.form.1panel_site_website_id.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
    </Form>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default DeployNodeConfigForm1PanelSiteConfig;
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ export interface AccessModel extends BaseModel {
 | 
			
		|||
    NOTICE: If you add new type, please keep ASCII order.
 | 
			
		||||
  */ Record<string, unknown> &
 | 
			
		||||
    (
 | 
			
		||||
      | AccessConfigFor1Panel
 | 
			
		||||
      | AccessConfigForACMEHttpReq
 | 
			
		||||
      | AccessConfigForAliyun
 | 
			
		||||
      | AccessConfigForAWS
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +47,11 @@ export interface AccessModel extends BaseModel {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// #region AccessConfig
 | 
			
		||||
export type AccessConfigFor1Panel = {
 | 
			
		||||
  apiUrl: string;
 | 
			
		||||
  apiKey: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AccessConfigForACMEHttpReq = {
 | 
			
		||||
  endpoint: string;
 | 
			
		||||
  mode?: string;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@
 | 
			
		|||
  NOTICE: If you add new constant, please keep ASCII order.
 | 
			
		||||
 */
 | 
			
		||||
export const ACCESS_PROVIDERS = Object.freeze({
 | 
			
		||||
  ["1PANEL"]: "1panel",
 | 
			
		||||
  ACMEHTTPREQ: "acmehttpreq",
 | 
			
		||||
  ALIYUN: "aliyun",
 | 
			
		||||
  AWS: "aws",
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +85,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
 | 
			
		|||
    [ACCESS_PROVIDERS.BYTEPLUS, "provider.byteplus", "/imgs/providers/byteplus.svg", [ACCESS_USAGES.DEPLOY]],
 | 
			
		||||
    [ACCESS_PROVIDERS.UCLOUD, "provider.ucloud", "/imgs/providers/ucloud.svg", [ACCESS_USAGES.DEPLOY]],
 | 
			
		||||
    [ACCESS_PROVIDERS.SAFELINE, "provider.safeline", "/imgs/providers/safeline.svg", [ACCESS_USAGES.DEPLOY]],
 | 
			
		||||
    [ACCESS_PROVIDERS["1PANEL"], "provider.1panel", "/imgs/providers/1panel.svg", [ACCESS_USAGES.DEPLOY]],
 | 
			
		||||
    [ACCESS_PROVIDERS.BAOTAPANEL, "provider.baotapanel", "/imgs/providers/baotapanel.svg", [ACCESS_USAGES.DEPLOY]],
 | 
			
		||||
    [ACCESS_PROVIDERS.CACHEFLY, "provider.cachefly", "/imgs/providers/cachefly.png", [ACCESS_USAGES.DEPLOY]],
 | 
			
		||||
    [ACCESS_PROVIDERS.CDNFLY, "provider.cdnfly", "/imgs/providers/cdnfly.png", [ACCESS_USAGES.DEPLOY]],
 | 
			
		||||
| 
						 | 
				
			
			@ -211,6 +213,8 @@ export const applyDNSProvidersMap: Map<ApplyDNSProvider["type"] | string, ApplyD
 | 
			
		|||
  NOTICE: If you add new constant, please keep ASCII order.
 | 
			
		||||
 */
 | 
			
		||||
export const DEPLOY_PROVIDERS = Object.freeze({
 | 
			
		||||
  ["1PANEL_CONSOLE"]: `${ACCESS_PROVIDERS["1PANEL"]}-console`,
 | 
			
		||||
  ["1PANEL_SITE"]: `${ACCESS_PROVIDERS["1PANEL"]}-site`,
 | 
			
		||||
  ALIYUN_ALB: `${ACCESS_PROVIDERS.ALIYUN}-alb`,
 | 
			
		||||
  ALIYUN_CAS_DEPLOY: `${ACCESS_PROVIDERS.ALIYUN}-casdeploy`,
 | 
			
		||||
  ALIYUN_CDN: `${ACCESS_PROVIDERS.ALIYUN}-cdn`,
 | 
			
		||||
| 
						 | 
				
			
			@ -345,6 +349,8 @@ export const deployProvidersMap: Map<DeployProvider["type"] | string, DeployProv
 | 
			
		|||
    [DEPLOY_PROVIDERS.CDNFLY, "provider.cdnfly", DEPLOY_CATEGORIES.CDN],
 | 
			
		||||
    [DEPLOY_PROVIDERS.EDGIO_APPLICATIONS, "provider.edgio.applications", DEPLOY_CATEGORIES.WEBSITE],
 | 
			
		||||
    [DEPLOY_PROVIDERS.GCORE_CDN, "provider.gcore.cdn", DEPLOY_CATEGORIES.CDN],
 | 
			
		||||
    [DEPLOY_PROVIDERS["1PANEL_SITE"], "provider.1panel.site", DEPLOY_CATEGORIES.WEBSITE],
 | 
			
		||||
    [DEPLOY_PROVIDERS["1PANEL_CONSOLE"], "provider.1panel.console", DEPLOY_CATEGORIES.OTHER],
 | 
			
		||||
    [DEPLOY_PROVIDERS.BAOTAPANEL_SITE, "provider.baotapanel.site", DEPLOY_CATEGORIES.WEBSITE],
 | 
			
		||||
    [DEPLOY_PROVIDERS.BAOTAPANEL_CONSOLE, "provider.baotapanel.console", DEPLOY_CATEGORIES.OTHER],
 | 
			
		||||
    [DEPLOY_PROVIDERS.SAFELINE, "provider.safeline", DEPLOY_CATEGORIES.FIREWALL],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,12 @@
 | 
			
		|||
  "access.form.provider.label": "Provider",
 | 
			
		||||
  "access.form.provider.placeholder": "Please select a provider",
 | 
			
		||||
  "access.form.provider.tooltip": "DNS provider: The provider that hosts your domain names and manages your DNS records.<br>Host provider: The provider that hosts your servers or cloud services for deploying certificates.<br><br><i>Cannot be edited after saving.</i>",
 | 
			
		||||
  "access.form.1panel_api_url.label": "1Panel URL",
 | 
			
		||||
  "access.form.1panel_api_url.placeholder": "Please enter 1Panel URL",
 | 
			
		||||
  "access.form.1panel_api_url.tooltip": "For more information, see <a href=\"https://docs.1panel.pro/dev_manual/api_manual/\" target=\"_blank\">https://docs.1panel.pro/dev_manual/api_manual/</a>",
 | 
			
		||||
  "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 <a href=\"https://docs.1panel.pro/dev_manual/api_manual/\" target=\"_blank\">https://docs.1panel.pro/dev_manual/api_manual/</a>",
 | 
			
		||||
  "access.form.acmehttpreq_endpoint.label": "Endpoint",
 | 
			
		||||
  "access.form.acmehttpreq_endpoint.placeholder": "Please enter endpoint",
 | 
			
		||||
  "access.form.acmehttpreq_endpoint.tooltip": "For more information, see <a href=\"https://go-acme.github.io/lego/dns/httpreq/\" target=\"_blank\">https://go-acme.github.io/lego/dns/httpreq/</a>",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
  "provider.1panel": "1Panel",
 | 
			
		||||
  "provider.1panel.console": "1Panel - Console",
 | 
			
		||||
  "provider.1panel.site": "1Panel - Website",
 | 
			
		||||
  "provider.acmehttpreq": "Http Request (ACME Proxy)",
 | 
			
		||||
  "provider.aliyun": "Alibaba Cloud",
 | 
			
		||||
  "provider.aliyun.alb": "Alibaba Cloud - ALB (Application Load Balancer)",
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +30,7 @@
 | 
			
		|||
  "provider.baishan.cdn": "Baishan - CDN (Content Delivery Network)",
 | 
			
		||||
  "provider.baotapanel": "aaPanel (aka BaoTaPanel)",
 | 
			
		||||
  "provider.baotapanel.console": "aaPanel (aka BaoTaPanel) - Console",
 | 
			
		||||
  "provider.baotapanel.site": "aaPanel (aka BaoTaPanel) - Site",
 | 
			
		||||
  "provider.baotapanel.site": "aaPanel (aka BaoTaPanel) - Website",
 | 
			
		||||
  "provider.byteplus": "BytePlus",
 | 
			
		||||
  "provider.byteplus.cdn": "BytePlus - CDN (Content Delivery Network)",
 | 
			
		||||
  "provider.cachefly": "CacheFly",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -90,6 +90,10 @@
 | 
			
		|||
  "workflow_node.deploy.form.certificate.placeholder": "Please select certificate",
 | 
			
		||||
  "workflow_node.deploy.form.certificate.tooltip": "The certificate to be deployed comes from the previous application stage node.",
 | 
			
		||||
  "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_site_website_id.label": "1Panel website ID",
 | 
			
		||||
  "workflow_node.deploy.form.1panel_site_website_id.placeholder": "Please enter 1Panel website ID",
 | 
			
		||||
  "workflow_node.deploy.form.1panel_site_website_id.tooltip": "You can find it on 1Panel WebUI.",
 | 
			
		||||
  "workflow_node.deploy.form.aliyun_alb_resource_type.label": "Resource type",
 | 
			
		||||
  "workflow_node.deploy.form.aliyun_alb_resource_type.placeholder": "Please select resource type",
 | 
			
		||||
  "workflow_node.deploy.form.aliyun_alb_resource_type.option.loadbalancer.label": "ALB load balancer",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,12 @@
 | 
			
		|||
  "access.form.provider.label": "提供商",
 | 
			
		||||
  "access.form.provider.placeholder": "请选择提供商",
 | 
			
		||||
  "access.form.provider.tooltip": "提供商分为两种类型:<br>【DNS 提供商】你的 DNS 托管方,通常等同于域名注册商,用于在申请证书时管理您的域名解析记录。<br>【主机提供商】你的服务器或云服务的托管方,用于部署签发的证书。<br><br>该字段保存后不可修改。",
 | 
			
		||||
  "access.form.1panel_api_url.label": "1Panel URL",
 | 
			
		||||
  "access.form.1panel_api_url.placeholder": "请输入 1Panel URL",
 | 
			
		||||
  "access.form.1panel_api_url.tooltip": "这是什么?请参阅 <a href=\"https://1panel.cn/docs/dev_manual/api_manual/\" target=\"_blank\">https://1panel.cn/docs/dev_manual/api_manual/</a>",
 | 
			
		||||
  "access.form.1panel_api_key.label": "1Panel 接口密钥",
 | 
			
		||||
  "access.form.1panel_api_key.placeholder": "请输入 1Panel 接口密钥",
 | 
			
		||||
  "access.form.1panel_api_key.tooltip": "这是什么?请参阅 <a href=\"https://1panel.cn/docs/dev_manual/api_manual/\" target=\"_blank\">https://1panel.cn/docs/dev_manual/api_manual/</a>",
 | 
			
		||||
  "access.form.acmehttpreq_endpoint.label": "服务端点",
 | 
			
		||||
  "access.form.acmehttpreq_endpoint.placeholder": "请输入服务端点",
 | 
			
		||||
  "access.form.acmehttpreq_endpoint.tooltip": "这是什么?请参阅 <a href=\"https://go-acme.github.io/lego/dns/httpreq/\" target=\"_blank\">https://go-acme.github.io/lego/dns/httpreq/</a>",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
  "provider.1panel": "1Panel",
 | 
			
		||||
  "provider.1panel.console": "1Panel - 面板",
 | 
			
		||||
  "provider.1panel.site": "1Panel - 网站",
 | 
			
		||||
  "provider.acmehttpreq": "Http Request (ACME Proxy)",
 | 
			
		||||
  "provider.aliyun": "阿里云",
 | 
			
		||||
  "provider.aliyun.alb": "阿里云 - 应用型负载均衡 ALB",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -90,6 +90,10 @@
 | 
			
		|||
  "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_site_website_id.label": "1Panel 网站 ID",
 | 
			
		||||
  "workflow_node.deploy.form.1panel_site_website_id.placeholder": "请输入 1Panel 网站 ID",
 | 
			
		||||
  "workflow_node.deploy.form.1panel_site_website_id.tooltip": "请在 1Panel 管理面板查看。",
 | 
			
		||||
  "workflow_node.deploy.form.aliyun_alb_resource_type.label": "证书替换方式",
 | 
			
		||||
  "workflow_node.deploy.form.aliyun_alb_resource_type.placeholder": "请选择证书替换方式",
 | 
			
		||||
  "workflow_node.deploy.form.aliyun_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/QUIC 监听的证书",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue