mirror of
https://github.com/certimate-go/certimate.git
synced 2026-06-22 21:05:48 +08:00
feat(provider): support configuring website match pattern in deployment to 1panel site
This commit is contained in:
parent
c25458439f
commit
6d296af020
@ -13,6 +13,7 @@ import (
|
||||
"github.com/certimate-go/certimate/pkg/core/deployer"
|
||||
onepanelsdk "github.com/certimate-go/certimate/pkg/sdk3rd/1panel"
|
||||
onepanelsdk2 "github.com/certimate-go/certimate/pkg/sdk3rd/1panel/v2"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
type DeployerConfig struct {
|
||||
@ -30,8 +31,11 @@ type DeployerConfig struct {
|
||||
NodeName string `json:"nodeName,omitempty"`
|
||||
// 部署资源类型。
|
||||
ResourceType string `json:"resourceType"`
|
||||
// 域名匹配模式。
|
||||
// 零值时默认值 [WEBSITE_MATCH_PATTERN_EXACT]。
|
||||
WebsiteMatchPattern string `json:"websiteMatchPattern,omitempty"`
|
||||
// 网站 ID。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_WEBSITE] 时必填。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_WEBSITE]、且匹配模式非 [WEBSITE_MATCH_PATTERN_CERTSAN] 时必填。
|
||||
WebsiteId int64 `json:"websiteId,omitempty"`
|
||||
// 证书 ID。
|
||||
// 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
|
||||
@ -106,10 +110,6 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
|
||||
}
|
||||
|
||||
func (d *Deployer) deployToWebsite(ctx context.Context, certPEM, privkeyPEM string) error {
|
||||
if d.config.WebsiteId == 0 {
|
||||
return errors.New("config `websiteId` is required")
|
||||
}
|
||||
|
||||
// 上传证书
|
||||
upres, err := d.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM)
|
||||
if err != nil {
|
||||
@ -118,65 +118,54 @@ func (d *Deployer) deployToWebsite(ctx context.Context, certPEM, privkeyPEM stri
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
switch sdkClient := d.sdkClient.(type) {
|
||||
case *onepanelsdk.Client:
|
||||
// 获取待部署的网站列表
|
||||
var websiteIds []int64
|
||||
switch d.config.WebsiteMatchPattern {
|
||||
case "", WEBSITE_MATCH_PATTERN_SPECIFIED:
|
||||
{
|
||||
// 获取网站 HTTPS 配置
|
||||
websiteHttpsGetResp, err := sdkClient.WebsiteHttpsGet(d.config.WebsiteId)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteHttpsGet'", slog.Int64("websiteId", d.config.WebsiteId), slog.Any("response", websiteHttpsGetResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.WebsiteHttpsGet': %w", err)
|
||||
if d.config.WebsiteId == 0 {
|
||||
return errors.New("config `websiteId` is required")
|
||||
}
|
||||
|
||||
// 修改网站 HTTPS 配置
|
||||
sslId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
websiteHttpsPostReq := &onepanelsdk.WebsiteHttpsPostRequest{
|
||||
WebsiteID: d.config.WebsiteId,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: sslId,
|
||||
Enable: websiteHttpsGetResp.Data.Enable,
|
||||
HttpConfig: websiteHttpsGetResp.Data.HttpConfig,
|
||||
SSLProtocol: websiteHttpsGetResp.Data.SSLProtocol,
|
||||
Algorithm: websiteHttpsGetResp.Data.Algorithm,
|
||||
Hsts: websiteHttpsGetResp.Data.Hsts,
|
||||
}
|
||||
websiteHttpsPostResp, err := sdkClient.WebsiteHttpsPost(d.config.WebsiteId, websiteHttpsPostReq)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteHttpsPost'", slog.Int64("websiteId", d.config.WebsiteId), slog.Any("request", websiteHttpsPostReq), slog.Any("response", websiteHttpsPostResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.WebsiteHttpsPost': %w", err)
|
||||
}
|
||||
websiteIds = []int64{d.config.WebsiteId}
|
||||
}
|
||||
|
||||
case *onepanelsdk2.Client:
|
||||
case WEBSITE_MATCH_PATTERN_CERTSAN:
|
||||
{
|
||||
// 获取网站 HTTPS 配置
|
||||
websiteHttpsGetResp, err := sdkClient.WebsiteHttpsGet(d.config.WebsiteId)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteHttpsGet'", slog.Int64("websiteId", d.config.WebsiteId), slog.Any("response", websiteHttpsGetResp))
|
||||
websiteIdCandidates, err := d.getMatchedWebsiteIdsByCertificate(ctx, certPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.WebsiteHttpsGet': %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// 修改网站 HTTPS 配置
|
||||
sslId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
websiteHttpsPostReq := &onepanelsdk2.WebsiteHttpsPostRequest{
|
||||
WebsiteID: d.config.WebsiteId,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: sslId,
|
||||
Enable: websiteHttpsGetResp.Data.Enable,
|
||||
HttpConfig: websiteHttpsGetResp.Data.HttpConfig,
|
||||
SSLProtocol: websiteHttpsGetResp.Data.SSLProtocol,
|
||||
Algorithm: websiteHttpsGetResp.Data.Algorithm,
|
||||
Hsts: websiteHttpsGetResp.Data.Hsts,
|
||||
}
|
||||
websiteHttpsPostResp, err := sdkClient.WebsiteHttpsPost(d.config.WebsiteId, websiteHttpsPostReq)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteHttpsPost'", slog.Int64("websiteId", d.config.WebsiteId), slog.Any("request", websiteHttpsPostReq), slog.Any("response", websiteHttpsPostResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.WebsiteHttpsPost': %w", err)
|
||||
}
|
||||
websiteIds = websiteIdCandidates
|
||||
}
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
return fmt.Errorf("unsupported website match pattern: '%s'", d.config.WebsiteMatchPattern)
|
||||
}
|
||||
|
||||
// 遍历更新网站证书
|
||||
if len(websiteIds) == 0 {
|
||||
d.logger.Info("no websites to deploy")
|
||||
} else {
|
||||
d.logger.Info("found websites to deploy", slog.Any("websiteIds", websiteIds))
|
||||
var errs []error
|
||||
|
||||
websiteSSLId, _ := strconv.ParseInt(upres.CertId, 10, 64)
|
||||
for _, websiteId := range websiteIds {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
if err := d.updateWebsiteCertificate(ctx, websiteId, websiteSSLId); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -198,6 +187,211 @@ func (d *Deployer) deployToCertificate(ctx context.Context, certPEM, privkeyPEM
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Deployer) getMatchedWebsiteIdsByCertificate(ctx context.Context, certPEM string) ([]int64, error) {
|
||||
var websiteIds []int64
|
||||
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch sdkClient := d.sdkClient.(type) {
|
||||
case *onepanelsdk.Client:
|
||||
{
|
||||
websiteSearchPage := 1
|
||||
websiteSearchPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
websiteSearchReq := &onepanelsdk.WebsiteSearchRequest{
|
||||
Order: "ascending",
|
||||
OrderBy: "primary_domain",
|
||||
Page: int32(websiteSearchPage),
|
||||
PageSize: int32(websiteSearchPageSize),
|
||||
}
|
||||
websiteSearchResp, err := sdkClient.WebsiteSearch(websiteSearchReq)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteSearch'", slog.Any("request", websiteSearchReq), slog.Any("response", websiteSearchResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request '1panel.WebsiteSearch': %w", err)
|
||||
}
|
||||
|
||||
if websiteSearchResp.Data == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, websiteItem := range websiteSearchResp.Data.Items {
|
||||
if certX509.VerifyHostname(websiteItem.PrimaryDomain) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
websiteGetResp, err := sdkClient.WebsiteGet(websiteItem.ID)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteGet'", slog.Int64("websiteId", websiteItem.ID), slog.Any("response", websiteGetResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request '1panel.WebsiteGet': %w", err)
|
||||
}
|
||||
|
||||
for _, domainInfo := range websiteGetResp.Data.Domains {
|
||||
if domainInfo.SSL {
|
||||
websiteIds = append(websiteIds, websiteItem.ID)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(websiteSearchResp.Data.Items) < websiteSearchPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
websiteSearchPage++
|
||||
}
|
||||
}
|
||||
|
||||
case *onepanelsdk2.Client:
|
||||
{
|
||||
websiteSearchPage := 1
|
||||
websiteSearchPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
websiteSearchReq := &onepanelsdk2.WebsiteSearchRequest{
|
||||
Order: "ascending",
|
||||
OrderBy: "primary_domain",
|
||||
Page: int32(websiteSearchPage),
|
||||
PageSize: int32(websiteSearchPageSize),
|
||||
}
|
||||
websiteSearchResp, err := sdkClient.WebsiteSearch(websiteSearchReq)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteSearch'", slog.Any("request", websiteSearchReq), slog.Any("response", websiteSearchResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request '1panel.WebsiteSearch': %w", err)
|
||||
}
|
||||
|
||||
if websiteSearchResp.Data == nil {
|
||||
break
|
||||
}
|
||||
|
||||
for _, websiteItem := range websiteSearchResp.Data.Items {
|
||||
if certX509.VerifyHostname(websiteItem.PrimaryDomain) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
websiteGetResp, err := sdkClient.WebsiteGet(websiteItem.ID)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteGet'", slog.Int64("websiteId", websiteItem.ID), slog.Any("response", websiteGetResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request '1panel.WebsiteGet': %w", err)
|
||||
}
|
||||
|
||||
for _, domainInfo := range websiteGetResp.Data.Domains {
|
||||
if domainInfo.SSL {
|
||||
websiteIds = append(websiteIds, websiteItem.ID)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(websiteSearchResp.Data.Items) < websiteSearchPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
websiteSearchPage++
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
if len(websiteIds) == 0 {
|
||||
return nil, errors.New("could not find any websites matched by certificate")
|
||||
}
|
||||
|
||||
return websiteIds, nil
|
||||
}
|
||||
|
||||
func (d *Deployer) updateWebsiteCertificate(ctx context.Context, websiteId int64, websiteSSLId int64) error {
|
||||
switch sdkClient := d.sdkClient.(type) {
|
||||
case *onepanelsdk.Client:
|
||||
{
|
||||
// 获取网站 HTTPS 配置
|
||||
websiteHttpsGetResp, err := sdkClient.WebsiteHttpsGet(websiteId)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteHttpsGet'", slog.Int64("websiteId", websiteId), slog.Any("response", websiteHttpsGetResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.WebsiteHttpsGet': %w", err)
|
||||
} else {
|
||||
if websiteHttpsGetResp.Data.Enable && websiteHttpsGetResp.Data.WebsiteSSLID == websiteSSLId {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 修改网站 HTTPS 配置
|
||||
websiteHttpsPostReq := &onepanelsdk.WebsiteHttpsPostRequest{
|
||||
WebsiteID: websiteId,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: websiteSSLId,
|
||||
Enable: true,
|
||||
HttpConfig: websiteHttpsGetResp.Data.HttpConfig,
|
||||
SSLProtocol: websiteHttpsGetResp.Data.SSLProtocol,
|
||||
Algorithm: websiteHttpsGetResp.Data.Algorithm,
|
||||
Hsts: websiteHttpsGetResp.Data.Hsts,
|
||||
}
|
||||
if websiteHttpsPostReq.HttpConfig == "" {
|
||||
websiteHttpsPostReq.HttpConfig = "HTTPToHTTPS"
|
||||
}
|
||||
websiteHttpsPostResp, err := sdkClient.WebsiteHttpsPost(websiteId, websiteHttpsPostReq)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteHttpsPost'", slog.Int64("websiteId", websiteId), slog.Any("request", websiteHttpsPostReq), slog.Any("response", websiteHttpsPostResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.WebsiteHttpsPost': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
case *onepanelsdk2.Client:
|
||||
{
|
||||
// 获取网站 HTTPS 配置
|
||||
websiteHttpsGetResp, err := sdkClient.WebsiteHttpsGet(websiteId)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteHttpsGet'", slog.Int64("websiteId", websiteId), slog.Any("response", websiteHttpsGetResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.WebsiteHttpsGet': %w", err)
|
||||
} else {
|
||||
if websiteHttpsGetResp.Data.Enable && websiteHttpsGetResp.Data.WebsiteSSLID == websiteSSLId {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 修改网站 HTTPS 配置
|
||||
websiteHttpsPostReq := &onepanelsdk2.WebsiteHttpsPostRequest{
|
||||
WebsiteID: websiteId,
|
||||
Type: "existed",
|
||||
WebsiteSSLID: websiteSSLId,
|
||||
Enable: true,
|
||||
HttpConfig: websiteHttpsGetResp.Data.HttpConfig,
|
||||
SSLProtocol: websiteHttpsGetResp.Data.SSLProtocol,
|
||||
Algorithm: websiteHttpsGetResp.Data.Algorithm,
|
||||
Hsts: websiteHttpsGetResp.Data.Hsts,
|
||||
Http3: websiteHttpsGetResp.Data.Http3,
|
||||
}
|
||||
if websiteHttpsPostReq.HttpConfig == "" {
|
||||
websiteHttpsPostReq.HttpConfig = "HTTPToHTTPS"
|
||||
}
|
||||
websiteHttpsPostResp, err := sdkClient.WebsiteHttpsPost(websiteId, websiteHttpsPostReq)
|
||||
d.logger.Debug("sdk request '1panel.WebsiteHttpsPost'", slog.Int64("websiteId", websiteId), slog.Any("request", websiteHttpsPostReq), slog.Any("response", websiteHttpsPostResp))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute sdk request '1panel.WebsiteHttpsPost': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
sdkVersionV1 = "v1"
|
||||
sdkVersionV2 = "v2"
|
||||
|
||||
@ -62,6 +62,7 @@ func TestDeploy(t *testing.T) {
|
||||
ApiKey: fApiKey,
|
||||
AllowInsecureConnections: true,
|
||||
ResourceType: provider.RESOURCE_TYPE_WEBSITE,
|
||||
WebsiteMatchPattern: provider.WEBSITE_MATCH_PATTERN_SPECIFIED,
|
||||
WebsiteId: fWebsiteId,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@ -6,3 +6,10 @@ const (
|
||||
// 资源类型:替换指定证书。
|
||||
RESOURCE_TYPE_CERTIFICATE = "certificate"
|
||||
)
|
||||
|
||||
const (
|
||||
// 匹配模式:指定 ID。
|
||||
WEBSITE_MATCH_PATTERN_SPECIFIED = "specified"
|
||||
// 匹配模式:证书 SAN 匹配。
|
||||
WEBSITE_MATCH_PATTERN_CERTSAN = "certsan"
|
||||
)
|
||||
|
||||
64
pkg/sdk3rd/1panel/api_website_get.go
Normal file
64
pkg/sdk3rd/1panel/api_website_get.go
Normal file
@ -0,0 +1,64 @@
|
||||
package onepanel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WebsiteGetRequest struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Page int32 `json:"page"`
|
||||
PageSize int32 `json:"pageSize"`
|
||||
}
|
||||
|
||||
type WebsiteGetResponse struct {
|
||||
apiResponseBase
|
||||
|
||||
Data *struct {
|
||||
ID int64 `json:"id"`
|
||||
Alias string `json:"alias"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
Protocol string `json:"protocol"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
SitePath string `json:"sitePath"`
|
||||
Remark string `json:"remark"`
|
||||
Domains []*struct {
|
||||
ID int64 `json:"id"`
|
||||
Domain string `json:"domain"`
|
||||
Port int32 `json:"port"`
|
||||
SSL bool `json:"ssl"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
} `json:"domains"`
|
||||
WebsiteSSLId int64 `json:"webSiteSSLId"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) WebsiteGet(websiteId int64) (*WebsiteGetResponse, error) {
|
||||
return c.WebsiteGetWithContext(context.Background(), websiteId)
|
||||
}
|
||||
|
||||
func (c *Client) WebsiteGetWithContext(ctx context.Context, websiteId int64) (*WebsiteGetResponse, error) {
|
||||
if websiteId == 0 {
|
||||
return nil, fmt.Errorf("sdkerr: unset websiteId")
|
||||
}
|
||||
|
||||
httpreq, err := c.newRequest(http.MethodGet, fmt.Sprintf("/websites/%d", websiteId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
httpreq.SetContext(ctx)
|
||||
}
|
||||
|
||||
result := &WebsiteGetResponse{}
|
||||
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@ -10,11 +10,12 @@ type WebsiteHttpsGetResponse struct {
|
||||
apiResponseBase
|
||||
|
||||
Data *struct {
|
||||
Enable bool `json:"enable"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
Enable bool `json:"enable"`
|
||||
WebsiteSSLID int64 `json:"websiteSSLId"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@ -7,19 +7,14 @@ import (
|
||||
)
|
||||
|
||||
type WebsiteHttpsPostRequest 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"`
|
||||
WebsiteID int64 `json:"websiteId"`
|
||||
Enable bool `json:"enable"`
|
||||
Type string `json:"type"`
|
||||
WebsiteSSLID int64 `json:"websiteSSLId"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
}
|
||||
|
||||
type WebsiteHttpsPostResponse struct {
|
||||
|
||||
58
pkg/sdk3rd/1panel/api_website_search.go
Normal file
58
pkg/sdk3rd/1panel/api_website_search.go
Normal file
@ -0,0 +1,58 @@
|
||||
package onepanel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WebsiteSearchRequest struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Order string `json:"order"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Page int32 `json:"page"`
|
||||
PageSize int32 `json:"pageSize"`
|
||||
}
|
||||
|
||||
type WebsiteSearchResponse struct {
|
||||
apiResponseBase
|
||||
|
||||
Data *struct {
|
||||
Items []*struct {
|
||||
ID int64 `json:"id"`
|
||||
Alias string `json:"alias"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
Protocol string `json:"protocol"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
SitePath string `json:"sitePath"`
|
||||
Remark string `json:"remark"`
|
||||
SSLStatus string `json:"sslStatus"`
|
||||
SSLExpireDate string `json:"sslExpireDate"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
} `json:"items"`
|
||||
Total int32 `json:"total"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) WebsiteSearch(req *WebsiteSearchRequest) (*WebsiteSearchResponse, error) {
|
||||
return c.WebsiteSearchWithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
func (c *Client) WebsiteSearchWithContext(ctx context.Context, req *WebsiteSearchRequest) (*WebsiteSearchResponse, error) {
|
||||
httpreq, err := c.newRequest(http.MethodPost, "/websites/search")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
httpreq.SetBody(req)
|
||||
httpreq.SetContext(ctx)
|
||||
}
|
||||
|
||||
result := &WebsiteSearchResponse{}
|
||||
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
64
pkg/sdk3rd/1panel/v2/api_website_get.go
Normal file
64
pkg/sdk3rd/1panel/v2/api_website_get.go
Normal file
@ -0,0 +1,64 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WebsiteGetRequest struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Page int32 `json:"page"`
|
||||
PageSize int32 `json:"pageSize"`
|
||||
}
|
||||
|
||||
type WebsiteGetResponse struct {
|
||||
apiResponseBase
|
||||
|
||||
Data *struct {
|
||||
ID int64 `json:"id"`
|
||||
Alias string `json:"alias"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
Protocol string `json:"protocol"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
SitePath string `json:"sitePath"`
|
||||
Remark string `json:"remark"`
|
||||
Domains []*struct {
|
||||
ID int64 `json:"id"`
|
||||
Domain string `json:"domain"`
|
||||
Port int32 `json:"port"`
|
||||
SSL bool `json:"ssl"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
} `json:"domains,omitempty"`
|
||||
WebsiteSSLId int64 `json:"webSiteSSLId"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) WebsiteGet(websiteId int64) (*WebsiteGetResponse, error) {
|
||||
return c.WebsiteGetWithContext(context.Background(), websiteId)
|
||||
}
|
||||
|
||||
func (c *Client) WebsiteGetWithContext(ctx context.Context, websiteId int64) (*WebsiteGetResponse, error) {
|
||||
if websiteId == 0 {
|
||||
return nil, fmt.Errorf("sdkerr: unset websiteId")
|
||||
}
|
||||
|
||||
httpreq, err := c.newRequest(http.MethodGet, fmt.Sprintf("/websites/%d", websiteId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
httpreq.SetContext(ctx)
|
||||
}
|
||||
|
||||
result := &WebsiteGetResponse{}
|
||||
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@ -10,11 +10,13 @@ type WebsiteHttpsGetResponse struct {
|
||||
apiResponseBase
|
||||
|
||||
Data *struct {
|
||||
Enable bool `json:"enable"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
Enable bool `json:"enable"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
WebsiteSSLID int64 `json:"websiteSSLId"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
Http3 bool `json:"http3"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@ -7,19 +7,15 @@ import (
|
||||
)
|
||||
|
||||
type WebsiteHttpsPostRequest 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"`
|
||||
WebsiteID int64 `json:"websiteId"`
|
||||
Enable bool `json:"enable"`
|
||||
Type string `json:"type"`
|
||||
WebsiteSSLID int64 `json:"websiteSSLId"`
|
||||
HttpConfig string `json:"httpConfig"`
|
||||
SSLProtocol []string `json:"SSLProtocol"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hsts bool `json:"hsts"`
|
||||
Http3 bool `json:"http3"`
|
||||
}
|
||||
|
||||
type WebsiteHttpsPostResponse struct {
|
||||
|
||||
58
pkg/sdk3rd/1panel/v2/api_website_search.go
Normal file
58
pkg/sdk3rd/1panel/v2/api_website_search.go
Normal file
@ -0,0 +1,58 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WebsiteSearchRequest struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Order string `json:"order"`
|
||||
OrderBy string `json:"orderBy"`
|
||||
Page int32 `json:"page"`
|
||||
PageSize int32 `json:"pageSize"`
|
||||
}
|
||||
|
||||
type WebsiteSearchResponse struct {
|
||||
apiResponseBase
|
||||
|
||||
Data *struct {
|
||||
Items []*struct {
|
||||
ID int64 `json:"id"`
|
||||
Alias string `json:"alias"`
|
||||
PrimaryDomain string `json:"primaryDomain"`
|
||||
Protocol string `json:"protocol"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
SitePath string `json:"sitePath"`
|
||||
Remark string `json:"remark"`
|
||||
SSLStatus string `json:"sslStatus"`
|
||||
SSLExpireDate string `json:"sslExpireDate"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
} `json:"items"`
|
||||
Total int32 `json:"total"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) WebsiteSearch(req *WebsiteSearchRequest) (*WebsiteSearchResponse, error) {
|
||||
return c.WebsiteSearchWithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
func (c *Client) WebsiteSearchWithContext(ctx context.Context, req *WebsiteSearchRequest) (*WebsiteSearchResponse, error) {
|
||||
httpreq, err := c.newRequest(http.MethodPost, "/websites/search")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
httpreq.SetBody(req)
|
||||
httpreq.SetContext(ctx)
|
||||
}
|
||||
|
||||
result := &WebsiteSearchResponse{}
|
||||
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input, Select } from "antd";
|
||||
import { Form, Input, Radio, Select } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
@ -10,6 +10,9 @@ import { useFormNestedFieldsContext } from "./_context";
|
||||
const RESOURCE_TYPE_WEBSITE = "website" as const;
|
||||
const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
|
||||
|
||||
const WEBSITE_MATCH_PATTERN_SPECIFIED = "specified" as const;
|
||||
const WEBSITE_MATCH_PATTERN_CERTSAN = "certsan" as const;
|
||||
|
||||
const BizDeployNodeConfigFieldsProvider1PanelSite = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
@ -22,6 +25,7 @@ const BizDeployNodeConfigFieldsProvider1PanelSite = () => {
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
|
||||
const fieldWebsiteMatchPattern = Form.useWatch([parentNamePath, "websiteMatchPattern"], { form: formInst, preserve: true });
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -53,14 +57,38 @@ const BizDeployNodeConfigFieldsProvider1PanelSite = () => {
|
||||
|
||||
<Show when={fieldResourceType === RESOURCE_TYPE_WEBSITE}>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "websiteId"]}
|
||||
initialValue={initialValues.websiteId}
|
||||
label={t("workflow_node.deploy.form.1panel_site_website_id.label")}
|
||||
name={[parentNamePath, "websiteMatchPattern"]}
|
||||
initialValue={initialValues.websiteMatchPattern}
|
||||
label={t("workflow_node.deploy.form.1panel_site_website_match_pattern.label")}
|
||||
extra={
|
||||
fieldWebsiteMatchPattern === WEBSITE_MATCH_PATTERN_CERTSAN ? (
|
||||
<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.1panel_site_website_match_pattern.help_certsan") }}></span>
|
||||
) : (
|
||||
void 0
|
||||
)
|
||||
}
|
||||
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")} />
|
||||
<Radio.Group
|
||||
options={[WEBSITE_MATCH_PATTERN_SPECIFIED, WEBSITE_MATCH_PATTERN_CERTSAN].map((s) => ({
|
||||
key: s,
|
||||
label: t(`workflow_node.deploy.form.1panel_site_website_match_pattern.option.${s}.label`),
|
||||
value: s,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Show when={fieldWebsiteMatchPattern !== WEBSITE_MATCH_PATTERN_CERTSAN}>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "websiteId"]}
|
||||
initialValue={initialValues.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>
|
||||
</Show>
|
||||
</Show>
|
||||
|
||||
<Show when={fieldResourceType === RESOURCE_TYPE_CERTIFICATE}>
|
||||
@ -81,6 +109,8 @@ const BizDeployNodeConfigFieldsProvider1PanelSite = () => {
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
resourceType: RESOURCE_TYPE_WEBSITE,
|
||||
websiteMatchPattern: WEBSITE_MATCH_PATTERN_SPECIFIED,
|
||||
websiteId: "",
|
||||
};
|
||||
};
|
||||
|
||||
@ -91,6 +121,7 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
|
||||
.object({
|
||||
nodeName: z.string().nullish(),
|
||||
resourceType: z.literal([RESOURCE_TYPE_WEBSITE, RESOURCE_TYPE_CERTIFICATE], t("workflow_node.deploy.form.shared_resource_type.placeholder")),
|
||||
websiteMatchPattern: z.string().nullish(),
|
||||
websiteId: z.union([z.string(), z.number()]).nullish(),
|
||||
certificateId: z.union([z.string(), z.number()]).nullish(),
|
||||
})
|
||||
@ -98,12 +129,26 @@ const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> })
|
||||
switch (values.resourceType) {
|
||||
case RESOURCE_TYPE_WEBSITE:
|
||||
{
|
||||
const res = z.preprocess((v) => Number(v), z.number().int().positive()).safeParse(values.websiteId);
|
||||
if (!res.success) {
|
||||
if (values.websiteMatchPattern) {
|
||||
switch (values.websiteMatchPattern) {
|
||||
case WEBSITE_MATCH_PATTERN_SPECIFIED:
|
||||
{
|
||||
const res = z.preprocess((v) => Number(v), z.number().int().positive()).safeParse(values.websiteId);
|
||||
if (!res.success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.deploy.form.1panel_site_website_id.placeholder"),
|
||||
path: ["websiteId"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: t("workflow_node.deploy.form.1panel_site_website_id.placeholder"),
|
||||
path: ["websiteId"],
|
||||
message: t("workflow_node.deploy.form.1panel_site_website_match_pattern.placeholder"),
|
||||
path: ["websiteMatchPattern"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,15 +203,20 @@
|
||||
"workflow_node.deploy.form.1panel_site_node_name.placeholder": "Please enter 1Panel node name",
|
||||
"workflow_node.deploy.form.1panel_site_node_name.help": "Notes: It is only used for 1Panel v2+.",
|
||||
"workflow_node.deploy.form.1panel_site_node_name.tooltip": "You can find it on 1Panel dashboard.",
|
||||
"workflow_node.deploy.form.aliyun_alb_region.label": "Alibaba Cloud region",
|
||||
"workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "Website",
|
||||
"workflow_node.deploy.form.1panel_site_resource_type.option.certificate.label": "Certificate",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.label": "Website match pattern",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.placeholder": "Please select website match pattern",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.option.specified.label": "Specified ID",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.option.certsan.label": "via Certificate",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.help_certsan": "Notes: The website name should be a domain name and include SSL configurations.",
|
||||
"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 dashboard.",
|
||||
"workflow_node.deploy.form.1panel_site_certificate_id.label": "1Panel certificate ID",
|
||||
"workflow_node.deploy.form.1panel_site_certificate_id.placeholder": "Please enter 1Panel certificate ID",
|
||||
"workflow_node.deploy.form.1panel_site_certificate_id.tooltip": "You can find it on 1Panel dashboard.",
|
||||
"workflow_node.deploy.form.aliyun_alb_region.label": "Alibaba Cloud region",
|
||||
"workflow_node.deploy.form.aliyun_alb_region.placeholder": "Please enter Alibaba Cloud ALB region (e.g. cn-hangzhou)",
|
||||
"workflow_node.deploy.form.aliyun_alb_region.tooltip": "For more information, see <a href=\"https://www.alibabacloud.com/help/en/slb/application-load-balancer/product-overview/supported-regions-and-zones\" target=\"_blank\">https://www.alibabacloud.com/help/en/slb/application-load-balancer/product-overview/supported-regions-and-zones</a>",
|
||||
"workflow_node.deploy.form.aliyun_alb_resource_type.option.loadbalancer.label": "ALB load balancer",
|
||||
|
||||
@ -204,6 +204,11 @@
|
||||
"workflow_node.deploy.form.1panel_site_node_name.tooltip": "请登录 1Panel 面板查看",
|
||||
"workflow_node.deploy.form.1panel_site_resource_type.option.website.label": "部署到指定网站",
|
||||
"workflow_node.deploy.form.1panel_site_resource_type.option.certificate.label": "替换指定证书",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.label": "网站匹配模式",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.placeholder": "请选择部署网站匹配模式",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.option.specified.label": "指定 ID",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.option.certsan.label": "根据证书自动匹配",
|
||||
"workflow_node.deploy.form.1panel_site_website_match_pattern.help_certsan": "注意:网站名称需要为域名、且包含开启了 SSL 的域名配置。",
|
||||
"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 面板查看",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user