mirror of
https://github.com/certimate-go/certimate.git
synced 2026-07-20 21:01:41 +08:00
Merge db6a4dfcbe into 240de250ab
This commit is contained in:
commit
f9f41e317f
30
internal/certmgmt/deployers/sp_f5_bigip.go
Normal file
30
internal/certmgmt/deployers/sp_f5_bigip.go
Normal file
@ -0,0 +1,30 @@
|
||||
package deployers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/f5bigip"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Registries.MustRegister(domain.DeploymentProviderTypeF5BigIP, func(options *ProviderFactoryOptions) (core.Deployer, error) {
|
||||
credentials := domain.AccessConfigForF5BigIP{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{
|
||||
ServerUrl: credentials.ServerUrl,
|
||||
Username: credentials.Username,
|
||||
Password: credentials.Password,
|
||||
AllowInsecureConnections: credentials.AllowInsecureConnections,
|
||||
DeployTarget: xmaps.GetString(options.ProviderExtendedConfig, "deployTarget"),
|
||||
Partition: xmaps.GetString(options.ProviderExtendedConfig, "partition"),
|
||||
ClientSSLProfileName: xmaps.GetString(options.ProviderExtendedConfig, "clientSSLProfileName"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
@ -267,6 +267,13 @@ type AccessConfigForEmail struct {
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForF5BigIP struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForFlexCDN struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
ApiRole string `json:"apiRole"`
|
||||
|
||||
@ -60,6 +60,7 @@ const (
|
||||
AccessProviderTypeDynu = AccessProviderType("dynu")
|
||||
AccessProviderTypeDynv6 = AccessProviderType("dynv6")
|
||||
AccessProviderTypeEmail = AccessProviderType("email")
|
||||
AccessProviderTypeF5BigIP = AccessProviderType("f5bigip")
|
||||
AccessProviderTypeFastly = AccessProviderType("fastly") // Fastly(预留)
|
||||
AccessProviderTypeFlexCDN = AccessProviderType("flexcdn")
|
||||
AccessProviderTypeFlyIO = AccessProviderType("flyio")
|
||||
@ -375,6 +376,7 @@ const (
|
||||
DeploymentProviderTypeDigitalOceanCertificate = DeploymentProviderType(AccessProviderTypeDigitalOcean + "-certificate")
|
||||
DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn")
|
||||
DeploymentProviderTypeDokploy = DeploymentProviderType(AccessProviderTypeDokploy)
|
||||
DeploymentProviderTypeF5BigIP = DeploymentProviderType(AccessProviderTypeF5BigIP)
|
||||
DeploymentProviderTypeFlexCDN = DeploymentProviderType(AccessProviderTypeFlexCDN)
|
||||
DeploymentProviderTypeFlyIO = DeploymentProviderType(AccessProviderTypeFlyIO)
|
||||
DeploymentProviderTypeFTP = DeploymentProviderType(AccessProviderTypeFTP)
|
||||
|
||||
6
pkg/core/deployer/providers/f5bigip/consts.go
Normal file
6
pkg/core/deployer/providers/f5bigip/consts.go
Normal file
@ -0,0 +1,6 @@
|
||||
package f5bigip
|
||||
|
||||
const (
|
||||
DEPLOY_TARGET_CERTIFICATE = "certificate"
|
||||
DEPLOY_TARGET_CLIENTSSL = "clientssl"
|
||||
)
|
||||
200
pkg/core/deployer/providers/f5bigip/f5bigip.go
Normal file
200
pkg/core/deployer/providers/f5bigip/f5bigip.go
Normal file
@ -0,0 +1,200 @@
|
||||
package f5bigip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
f5sdk "github.com/certimate-go/certimate/pkg/sdk3rd/f5bigip"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
)
|
||||
|
||||
var nonAlphaNumericRegexp = regexp.MustCompile(`[^a-zA-Z0-9_-]`)
|
||||
|
||||
type (
|
||||
Provider = core.Deployer
|
||||
DeployResult = core.DeployerDeployResult
|
||||
)
|
||||
|
||||
type DeployerConfig struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
DeployTarget string `json:"deployTarget"`
|
||||
Partition string `json:"partition,omitempty"`
|
||||
ClientSSLProfileName string `json:"clientSSLProfileName,omitempty"`
|
||||
}
|
||||
|
||||
type Deployer struct {
|
||||
config *DeployerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *f5sdk.Client
|
||||
}
|
||||
|
||||
var _ Provider = (*Deployer)(nil)
|
||||
|
||||
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
|
||||
}
|
||||
|
||||
client, err := createSDKClient(config.ServerUrl, config.AllowInsecureConnections)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
return &Deployer{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Deployer) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
d.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
d.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) {
|
||||
partition := d.config.Partition
|
||||
if partition == "" {
|
||||
partition = "Common"
|
||||
}
|
||||
|
||||
if err := d.sdkClient.Login(ctx, d.config.Username, d.config.Password); err != nil {
|
||||
return nil, fmt.Errorf("failed to login to F5 Big-IP: %w", err)
|
||||
}
|
||||
|
||||
certX509, err := xcert.ParseCertificateFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %w", err)
|
||||
}
|
||||
|
||||
leafCertPEM, chainCertPEM, err := xcert.ExtractCertificatesFromPEM(certPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract certificates from PEM: %w", err)
|
||||
}
|
||||
|
||||
certName := buildCertName(certX509)
|
||||
d.logger.Info("deploying certificate to F5 Big-IP", slog.String("certName", certName), slog.String("subject", certX509.Subject.CommonName))
|
||||
|
||||
if err := d.sdkClient.UploadCertificate(ctx, certName, partition, leafCertPEM); err != nil {
|
||||
return nil, fmt.Errorf("failed to upload certificate: %w", err)
|
||||
}
|
||||
d.logger.Info("certificate uploaded", slog.String("certName", certName))
|
||||
|
||||
if err := d.sdkClient.UploadKey(ctx, certName, partition, privkeyPEM); err != nil {
|
||||
return nil, fmt.Errorf("failed to upload private key: %w", err)
|
||||
}
|
||||
d.logger.Info("private key uploaded", slog.String("keyName", certName))
|
||||
|
||||
var chainPath string
|
||||
if chainCertPEM != "" {
|
||||
chainName := certName + "-ca"
|
||||
if err := d.sdkClient.UploadCertificate(ctx, chainName, partition, chainCertPEM); err != nil {
|
||||
return nil, fmt.Errorf("failed to upload chain certificate: %w", err)
|
||||
}
|
||||
d.logger.Info("chain certificate uploaded", slog.String("chainName", chainName))
|
||||
chainPath = fmt.Sprintf("/%s/%s", partition, chainName)
|
||||
}
|
||||
|
||||
switch d.config.DeployTarget {
|
||||
case "", DEPLOY_TARGET_CERTIFICATE:
|
||||
d.logger.Info("deployment completed (certificate only)")
|
||||
|
||||
case DEPLOY_TARGET_CLIENTSSL:
|
||||
if d.config.ClientSSLProfileName == "" {
|
||||
return nil, fmt.Errorf("config `clientSSLProfileName` is required when deploy target is 'clientssl'")
|
||||
}
|
||||
|
||||
certPath := fmt.Sprintf("/%s/%s", partition, certName)
|
||||
keyPath := fmt.Sprintf("/%s/%s", partition, certName)
|
||||
|
||||
_, err := d.sdkClient.GetClientSSLProfile(ctx, d.config.ClientSSLProfileName, partition)
|
||||
if err != nil {
|
||||
if errors.Is(err, f5sdk.ErrNotFound) {
|
||||
d.logger.Info("client-ssl profile not found, creating it", slog.String("profile", d.config.ClientSSLProfileName))
|
||||
if err := d.sdkClient.CreateClientSSLProfile(ctx, d.config.ClientSSLProfileName, partition, certPath, keyPath, chainPath); err != nil {
|
||||
return nil, fmt.Errorf("failed to create client-ssl profile: %w", err)
|
||||
}
|
||||
d.logger.Info("client-ssl profile created", slog.String("profile", d.config.ClientSSLProfileName))
|
||||
} else {
|
||||
return nil, fmt.Errorf("failed to get client-ssl profile: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err := d.sdkClient.UpdateClientSSLProfile(ctx, d.config.ClientSSLProfileName, partition, certPath, keyPath, chainPath); err != nil {
|
||||
return nil, fmt.Errorf("failed to update client-ssl profile: %w", err)
|
||||
}
|
||||
d.logger.Info("client-ssl profile updated", slog.String("profile", d.config.ClientSSLProfileName))
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported deploy target '%s'", d.config.DeployTarget)
|
||||
}
|
||||
|
||||
return &DeployResult{}, nil
|
||||
}
|
||||
|
||||
// buildCertName generates a unique object name for F5 BIG-IP ssl-cert and ssl-key uploads.
|
||||
//
|
||||
// Naming pattern: certimate_<san>_<issuer>_<hash>
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// certimate_example.com_letsencrypt_a1b2c3d4 (SAN: example.com, CA: Let's Encrypt)
|
||||
// certimate_wildcard.example.com_letsencrypt_e5f6g7h8 (SAN: *.example.com, CA: Let's Encrypt)
|
||||
// certimate_example.com_zerossl_i9j0k1l2 (SAN: example.com, CA: ZeroSSL)
|
||||
// certimate_example.com_googletrust_m3n4o5p6 (SAN: example.com, CA: Google Trust Services)
|
||||
func buildCertName(cert *x509.Certificate) string {
|
||||
san := ""
|
||||
if len(cert.DNSNames) > 0 {
|
||||
san = cert.DNSNames[0]
|
||||
} else if cert.Subject.CommonName != "" {
|
||||
san = cert.Subject.CommonName
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
|
||||
san = strings.Replace(san, "*", "wildcard", 1)
|
||||
|
||||
issuerName := ""
|
||||
if len(cert.Issuer.Organization) > 0 {
|
||||
issuerName = cert.Issuer.Organization[0]
|
||||
} else {
|
||||
issuerName = cert.Issuer.CommonName
|
||||
}
|
||||
issuerName = nonAlphaNumericRegexp.ReplaceAllString(issuerName, "")
|
||||
if issuerName == "" {
|
||||
issuerName = "unknown"
|
||||
}
|
||||
|
||||
fingerprint := sha1.Sum(cert.Raw)
|
||||
shortHash := hex.EncodeToString(fingerprint[:4])
|
||||
|
||||
return fmt.Sprintf("certimate_%s_%s_%s", san, strings.ToLower(issuerName), shortHash)
|
||||
}
|
||||
|
||||
func createSDKClient(serverUrl string, skipTlsVerify bool) (*f5sdk.Client, error) {
|
||||
client, err := f5sdk.NewClient(serverUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if skipTlsVerify {
|
||||
client.SetTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
53
pkg/core/deployer/providers/f5bigip/f5bigip_test.go
Normal file
53
pkg/core/deployer/providers/f5bigip/f5bigip_test.go
Normal file
@ -0,0 +1,53 @@
|
||||
package f5bigip_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core/deployer/internal/tester"
|
||||
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/f5bigip"
|
||||
)
|
||||
|
||||
var (
|
||||
fp = tester.Args("F5BIGIP_")
|
||||
fTestCertPath string
|
||||
fTestKeyPath string
|
||||
fServerUrl string
|
||||
fUsername string
|
||||
fPassword string
|
||||
fPartition string
|
||||
fClientSSLName string
|
||||
fDeployTarget string
|
||||
)
|
||||
|
||||
func init() {
|
||||
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
|
||||
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
|
||||
fp.DefineString(&fServerUrl, "SERVERURL")
|
||||
fp.DefineString(&fUsername, "USERNAME")
|
||||
fp.DefineString(&fPassword, "PASSWORD")
|
||||
fp.DefineString(&fPartition, "PARTITION")
|
||||
fp.DefineString(&fClientSSLName, "CLIENTSSLNAME")
|
||||
fp.DefineString(&fDeployTarget, "DEPLOYTARGET")
|
||||
}
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
fp.Parse()
|
||||
|
||||
t.Run("Deploy", func(t *testing.T) {
|
||||
provider, err := impl.NewDeployer(&impl.DeployerConfig{
|
||||
ServerUrl: fServerUrl,
|
||||
Username: fUsername,
|
||||
Password: fPassword,
|
||||
AllowInsecureConnections: true,
|
||||
DeployTarget: fDeployTarget,
|
||||
Partition: fPartition,
|
||||
ClientSSLProfileName: fClientSSLName,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
|
||||
})
|
||||
}
|
||||
258
pkg/sdk3rd/f5bigip/client.go
Normal file
258
pkg/sdk3rd/f5bigip/client.go
Normal file
@ -0,0 +1,258 @@
|
||||
package f5bigip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/app"
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("not found")
|
||||
|
||||
type Client struct {
|
||||
baseUrl string
|
||||
token string
|
||||
rc *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(serverUrl string) (*Client, error) {
|
||||
if serverUrl == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset serverUrl")
|
||||
}
|
||||
if _, err := url.Parse(serverUrl); err != nil {
|
||||
return nil, fmt.Errorf("sdkerr: invalid serverUrl: %w", err)
|
||||
}
|
||||
|
||||
c := &Client{
|
||||
baseUrl: strings.TrimSuffix(serverUrl, "/"),
|
||||
}
|
||||
|
||||
c.rc = resty.New().
|
||||
SetBaseURL(c.baseUrl).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
SetPreRequestHook(func(_ *resty.Client, req *http.Request) error {
|
||||
if c.token != "" {
|
||||
req.Header.Set("X-F5-Auth-Token", c.token)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
c.rc.SetTimeout(timeout)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) SetTLSConfig(config *tls.Config) *Client {
|
||||
c.rc.SetTLSClientConfig(config)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) Login(ctx context.Context, username, password string) error {
|
||||
loginReq := map[string]string{
|
||||
"username": username,
|
||||
"password": password,
|
||||
"loginProviderName": "tmos",
|
||||
}
|
||||
|
||||
httpreq, err := c.newRequest(http.MethodPost, "/mgmt/shared/authn/login")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpreq.SetBody(loginReq)
|
||||
httpreq.SetContext(ctx)
|
||||
|
||||
type loginResponse struct {
|
||||
Token struct {
|
||||
Token string `json:"token"`
|
||||
} `json:"token"`
|
||||
}
|
||||
|
||||
result := &loginResponse{}
|
||||
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.token = result.Token.Token
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) UploadCertificate(ctx context.Context, name, partition, content string) error {
|
||||
path := fmt.Sprintf("/mgmt/tm/sys/file/ssl-cert")
|
||||
body := map[string]string{
|
||||
"name": name,
|
||||
"partition": partition,
|
||||
"content": content,
|
||||
}
|
||||
|
||||
httpreq, err := c.newRequest(http.MethodPost, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpreq.SetBody(body)
|
||||
httpreq.SetContext(ctx)
|
||||
|
||||
if _, err := c.doRequest(httpreq); err != nil {
|
||||
return fmt.Errorf("failed to upload certificate: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) UploadKey(ctx context.Context, name, partition, content string) error {
|
||||
path := fmt.Sprintf("/mgmt/tm/sys/file/ssl-key")
|
||||
body := map[string]string{
|
||||
"name": name,
|
||||
"partition": partition,
|
||||
"content": content,
|
||||
}
|
||||
|
||||
httpreq, err := c.newRequest(http.MethodPost, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpreq.SetBody(body)
|
||||
httpreq.SetContext(ctx)
|
||||
|
||||
if _, err := c.doRequest(httpreq); err != nil {
|
||||
return fmt.Errorf("failed to upload key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) GetClientSSLProfile(ctx context.Context, name, partition string) (map[string]any, error) {
|
||||
path := fmt.Sprintf("/mgmt/tm/ltm/profile/client-ssl/~%s~%s", url.PathEscape(partition), url.PathEscape(name))
|
||||
|
||||
httpreq, err := c.newRequest(http.MethodGet, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpreq.SetContext(ctx)
|
||||
|
||||
result := make(map[string]any)
|
||||
resp, err := c.doRequestWithResult(httpreq, &result)
|
||||
if err != nil {
|
||||
if resp != nil && resp.StatusCode() == http.StatusNotFound {
|
||||
return nil, fmt.Errorf("failed to get client-ssl profile: %w", ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get client-ssl profile: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) CreateClientSSLProfile(ctx context.Context, name, partition, certPath, keyPath, chainPath string) error {
|
||||
path := fmt.Sprintf("/mgmt/tm/ltm/profile/client-ssl")
|
||||
body := map[string]string{
|
||||
"name": name,
|
||||
"partition": partition,
|
||||
"defaultsFrom": "clientssl",
|
||||
"cert": certPath,
|
||||
"key": keyPath,
|
||||
}
|
||||
if chainPath != "" {
|
||||
body["chain"] = chainPath
|
||||
}
|
||||
|
||||
httpreq, err := c.newRequest(http.MethodPost, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpreq.SetBody(body)
|
||||
httpreq.SetContext(ctx)
|
||||
|
||||
if _, err := c.doRequest(httpreq); err != nil {
|
||||
return fmt.Errorf("failed to create client-ssl profile: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateClientSSLProfile(ctx context.Context, name, partition, certPath, keyPath, chainPath string) error {
|
||||
path := fmt.Sprintf("/mgmt/tm/ltm/profile/client-ssl/~%s~%s", url.PathEscape(partition), url.PathEscape(name))
|
||||
body := map[string]string{
|
||||
"cert": certPath,
|
||||
"key": keyPath,
|
||||
}
|
||||
if chainPath != "" {
|
||||
body["chain"] = chainPath
|
||||
}
|
||||
|
||||
httpreq, err := c.newRequest(http.MethodPatch, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpreq.SetBody(body)
|
||||
httpreq.SetContext(ctx)
|
||||
|
||||
if _, err := c.doRequest(httpreq); err != nil {
|
||||
return fmt.Errorf("failed to update client-ssl profile: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) newRequest(method string, path string) (*resty.Request, error) {
|
||||
if method == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset method")
|
||||
}
|
||||
if path == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset path")
|
||||
}
|
||||
|
||||
req := c.rc.R()
|
||||
req.Method = method
|
||||
req.URL = path
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) doRequest(req *resty.Request) (*resty.Response, error) {
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("sdkerr: nil request")
|
||||
}
|
||||
|
||||
resp, err := req.Send()
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("sdkerr: failed to send request: %w", err)
|
||||
} else if resp.IsError() {
|
||||
return resp, fmt.Errorf("sdkerr: unexpected status code: %d (resp: %s)", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) doRequestWithResult(req *resty.Request, res interface{}) (*resty.Response, error) {
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("sdkerr: nil request")
|
||||
}
|
||||
|
||||
resp, err := c.doRequest(req)
|
||||
if err != nil {
|
||||
if resp != nil {
|
||||
json.Unmarshal(resp.Body(), &res)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if len(resp.Body()) != 0 {
|
||||
if err := json.Unmarshal(resp.Body(), &res); err != nil {
|
||||
return resp, fmt.Errorf("sdkerr: failed to unmarshal response: %w (resp: %s)", err, resp.String())
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
6
ui/public/imgs/providers/f5bigip.svg
Normal file
6
ui/public/imgs/providers/f5bigip.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
|
||||
<path d="M239.243 238.06 L241.11 238.06 L241.11 233.896 L242.585 233.896 C243.566 233.896 244.264 233.992 244.661 234.242 C245.319 234.633 245.654 235.445 245.654 236.664 L245.654 237.515 L245.694 237.839 C245.716 237.901 245.716 237.93 245.722 237.964 C245.734 238.02 245.739 238.037 245.768 238.06 L247.498 238.06 L247.435 237.941 C247.384 237.856 247.356 237.68 247.35 237.396 C247.328 237.101 247.328 236.846 247.328 236.608 L247.328 235.819 C247.328 235.286 247.129 234.747 246.743 234.174 C246.352 233.612 245.739 233.278 244.905 233.136 C245.563 233.028 246.085 232.846 246.437 232.636 C247.146 232.188 247.475 231.468 247.475 230.537 C247.475 229.21 246.948 228.297 245.836 227.848 C245.223 227.604 244.264 227.468 242.942 227.468 L239.243 227.468 L239.243 238.06 Z M244.559 232.353 C244.179 232.512 243.583 232.591 242.783 232.591 L241.11 232.591 L241.11 228.733 L242.704 228.733 C243.737 228.733 244.491 228.887 244.951 229.142 C245.427 229.414 245.654 229.925 245.654 230.691 C245.654 231.513 245.297 232.046 244.559 232.353 Z M249.983 225.988 C248.082 224.11 245.807 223.179 243.118 223.179 C240.486 223.179 238.2 224.11 236.333 225.988 C234.438 227.865 233.514 230.146 233.514 232.818 C233.514 235.49 234.427 237.776 236.299 239.648 C238.171 241.549 240.463 242.491 243.118 242.491 C245.807 242.491 248.082 241.549 249.983 239.648 C251.849 237.765 252.814 235.473 252.814 232.818 C252.814 230.135 251.849 227.865 249.983 225.988 Z M249.007 226.946 C250.635 228.535 251.424 230.515 251.424 232.818 C251.424 235.104 250.635 237.096 249.007 238.724 C247.396 240.341 245.433 241.169 243.118 241.169 C240.826 241.169 238.863 240.341 237.241 238.724 C235.652 237.096 234.835 235.104 234.835 232.818 C234.835 230.515 235.652 228.535 237.241 226.946 C238.892 225.29 240.843 224.479 243.118 224.479 C245.427 224.479 247.384 225.29 249.007 226.946 Z" fill="#E6333C"/>
|
||||
<path d="M245.404 158.797 C246.091 145.147 243.566 130.431 231.687 117.122 C219.478 103.96 198.873 92.795 156.495 90.186 C158.696 83.321 160.625 77.024 162.65 70.596 C187.992 71.487 210.537 73.314 229.735 75.804 C231.256 68.929 232.072 62.212 233.644 55.727 C231.687 52.874 229.616 50.105 227.443 47.422 C218.315 46.366 209.215 44.086 199.424 42.838 C186.182 41.13 172.312 39.905 157.34 39.218 C147.906 67.312 136.117 102.655 124.022 139.185 C187.067 144.642 211.309 161.627 209.947 185.596 C208.693 198.537 196.854 210.309 181.292 211.699 C162.786 212.947 154.305 205.469 148.746 197.26 C143.85 189.891 138.959 182.516 133.74 174.471 C132.322 172.106 130.45 173.637 128.765 175.288 C124.947 178.999 121.316 182.499 117.543 186.107 C115.115 188.223 115.654 190.039 116.511 191.803 C120.034 200.114 123.319 207.733 126.632 215.295 C132.157 218.676 158.095 223.294 177.497 221.842 C190.647 220.736 207.088 215.653 220.675 205.418 C234.103 195.02 243.884 180.984 245.404 158.797 Z" fill="#FEFEFE"/>
|
||||
<path d="M24.914 203.855 C27.342 207.145 29.917 210.317 32.646 213.357 C54.454 217.277 81.645 219.989 109.971 220.818 C109.874 217.879 109.789 215.025 109.721 211.996 C92.327 211.014 84.334 208.365 82.944 204.921 C81.781 202.187 81.582 198.102 81.333 194.069 C80.028 167.354 79.597 138.461 80.051 109.46 C89.791 109.289 99.464 109.21 109.625 109.091 C114.549 106.85 119.184 104.626 124.103 102.431 C124.103 98.919 124.125 95.776 124.125 92.349 C109.052 92.418 94.767 92.656 80.442 93.07 C80.845 80.878 81.327 69.623 82.042 58.645 C82.558 51.429 87.551 46.159 92.702 45.745 C100.962 45.421 108.377 48.7 115.553 51.894 C119.513 53.812 123.416 55.718 127.467 57.794 C129.401 58.322 131.705 58.765 133.089 57.08 C135.494 54.232 137.78 51.588 140.14 48.82 C141.423 46.93 140.867 45.847 140.345 45.149 C135.194 41.081 130.343 37.422 125.441 33.746 C122.412 31.687 117.63 31.25 112.921 31.25 C111.037 31.25 109.154 31.318 107.401 31.375 C102.562 31.63 96.389 32.402 86.297 34.858 C63.559 40.979 36.265 56.836 33.384 82.756 C33.015 86.988 32.703 91.181 32.425 95.504 C26.008 96.048 20.165 96.514 14.645 97.041 C14.242 101.999 13.97 106.759 13.743 111.786 C19.331 111.457 25.157 111.167 31.648 110.884 C30.689 138.058 31.596 165.187 34.149 190.296 C34.581 194.114 34.995 197.898 34.495 200.343 C34.093 202.681 30.337 203.895 24.914 203.855 Z" fill="#FEFEFE"/>
|
||||
<path d="M233.648 55.727 C232.076 62.212 231.254 68.929 229.739 75.804 C210.541 73.314 187.991 71.487 162.649 70.596 C160.623 77.024 158.694 83.321 156.493 90.186 C198.872 92.795 219.476 103.96 231.685 117.122 C243.564 130.431 246.089 145.147 245.403 158.797 C243.888 180.984 234.102 195.02 220.673 205.418 C207.086 215.653 190.646 220.736 177.501 221.842 C158.099 223.294 132.156 218.676 126.636 215.295 C123.317 207.733 120.038 200.114 116.515 191.803 C115.658 190.039 115.113 188.217 117.542 186.107 C121.314 182.499 124.945 178.999 128.763 175.288 C130.454 173.637 132.326 172.106 133.744 174.471 C138.963 182.516 143.848 189.891 148.744 197.26 C154.309 205.469 162.785 212.947 181.291 211.699 C196.858 210.309 208.697 198.537 209.946 185.596 C211.307 161.627 187.066 144.642 124.02 139.185 C136.115 102.655 147.91 67.312 157.339 39.218 C172.31 39.905 186.181 41.13 199.422 42.838 C209.214 44.086 218.313 46.366 227.441 47.422 C203.977 18.5 168.163 0 128.014 0 C57.31 0 0 57.304 0 127.997 C0 156.397 9.264 182.624 24.916 203.853 C30.334 203.892 34.09 202.678 34.498 200.341 C34.992 197.901 34.578 194.117 34.147 190.299 C31.599 165.185 30.686 138.061 31.65 110.881 C25.16 111.165 19.334 111.46 13.746 111.783 C13.973 106.757 14.24 101.997 14.642 97.039 C20.162 96.511 26.006 96.046 32.428 95.507 C32.706 91.179 33.012 86.986 33.386 82.754 C36.268 56.839 63.562 40.977 86.3 34.861 C96.386 32.405 102.559 31.628 107.404 31.372 C109.151 31.316 111.04 31.248 112.918 31.248 C117.632 31.248 122.415 31.69 125.444 33.744 C130.346 37.42 135.196 41.079 140.348 45.147 C140.869 45.845 141.42 46.928 140.143 48.823 C137.778 51.591 135.491 54.235 133.086 57.083 C131.702 58.762 129.404 58.32 127.47 57.798 C123.413 55.721 119.51 53.815 115.556 51.892 C108.374 48.704 100.959 45.425 92.699 45.742 C87.548 46.157 82.561 51.433 82.045 58.643 C81.324 69.621 80.848 80.876 80.445 93.068 C94.77 92.659 109.055 92.421 124.122 92.347 C124.122 95.779 124.1 98.922 124.1 102.428 C119.187 104.63 114.552 106.848 109.628 109.094 C99.461 109.208 89.789 109.287 80.048 109.457 C79.6 138.458 80.025 167.357 81.33 194.072 C81.58 198.106 81.784 202.185 82.947 204.925 C84.337 208.363 92.33 211.012 109.724 211.994 C109.786 215.023 109.872 217.877 109.968 220.821 C81.648 219.987 54.451 217.281 32.643 213.355 C56.085 239.52 90.118 256 128.014 256 C198.713 256 256 198.69 256 127.997 C256 101.18 247.74 76.292 233.648 55.727 Z" fill="#E6333C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
@ -48,6 +48,7 @@ import AccessConfigFieldsProviderDynadot from "./AccessConfigFieldsProviderDynad
|
||||
import AccessConfigFieldsProviderDynu from "./AccessConfigFieldsProviderDynu";
|
||||
import AccessConfigFieldsProviderDynv6 from "./AccessConfigFieldsProviderDynv6";
|
||||
import AccessConfigFieldsProviderEmail from "./AccessConfigFieldsProviderEmail";
|
||||
import AccessConfigFieldsProviderF5BigIP from "./AccessConfigFieldsProviderF5BigIP";
|
||||
import AccessConfigFieldsProviderFlexCDN from "./AccessConfigFieldsProviderFlexCDN";
|
||||
import AccessConfigFieldsProviderFlyIO from "./AccessConfigFieldsProviderFlyIO";
|
||||
import AccessConfigFieldsProviderFTP from "./AccessConfigFieldsProviderFTP";
|
||||
@ -174,6 +175,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
|
||||
[ACCESS_PROVIDERS.DYNU]: AccessConfigFieldsProviderDynu,
|
||||
[ACCESS_PROVIDERS.DYNV6]: AccessConfigFieldsProviderDynv6,
|
||||
[ACCESS_PROVIDERS.EMAIL]: AccessConfigFieldsProviderEmail,
|
||||
[ACCESS_PROVIDERS.F5BIGIP]: AccessConfigFieldsProviderF5BigIP,
|
||||
[ACCESS_PROVIDERS.FLEXCDN]: AccessConfigFieldsProviderFlexCDN,
|
||||
[ACCESS_PROVIDERS.FLYIO]: AccessConfigFieldsProviderFlyIO,
|
||||
[ACCESS_PROVIDERS.FTP]: AccessConfigFieldsProviderFTP,
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input, Switch } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFormFieldsProviderF5BigIP = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item name={[parentNamePath, "serverUrl"]} initialValue={initialValues.serverUrl} label={t("access.form.f5bigip_server_url.label")} rules={[formRule]}>
|
||||
<Input type="url" placeholder={t("access.form.f5bigip_server_url.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "username"]}
|
||||
initialValue={initialValues.username}
|
||||
label={t("access.form.f5bigip_username.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.f5bigip_username.tooltip") }}></span>}
|
||||
>
|
||||
<Input allowClear placeholder={t("access.form.f5bigip_username.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "password"]}
|
||||
initialValue={initialValues.password}
|
||||
label={t("access.form.f5bigip_password.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.f5bigip_password.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.f5bigip_password.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "allowInsecureConnections"]}
|
||||
initialValue={initialValues.allowInsecureConnections}
|
||||
label={t("access.form.shared_allow_insecure_conns.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
serverUrl: "https://<your-bigip-host>",
|
||||
username: "",
|
||||
password: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t: _ } = i18n;
|
||||
|
||||
return z.object({
|
||||
serverUrl: z.url({ protocol: z.core.regexes.httpProtocol }),
|
||||
username: z.string().nonempty(),
|
||||
password: z.string().nonempty(),
|
||||
allowInsecureConnections: z.boolean().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderF5BigIP, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -61,6 +61,7 @@ import BizDeployNodeConfigFieldsProviderCTCCCloudFaaS from "./BizDeployNodeConfi
|
||||
import BizDeployNodeConfigFieldsProviderCTCCCloudICDN from "./BizDeployNodeConfigFieldsProviderCTCCCloudICDN";
|
||||
import BizDeployNodeConfigFieldsProviderCTCCCloudLVDN from "./BizDeployNodeConfigFieldsProviderCTCCCloudLVDN";
|
||||
import BizDeployNodeConfigFieldsProviderDogeCloudCDN from "./BizDeployNodeConfigFieldsProviderDogeCloudCDN";
|
||||
import BizDeployNodeConfigFieldsProviderF5BigIP from "./BizDeployNodeConfigFieldsProviderF5BigIP";
|
||||
import BizDeployNodeConfigFieldsProviderFlexCDN from "./BizDeployNodeConfigFieldsProviderFlexCDN";
|
||||
import BizDeployNodeConfigFieldsProviderFlyIO from "./BizDeployNodeConfigFieldsProviderFlyIO";
|
||||
import BizDeployNodeConfigFieldsProviderFTP from "./BizDeployNodeConfigFieldsProviderFTP";
|
||||
@ -211,6 +212,7 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
|
||||
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN]: BizDeployNodeConfigFieldsProviderCTCCCloudICDN,
|
||||
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_LVDN]: BizDeployNodeConfigFieldsProviderCTCCCloudLVDN,
|
||||
[DEPLOYMENT_PROVIDERS.DOGECLOUD_CDN]: BizDeployNodeConfigFieldsProviderDogeCloudCDN,
|
||||
[DEPLOYMENT_PROVIDERS.F5BIGIP]: BizDeployNodeConfigFieldsProviderF5BigIP,
|
||||
[DEPLOYMENT_PROVIDERS.FLEXCDN]: BizDeployNodeConfigFieldsProviderFlexCDN,
|
||||
[DEPLOYMENT_PROVIDERS.FLYIO]: BizDeployNodeConfigFieldsProviderFlyIO,
|
||||
[DEPLOYMENT_PROVIDERS.FTP]: BizDeployNodeConfigFieldsProviderFTP,
|
||||
|
||||
@ -0,0 +1,113 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input, Select } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import Tips from "@/components/Tips";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const DEPLOY_TARGET_CERTIFICATE = "certificate" as const;
|
||||
const DEPLOY_TARGET_CLIENTSSL = "clientssl" as const;
|
||||
|
||||
const BizDeployNodeConfigFieldsProviderF5BigIP = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const formInst = Form.useFormInstance();
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
const fieldResourceType = Form.useWatch([parentNamePath, "deployTarget"], formInst);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item>
|
||||
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.f5bigip.guide") }}></span>} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "deployTarget"]}
|
||||
initialValue={initialValues.deployTarget}
|
||||
label={t("workflow_node.deploy.form.shared_deploy_target.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Select
|
||||
options={[DEPLOY_TARGET_CERTIFICATE, DEPLOY_TARGET_CLIENTSSL].map((s) => ({
|
||||
label: t(`workflow_node.deploy.form.f5bigip_deploy_target.option.${s}.label`),
|
||||
value: s,
|
||||
}))}
|
||||
placeholder={t("workflow_node.deploy.form.shared_deploy_target.placeholder")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "partition"]}
|
||||
initialValue={initialValues.partition}
|
||||
label={t("workflow_node.deploy.form.f5bigip_partition.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.f5bigip_partition.tooltip") }}></span>}
|
||||
>
|
||||
<Input allowClear placeholder={t("workflow_node.deploy.form.f5bigip_partition.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Show when={fieldResourceType === DEPLOY_TARGET_CLIENTSSL}>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "clientSSLProfileName"]}
|
||||
initialValue={initialValues.clientSSLProfileName}
|
||||
label={t("workflow_node.deploy.form.f5bigip_client_ssl_profile_name.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.f5bigip_client_ssl_profile_name.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.f5bigip_client_ssl_profile_name.placeholder")} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
deployTarget: DEPLOY_TARGET_CERTIFICATE,
|
||||
partition: "Common",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t: _ } = i18n;
|
||||
|
||||
return z
|
||||
.object({
|
||||
deployTarget: z.enum([DEPLOY_TARGET_CERTIFICATE, DEPLOY_TARGET_CLIENTSSL]),
|
||||
partition: z.string().nonempty(),
|
||||
clientSSLProfileName: z.string().nullish(),
|
||||
})
|
||||
.superRefine((values, ctx) => {
|
||||
switch (values.deployTarget) {
|
||||
case DEPLOY_TARGET_CLIENTSSL:
|
||||
{
|
||||
const scClientSSLProfileName = z.string().nonempty();
|
||||
const spClientSSLProfileName = scClientSSLProfileName.safeParse(values.clientSSLProfileName);
|
||||
if (!spClientSSLProfileName.success) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: z.treeifyError(spClientSSLProfileName.error).errors.join(),
|
||||
path: ["clientSSLProfileName"],
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizDeployNodeConfigFieldsProviderF5BigIP, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -61,6 +61,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
DYNU: "dynu",
|
||||
DYNV6: "dynv6",
|
||||
EMAIL: "email",
|
||||
F5BIGIP: "f5bigip",
|
||||
FLEXCDN: "flexcdn",
|
||||
FLYIO: "flyio",
|
||||
FTP: "ftp",
|
||||
@ -212,6 +213,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.BAOTAPANELGO, "provider.baotapanelgo", "/imgs/providers/baota.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.BAOTAWAF, "provider.baotawaf", "/imgs/providers/baota.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.DOKPLOY, "provider.dokploy", "/imgs/providers/dokploy.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.F5BIGIP, "provider.f5bigip", "/imgs/providers/f5bigip.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.CDNFLY, "provider.cdnfly", "/imgs/providers/cdnfly.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.GOEDGE, "provider.goedge", "/imgs/providers/goedge.png", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.KONG, "provider.kong", "/imgs/providers/kong.svg", [ACCESS_USAGES.HOSTING]],
|
||||
@ -665,6 +667,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
|
||||
DOGECLOUD_CDN: `${ACCESS_PROVIDERS.DOGECLOUD}-cdn`,
|
||||
DIGITALOCEAN_CERTIFICATE: `${ACCESS_PROVIDERS.DIGITALOCEAN}-certificate`,
|
||||
DOKPLOY: `${ACCESS_PROVIDERS.DOKPLOY}`,
|
||||
F5BIGIP: `${ACCESS_PROVIDERS.F5BIGIP}`,
|
||||
FLEXCDN: `${ACCESS_PROVIDERS.FLEXCDN}`,
|
||||
FLYIO: `${ACCESS_PROVIDERS.FLYIO}`,
|
||||
FTP: `${ACCESS_PROVIDERS.FTP}`,
|
||||
@ -940,6 +943,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
|
||||
[DEPLOYMENT_PROVIDERS.KONG, "provider.kong", DEPLOYMENT_CATEGORIES.APIGATEWAY],
|
||||
[DEPLOYMENT_PROVIDERS.CPANEL, "provider.cpanel", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.DOKPLOY, "provider.dokploy", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.F5BIGIP, "provider.f5bigip", DEPLOYMENT_CATEGORIES.LOADBALANCE],
|
||||
[DEPLOYMENT_PROVIDERS.NGINXPROXYMANAGER, "provider.nginxproxymanager", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.PROXMOXVE, "provider.proxmoxve", DEPLOYMENT_CATEGORIES.OTHER],
|
||||
[DEPLOYMENT_PROVIDERS.SYNOLOGYDSM, "provider.synologydsm", DEPLOYMENT_CATEGORIES.OTHER],
|
||||
|
||||
@ -629,6 +629,20 @@
|
||||
"placeholder": "Please enter the default receiver email address",
|
||||
"help": "Notes: It can be overrided in the workflows."
|
||||
},
|
||||
"f5bigip_server_url": {
|
||||
"label": "F5 Big-IP server URL",
|
||||
"placeholder": "Please enter F5 Big-IP server URL, e.g. https://192.168.1.100"
|
||||
},
|
||||
"f5bigip_username": {
|
||||
"label": "F5 Big-IP username",
|
||||
"placeholder": "Please enter F5 Big-IP username",
|
||||
"tooltip": "The username used to authenticate with the F5 Big-IP REST API."
|
||||
},
|
||||
"f5bigip_password": {
|
||||
"label": "F5 Big-IP password",
|
||||
"placeholder": "Please enter F5 Big-IP password",
|
||||
"tooltip": "The password used to authenticate with the F5 Big-IP REST API."
|
||||
},
|
||||
"flexcdn_server_url": {
|
||||
"label": "FlexCDN server URL",
|
||||
"placeholder": "Please enter FlexCDN server URL"
|
||||
|
||||
@ -141,6 +141,7 @@
|
||||
"dynu": "Dynu",
|
||||
"dynv6": "dynv6",
|
||||
"email": "Email (SMTP)",
|
||||
"f5bigip": "F5 Big-IP",
|
||||
"fastly": "Fastly",
|
||||
"flexcdn": "FlexCDN",
|
||||
"flyio": "Fly.io",
|
||||
|
||||
@ -1537,6 +1537,29 @@
|
||||
"label": "Doge Cloud CDN domain",
|
||||
"placeholder": "Please enter Doge Cloud CDN domain name"
|
||||
},
|
||||
"f5bigip": {
|
||||
"guide": "This provider deploys the certificate to an F5 Big-IP appliance. You can upload only (certificate) or upload and assign to a client-ssl profile."
|
||||
},
|
||||
"f5bigip_deploy_target": {
|
||||
"option": {
|
||||
"certificate": {
|
||||
"label": "Certificate"
|
||||
},
|
||||
"clientssl": {
|
||||
"label": "Certificate + client-ssl profile"
|
||||
}
|
||||
}
|
||||
},
|
||||
"f5bigip_partition": {
|
||||
"label": "Partition",
|
||||
"placeholder": "Please enter F5 Big-IP partition (default: Common)",
|
||||
"tooltip": "The F5 Big-IP partition where the certificate and key will be stored."
|
||||
},
|
||||
"f5bigip_client_ssl_profile_name": {
|
||||
"label": "Client-SSL profile name",
|
||||
"placeholder": "Please enter client-ssl profile name",
|
||||
"tooltip": "The name of an existing client-ssl profile to update with the new certificate."
|
||||
},
|
||||
"flexcdn_deploy_target": {
|
||||
"option": {
|
||||
"certificate": {
|
||||
|
||||
@ -629,6 +629,20 @@
|
||||
"placeholder": "请输入默认的收件人邮箱",
|
||||
"help": "提示:可在工作流中覆盖此设置。"
|
||||
},
|
||||
"f5bigip_server_url": {
|
||||
"label": "F5 Big-IP 服务地址",
|
||||
"placeholder": "请输入 F5 Big-IP 服务地址,例如 https://192.168.1.100"
|
||||
},
|
||||
"f5bigip_username": {
|
||||
"label": "F5 Big-IP 用户名",
|
||||
"placeholder": "请输入 F5 Big-IP 用户名",
|
||||
"tooltip": "用于 F5 Big-IP 认证的用户名。"
|
||||
},
|
||||
"f5bigip_password": {
|
||||
"label": "F5 Big-IP 密码",
|
||||
"placeholder": "请输入 F5 Big-IP 密码",
|
||||
"tooltip": "用于 F5 Big-IP 认证的密码。"
|
||||
},
|
||||
"flexcdn_server_url": {
|
||||
"label": "FlexCDN 服务地址",
|
||||
"placeholder": "请输入 FlexCDN 服务地址"
|
||||
|
||||
@ -141,6 +141,7 @@
|
||||
"dynu": "Dynu",
|
||||
"dynv6": "dynv6",
|
||||
"email": "邮件(SMTP)",
|
||||
"f5bigip": "F5 Big-IP",
|
||||
"fastly": "Fastly",
|
||||
"flexcdn": "FlexCDN",
|
||||
"flyio": "Fly.io",
|
||||
|
||||
@ -1536,6 +1536,29 @@
|
||||
"label": "多吉云 CDN 加速域名",
|
||||
"placeholder": "请输入多吉云 CDN 加速域名"
|
||||
},
|
||||
"f5bigip": {
|
||||
"guide": "此部署器可将证书部署至 F5 Big-IP 设备。可选择仅上传证书,或上传并关联至已有的 client-ssl 配置。"
|
||||
},
|
||||
"f5bigip_deploy_target": {
|
||||
"option": {
|
||||
"certificate": {
|
||||
"label": "仅上传证书"
|
||||
},
|
||||
"clientssl": {
|
||||
"label": "上传证书并更新 client-ssl 配置"
|
||||
}
|
||||
}
|
||||
},
|
||||
"f5bigip_partition": {
|
||||
"label": "分区",
|
||||
"placeholder": "请输入 F5 Big-IP 分区(默认为 Common)",
|
||||
"tooltip": "证书和私钥将存储在该 F5 Big-IP 分区下。"
|
||||
},
|
||||
"f5bigip_client_ssl_profile_name": {
|
||||
"label": "Client-SSL 配置名称",
|
||||
"placeholder": "请输入 client-ssl 配置名称",
|
||||
"tooltip": "已有的 client-ssl 配置名称,证书上传后将自动关联到此配置。"
|
||||
},
|
||||
"flexcdn_deploy_target": {
|
||||
"option": {
|
||||
"certificate": {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user