mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-08 05:29:51 +00:00
refactor: rename Timeout
to PropagationTimeout
during ACME DNS-01 authentication
This commit is contained in:
parent
dae6ad2951
commit
77537e7005
@ -30,8 +30,8 @@ func (a *acmeHttpReqApplicant) Apply() (*Certificate, error) {
|
||||
config.Mode = access.Mode
|
||||
config.Username = access.Username
|
||||
config.Password = access.Password
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := httpreq.NewDNSProviderConfig(config)
|
||||
|
@ -26,8 +26,8 @@ func (a *aliyunApplicant) Apply() (*Certificate, error) {
|
||||
config := alidns.NewDefaultConfig()
|
||||
config.APIKey = access.AccessKeyId
|
||||
config.SecretKey = access.AccessKeySecret
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := alidns.NewDNSProviderConfig(config)
|
||||
|
@ -67,8 +67,6 @@ var sslProviderUrls = map[string]string{
|
||||
|
||||
const defaultEmail = "536464346@qq.com"
|
||||
|
||||
const defaultTimeout = 60
|
||||
|
||||
type Certificate struct {
|
||||
CertUrl string `json:"certUrl"`
|
||||
CertStableUrl string `json:"certStableUrl"`
|
||||
@ -84,7 +82,7 @@ type ApplyOption struct {
|
||||
Access string `json:"access"`
|
||||
KeyAlgorithm string `json:"keyAlgorithm"`
|
||||
Nameservers string `json:"nameservers"`
|
||||
Timeout int64 `json:"timeout"`
|
||||
PropagationTimeout int64 `json:"propagationTimeout"`
|
||||
DisableFollowCNAME bool `json:"disableFollowCNAME"`
|
||||
}
|
||||
|
||||
@ -164,17 +162,13 @@ func Get(record *models.Record) (Applicant, error) {
|
||||
applyConfig.Email = defaultEmail
|
||||
}
|
||||
|
||||
if applyConfig.Timeout == 0 {
|
||||
applyConfig.Timeout = defaultTimeout
|
||||
}
|
||||
|
||||
option := &ApplyOption{
|
||||
Email: applyConfig.Email,
|
||||
Domain: record.GetString("domain"),
|
||||
Access: access.GetString("config"),
|
||||
KeyAlgorithm: applyConfig.KeyAlgorithm,
|
||||
Nameservers: applyConfig.Nameservers,
|
||||
Timeout: applyConfig.Timeout,
|
||||
PropagationTimeout: applyConfig.PropagationTimeout,
|
||||
DisableFollowCNAME: applyConfig.DisableFollowCNAME,
|
||||
}
|
||||
|
||||
@ -190,18 +184,13 @@ func GetWithApplyNode(node *domain.WorkflowNode) (Applicant, error) {
|
||||
return nil, fmt.Errorf("access record not found: %w", err)
|
||||
}
|
||||
|
||||
timeout := node.GetConfigInt64("timeout")
|
||||
if timeout == 0 {
|
||||
timeout = defaultTimeout
|
||||
}
|
||||
|
||||
applyConfig := &ApplyOption{
|
||||
Email: node.GetConfigString("email"),
|
||||
Domain: node.GetConfigString("domain"),
|
||||
Access: access.Config,
|
||||
KeyAlgorithm: node.GetConfigString("keyAlgorithm"),
|
||||
Nameservers: node.GetConfigString("nameservers"),
|
||||
Timeout: timeout,
|
||||
PropagationTimeout: node.GetConfigInt64("propagationTimeout"),
|
||||
DisableFollowCNAME: node.GetConfigBool("disableFollowCNAME"),
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,8 @@ func (a *awsApplicant) Apply() (*Certificate, error) {
|
||||
config.SecretAccessKey = access.SecretAccessKey
|
||||
config.Region = access.Region
|
||||
config.HostedZoneID = access.HostedZoneId
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := route53.NewDNSProviderConfig(config)
|
||||
|
@ -25,8 +25,8 @@ func (a *cloudflareApplicant) Apply() (*Certificate, error) {
|
||||
|
||||
config := cloudflare.NewDefaultConfig()
|
||||
config.AuthToken = access.DnsApiToken
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := cloudflare.NewDNSProviderConfig(config)
|
||||
|
@ -26,8 +26,8 @@ func (a *godaddyApplicant) Apply() (*Certificate, error) {
|
||||
config := godaddy.NewDefaultConfig()
|
||||
config.APIKey = access.ApiKey
|
||||
config.APISecret = access.ApiSecret
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := godaddy.NewDNSProviderConfig(config)
|
||||
|
@ -33,8 +33,8 @@ func (a *huaweicloudApplicant) Apply() (*Certificate, error) {
|
||||
config.AccessKeyID = access.AccessKeyId
|
||||
config.SecretAccessKey = access.SecretAccessKey
|
||||
config.Region = region
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := huaweicloud.NewDNSProviderConfig(config)
|
||||
|
@ -25,8 +25,8 @@ func (a *nameDotComApplicant) Apply() (*Certificate, error) {
|
||||
config := namedotcom.NewDefaultConfig()
|
||||
config.Username = access.Username
|
||||
config.APIToken = access.ApiToken
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := namedotcom.NewDNSProviderConfig(config)
|
||||
|
@ -25,8 +25,8 @@ func (a *namesiloApplicant) Apply() (*Certificate, error) {
|
||||
|
||||
config := namesilo.NewDefaultConfig()
|
||||
config.APIKey = access.ApiKey
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := namesilo.NewDNSProviderConfig(config)
|
||||
|
@ -28,8 +28,8 @@ func (a *powerdnsApplicant) Apply() (*Certificate, error) {
|
||||
host, _ := url.Parse(access.ApiUrl)
|
||||
config.Host = host
|
||||
config.APIKey = access.ApiKey
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := pdns.NewDNSProviderConfig(config)
|
||||
|
@ -26,8 +26,8 @@ func (a *tencentcloudApplicant) Apply() (*Certificate, error) {
|
||||
config := tencentcloud.NewDefaultConfig()
|
||||
config.SecretID = access.SecretId
|
||||
config.SecretKey = access.SecretKey
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := tencentcloud.NewDNSProviderConfig(config)
|
||||
|
@ -25,8 +25,8 @@ func (a *volcengineApplicant) Apply() (*Certificate, error) {
|
||||
config := volcengine.NewDefaultConfig()
|
||||
config.AccessKey = access.AccessKeyId
|
||||
config.SecretKey = access.SecretAccessKey
|
||||
if a.option.Timeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.Timeout) * time.Second
|
||||
if a.option.PropagationTimeout != 0 {
|
||||
config.PropagationTimeout = time.Duration(a.option.PropagationTimeout) * time.Second
|
||||
}
|
||||
|
||||
provider, err := volcengine.NewDNSProviderConfig(config)
|
||||
|
@ -12,7 +12,7 @@ type ApplyConfig struct {
|
||||
Access string `json:"access"`
|
||||
KeyAlgorithm string `json:"keyAlgorithm"`
|
||||
Nameservers string `json:"nameservers"`
|
||||
Timeout int64 `json:"timeout"`
|
||||
PropagationTimeout int64 `json:"propagationTimeout"`
|
||||
DisableFollowCNAME bool `json:"disableFollowCNAME"`
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,10 @@ export type AccessEditModalProps = {
|
||||
preset: AccessEditFormProps["preset"];
|
||||
trigger?: React.ReactElement;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
onSubmit?: (record: AccessModel) => void;
|
||||
};
|
||||
|
||||
const AccessEditModal = ({ data, loading, trigger, preset, ...props }: AccessEditModalProps) => {
|
||||
const AccessEditModal = ({ data, loading, trigger, preset, onSubmit, ...props }: AccessEditModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [notificationApi, NotificationContextHolder] = notification.useNotification();
|
||||
@ -57,20 +58,24 @@ const AccessEditModal = ({ data, loading, trigger, preset, ...props }: AccessEdi
|
||||
}
|
||||
|
||||
try {
|
||||
let res: AccessModel;
|
||||
if (preset === "add") {
|
||||
if (data?.id) {
|
||||
throw "Invalid props: `data`";
|
||||
}
|
||||
|
||||
await createAccess(formRef.current!.getFieldsValue() as AccessModel);
|
||||
res = await createAccess(formRef.current!.getFieldsValue() as AccessModel);
|
||||
} else if (preset === "edit") {
|
||||
if (!data?.id) {
|
||||
throw "Invalid props: `data`";
|
||||
}
|
||||
|
||||
await updateAccess({ ...data, ...formRef.current!.getFieldsValue() } as AccessModel);
|
||||
res = await updateAccess({ ...data, ...formRef.current!.getFieldsValue() } as AccessModel);
|
||||
} else {
|
||||
throw "Invalid props: `preset`";
|
||||
}
|
||||
|
||||
onSubmit?.(res);
|
||||
setOpen(false);
|
||||
} catch (err) {
|
||||
notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) });
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Plus } from "lucide-react";
|
||||
import { BrandNodeProps, NodeProps } from "./types";
|
||||
import { newWorkflowNode, workflowNodeDropdownList, WorkflowNodeType } from "@/domain/workflow";
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
import { useWorkflowStore } from "@/stores/workflow";
|
||||
import { Dropdown } from "antd";
|
||||
import DropdownMenuItemIcon from "./DropdownMenuItemIcon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Dropdown } from "antd";
|
||||
import { Plus as PlusIcon } from "lucide-react";
|
||||
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
import { newWorkflowNode, workflowNodeDropdownList, WorkflowNodeType } from "@/domain/workflow";
|
||||
import { useWorkflowStore } from "@/stores/workflow";
|
||||
import { type BrandNodeProps, type NodeProps } from "./types";
|
||||
import DropdownMenuItemIcon from "./DropdownMenuItemIcon";
|
||||
|
||||
const AddNode = ({ data }: NodeProps | BrandNodeProps) => {
|
||||
const { t } = useTranslation();
|
||||
@ -56,7 +57,7 @@ const AddNode = ({ data }: NodeProps | BrandNodeProps) => {
|
||||
trigger={["click"]}
|
||||
>
|
||||
<div className="bg-stone-400 hover:bg-stone-500 rounded-full z-10 relative outline-none">
|
||||
<Plus size={18} className="text-white" />
|
||||
<PlusIcon className="text-white" size={18} />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { memo, useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useControllableValue } from "ahooks";
|
||||
import { AutoComplete, Button, Divider, Form, Input, InputNumber, Select, Switch, Typography, type AutoCompleteProps } from "antd";
|
||||
import { AutoComplete, Button, Divider, Form, Input, InputNumber, Select, Switch, Tooltip, Typography, type AutoCompleteProps } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { PlusOutlined as PlusOutlinedIcon } from "@ant-design/icons";
|
||||
import { PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons";
|
||||
import z from "zod";
|
||||
|
||||
import AccessEditModal from "@/components/access/AccessEditModal";
|
||||
@ -24,7 +24,8 @@ const initFormModel = (): WorkflowNodeConfig => {
|
||||
return {
|
||||
domain: "",
|
||||
keyAlgorithm: "RSA2048",
|
||||
timeout: 60,
|
||||
nameservers: "",
|
||||
propagationTimeout: 60,
|
||||
disableFollowCNAME: true,
|
||||
};
|
||||
};
|
||||
@ -32,6 +33,7 @@ const initFormModel = (): WorkflowNodeConfig => {
|
||||
const ApplyNodeForm = ({ data }: ApplyNodeFormProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { addEmail } = useContactStore();
|
||||
const { updateNode } = useWorkflowStore(useZustandShallowSelector(["updateNode"]));
|
||||
const { hidePanel } = usePanel();
|
||||
|
||||
@ -59,7 +61,7 @@ const ApplyNodeForm = ({ data }: ApplyNodeFormProps) => {
|
||||
{ message: t("common.errmsg.host_invalid") }
|
||||
)
|
||||
.nullish(),
|
||||
timeout: z.number().gte(1, t("workflow.nodes.apply.form.timeout.placeholder")).nullish(),
|
||||
timeout: z.number().gte(1, t("workflow.nodes.apply.form.propagation_timeout.placeholder")).nullish(),
|
||||
disableFollowCNAME: z.boolean().nullish(),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
@ -71,6 +73,7 @@ const ApplyNodeForm = ({ data }: ApplyNodeFormProps) => {
|
||||
initialValues: data?.config ?? initFormModel(),
|
||||
onSubmit: async (values) => {
|
||||
await updateNode({ ...data, config: { ...values }, validated: true });
|
||||
await addEmail(values.email);
|
||||
hidePanel();
|
||||
},
|
||||
});
|
||||
@ -96,18 +99,31 @@ const ApplyNodeForm = ({ data }: ApplyNodeFormProps) => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<label className="block mb-[2px]">
|
||||
<label className="block mb-1">
|
||||
<div className="flex items-center justify-between gap-4 w-full overflow-hidden">
|
||||
<div className="flex-grow max-w-full truncate">{t("workflow.nodes.apply.form.access.label")}</div>
|
||||
<div className="flex-grow max-w-full truncate">
|
||||
<span>{t("workflow.nodes.apply.form.access.label")}</span>
|
||||
<Tooltip title={t("workflow.nodes.apply.form.access.tooltip")}>
|
||||
<Typography.Text className="ms-1" type="secondary">
|
||||
<QuestionCircleOutlinedIcon />
|
||||
</Typography.Text>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<AccessEditModal
|
||||
preset="add"
|
||||
trigger={
|
||||
<Button className="p-0" type="link">
|
||||
<Button size="small" type="link">
|
||||
<PlusOutlinedIcon />
|
||||
{t("workflow.nodes.apply.form.access.button")}
|
||||
</Button>
|
||||
}
|
||||
onSubmit={(record) => {
|
||||
const provider = accessProvidersMap.get(record.configType);
|
||||
if (ACCESS_PROVIDER_USAGES.ALL === provider?.usage || ACCESS_PROVIDER_USAGES.APPLY === provider?.usage) {
|
||||
formInst.setFieldValue("access", record.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -149,17 +165,17 @@ const ApplyNodeForm = ({ data }: ApplyNodeFormProps) => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="timeout"
|
||||
label={t("workflow.nodes.apply.form.timeout.label")}
|
||||
name="propagationTimeout"
|
||||
label={t("workflow.nodes.apply.form.propagation_timeout.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow.nodes.apply.form.timeout.tooltip") }}></span>}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow.nodes.apply.form.propagation_timeout.tooltip") }}></span>}
|
||||
>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
min={0}
|
||||
max={3600}
|
||||
placeholder={t("workflow.nodes.apply.form.timeout.placeholder")}
|
||||
addonAfter={t("workflow.nodes.apply.form.timeout.suffix")}
|
||||
placeholder={t("workflow.nodes.apply.form.propagation_timeout.placeholder")}
|
||||
addonAfter={t("workflow.nodes.apply.form.propagation_timeout.suffix")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
|
@ -70,7 +70,7 @@ const NotifyNodeForm = ({ data }: NotifyNodeFormProps) => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<label className="block mb-[2px]">
|
||||
<label className="block mb-1">
|
||||
<div className="flex items-center justify-between gap-4 w-full overflow-hidden">
|
||||
<div className="flex-grow max-w-full truncate">{t("workflow.nodes.notify.form.channel.label")}</div>
|
||||
<div className="text-right">
|
||||
|
@ -63,6 +63,14 @@ const WorkflowRuns = ({ className, style }: WorkflowRunsProps) => {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "trigger",
|
||||
title: t("workflow_run.props.trigger"),
|
||||
ellipsis: true,
|
||||
render: () => {
|
||||
return "TODO";
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "startedAt",
|
||||
title: t("workflow_run.props.started_at"),
|
||||
|
@ -1,53 +1,4 @@
|
||||
{
|
||||
"domain.page.title": "Domain List",
|
||||
|
||||
"domain.nodata": "Please add a domain to start deploying the certificate.",
|
||||
|
||||
"domain.add": "Add Domain",
|
||||
"domain.edit": "Edit Domain",
|
||||
"domain.delete": "Delete Domain",
|
||||
"domain.delete.confirm": "Are you sure you want to delete this domain?",
|
||||
"domain.history": "Deployment History",
|
||||
"domain.deploy": "Deploy Now",
|
||||
"domain.deploy.started.message": "Deploy Started",
|
||||
"domain.deploy.started.tips": "Deployment initiated, please check the deployment log later.",
|
||||
"domain.deploy.failed.message": "Execution Failed",
|
||||
"domain.deploy.failed.tips": "Execution failed, please check the details in <1>Deployment History</1>.",
|
||||
"domain.deploy_forced": "Force Deploy",
|
||||
|
||||
"domain.props.expiry": "Validity Period",
|
||||
"domain.props.expiry.date1": "Valid for {{date}} days",
|
||||
"domain.props.expiry.date2": "Expiry on {{date}}",
|
||||
"domain.props.last_execution_status": "Last Execution Status",
|
||||
"domain.props.last_execution_stage": "Last Execution Stage",
|
||||
"domain.props.last_execution_time": "Last Execution Time",
|
||||
"domain.props.enable": "Enable",
|
||||
"domain.props.enable.enabled": "Enable",
|
||||
"domain.props.enable.disabled": "Disable",
|
||||
|
||||
"domain.application.tab": "Apply Settings",
|
||||
"domain.application.form.domain.added.message": "Domain added successfully",
|
||||
"domain.application.form.domain.changed.message": "Domain updated successfully",
|
||||
"domain.application.form.email.label": "Email",
|
||||
"domain.application.form.email.tips": "(A email is required to apply for a certificate)",
|
||||
"domain.application.form.email.placeholder": "Please select email",
|
||||
"domain.application.form.email.add": "Add Email",
|
||||
"domain.application.form.email.list": "Email List",
|
||||
"domain.application.form.access.label": "DNS Provider Authorization Configuration",
|
||||
"domain.application.form.access.placeholder": "Please select DNS provider authorization configuration",
|
||||
"domain.application.form.access.list": "Provider Authorization Configurations",
|
||||
"domain.application.form.advanced_settings.label": "Advanced Settings",
|
||||
"domain.application.form.key_algorithm.label": "Certificate Key Algorithm (Default: RSA2048)",
|
||||
"domain.application.form.key_algorithm.placeholder": "Please select certificate key algorithm",
|
||||
"domain.application.form.timeout.label": "DNS Propagation Timeout (Seconds)",
|
||||
"domain.application.form.timeout.placeholder": "Please enter maximum waiting time for DNS propagation",
|
||||
"domain.application.form.disable_follow_cname.label": "Disable DNS CNAME following",
|
||||
"domain.application.form.disable_follow_cname.tips": "This option will disable Acme DNS authentication CNAME follow. If you don't understand this option, just keep it by default. ",
|
||||
"domain.application.form.disable_follow_cname.tips_link": "Learn more",
|
||||
"domain.application.unsaved.message": "Please save applyment configuration first",
|
||||
|
||||
"domain.deployment.tab": "Deploy Settings",
|
||||
"domain.deployment.nodata": "Deployment not added yet",
|
||||
"domain.deployment.form.type.label": "Deploy Method",
|
||||
"domain.deployment.form.type.placeholder": "Please select deploy method",
|
||||
"domain.deployment.form.type.list": "Deploy Method List",
|
||||
|
@ -41,24 +41,25 @@
|
||||
"workflow.nodes.start.form.trigger_cron.extra": "Expected execution time for the last 5 times:",
|
||||
"workflow.nodes.start.form.trigger_cron_alert.content": "Tips: If you have multiple workflows, it is recommended to set them to run at multiple times of the day instead of always running at specific times.<br><br>Reference links:<br>1. <a href=\"https://letsencrypt.org/docs/rate-limits/\" target=\"_blank\">Let’s Encrypt rate limits</a><br>2. <a href=\"https://letsencrypt.org/docs/faq/#why-should-my-let-s-encrypt-acme-client-run-at-a-random-time\" target=\"_blank\">Why should my Let’s Encrypt (ACME) client run at a random time?</a>",
|
||||
"workflow.nodes.apply.form.domain.label": "Domain",
|
||||
"workflow.nodes.apply.form.domain.placeholder": "Please enter domain",
|
||||
"workflow.nodes.apply.form.domain.placeholder": "Please enter domain (separated by semicolons)",
|
||||
"workflow.nodes.apply.form.domain.tooltip": "Wildcard domain: *.example.com",
|
||||
"workflow.nodes.apply.form.email.label": "Contact Email",
|
||||
"workflow.nodes.apply.form.email.placeholder": "Please enter contact email",
|
||||
"workflow.nodes.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.nodes.apply.form.access.label": "DNS Provider Authorization",
|
||||
"workflow.nodes.apply.form.access.placeholder": "Please select an authorization of DNS provider",
|
||||
"workflow.nodes.apply.form.access.tooltip": "Used to manage DNS records during ACME DNS-01 authentication.",
|
||||
"workflow.nodes.apply.form.access.button": "Create",
|
||||
"workflow.nodes.apply.form.advanced_settings.label": "Advanced Settings",
|
||||
"workflow.nodes.apply.form.key_algorithm.label": "Certificate Key Algorithm",
|
||||
"workflow.nodes.apply.form.key_algorithm.placeholder": "Please select certificate key algorithm",
|
||||
"workflow.nodes.apply.form.nameservers.label": "DNS Recursive Nameservers",
|
||||
"workflow.nodes.apply.form.nameservers.placeholder": "Please enter DNS recursive nameservers",
|
||||
"workflow.nodes.apply.form.nameservers.placeholder": "Please enter DNS recursive nameservers (separated by semicolons)",
|
||||
"workflow.nodes.apply.form.nameservers.tooltip": "It determines whether to custom DNS recursive nameservers during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.",
|
||||
"workflow.nodes.apply.form.timeout.label": "DNS Propagation Timeout",
|
||||
"workflow.nodes.apply.form.timeout.placeholder": "Please enter DNS propagation timeout",
|
||||
"workflow.nodes.apply.form.timeout.suffix": "Seconds",
|
||||
"workflow.nodes.apply.form.timeout.tooltip": "It determines the maximum waiting time for DNS propagation checks during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.",
|
||||
"workflow.nodes.apply.form.propagation_timeout.label": "DNS Propagation Timeout",
|
||||
"workflow.nodes.apply.form.propagation_timeout.placeholder": "Please enter DNS propagation timeout",
|
||||
"workflow.nodes.apply.form.propagation_timeout.suffix": "Seconds",
|
||||
"workflow.nodes.apply.form.propagation_timeout.tooltip": "It determines the maximum waiting time for DNS propagation checks during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.",
|
||||
"workflow.nodes.apply.form.disable_follow_cname.label": "Disable CNAME following",
|
||||
"workflow.nodes.apply.form.disable_follow_cname.tooltip": "It determines whether to disable CNAME following during ACME DNS-01 authentication. If you don't understand this option, just keep it by default.<br><a href=\"https://letsencrypt.org/2019/10/09/onboarding-your-customers-with-lets-encrypt-and-acme/#the-advantages-of-a-cname\" target=\"_blank\">Learn more</a>.",
|
||||
"workflow.nodes.notify.form.subject.label": "Subject",
|
||||
@ -73,6 +74,7 @@
|
||||
"workflow_run.props.status": "Status",
|
||||
"workflow_run.props.status.succeeded": "Succeeded",
|
||||
"workflow_run.props.status.failed": "Failed",
|
||||
"workflow_run.props.trigger": "Trigger",
|
||||
"workflow_run.props.started_at": "Started At",
|
||||
"workflow_run.props.completed_at": "Completed At",
|
||||
|
||||
|
@ -1,53 +1,4 @@
|
||||
{
|
||||
"domain.page.title": "域名列表",
|
||||
|
||||
"domain.nodata": "请添加域名开始部署证书吧。",
|
||||
|
||||
"domain.add": "新增域名",
|
||||
"domain.edit": "编辑域名",
|
||||
"domain.delete": "删除域名",
|
||||
"domain.delete.confirm": "确定要删除域名吗?",
|
||||
"domain.history": "部署历史",
|
||||
"domain.deploy": "立即部署",
|
||||
"domain.deploy.started.message": "开始部署",
|
||||
"domain.deploy.started.tips": "已发起部署,请稍后查看部署日志。",
|
||||
"domain.deploy.failed.message": "执行失败",
|
||||
"domain.deploy.failed.tips": "执行失败,请在 <1>部署历史</1> 查看详情。",
|
||||
"domain.deploy_forced": "强行部署",
|
||||
|
||||
"domain.props.expiry": "有效期限",
|
||||
"domain.props.expiry.date1": "有效期 {{date}} 天",
|
||||
"domain.props.expiry.date2": "{{date}} 到期",
|
||||
"domain.props.last_execution_status": "最近执行状态",
|
||||
"domain.props.last_execution_stage": "最近执行阶段",
|
||||
"domain.props.last_execution_time": "最近执行时间",
|
||||
"domain.props.enable": "是否启用",
|
||||
"domain.props.enable.enabled": "启用",
|
||||
"domain.props.enable.disabled": "禁用",
|
||||
|
||||
"domain.application.tab": "申请配置",
|
||||
"domain.application.form.domain.added.message": "域名添加成功",
|
||||
"domain.application.form.domain.changed.message": "域名编辑成功",
|
||||
"domain.application.form.email.label": "邮箱",
|
||||
"domain.application.form.email.tips": "(申请证书需要提供邮箱)",
|
||||
"domain.application.form.email.placeholder": "请选择邮箱",
|
||||
"domain.application.form.email.add": "添加邮箱",
|
||||
"domain.application.form.email.list": "邮箱列表",
|
||||
"domain.application.form.access.label": "DNS 提供商授权配置",
|
||||
"domain.application.form.access.placeholder": "请选择 DNS 提供商授权配置",
|
||||
"domain.application.form.access.list": "DNS 提供商授权配置列表",
|
||||
"domain.application.form.advanced_settings.label": "高级设置",
|
||||
"domain.application.form.key_algorithm.label": "数字证书算法(默认:RSA2048)",
|
||||
"domain.application.form.key_algorithm.placeholder": "请选择数字证书算法",
|
||||
"domain.application.form.timeout.label": "DNS 传播检查超时时间(单位:秒)",
|
||||
"domain.application.form.timeout.placeholder": "请输入 DNS 传播检查超时时间",
|
||||
"domain.application.form.disable_follow_cname.label": "禁用 DNS CNAME 跟随",
|
||||
"domain.application.form.disable_follow_cname.tips": "该选项将禁用 Acme DNS 认证 CNAME 跟随,如果你不了解此选项保持默认即可,",
|
||||
"domain.application.form.disable_follow_cname.tips_link": "了解更多",
|
||||
"domain.application.unsaved.message": "请先保存申请配置",
|
||||
|
||||
"domain.deployment.tab": "部署配置",
|
||||
"domain.deployment.nodata": "暂无部署配置,请添加后开始部署证书吧",
|
||||
"domain.deployment.form.type.label": "部署方式",
|
||||
"domain.deployment.form.type.placeholder": "请选择部署方式",
|
||||
"domain.deployment.form.type.list": "部署方式列表",
|
||||
|
@ -41,24 +41,25 @@
|
||||
"workflow.nodes.start.form.trigger_cron.extra": "预计最近 5 次执行时间:",
|
||||
"workflow.nodes.start.form.trigger_cron_alert.content": "小贴士:如果你有多个工作流,建议将它们设置为在一天中的多个时间段运行,而非总是在相同的特定时间。<br><br>参考链接:<br>1. <a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">Let’s Encrypt 速率限制</a><br>2. <a href=\"https://letsencrypt.org/zh-cn/docs/faq/#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E7%9A%84-let-s-encrypt-acme-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%90%AF%E5%8A%A8%E6%97%B6%E9%97%B4%E5%BA%94%E5%BD%93%E9%9A%8F%E6%9C%BA\" target=\"_blank\">为什么我的 Let’s Encrypt (ACME) 客户端启动时间应当随机?</a>",
|
||||
"workflow.nodes.apply.form.domain.label": "域名",
|
||||
"workflow.nodes.apply.form.domain.placeholder": "请输入域名",
|
||||
"workflow.nodes.apply.form.domain.placeholder": "请输入域名(多个值请用半角分号隔开)",
|
||||
"workflow.nodes.apply.form.domain.tooltip": "泛域名表示形式为:*.example.com",
|
||||
"workflow.nodes.apply.form.email.label": "联系邮箱",
|
||||
"workflow.nodes.apply.form.email.placeholder": "请输入联系邮箱",
|
||||
"workflow.nodes.apply.form.email.tooltip": "申请签发 SSL 证书时所需的联系方式。请注意<a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">速率限制</a>。",
|
||||
"workflow.nodes.apply.form.email.tooltip": "申请签发 SSL 证书时所需的联系方式。请注意 Let's Encrypt 账户注册的<a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">速率限制(点此了解更多)</a>。",
|
||||
"workflow.nodes.apply.form.access.label": "DNS 提供商授权",
|
||||
"workflow.nodes.apply.form.access.placeholder": "请选择 DNS 提供商授权",
|
||||
"workflow.nodes.apply.form.access.tooltip": "用于 ACME DNS-01 认证时操作域名解析记录,注意与部署阶段所需的主机提供商相区分。",
|
||||
"workflow.nodes.apply.form.access.button": "新建",
|
||||
"workflow.nodes.apply.form.advanced_settings.label": "高级设置",
|
||||
"workflow.nodes.apply.form.key_algorithm.label": "数字证书算法",
|
||||
"workflow.nodes.apply.form.key_algorithm.placeholder": "请选择数字证书算法",
|
||||
"workflow.nodes.apply.form.nameservers.label": "DNS 递归服务器",
|
||||
"workflow.nodes.apply.form.nameservers.placeholder": "请输入 DNS 递归服务器",
|
||||
"workflow.nodes.apply.form.nameservers.placeholder": "请输入 DNS 递归服务器(多个值请用半角分号隔开)",
|
||||
"workflow.nodes.apply.form.nameservers.tooltip": "在 ACME DNS-01 认证时使用自定义的 DNS 递归服务器。如果你不了解该选项的用途,保持默认即可。",
|
||||
"workflow.nodes.apply.form.timeout.label": "DNS 传播检查超时时间",
|
||||
"workflow.nodes.apply.form.timeout.placeholder": "请输入 DNS 传播检查超时时间",
|
||||
"workflow.nodes.apply.form.timeout.suffix": "秒",
|
||||
"workflow.nodes.apply.form.timeout.tooltip": "在 ACME DNS-01 认证时等待 DNS 传播检查的最长时间。如果你不了解此选项的用途,保持默认即可。",
|
||||
"workflow.nodes.apply.form.propagation_timeout.label": "DNS 传播检查超时时间",
|
||||
"workflow.nodes.apply.form.propagation_timeout.placeholder": "请输入 DNS 传播检查超时时间",
|
||||
"workflow.nodes.apply.form.propagation_timeout.suffix": "秒",
|
||||
"workflow.nodes.apply.form.propagation_timeout.tooltip": "在 ACME DNS-01 认证时等待 DNS 传播检查的最长时间。如果你不了解此选项的用途,保持默认即可。",
|
||||
"workflow.nodes.apply.form.disable_follow_cname.label": "禁止 CNAME 跟随",
|
||||
"workflow.nodes.apply.form.disable_follow_cname.tooltip": "在 ACME DNS-01 认证时是否禁止 CNAME 跟随。如果你不了解该选项的用途,保持默认即可。<br><a href=\"https://letsencrypt.org/2019/10/09/onboarding-your-customers-with-lets-encrypt-and-acme/#the-advantages-of-a-cname\" target=\"_blank\">点此了解更多</a>。",
|
||||
"workflow.nodes.notify.form.subject.label": "通知主题",
|
||||
@ -73,6 +74,7 @@
|
||||
"workflow_run.props.status": "状态",
|
||||
"workflow_run.props.status.succeeded": "成功",
|
||||
"workflow_run.props.status.failed": "失败",
|
||||
"workflow_run.props.trigger": "触发方式",
|
||||
"workflow_run.props.started_at": "开始时间",
|
||||
"workflow_run.props.completed_at": "完成时间",
|
||||
|
||||
|
@ -10,9 +10,9 @@ export interface AccessState {
|
||||
loadedAtOnce: boolean;
|
||||
|
||||
fetchAccesses: () => Promise<void>;
|
||||
createAccess: (access: MaybeModelRecord<AccessModel>) => Promise<void>;
|
||||
updateAccess: (access: MaybeModelRecordWithId<AccessModel>) => Promise<void>;
|
||||
deleteAccess: (access: MaybeModelRecordWithId<AccessModel>) => Promise<void>;
|
||||
createAccess: (access: MaybeModelRecord<AccessModel>) => Promise<AccessModel>;
|
||||
updateAccess: (access: MaybeModelRecordWithId<AccessModel>) => Promise<AccessModel>;
|
||||
deleteAccess: (access: MaybeModelRecordWithId<AccessModel>) => Promise<AccessModel>;
|
||||
}
|
||||
|
||||
export const useAccessStore = create<AccessState>((set) => {
|
||||
@ -25,17 +25,17 @@ export const useAccessStore = create<AccessState>((set) => {
|
||||
|
||||
createAccess: async (access) => {
|
||||
const record = await saveAccess(access);
|
||||
|
||||
set(
|
||||
produce((state: AccessState) => {
|
||||
state.accesses.unshift(record);
|
||||
})
|
||||
);
|
||||
|
||||
return record as AccessModel;
|
||||
},
|
||||
|
||||
updateAccess: async (access) => {
|
||||
const record = await saveAccess(access);
|
||||
|
||||
set(
|
||||
produce((state: AccessState) => {
|
||||
const index = state.accesses.findIndex((e) => e.id === record.id);
|
||||
@ -44,16 +44,19 @@ export const useAccessStore = create<AccessState>((set) => {
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return record as AccessModel;
|
||||
},
|
||||
|
||||
deleteAccess: async (access) => {
|
||||
await removeAccess(access);
|
||||
|
||||
set(
|
||||
produce((state: AccessState) => {
|
||||
state.accesses = state.accesses.filter((a) => a.id !== access.id);
|
||||
})
|
||||
);
|
||||
|
||||
return access as AccessModel;
|
||||
},
|
||||
|
||||
fetchAccesses: async () => {
|
||||
|
@ -11,9 +11,11 @@ export interface ContactState {
|
||||
|
||||
fetchEmails: () => Promise<void>;
|
||||
setEmails: (emails: string[]) => Promise<void>;
|
||||
addEmail: (email: string) => Promise<void>;
|
||||
removeEmail: (email: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export const useContactStore = create<ContactState>((set) => {
|
||||
export const useContactStore = create<ContactState>((set, get) => {
|
||||
let fetcher: Promise<SettingsModel<EmailsSettingsContent>> | null = null; // 防止多次重复请求
|
||||
let settings: SettingsModel<EmailsSettingsContent>; // 记录当前设置的其他字段,保存回数据库时用
|
||||
|
||||
@ -34,12 +36,29 @@ export const useContactStore = create<ContactState>((set) => {
|
||||
|
||||
set(
|
||||
produce((state: ContactState) => {
|
||||
state.emails = settings.content.emails;
|
||||
state.emails = settings.content.emails?.sort() ?? [];
|
||||
state.loadedAtOnce = true;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
addEmail: async (email) => {
|
||||
const emails = produce(get().emails, (draft) => {
|
||||
if (draft.includes(email)) return;
|
||||
draft.push(email);
|
||||
draft.sort();
|
||||
});
|
||||
get().setEmails(emails);
|
||||
},
|
||||
|
||||
removeEmail: async (email) => {
|
||||
const emails = produce(get().emails, (draft) => {
|
||||
draft = draft.filter((e) => e !== email);
|
||||
draft.sort();
|
||||
});
|
||||
get().setEmails(emails);
|
||||
},
|
||||
|
||||
fetchEmails: async () => {
|
||||
fetcher ??= getSettings<EmailsSettingsContent>(SETTINGS_NAMES.EMAILS);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user