mirror of
https://github.com/certimate-go/certimate.git
synced 2026-06-22 21:05:48 +08:00
265 lines
12 KiB
Go
265 lines
12 KiB
Go
package domain
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/samber/lo"
|
||
|
||
"github.com/certimate-go/certimate/internal/domain/expr"
|
||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||
)
|
||
|
||
const CollectionNameWorkflow = "workflow"
|
||
|
||
type Workflow struct {
|
||
Meta
|
||
Name string `json:"name" db:"name"`
|
||
Description string `json:"description" db:"description"`
|
||
Trigger WorkflowTriggerType `json:"trigger" db:"trigger"`
|
||
TriggerCron string `json:"triggerCron" db:"triggerCron"`
|
||
Enabled bool `json:"enabled" db:"enabled"`
|
||
GraphDraft *WorkflowGraph `json:"graphDraft" db:"graphDraft"`
|
||
GraphContent *WorkflowGraph `json:"graphContent" db:"graphContent"`
|
||
HasDraft bool `json:"hasDraft" db:"hasDraft"`
|
||
HasContent bool `json:"hasContent" db:"hasContent"`
|
||
LastRunId string `json:"lastRunId" db:"lastRunRef"`
|
||
LastRunStatus WorkflowRunStatusType `json:"lastRunStatus" db:"lastRunStatus"`
|
||
LastRunTime time.Time `json:"lastRunTime" db:"lastRunTime"`
|
||
}
|
||
|
||
type WorkflowGraph struct {
|
||
Nodes []*WorkflowNode `json:"nodes"`
|
||
}
|
||
|
||
func (g *WorkflowGraph) GetNodeById(nodeId string) (*WorkflowNode, bool) {
|
||
return g.getNodeInBlocksById(g.Nodes, nodeId)
|
||
}
|
||
|
||
func (g *WorkflowGraph) getNodeInBlocksById(blocks []*WorkflowNode, nodeId string) (*WorkflowNode, bool) {
|
||
for _, node := range blocks {
|
||
if node.Id == nodeId {
|
||
return node, true
|
||
}
|
||
|
||
if len(node.Blocks) > 0 {
|
||
if found, ok := g.getNodeInBlocksById(node.Blocks, nodeId); ok {
|
||
return found, true
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil, false
|
||
}
|
||
|
||
func (g *WorkflowGraph) Verify() error {
|
||
if len(g.Nodes) < 2 {
|
||
return fmt.Errorf("invalid nodes length of graph")
|
||
} else if g.Nodes[0].Type != WorkflowNodeTypeStart {
|
||
return fmt.Errorf("the first node is not a start node")
|
||
} else if g.Nodes[len(g.Nodes)-1].Type != WorkflowNodeTypeEnd {
|
||
return fmt.Errorf("the last node is not an end node")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (g *WorkflowGraph) Clone() *WorkflowGraph {
|
||
return &WorkflowGraph{
|
||
Nodes: g.Nodes,
|
||
}
|
||
}
|
||
|
||
type WorkflowTriggerType string
|
||
|
||
const (
|
||
WorkflowTriggerTypeScheduled = WorkflowTriggerType("scheduled")
|
||
WorkflowTriggerTypeManual = WorkflowTriggerType("manual")
|
||
)
|
||
|
||
type WorkflowNode struct {
|
||
Id string `json:"id"` // 节点 ID 只在该工作流中唯一,在全局中不保证唯一性
|
||
Type WorkflowNodeType `json:"type"`
|
||
Data WorkflowNodeData `json:"data"`
|
||
Blocks []*WorkflowNode `json:"blocks,omitempty"`
|
||
}
|
||
|
||
type WorkflowNodeType string
|
||
|
||
const (
|
||
WorkflowNodeTypeStart = WorkflowNodeType("start")
|
||
WorkflowNodeTypeEnd = WorkflowNodeType("end")
|
||
WorkflowNodeTypeCondition = WorkflowNodeType("condition")
|
||
WorkflowNodeTypeBranchBlock = WorkflowNodeType("branchBlock")
|
||
WorkflowNodeTypeTryCatch = WorkflowNodeType("tryCatch")
|
||
WorkflowNodeTypeTryBlock = WorkflowNodeType("tryBlock")
|
||
WorkflowNodeTypeCatchBlock = WorkflowNodeType("catchBlock")
|
||
WorkflowNodeTypeDelay = WorkflowNodeType("delay")
|
||
WorkflowNodeTypeBizApply = WorkflowNodeType("bizApply")
|
||
WorkflowNodeTypeBizUpload = WorkflowNodeType("bizUpload")
|
||
WorkflowNodeTypeBizMonitor = WorkflowNodeType("bizMonitor")
|
||
WorkflowNodeTypeBizDeploy = WorkflowNodeType("bizDeploy")
|
||
WorkflowNodeTypeBizNotify = WorkflowNodeType("bizNotify")
|
||
)
|
||
|
||
type WorkflowNodeData struct {
|
||
Name string `json:"name"`
|
||
Disabled bool `json:"disabled,omitempty,omitzero"`
|
||
Config WorkflowNodeConfig `json:"config,omitempty,omitzero"`
|
||
}
|
||
|
||
type WorkflowNodeConfig map[string]any
|
||
|
||
func (c WorkflowNodeConfig) AsDelay() WorkflowNodeConfigForDelay {
|
||
return WorkflowNodeConfigForDelay{
|
||
Wait: xmaps.GetInt32(c, "wait"),
|
||
}
|
||
}
|
||
|
||
func (c WorkflowNodeConfig) AsBranchBlock() WorkflowNodeConfigForBranchBlock {
|
||
expression := c["expression"]
|
||
if expression == nil {
|
||
return WorkflowNodeConfigForBranchBlock{}
|
||
}
|
||
|
||
exprRaw, _ := json.Marshal(expression)
|
||
expr, err := expr.UnmarshalExpr([]byte(exprRaw))
|
||
if err != nil {
|
||
return WorkflowNodeConfigForBranchBlock{}
|
||
}
|
||
|
||
return WorkflowNodeConfigForBranchBlock{
|
||
Expression: expr,
|
||
}
|
||
}
|
||
|
||
func (c WorkflowNodeConfig) AsBizApply() WorkflowNodeConfigForBizApply {
|
||
domains := lo.Filter(strings.Split(xmaps.GetString(c, "domains"), ";"), func(s string, _ int) bool { return s != "" })
|
||
nameservers := lo.Filter(strings.Split(xmaps.GetString(c, "nameservers"), ";"), func(s string, _ int) bool { return s != "" })
|
||
|
||
return WorkflowNodeConfigForBizApply{
|
||
Domains: domains,
|
||
ContactEmail: xmaps.GetString(c, "contactEmail"),
|
||
ChallengeType: xmaps.GetString(c, "challengeType"),
|
||
Provider: xmaps.GetString(c, "provider"),
|
||
ProviderAccessId: xmaps.GetString(c, "providerAccessId"),
|
||
ProviderConfig: xmaps.GetKVMapAny(c, "providerConfig"),
|
||
KeyAlgorithm: xmaps.GetOrDefaultString(c, "keyAlgorithm", string(CertificateKeyAlgorithmTypeRSA2048)),
|
||
CAProvider: xmaps.GetString(c, "caProvider"),
|
||
CAProviderAccessId: xmaps.GetString(c, "caProviderAccessId"),
|
||
CAProviderConfig: xmaps.GetKVMapAny(c, "caProviderConfig"),
|
||
ValidityLifetime: xmaps.GetString(c, "validityLifetime"),
|
||
ACMEProfile: xmaps.GetString(c, "acmeProfile"),
|
||
Nameservers: nameservers,
|
||
DnsPropagationWait: xmaps.GetInt32(c, "dnsPropagationWait"),
|
||
DnsPropagationTimeout: xmaps.GetInt32(c, "dnsPropagationTimeout"),
|
||
DnsTTL: xmaps.GetInt32(c, "dnsTTL"),
|
||
HttpDelayWait: xmaps.GetInt32(c, "httpDelayWait"),
|
||
DisableFollowCNAME: xmaps.GetBool(c, "disableFollowCNAME"),
|
||
DisableARI: xmaps.GetBool(c, "disableARI"),
|
||
SkipBeforeExpiryDays: xmaps.GetInt32(c, "skipBeforeExpiryDays"),
|
||
}
|
||
}
|
||
|
||
func (c WorkflowNodeConfig) AsBizUpload() WorkflowNodeConfigForBizUpload {
|
||
return WorkflowNodeConfigForBizUpload{
|
||
Source: xmaps.GetOrDefaultString(c, "source", "form"),
|
||
Certificate: xmaps.GetString(c, "certificate"),
|
||
PrivateKey: xmaps.GetString(c, "privateKey"),
|
||
}
|
||
}
|
||
|
||
func (c WorkflowNodeConfig) AsBizMonitor() WorkflowNodeConfigForBizMonitor {
|
||
host := xmaps.GetString(c, "host")
|
||
return WorkflowNodeConfigForBizMonitor{
|
||
Host: host,
|
||
Port: xmaps.GetOrDefaultInt32(c, "port", 443),
|
||
Domain: xmaps.GetOrDefaultString(c, "domain", host),
|
||
RequestPath: xmaps.GetString(c, "path"),
|
||
}
|
||
}
|
||
|
||
func (c WorkflowNodeConfig) AsBizDeploy() WorkflowNodeConfigForBizDeploy {
|
||
return WorkflowNodeConfigForBizDeploy{
|
||
CertificateOutputNodeId: xmaps.GetString(c, "certificateOutputNodeId"),
|
||
Provider: xmaps.GetString(c, "provider"),
|
||
ProviderAccessId: xmaps.GetString(c, "providerAccessId"),
|
||
ProviderConfig: xmaps.GetKVMapAny(c, "providerConfig"),
|
||
SkipOnLastSucceeded: xmaps.GetBool(c, "skipOnLastSucceeded"),
|
||
}
|
||
}
|
||
|
||
func (c WorkflowNodeConfig) AsBizNotify() WorkflowNodeConfigForBizNotify {
|
||
return WorkflowNodeConfigForBizNotify{
|
||
Provider: xmaps.GetString(c, "provider"),
|
||
ProviderAccessId: xmaps.GetString(c, "providerAccessId"),
|
||
ProviderConfig: xmaps.GetKVMapAny(c, "providerConfig"),
|
||
Subject: xmaps.GetString(c, "subject"),
|
||
Message: xmaps.GetString(c, "message"),
|
||
SkipOnAllPrevSkipped: xmaps.GetBool(c, "skipOnAllPrevSkipped"),
|
||
}
|
||
}
|
||
|
||
type WorkflowNodeConfigForDelay struct {
|
||
Wait int32 `json:"wait"` // 等待时间
|
||
}
|
||
|
||
type WorkflowNodeConfigForBranchBlock struct {
|
||
Expression expr.Expr `json:"expression"` // 条件表达式
|
||
}
|
||
|
||
type WorkflowNodeConfigForBizApply struct {
|
||
Domains []string `json:"domains"` // 域名列表,以半角分号分隔
|
||
ContactEmail string `json:"contactEmail"` // 联系邮箱
|
||
ChallengeType string `json:"challengeType"` // 质询方式
|
||
Provider string `json:"provider"` // 质询提供商
|
||
ProviderAccessId string `json:"providerAccessId"` // 质询提供商授权记录 ID
|
||
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 质询提供商额外配置
|
||
CAProvider string `json:"caProvider,omitempty"` // CA 提供商(零值时使用全局配置)
|
||
CAProviderAccessId string `json:"caProviderAccessId,omitempty"` // CA 提供商授权记录 ID
|
||
CAProviderConfig map[string]any `json:"caProviderConfig,omitempty"` // CA 提供商额外配置
|
||
KeyAlgorithm string `json:"keyAlgorithm,omitempty"` // 证书算法
|
||
ValidityLifetime string `json:"validityLifetime,omitempty"` // 证书有效期,形如 "30d"、"6h"
|
||
ACMEProfile string `json:"acmeProfile,omitempty"` // ACME Profiles Extension
|
||
Nameservers []string `json:"nameservers,omitempty"` // DNS 服务器列表,以半角分号分隔
|
||
DnsPropagationWait int32 `json:"dnsPropagationWait,omitempty"` // DNS 传播等待时间,等同于 lego 的 `--dns-propagation-wait` 参数
|
||
DnsPropagationTimeout int32 `json:"dnsPropagationTimeout,omitempty"` // DNS 传播检查超时时间(零值时使用提供商的默认值)
|
||
DnsTTL int32 `json:"dnsTTL,omitempty"` // DNS 解析记录 TTL(零值时使用提供商的默认值)
|
||
HttpDelayWait int32 `json:"httpDelayWait,omitempty"` // HTTP 等待时间
|
||
DisableFollowCNAME bool `json:"disableFollowCNAME,omitempty"` // 是否关闭 CNAME 跟随
|
||
DisableARI bool `json:"disableARI,omitempty"` // 是否关闭 ARI
|
||
SkipBeforeExpiryDays int32 `json:"skipBeforeExpiryDays,omitempty"` // 证书到期前多少天前跳过续期
|
||
}
|
||
|
||
type WorkflowNodeConfigForBizUpload struct {
|
||
Source string `json:"source"` // 证书来源(零值时默认值 "form")
|
||
Certificate string `json:"certificate"` // 证书,根据证书来源决定是 PEM 内容 / 文件路径 / URL
|
||
PrivateKey string `json:"privateKey"` // 私钥,根据证书来源决定是 PEM 内容 / 文件路径 / URL
|
||
}
|
||
|
||
type WorkflowNodeConfigForBizMonitor struct {
|
||
Host string `json:"host"` // 主机地址
|
||
Port int32 `json:"port,omitempty"` // 端口(零值时默认值 443)
|
||
Domain string `json:"domain,omitempty"` // 域名(零值时默认值 [Host])
|
||
RequestPath string `json:"requestPath,omitempty"` // 请求路径
|
||
}
|
||
|
||
type WorkflowNodeConfigForBizDeploy struct {
|
||
CertificateOutputNodeId string `json:"certificateOutputNodeId"` // 前序证书输出节点 ID
|
||
Provider string `json:"provider"` // 主机提供商
|
||
ProviderAccessId string `json:"providerAccessId,omitempty"` // 主机提供商授权记录 ID
|
||
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 主机提供商额外配置
|
||
SkipOnLastSucceeded bool `json:"skipOnLastSucceeded"` // 上次部署成功时是否跳过
|
||
}
|
||
|
||
type WorkflowNodeConfigForBizNotify struct {
|
||
Provider string `json:"provider"` // 通知提供商
|
||
ProviderAccessId string `json:"providerAccessId"` // 通知提供商授权记录 ID
|
||
ProviderConfig map[string]any `json:"providerConfig,omitempty"` // 通知提供商额外配置
|
||
Subject string `json:"subject"` // 通知主题
|
||
Message string `json:"message"` // 通知内容
|
||
SkipOnAllPrevSkipped bool `json:"skipOnAllPrevSkipped"` // 前序节点均已跳过时是否跳过
|
||
}
|