mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
feat: support mustache syntax on notification
This commit is contained in:
@@ -66,13 +66,13 @@ func (we *workflowEngine) Invoke(ctx context.Context, execution WorkflowExecutio
|
||||
wfIOs := newInOutManager()
|
||||
|
||||
wfVars := newVariableManager()
|
||||
wfVars.Set("workflow.id", execution.WorkflowId, "string")
|
||||
wfVars.Set("workflow.name", execution.WorkflowName, "string")
|
||||
wfVars.Set("run.id", execution.RunId, "string")
|
||||
wfVars.Set("run.trigger", execution.RunTrigger, "string")
|
||||
wfVars.Set("error.nodeId", "", "string")
|
||||
wfVars.Set("error.nodeName", "", "string")
|
||||
wfVars.Set("error.message", "", "string")
|
||||
wfVars.Set(stateVarKeyWorkflowId, execution.WorkflowId, "string")
|
||||
wfVars.Set(stateVarKeyWorkflowName, execution.WorkflowName, "string")
|
||||
wfVars.Set(stateVarKeyRunId, execution.RunId, "string")
|
||||
wfVars.Set(stateVarKeyRunTrigger, execution.RunTrigger, "string")
|
||||
wfVars.Set(stateVarKeyErrorNodeId, "", "string")
|
||||
wfVars.Set(stateVarKeyErrorNodeName, "", "string")
|
||||
wfVars.Set(stateVarKeyErrorMessage, "", "string")
|
||||
|
||||
wfCtx := (&WorkflowContext{}).
|
||||
SetExecutingWorkflow(execution.WorkflowId, execution.RunId, execution.Graph).
|
||||
@@ -164,9 +164,9 @@ func (we *workflowEngine) executeNode(wfCtx *WorkflowContext, node *Node) error
|
||||
execRes, err := executor.Execute(execCtx)
|
||||
if err != nil && !errors.Is(err, ErrTerminated) {
|
||||
if !errors.Is(err, ErrBlocksException) {
|
||||
wfCtx.variables.Set("error.nodeId", node.Id, "string")
|
||||
wfCtx.variables.Set("error.nodeName", node.Data.Name, "string")
|
||||
wfCtx.variables.Set("error.message", err.Error(), "string")
|
||||
wfCtx.variables.Set(stateVarKeyErrorNodeId, node.Id, "string")
|
||||
wfCtx.variables.Set(stateVarKeyErrorNodeName, node.Data.Name, "string")
|
||||
wfCtx.variables.Set(stateVarKeyErrorMessage, err.Error(), "string")
|
||||
}
|
||||
|
||||
we.fireOnNodeErrorHooks(wfCtx.ctx, node, err)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/notify"
|
||||
"github.com/certimate-go/certimate/internal/repository"
|
||||
@@ -37,14 +40,39 @@ func (ne *bizNotifyNodeExecutor) Execute(execCtx *NodeExecutionContext) (*NodeEx
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染通知模板
|
||||
reMustache := regexp.MustCompile(`\{\{\s*(\$[^\s]+)\s*\}\}`)
|
||||
reMustacheReplacer := func(match string) string {
|
||||
mustache := strings.TrimSpace(match[2 : len(match)-2])
|
||||
if mustache == "" {
|
||||
return match
|
||||
}
|
||||
|
||||
key := mustache[1:]
|
||||
if key == "" {
|
||||
return match
|
||||
} else if key == "now" {
|
||||
return time.Now().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// TODO: 支持作用域变量
|
||||
if state, ok := execCtx.variables.Get(key); ok {
|
||||
return state.ValueString()
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
subject := reMustache.ReplaceAllStringFunc(nodeCfg.Subject, reMustacheReplacer)
|
||||
message := reMustache.ReplaceAllStringFunc(nodeCfg.Message, reMustacheReplacer)
|
||||
|
||||
// 推送通知
|
||||
notifier := notify.NewClient(notify.WithLogger(ne.logger))
|
||||
notifyReq := ¬ify.SendNotificationRequest{
|
||||
Provider: nodeCfg.Provider,
|
||||
ProviderAccessConfig: providerAccessConfig,
|
||||
ProviderExtendedConfig: nodeCfg.ProviderConfig,
|
||||
Subject: nodeCfg.Subject,
|
||||
Message: nodeCfg.Message,
|
||||
Subject: subject,
|
||||
Message: message,
|
||||
}
|
||||
if _, err := notifier.SendNotification(execCtx.ctx, notifyReq); err != nil {
|
||||
ne.logger.Warn("could not send notification")
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -32,7 +30,7 @@ func (s VariableState) ValueString() string {
|
||||
}
|
||||
return valueAsTime.Format(time.RFC3339)
|
||||
default:
|
||||
return fmt.Sprintf("%v", s.Value)
|
||||
return fmt.Sprintf("[%s]%v", s.ValueType, s.Value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +47,6 @@ type VariableManager interface {
|
||||
TakeScoped(scope string, key string) (*VariableState, bool)
|
||||
Remove(key string) bool
|
||||
RemoveScoped(scope string, key string) bool
|
||||
|
||||
RenderTemplateString(template string) string
|
||||
}
|
||||
|
||||
type variableManager struct {
|
||||
@@ -153,35 +149,6 @@ func (m *variableManager) RemoveScoped(scope string, key string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m *variableManager) RenderTemplateString(template string) string {
|
||||
m.statesMtx.RLock()
|
||||
defer m.statesMtx.RUnlock()
|
||||
|
||||
replaceFunc := func(match string) string {
|
||||
mustache := strings.TrimSpace(match[2 : len(match)-2])
|
||||
if mustache == "" {
|
||||
return match
|
||||
}
|
||||
|
||||
key := mustache[1:]
|
||||
if key == "" {
|
||||
return match
|
||||
} else if key == "$now" {
|
||||
return time.Now().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// TODO: 支持作用域变量
|
||||
if state, ok := m.Get(key); ok {
|
||||
return state.ValueString()
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`\{\{\s*(\$[^\s]+)\s*\}\}`)
|
||||
return re.ReplaceAllStringFunc(template, replaceFunc)
|
||||
}
|
||||
|
||||
func newVariableManager() VariableManager {
|
||||
return &variableManager{
|
||||
states: make([]VariableState, 0),
|
||||
@@ -316,9 +283,16 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
stateVarKeyWorkflowId = "workflow.id" // ValueType: "string"
|
||||
stateVarKeyWorkflowName = "workflow.name" // ValueType: "string"
|
||||
stateVarKeyRunId = "run.id" // ValueType: "string"
|
||||
stateVarKeyRunTrigger = "run.trigger" // ValueType: "string"
|
||||
stateVarKeyNodeId = "node.id" // ValueType: "string"
|
||||
stateVarKeyNodeName = "node.name" // ValueType: "string"
|
||||
stateVarKeyNodeSkipped = "node.skipped" // ValueType: "boolean"
|
||||
stateVarKeyErrorNodeId = "error.nodeId" // ValueType: "string"
|
||||
stateVarKeyErrorNodeName = "error.nodeName" // ValueType: "string"
|
||||
stateVarKeyErrorMessage = "error.message" // ValueType: "string"
|
||||
stateVarKeyCertificateDomain = "certificate.domain" // ValueType: "string"
|
||||
stateVarKeyCertificateDomains = "certificate.domains" // ValueType: "string"
|
||||
stateVarKeyCertificateNotBefore = "certificate.notBefore" // ValueType: "datetime"
|
||||
|
||||
@@ -4,7 +4,6 @@ import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import CodeInput from "@/components/CodeInput";
|
||||
import Tips from "@/components/Tips";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
@@ -48,10 +47,6 @@ const BizDeployNodeConfigFieldsProviderWebhook = () => {
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.webhook_data.guide") }}></span>} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name={[parentNamePath, "timeout"]} label={t("workflow_node.deploy.form.webhook_timeout.label")} rules={[formRule]}>
|
||||
<Input
|
||||
type="number"
|
||||
|
||||
@@ -4,7 +4,6 @@ import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import CodeInput from "@/components/CodeInput";
|
||||
import Tips from "@/components/Tips";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
@@ -48,10 +47,6 @@ const BizNotifyNodeConfigFieldsProviderWebhook = () => {
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.webhook_data.guide") }}></span>} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name={[parentNamePath, "timeout"]} label={t("workflow_node.notify.form.webhook_timeout.label")} rules={[formRule]}>
|
||||
<Input
|
||||
type="number"
|
||||
|
||||
@@ -11,6 +11,7 @@ import AccessSelect from "@/components/access/AccessSelect";
|
||||
import NotificationProviderPicker from "@/components/provider/NotificationProviderPicker";
|
||||
import NotificationProviderSelect from "@/components/provider/NotificationProviderSelect";
|
||||
import Show from "@/components/Show";
|
||||
import Tips from "@/components/Tips";
|
||||
import { type AccessModel } from "@/domain/access";
|
||||
import { NOTIFICATION_PROVIDERS, notificationProvidersMap } from "@/domain/provider";
|
||||
import { type WorkflowNodeConfigForBizNotify, defaultNodeConfigForBizNotify } from "@/domain/workflow";
|
||||
@@ -143,6 +144,10 @@ const BizNotifyNodeConfigForm = ({ node, ...props }: BizNotifyNodeConfigFormProp
|
||||
<Form.Item name="message" label={t("workflow_node.notify.form.message.label")} rules={[formRule]}>
|
||||
<Input.TextArea autoSize={{ minRows: 3, maxRows: 10 }} placeholder={t("workflow_node.notify.form.message.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Tips message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.template.guide") }}></span>} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
<div id="channel" data-anchor="channel">
|
||||
|
||||
@@ -189,13 +189,17 @@ const BranchBlockNodeConfigExprInputBox = forwardRef<BranchBlockNodeConfigExprIn
|
||||
};
|
||||
|
||||
group.options.push({
|
||||
label: `${t("workflow.variables.type.certificate.label")} - ${t("workflow.variables.selector.validity.label")}`,
|
||||
value: `${node.id}#certificate.validity#boolean`,
|
||||
label: `${t("workflow.variables.type.certificate.label")} - ${t("workflow.variables.selector.hours_left.label")}`,
|
||||
value: `${node.id}#certificate.hoursLeft#number`,
|
||||
});
|
||||
group.options.push({
|
||||
label: `${t("workflow.variables.type.certificate.label")} - ${t("workflow.variables.selector.days_left.label")}`,
|
||||
value: `${node.id}#certificate.daysLeft#number`,
|
||||
});
|
||||
group.options.push({
|
||||
label: `${t("workflow.variables.type.certificate.label")} - ${t("workflow.variables.selector.validity.label")}`,
|
||||
value: `${node.id}#certificate.validity#boolean`,
|
||||
});
|
||||
|
||||
return group;
|
||||
})
|
||||
|
||||
@@ -518,7 +518,7 @@
|
||||
"access.form.webhook_data.label": "Webhook data (Optional)",
|
||||
"access.form.webhook_data.placeholder": "Please enter the default Webhook data",
|
||||
"access.form.webhook_data.help": "Notes: It can be overrided in the workflows.",
|
||||
"access.form.webhook_data.guide_for_deployment": "The Webhook data should be in JSON format. <br><br>The values in JSON support template variables, which will be replaced by actual values when sent to the Webhook URL. Supported variables: <br><ol style=\"list-style: disc;\"><li><strong>${DOMAIN}</strong>: The primary domain of the certificate (<i>CommonName</i>).</li><li><strong>${DOMAINS}</strong>: The domain list of the certificate (<i>SubjectAltNames</i>).</li><li><strong>${CERTIFICATE}</strong>: The PEM format content of the certificate file.</li><li><strong>${SERVER_CERTIFICATE}</strong>: The PEM format content of the server certificate file.</li><li><strong>${INTERMEDIA_CERTIFICATE}</strong>: The PEM format content of the intermediate CA certificate file.</li><li><strong>${PRIVATE_KEY}</strong>: The PEM format content of the private key file.</li></ol><br>When the request method is GET, the data will be passed as query string. Otherwise, the data will be encoded in the format indicated by the Content-Type in the request headers. Supported formats: <br><ol style=\"list-style: disc;\"><li>application/json (default).</li><li>application/x-www-form-urlencoded: Nested data is not supported.</li><li>multipart/form-data: Nested data is not supported.</li>",
|
||||
"access.form.webhook_data.guide_for_deployment": "The Webhook data should be in JSON format. <br><br>The values in JSON support template variables, which will be replaced by actual values when sent to the Webhook URL. Supported variables: <br><ol style=\"list-style: disc;\"><li><strong>${DOMAIN}</strong>: The primary domain of the certificate (a.k.a. <i>CommonName</i>).</li><li><strong>${DOMAINS}</strong>: The domains list of the certificate (a.k.a. <i>SubjectAltNames</i>).</li><li><strong>${CERTIFICATE}</strong>: The PEM format content of the certificate file.</li><li><strong>${SERVER_CERTIFICATE}</strong>: The PEM format content of the server certificate file.</li><li><strong>${INTERMEDIA_CERTIFICATE}</strong>: The PEM format content of the intermediate CA certificate file.</li><li><strong>${PRIVATE_KEY}</strong>: The PEM format content of the private key file.</li></ol><br>When the request method is GET, the data will be passed as query string. Otherwise, the data will be encoded in the format indicated by the Content-Type in the request headers. Supported formats: <br><ol style=\"list-style: disc;\"><li>application/json (default).</li><li>application/x-www-form-urlencoded: Nested data is not supported.</li><li>multipart/form-data: Nested data is not supported.</li>",
|
||||
"access.form.webhook_data.guide_for_notification": "The Webhook data should be in JSON format. <br><br>The values in JSON support template variables, which will be replaced by actual values when sent to the Webhook URL. Supported variables: <br><ol style=\"list-style: disc;\"><li><strong>${SUBJECT}</strong>: The subject of notification.</li><li><strong>${MESSAGE}</strong>: The message of notification.</li></ol><br>When the request method is GET, the data will be passed as query string. Otherwise, the data will be encoded in the format indicated by the Content-Type in the request headers. Supported formats: <br><ol style=\"list-style: disc;\"><li>application/json (default).</li><li>application/x-www-form-urlencoded: Nested data is not supported.</li><li>multipart/form-data: Nested data is not supported.</li>",
|
||||
"access.form.webhook_preset_data.button": "Use preset template",
|
||||
"access.form.webhook_preset_data.option.bark.label": "Bark",
|
||||
|
||||
@@ -1036,7 +1036,6 @@
|
||||
"workflow_node.deploy.form.webhook_data.label": "Webhook data (Optional)",
|
||||
"workflow_node.deploy.form.webhook_data.placeholder": "Please enter Webhook data",
|
||||
"workflow_node.deploy.form.webhook_data.help": "Notes: Leave it blank to use the default Webhook data provided by the credential.",
|
||||
"workflow_node.deploy.form.webhook_data.guide": "<details><summary>Supported variables: </summary><ol style=\"list-style: disc;\"><li><strong>${DOMAIN}</strong>: The primary domain of the certificate (<i>CommonName</i>).</li><li><strong>${DOMAINS}</strong>: The domain list of the certificate (<i>SubjectAltNames</i>).</li><li><strong>${CERTIFICATE}</strong>: The PEM format content of the certificate file.</li><li><strong>${SERVER_CERTIFICATE}</strong>: The PEM format content of the server certificate file.</li><li><strong>${INTERMEDIA_CERTIFICATE}</strong>: The PEM format content of the intermediate CA certificate file.</li><li><strong>${PRIVATE_KEY}</strong>: The PEM format content of the private key file.</li></ol></details><br>Please visit the credentials page for addtional notes.",
|
||||
"workflow_node.deploy.form.webhook_data.errmsg.json_invalid": "Please enter a valid JSON string",
|
||||
"workflow_node.deploy.form.webhook_timeout.label": "Webhook timeout (Optional)",
|
||||
"workflow_node.deploy.form.webhook_timeout.placeholder": "Please enter Webhook timeout",
|
||||
@@ -1046,6 +1045,7 @@
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.suffix": " to re-deploy.",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.switch.on": "skip",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.switch.off": "not skip",
|
||||
|
||||
"workflow_node.notify.label": "Send notification",
|
||||
"workflow_node.notify.help": "Invoke the APIs of the service provider to push message notifications.",
|
||||
"workflow_node.notify.default_name": "Notification",
|
||||
@@ -1058,6 +1058,7 @@
|
||||
"workflow_node.notify.form.subject.placeholder": "Please enter subject",
|
||||
"workflow_node.notify.form.message.label": "Message",
|
||||
"workflow_node.notify.form.message.placeholder": "Please enter message",
|
||||
"workflow_node.notify.form.template.guide": "<details><summary>The content using the \"Mustache\" syntax (double curly braces) and preceded by \"$\" in the subject or message are text interpolations. They will be replaced by the actual values. </summary><br>Supported text interpolations: <ol style=\"list-style: disc;\"><li><em>workflow.id</em>: The ID of the workflow.</li><li><em>workflow.name</em>: The name of the workflow.</li><li><em>run.id</em>: The ID of the workflow run.</li><li><em>error.nodeId</em>: The node ID that execution failed. If there are multiple nodes that have failed before this, it always indicate the nearest one.</li><li><em>error.nodeName</em>: The node name that execution failed. If there are multiple nodes that have failed before this, it always indicate the nearest one.</li><li><em>error.message</em>: The error message that execution failed. If there are multiple nodes that have failed before this, it always indicate the nearest one.</li><li><em>certificate.domain</em>: The primary domain of the certificate (a.k.a. <i>CommonName</i>). If there are multiple nodes outputting a certificate before this, it always indicate the nearest one.</li><li><em>certificate.domains</em>: The domains list of the certificate (a.k.a. <i>SubjectAltNames</i>). If there are multiple nodes outputting a certificate before this, it always indicate the nearest one.</li><li><em>certificate.notBefore</em>: The effect time of the certificate, formatted in RFC3339. If there are multiple nodes outputting a certificate before this, it always indicate the nearest one.</li><li><em>certificate.notAfter</em>: The effect time of the certificate, formatted in RFC3339. If there are multiple nodes outputting a certificate before this, it always indicate the nearest one.</li><li><em>certificate.hoursLeft</em>: The left hours of the certificate. If there are multiple nodes outputting a certificate before this, it always indicate the nearest one.</li><li><em>certificate.daysLeft</em>: The left days of the certificate. If there are multiple nodes outputting a certificate before this, it always indicate the nearest one.</li><li><em>certificate.validity</em>: The validity of the certificate. If there are multiple nodes outputting a certificate before this, it always indicate the nearest one.</li><li><em>now</em>: The current time on the server, formatted in RFC3339. </li></ol><br>Example: <br><em>Your workflow {{ $workflow.name }} has failed on node {{ $error.nodeName }} at {{ $now }}.</em><br><br>Please visit the documentation for more details.</details>",
|
||||
"workflow_node.notify.form.provider.label": "Notification channel",
|
||||
"workflow_node.notify.form.provider.placeholder": "Please select notification channel",
|
||||
"workflow_node.notify.form.provider.search.placeholder": "Search notification channel ...",
|
||||
@@ -1083,7 +1084,6 @@
|
||||
"workflow_node.notify.form.webhook_data.label": "Webhook data (Optional)",
|
||||
"workflow_node.notify.form.webhook_data.placeholder": "Please enter Webhook data",
|
||||
"workflow_node.notify.form.webhook_data.help": "Notes: Leave it blank to use the default Webhook data provided by the credential.",
|
||||
"workflow_node.notify.form.webhook_data.guide": "<details><summary>Supported variables: </summary><ol style=\"list-style: disc;\"><li><strong>${SUBJECT}</strong>: The subject of notification.</li><li><strong>${MESSAGE}</strong>: The message of notification.</li></ol></details><br>Please visit the credentials page for addtional notes.",
|
||||
"workflow_node.notify.form.webhook_data.errmsg.json_invalid": "Please enter a valid JSON string",
|
||||
"workflow_node.notify.form.webhook_timeout.label": "Webhook timeout (Optional)",
|
||||
"workflow_node.notify.form.webhook_timeout.placeholder": "Please enter Webhook timeout",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"workflow.variables.type.certificate.label": "Certificate",
|
||||
|
||||
"workflow.variables.selector.validity.label": "Validity",
|
||||
"workflow.variables.selector.days_left.label": "Days left"
|
||||
"workflow.variables.selector.hours_left.label": "Hours left",
|
||||
"workflow.variables.selector.days_left.label": "Days left",
|
||||
"workflow.variables.selector.validity.label": "Validity"
|
||||
}
|
||||
|
||||
@@ -1034,7 +1034,6 @@
|
||||
"workflow_node.deploy.form.webhook_data.label": "Webhook 回调数据(可选)",
|
||||
"workflow_node.deploy.form.webhook_data.placeholder": "请输入 Webhook 回调数据以覆盖默认值",
|
||||
"workflow_node.deploy.form.webhook_data.help": "提示:不填写时,将使用所选部署目标授权的默认 Webhook 回调数据。",
|
||||
"workflow_node.deploy.form.webhook_data.guide": "<details><summary>支持的变量:</summary><ol style=\"list-style: disc;\"><li><strong>${DOMAIN}</strong>:证书的主域名(即 <i>CommonName</i>)。</li><li><strong>${DOMAINS}</strong>:证书的多域名列表(即 <i>SubjectAltNames</i>)。</li><li><strong>${CERTIFICATE}</strong>:证书文件 PEM 格式内容。</li><li><strong>${SERVER_CERTIFICATE}</strong>:证书文件(仅含服务器证书)PEM 格式内容。</li><li><strong>${INTERMEDIA_CERTIFICATE}</strong>:证书文件(仅含中间证书)PEM 格式内容。</li><li><strong>${PRIVATE_KEY}</strong>:私钥文件 PEM 格式内容。</li></ol></details><br>其他注意事项请前往授权凭据页面查看。",
|
||||
"workflow_node.deploy.form.webhook_data.errmsg.json_invalid": "请输入有效的 JSON 格式字符串",
|
||||
"workflow_node.deploy.form.webhook_timeout.label": "Webhook 超时时间(可选)",
|
||||
"workflow_node.deploy.form.webhook_timeout.placeholder": "请输入 Webhook 超时时间",
|
||||
@@ -1044,6 +1043,7 @@
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.suffix": "此部署节点。",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.switch.on": "跳过",
|
||||
"workflow_node.deploy.form.skip_on_last_succeeded.switch.off": "不跳过",
|
||||
|
||||
"workflow_node.notify.label": "推送通知",
|
||||
"workflow_node.notify.help": "调用服务提供商相关 API,将消息通知推送到指定的目标。",
|
||||
"workflow_node.notify.default_name": "通知",
|
||||
@@ -1055,7 +1055,7 @@
|
||||
"workflow_node.notify.form.subject.label": "通知主题",
|
||||
"workflow_node.notify.form.subject.placeholder": "请输入通知主题",
|
||||
"workflow_node.notify.form.message.label": "通知内容",
|
||||
"workflow_node.notify.form.message.placeholder": "请输入通知内容",
|
||||
"workflow_node.notify.form.template.guide": "<details><summary>通知主题或内容中使用「Mustache」语法(即双大括号)包裹、并以「$」符号开头的文本会被视为模板插值,将在推送时被替换为实际值。</summary><br>支持的模板插值:<ol style=\"list-style: disc;\"><li><em>workflow.id</em>:工作流 ID。</li><li><em>workflow.name</em>:工作流名称。</li><li><em>run.id</em>:运行 ID。</li><li><em>error.nodeId</em>:执行失败时的节点 ID。如果在此之前有多个执行失败的节点,始终表示最近的一个。</li><li><em>error.nodeName</em>:执行失败时的节点名称。如果在此之前有多个执行失败的节点,始终表示最近的一个。</li><li><em>error.message</em>:执行失败时的错误信息。如果在此之前有多个执行失败的节点,始终表示最近的一个。</li><li><em>certificate.domain</em>:证书主域名(即 <i>CommonName</i>)。如果在此之前有多个输出证书的节点,始终表示最近的一个。</li><li><em>certificate.domains</em>:证书多域名列表(即 <i>SubjectAltNames</i>)。如果在此之前有多个输出证书的节点,始终表示最近的一个。</li><li><em>certificate.notBefore</em>:证书生效时间,以 RFC3339 格式化。如果在此之前有多个输出证书的节点,始终表示最近的一个。</li><li><em>certificate.notAfter</em>:证书过期时间,以 RFC3339 格式化。如果在此之前有多个输出证书的节点,始终表示最近的一个。</li><li><em>certificate.hoursLeft</em>:证书剩余小时数。如果在此之前有多个输出证书的节点,始终表示最近的一个。</li><li><em>certificate.daysLeft</em>:证书剩余天数。如果在此之前有多个输出证书的节点,始终表示最近的一个。</li><li><em>certificate.validity</em>:证书是否有效。如果在此之前有多个输出证书的节点,始终表示最近的一个。</li><li><em>now</em>:服务器当前时间,以 RFC3339 格式化。</li></ol><br>示例:<br><em>Your workflow {{ $workflow.name }} has failed on node {{ $error.nodeName }} at {{ $now }}.</em><br><br>更多内容请查看文档。</details>",
|
||||
"workflow_node.notify.form.provider.label": "通知渠道",
|
||||
"workflow_node.notify.form.provider.placeholder": "请选择通知渠道",
|
||||
"workflow_node.notify.form.provider.search.placeholder": "搜索通知渠道……",
|
||||
@@ -1081,7 +1081,6 @@
|
||||
"workflow_node.notify.form.webhook_data.label": "Webhook 回调数据(可选)",
|
||||
"workflow_node.notify.form.webhook_data.placeholder": "请输入 Webhook 回调数据以覆盖默认值",
|
||||
"workflow_node.notify.form.webhook_data.help": "提示:不填写时,将使用所选部署目标授权的默认 Webhook 回调数据。",
|
||||
"workflow_node.notify.form.webhook_data.guide": "<details><summary>支持的变量:</summary><ol style=\"list-style: disc;\"><li><strong>${SUBJECT}</strong>:通知主题。</li><li><strong>${MESSAGE}</strong>:通知内容。</ol></details><br>其他注意事项请前往授权凭据页面查看。",
|
||||
"workflow_node.notify.form.webhook_data.errmsg.json_invalid": "请输入有效的 JSON 格式字符串",
|
||||
"workflow_node.notify.form.webhook_timeout.label": "Webhook 超时时间(可选)",
|
||||
"workflow_node.notify.form.webhook_timeout.placeholder": "请输入 Webhook 超时时间",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"workflow.variables.type.certificate.label": "证书",
|
||||
|
||||
"workflow.variables.selector.validity.label": "有效性",
|
||||
"workflow.variables.selector.days_left.label": "剩余天数"
|
||||
"workflow.variables.selector.hours_left.label": "剩余小时数",
|
||||
"workflow.variables.selector.days_left.label": "剩余天数",
|
||||
"workflow.variables.selector.validity.label": "有效性"
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ const WorkflowNew = () => {
|
||||
notifyOnFailureNode.data.config = {
|
||||
...notifyOnFailureNode.data.config,
|
||||
subject: "[Certimate] Workflow Failure Alert!",
|
||||
message: "Your workflow run has failed. Please check the details.",
|
||||
message: 'Your workflow "{{ $workflow.name }}" run has failed. Please check the details.',
|
||||
} as WorkflowNodeConfigForBizNotify;
|
||||
|
||||
tryCatchNode.blocks!.at(0)!.blocks ??= [];
|
||||
@@ -142,19 +142,21 @@ const WorkflowNew = () => {
|
||||
notifyOnExpiringSoonNode.data.config = {
|
||||
...notifyOnExpiringSoonNode.data.config,
|
||||
subject: "[Certimate] Certificate Expiry Alert!",
|
||||
message: "The certificate which you are monitoring will be expiring soon. Please pay attention to your website.",
|
||||
message:
|
||||
"The certificate which you are monitoring will be expiring soon. Please pay attention to your website. \r\nDomains: {{ $certificate.domains }} \r\nExpiration: {{ $certificate.notAfter }}({{ $certificate.daysLeft }} days left)",
|
||||
} as WorkflowNodeConfigForBizNotify;
|
||||
|
||||
notifyOnExpiredNode.data.config = {
|
||||
...notifyOnExpiredNode.data.config,
|
||||
subject: "[Certimate] Certificate Expiry Alert!",
|
||||
message: "The certificate which you are monitoring has already expired. Please pay attention to your website.",
|
||||
message:
|
||||
"The certificate which you are monitoring has already expired. Please pay attention to your website. \r\nDomains: {{ $certificate.domains }} \r\nExpiration: {{ $certificate.notAfter }}",
|
||||
} as WorkflowNodeConfigForBizNotify;
|
||||
|
||||
notifyOnFailureNode.data.config = {
|
||||
...notifyOnFailureNode.data.config,
|
||||
subject: "[Certimate] Workflow Failure Alert!",
|
||||
message: "Your workflow run has failed. Please check the details.",
|
||||
message: 'Your workflow "{{ $workflow.name }}" run has failed. Please check the details.',
|
||||
} as WorkflowNodeConfigForBizNotify;
|
||||
|
||||
tryCatchNode.blocks!.at(0)!.blocks ??= [];
|
||||
|
||||
Reference in New Issue
Block a user