diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go
index e4467a40..56234b95 100644
--- a/internal/deployer/providers.go
+++ b/internal/deployer/providers.go
@@ -25,6 +25,7 @@ import (
pAliyunOSS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-oss"
pAliyunVOD "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-vod"
pAliyunWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aliyun-waf"
+ pAPISIX "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/apisix"
pAWSACM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-acm"
pAWSCloudFront "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-cloudfront"
pAWSIAM "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/aws-iam"
@@ -333,6 +334,23 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
}
}
+ case domain.DeploymentProviderTypeAPISIX:
+ {
+ access := domain.AccessConfigForAPISIX{}
+ if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
+ return nil, fmt.Errorf("failed to populate provider access config: %w", err)
+ }
+
+ deployer, err := pAPISIX.NewDeployer(&pAPISIX.DeployerConfig{
+ ServerUrl: access.ServerUrl,
+ ApiKey: access.ApiKey,
+ AllowInsecureConnections: access.AllowInsecureConnections,
+ ResourceType: pAPISIX.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
+ CertificateId: maputil.GetString(options.ProviderServiceConfig, "certificateId"),
+ })
+ return deployer, err
+ }
+
case domain.DeploymentProviderTypeAWSACM, domain.DeploymentProviderTypeAWSCloudFront, domain.DeploymentProviderTypeAWSIAM:
{
access := domain.AccessConfigForAWS{}
diff --git a/internal/domain/access.go b/internal/domain/access.go
index c6071aef..36e9fb40 100644
--- a/internal/domain/access.go
+++ b/internal/domain/access.go
@@ -41,6 +41,12 @@ type AccessConfigForAliyun struct {
ResourceGroupId string `json:"resourceGroupId,omitempty"`
}
+type AccessConfigForAPISIX struct {
+ ServerUrl string `json:"serverUrl"`
+ ApiKey string `json:"apiKey"`
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+}
+
type AccessConfigForAWS struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
diff --git a/internal/domain/provider.go b/internal/domain/provider.go
index e0ace3ac..2ca69241 100644
--- a/internal/domain/provider.go
+++ b/internal/domain/provider.go
@@ -14,6 +14,7 @@ const (
AccessProviderTypeACMEHttpReq = AccessProviderType("acmehttpreq")
AccessProviderTypeAkamai = AccessProviderType("akamai") // Akamai(预留)
AccessProviderTypeAliyun = AccessProviderType("aliyun")
+ AccessProviderTypeAPISIX = AccessProviderType("apisix")
AccessProviderTypeAWS = AccessProviderType("aws")
AccessProviderTypeAzure = AccessProviderType("azure")
AccessProviderTypeBaiduCloud = AccessProviderType("baiducloud")
@@ -193,6 +194,7 @@ const (
DeploymentProviderTypeAliyunOSS = DeploymentProviderType(AccessProviderTypeAliyun + "-oss")
DeploymentProviderTypeAliyunVOD = DeploymentProviderType(AccessProviderTypeAliyun + "-vod")
DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf")
+ DeploymentProviderTypeAPISIX = DeploymentProviderType(AccessProviderTypeAWS + "-apisix")
DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm")
DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront")
DeploymentProviderTypeAWSIAM = DeploymentProviderType(AccessProviderTypeAWS + "-iam")
diff --git a/internal/pkg/core/deployer/providers/apisix/apisix.go b/internal/pkg/core/deployer/providers/apisix/apisix.go
new file mode 100644
index 00000000..922b3597
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/apisix/apisix.go
@@ -0,0 +1,125 @@
+package apisix
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log/slog"
+ "net/url"
+
+ "github.com/usual2970/certimate/internal/pkg/core/deployer"
+ apisixsdk "github.com/usual2970/certimate/internal/pkg/sdk3rd/apisix"
+ certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
+ typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
+)
+
+type DeployerConfig struct {
+ // APISIX 服务地址。
+ ServerUrl string `json:"serverUrl"`
+ // APISIX Admin API Key。
+ ApiKey string `json:"apiKey"`
+ // 是否允许不安全的连接。
+ AllowInsecureConnections bool `json:"allowInsecureConnections,omitempty"`
+ // 部署资源类型。
+ ResourceType ResourceType `json:"resourceType"`
+ // 证书 ID。
+ // 部署资源类型为 [RESOURCE_TYPE_CERTIFICATE] 时必填。
+ CertificateId string `json:"certificateId,omitempty"`
+}
+
+type DeployerProvider struct {
+ config *DeployerConfig
+ logger *slog.Logger
+ sdkClient *apisixsdk.Client
+}
+
+var _ deployer.Deployer = (*DeployerProvider)(nil)
+
+func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
+ if config == nil {
+ panic("config is nil")
+ }
+
+ client, err := createSdkClient(config.ServerUrl, config.ApiKey, config.AllowInsecureConnections)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create sdk client: %w", err)
+ }
+
+ return &DeployerProvider{
+ config: config,
+ logger: slog.Default(),
+ sdkClient: client,
+ }, nil
+}
+
+func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
+ if logger == nil {
+ d.logger = slog.New(slog.DiscardHandler)
+ } else {
+ d.logger = logger
+ }
+ return d
+}
+
+func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
+ // 根据部署资源类型决定部署方式
+ switch d.config.ResourceType {
+ case RESOURCE_TYPE_CERTIFICATE:
+ if err := d.deployToCertificate(ctx, certPEM, privkeyPEM); err != nil {
+ return nil, err
+ }
+
+ default:
+ return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
+ }
+
+ return &deployer.DeployResult{}, nil
+}
+
+func (d *DeployerProvider) deployToCertificate(ctx context.Context, certPEM string, privkeyPEM string) error {
+ if d.config.CertificateId == "" {
+ return errors.New("config `certificateId` is required")
+ }
+
+ // 解析证书内容
+ certX509, err := certutil.ParseCertificateFromPEM(certPEM)
+ if err != nil {
+ return err
+ }
+
+ // 更新 SSL 证书
+ // REF: https://apisix.apache.org/zh/docs/apisix/admin-api/#ssl
+ updateSSLReq := &apisixsdk.UpdateSSLRequest{
+ ID: d.config.CertificateId,
+ Cert: typeutil.ToPtr(certPEM),
+ Key: typeutil.ToPtr(privkeyPEM),
+ SNIs: typeutil.ToPtr(certX509.DNSNames),
+ Type: typeutil.ToPtr("server"),
+ Status: typeutil.ToPtr(int32(1)),
+ }
+ updateSSLResp, err := d.sdkClient.UpdateSSL(updateSSLReq)
+ d.logger.Debug("sdk request 'apisix.UpdateSSL'", slog.Any("request", updateSSLReq), slog.Any("response", updateSSLResp))
+ if err != nil {
+ return fmt.Errorf("failed to execute sdk request 'apisix.UpdateSSL': %w", err)
+ }
+
+ return nil
+}
+
+func createSdkClient(serverUrl, apiKey string, skipTlsVerify bool) (*apisixsdk.Client, error) {
+ if _, err := url.Parse(serverUrl); err != nil {
+ return nil, errors.New("invalid apisix server url")
+ }
+
+ if apiKey == "" {
+ return nil, errors.New("invalid apisix api key")
+ }
+
+ client := apisixsdk.NewClient(serverUrl, apiKey)
+ if skipTlsVerify {
+ client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true})
+ }
+
+ return client, nil
+}
diff --git a/internal/pkg/core/deployer/providers/apisix/apisix_test.go b/internal/pkg/core/deployer/providers/apisix/apisix_test.go
new file mode 100644
index 00000000..d7d7dffd
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/apisix/apisix_test.go
@@ -0,0 +1,77 @@
+package apisix_test
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/apisix"
+)
+
+var (
+ fInputCertPath string
+ fInputKeyPath string
+ fServerUrl string
+ fApiKey string
+ fCertificateId string
+)
+
+func init() {
+ argsPrefix := "CERTIMATE_DEPLOYER_APISIX_"
+
+ flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
+ flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
+ flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "")
+ flag.StringVar(&fApiKey, argsPrefix+"APIKEY", "", "")
+ flag.StringVar(&fCertificateId, argsPrefix+"CERTIFICATEID", "", "")
+}
+
+/*
+Shell command to run this test:
+
+ go test -v ./apisix_test.go -args \
+ --CERTIMATE_DEPLOYER_APISIX_INPUTCERTPATH="/path/to/your-input-cert.pem" \
+ --CERTIMATE_DEPLOYER_APISIX_INPUTKEYPATH="/path/to/your-input-key.pem" \
+ --CERTIMATE_DEPLOYER_APISIX_SERVERURL="http://127.0.0.1:9080" \
+ --CERTIMATE_DEPLOYER_APISIX_APIKEY="your-api-key" \
+ --CERTIMATE_DEPLOYER_APISIX_CERTIFICATEID="your-cerficiate-id"
+*/
+func TestDeploy(t *testing.T) {
+ flag.Parse()
+
+ t.Run("Deploy", func(t *testing.T) {
+ t.Log(strings.Join([]string{
+ "args:",
+ fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
+ fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
+ fmt.Sprintf("SERVERURL: %v", fServerUrl),
+ fmt.Sprintf("APIKEY: %v", fApiKey),
+ fmt.Sprintf("CERTIFICATEID: %v", fCertificateId),
+ }, "\n"))
+
+ deployer, err := provider.NewDeployer(&provider.DeployerConfig{
+ ServerUrl: fServerUrl,
+ ApiKey: fApiKey,
+ AllowInsecureConnections: true,
+ ResourceType: provider.RESOURCE_TYPE_CERTIFICATE,
+ CertificateId: fCertificateId,
+ })
+ if err != nil {
+ t.Errorf("err: %+v", err)
+ return
+ }
+
+ fInputCertData, _ := os.ReadFile(fInputCertPath)
+ fInputKeyData, _ := os.ReadFile(fInputKeyPath)
+ res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
+ if err != nil {
+ t.Errorf("err: %+v", err)
+ return
+ }
+
+ t.Logf("ok: %v", res)
+ })
+}
diff --git a/internal/pkg/core/deployer/providers/apisix/consts.go b/internal/pkg/core/deployer/providers/apisix/consts.go
new file mode 100644
index 00000000..75aa1b60
--- /dev/null
+++ b/internal/pkg/core/deployer/providers/apisix/consts.go
@@ -0,0 +1,8 @@
+package apisix
+
+type ResourceType string
+
+const (
+ // 资源类型:替换指定证书。
+ RESOURCE_TYPE_CERTIFICATE = ResourceType("certificate")
+)
diff --git a/internal/pkg/sdk3rd/apisix/api.go b/internal/pkg/sdk3rd/apisix/api.go
new file mode 100644
index 00000000..7ebfba04
--- /dev/null
+++ b/internal/pkg/sdk3rd/apisix/api.go
@@ -0,0 +1,16 @@
+package apisix
+
+import (
+ "fmt"
+ "net/http"
+)
+
+func (c *Client) UpdateSSL(req *UpdateSSLRequest) (*UpdateSSLResponse, error) {
+ if req.ID == "" {
+ return nil, fmt.Errorf("1panel api error: invalid parameter: ID")
+ }
+
+ resp := &UpdateSSLResponse{}
+ err := c.sendRequestWithResult(http.MethodGet, fmt.Sprintf("/ssls/%s", req.ID), req, resp)
+ return resp, err
+}
diff --git a/internal/pkg/sdk3rd/apisix/client.go b/internal/pkg/sdk3rd/apisix/client.go
new file mode 100644
index 00000000..66784824
--- /dev/null
+++ b/internal/pkg/sdk3rd/apisix/client.go
@@ -0,0 +1,87 @@
+package apisix
+
+import (
+ "crypto/tls"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/go-resty/resty/v2"
+)
+
+type Client struct {
+ client *resty.Client
+}
+
+func NewClient(serverUrl, apiKey string) *Client {
+ client := resty.New().
+ SetBaseURL(strings.TrimRight(serverUrl, "/")+"/apisix/admin").
+ SetHeader("User-Agent", "certimate").
+ SetPreRequestHook(func(c *resty.Client, req *http.Request) error {
+ req.Header.Set("X-API-KEY", apiKey)
+
+ return nil
+ })
+
+ return &Client{
+ client: client,
+ }
+}
+
+func (c *Client) WithTimeout(timeout time.Duration) *Client {
+ c.client.SetTimeout(timeout)
+ return c
+}
+
+func (c *Client) WithTLSConfig(config *tls.Config) *Client {
+ c.client.SetTLSClientConfig(config)
+ return c
+}
+
+func (c *Client) sendRequest(method string, path string, params interface{}) (*resty.Response, error) {
+ req := c.client.R()
+ if strings.EqualFold(method, http.MethodGet) {
+ qs := make(map[string]string)
+ if params != nil {
+ temp := make(map[string]any)
+ jsonb, _ := json.Marshal(params)
+ json.Unmarshal(jsonb, &temp)
+ for k, v := range temp {
+ if v != nil {
+ qs[k] = fmt.Sprintf("%v", v)
+ }
+ }
+ }
+
+ req = req.SetQueryParams(qs)
+ } else {
+ req = req.SetHeader("Content-Type", "application/json").SetBody(params)
+ }
+
+ resp, err := req.Execute(method, path)
+ if err != nil {
+ return resp, fmt.Errorf("apisix api error: failed to send request: %w", err)
+ } else if resp.IsError() {
+ return resp, fmt.Errorf("apisix api error: unexpected status code: %d, resp: %s", resp.StatusCode(), resp.String())
+ }
+
+ return resp, nil
+}
+
+func (c *Client) sendRequestWithResult(method string, path string, params interface{}, result interface{}) error {
+ resp, err := c.sendRequest(method, path, params)
+ if err != nil {
+ if resp != nil {
+ json.Unmarshal(resp.Body(), &result)
+ }
+ return err
+ }
+
+ if err := json.Unmarshal(resp.Body(), &result); err != nil {
+ return fmt.Errorf("apisix api error: failed to unmarshal response: %w", err)
+ }
+
+ return nil
+}
diff --git a/internal/pkg/sdk3rd/apisix/models.go b/internal/pkg/sdk3rd/apisix/models.go
new file mode 100644
index 00000000..960c8489
--- /dev/null
+++ b/internal/pkg/sdk3rd/apisix/models.go
@@ -0,0 +1,12 @@
+package apisix
+
+type UpdateSSLRequest struct {
+ ID string `json:"-"`
+ Cert *string `json:"cert,omitempty"`
+ Key *string `json:"key,omitempty"`
+ SNIs *[]string `json:"snis,omitempty"`
+ Type *string `json:"type,omitempty"`
+ Status *int32 `json:"status,omitempty"`
+}
+
+type UpdateSSLResponse struct{}
diff --git a/ui/public/imgs/providers/apisix.svg b/ui/public/imgs/providers/apisix.svg
new file mode 100644
index 00000000..55b6e4f2
--- /dev/null
+++ b/ui/public/imgs/providers/apisix.svg
@@ -0,0 +1 @@
+
diff --git a/ui/src/components/access/AccessForm.tsx b/ui/src/components/access/AccessForm.tsx
index 4bb1d439..7b7640d3 100644
--- a/ui/src/components/access/AccessForm.tsx
+++ b/ui/src/components/access/AccessForm.tsx
@@ -15,6 +15,7 @@ import AccessForm1PanelConfig from "./AccessForm1PanelConfig";
import AccessFormACMECAConfig from "./AccessFormACMECAConfig";
import AccessFormACMEHttpReqConfig from "./AccessFormACMEHttpReqConfig";
import AccessFormAliyunConfig from "./AccessFormAliyunConfig";
+import AccessFormAPISIXConfig from "./AccessFormAPISIXConfig";
import AccessFormAWSConfig from "./AccessFormAWSConfig";
import AccessFormAzureConfig from "./AccessFormAzureConfig";
import AccessFormBaiduCloudConfig from "./AccessFormBaiduCloudConfig";
@@ -194,6 +195,8 @@ const AccessForm = forwardRef(({ className,
return ;
case ACCESS_PROVIDERS.ALIYUN:
return ;
+ case ACCESS_PROVIDERS.APISIX:
+ return ;
case ACCESS_PROVIDERS.AWS:
return ;
case ACCESS_PROVIDERS.AZURE:
diff --git a/ui/src/components/access/AccessFormAPISIXConfig.tsx b/ui/src/components/access/AccessFormAPISIXConfig.tsx
new file mode 100644
index 00000000..856a6565
--- /dev/null
+++ b/ui/src/components/access/AccessFormAPISIXConfig.tsx
@@ -0,0 +1,71 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Switch } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import { type AccessConfigForAPISIX } from "@/domain/access";
+
+type AccessFormAPISIXConfigFieldValues = Nullish;
+
+export type AccessFormAPISIXConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: AccessFormAPISIXConfigFieldValues;
+ onValuesChange?: (values: AccessFormAPISIXConfigFieldValues) => void;
+};
+
+const initFormModel = (): AccessFormAPISIXConfigFieldValues => {
+ return {
+ serverUrl: "http://:9180/",
+ apiKey: "",
+ };
+};
+
+const AccessFormAPISIXConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormAPISIXConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ serverUrl: z.string().url(t("common.errmsg.url_invalid")),
+ apiKey: z.string().trim().nonempty(t("access.form.apisix_api_key.placeholder")),
+ allowInsecureConnections: z.boolean().nullish(),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+ }
+ >
+
+
+
+
+
+
+
+ );
+};
+
+export default AccessFormAPISIXConfig;
diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
index d9a34629..59e5146e 100644
--- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
+++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx
@@ -33,6 +33,7 @@ import DeployNodeConfigFormAliyunNLBConfig from "./DeployNodeConfigFormAliyunNLB
import DeployNodeConfigFormAliyunOSSConfig from "./DeployNodeConfigFormAliyunOSSConfig";
import DeployNodeConfigFormAliyunVODConfig from "./DeployNodeConfigFormAliyunVODConfig";
import DeployNodeConfigFormAliyunWAFConfig from "./DeployNodeConfigFormAliyunWAFConfig";
+import DeployNodeConfigFormAPISIXConfig from "./DeployNodeConfigFormAPISIXConfig";
import DeployNodeConfigFormAWSACMConfig from "./DeployNodeConfigFormAWSACMConfig";
import DeployNodeConfigFormAWSCloudFrontConfig from "./DeployNodeConfigFormAWSCloudFrontConfig";
import DeployNodeConfigFormAWSIAMConfig from "./DeployNodeConfigFormAWSIAMConfig";
@@ -234,6 +235,8 @@ const DeployNodeConfigForm = forwardRef;
case DEPLOYMENT_PROVIDERS.ALIYUN_WAF:
return ;
+ case DEPLOYMENT_PROVIDERS.APISIX:
+ return ;
case DEPLOYMENT_PROVIDERS.AWS_ACM:
return ;
case DEPLOYMENT_PROVIDERS.AWS_CLOUDFRONT:
diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormAPISIXConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormAPISIXConfig.tsx
new file mode 100644
index 00000000..0fd67674
--- /dev/null
+++ b/ui/src/components/workflow/node/DeployNodeConfigFormAPISIXConfig.tsx
@@ -0,0 +1,81 @@
+import { useTranslation } from "react-i18next";
+import { Form, type FormInstance, Input, Select } from "antd";
+import { createSchemaFieldRule } from "antd-zod";
+import { z } from "zod";
+
+import Show from "@/components/Show";
+
+type DeployNodeConfigFormAPISIXConfigFieldValues = Nullish<{
+ resourceType: string;
+ certificateId?: string;
+}>;
+
+export type DeployNodeConfigFormAPISIXConfigProps = {
+ form: FormInstance;
+ formName: string;
+ disabled?: boolean;
+ initialValues?: DeployNodeConfigFormAPISIXConfigFieldValues;
+ onValuesChange?: (values: DeployNodeConfigFormAPISIXConfigFieldValues) => void;
+};
+
+const RESOURCE_TYPE_CERTIFICATE = "certificate" as const;
+
+const initFormModel = (): DeployNodeConfigFormAPISIXConfigFieldValues => {
+ return {
+ resourceType: RESOURCE_TYPE_CERTIFICATE,
+ certificateId: "",
+ };
+};
+
+const DeployNodeConfigFormAPISIXConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: DeployNodeConfigFormAPISIXConfigProps) => {
+ const { t } = useTranslation();
+
+ const formSchema = z.object({
+ resourceType: z.literal(RESOURCE_TYPE_CERTIFICATE, {
+ message: t("workflow_node.deploy.form.apisix_resource_type.placeholder"),
+ }),
+ certificateId: z
+ .string()
+ .nullish()
+ .refine((v) => fieldResourceType !== RESOURCE_TYPE_CERTIFICATE || !!v?.trim(), t("workflow_node.deploy.form.apisix_certificate_id.placeholder")),
+ });
+ const formRule = createSchemaFieldRule(formSchema);
+
+ const fieldResourceType = Form.useWatch("resourceType", formInst);
+
+ const handleFormChange = (_: unknown, values: z.infer) => {
+ onValuesChange?.(values);
+ };
+
+ return (
+
+
+
+
+
+ }
+ >
+
+
+
+
+ );
+};
+
+export default DeployNodeConfigFormAPISIXConfig;
diff --git a/ui/src/domain/access.ts b/ui/src/domain/access.ts
index 1ab660a3..cdf01c89 100644
--- a/ui/src/domain/access.ts
+++ b/ui/src/domain/access.ts
@@ -10,6 +10,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForACMECA
| AccessConfigForACMEHttpReq
| AccessConfigForAliyun
+ | AccessConfigForAPISIX
| AccessConfigForAWS
| AccessConfigForAzure
| AccessConfigForBaiduCloud
@@ -105,6 +106,12 @@ export type AccessConfigForAliyun = {
resourceGroupId?: string;
};
+export type AccessConfigForAPISIX = {
+ serverUrl: string;
+ apiKey: string;
+ allowInsecureConnections?: boolean;
+};
+
export type AccessConfigForAWS = {
accessKeyId: string;
secretAccessKey: string;
diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts
index d57cc380..9956a8c9 100644
--- a/ui/src/domain/provider.ts
+++ b/ui/src/domain/provider.ts
@@ -8,6 +8,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
ACMECA: "acmeca",
ACMEHTTPREQ: "acmehttpreq",
ALIYUN: "aliyun",
+ APISIX: "apisix",
AWS: "aws",
AZURE: "azure",
BAIDUCLOUD: "baiducloud",
@@ -141,6 +142,7 @@ export const accessProvidersMap: Map [
type,
diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json
index 23946f6d..63860919 100644
--- a/ui/src/i18n/locales/en/nls.access.json
+++ b/ui/src/i18n/locales/en/nls.access.json
@@ -72,6 +72,11 @@
"access.form.aliyun_resource_group_id.label": "Aliyun resource group ID (Optional)",
"access.form.aliyun_resource_group_id.placeholder": "Please enter Aliyun resource group ID",
"access.form.aliyun_resource_group_id.tooltip": "For more information, see https://www.alibabacloud.com/help/en/resource-management/product-overview",
+ "access.form.apisix_server_url.label": "APISIX server URL",
+ "access.form.apisix_server_url.placeholder": "Please enter APISIX server URL",
+ "access.form.apisix_api_key.label": "APISIX Admin API key",
+ "access.form.apisix_api_key.placeholder": "Please enter APISIX Admin API key",
+ "access.form.apisix_api_key.tooltip": "For more information, see https://apisix.apache.org/docs/apisix/admin-api/",
"access.form.aws_access_key_id.label": "AWS AccessKeyId",
"access.form.aws_access_key_id.placeholder": "Please enter AWS AccessKeyId",
"access.form.aws_access_key_id.tooltip": "For more information, see https://docs.aws.amazon.com/en_us/IAM/latest/UserGuide/id_credentials_access-keys.html",
diff --git a/ui/src/i18n/locales/en/nls.provider.json b/ui/src/i18n/locales/en/nls.provider.json
index b2f5ce54..f72c0ca1 100644
--- a/ui/src/i18n/locales/en/nls.provider.json
+++ b/ui/src/i18n/locales/en/nls.provider.json
@@ -24,6 +24,7 @@
"provider.aliyun.waf": "Alibaba Cloud - WAF (Web Application Firewall)",
"provider.akamai": "Akamai",
"provider.akamai.cdn": "Akamai - CDN (Content Delivery Network)",
+ "provider.apisix": "Apache APISIX",
"provider.aws": "AWS",
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
"provider.aws.cloudfront": "AWS - CloudFront",
diff --git a/ui/src/i18n/locales/en/nls.workflow.nodes.json b/ui/src/i18n/locales/en/nls.workflow.nodes.json
index a482f835..ac8b8c60 100644
--- a/ui/src/i18n/locales/en/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/en/nls.workflow.nodes.json
@@ -285,6 +285,12 @@
"workflow_node.deploy.form.aliyun_waf_domain.label": "Alibaba Cloud WAF domain (Optional)",
"workflow_node.deploy.form.aliyun_waf_domain.placeholder": "Please enter Alibaba Cloud WAF domain name",
"workflow_node.deploy.form.aliyun_waf_domain.tooltip": "For more information, see https://waf.console.aliyun.com",
+ "workflow_node.deploy.form.apisix_resource_type.label": "Resource type",
+ "workflow_node.deploy.form.apisix_resource_type.placeholder": "Please select resource type",
+ "workflow_node.deploy.form.apisix_resource_type.option.certificate.label": "SSL certificate",
+ "workflow_node.deploy.form.apisix_certificate_id.label": "APISIX certificate ID",
+ "workflow_node.deploy.form.apisix_certificate_id.placeholder": "Please enter APISIX certificate ID",
+ "workflow_node.deploy.form.apisix_certificate_id.tooltip": "You can find it on APISIX WebUI.",
"workflow_node.deploy.form.aws_acm_region.label": "AWS ACM Region",
"workflow_node.deploy.form.aws_acm_region.placeholder": "Please enter AWS ACM region (e.g. us-east-1)",
"workflow_node.deploy.form.aws_acm_region.tooltip": "For more information, see https://docs.aws.amazon.com/en_us/general/latest/gr/rande.html#regional-endpoints",
diff --git a/ui/src/i18n/locales/zh/nls.access.json b/ui/src/i18n/locales/zh/nls.access.json
index b4cc3846..d55c3066 100644
--- a/ui/src/i18n/locales/zh/nls.access.json
+++ b/ui/src/i18n/locales/zh/nls.access.json
@@ -72,6 +72,11 @@
"access.form.aliyun_resource_group_id.label": "阿里云资源组 ID(可选)",
"access.form.aliyun_resource_group_id.placeholder": "请输入阿里云资源组 ID",
"access.form.aliyun_resource_group_id.tooltip": "这是什么?请参阅 https://help.aliyun.com/zh/resource-management/resource-group/product-overview",
+ "access.form.apisix_server_url.label": "APISIX 服务地址",
+ "access.form.apisix_server_url.placeholder": "请输入 APISIX 服务地址",
+ "access.form.apisix_api_key.label": "APISIX Admin API Key",
+ "access.form.apisix_api_key.placeholder": "请输入 APISIX Admin API Key",
+ "access.form.apisix_api_key.tooltip": "这是什么?请参阅 https://apisix.apache.org/zh/docs/apisix/admin-api/",
"access.form.aws_access_key_id.label": "AWS AccessKeyId",
"access.form.aws_access_key_id.placeholder": "请输入 AWS AccessKeyId",
"access.form.aws_access_key_id.tooltip": "这是什么?请参阅 https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_credentials_access-keys.html",
diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json
index a5ff9e05..bf542b34 100644
--- a/ui/src/i18n/locales/zh/nls.provider.json
+++ b/ui/src/i18n/locales/zh/nls.provider.json
@@ -24,6 +24,7 @@
"provider.aliyun.waf": "阿里云 - Web 应用防火墙 WAF",
"provider.akamai": "Akamai",
"provider.akamai.cdn": "Akamai - 内容分发网络 CDN",
+ "provider.apisix": "Apache APISIX",
"provider.aws": "AWS",
"provider.aws.acm": "AWS - ACM (Amazon Certificate Manager)",
"provider.aws.cloudfront": "AWS - CloudFront",
diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
index df6b0a1b..f88b055a 100644
--- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json
+++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json
@@ -284,6 +284,12 @@
"workflow_node.deploy.form.aliyun_waf_domain.label": "阿里云 WAF 接入域名(可选)",
"workflow_node.deploy.form.aliyun_waf_domain.placeholder": "请输入阿里云 WAF 接入域名(支持泛域名)",
"workflow_node.deploy.form.aliyun_waf_domain.tooltip": "这是什么?请参阅 waf.console.aliyun.com
不填写时,将替换实例的默认证书;否则,将替换扩展域名证书。",
+ "workflow_node.deploy.form.apisix_resource_type.label": "证书部署方式",
+ "workflow_node.deploy.form.apisix_resource_type.placeholder": "请选择证书部署方式",
+ "workflow_node.deploy.form.apisix_resource_type.option.certificate.label": "替换指定证书",
+ "workflow_node.deploy.form.apisix_certificate_id.label": "APISIX 证书 ID",
+ "workflow_node.deploy.form.apisix_certificate_id.placeholder": "请输入 APISIX 证书 ID",
+ "workflow_node.deploy.form.apisix_certificate_id.tooltip": "请登录 APISIX 控制台查看。",
"workflow_node.deploy.form.aws_acm_region.label": "AWS ACM 服务区域",
"workflow_node.deploy.form.aws_acm_region.placeholder": "请输入 AWS ACM 服务区域(例如:us-east-1)",
"workflow_node.deploy.form.aws_acm_region.tooltip": "这是什么?请参阅 https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints",