mirror of
https://github.com/woodchen-ink/certimate.git
synced 2025-07-18 17:31:55 +08:00
feat: rename access
to providerAccessId
This commit is contained in:
parent
90058b2dae
commit
52dfa5e8c3
@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/go-acme/lego/v4/registration"
|
"github.com/go-acme/lego/v4/registration"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
|
||||||
|
|
||||||
"github.com/usual2970/certimate/internal/app"
|
"github.com/usual2970/certimate/internal/app"
|
||||||
"github.com/usual2970/certimate/internal/domain"
|
"github.com/usual2970/certimate/internal/domain"
|
||||||
@ -57,12 +56,12 @@ type Certificate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ApplyOption struct {
|
type ApplyOption struct {
|
||||||
Email string `json:"email"`
|
|
||||||
SubjectAltNames string `json:"subjectAltNames"`
|
SubjectAltNames string `json:"subjectAltNames"`
|
||||||
|
Email string `json:"email"`
|
||||||
AccessConfig string `json:"accessConfig"`
|
AccessConfig string `json:"accessConfig"`
|
||||||
KeyAlgorithm string `json:"keyAlgorithm"`
|
KeyAlgorithm string `json:"keyAlgorithm"`
|
||||||
Nameservers string `json:"nameservers"`
|
Nameservers string `json:"nameservers"`
|
||||||
PropagationTimeout int64 `json:"propagationTimeout"`
|
PropagationTimeout int32 `json:"propagationTimeout"`
|
||||||
DisableFollowCNAME bool `json:"disableFollowCNAME"`
|
DisableFollowCNAME bool `json:"disableFollowCNAME"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,41 +124,11 @@ type Applicant interface {
|
|||||||
Apply() (*Certificate, error)
|
Apply() (*Certificate, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Get(record *models.Record) (Applicant, error) {
|
|
||||||
if record.GetString("applyConfig") == "" {
|
|
||||||
return nil, errors.New("applyConfig is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
applyConfig := &domain.ApplyConfig{}
|
|
||||||
record.UnmarshalJSONField("applyConfig", applyConfig)
|
|
||||||
|
|
||||||
access, err := app.GetApp().Dao().FindRecordById("access", applyConfig.Access)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("access record not found: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if applyConfig.Email == "" {
|
|
||||||
applyConfig.Email = defaultEmail
|
|
||||||
}
|
|
||||||
|
|
||||||
option := &ApplyOption{
|
|
||||||
Email: applyConfig.Email,
|
|
||||||
SubjectAltNames: record.GetString("domain"),
|
|
||||||
AccessConfig: access.GetString("config"),
|
|
||||||
KeyAlgorithm: applyConfig.KeyAlgorithm,
|
|
||||||
Nameservers: applyConfig.Nameservers,
|
|
||||||
PropagationTimeout: applyConfig.PropagationTimeout,
|
|
||||||
DisableFollowCNAME: applyConfig.DisableFollowCNAME,
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetWithTypeOption(domain.AccessProviderType(access.GetString("provider")), option)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
func GetWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
||||||
// 获取授权配置
|
// 获取授权配置
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
|
|
||||||
access, err := accessRepo.GetById(context.Background(), node.GetConfigString("access"))
|
access, err := accessRepo.GetById(context.Background(), node.GetConfigString("providerAccessId"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("access record not found: %w", err)
|
return nil, fmt.Errorf("access record not found: %w", err)
|
||||||
}
|
}
|
||||||
@ -170,7 +139,7 @@ func GetWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
|||||||
AccessConfig: access.Config,
|
AccessConfig: access.Config,
|
||||||
KeyAlgorithm: node.GetConfigString("keyAlgorithm"),
|
KeyAlgorithm: node.GetConfigString("keyAlgorithm"),
|
||||||
Nameservers: node.GetConfigString("nameservers"),
|
Nameservers: node.GetConfigString("nameservers"),
|
||||||
PropagationTimeout: node.GetConfigInt64("propagationTimeout"),
|
PropagationTimeout: node.GetConfigInt32("propagationTimeout"),
|
||||||
DisableFollowCNAME: node.GetConfigBool("disableFollowCNAME"),
|
DisableFollowCNAME: node.GetConfigBool("disableFollowCNAME"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ const (
|
|||||||
type DeployerOption struct {
|
type DeployerOption struct {
|
||||||
DomainId string `json:"domainId"`
|
DomainId string `json:"domainId"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
Access string `json:"access"`
|
AccessConfig string `json:"accessConfig"`
|
||||||
AccessRecord *domain.Access `json:"-"`
|
AccessRecord *domain.Access `json:"-"`
|
||||||
DeployConfig domain.DeployConfig `json:"deployConfig"`
|
DeployConfig domain.DeployConfig `json:"deployConfig"`
|
||||||
Certificate applicant.Certificate `json:"certificate"`
|
Certificate applicant.Certificate `json:"certificate"`
|
||||||
@ -97,7 +97,7 @@ func GetWithTypeAndOption(deployType string, option *DeployerOption) (Deployer,
|
|||||||
|
|
||||||
func newWithDeployConfig(record *models.Record, cert *applicant.Certificate, deployConfig domain.DeployConfig) (Deployer, error) {
|
func newWithDeployConfig(record *models.Record, cert *applicant.Certificate, deployConfig domain.DeployConfig) (Deployer, error) {
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
access, err := accessRepo.GetById(context.Background(), deployConfig.Access)
|
access, err := accessRepo.GetById(context.Background(), deployConfig.ProviderAccessId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("获取access失败:%w", err)
|
return nil, fmt.Errorf("获取access失败:%w", err)
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ func newWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
|
|||||||
option := &DeployerOption{
|
option := &DeployerOption{
|
||||||
DomainId: record.Id,
|
DomainId: record.Id,
|
||||||
Domain: record.GetString("domain"),
|
Domain: record.GetString("domain"),
|
||||||
Access: access.Config,
|
AccessConfig: access.Config,
|
||||||
AccessRecord: access,
|
AccessRecord: access,
|
||||||
DeployConfig: deployConfig,
|
DeployConfig: deployConfig,
|
||||||
}
|
}
|
||||||
@ -118,11 +118,11 @@ func newWithDeployConfig(record *models.Record, cert *applicant.Certificate, dep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newWithTypeAndOption(deployConfig.Type, option)
|
return newWithTypeAndOption(deployConfig.Provider, option)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWithTypeAndOption(deployType string, option *DeployerOption) (Deployer, error) {
|
func newWithTypeAndOption(deployType string, option *DeployerOption) (Deployer, error) {
|
||||||
deployer, logger, err := createDeployer(deployType, option.AccessRecord.Config, option.DeployConfig.Config)
|
deployer, logger, err := createDeployer(deployType, option.AccessRecord.Config, option.DeployConfig.NodeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,17 @@ package domain
|
|||||||
// Deprecated: TODO: 即将废弃
|
// Deprecated: TODO: 即将废弃
|
||||||
type ApplyConfig struct {
|
type ApplyConfig struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Access string `json:"access"`
|
ProviderAccessId string `json:"providerAccessId"`
|
||||||
KeyAlgorithm string `json:"keyAlgorithm"`
|
KeyAlgorithm string `json:"keyAlgorithm"`
|
||||||
Nameservers string `json:"nameservers"`
|
Nameservers string `json:"nameservers"`
|
||||||
PropagationTimeout int64 `json:"propagationTimeout"`
|
PropagationTimeout int32 `json:"propagationTimeout"`
|
||||||
DisableFollowCNAME bool `json:"disableFollowCNAME"`
|
DisableFollowCNAME bool `json:"disableFollowCNAME"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: TODO: 即将废弃
|
// Deprecated: TODO: 即将废弃
|
||||||
type DeployConfig struct {
|
type DeployConfig struct {
|
||||||
Id string `json:"id"`
|
NodeId string `json:"nodeId"`
|
||||||
Access string `json:"access"`
|
NodeConfig map[string]any `json:"nodeConfig"`
|
||||||
Type string `json:"type"`
|
Provider string `json:"provider"`
|
||||||
Config map[string]any `json:"config"`
|
ProviderAccessId string `json:"providerAccessId"`
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/usual2970/certimate/internal/pkg/utils/maps"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -47,33 +46,19 @@ type WorkflowNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigString(key string) string {
|
func (n *WorkflowNode) GetConfigString(key string) string {
|
||||||
if v, ok := n.Config[key]; ok {
|
return maps.GetValueAsString(n.Config, key)
|
||||||
if s, ok := v.(string); ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigBool(key string) bool {
|
func (n *WorkflowNode) GetConfigBool(key string) bool {
|
||||||
if v, ok := n.Config[key]; ok {
|
return maps.GetValueAsBool(n.Config, key)
|
||||||
if b, ok := v.(bool); ok {
|
}
|
||||||
return b
|
|
||||||
}
|
func (n *WorkflowNode) GetConfigInt32(key string) int32 {
|
||||||
}
|
return maps.GetValueAsInt32(n.Config, key)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *WorkflowNode) GetConfigInt64(key string) int64 {
|
func (n *WorkflowNode) GetConfigInt64(key string) int64 {
|
||||||
// 先转成字符串,再转成 int64
|
return maps.GetValueAsInt64(n.Config, key)
|
||||||
if v, ok := n.Config[key]; ok {
|
|
||||||
temp := fmt.Sprintf("%v", v)
|
|
||||||
if i, err := strconv.ParseInt(temp, 10, 64); err == nil {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorkflowNodeIO struct {
|
type WorkflowNodeIO struct {
|
||||||
|
@ -54,13 +54,6 @@ func NewWithLogger(config *WebhookDeployerConfig, logger logger.Logger) (*Webhoo
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type webhookData struct {
|
|
||||||
SubjectAltNames string `json:"subjectAltNames"`
|
|
||||||
Certificate string `json:"certificate"`
|
|
||||||
PrivateKey string `json:"privateKey"`
|
|
||||||
Variables map[string]string `json:"variables"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *WebhookDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
func (d *WebhookDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||||
certX509, err := x509.ParseCertificateFromPEM(certPem)
|
certX509, err := x509.ParseCertificateFromPEM(certPem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -118,6 +118,12 @@ func GetValueOrDefaultAsInt64(dict map[string]any, key string, defaultValue int6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if result, ok := value.(int32); ok {
|
||||||
|
if result != 0 {
|
||||||
|
return int64(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 兼容字符串类型的值
|
// 兼容字符串类型的值
|
||||||
if str, ok := value.(string); ok {
|
if str, ok := value.(string); ok {
|
||||||
if result, err := strconv.ParseInt(str, 10, 64); err == nil {
|
if result, err := strconv.ParseInt(str, 10, 64); err == nil {
|
||||||
|
@ -59,15 +59,16 @@ func (d *deployNode) Run(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
accessRepo := repository.NewAccessRepository()
|
accessRepo := repository.NewAccessRepository()
|
||||||
access, err := accessRepo.GetById(context.Background(), d.node.GetConfigString("access"))
|
access, err := accessRepo.GetById(context.Background(), d.node.GetConfigString("providerAccessId"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.AddOutput(ctx, d.node.Name, "获取授权配置失败", err.Error())
|
d.AddOutput(ctx, d.node.Name, "获取授权配置失败", err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
option := &deployer.DeployerOption{
|
option := &deployer.DeployerOption{
|
||||||
DomainId: d.node.Id,
|
DomainId: d.node.Id,
|
||||||
Domain: cert.SAN,
|
Domain: cert.SAN,
|
||||||
Access: access.Config,
|
AccessConfig: access.Config,
|
||||||
AccessRecord: access,
|
AccessRecord: access,
|
||||||
Certificate: applicant.Certificate{
|
Certificate: applicant.Certificate{
|
||||||
CertUrl: cert.CertUrl,
|
CertUrl: cert.CertUrl,
|
||||||
@ -77,10 +78,10 @@ func (d *deployNode) Run(ctx context.Context) error {
|
|||||||
IssuerCertificate: cert.IssuerCertificate,
|
IssuerCertificate: cert.IssuerCertificate,
|
||||||
},
|
},
|
||||||
DeployConfig: domain.DeployConfig{
|
DeployConfig: domain.DeployConfig{
|
||||||
Id: d.node.Id,
|
NodeId: d.node.Id,
|
||||||
Access: access.Id,
|
NodeConfig: d.node.Config,
|
||||||
Type: d.node.GetConfigString("provider"),
|
Provider: d.node.GetConfigString("provider"),
|
||||||
Config: d.node.Config,
|
ProviderAccessId: access.Id,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import AccessProviderSelect from "@/components/provider/AccessProviderSelect";
|
import AccessProviderSelect from "@/components/provider/AccessProviderSelect";
|
||||||
import { type AccessModel } from "@/domain/access";
|
import { type AccessModel } from "@/domain/access";
|
||||||
import { ACCESS_PROVIDERS, accessProvidersMap } from "@/domain/provider";
|
import { ACCESS_PROVIDERS } from "@/domain/provider";
|
||||||
import { useAntdForm } from "@/hooks";
|
import { useAntdForm } from "@/hooks";
|
||||||
|
|
||||||
import AccessEditFormACMEHttpReqConfig from "./AccessEditFormACMEHttpReqConfig";
|
import AccessEditFormACMEHttpReqConfig from "./AccessEditFormACMEHttpReqConfig";
|
||||||
|
@ -50,7 +50,7 @@ const WorkflowRunDetailDrawer = ({ data, loading, trigger, ...props }: WorkflowR
|
|||||||
{item.outputs.map((output, j) => {
|
{item.outputs.map((output, j) => {
|
||||||
return (
|
return (
|
||||||
<div key={j} className="flex space-x-2 text-sm">
|
<div key={j} className="flex space-x-2 text-sm">
|
||||||
<div>[{dayjs(output.time).format("YYYY-MM-DD HH:mm:ss")}]</div>
|
<div className="whitespace-nowrap">[{dayjs(output.time).format("YYYY-MM-DD HH:mm:ss")}]</div>
|
||||||
{output.error ? <div className="text-red-500">{output.error}</div> : <div>{output.content}</div>}
|
{output.error ? <div className="text-red-500">{output.error}</div> : <div>{output.content}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -12,8 +12,9 @@ import MultipleInput from "@/components/MultipleInput";
|
|||||||
import AccessEditModal from "@/components/access/AccessEditModal";
|
import AccessEditModal from "@/components/access/AccessEditModal";
|
||||||
import AccessSelect from "@/components/access/AccessSelect";
|
import AccessSelect from "@/components/access/AccessSelect";
|
||||||
import { ACCESS_USAGES, accessProvidersMap } from "@/domain/provider";
|
import { ACCESS_USAGES, accessProvidersMap } from "@/domain/provider";
|
||||||
import { type WorkflowNode } from "@/domain/workflow";
|
import { type WorkflowApplyNodeConfig, type WorkflowNode } from "@/domain/workflow";
|
||||||
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
||||||
|
import { useAccessesStore } from "@/stores/access";
|
||||||
import { useContactEmailsStore } from "@/stores/contact";
|
import { useContactEmailsStore } from "@/stores/contact";
|
||||||
import { useWorkflowStore } from "@/stores/workflow";
|
import { useWorkflowStore } from "@/stores/workflow";
|
||||||
import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators";
|
import { validDomainName, validIPv4Address, validIPv6Address } from "@/utils/validators";
|
||||||
@ -25,12 +26,10 @@ export type ApplyNodeFormProps = {
|
|||||||
|
|
||||||
const MULTIPLE_INPUT_DELIMITER = ";";
|
const MULTIPLE_INPUT_DELIMITER = ";";
|
||||||
|
|
||||||
const initFormModel = () => {
|
const initFormModel = (): Partial<WorkflowApplyNodeConfig> => {
|
||||||
return {
|
return {
|
||||||
domain: "",
|
|
||||||
keyAlgorithm: "RSA2048",
|
keyAlgorithm: "RSA2048",
|
||||||
nameservers: "",
|
propagationTimeout: 120,
|
||||||
propagationTimeout: 60,
|
|
||||||
disableFollowCNAME: true,
|
disableFollowCNAME: true,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -38,6 +37,7 @@ const initFormModel = () => {
|
|||||||
const ApplyNodeForm = ({ node }: ApplyNodeFormProps) => {
|
const ApplyNodeForm = ({ node }: ApplyNodeFormProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { accesses } = useAccessesStore(useZustandShallowSelector("accesses"));
|
||||||
const { addEmail } = useContactEmailsStore(useZustandShallowSelector("addEmail"));
|
const { addEmail } = useContactEmailsStore(useZustandShallowSelector("addEmail"));
|
||||||
const { updateNode } = useWorkflowStore(useZustandShallowSelector(["updateNode"]));
|
const { updateNode } = useWorkflowStore(useZustandShallowSelector(["updateNode"]));
|
||||||
const { hidePanel } = usePanel();
|
const { hidePanel } = usePanel();
|
||||||
@ -49,7 +49,9 @@ const ApplyNodeForm = ({ node }: ApplyNodeFormProps) => {
|
|||||||
.every((e) => validDomainName(e, true));
|
.every((e) => validDomainName(e, true));
|
||||||
}, t("common.errmsg.domain_invalid")),
|
}, t("common.errmsg.domain_invalid")),
|
||||||
email: z.string({ message: t("workflow_node.apply.form.email.placeholder") }).email("common.errmsg.email_invalid"),
|
email: z.string({ message: t("workflow_node.apply.form.email.placeholder") }).email("common.errmsg.email_invalid"),
|
||||||
access: z.string({ message: t("workflow_node.apply.form.access.placeholder") }).min(1, t("workflow_node.apply.form.access.placeholder")),
|
providerAccessId: z
|
||||||
|
.string({ message: t("workflow_node.apply.form.provider_access.placeholder") })
|
||||||
|
.min(1, t("workflow_node.apply.form.provider_access.placeholder")),
|
||||||
keyAlgorithm: z.string().nullish(),
|
keyAlgorithm: z.string().nullish(),
|
||||||
nameservers: z
|
nameservers: z
|
||||||
.string()
|
.string()
|
||||||
@ -74,13 +76,16 @@ const ApplyNodeForm = ({ node }: ApplyNodeFormProps) => {
|
|||||||
formPending,
|
formPending,
|
||||||
formProps,
|
formProps,
|
||||||
} = useAntdForm<z.infer<typeof formSchema>>({
|
} = useAntdForm<z.infer<typeof formSchema>>({
|
||||||
initialValues: node?.config ?? initFormModel(),
|
initialValues: (node?.config as WorkflowApplyNodeConfig) ?? initFormModel(),
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
await formInst.validateFields();
|
await formInst.validateFields();
|
||||||
await addEmail(values.email);
|
await addEmail(values.email);
|
||||||
await updateNode(
|
await updateNode(
|
||||||
produce(node, (draft) => {
|
produce(node, (draft) => {
|
||||||
draft.config = { ...values };
|
draft.config = {
|
||||||
|
provider: accesses.find((e) => e.id === values.providerAccessId)?.provider,
|
||||||
|
...values,
|
||||||
|
} as WorkflowApplyNodeConfig;
|
||||||
draft.validated = true;
|
draft.validated = true;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -146,8 +151,8 @@ const ApplyNodeForm = ({ node }: ApplyNodeFormProps) => {
|
|||||||
<label className="mb-1 block">
|
<label className="mb-1 block">
|
||||||
<div className="flex w-full items-center justify-between gap-4">
|
<div className="flex w-full items-center justify-between gap-4">
|
||||||
<div className="max-w-full grow truncate">
|
<div className="max-w-full grow truncate">
|
||||||
<span>{t("workflow_node.apply.form.access.label")}</span>
|
<span>{t("workflow_node.apply.form.provider_access.label")}</span>
|
||||||
<Tooltip title={t("workflow_node.apply.form.access.tooltip")}>
|
<Tooltip title={t("workflow_node.apply.form.provider_access.tooltip")}>
|
||||||
<Typography.Text className="ms-1" type="secondary">
|
<Typography.Text className="ms-1" type="secondary">
|
||||||
<QuestionCircleOutlinedIcon />
|
<QuestionCircleOutlinedIcon />
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
@ -159,22 +164,22 @@ const ApplyNodeForm = ({ node }: ApplyNodeFormProps) => {
|
|||||||
trigger={
|
trigger={
|
||||||
<Button size="small" type="link">
|
<Button size="small" type="link">
|
||||||
<PlusOutlinedIcon />
|
<PlusOutlinedIcon />
|
||||||
{t("workflow_node.apply.form.access.button")}
|
{t("workflow_node.apply.form.provider_access.button")}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
onSubmit={(record) => {
|
onSubmit={(record) => {
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
if (ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.APPLY === provider?.usage) {
|
if (ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.APPLY === provider?.usage) {
|
||||||
formInst.setFieldValue("access", record.id);
|
formInst.setFieldValue("providerAccessId", record.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<Form.Item name="access" rules={[formRule]}>
|
<Form.Item name="providerAccessId" rules={[formRule]}>
|
||||||
<AccessSelect
|
<AccessSelect
|
||||||
placeholder={t("workflow_node.apply.form.access.placeholder")}
|
placeholder={t("workflow_node.apply.form.provider_access.placeholder")}
|
||||||
filter={(record) => {
|
filter={(record) => {
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
return ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.APPLY === provider?.usage;
|
return ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.APPLY === provider?.usage;
|
||||||
|
@ -12,7 +12,7 @@ import AccessSelect from "@/components/access/AccessSelect";
|
|||||||
import DeployProviderPicker from "@/components/provider/DeployProviderPicker";
|
import DeployProviderPicker from "@/components/provider/DeployProviderPicker";
|
||||||
import DeployProviderSelect from "@/components/provider/DeployProviderSelect";
|
import DeployProviderSelect from "@/components/provider/DeployProviderSelect";
|
||||||
import { ACCESS_USAGES, DEPLOY_PROVIDERS, accessProvidersMap, deployProvidersMap } from "@/domain/provider";
|
import { ACCESS_USAGES, DEPLOY_PROVIDERS, accessProvidersMap, deployProvidersMap } from "@/domain/provider";
|
||||||
import { type WorkflowNode } from "@/domain/workflow";
|
import { type WorkflowDeployNodeConfig, type WorkflowNode } from "@/domain/workflow";
|
||||||
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
||||||
import { useWorkflowStore } from "@/stores/workflow";
|
import { useWorkflowStore } from "@/stores/workflow";
|
||||||
import { usePanel } from "../PanelProvider";
|
import { usePanel } from "../PanelProvider";
|
||||||
@ -44,7 +44,7 @@ export type DeployFormProps = {
|
|||||||
node: WorkflowNode;
|
node: WorkflowNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initFormModel = () => {
|
const initFormModel = (): Partial<WorkflowDeployNodeConfig> => {
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ const DeployNodeForm = ({ node }: DeployFormProps) => {
|
|||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.string({ message: t("workflow_node.deploy.form.provider.placeholder") }).nonempty(t("workflow_node.deploy.form.provider.placeholder")),
|
provider: z.string({ message: t("workflow_node.deploy.form.provider.placeholder") }).nonempty(t("workflow_node.deploy.form.provider.placeholder")),
|
||||||
access: z
|
providerAccessId: z
|
||||||
.string({ message: t("workflow_node.deploy.form.provider_access.placeholder") })
|
.string({ message: t("workflow_node.deploy.form.provider_access.placeholder") })
|
||||||
.nonempty(t("workflow_node.deploy.form.provider_access.placeholder")),
|
.nonempty(t("workflow_node.deploy.form.provider_access.placeholder")),
|
||||||
certificate: z.string({ message: t("workflow_node.deploy.form.certificate.placeholder") }).nonempty(t("workflow_node.deploy.form.certificate.placeholder")),
|
certificate: z.string({ message: t("workflow_node.deploy.form.certificate.placeholder") }).nonempty(t("workflow_node.deploy.form.certificate.placeholder")),
|
||||||
@ -67,7 +67,7 @@ const DeployNodeForm = ({ node }: DeployFormProps) => {
|
|||||||
formPending,
|
formPending,
|
||||||
formProps,
|
formProps,
|
||||||
} = useAntdForm<z.infer<typeof formSchema>>({
|
} = useAntdForm<z.infer<typeof formSchema>>({
|
||||||
initialValues: node?.config ?? initFormModel(),
|
initialValues: (node?.config as WorkflowDeployNodeConfig) ?? initFormModel(),
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
await formInst.validateFields();
|
await formInst.validateFields();
|
||||||
await updateNode(
|
await updateNode(
|
||||||
@ -160,7 +160,7 @@ const DeployNodeForm = ({ node }: DeployFormProps) => {
|
|||||||
const oldValues = formInst.getFieldsValue();
|
const oldValues = formInst.getFieldsValue();
|
||||||
const newValues: Record<string, unknown> = {};
|
const newValues: Record<string, unknown> = {};
|
||||||
for (const key in oldValues) {
|
for (const key in oldValues) {
|
||||||
if (key === "provider" || key === "access" || key === "certificate") {
|
if (key === "provider" || key === "providerAccessId" || key === "certificate") {
|
||||||
newValues[key] = oldValues[key];
|
newValues[key] = oldValues[key];
|
||||||
} else {
|
} else {
|
||||||
newValues[key] = undefined;
|
newValues[key] = undefined;
|
||||||
@ -169,7 +169,7 @@ const DeployNodeForm = ({ node }: DeployFormProps) => {
|
|||||||
formInst.setFieldsValue(newValues);
|
formInst.setFieldsValue(newValues);
|
||||||
|
|
||||||
if (deployProvidersMap.get(fieldProvider)?.provider !== deployProvidersMap.get(value)?.provider) {
|
if (deployProvidersMap.get(fieldProvider)?.provider !== deployProvidersMap.get(value)?.provider) {
|
||||||
formInst.setFieldValue("access", undefined);
|
formInst.setFieldValue("providerAccessId", undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -205,14 +205,14 @@ const DeployNodeForm = ({ node }: DeployFormProps) => {
|
|||||||
onSubmit={(record) => {
|
onSubmit={(record) => {
|
||||||
const provider = accessProvidersMap.get(record.provider);
|
const provider = accessProvidersMap.get(record.provider);
|
||||||
if (ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.DEPLOY === provider?.usage) {
|
if (ACCESS_USAGES.ALL === provider?.usage || ACCESS_USAGES.DEPLOY === provider?.usage) {
|
||||||
formInst.setFieldValue("access", record.id);
|
formInst.setFieldValue("providerAccessId", record.id);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<Form.Item name="access" rules={[formRule]}>
|
<Form.Item name="providerAccessId" rules={[formRule]}>
|
||||||
<AccessSelect
|
<AccessSelect
|
||||||
placeholder={t("workflow_node.deploy.form.provider_access.placeholder")}
|
placeholder={t("workflow_node.deploy.form.provider_access.placeholder")}
|
||||||
filter={(record) => {
|
filter={(record) => {
|
||||||
|
@ -8,7 +8,7 @@ import { produce } from "immer";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { notifyChannelsMap } from "@/domain/settings";
|
import { notifyChannelsMap } from "@/domain/settings";
|
||||||
import { type WorkflowNode } from "@/domain/workflow";
|
import { type WorkflowNode, type WorkflowNotifyNodeConfig } from "@/domain/workflow";
|
||||||
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
||||||
import { useNotifyChannelsStore } from "@/stores/notify";
|
import { useNotifyChannelsStore } from "@/stores/notify";
|
||||||
import { useWorkflowStore } from "@/stores/workflow";
|
import { useWorkflowStore } from "@/stores/workflow";
|
||||||
@ -18,7 +18,7 @@ export type NotifyNodeFormProps = {
|
|||||||
node: WorkflowNode;
|
node: WorkflowNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initFormModel = () => {
|
const initFormModel = (): Partial<WorkflowNotifyNodeConfig> => {
|
||||||
return {
|
return {
|
||||||
subject: "Completed!",
|
subject: "Completed!",
|
||||||
message: "Your workflow has been completed on Certimate.",
|
message: "Your workflow has been completed on Certimate.",
|
||||||
@ -57,7 +57,7 @@ const NotifyNodeForm = ({ node }: NotifyNodeFormProps) => {
|
|||||||
formPending,
|
formPending,
|
||||||
formProps,
|
formProps,
|
||||||
} = useAntdForm<z.infer<typeof formSchema>>({
|
} = useAntdForm<z.infer<typeof formSchema>>({
|
||||||
initialValues: node?.config ?? initFormModel(),
|
initialValues: (node?.config as WorkflowNotifyNodeConfig) ?? initFormModel(),
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
await formInst.validateFields();
|
await formInst.validateFields();
|
||||||
await updateNode(
|
await updateNode(
|
||||||
|
@ -7,7 +7,7 @@ import { produce } from "immer";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import Show from "@/components/Show";
|
import Show from "@/components/Show";
|
||||||
import { type WorkflowNode } from "@/domain/workflow";
|
import { type WorkflowNode, type WorkflowStartNodeConfig } from "@/domain/workflow";
|
||||||
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
import { useAntdForm, useZustandShallowSelector } from "@/hooks";
|
||||||
import { useWorkflowStore } from "@/stores/workflow";
|
import { useWorkflowStore } from "@/stores/workflow";
|
||||||
import { getNextCronExecutions, validCronExpression } from "@/utils/cron";
|
import { getNextCronExecutions, validCronExpression } from "@/utils/cron";
|
||||||
@ -17,7 +17,7 @@ export type StartNodeFormProps = {
|
|||||||
node: WorkflowNode;
|
node: WorkflowNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initFormModel = () => {
|
const initFormModel = (): WorkflowStartNodeConfig => {
|
||||||
return {
|
return {
|
||||||
executionMethod: "auto",
|
executionMethod: "auto",
|
||||||
crontab: "0 0 * * *",
|
crontab: "0 0 * * *",
|
||||||
@ -54,7 +54,7 @@ const StartNodeForm = ({ node }: StartNodeFormProps) => {
|
|||||||
formPending,
|
formPending,
|
||||||
formProps,
|
formProps,
|
||||||
} = useAntdForm<z.infer<typeof formSchema>>({
|
} = useAntdForm<z.infer<typeof formSchema>>({
|
||||||
initialValues: node?.config ?? initFormModel(),
|
initialValues: (node?.config as WorkflowStartNodeConfig) ?? initFormModel(),
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
await formInst.validateFields();
|
await formInst.validateFields();
|
||||||
await updateNode(
|
await updateNode(
|
||||||
|
@ -85,6 +85,39 @@ export type WorkflowNode = {
|
|||||||
validated?: boolean;
|
validated?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type WorkflowStartNodeConfig = {
|
||||||
|
executionMethod: string;
|
||||||
|
crontab?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkflowApplyNodeConfig = {
|
||||||
|
domain: string;
|
||||||
|
email: string;
|
||||||
|
provider: string;
|
||||||
|
providerAccessId: string;
|
||||||
|
keyAlgorithm: string;
|
||||||
|
nameservers?: string;
|
||||||
|
propagationTimeout?: number;
|
||||||
|
disableFollowCNAME?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkflowDeployNodeConfig = {
|
||||||
|
provider: string;
|
||||||
|
providerAccessId: string;
|
||||||
|
certificate: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkflowNotifyNodeConfig = {
|
||||||
|
channel: string;
|
||||||
|
subject: string;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkflowBranchNodeConfig = never;
|
||||||
|
|
||||||
|
export type WorkflowEndNodeConfig = never;
|
||||||
|
|
||||||
export type WorkflowNodeIO = {
|
export type WorkflowNodeIO = {
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
@ -150,7 +183,7 @@ export const newNode = (nodeType: WorkflowNodeType, options: NewNodeOptions = {}
|
|||||||
case WorkflowNodeType.Apply:
|
case WorkflowNodeType.Apply:
|
||||||
case WorkflowNodeType.Deploy:
|
case WorkflowNodeType.Deploy:
|
||||||
{
|
{
|
||||||
node.config = {};
|
node.config = {} as Record<string, unknown>;
|
||||||
node.input = workflowNodeTypeDefaultInputs.get(nodeType);
|
node.input = workflowNodeTypeDefaultInputs.get(nodeType);
|
||||||
node.output = workflowNodeTypeDefaultOutputs.get(nodeType);
|
node.output = workflowNodeTypeDefaultOutputs.get(nodeType);
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,10 @@
|
|||||||
"workflow_node.apply.form.email.label": "Contact email",
|
"workflow_node.apply.form.email.label": "Contact email",
|
||||||
"workflow_node.apply.form.email.placeholder": "Please enter contact email",
|
"workflow_node.apply.form.email.placeholder": "Please enter contact email",
|
||||||
"workflow_node.apply.form.email.tooltip": "Contact information required for SSL certificate application. Please pay attention to the <a href=\"https://letsencrypt.org/docs/rate-limits/\" target=\"_blank\">rate limits</a>.",
|
"workflow_node.apply.form.email.tooltip": "Contact information required for SSL certificate application. Please pay attention to the <a href=\"https://letsencrypt.org/docs/rate-limits/\" target=\"_blank\">rate limits</a>.",
|
||||||
"workflow_node.apply.form.access.label": "DNS provider authorization",
|
"workflow_node.apply.form.provider_access.label": "DNS provider authorization",
|
||||||
"workflow_node.apply.form.access.placeholder": "Please select an authorization of DNS provider",
|
"workflow_node.apply.form.provider_access.placeholder": "Please select an authorization of DNS provider",
|
||||||
"workflow_node.apply.form.access.tooltip": "Used to manage DNS records during ACME DNS-01 authentication.",
|
"workflow_node.apply.form.provider_access.tooltip": "Used to manage DNS records during ACME DNS-01 authentication.",
|
||||||
"workflow_node.apply.form.access.button": "Create",
|
"workflow_node.apply.form.provider_access.button": "Create",
|
||||||
"workflow_node.apply.form.advanced_config.label": "Advanced settings",
|
"workflow_node.apply.form.advanced_config.label": "Advanced settings",
|
||||||
"workflow_node.apply.form.key_algorithm.label": "Certificate key algorithm",
|
"workflow_node.apply.form.key_algorithm.label": "Certificate key algorithm",
|
||||||
"workflow_node.apply.form.key_algorithm.placeholder": "Please select certificate key algorithm",
|
"workflow_node.apply.form.key_algorithm.placeholder": "Please select certificate key algorithm",
|
||||||
|
@ -27,10 +27,10 @@
|
|||||||
"workflow_node.apply.form.email.label": "联系邮箱",
|
"workflow_node.apply.form.email.label": "联系邮箱",
|
||||||
"workflow_node.apply.form.email.placeholder": "请输入联系邮箱",
|
"workflow_node.apply.form.email.placeholder": "请输入联系邮箱",
|
||||||
"workflow_node.apply.form.email.tooltip": "申请签发 SSL 证书时所需的联系方式。请注意 Let's Encrypt 账户注册的速率限制。<br><a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">点此了解更多</a>。",
|
"workflow_node.apply.form.email.tooltip": "申请签发 SSL 证书时所需的联系方式。请注意 Let's Encrypt 账户注册的速率限制。<br><a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">点此了解更多</a>。",
|
||||||
"workflow_node.apply.form.access.label": "DNS 提供商授权",
|
"workflow_node.apply.form.provider_access.label": "DNS 提供商授权",
|
||||||
"workflow_node.apply.form.access.placeholder": "请选择 DNS 提供商授权",
|
"workflow_node.apply.form.provider_access.placeholder": "请选择 DNS 提供商授权",
|
||||||
"workflow_node.apply.form.access.tooltip": "用于 ACME DNS-01 认证时操作域名解析记录,注意与部署阶段所需的主机提供商相区分。",
|
"workflow_node.apply.form.provider_access.tooltip": "用于 ACME DNS-01 认证时操作域名解析记录,注意与部署阶段所需的主机提供商相区分。",
|
||||||
"workflow_node.apply.form.access.button": "新建",
|
"workflow_node.apply.form.provider_access.button": "新建",
|
||||||
"workflow_node.apply.form.advanced_config.label": "高级设置",
|
"workflow_node.apply.form.advanced_config.label": "高级设置",
|
||||||
"workflow_node.apply.form.key_algorithm.label": "数字证书算法",
|
"workflow_node.apply.form.key_algorithm.label": "数字证书算法",
|
||||||
"workflow_node.apply.form.key_algorithm.placeholder": "请选择数字证书算法",
|
"workflow_node.apply.form.key_algorithm.placeholder": "请选择数字证书算法",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user