diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index 4c45b724..056ce160 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -18,6 +18,7 @@ import ( providerAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-waf" providerAWSCloudFront "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-cloudfront" providerBaiduCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baiducloud-cdn" + providerBaotaPanelSite "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-site" providerBytePlusCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/byteplus-cdn" providerDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn" providerEdgioApplications "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/edgio-applications" @@ -210,6 +211,21 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, logger.Logger, } } + case domain.DeployProviderTypeBaotaPanelSite: + { + access := domain.AccessConfigForBaotaPanel{} + if err := maps.Decode(options.ProviderAccessConfig, &access); err != nil { + return nil, nil, fmt.Errorf("failed to decode provider access config: %w", err) + } + + deployer, err := providerBaotaPanelSite.NewWithLogger(&providerBaotaPanelSite.BaotaPanelSiteDeployerConfig{ + ApiUrl: access.ApiUrl, + ApiKey: access.ApiKey, + SiteName: maps.GetValueAsString(options.ProviderDeployConfig, "siteName"), + }, logger) + return deployer, logger, err + } + case domain.DeployProviderTypeBytePlusCDN: { access := domain.AccessConfigForBytePlus{} diff --git a/internal/domain/access.go b/internal/domain/access.go index c8448c21..e6d94764 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -54,6 +54,11 @@ type AccessConfigForBaiduCloud struct { SecretAccessKey string `json:"secretAccessKey"` } +type AccessConfigForBaotaPanel struct { + ApiUrl string `json:"apiUrl"` + ApiKey string `json:"apiKey"` +} + type AccessConfigForBytePlus struct { AccessKey string `json:"accessKey"` SecretKey string `json:"secretKey"` diff --git a/internal/domain/provider.go b/internal/domain/provider.go index cfbe2d70..49bf2cee 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -14,6 +14,7 @@ const ( AccessProviderTypeAWS = AccessProviderType("aws") AccessProviderTypeAzure = AccessProviderType("azure") AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud") + AccessProviderTypeBaotaPanel = AccessProviderType("baotapanel") AccessProviderTypeBytePlus = AccessProviderType("byteplus") AccessProviderTypeCloudflare = AccessProviderType("cloudflare") AccessProviderTypeClouDNS = AccessProviderType("cloudns") @@ -94,6 +95,7 @@ const ( DeployProviderTypeAliyunWAF = DeployProviderType("aliyun-waf") DeployProviderTypeAWSCloudFront = DeployProviderType("aws-cloudfront") DeployProviderTypeBaiduCloudCDN = DeployProviderType("baiducloud-cdn") + DeployProviderTypeBaotaPanelSite = DeployProviderType("baotapanel-site") DeployProviderTypeBytePlusCDN = DeployProviderType("byteplus-cdn") DeployProviderTypeDogeCloudCDN = DeployProviderType("dogecloud-cdn") DeployProviderTypeEdgioApplications = DeployProviderType("edgio-applications") diff --git a/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel-site.go b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel-site.go new file mode 100644 index 00000000..336e2a5d --- /dev/null +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel-site.go @@ -0,0 +1,81 @@ +package baotapanelsite + +import ( + "context" + "errors" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + btsdk "github.com/usual2970/certimate/internal/pkg/vendors/btpanel-sdk" +) + +type BaotaPanelSiteDeployerConfig struct { + // 宝塔面板地址。 + ApiUrl string `json:"apiUrl"` + // 宝塔面板接口密钥。 + ApiKey string `json:"apiKey"` + // 站点名称 + SiteName string `json:"siteName"` +} + +type BaotaPanelSiteDeployer struct { + config *BaotaPanelSiteDeployerConfig + logger logger.Logger + sdkClient *btsdk.BaoTaPanelClient +} + +var _ deployer.Deployer = (*BaotaPanelSiteDeployer)(nil) + +func New(config *BaotaPanelSiteDeployerConfig) (*BaotaPanelSiteDeployer, error) { + return NewWithLogger(config, logger.NewNilLogger()) +} + +func NewWithLogger(config *BaotaPanelSiteDeployerConfig, logger logger.Logger) (*BaotaPanelSiteDeployer, error) { + if config == nil { + return nil, errors.New("config is nil") + } + + if logger == nil { + return nil, errors.New("logger is nil") + } + + client, err := createSdkClient(config.ApiUrl, config.ApiKey) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &BaotaPanelSiteDeployer{ + logger: logger, + config: config, + sdkClient: client, + }, nil +} + +func (d *BaotaPanelSiteDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + if d.config.SiteName == "" { + return nil, errors.New("config `siteName` is required") + } + + // 设置站点 SSL 证书 + setSiteSSLReq := &btsdk.SetSiteSSLRequest{ + SiteName: d.config.SiteName, + Type: "1", + Key: privkeyPem, + Csr: certPem, + } + setSiteSSLResp, err := d.sdkClient.SetSiteSSL(setSiteSSLReq) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'bt.SetSiteSSL'") + } + + d.logger.Logt("已设置站点 SSL 证书", setSiteSSLResp) + + return &deployer.DeployResult{}, nil +} + +func createSdkClient(apiUrl, apiKey string) (*btsdk.BaoTaPanelClient, error) { + client := btsdk.NewBaoTaPanelClient(apiUrl, apiKey) + return client, nil +} 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 new file mode 100644 index 00000000..486e8428 --- /dev/null +++ b/internal/pkg/core/deployer/providers/baotapanel-site/baotapanel-site_test.go @@ -0,0 +1,75 @@ +package baotapanelsite_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/baotapanel-site" +) + +var ( + fInputCertPath string + fInputKeyPath string + fApiUrl string + fApiKey string + fSiteName string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_BAOTAPANELSITE_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fApiUrl, argsPrefix+"APIURL", "", "") + flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "") + flag.StringVar(&fSiteName, argsPrefix+"SITENAME", "", "") +} + +/* +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="your-baota-panel-url" \ + --CERTIMATE_DEPLOYER_BAOTAPANELSITE_APIKEY="your-baota-panel-key" \ + --CERTIMATE_DEPLOYER_BAOTAPANELSITE_SITENAME="your-baota-site-name" +*/ +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("SITENAME: %v", fSiteName), + }, "\n")) + + deployer, err := provider.New(&provider.BaotaPanelSiteDeployerConfig{ + ApiUrl: fApiUrl, + ApiKey: fApiKey, + SiteName: fSiteName, + }) + 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/vendors/btpanel-sdk/api.go b/internal/pkg/vendors/btpanel-sdk/api.go new file mode 100644 index 00000000..5c58e3df --- /dev/null +++ b/internal/pkg/vendors/btpanel-sdk/api.go @@ -0,0 +1,26 @@ +package btpanelsdk + +type BaseResponse interface { + GetStatus() *bool + GetMsg() *string +} + +type SetSiteSSLRequest struct { + Type string `json:"type"` + SiteName string `json:"siteName"` + Key string `json:"key"` + Csr string `json:"csr"` +} + +type SetSiteSSLResponse struct { + Status *bool `json:"status,omitempty"` + Msg *string `json:"msg,omitempty"` +} + +func (r *SetSiteSSLResponse) GetStatus() *bool { + return r.Status +} + +func (r *SetSiteSSLResponse) GetMsg() *string { + return r.Msg +} diff --git a/internal/pkg/vendors/btpanel-sdk/client.go b/internal/pkg/vendors/btpanel-sdk/client.go new file mode 100644 index 00000000..43e3d415 --- /dev/null +++ b/internal/pkg/vendors/btpanel-sdk/client.go @@ -0,0 +1,107 @@ +package btpanelsdk + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/go-resty/resty/v2" + + "github.com/usual2970/certimate/internal/pkg/utils/maps" +) + +type BaoTaPanelClient struct { + apiHost string + apiKey string + client *resty.Client +} + +func NewBaoTaPanelClient(apiHost, apiKey string) *BaoTaPanelClient { + client := resty.New() + + return &BaoTaPanelClient{ + apiHost: apiHost, + apiKey: apiKey, + client: client, + } +} + +func (c *BaoTaPanelClient) WithTimeout(timeout time.Duration) *BaoTaPanelClient { + c.client.SetTimeout(timeout) + return c +} + +func (c *BaoTaPanelClient) SetSiteSSL(req *SetSiteSSLRequest) (*SetSiteSSLResponse, error) { + params := make(map[string]any) + jsonData, _ := json.Marshal(req) + json.Unmarshal(jsonData, ¶ms) + + result := SetSiteSSLResponse{} + err := c.sendRequestWithResult("/site?action=SetSSL", params, &result) + if err != nil { + return nil, err + } + return &result, nil +} + +func (c *BaoTaPanelClient) generateSignature(timestamp string) string { + keyMd5 := md5.Sum([]byte(c.apiKey)) + keyMd5Hex := strings.ToLower(hex.EncodeToString(keyMd5[:])) + + signMd5 := md5.Sum([]byte(timestamp + keyMd5Hex)) + signMd5Hex := strings.ToLower(hex.EncodeToString(signMd5[:])) + return signMd5Hex +} + +func (c *BaoTaPanelClient) sendRequest(path string, params map[string]any) (*resty.Response, error) { + if params == nil { + params = make(map[string]any) + } + + timestamp := time.Now().Unix() + params["request_time"] = timestamp + params["request_token"] = c.generateSignature(fmt.Sprintf("%d", timestamp)) + + url := strings.TrimRight(c.apiHost, "/") + path + req := c.client.R(). + SetHeader("Content-Type", "application/json"). + SetBody(params) + resp, err := req.Post(url) + if err != nil { + return nil, fmt.Errorf("baota: failed to send request: %w", err) + } + + if resp.IsError() { + return nil, fmt.Errorf("baota: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + } + + return resp, nil +} + +func (c *BaoTaPanelClient) sendRequestWithResult(path string, params map[string]any, result BaseResponse) error { + resp, err := c.sendRequest(path, params) + if err != nil { + return err + } + + jsonResp := make(map[string]any) + if err := json.Unmarshal(resp.Body(), &jsonResp); err != nil { + return fmt.Errorf("baota: failed to parse response: %w", err) + } + if err := maps.Decode(jsonResp, &result); err != nil { + return fmt.Errorf("baota: failed to parse response: %w", err) + } + + if result.GetStatus() != nil && !*result.GetStatus() { + if result.GetMsg() == nil { + return fmt.Errorf("baota api error: unknown error") + } else { + return fmt.Errorf("baota api error: %s", result.GetMsg()) + } + } + + return nil +} diff --git a/internal/pkg/vendors/gname-sdk/client.go b/internal/pkg/vendors/gname-sdk/client.go index d034cfeb..6bf4e2db 100644 --- a/internal/pkg/vendors/gname-sdk/client.go +++ b/internal/pkg/vendors/gname-sdk/client.go @@ -130,11 +130,11 @@ func (c *GnameClient) sendRequest(path string, params map[string]any) (*resty.Re SetFormData(data) resp, err := req.Post(url) if err != nil { - return nil, fmt.Errorf("failed to send request: %w", err) + return nil, fmt.Errorf("gname: failed to send request: %w", err) } if resp.IsError() { - return nil, fmt.Errorf("unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) + return nil, fmt.Errorf("gname: unexpected status code: %d, %s", resp.StatusCode(), resp.Body()) } return resp, nil @@ -148,14 +148,14 @@ func (c *GnameClient) sendRequestWithResult(path string, params map[string]any, jsonResp := make(map[string]any) if err := json.Unmarshal(resp.Body(), &jsonResp); err != nil { - return fmt.Errorf("failed to parse response: %w", err) + return fmt.Errorf("gname: failed to parse response: %w", err) } if err := maps.Decode(jsonResp, &result); err != nil { - return fmt.Errorf("failed to parse response: %w", err) + return fmt.Errorf("gname: failed to parse response: %w", err) } if result.GetCode() != 1 { - return fmt.Errorf("API error: %s", result.GetMsg()) + return fmt.Errorf("gname api error: %s", result.GetMsg()) } return nil diff --git a/migrations/1739202463_updated_access.go b/migrations/1739202463_updated_access.go new file mode 100644 index 00000000..ccaffbce --- /dev/null +++ b/migrations/1739202463_updated_access.go @@ -0,0 +1,99 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // 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": [ + "acmehttpreq", + "aliyun", + "aws", + "azure", + "baiducloud", + "baotapanel", + "byteplus", + "cloudflare", + "dogecloud", + "godaddy", + "huaweicloud", + "k8s", + "local", + "namedotcom", + "namesilo", + "powerdns", + "qiniu", + "ssh", + "tencentcloud", + "ucloud", + "volcengine", + "webhook" + ] + }`)); err != nil { + return err + } + + return app.Save(collection) + }, func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e") + if err != nil { + return err + } + + // 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": [ + "acmehttpreq", + "aliyun", + "aws", + "azure", + "baiducloud", + "byteplus", + "cloudflare", + "dogecloud", + "godaddy", + "huaweicloud", + "k8s", + "local", + "namedotcom", + "namesilo", + "powerdns", + "qiniu", + "ssh", + "tencentcloud", + "ucloud", + "volcengine", + "webhook" + ] + }`)); err != nil { + return err + } + + return app.Save(collection) + }) +} diff --git a/ui/public/imgs/providers/baotapanel.svg b/ui/public/imgs/providers/baotapanel.svg new file mode 100644 index 00000000..34ab8ec8 --- /dev/null +++ b/ui/public/imgs/providers/baotapanel.svg @@ -0,0 +1 @@ + diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx index c6c44d2d..d9632f28 100644 --- a/ui/src/components/access/AccessForm.tsx +++ b/ui/src/components/access/AccessForm.tsx @@ -14,6 +14,7 @@ import AccessFormAliyunConfig from "./AccessFormAliyunConfig"; import AccessFormAWSConfig from "./AccessFormAWSConfig"; import AccessFormAzureConfig from "./AccessFormAzureConfig"; import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig"; +import AccessFormBaotaPanelConfig from "./AccessFormBaotaPanelConfig"; import AccessFormBytePlusConfig from "./AccessFormBytePlusConfig"; import AccessFormCloudflareConfig from "./AccessFormCloudflareConfig"; import AccessFormClouDNSConfig from "./AccessFormClouDNSConfig"; @@ -99,6 +100,8 @@ const AccessForm = forwardRef(({ className, return ; case ACCESS_PROVIDERS.BAIDUCLOUD: return ; + case ACCESS_PROVIDERS.BAOTAPANEL: + return ; case ACCESS_PROVIDERS.BYTEPLUS: return ; case ACCESS_PROVIDERS.CLOUDFLARE: diff --git a/ui/src/components/access/AccessFormBaotaPanelConfig.tsx b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx new file mode 100644 index 00000000..4f619c1c --- /dev/null +++ b/ui/src/components/access/AccessFormBaotaPanelConfig.tsx @@ -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 AccessConfigForBaotaPanel } from "@/domain/access"; + +type AccessFormBaotaPanelConfigFieldValues = Nullish; + +export type AccessFormBaotaPanelConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: AccessFormBaotaPanelConfigFieldValues; + onValuesChange?: (values: AccessFormBaotaPanelConfigFieldValues) => void; +}; + +const initFormModel = (): AccessFormBaotaPanelConfigFieldValues => { + return { + apiUrl: "", + apiKey: "", + }; +}; + +const AccessFormBaotaPanelConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormBaotaPanelConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + apiUrl: z.string().url(t("common.errmsg.url_invalid")), + apiKey: z + .string() + .min(1, t("access.form.baotapanel_api_key.placeholder")) + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + + + } + > + + +
+ ); +}; + +export default AccessFormBaotaPanelConfig; diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index fe03a6ab..92ce46cc 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -27,6 +27,7 @@ import DeployNodeConfigFormAliyunOSSConfig from "./DeployNodeConfigFormAliyunOSS import DeployNodeConfigFormAliyunWAFConfig from "./DeployNodeConfigFormAliyunWAFConfig"; import DeployNodeConfigFormAWSCloudFrontConfig from "./DeployNodeConfigFormAWSCloudFrontConfig"; import DeployNodeConfigFormBaiduCloudCDNConfig from "./DeployNodeConfigFormBaiduCloudCDNConfig"; +import DeployNodeConfigFormBaotaPanelSiteConfig from "./DeployNodeConfigFormBaotaPanelSiteConfig"; import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlusCDNConfig"; import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig"; import DeployNodeConfigFormEdgioApplicationsConfig from "./DeployNodeConfigFormEdgioApplicationsConfig"; @@ -148,6 +149,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOY_PROVIDERS.BAIDUCLOUD_CDN: return ; + case DEPLOY_PROVIDERS.BAOTAPANEL_SITE: + return ; case DEPLOY_PROVIDERS.BYTEPLUS_CDN: return ; case DEPLOY_PROVIDERS.DOGECLOUD_CDN: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx new file mode 100644 index 00000000..aa891245 --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormBaotaPanelSiteConfig.tsx @@ -0,0 +1,64 @@ +import { useTranslation } from "react-i18next"; +import { Form, type FormInstance, Input } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +type DeployNodeConfigFormBaotaPanelSiteConfigFieldValues = Nullish<{ + siteName: string; +}>; + +export type DeployNodeConfigFormBaotaPanelSiteConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormBaotaPanelSiteConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormBaotaPanelSiteConfigFieldValues) => void; +}; + +const initFormModel = (): DeployNodeConfigFormBaotaPanelSiteConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormBaotaPanelSiteConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormBaotaPanelSiteConfigProps) => { + const { t } = useTranslation(); + + const formSchema = z.object({ + siteName: z + .string({ message: t("workflow_node.deploy.form.baotapanel_site_name.placeholder") }) + .nonempty(t("workflow_node.deploy.form.baotapanel_site_name.placeholder")) + .trim(), + }); + const formRule = createSchemaFieldRule(formSchema); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ } + > + + +
+ ); +}; + +export default DeployNodeConfigFormBaotaPanelSiteConfig; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index 1e229751..ed7f8559 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -13,6 +13,7 @@ export interface AccessModel extends BaseModel { | AccessConfigForAWS | AccessConfigForAzure | AccessConfigForBaiduCloud + | AccessConfigForBaotaPanel | AccessConfigForBytePlus | AccessConfigForCloudflare | AccessConfigForClouDNS @@ -68,6 +69,11 @@ export type AccessConfigForBaiduCloud = { secretAccessKey: string; }; +export type AccessConfigForBaotaPanel = { + apiUrl: string; + apiKey: string; +}; + export type AccessConfigForBytePlus = { accessKey: string; secretKey: string; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 2edf0531..15562238 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -9,6 +9,7 @@ export const ACCESS_PROVIDERS = Object.freeze({ AWS: "aws", AZURE: "azure", BAIDUCLOUD: "baiducloud", + BAOTAPANEL: "baotapanel", BYTEPLUS: "byteplus", CLOUDFLARE: "cloudflare", CLOUDNS: "cloudns", @@ -70,6 +71,7 @@ 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 e2c94fc7..25f8765d 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -63,6 +63,12 @@ "access.form.baiducloud_secret_access_key.label": "Baidu Cloud SecretAccessKey", "access.form.baiducloud_secret_access_key.placeholder": "Please enter Baidu Cloud SecretAccessKey", "access.form.baiducloud_secret_access_key.tooltip": "For more information, see https://intl.cloud.baidu.com/doc/Reference/s/jjwvz2e3p-en", + "access.form.baotapanel_api_url.label": "BaoTa Panel URL", + "access.form.baotapanel_api_url.placeholder": "Please enter BaoTa Panel URL", + "access.form.baotapanel_api_url.tooltip": "For more information, see https://www.bt.cn/bbs/thread-20376-1-1.html", + "access.form.baotapanel_api_key.label": "BaoTa Panel API key", + "access.form.baotapanel_api_key.placeholder": "Please enter BaoTa Panel API key", + "access.form.baotapanel_api_key.tooltip": "For more information, see https://www.bt.cn/bbs/thread-113890-1-1.html", "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", diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index c8408cd0..5b305561 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -55,6 +55,8 @@ "common.provider.azure.dns": "Azure - DNS", "common.provider.baiducloud": "Baidu Cloud", "common.provider.baiducloud.cdn": "Baidu Cloud - CDN (Content Delivery Network)", + "common.provider.baotapanel": "BaoTa Panel", + "common.provider.baotapanel.site": "BaoTa Panel - Site", "common.provider.byteplus": "BytePlus", "common.provider.byteplus.cdn": "BytePlus - CDN (Content Delivery Network)", "common.provider.cloudflare": "Cloudflare", diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json index e5894fe5..494269a5 100644 --- a/ui/src/i18n/locales/en/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json @@ -190,6 +190,9 @@ "workflow_node.deploy.form.baiducloud_cdn_domain.label": "Baidu Cloud CDN domain", "workflow_node.deploy.form.baiducloud_cdn_domain.placeholder": "Please enter Baidu Cloud CDN domain name", "workflow_node.deploy.form.baiducloud_cdn_domain.tooltip": "For more information, see https://console.bce.baidu.com/cdn", + "workflow_node.deploy.form.baotapanel_site_name.label": "BaoTa Panel site name", + "workflow_node.deploy.form.baotapanel_site_name.placeholder": "Please enter BaoTa Panel site name", + "workflow_node.deploy.form.baotapanel_site_name.tooltip": "Usually equal to the website domain name.", "workflow_node.deploy.form.byteplus_cdn_domain.label": "BytePlus CDN domain", "workflow_node.deploy.form.byteplus_cdn_domain.placeholder": "Please enter BytePlus CDN domain name", "workflow_node.deploy.form.byteplus_cdn_domain.tooltip": "For more information, see https://console.byteplus.com/cdn", diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json index 73fe7105..3581a527 100644 --- a/ui/src/i18n/locales/zh/nls.access.json +++ b/ui/src/i18n/locales/zh/nls.access.json @@ -63,6 +63,12 @@ "access.form.baiducloud_secret_access_key.label": "百度智能云 SecretAccessKey", "access.form.baiducloud_secret_access_key.placeholder": "请输入百度智能云 SecretAccessKey", "access.form.baiducloud_secret_access_key.tooltip": "这是什么?请参阅 https://cloud.baidu.com/doc/Reference/s/jjwvz2e3p", + "access.form.baotapanel_api_url.label": "宝塔面板 URL", + "access.form.baotapanel_api_url.placeholder": "请输入宝塔面板 URL", + "access.form.baotapanel_api_url.tooltip": "这是什么?请参阅 https://www.bt.cn/bbs/thread-20376-1-1.html", + "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.byteplus_access_key.label": "BytePlus AccessKey", "access.form.byteplus_access_key.placeholder": "请输入 BytePlus AccessKey", "access.form.byteplus_access_key.tooltip": "这是什么?请参阅 https://docs.byteplus.com/zh-CN/docs/byteplus-platform/docs-managing-keys", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 06ee5f73..cfc23730 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -55,6 +55,8 @@ "common.provider.azure.dns": "Azure - DNS", "common.provider.baiducloud": "百度智能云", "common.provider.baiducloud.cdn": "百度智能云 - 内容分发网络 CDN", + "common.provider.baotapanel": "宝塔面板", + "common.provider.baotapanel.site": "宝塔面板 - 网站", "common.provider.byteplus": "BytePlus", "common.provider.byteplus.cdn": "BytePlus - 内容分发网络 CDN", "common.provider.cloudflare": "Cloudflare", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index a4436d4e..2f79e949 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -190,6 +190,9 @@ "workflow_node.deploy.form.baiducloud_cdn_domain.label": "百度智能云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.baiducloud_cdn_domain.placeholder": "请输入百度智能云 CDN 加速域名", "workflow_node.deploy.form.baiducloud_cdn_domain.tooltip": "这是什么?请参阅 https://console.bce.baidu.com/cdn

泛域名表示形式为:*.example.com", + "workflow_node.deploy.form.baotapanel_site_name.label": "宝塔面板网站名称", + "workflow_node.deploy.form.baotapanel_site_name.placeholder": "请输入宝塔面板网站名称", + "workflow_node.deploy.form.baotapanel_site_name.tooltip": "通常为网站域名。", "workflow_node.deploy.form.byteplus_cdn_domain.label": "BytePlus CDN 域名(支持泛域名)", "workflow_node.deploy.form.byteplus_cdn_domain.placeholder": "请输入 BytePlus CDN 域名", "workflow_node.deploy.form.byteplus_cdn_domain.tooltip": "这是什么?请参阅 https://console.byteplus.com/cdn

泛域名表示形式为:*.example.com",