mirror of
https://github.com/certimate-go/certimate.git
synced 2026-08-29 02:02:04 +08:00
feat(provider): add Proxmox Backup Server deploy target
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
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/proxmoxbs"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Registries.MustRegister(domain.DeploymentProviderTypeProxmoxBS, func(options *ProviderFactoryOptions) (core.Deployer, error) {
|
||||
credentials := domain.AccessConfigForProxmoxBS{}
|
||||
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,
|
||||
ApiToken: credentials.ApiToken,
|
||||
ApiTokenSecret: credentials.ApiTokenSecret,
|
||||
AllowInsecureConnections: credentials.AllowInsecureConnections,
|
||||
NodeName: xmaps.GetString(options.ProviderExtendedConfig, "nodeName"),
|
||||
AutoRestart: xmaps.GetBool(options.ProviderExtendedConfig, "autoRestart"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
@@ -491,6 +491,13 @@ type AccessConfigForPowerDNS struct {
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForProxmoxBS struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
ApiToken string `json:"apiToken"`
|
||||
ApiTokenSecret string `json:"apiTokenSecret,omitempty"`
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForProxmoxVE struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
ApiToken string `json:"apiToken"`
|
||||
|
||||
@@ -104,6 +104,7 @@ const (
|
||||
AccessProviderTypeOVHcloud = AccessProviderType("ovhcloud")
|
||||
AccessProviderTypePorkbun = AccessProviderType("porkbun")
|
||||
AccessProviderTypePowerDNS = AccessProviderType("powerdns")
|
||||
AccessProviderTypeProxmoxBS = AccessProviderType("proxmoxbs")
|
||||
AccessProviderTypeProxmoxVE = AccessProviderType("proxmoxve")
|
||||
AccessProviderTypeQiniu = AccessProviderType("qiniu")
|
||||
AccessProviderTypeQingCloud = AccessProviderType("qingcloud")
|
||||
@@ -413,6 +414,7 @@ const (
|
||||
DeploymentProviderTypeNetlify = DeploymentProviderType(AccessProviderTypeNetlify)
|
||||
DeploymentProviderTypeNginxProxyManager = DeploymentProviderType(AccessProviderTypeNginxProxyManager)
|
||||
DeploymentProviderTypeOracleCloudCertificatesMgmt = DeploymentProviderType(AccessProviderTypeOracleCloud + "-certificatesmgmt")
|
||||
DeploymentProviderTypeProxmoxBS = DeploymentProviderType(AccessProviderTypeProxmoxBS)
|
||||
DeploymentProviderTypeProxmoxVE = DeploymentProviderType(AccessProviderTypeProxmoxVE)
|
||||
DeploymentProviderTypeQingCloudLB = DeploymentProviderType(AccessProviderTypeQingCloud + "-lb")
|
||||
DeploymentProviderTypeQiniuCDN = DeploymentProviderType(AccessProviderTypeQiniu + "-cdn")
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package proxmoxbs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
pbssdk "github.com/certimate-go/certimate/pkg/sdk3rd/proxmoxbs"
|
||||
)
|
||||
|
||||
type (
|
||||
Provider = core.Deployer
|
||||
DeployResult = core.DeployerDeployResult
|
||||
)
|
||||
|
||||
type DeployerConfig struct {
|
||||
// Proxmox BS 服务地址。
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
// Proxmox BS API Token。
|
||||
ApiToken string `json:"apiToken"`
|
||||
// Proxmox BS API Token Secret。
|
||||
ApiTokenSecret string `json:"apiTokenSecret,omitempty"`
|
||||
// 是否允许不安全的连接。
|
||||
AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
|
||||
// 节点名称。
|
||||
NodeName string `json:"nodeName"`
|
||||
// 是否自动重启。
|
||||
AutoRestart bool `json:"autoRestart"`
|
||||
}
|
||||
|
||||
type Deployer struct {
|
||||
config *DeployerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *pbssdk.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.ApiToken, config.ApiTokenSecret, 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) {
|
||||
if d.config.NodeName == "" {
|
||||
return nil, fmt.Errorf("config `nodeName` is required")
|
||||
}
|
||||
|
||||
// 上传自定义证书
|
||||
// REF: https://pbs.proxmox.com/docs/api-viewer/index.html#/nodes/{node}/certificates/custom
|
||||
nodeUploadCustomCertificateReq := &pbssdk.NodeUploadCustomCertificateRequest{
|
||||
Certificates: certPEM,
|
||||
Key: privkeyPEM,
|
||||
Force: true,
|
||||
Restart: d.config.AutoRestart,
|
||||
}
|
||||
nodeUploadCustomCertificateResp, err := d.sdkClient.NodeUploadCustomCertificateWithContext(ctx, d.config.NodeName, nodeUploadCustomCertificateReq)
|
||||
d.logger.Debug("sdk request 'node.UploadCustomCertificate'", slog.String("params.node", d.config.NodeName), slog.Any("request", nodeUploadCustomCertificateReq), slog.Any("response", nodeUploadCustomCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'node.UploadCustomCertificate': %w", err)
|
||||
}
|
||||
|
||||
return &DeployResult{}, nil
|
||||
}
|
||||
|
||||
func createSDKClient(serverUrl, apiToken, apiTokenSecret string, skipTlsVerify bool) (*pbssdk.Client, error) {
|
||||
client, err := pbssdk.NewClient(
|
||||
serverUrl,
|
||||
pbssdk.WithApiToken(apiToken, apiTokenSecret),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if skipTlsVerify {
|
||||
client.SetTLSConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package proxmoxbs_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/proxmoxbs"
|
||||
it "github.com/certimate-go/certimate/pkg/core/deployer/testing"
|
||||
)
|
||||
|
||||
var (
|
||||
fp = it.Args("PROXMOXBS_")
|
||||
fTestCertPath string
|
||||
fTestKeyPath string
|
||||
fServerUrl string
|
||||
fApiToken string
|
||||
fApiTokenSecret string
|
||||
fNodeName string
|
||||
)
|
||||
|
||||
func init() {
|
||||
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
|
||||
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
|
||||
fp.DefineString(&fServerUrl, "SERVERURL")
|
||||
fp.DefineString(&fApiToken, "APITOKEN")
|
||||
fp.DefineString(&fApiTokenSecret, "APITOKENSECRET")
|
||||
fp.DefineString(&fNodeName, "NODENAME")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./proxmoxbs_test.go -args \
|
||||
--PROXMOXBS_TESTCERTPATH="/path/to/your-test-cert.pem" \
|
||||
--PROXMOXBS_TESTKEYPATH="/path/to/your-test-key.pem" \
|
||||
--PROXMOXBS_SERVERURL="http://127.0.0.1:8007" \
|
||||
--PROXMOXBS_APITOKEN="your-api-token" \
|
||||
--PROXMOXBS_APITOKENSECRET="your-api-token-secret" \
|
||||
--PROXMOXBS_NODENAME="your-node-name"
|
||||
*/
|
||||
func TestProvider(t *testing.T) {
|
||||
fp.Parse()
|
||||
|
||||
t.Run("Deploy", func(t *testing.T) {
|
||||
provider, err := impl.NewDeployer(&impl.DeployerConfig{
|
||||
ServerUrl: fServerUrl,
|
||||
ApiToken: fApiToken,
|
||||
ApiTokenSecret: fApiTokenSecret,
|
||||
AllowInsecureConnections: true,
|
||||
NodeName: fNodeName,
|
||||
AutoRestart: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
it.TestDeploy(t, provider, it.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
|
||||
})
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
proxmoxvesdk "github.com/certimate-go/certimate/pkg/sdk3rd/proxmoxve"
|
||||
pvesdk "github.com/certimate-go/certimate/pkg/sdk3rd/proxmoxve"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -33,7 +33,7 @@ type DeployerConfig struct {
|
||||
type Deployer struct {
|
||||
config *DeployerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *proxmoxvesdk.Client
|
||||
sdkClient *pvesdk.Client
|
||||
}
|
||||
|
||||
var _ Provider = (*Deployer)(nil)
|
||||
@@ -70,7 +70,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
|
||||
// 上传自定义证书
|
||||
// REF: https://pve.proxmox.com/pve-docs/api-viewer/index.html#/nodes/{node}/certificates/custom
|
||||
nodeUploadCustomCertificateReq := &proxmoxvesdk.NodeUploadCustomCertificateRequest{
|
||||
nodeUploadCustomCertificateReq := &pvesdk.NodeUploadCustomCertificateRequest{
|
||||
Certificates: certPEM,
|
||||
Key: privkeyPEM,
|
||||
Force: true,
|
||||
@@ -85,9 +85,10 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep
|
||||
return &DeployResult{}, nil
|
||||
}
|
||||
|
||||
func createSDKClient(serverUrl, apiToken, apiTokenSecret string, skipTlsVerify bool) (*proxmoxvesdk.Client, error) {
|
||||
client, err := proxmoxvesdk.NewClient(serverUrl,
|
||||
proxmoxvesdk.WithApiToken(apiToken, apiTokenSecret),
|
||||
func createSDKClient(serverUrl, apiToken, apiTokenSecret string, skipTlsVerify bool) (*pvesdk.Client, error) {
|
||||
client, err := pvesdk.NewClient(
|
||||
serverUrl,
|
||||
pvesdk.WithApiToken(apiToken, apiTokenSecret),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package proxmoxbs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type NodeUploadCustomCertificateRequest CustomCertificate
|
||||
|
||||
type NodeUploadCustomCertificateResponse struct {
|
||||
Data []struct {
|
||||
FileName string `json:"filename,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
SAN []string `json:"san,omitempty"`
|
||||
NotAfter int64 `json:"notafter,omitempty"`
|
||||
NotBefore int64 `json:"notbefore,omitempty"`
|
||||
} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Client) NodeUploadCustomCertificate(node string, req *NodeUploadCustomCertificateRequest) (*NodeUploadCustomCertificateResponse, error) {
|
||||
return c.NodeUploadCustomCertificateWithContext(context.Background(), node, req)
|
||||
}
|
||||
|
||||
func (c *Client) NodeUploadCustomCertificateWithContext(ctx context.Context, node string, req *NodeUploadCustomCertificateRequest) (*NodeUploadCustomCertificateResponse, error) {
|
||||
if node == "" {
|
||||
return nil, fmt.Errorf("sdkerr: bad request: unset node")
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/nodes/%s/certificates/custom", node)
|
||||
httpreq, err := c.newRequest(http.MethodPost, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
httpreq.SetBody(req)
|
||||
httpreq.SetContext(ctx)
|
||||
}
|
||||
|
||||
result := &NodeUploadCustomCertificateResponse{}
|
||||
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// A simple SDK client for Proxmox Backup Server.
|
||||
// API documentation: https://pbs.proxmox.com/docs/api-viewer/
|
||||
package proxmoxbs
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/app"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
rc *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
opts := &Options{}
|
||||
for _, fn := range optFns {
|
||||
fn(opts)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
if opts.TokenId == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset token")
|
||||
}
|
||||
if opts.TokenSecret == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset tokenSecret")
|
||||
}
|
||||
|
||||
httper := resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(serverUrl, "/")+"/api2/json").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("PBSAPIToken=%s:%s", opts.TokenId, opts.TokenSecret)).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent)
|
||||
|
||||
return &Client{rc: httper}, 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) 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
|
||||
|
||||
// WARN:
|
||||
// DO NOT CALL `req.SetResult` or `req.SetError` AGAIN! USE `doRequestWithResult` INSTEAD.
|
||||
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 any) (*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
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package proxmoxbs
|
||||
|
||||
type CustomCertificate struct {
|
||||
Certificates string `json:"certificates,omitempty"`
|
||||
Force bool `json:"force,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
Restart bool `json:"restart,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package proxmoxbs
|
||||
|
||||
type Options struct {
|
||||
TokenId string
|
||||
TokenSecret string
|
||||
}
|
||||
|
||||
type OptionsFunc func(*Options)
|
||||
|
||||
func WithApiToken(tokenId string, tokenSecret string) OptionsFunc {
|
||||
return func(o *Options) {
|
||||
o.TokenId = tokenId
|
||||
o.TokenSecret = tokenSecret
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// A simple SDK client for Proxmox VE.
|
||||
// A simple SDK client for Proxmox Virtual Environment.
|
||||
// API documentation: https://pve.proxmox.com/pve-docs/api-viewer/index.html
|
||||
package proxmoxve
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 1024 1024"><path fill="#e57000" d="M512 64 64 224v384c0 212 180 352 448 352s448-140 448-352V224zm0 128c123 0 224 39 224 86s-101 86-224 86-224-39-224-86 101-86 224-86m0 224c140 0 256-47 256-106v280c0 57-115 106-256 106s-256-49-256-106V310c0 59 116 106 256 106m0 86c-88 0-160 28-160 64s72 64 160 64 160-28 160-64-72-64-160-64"/></svg>
|
||||
|
After Width: | Height: | Size: 411 B |
@@ -88,6 +88,7 @@ import AccessConfigFieldsProviderOracleCloud from "./AccessConfigFieldsProviderO
|
||||
import AccessConfigFieldsProviderOVHcloud from "./AccessConfigFieldsProviderOVHcloud";
|
||||
import AccessConfigFieldsProviderPorkbun from "./AccessConfigFieldsProviderPorkbun";
|
||||
import AccessConfigFieldsProviderPowerDNS from "./AccessConfigFieldsProviderPowerDNS";
|
||||
import AccessConfigFieldsProviderProxmoxBS from "./AccessConfigFieldsProviderProxmoxBS";
|
||||
import AccessConfigFieldsProviderProxmoxVE from "./AccessConfigFieldsProviderProxmoxVE";
|
||||
import AccessConfigFieldsProviderQingCloud from "./AccessConfigFieldsProviderQingCloud";
|
||||
import AccessConfigFieldsProviderQiniu from "./AccessConfigFieldsProviderQiniu";
|
||||
@@ -216,6 +217,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
|
||||
[ACCESS_PROVIDERS.OVHCLOUD]: AccessConfigFieldsProviderOVHcloud,
|
||||
[ACCESS_PROVIDERS.PORKBUN]: AccessConfigFieldsProviderPorkbun,
|
||||
[ACCESS_PROVIDERS.POWERDNS]: AccessConfigFieldsProviderPowerDNS,
|
||||
[ACCESS_PROVIDERS.PROXMOXBS]: AccessConfigFieldsProviderProxmoxBS,
|
||||
[ACCESS_PROVIDERS.PROXMOXVE]: AccessConfigFieldsProviderProxmoxVE,
|
||||
[ACCESS_PROVIDERS.QINGCLOUD]: AccessConfigFieldsProviderQingCloud,
|
||||
[ACCESS_PROVIDERS.QINIU]: AccessConfigFieldsProviderQiniu,
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
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 AccessConfigFormFieldsProviderProxmoxBS = () => {
|
||||
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.proxmoxbs_server_url.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input type="url" placeholder={t("access.form.proxmoxbs_server_url.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "apiToken"]}
|
||||
initialValue={initialValues.apiToken}
|
||||
label={t("access.form.proxmoxbs_api_token.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.proxmoxbs_api_token.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.proxmoxbs_api_token.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "apiTokenSecret"]}
|
||||
initialValue={initialValues.apiTokenSecret}
|
||||
label={t("access.form.proxmoxbs_api_token_secret.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.proxmoxbs_api_token_secret.tooltip") }}></span>}
|
||||
>
|
||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.proxmoxbs_api_token_secret.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: "http://<your-host-addr>:8007/",
|
||||
apiToken: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t: _ } = i18n;
|
||||
|
||||
return z.object({
|
||||
serverUrl: z.url({ protocol: z.core.regexes.httpProtocol }),
|
||||
apiToken: z.string().nonempty(),
|
||||
apiTokenSecret: z.string().nullish(),
|
||||
allowInsecureConnections: z.boolean().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderProxmoxBS, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -90,6 +90,7 @@ import BizDeployNodeConfigFieldsProviderMohuaMVH from "./BizDeployNodeConfigFiel
|
||||
import BizDeployNodeConfigFieldsProviderNetlify from "./BizDeployNodeConfigFieldsProviderNetlify";
|
||||
import BizDeployNodeConfigFieldsProviderNginxProxyManager from "./BizDeployNodeConfigFieldsProviderNginxProxyManager";
|
||||
import BizDeployNodeConfigFieldsProviderOracleCloudCertificatesMgmt from "./BizDeployNodeConfigFieldsProviderOracleCloudCertificatesMgmt";
|
||||
import BizDeployNodeConfigFieldsProviderProxmoxBS from "./BizDeployNodeConfigFieldsProviderProxmoxBS";
|
||||
import BizDeployNodeConfigFieldsProviderProxmoxVE from "./BizDeployNodeConfigFieldsProviderProxmoxVE";
|
||||
import BizDeployNodeConfigFieldsProviderQingCloudLB from "./BizDeployNodeConfigFieldsProviderQingCloudLB";
|
||||
import BizDeployNodeConfigFieldsProviderQiniuCDN from "./BizDeployNodeConfigFieldsProviderQiniuCDN";
|
||||
@@ -241,6 +242,7 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
|
||||
[DEPLOYMENT_PROVIDERS.NETLIFY]: BizDeployNodeConfigFieldsProviderNetlify,
|
||||
[DEPLOYMENT_PROVIDERS.NGINXPROXYMANAGER]: BizDeployNodeConfigFieldsProviderNginxProxyManager,
|
||||
[DEPLOYMENT_PROVIDERS.ORACLECLOUD_CERTIFICATESMGMT]: BizDeployNodeConfigFieldsProviderOracleCloudCertificatesMgmt,
|
||||
[DEPLOYMENT_PROVIDERS.PROXMOXBS]: BizDeployNodeConfigFieldsProviderProxmoxBS,
|
||||
[DEPLOYMENT_PROVIDERS.PROXMOXVE]: BizDeployNodeConfigFieldsProviderProxmoxVE,
|
||||
[DEPLOYMENT_PROVIDERS.QINGCLOUD_LB]: BizDeployNodeConfigFieldsProviderQingCloudLB,
|
||||
[DEPLOYMENT_PROVIDERS.QINIU_CDN]: BizDeployNodeConfigFieldsProviderQiniuCDN,
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input, Switch } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import Tips from "@/components/Tips";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const BizDeployNodeConfigFieldsProviderProxmoxBS = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item>
|
||||
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.proxmoxbs.guide") }}></span>} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "nodeName"]}
|
||||
initialValue={initialValues.nodeName}
|
||||
label={t("workflow_node.deploy.form.proxmoxbs_node_name.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.deploy.form.proxmoxbs_node_name.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "autoRestart"]}
|
||||
initialValue={initialValues.autoRestart}
|
||||
label={t("workflow_node.deploy.form.proxmoxbs_auto_restart.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
nodeName: "",
|
||||
autoRestart: true,
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t: _ } = i18n;
|
||||
|
||||
return z.object({
|
||||
nodeName: z.string().nonempty(),
|
||||
autoRestart: z.boolean().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizDeployNodeConfigFieldsProviderProxmoxBS, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -106,6 +106,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
OVHCLOUD: "ovhcloud",
|
||||
PORKBUN: "porkbun",
|
||||
POWERDNS: "powerdns",
|
||||
PROXMOXBS: "proxmoxbs",
|
||||
PROXMOXVE: "proxmoxve",
|
||||
QINGCLOUD: "qingcloud",
|
||||
QINIU: "qiniu",
|
||||
@@ -223,6 +224,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.KONG, "provider.kong", "/imgs/providers/kong.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.LECDN, "provider.lecdn", "/imgs/providers/lecdn.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.NGINXPROXYMANAGER, "provider.nginxproxymanager", "/imgs/providers/nginxproxymanager.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.PROXMOXBS, "provider.proxmoxbs", "/imgs/providers/proxmoxbs.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.PROXMOXVE, "provider.proxmoxve", "/imgs/providers/proxmoxve.svg", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.RATPANEL, "provider.ratpanel", "/imgs/providers/ratpanel.png", [ACCESS_USAGES.HOSTING]],
|
||||
[ACCESS_PROVIDERS.SAFELINE, "provider.safeline", "/imgs/providers/safeline.svg", [ACCESS_USAGES.HOSTING]],
|
||||
@@ -708,6 +710,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
|
||||
NETLIFY: `${ACCESS_PROVIDERS.NETLIFY}`,
|
||||
NGINXPROXYMANAGER: `${ACCESS_PROVIDERS.NGINXPROXYMANAGER}`,
|
||||
ORACLECLOUD_CERTIFICATESMGMT: `${ACCESS_PROVIDERS.ORACLECLOUD}-certificatesmgmt`,
|
||||
PROXMOXBS: `${ACCESS_PROVIDERS.PROXMOXBS}`,
|
||||
PROXMOXVE: `${ACCESS_PROVIDERS.PROXMOXVE}`,
|
||||
QINGCLOUD_LB: `${ACCESS_PROVIDERS.QINGCLOUD}-lb`,
|
||||
QINIU_CDN: `${ACCESS_PROVIDERS.QINIU}-cdn`,
|
||||
@@ -954,6 +957,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
|
||||
[DEPLOYMENT_PROVIDERS.CPANEL, "provider.cpanel", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.DOKPLOY, "provider.dokploy", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.NGINXPROXYMANAGER, "provider.nginxproxymanager", DEPLOYMENT_CATEGORIES.WEBSITE],
|
||||
[DEPLOYMENT_PROVIDERS.PROXMOXBS, "provider.proxmoxbs", DEPLOYMENT_CATEGORIES.OTHER],
|
||||
[DEPLOYMENT_PROVIDERS.PROXMOXVE, "provider.proxmoxve", DEPLOYMENT_CATEGORIES.OTHER],
|
||||
[DEPLOYMENT_PROVIDERS.SYNOLOGYDSM, "provider.synologydsm", DEPLOYMENT_CATEGORIES.OTHER],
|
||||
] satisfies Array<[DeploymentProviderType, string, DeploymentCategoryType, "builtin"] | [DeploymentProviderType, string, DeploymentCategoryType]>
|
||||
|
||||
@@ -1136,6 +1136,20 @@
|
||||
"placeholder": "Please enter Proxmox VE API token secret",
|
||||
"tooltip": "For more information, see <a href=\"https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens\" target=\"_blank\">https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens</a>"
|
||||
},
|
||||
"proxmoxbs_server_url": {
|
||||
"label": "Proxmox BS server URL",
|
||||
"placeholder": "Please enter Proxmox BS server URL"
|
||||
},
|
||||
"proxmoxbs_api_token": {
|
||||
"label": "Proxmox BS API token",
|
||||
"placeholder": "Please enter Proxmox BS API token",
|
||||
"tooltip": "For more information, see <a href=\"https://pbs.proxmox.com/docs/user-management.html#api-tokens\" target=\"_blank\">https://pbs.proxmox.com/docs/user-management.html#api-tokens</a>"
|
||||
},
|
||||
"proxmoxbs_api_token_secret": {
|
||||
"label": "Proxmox BS API token secret (Optional)",
|
||||
"placeholder": "Please enter Proxmox BS API token secret",
|
||||
"tooltip": "For more information, see <a href=\"https://pbs.proxmox.com/docs/user-management.html#api-tokens\" target=\"_blank\">https://pbs.proxmox.com/docs/user-management.html#api-tokens</a>"
|
||||
},
|
||||
"qingcloud_access_key_id": {
|
||||
"label": "QingCloud AccessKeyID",
|
||||
"placeholder": "Please enter QingCloud AccessKeyID",
|
||||
|
||||
@@ -213,7 +213,8 @@
|
||||
"ovhcloud": "OVHcloud",
|
||||
"porkbun": "Porkbun",
|
||||
"powerdns": "PowerDNS",
|
||||
"proxmoxve": "Proxmox VE",
|
||||
"proxmoxve": "PVE (Proxmox Virtual Environment)",
|
||||
"proxmoxbs": "PBS (Proxmox Backup Server)",
|
||||
"qingcloud": "QingCloud",
|
||||
"qingcloud_dns": "QingCloud - DNS",
|
||||
"qingcloud_lb": "QingCloud - LBC (Load Balancer Cluster)",
|
||||
|
||||
@@ -2020,7 +2020,7 @@
|
||||
"tooltip": "For more information, see <a href=\"https://docs.oracle.com/en-us/iaas/Content/Identity/compartments/managingcompartments.htm\" target=\"_blank\">https://docs.oracle.com/en-us/iaas/Content/Identity/compartments/managingcompartments.htm</a>"
|
||||
},
|
||||
"proxmoxve": {
|
||||
"guide": "Requires Proxmox VE v5.2 or higher."
|
||||
"guide": "Requires Proxmox Virtual Environment v5.2 or higher."
|
||||
},
|
||||
"proxmoxve_node_name": {
|
||||
"label": "Proxmox VE cluster node name",
|
||||
@@ -2029,6 +2029,16 @@
|
||||
"proxmoxve_auto_restart": {
|
||||
"label": "Auto restart Proxmox VE after deployment"
|
||||
},
|
||||
"proxmoxbs": {
|
||||
"guide": "Requires Proxmox Backup Server v2.1 or higher."
|
||||
},
|
||||
"proxmoxbs_node_name": {
|
||||
"label": "Proxmox BS node name",
|
||||
"placeholder": "Please enter Proxmox BS node name"
|
||||
},
|
||||
"proxmoxbs_auto_restart": {
|
||||
"label": "Auto restart Proxmox BS after deployment"
|
||||
},
|
||||
"qingcloud_lb_zone_id": {
|
||||
"label": "QingCloud zone ID",
|
||||
"placeholder": "Please enter QingCloud LBC zone ID (e.g. pek3a)",
|
||||
|
||||
@@ -1136,6 +1136,20 @@
|
||||
"placeholder": "请输入 Proxmox VE API Token Secret",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens\" target=\"_blank\">https://pve.proxmox.com/pve-docs/pve-admin-guide.html#pveum_tokens</a>"
|
||||
},
|
||||
"proxmoxbs_server_url": {
|
||||
"label": "Proxmox BS 服务地址",
|
||||
"placeholder": "请输入 Proxmox BS 服务地址"
|
||||
},
|
||||
"proxmoxbs_api_token": {
|
||||
"label": "Proxmox BS API Token",
|
||||
"placeholder": "请输入 Proxmox BS API Token",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://pbs.proxmox.com/docs/user-management.html#api-tokens\" target=\"_blank\">https://pbs.proxmox.com/docs/user-management.html#api-tokens</a>"
|
||||
},
|
||||
"proxmoxbs_api_token_secret": {
|
||||
"label": "Proxmox BS API Token Secret(可选)",
|
||||
"placeholder": "请输入 Proxmox BS API Token Secret",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://pbs.proxmox.com/docs/user-management.html#api-tokens\" target=\"_blank\">https://pbs.proxmox.com/docs/user-management.html#api-tokens</a>"
|
||||
},
|
||||
"qingcloud_access_key_id": {
|
||||
"label": "青云 AccessKeyID",
|
||||
"placeholder": "请输入青云 AccessKeyID",
|
||||
|
||||
@@ -213,7 +213,8 @@
|
||||
"ovhcloud": "OVHcloud",
|
||||
"porkbun": "Porkbun",
|
||||
"powerdns": "PowerDNS",
|
||||
"proxmoxve": "Proxmox VE",
|
||||
"proxmoxve": "PVE(Proxmox Virtual Environment)",
|
||||
"proxmoxbs": "PBS(Proxmox Backup Server)",
|
||||
"qingcloud": "青云",
|
||||
"qingcloud_dns": "青云 - 云解析 DNS",
|
||||
"qingcloud_lb": "青云 - 负载均衡 LB",
|
||||
|
||||
@@ -2019,7 +2019,7 @@
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://docs.oracle.com/en-us/iaas/Content/Identity/compartments/managingcompartments.htm\" target=\"_blank\">https://docs.oracle.com/en-us/iaas/Content/Identity/compartments/managingcompartments.htm</a>"
|
||||
},
|
||||
"proxmoxve": {
|
||||
"guide": "需要 Proxmox VE v5.2 或更高版本。"
|
||||
"guide": "需要 Proxmox Virtual Environment v5.2 或更高版本。"
|
||||
},
|
||||
"proxmoxve_node_name": {
|
||||
"label": "Proxmox VE 集群节点名称",
|
||||
@@ -2028,6 +2028,16 @@
|
||||
"proxmoxve_auto_restart": {
|
||||
"label": "部署后自动重启 Proxmox VE 服务"
|
||||
},
|
||||
"proxmoxbs": {
|
||||
"guide": "需要 Proxmox Backup Server v2.1 或更高版本。"
|
||||
},
|
||||
"proxmoxbs_node_name": {
|
||||
"label": "Proxmox BS 节点名称",
|
||||
"placeholder": "请输入 Proxmox BS 节点名称"
|
||||
},
|
||||
"proxmoxbs_auto_restart": {
|
||||
"label": "部署后自动重启 Proxmox BS 服务"
|
||||
},
|
||||
"qingcloud_lb_zone_id": {
|
||||
"label": "青云服务区域 ID",
|
||||
"placeholder": "请输入青云 ELB 服务区域 ID(例如:pek3a)",
|
||||
|
||||
Reference in New Issue
Block a user