diff --git a/go.mod b/go.mod index 7c710d35..7b565e12 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/alibabacloud-go/slb-20140515/v4 v4.0.9 github.com/alibabacloud-go/tea v1.2.2 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible + github.com/baidubce/bce-sdk-go v0.9.197 github.com/go-acme/lego/v4 v4.19.2 github.com/gojek/heimdall/v7 v7.0.3 github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.114 diff --git a/go.sum b/go.sum index d93ba3da..676b48cf 100644 --- a/go.sum +++ b/go.sum @@ -159,6 +159,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 h1:NKTa1eqZYw8tiHSRGpP0VtTdub/8 github.com/aws/aws-sdk-go-v2/service/sts v1.30.7/go.mod h1:NXi1dIAGteSaRLqYgarlhP/Ij0cFT+qmCwiJqWh/U5o= github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4= github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/baidubce/bce-sdk-go v0.9.197 h1:TQqa4J+FTagrywhaTQ707ffE1eG3ix1s06eSZ/K+Wk0= +github.com/baidubce/bce-sdk-go v0.9.197/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/blinkbean/dingtalk v1.1.3 h1:MbidFZYom7DTFHD/YIs+eaI7kRy52kmWE/sy0xjo6E4= github.com/blinkbean/dingtalk v1.1.3/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto= github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= diff --git a/internal/deployer/baiducloud_cdn.go b/internal/deployer/baiducloud_cdn.go new file mode 100644 index 00000000..31d789df --- /dev/null +++ b/internal/deployer/baiducloud_cdn.go @@ -0,0 +1,80 @@ +package deployer + +import ( + "context" + "encoding/json" + "fmt" + "time" + + bceCdn "github.com/baidubce/bce-sdk-go/services/cdn" + bceCdnApi "github.com/baidubce/bce-sdk-go/services/cdn/api" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/domain" +) + +type BaiduCloudCDNDeployer struct { + option *DeployerOption + infos []string + + sdkClient *bceCdn.Client +} + +func NewBaiduCloudCDNDeployer(option *DeployerOption) (Deployer, error) { + access := &domain.BaiduCloudAccess{} + if err := json.Unmarshal([]byte(option.Access), access); err != nil { + return nil, xerrors.Wrap(err, "failed to get access") + } + + client, err := (&BaiduCloudCDNDeployer{}).createSdkClient( + access.AccessKeyId, + access.SecretAccessKey, + ) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &BaiduCloudCDNDeployer{ + option: option, + infos: make([]string, 0), + sdkClient: client, + }, nil +} + +func (d *BaiduCloudCDNDeployer) GetID() string { + return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) +} + +func (d *BaiduCloudCDNDeployer) GetInfos() []string { + return d.infos +} + +func (d *BaiduCloudCDNDeployer) Deploy(ctx context.Context) error { + // 修改域名证书 + // REF: https://cloud.baidu.com/doc/CDN/s/qjzuz2hp8 + putCertResp, err := d.sdkClient.PutCert( + d.option.DeployConfig.GetConfigAsString("domain"), + &bceCdnApi.UserCertificate{ + CertName: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), + ServerData: d.option.Certificate.Certificate, + PrivateData: d.option.Certificate.PrivateKey, + }, + "ON", + ) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'cdn.PutCert'") + } + + d.infos = append(d.infos, toStr("已修改域名证书", putCertResp)) + + return nil +} + +func (d *BaiduCloudCDNDeployer) createSdkClient(accessKeyId, secretAccessKey string) (*bceCdn.Client, error) { + client, err := bceCdn.NewClient(accessKeyId, secretAccessKey, "") + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index 38ee209b..c8456ae6 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -33,7 +33,9 @@ const ( targetTencentTEO = "tencent-teo" targetHuaweiCloudCDN = "huaweicloud-cdn" targetHuaweiCloudELB = "huaweicloud-elb" + targetBaiduCloudCDN = "baiducloud-cdn" targetQiniuCdn = "qiniu-cdn" + targetDogeCloudCdn = "dogecloud-cdn" targetLocal = "local" targetSSH = "ssh" targetWebhook = "webhook" @@ -134,8 +136,12 @@ func getWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep return NewHuaweiCloudCDNDeployer(option) case targetHuaweiCloudELB: return NewHuaweiCloudELBDeployer(option) + case targetBaiduCloudCDN: + return NewBaiduCloudCDNDeployer(option) case targetQiniuCdn: return NewQiniuCDNDeployer(option) + case targetDogeCloudCdn: + return NewDogeCloudCDNDeployer(option) case targetLocal: return NewLocalDeployer(option) case targetSSH: diff --git a/internal/deployer/dogecloud_cdn.go b/internal/deployer/dogecloud_cdn.go new file mode 100644 index 00000000..c68cb3ba --- /dev/null +++ b/internal/deployer/dogecloud_cdn.go @@ -0,0 +1,86 @@ +package deployer + +import ( + "context" + "encoding/json" + "fmt" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploaderDoge "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/dogecloud" + doge "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk" +) + +type DogeCloudCDNDeployer struct { + option *DeployerOption + infos []string + + sdkClient *doge.Client + sslUploader uploader.Uploader +} + +func NewDogeCloudCDNDeployer(option *DeployerOption) (Deployer, error) { + access := &domain.DogeCloudAccess{} + if err := json.Unmarshal([]byte(option.Access), access); err != nil { + return nil, xerrors.Wrap(err, "failed to get access") + } + + client, err := (&DogeCloudCDNDeployer{}).createSdkClient( + access.AccessKey, + access.SecretKey, + ) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + uploader, err := uploaderDoge.New(&uploaderDoge.DogeCloudUploaderConfig{ + AccessKey: access.AccessKey, + SecretKey: access.SecretKey, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DogeCloudCDNDeployer{ + option: option, + infos: make([]string, 0), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DogeCloudCDNDeployer) GetID() string { + return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) +} + +func (d *DogeCloudCDNDeployer) GetInfos() []string { + return d.infos +} + +func (d *DogeCloudCDNDeployer) Deploy(ctx context.Context) error { + // 上传证书到 CDN + upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey) + if err != nil { + return err + } + + d.infos = append(d.infos, toStr("已上传证书", upres)) + + // 绑定证书 + // REF: https://docs.dogecloud.com/cdn/api-cert-bind + bindCdnCertResp, err := d.sdkClient.BindCdnCertWithDomain(upres.CertId, d.option.DeployConfig.GetConfigAsString("domain")) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'cdn.BindCdnCert'") + } + + d.infos = append(d.infos, toStr("已绑定证书", bindCdnCertResp)) + + return nil +} + +func (d *DogeCloudCDNDeployer) createSdkClient(accessKey, secretKey string) (*doge.Client, error) { + client := doge.NewClient(accessKey, secretKey) + return client, nil +} diff --git a/internal/domain/access.go b/internal/domain/access.go index 889d37fa..9e351e99 100644 --- a/internal/domain/access.go +++ b/internal/domain/access.go @@ -11,7 +11,12 @@ type TencentAccess struct { } type HuaweiCloudAccess struct { + AccessKeyId string `json:"accessKeyId"` + SecretAccessKey string `json:"secretAccessKey"` Region string `json:"region"` +} + +type BaiduCloudAccess struct { AccessKeyId string `json:"accessKeyId"` SecretAccessKey string `json:"secretAccessKey"` } @@ -32,6 +37,11 @@ type QiniuAccess struct { SecretKey string `json:"secretKey"` } +type DogeCloudAccess struct { + AccessKey string `json:"accessKey"` + SecretKey string `json:"secretKey"` +} + type NameSiloAccess struct { ApiKey string `json:"apiKey"` } diff --git a/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go b/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go new file mode 100644 index 00000000..cc2475d5 --- /dev/null +++ b/internal/pkg/core/uploader/providers/dogecloud/dogecloud.go @@ -0,0 +1,61 @@ +package dogecloud + +import ( + "context" + "fmt" + "time" + + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/uploader" + doge "github.com/usual2970/certimate/internal/pkg/vendors/dogecloud-sdk" +) + +type DogeCloudUploaderConfig struct { + AccessKey string `json:"accessKey"` + SecretKey string `json:"secretKey"` +} + +type DogeCloudUploader struct { + config *DogeCloudUploaderConfig + sdkClient *doge.Client +} + +func New(config *DogeCloudUploaderConfig) (*DogeCloudUploader, error) { + client, err := createSdkClient( + config.AccessKey, + config.SecretKey, + ) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + return &DogeCloudUploader{ + config: config, + sdkClient: client, + }, nil +} + +func (u *DogeCloudUploader) Upload(ctx context.Context, certPem string, privkeyPem string) (res *uploader.UploadResult, err error) { + // 生成新证书名(需符合多吉云命名规则) + var certId, certName string + certName = fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) + + // 上传新证书 + // REF: https://docs.dogecloud.com/cdn/api-cert-upload + uploadSslCertResp, err := u.sdkClient.UploadCdnCert(certName, certName, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadCdnCert'") + } + + certId = uploadSslCertResp.Data.Id + return &uploader.UploadResult{ + CertId: certId, + CertName: certName, + }, nil +} + +func createSdkClient(accessKey, secretKey string) (*doge.Client, error) { + client := doge.NewClient(accessKey, secretKey) + return client, nil +} diff --git a/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go b/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go index 534881d1..010c55d1 100644 --- a/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go +++ b/internal/pkg/core/uploader/providers/qiniu-sslcert/qiniu_sslcert.go @@ -51,7 +51,7 @@ func (u *QiniuSSLCertUploader) Upload(ctx context.Context, certPem string, privk // 上传新证书 // REF: https://developer.qiniu.com/fusion/8593/interface-related-certificate - uploadSslCertResp, err := u.sdkClient.UploadSslCert(certName, certX509.Subject.CommonName, privkeyPem, certPem) + uploadSslCertResp, err := u.sdkClient.UploadSslCert(certName, certX509.Subject.CommonName, certPem, privkeyPem) if err != nil { return nil, xerrors.Wrap(err, "failed to execute sdk request 'cdn.UploadSslCert'") } diff --git a/internal/pkg/vendors/dogecloud-sdk/client.go b/internal/pkg/vendors/dogecloud-sdk/client.go new file mode 100644 index 00000000..86a2e80a --- /dev/null +++ b/internal/pkg/vendors/dogecloud-sdk/client.go @@ -0,0 +1,182 @@ +package dogecloudsdk + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +const dogeHost = "https://api.dogecloud.com" + +type Client struct { + accessKey string + secretKey string +} + +func NewClient(accessKey, secretKey string) *Client { + return &Client{accessKey: accessKey, secretKey: secretKey} +} + +func (c *Client) UploadCdnCert(note, cert, private string) (*UploadCdnCertResponse, error) { + req := &UploadCdnCertRequest{ + Note: note, + Certificate: cert, + PrivateKey: private, + } + + reqBts, err := json.Marshal(req) + if err != nil { + return nil, err + } + + reqMap := make(map[string]interface{}) + err = json.Unmarshal(reqBts, &reqMap) + if err != nil { + return nil, err + } + + respBts, err := c.sendReq(http.MethodPost, "cdn/cert/upload.json", reqMap, true) + if err != nil { + return nil, err + } + + resp := &UploadCdnCertResponse{} + err = json.Unmarshal(respBts, resp) + if err != nil { + return nil, err + } + if resp.Code != nil && *resp.Code != 0 && *resp.Code != 200 { + return nil, fmt.Errorf("dogecloud api error, code: %d, msg: %s", *resp.Code, *resp.Message) + } + + return resp, nil +} + +func (c *Client) BindCdnCertWithDomain(certId string, domain string) (*BindCdnCertResponse, error) { + req := &BindCdnCertRequest{ + CertId: certId, + Domain: &domain, + } + + reqBts, err := json.Marshal(req) + if err != nil { + return nil, err + } + + reqMap := make(map[string]interface{}) + err = json.Unmarshal(reqBts, &reqMap) + if err != nil { + return nil, err + } + + respBts, err := c.sendReq(http.MethodPost, "cdn/cert/bind.json", reqMap, true) + if err != nil { + return nil, err + } + + resp := &BindCdnCertResponse{} + err = json.Unmarshal(respBts, resp) + if err != nil { + return nil, err + } + if resp.Code != nil && *resp.Code != 0 && *resp.Code != 200 { + return nil, fmt.Errorf("dogecloud api error, code: %d, msg: %s", *resp.Code, *resp.Message) + } + + return resp, nil +} + +func (c *Client) BindCdnCertWithDomainId(certId string, domainId int32) (*BindCdnCertResponse, error) { + req := &BindCdnCertRequest{ + CertId: certId, + DomainId: &domainId, + } + + reqBts, err := json.Marshal(req) + if err != nil { + return nil, err + } + + reqMap := make(map[string]interface{}) + err = json.Unmarshal(reqBts, &reqMap) + if err != nil { + return nil, err + } + + respBts, err := c.sendReq(http.MethodPost, "cdn/cert/bind.json", reqMap, true) + if err != nil { + return nil, err + } + + resp := &BindCdnCertResponse{} + err = json.Unmarshal(respBts, resp) + if err != nil { + return nil, err + } + if resp.Code != nil && *resp.Code != 0 && *resp.Code != 200 { + return nil, fmt.Errorf("dogecloud api error, code: %d, msg: %s", *resp.Code, *resp.Message) + } + + return resp, nil +} + +// 调用多吉云的 API。 +// https://docs.dogecloud.com/cdn/api-access-token?id=go +// +// 入参: +// - method:GET 或 POST +// - path:是调用的 API 接口地址,包含 URL 请求参数 QueryString,例如:/console/vfetch/add.json?url=xxx&a=1&b=2 +// - data:POST 的数据,对象,例如 {a: 1, b: 2},传递此参数表示不是 GET 请求而是 POST 请求 +// - jsonMode:数据 data 是否以 JSON 格式请求,默认为 false 则使用表单形式(a=1&b=2) +func (c *Client) sendReq(method string, path string, data map[string]interface{}, jsonMode bool) ([]byte, error) { + body := "" + mime := "" + if jsonMode { + _body, err := json.Marshal(data) + if err != nil { + return nil, err + } + body = string(_body) + mime = "application/json" + } else { + values := url.Values{} + for k, v := range data { + values.Set(k, v.(string)) + } + body = values.Encode() + mime = "application/x-www-form-urlencoded" + } + + signStr := path + "\n" + body + hmacObj := hmac.New(sha1.New, []byte(c.secretKey)) + hmacObj.Write([]byte(signStr)) + sign := hex.EncodeToString(hmacObj.Sum(nil)) + auth := fmt.Sprintf("TOKEN %s:%s", c.accessKey, sign) + + req, err := http.NewRequest(method, fmt.Sprintf("%s/%s", dogeHost, path), strings.NewReader(body)) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", mime) + req.Header.Add("Authorization", auth) + + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + r, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return r, nil +} diff --git a/internal/pkg/vendors/dogecloud-sdk/models.go b/internal/pkg/vendors/dogecloud-sdk/models.go new file mode 100644 index 00000000..b721a00e --- /dev/null +++ b/internal/pkg/vendors/dogecloud-sdk/models.go @@ -0,0 +1,31 @@ +package dogecloudsdk + +type BaseResponse struct { + Code *int `json:"code,omitempty"` + Message *string `json:"msg,omitempty"` +} + +type UploadCdnCertRequest struct { + Note string `json:"note"` + Certificate string `json:"cert"` + PrivateKey string `json:"private"` +} + +type UploadCdnCertResponseData struct { + Id string `json:"id"` +} + +type UploadCdnCertResponse struct { + *BaseResponse + Data *UploadCdnCertResponseData `json:"data,omitempty"` +} + +type BindCdnCertRequest struct { + CertId string `json:"id"` + DomainId *int32 `json:"did,omitempty"` + Domain *string `json:"domain,omitempty"` +} + +type BindCdnCertResponse struct { + *BaseResponse +} diff --git a/internal/pkg/vendors/qiniu-sdk/client.go b/internal/pkg/vendors/qiniu-sdk/client.go index eceff741..76c07ba9 100644 --- a/internal/pkg/vendors/qiniu-sdk/client.go +++ b/internal/pkg/vendors/qiniu-sdk/client.go @@ -12,7 +12,7 @@ import ( xhttp "github.com/usual2970/certimate/internal/utils/http" ) -const qiniuHost = "http://api.qiniu.com" +const qiniuHost = "https://api.qiniu.com" type Client struct { mac *auth.Credentials @@ -105,12 +105,12 @@ func (c *Client) EnableDomainHttps(domain, certId string, forceHttps, http2Enabl return resp, nil } -func (c *Client) UploadSslCert(name, commonName, pri, ca string) (*UploadSslCertResponse, error) { +func (c *Client) UploadSslCert(name, commonName, certificate, privateKey string) (*UploadSslCertResponse, error) { req := &UploadSslCertRequest{ - Name: name, - CommonName: commonName, - Pri: pri, - Ca: ca, + Name: name, + CommonName: commonName, + Certificate: certificate, + PrivateKey: privateKey, } reqBytes, err := json.Marshal(req) @@ -129,7 +129,7 @@ func (c *Client) UploadSslCert(name, commonName, pri, ca string) (*UploadSslCert return nil, err } if resp.Code != nil && *resp.Code != 0 && *resp.Code != 200 { - return nil, fmt.Errorf("code: %d, error: %s", *resp.Code, *resp.Error) + return nil, fmt.Errorf("qiniu api error, code: %d, error: %s", *resp.Code, *resp.Error) } return resp, nil diff --git a/internal/pkg/vendors/qiniu-sdk/models.go b/internal/pkg/vendors/qiniu-sdk/models.go index 433909e1..b8652fd4 100644 --- a/internal/pkg/vendors/qiniu-sdk/models.go +++ b/internal/pkg/vendors/qiniu-sdk/models.go @@ -1,16 +1,20 @@ package qiniusdk +type BaseResponse struct { + Code *int `json:"code,omitempty"` + Error *string `json:"error,omitempty"` +} + type UploadSslCertRequest struct { - Name string `json:"name"` - CommonName string `json:"common_name"` - Pri string `json:"pri"` - Ca string `json:"ca"` + Name string `json:"name"` + CommonName string `json:"common_name"` + Certificate string `json:"ca"` + PrivateKey string `json:"pri"` } type UploadSslCertResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` - CertID string `json:"certID"` + *BaseResponse + CertID string `json:"certID"` } type DomainInfoHttpsData struct { @@ -20,8 +24,7 @@ type DomainInfoHttpsData struct { } type GetDomainInfoResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` + *BaseResponse Name string `json:"name"` Type string `json:"type"` CName string `json:"cname"` @@ -39,8 +42,7 @@ type ModifyDomainHttpsConfRequest struct { } type ModifyDomainHttpsConfResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` + *BaseResponse } type EnableDomainHttpsRequest struct { @@ -48,6 +50,5 @@ type EnableDomainHttpsRequest struct { } type EnableDomainHttpsResponse struct { - Code *int `json:"code,omitempty"` - Error *string `json:"error,omitempty"` + *BaseResponse } diff --git a/migrations/1730766480_collections_snapshot.go b/migrations/1730766480_collections_snapshot.go new file mode 100644 index 00000000..74be734c --- /dev/null +++ b/migrations/1730766480_collections_snapshot.go @@ -0,0 +1,807 @@ +package migrations + +import ( + "encoding/json" + + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/models" +) + +func init() { + m.Register(func(db dbx.Builder) error { + jsonData := `[ + { + "id": "z3p974ainxjqlvs", + "created": "2024-07-29 10:02:48.334Z", + "updated": "2024-10-23 09:25:43.083Z", + "name": "domains", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "iuaerpl2", + "name": "domain", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "ukkhuw85", + "name": "email", + "type": "email", + "required": false, + "presentable": false, + "unique": false, + "options": { + "exceptDomains": null, + "onlyDomains": null + } + }, + { + "system": false, + "id": "v98eebqq", + "name": "crontab", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "alc8e9ow", + "name": "access", + "type": "relation", + "required": false, + "presentable": false, + "unique": false, + "options": { + "collectionId": "4yzbv8urny5ja1e", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "topsc9bj", + "name": "certUrl", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "vixgq072", + "name": "certStableUrl", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "g3a3sza5", + "name": "privateKey", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "gr6iouny", + "name": "certificate", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "tk6vnrmn", + "name": "issuerCertificate", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "sjo6ibse", + "name": "csr", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "x03n1bkj", + "name": "expiredAt", + "type": "date", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": "", + "max": "" + } + }, + { + "system": false, + "id": "srybpixz", + "name": "targetType", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "aliyun-oss", + "aliyun-cdn", + "aliyun-dcdn", + "ssh", + "webhook", + "tencent-cdn", + "qiniu-cdn", + "local" + ] + } + }, + { + "system": false, + "id": "xy7yk0mb", + "name": "targetAccess", + "type": "relation", + "required": false, + "presentable": false, + "unique": false, + "options": { + "collectionId": "4yzbv8urny5ja1e", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "6jqeyggw", + "name": "enabled", + "type": "bool", + "required": false, + "presentable": false, + "unique": false, + "options": {} + }, + { + "system": false, + "id": "hdsjcchf", + "name": "deployed", + "type": "bool", + "required": false, + "presentable": false, + "unique": false, + "options": {} + }, + { + "system": false, + "id": "aiya3rev", + "name": "rightnow", + "type": "bool", + "required": false, + "presentable": false, + "unique": false, + "options": {} + }, + { + "system": false, + "id": "ixznmhzc", + "name": "lastDeployedAt", + "type": "date", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": "", + "max": "" + } + }, + { + "system": false, + "id": "ghtlkn5j", + "name": "lastDeployment", + "type": "relation", + "required": false, + "presentable": false, + "unique": false, + "options": { + "collectionId": "0a1o4e6sstp694f", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "zfnyj9he", + "name": "variables", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "1bspzuku", + "name": "group", + "type": "relation", + "required": false, + "presentable": false, + "unique": false, + "options": { + "collectionId": "teolp9pl72dxlxq", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "g65gfh7a", + "name": "nameservers", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "wwrzc3jo", + "name": "applyConfig", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 2000000 + } + }, + { + "system": false, + "id": "474iwy8r", + "name": "deployConfig", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 2000000 + } + } + ], + "indexes": [ + "CREATE UNIQUE INDEX ` + "`" + `idx_4ABO6EQ` + "`" + ` ON ` + "`" + `domains` + "`" + ` (` + "`" + `domain` + "`" + `)" + ], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "4yzbv8urny5ja1e", + "created": "2024-07-29 10:04:39.685Z", + "updated": "2024-11-05 00:21:32.129Z", + "name": "access", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "geeur58v", + "name": "name", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "iql7jpwx", + "name": "config", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 2000000 + } + }, + { + "system": false, + "id": "hwy7m03o", + "name": "configType", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "aliyun", + "tencent", + "huaweicloud", + "qiniu", + "aws", + "cloudflare", + "namesilo", + "godaddy", + "pdns", + "httpreq", + "local", + "ssh", + "webhook", + "k8s", + "baiducloud", + "dogecloud" + ] + } + }, + { + "system": false, + "id": "lr33hiwg", + "name": "deleted", + "type": "date", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": "", + "max": "" + } + }, + { + "system": false, + "id": "hsxcnlvd", + "name": "usage", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "apply", + "deploy", + "all" + ] + } + }, + { + "system": false, + "id": "c8egzzwj", + "name": "group", + "type": "relation", + "required": false, + "presentable": false, + "unique": false, + "options": { + "collectionId": "teolp9pl72dxlxq", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + } + ], + "indexes": [ + "CREATE UNIQUE INDEX ` + "`" + `idx_wkoST0j` + "`" + ` ON ` + "`" + `access` + "`" + ` (` + "`" + `name` + "`" + `)" + ], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "0a1o4e6sstp694f", + "created": "2024-07-30 06:30:27.801Z", + "updated": "2024-10-23 09:25:43.084Z", + "name": "deployments", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "farvlzk7", + "name": "domain", + "type": "relation", + "required": false, + "presentable": false, + "unique": false, + "options": { + "collectionId": "z3p974ainxjqlvs", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "jx5f69i3", + "name": "log", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 2000000 + } + }, + { + "system": false, + "id": "qbxdtg9q", + "name": "phase", + "type": "select", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "check", + "apply", + "deploy" + ] + } + }, + { + "system": false, + "id": "rglrp1hz", + "name": "phaseSuccess", + "type": "bool", + "required": false, + "presentable": false, + "unique": false, + "options": {} + }, + { + "system": false, + "id": "lt1g1blu", + "name": "deployedAt", + "type": "date", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": "", + "max": "" + } + }, + { + "system": false, + "id": "wledpzgb", + "name": "wholeSuccess", + "type": "bool", + "required": false, + "presentable": false, + "unique": false, + "options": {} + } + ], + "indexes": [], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "_pb_users_auth_", + "created": "2024-09-12 13:09:54.234Z", + "updated": "2024-10-23 09:25:43.085Z", + "name": "users", + "type": "auth", + "system": false, + "schema": [ + { + "system": false, + "id": "users_name", + "name": "name", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "users_avatar", + "name": "avatar", + "type": "file", + "required": false, + "presentable": false, + "unique": false, + "options": { + "mimeTypes": [ + "image/jpeg", + "image/png", + "image/svg+xml", + "image/gif", + "image/webp" + ], + "thumbs": null, + "maxSelect": 1, + "maxSize": 5242880, + "protected": false + } + } + ], + "indexes": [], + "listRule": "id = @request.auth.id", + "viewRule": "id = @request.auth.id", + "createRule": "", + "updateRule": "id = @request.auth.id", + "deleteRule": "id = @request.auth.id", + "options": { + "allowEmailAuth": true, + "allowOAuth2Auth": true, + "allowUsernameAuth": true, + "exceptEmailDomains": null, + "manageRule": null, + "minPasswordLength": 8, + "onlyEmailDomains": null, + "onlyVerified": false, + "requireEmail": false + } + }, + { + "id": "dy6ccjb60spfy6p", + "created": "2024-09-12 23:12:21.677Z", + "updated": "2024-10-23 09:25:43.085Z", + "name": "settings", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "1tcmdsdf", + "name": "name", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "f9wyhypi", + "name": "content", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 2000000 + } + } + ], + "indexes": [ + "CREATE UNIQUE INDEX ` + "`" + `idx_RO7X9Vw` + "`" + ` ON ` + "`" + `settings` + "`" + ` (` + "`" + `name` + "`" + `)" + ], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "teolp9pl72dxlxq", + "created": "2024-09-13 12:51:05.611Z", + "updated": "2024-10-23 09:25:43.086Z", + "name": "access_groups", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "7sajiv6i", + "name": "name", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "xp8admif", + "name": "access", + "type": "relation", + "required": false, + "presentable": false, + "unique": false, + "options": { + "collectionId": "4yzbv8urny5ja1e", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": null, + "displayFields": null + } + } + ], + "indexes": [ + "CREATE UNIQUE INDEX ` + "`" + `idx_RgRXp0R` + "`" + ` ON ` + "`" + `access_groups` + "`" + ` (` + "`" + `name` + "`" + `)" + ], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }, + { + "id": "012d7abbod1hwvr", + "created": "2024-10-23 06:37:13.155Z", + "updated": "2024-10-23 09:25:43.086Z", + "name": "acme_accounts", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "fmjfn0yw", + "name": "ca", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "qqwijqzt", + "name": "email", + "type": "email", + "required": false, + "presentable": false, + "unique": false, + "options": { + "exceptDomains": null, + "onlyDomains": null + } + }, + { + "system": false, + "id": "genxqtii", + "name": "key", + "type": "text", + "required": false, + "presentable": false, + "unique": false, + "options": { + "min": null, + "max": null, + "pattern": "" + } + }, + { + "system": false, + "id": "1aoia909", + "name": "resource", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 2000000 + } + } + ], + "indexes": [], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + } + ]` + + collections := []*models.Collection{} + if err := json.Unmarshal([]byte(jsonData), &collections); err != nil { + return err + } + + return daos.New(db).ImportCollections(collections, true, nil) + }, func(db dbx.Builder) error { + return nil + }) +} diff --git a/ui/public/imgs/providers/baiducloud.svg b/ui/public/imgs/providers/baiducloud.svg new file mode 100644 index 00000000..25ab747a --- /dev/null +++ b/ui/public/imgs/providers/baiducloud.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/dogecloud.svg b/ui/public/imgs/providers/dogecloud.svg new file mode 100644 index 00000000..253f9ae7 --- /dev/null +++ b/ui/public/imgs/providers/dogecloud.svg @@ -0,0 +1 @@ + diff --git a/ui/public/imgs/providers/google.svg b/ui/public/imgs/providers/google.svg index d5f4dbeb..120a7921 100644 --- a/ui/public/imgs/providers/google.svg +++ b/ui/public/imgs/providers/google.svg @@ -1,28 +1 @@ - - - - - Google-color - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/ui/src/components/certimate/AccessBaiduCloudForm.tsx b/ui/src/components/certimate/AccessBaiduCloudForm.tsx new file mode 100644 index 00000000..2a6e4781 --- /dev/null +++ b/ui/src/components/certimate/AccessBaiduCloudForm.tsx @@ -0,0 +1,194 @@ +import { useForm } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import z from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { ClientResponseError } from "pocketbase"; + +import { Input } from "@/components/ui/input"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; +import { Button } from "@/components/ui/button"; +import { PbErrorData } from "@/domain/base"; +import { accessProvidersMap, accessTypeFormSchema, type Access, type BaiduCloudConfig } from "@/domain/access"; +import { save } from "@/repository/access"; +import { useConfigContext } from "@/providers/config"; + +type AccessBaiduCloudFormProps = { + op: "add" | "edit" | "copy"; + data?: Access; + onAfterReq: () => void; +}; + +const AccessBaiduCloudForm = ({ data, op, onAfterReq }: AccessBaiduCloudFormProps) => { + const { addAccess, updateAccess } = useConfigContext(); + const { t } = useTranslation(); + const formSchema = z.object({ + id: z.string().optional(), + name: z + .string() + .min(1, "access.authorization.form.name.placeholder") + .max(64, t("common.errmsg.string_max", { max: 64 })), + configType: accessTypeFormSchema, + accessKeyId: z + .string() + .min(1, "access.authorization.form.access_key_id.placeholder") + .max(64, t("common.errmsg.string_max", { max: 64 })), + secretAccessKey: z + .string() + .min(1, "access.authorization.form.secret_access_key.placeholder") + .max(64, t("common.errmsg.string_max", { max: 64 })), + }); + + let config: BaiduCloudConfig = { + accessKeyId: "", + secretAccessKey: "", + }; + if (data) config = data.config as BaiduCloudConfig; + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + id: data?.id, + name: data?.name || "", + configType: "baiducloud", + accessKeyId: config.accessKeyId, + secretAccessKey: config.secretAccessKey, + }, + }); + + const onSubmit = async (data: z.infer) => { + const req: Access = { + id: data.id as string, + name: data.name, + configType: data.configType, + usage: accessProvidersMap.get(data.configType)!.usage, + config: { + accessKeyId: data.accessKeyId, + secretAccessKey: data.secretAccessKey, + }, + }; + + try { + req.id = op == "copy" ? "" : req.id; + const rs = await save(req); + + onAfterReq(); + + req.id = rs.id; + req.created = rs.created; + req.updated = rs.updated; + if (data.id && op == "edit") { + updateAccess(req); + return; + } + addAccess(req); + } catch (e) { + const err = e as ClientResponseError; + + Object.entries(err.response.data as PbErrorData).forEach(([key, value]) => { + form.setError(key as keyof z.infer, { + type: "manual", + message: value.message, + }); + }); + + return; + } + }; + + return ( + <> + + { + e.stopPropagation(); + form.handleSubmit(onSubmit)(e); + }} + className="space-y-8" + > + ( + + {t("access.authorization.form.name.label")} + + + + + + + )} + /> + + ( + + {t("access.authorization.form.config.label")} + + + + + + + )} + /> + + ( + + {t("access.authorization.form.config.label")} + + + + + + + )} + /> + + ( + + {t("access.authorization.form.access_key_id.label")} + + + + + + + )} + /> + + ( + + {t("access.authorization.form.secret_access_key.label")} + + + + + + + )} + /> + + + + + {t("common.save")} + + + + > + ); +}; + +export default AccessBaiduCloudForm; diff --git a/ui/src/components/certimate/AccessDogeCloudForm.tsx b/ui/src/components/certimate/AccessDogeCloudForm.tsx new file mode 100644 index 00000000..66c8db6f --- /dev/null +++ b/ui/src/components/certimate/AccessDogeCloudForm.tsx @@ -0,0 +1,188 @@ +import { useForm } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import z from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { ClientResponseError } from "pocketbase"; + +import { Button } from "@/components/ui/button"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { PbErrorData } from "@/domain/base"; +import { accessProvidersMap, accessTypeFormSchema, type Access, type DogeCloudConfig } from "@/domain/access"; +import { save } from "@/repository/access"; +import { useConfigContext } from "@/providers/config"; + +type AccessDogeCloudFormProps = { + op: "add" | "edit" | "copy"; + data?: Access; + onAfterReq: () => void; +}; + +const AccessDogeCloudForm = ({ data, op, onAfterReq }: AccessDogeCloudFormProps) => { + const { addAccess, updateAccess } = useConfigContext(); + const { t } = useTranslation(); + const formSchema = z.object({ + id: z.string().optional(), + name: z + .string() + .min(1, "access.authorization.form.name.placeholder") + .max(64, t("common.errmsg.string_max", { max: 64 })), + configType: accessTypeFormSchema, + accessKey: z.string().min(1, "access.authorization.form.access_key.placeholder").max(64), + secretKey: z.string().min(1, "access.authorization.form.secret_key.placeholder").max(64), + }); + + let config: DogeCloudConfig = { + accessKey: "", + secretKey: "", + }; + if (data) config = data.config as DogeCloudConfig; + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + id: data?.id, + name: data?.name || "", + configType: "dogecloud", + accessKey: config.accessKey, + secretKey: config.secretKey, + }, + }); + + const onSubmit = async (data: z.infer) => { + const req: Access = { + id: data.id as string, + name: data.name, + configType: data.configType, + usage: accessProvidersMap.get(data.configType)!.usage, + config: { + accessKey: data.accessKey, + secretKey: data.secretKey, + }, + }; + + try { + req.id = op == "copy" ? "" : req.id; + const rs = await save(req); + + onAfterReq(); + + req.id = rs.id; + req.created = rs.created; + req.updated = rs.updated; + if (data.id && op == "edit") { + updateAccess(req); + return; + } + addAccess(req); + } catch (e) { + const err = e as ClientResponseError; + + Object.entries(err.response.data as PbErrorData).forEach(([key, value]) => { + form.setError(key as keyof z.infer, { + type: "manual", + message: value.message, + }); + }); + + return; + } + }; + + return ( + <> + + { + e.stopPropagation(); + form.handleSubmit(onSubmit)(e); + }} + className="space-y-8" + > + ( + + {t("access.authorization.form.name.label")} + + + + + + + )} + /> + + ( + + {t("access.authorization.form.config.label")} + + + + + + + )} + /> + + ( + + {t("access.authorization.form.config.label")} + + + + + + + )} + /> + + ( + + {t("access.authorization.form.access_key.label")} + + + + + + + )} + /> + + ( + + {t("access.authorization.form.secret_key.label")} + + + + + + + )} + /> + + + + + {t("common.save")} + + + + > + ); +}; + +export default AccessDogeCloudForm; diff --git a/ui/src/components/certimate/AccessEditDialog.tsx b/ui/src/components/certimate/AccessEditDialog.tsx index afb23068..8f226343 100644 --- a/ui/src/components/certimate/AccessEditDialog.tsx +++ b/ui/src/components/certimate/AccessEditDialog.tsx @@ -8,7 +8,9 @@ import { ScrollArea } from "@/components/ui/scroll-area"; import AccessAliyunForm from "./AccessAliyunForm"; import AccessTencentForm from "./AccessTencentForm"; import AccessHuaweiCloudForm from "./AccessHuaweicloudForm"; +import AccessBaiduCloudForm from "./AccessBaiduCloudForm"; import AccessQiniuForm from "./AccessQiniuForm"; +import AccessDogeCloudForm from "./AccessDogeCloudForm"; import AccessAwsForm from "./AccessAwsForm"; import AccessCloudflareForm from "./AccessCloudflareForm"; import AccessNamesiloForm from "./AccessNamesiloForm"; @@ -71,6 +73,17 @@ const AccessEditDialog = ({ trigger, op, data, className }: AccessEditProps) => /> ); break; + case "baiducloud": + childComponent = ( + { + setOpen(false); + }} + /> + ); + break; case "qiniu": childComponent = ( /> ); break; + case "dogecloud": + childComponent = ( + { + setOpen(false); + }} + /> + ); + break; case "aws": childComponent = ( ; break; + case "baiducloud-cdn": + childComponent = ; + break; case "qiniu-cdn": childComponent = ; break; + case "dogecloud-cdn": + childComponent = ; + break; case "local": childComponent = ; break; diff --git a/ui/src/components/certimate/DeployToBaiduCloudCDN.tsx b/ui/src/components/certimate/DeployToBaiduCloudCDN.tsx new file mode 100644 index 00000000..262fb3e5 --- /dev/null +++ b/ui/src/components/certimate/DeployToBaiduCloudCDN.tsx @@ -0,0 +1,68 @@ +import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { z } from "zod"; +import { produce } from "immer"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { useDeployEditContext } from "./DeployEdit"; + +type DeployToBaiduCloudCDNConfigParams = { + domain?: string; +}; + +const DeployToBaiduCloudCDN = () => { + const { t } = useTranslation(); + + const { config, setConfig, errors, setErrors } = useDeployEditContext(); + + useEffect(() => { + if (!config.id) { + setConfig({ + ...config, + config: {}, + }); + } + }, []); + + useEffect(() => { + setErrors({}); + }, []); + + const formSchema = z.object({ + domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { + message: t("common.errmsg.domain_invalid"), + }), + }); + + useEffect(() => { + const res = formSchema.safeParse(config.config); + setErrors({ + ...errors, + domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message, + }); + }, [config]); + + return ( + + + {t("domain.deployment.form.domain.label")} + { + const nv = produce(config, (draft) => { + draft.config ??= {}; + draft.config.domain = e.target.value?.trim(); + }); + setConfig(nv); + }} + /> + {errors?.domain} + + + ); +}; + +export default DeployToBaiduCloudCDN; diff --git a/ui/src/components/certimate/DeployToDogeCloudCDN.tsx b/ui/src/components/certimate/DeployToDogeCloudCDN.tsx new file mode 100644 index 00000000..5b0ff52d --- /dev/null +++ b/ui/src/components/certimate/DeployToDogeCloudCDN.tsx @@ -0,0 +1,68 @@ +import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { z } from "zod"; +import { produce } from "immer"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { useDeployEditContext } from "./DeployEdit"; + +type DeployToDogeCloudCDNConfigParams = { + domain?: string; +}; + +const DeployToDogeCloudCDN = () => { + const { t } = useTranslation(); + + const { config, setConfig, errors, setErrors } = useDeployEditContext(); + + useEffect(() => { + if (!config.id) { + setConfig({ + ...config, + config: {}, + }); + } + }, []); + + useEffect(() => { + setErrors({}); + }, []); + + const formSchema = z.object({ + domain: z.string().regex(/^(?:\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/, { + message: t("common.errmsg.domain_invalid"), + }), + }); + + useEffect(() => { + const res = formSchema.safeParse(config.config); + setErrors({ + ...errors, + domain: res.error?.errors?.find((e) => e.path[0] === "domain")?.message, + }); + }, [config]); + + return ( + + + {t("domain.deployment.form.domain.label")} + { + const nv = produce(config, (draft) => { + draft.config ??= {}; + draft.config.domain = e.target.value?.trim(); + }); + setConfig(nv); + }} + /> + {errors?.domain} + + + ); +}; + +export default DeployToDogeCloudCDN; diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts index b62d48ed..8f2844fd 100644 --- a/ui/src/domain/access.ts +++ b/ui/src/domain/access.ts @@ -15,7 +15,9 @@ export const accessProvidersMap: Map = n ["aliyun", "common.provider.aliyun", "/imgs/providers/aliyun.svg", "all", "阿里云:alibaba cloud"], ["tencent", "common.provider.tencent", "/imgs/providers/tencent.svg", "all", "腾讯云:tencent cloud"], ["huaweicloud", "common.provider.huaweicloud", "/imgs/providers/huaweicloud.svg", "all", "华为云:huawei cloud"], - ["qiniu", "common.provider.qiniu", "/imgs/providers/qiniu.svg", "deploy", "七牛:qiniu"], + ["baiducloud", "common.provider.baiducloud", "/imgs/providers/baiducloud.svg", "all", "百度智能云:百度云:baidu cloud"], + ["qiniu", "common.provider.qiniu", "/imgs/providers/qiniu.svg", "deploy", "七牛云:qiniu"], + ["dogecloud", "common.provider.dogecloud", "/imgs/providers/dogecloud.svg", "deploy", "多吉云:doge cloud"], ["aws", "common.provider.aws", "/imgs/providers/aws.svg", "apply", "亚马逊:amazon:aws"], ["cloudflare", "common.provider.cloudflare", "/imgs/providers/cloudflare.svg", "apply", "cloudflare:cf:cloud flare"], ["namesilo", "common.provider.namesilo", "/imgs/providers/namesilo.svg", "apply", "namesilo"], @@ -34,7 +36,9 @@ export const accessTypeFormSchema = z.union( z.literal("aliyun"), z.literal("tencent"), z.literal("huaweicloud"), + z.literal("baiducloud"), z.literal("qiniu"), + z.literal("dogecloud"), z.literal("aws"), z.literal("cloudflare"), z.literal("namesilo"), @@ -60,6 +64,7 @@ export type Access = { | TencentConfig | HuaweiCloudConfig | QiniuConfig + | DogeCloudConfig | AwsConfig | CloudflareConfig | NamesiloConfig @@ -91,11 +96,21 @@ export type HuaweiCloudConfig = { secretAccessKey: string; }; +export type BaiduCloudConfig = { + accessKeyId: string; + secretAccessKey: string; +}; + export type QiniuConfig = { accessKey: string; secretKey: string; }; +export type DogeCloudConfig = { + accessKey: string; + secretKey: string; +}; + export type AwsConfig = { region: string; accessKeyId: string; diff --git a/ui/src/domain/domain.ts b/ui/src/domain/domain.ts index 5e4786c4..c4b5f683 100644 --- a/ui/src/domain/domain.ts +++ b/ui/src/domain/domain.ts @@ -85,7 +85,9 @@ export const deployTargetsMap: Map = new Map ["tencent-teo", "common.provider.tencent.teo", "/imgs/providers/tencent.svg"], ["huaweicloud-cdn", "common.provider.huaweicloud.cdn", "/imgs/providers/huaweicloud.svg"], ["huaweicloud-elb", "common.provider.huaweicloud.elb", "/imgs/providers/huaweicloud.svg"], + ["baiducloud-cdn", "common.provider.baiducloud.cdn", "/imgs/providers/baiducloud.svg"], ["qiniu-cdn", "common.provider.qiniu.cdn", "/imgs/providers/qiniu.svg"], + ["dogecloud-cdn", "common.provider.dogecloud.cdn", "/imgs/providers/dogecloud.svg"], ["local", "common.provider.local", "/imgs/providers/local.svg"], ["ssh", "common.provider.ssh", "/imgs/providers/ssh.svg"], ["webhook", "common.provider.webhook", "/imgs/providers/webhook.svg"], diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index fc545b4c..69517f1e 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -68,8 +68,12 @@ "common.provider.huaweicloud": "Huawei Cloud", "common.provider.huaweicloud.cdn": "Huawei Cloud - CDN", "common.provider.huaweicloud.elb": "Huawei Cloud - ELB", + "common.provider.baiducloud": "Baidu Cloud", + "common.provider.baiducloud.cdn": "Baidu Cloud - CDN", "common.provider.qiniu": "Qiniu Cloud", "common.provider.qiniu.cdn": "Qiniu Cloud - CDN", + "common.provider.dogecloud": "Doge Cloud", + "common.provider.dogecloud.cdn": "Doge Cloud - CDN", "common.provider.aws": "AWS", "common.provider.cloudflare": "Cloudflare", "common.provider.namesilo": "Namesilo", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 487f90c5..5657836e 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -68,8 +68,12 @@ "common.provider.huaweicloud": "华为云", "common.provider.huaweicloud.cdn": "华为云 - 内容分发网络 CDN", "common.provider.huaweicloud.elb": "华为云 - 弹性负载均衡 ELB", + "common.provider.baiducloud": "百度智能云", + "common.provider.baiducloud.cdn": "百度智能云 - 内容分发网络 CDN", "common.provider.qiniu": "七牛云", "common.provider.qiniu.cdn": "七牛云 - 内容分发网络 CDN", + "common.provider.dogecloud": "多吉云", + "common.provider.dogecloud.cdn": "多吉云 - 内容分发网络 CDN", "common.provider.aws": "AWS", "common.provider.cloudflare": "Cloudflare", "common.provider.namesilo": "Namesilo",