From 1aa50cf53a0deab752f35ec973912e41ab8161b6 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Mon, 27 Apr 2026 00:16:14 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E5=A2=9E=E5=8A=A0=E6=9D=83=E5=A8=81NS?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E5=BC=80=E5=85=B3=EF=BC=8C=E6=9F=90=E4=BA=9B?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=9C=8D=E5=8A=A1=E5=99=A8=E7=A6=81=E6=AD=A2?= =?UTF-8?q?=E5=90=91=E9=BB=91=E5=90=8D=E5=8D=95NS=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E5=8F=91=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/acme-client/src/client.js | 2 +- packages/core/acme-client/src/logs/app.log | 0 packages/core/acme-client/src/util.js | 4 +- packages/core/acme-client/src/verify.js | 355 +++++++++--------- packages/core/acme-client/types/index.d.ts | 4 +- .../src/system/settings/service/models.ts | 3 + .../settings/service/sys-settings-service.ts | 5 +- .../src/locales/langs/en-US/certd.ts | 12 + .../src/locales/langs/zh-CN/certd.ts | 3 + .../src/store/settings/api.basic.ts | 1 + .../certd-client/src/store/settings/index.tsx | 21 +- .../src/views/sys/settings/tabs/base.vue | 1 + .../src/views/sys/settings/tabs/pipeline.vue | 11 +- 13 files changed, 242 insertions(+), 180 deletions(-) create mode 100644 packages/core/acme-client/src/logs/app.log diff --git a/packages/core/acme-client/src/client.js b/packages/core/acme-client/src/client.js index 0811648b7..4efe793be 100644 --- a/packages/core/acme-client/src/client.js +++ b/packages/core/acme-client/src/client.js @@ -494,7 +494,7 @@ class AcmeClient { throw new Error('Unable to verify ACME challenge, URL not found'); } - const {challenges} = createChallengeFn({logger:this.logger}); + const {challenges} = createChallengeFn({logger:this.logger,walkFromAuthoritative: this.opts.walkFromAuthoritative}); const verify = challenges if (typeof verify[challenge.type] === 'undefined') { diff --git a/packages/core/acme-client/src/logs/app.log b/packages/core/acme-client/src/logs/app.log new file mode 100644 index 000000000..e69de29bb diff --git a/packages/core/acme-client/src/util.js b/packages/core/acme-client/src/util.js index 19846200e..26e259936 100644 --- a/packages/core/acme-client/src/util.js +++ b/packages/core/acme-client/src/util.js @@ -252,7 +252,7 @@ async function resolveDomainBySoaRecord(recordName, logger = log) { async function getAuthoritativeDnsResolver(recordName, logger = log) { logger(`获取域名${recordName}的权威NS服务器: `); - const resolver = new dns.Resolver({ timeout: 10000,maxTimeout: 60000 }); + const resolver = new dns.Resolver({timeout: 2000,tries: 2}); try { /* Resolve root domain by SOA */ @@ -352,3 +352,5 @@ export { resolveDomainBySoaRecord }; + + diff --git a/packages/core/acme-client/src/verify.js b/packages/core/acme-client/src/verify.js index 10a4b54a3..f8b7d1377 100644 --- a/packages/core/acme-client/src/verify.js +++ b/packages/core/acme-client/src/verify.js @@ -4,19 +4,23 @@ import dnsSdk from "dns" import https from 'https' -import {log as defaultLog} from './logger.js' +import { log as defaultLog } from './logger.js' import axios from './axios.js' import * as util from './util.js' -import {isAlpnCertificateAuthorizationValid} from './crypto/index.js' -import {utils} from '@certd/basic' +import { isAlpnCertificateAuthorizationValid } from './crypto/index.js' +import { utils } from '@certd/basic' const dns = dnsSdk.promises +let walkFromAuthoritative = true +export function setWalkFromAuthoritative(value = true) { + walkFromAuthoritative = value +} -export function createChallengeFn(opts = {}){ - const logger = opts?.logger || {info:defaultLog,error:defaultLog,warn:defaultLog,debug:defaultLog} - - const log = function(...args){ +export function createChallengeFn(opts = {}) { + const logger = opts?.logger || { info: defaultLog, error: defaultLog, warn: defaultLog, debug: defaultLog } + + const log = function (...args) { logger.info(...args) } /** @@ -31,201 +35,210 @@ export function createChallengeFn(opts = {}){ * @returns {Promise} */ -async function verifyHttpChallenge(authz, challenge, keyAuthorization, suffix = `/.well-known/acme-challenge/${challenge.token}`) { + async function verifyHttpChallenge(authz, challenge, keyAuthorization, suffix = `/.well-known/acme-challenge/${challenge.token}`) { - async function doQuery(challengeUrl){ - log(`正在测试请求 ${challengeUrl} `) - // const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443; - // const challengeUrl = `https://${authz.identifier.value}:${httpsPort}${suffix}`; + async function doQuery(challengeUrl) { + log(`正在测试请求 ${challengeUrl} `) + // const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443; + // const challengeUrl = `https://${authz.identifier.value}:${httpsPort}${suffix}`; - /* May redirect to HTTPS with invalid/self-signed cert - https://letsencrypt.org/docs/challenge-types/#http-01-challenge */ - const httpsAgent = new https.Agent({ rejectUnauthorized: false }); + /* May redirect to HTTPS with invalid/self-signed cert - https://letsencrypt.org/docs/challenge-types/#http-01-challenge */ + const httpsAgent = new https.Agent({ rejectUnauthorized: false }); - log(`Sending HTTP query to ${authz.identifier.value}, suffix: ${suffix}, port: ${httpPort}`); - let data = "" - try{ - const resp = await axios.get(challengeUrl, { httpsAgent }); - data = (resp.data || '').replace(/\s+$/, ''); - }catch (e) { - log(`[error] HTTP request error from ${authz.identifier.value}`,e.message); - return false - } - - if (!data || (data !== keyAuthorization)) { - log(`[error] Authorization not found in HTTP response from ${authz.identifier.value}`); - return false - } - return true - - } - - const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80; - let host = authz.identifier.value; - if(utils.domain.isIpv6(host)){ - host = `[${host}]`; - } - const challengeUrl = `http://${host}:${httpPort}${suffix}`; - - if (!await doQuery(challengeUrl)) { - const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443; - const httpsChallengeUrl = `https://${host}:${httpsPort}${suffix}`; - const res = await doQuery(httpsChallengeUrl) - if (!res) { - throw new Error(`[error] 验证失败,请检查以上测试url是否可以正常访问`); - } - } - - - log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`); - return true; -} - -/** - * Walk DNS until TXT records are found - */ - -async function walkDnsChallengeRecord(recordName, resolver = dns,deep = 0) { - - let records = []; - - /* Resolve TXT records */ - try { - log(`检查域名 ${recordName} 的TXT记录`); - const txtRecords = await resolver.resolveTxt(recordName); - if (txtRecords && txtRecords.length) { - log(`找到 ${txtRecords.length} 条 TXT记录( ${recordName})`); - log(`TXT records: ${JSON.stringify(txtRecords)}`); - records = records.concat(...txtRecords); - } - } catch (e) { - log(`解析 TXT 记录出错, ${recordName} :${e.message}`); - } - - /* Resolve CNAME record first */ - try { - log(`检查是否存在CNAME映射: ${recordName}`); - const cnameRecords = await resolver.resolveCname(recordName); - - if (cnameRecords.length) { - const cnameRecord = cnameRecords[0]; - log(`已找到${recordName}的CNAME记录,将检查: ${cnameRecord}`); - let res= await walkTxtRecord(cnameRecord,deep+1); - if (res && res.length) { - log(`从CNAME中找到TXT记录: ${JSON.stringify(res)}`); - records = records.concat(...res); + log(`Sending HTTP query to ${authz.identifier.value}, suffix: ${suffix}, port: ${httpPort}`); + let data = "" + try { + const resp = await axios.get(challengeUrl, { httpsAgent }); + data = (resp.data || '').replace(/\s+$/, ''); + } catch (e) { + log(`[error] HTTP request error from ${authz.identifier.value}`, e.message); + return false } - }else{ - log(`没有CNAME映射(${recordName})`); + + if (!data || (data !== keyAuthorization)) { + log(`[error] Authorization not found in HTTP response from ${authz.identifier.value}`); + return false + } + return true + } - } catch (e) { - log(`检查CNAME出错(${recordName}) :${e.message}`); - } - return records -} - async function walkTxtRecord(recordName,deep = 0) { - if(deep >5){ - log(`walkTxtRecord too deep (#${deep}) , skip walk`) - return [] - } + const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80; + let host = authz.identifier.value; + if (utils.domain.isIpv6(host)) { + host = `[${host}]`; + } + const challengeUrl = `http://${host}:${httpPort}${suffix}`; - const txtRecords = [] - try { - /* Default DNS resolver first */ - log('从本地DNS服务器获取TXT解析记录'); - const res = await walkDnsChallengeRecord(recordName,dns,deep); - if (res && res.length > 0) { - for (const item of res) { - txtRecords.push(item) + if (!await doQuery(challengeUrl)) { + const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443; + const httpsChallengeUrl = `https://${host}:${httpsPort}${suffix}`; + const res = await doQuery(httpsChallengeUrl) + if (!res) { + throw new Error(`[error] 验证失败,请检查以上测试url是否可以正常访问`); } } - } catch (e) { - log(`本地获取TXT解析记录失败:${e.message}`) + + log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`); + return true; } - try{ - /* Authoritative DNS resolver */ - log(`从域名权威服务器获取TXT解析记录`); - const authoritativeResolver = await util.getAuthoritativeDnsResolver(recordName,log); - const res = await walkDnsChallengeRecord(recordName, authoritativeResolver,deep); - if (res && res.length > 0) { - for (const item of res) { - txtRecords.push(item) + /** + * Walk DNS until TXT records are found + */ + + async function walkDnsChallengeRecord(recordName, resolver = dns, deep = 0) { + + let records = []; + + const isAuthoritative = resolver === dns + /* Resolve TXT records */ + try { + log(`检查域名 ${recordName} 的TXT记录(from ${isAuthoritative ? '本地DNS' : '权威DNS服务器'})`); + const txtRecords = await resolver.resolveTxt(recordName); + if (txtRecords && txtRecords.length) { + log(`找到 ${txtRecords.length} 条 TXT记录( ${recordName})`); + log(`TXT records: ${JSON.stringify(txtRecords)}`); + records = records.concat(...txtRecords); + } + } catch (e) { + log(`解析 TXT 记录出错, ${recordName} :${e.message}`); + } + + /* Resolve CNAME record first */ + try { + log(`检查是否存在CNAME映射: ${recordName}`); + const cnameRecords = await resolver.resolveCname(recordName); + + if (cnameRecords.length) { + const cnameRecord = cnameRecords[0]; + log(`已找到${recordName}的CNAME记录,将检查: ${cnameRecord}`); + let res = await walkTxtRecord(cnameRecord, deep + 1); + if (res && res.length) { + log(`从CNAME中找到TXT记录: ${JSON.stringify(res)}`); + records = records.concat(...res); + } + } else { + log(`没有CNAME映射(${recordName})`); + } + } catch (e) { + log(`检查CNAME出错(${recordName}) :${e.message}`); + } + return records + } + + async function walkTxtRecord(recordName, deep = 0) { + if (deep > 5) { + log(`walkTxtRecord too deep (#${deep}) , skip walk`) + return [] + } + + const txtRecords = [] + try { + /* Default DNS resolver first */ + log('从本地DNS服务器获取TXT解析记录'); + const res = await walkDnsChallengeRecord(recordName, dns, deep); + if (res && res.length > 0) { + for (const item of res) { + txtRecords.push(item) + } + } + + } catch (e) { + log(`本地获取TXT解析记录失败:${e.message}`) + } + + if (walkFromAuthoritative !==false) { + try { + /* Authoritative DNS resolver */ + log(`从域名权威服务器获取TXT解析记录`); + const authoritativeResolver = await util.getAuthoritativeDnsResolver(recordName, log); + const res = await walkDnsChallengeRecord(recordName, authoritativeResolver, deep); + if (res && res.length > 0) { + for (const item of res) { + txtRecords.push(item) + } + } + } catch (e) { + log(`权威服务器获取TXT解析记录失败:${e.message}`) } } - }catch (e) { - log(`权威服务器获取TXT解析记录失败:${e.message}`) + + + if (txtRecords.length === 0) { + throw new Error(`没有找到TXT解析记录(${recordName})`); + } + return txtRecords; } - if (txtRecords.length === 0) { - throw new Error(`没有找到TXT解析记录(${recordName})`); - } - return txtRecords; -} + /** + * Verify ACME DNS challenge + * + * https://datatracker.ietf.org/doc/html/rfc8555#section-8.4 + * + * @param {object} authz Identifier authorization + * @param {object} challenge Authorization challenge + * @param {string} keyAuthorization Challenge key authorization + * @param {string} [prefix] DNS prefix + * @returns {Promise} + */ -/** - * Verify ACME DNS challenge - * - * https://datatracker.ietf.org/doc/html/rfc8555#section-8.4 - * - * @param {object} authz Identifier authorization - * @param {object} challenge Authorization challenge - * @param {string} keyAuthorization Challenge key authorization - * @param {string} [prefix] DNS prefix - * @returns {Promise} - */ + async function verifyDnsChallenge(authz, challenge, keyAuthorization, prefix = '_acme-challenge.') { + const recordName = `${prefix}${authz.identifier.value}`; + log(`本地校验TXT记录): ${recordName}`); + let recordValues = await walkTxtRecord(recordName, 0, walkFromAuthoritative); + //去重 + recordValues = [...new Set(recordValues)]; + log(`DNS查询成功, 找到 ${recordValues.length} 条TXT记录:${recordValues}`); + if (!recordValues.length || !recordValues.includes(keyAuthorization)) { + const err = `没有找到需要的DNS TXT记录: ${recordName},期望:${keyAuthorization},结果:${recordValues}` + throw new Error(err); + } -async function verifyDnsChallenge(authz, challenge, keyAuthorization, prefix = '_acme-challenge.') { - const recordName = `${prefix}${authz.identifier.value}`; - log(`本地校验TXT记录): ${recordName}`); - let recordValues = await walkTxtRecord(recordName); - //去重 - recordValues = [...new Set(recordValues)]; - log(`DNS查询成功, 找到 ${recordValues.length} 条TXT记录:${recordValues}`); - if (!recordValues.length || !recordValues.includes(keyAuthorization)) { - const err = `没有找到需要的DNS TXT记录: ${recordName},期望:${keyAuthorization},结果:${recordValues}` - throw new Error(err); + log(`关键授权匹配成功(${challenge.type}/${recordName}):${keyAuthorization},校验成功, ACME challenge verified`); + return true; } - log(`关键授权匹配成功(${challenge.type}/${recordName}):${keyAuthorization},校验成功, ACME challenge verified`); - return true; -} + /** + * Verify ACME TLS ALPN challenge + * + * https://datatracker.ietf.org/doc/html/rfc8737 + * + * @param {object} authz Identifier authorization + * @param {object} challenge Authorization challenge + * @param {string} keyAuthorization Challenge key authorization + * @returns {Promise} + */ -/** - * Verify ACME TLS ALPN challenge - * - * https://datatracker.ietf.org/doc/html/rfc8737 - * - * @param {object} authz Identifier authorization - * @param {object} challenge Authorization challenge - * @param {string} keyAuthorization Challenge key authorization - * @returns {Promise} - */ + async function verifyTlsAlpnChallenge(authz, challenge, keyAuthorization) { + const tlsAlpnPort = axios.defaults.acmeSettings.tlsAlpnChallengePort || 443; + const host = authz.identifier.value; + log(`Establishing TLS connection with host: ${host}:${tlsAlpnPort}`); -async function verifyTlsAlpnChallenge(authz, challenge, keyAuthorization) { - const tlsAlpnPort = axios.defaults.acmeSettings.tlsAlpnChallengePort || 443; - const host = authz.identifier.value; - log(`Establishing TLS connection with host: ${host}:${tlsAlpnPort}`); + const certificate = await util.retrieveTlsAlpnCertificate(host, tlsAlpnPort); + log('Certificate received from server successfully, matching key authorization in ALPN'); - const certificate = await util.retrieveTlsAlpnCertificate(host, tlsAlpnPort); - log('Certificate received from server successfully, matching key authorization in ALPN'); + if (!isAlpnCertificateAuthorizationValid(certificate, keyAuthorization)) { + throw new Error(`Authorization not found in certificate from ${authz.identifier.value}`); + } - if (!isAlpnCertificateAuthorizationValid(certificate, keyAuthorization)) { - throw new Error(`Authorization not found in certificate from ${authz.identifier.value}`); + log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`); + return true; } - log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`); - return true; -} - return { - challenges:{ + challenges: { 'http-01': verifyHttpChallenge, 'dns-01': verifyDnsChallenge, 'tls-alpn-01': verifyTlsAlpnChallenge, }, walkTxtRecord, + walkDnsChallengeRecord, } -} \ No newline at end of file +} + + + +// createChallengeFn({logger:{info:console.log}}).walkDnsChallengeRecord("handsfree.work") diff --git a/packages/core/acme-client/types/index.d.ts b/packages/core/acme-client/types/index.d.ts index 805451f60..a449eb650 100644 --- a/packages/core/acme-client/types/index.d.ts +++ b/packages/core/acme-client/types/index.d.ts @@ -219,4 +219,6 @@ export function getAuthoritativeDnsResolver(record:string): Promise; export const CancelError: typeof CancelError; -export function resolveDomainBySoaRecord(domain: string): Promise; \ No newline at end of file +export function resolveDomainBySoaRecord(domain: string): Promise; + +export function setWalkFromAuthoritative(value = true): void; \ No newline at end of file diff --git a/packages/libs/lib-server/src/system/settings/service/models.ts b/packages/libs/lib-server/src/system/settings/service/models.ts index 6218cdf99..baa340611 100644 --- a/packages/libs/lib-server/src/system/settings/service/models.ts +++ b/packages/libs/lib-server/src/system/settings/service/models.ts @@ -92,6 +92,9 @@ export class SysPrivateSettings extends BaseSettings { environmentVars?: string = ''; + acmeWalkFromAuthoritative?: boolean = true; + + sms?: { type?: string; config?: any; diff --git a/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts b/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts index 7b52bef84..86a16e4ed 100644 --- a/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts +++ b/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts @@ -4,7 +4,7 @@ import { Repository } from 'typeorm'; import { SysSettingsEntity } from '../entity/sys-settings.js'; import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, SysSecret, SysSecretBackup } from './models.js'; -import { getAllSslProviderDomains, setSslProviderReverseProxies } from '@certd/acme-client'; +import { getAllSslProviderDomains, setSslProviderReverseProxies, setWalkFromAuthoritative } from '@certd/acme-client'; import { cache, logger, mergeUtils, setGlobalProxy } from '@certd/basic'; import { isPlus } from '@certd/plus-core'; import * as dns from 'node:dns'; @@ -180,6 +180,9 @@ export class SysSettingsService extends BaseService { //加载环境变量 this.setEnvironmentVars(privateSetting.environmentVars); + + setWalkFromAuthoritative(privateSetting.acmeWalkFromAuthoritative); + } setEnvironmentVars(vars: string) { diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts index e6dbf8e83..e7c7be4f8 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts @@ -749,6 +749,18 @@ export default { pipelineValidTimeEnabledHelper: "Whether to enable the valid time of the pipeline", certDomainAddToMonitorEnabled: "Add Domain to Certificate Monitor", certDomainAddToMonitorEnabledHelper: "Whether to add the domain to the certificate monitor", + + defaultCertRenewDays: "Default Certificate Renew Days", + defaultCertRenewDaysHelper: "Default certificate renewal days, helpful for table list progress bar display", + defaultCertRenewDaysRecommend: "Recommend 15", + + pipelineMaxRunningCount: "Max Running Count", + pipelineMaxRunningCountHelper: "Max running count of the pipeline", + pipelineMaxRunningCountRecommend: "Recommend 5-15, default 10", + + acmeWalkFromAuthoritative: "Check TXT Record from Authoritative NS", + acmeWalkFromAuthoritativeHelper: "Apply certificate when whether to check the TXT record from authoritative NS server first", + fixedCertExpireDays: "Fixed Cert Expire Days", fixedCertExpireDaysHelper: "Fixed cert expiration days, helpful for table list progress bar display", fixedCertExpireDaysRecommend: "Recommend 90", diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts index bcfc5cac7..7a12dfe30 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -760,6 +760,8 @@ export default { pipelineMaxRunningCount: "同时最大运行流水线数量", pipelineMaxRunningCountHelper: "同一个用户同时运行的最大流水线数量,避免同时触发太多导致ACME账户被限制", pipelineMaxRunningCountRecommend: "推荐5-15,默认10", + acmeWalkFromAuthoritative: "从权威NS检查TXT记录", + acmeWalkFromAuthoritativeHelper: "申请证书时,是否从权威NS服务器检查TXT记录,如果影响申请证书,可以关闭", fixedCertExpireDays: "固定证书有效期天数", fixedCertExpireDaysHelper: "固定证书有效期天数,有助于列表进度条整齐显示", @@ -807,6 +809,7 @@ export default { environmentVars: "环境变量", environmentVarsHelper: "配置运行时环境变量,每行一个,格式:KEY=VALUE", bindUrl: "绑定URL", + bindUrlHelper: "绑定URL,在各类通知中显示你的站点URL", }, }, modal: { diff --git a/packages/ui/certd-client/src/store/settings/api.basic.ts b/packages/ui/certd-client/src/store/settings/api.basic.ts index 10cae7ca3..83b300033 100644 --- a/packages/ui/certd-client/src/store/settings/api.basic.ts +++ b/packages/ui/certd-client/src/store/settings/api.basic.ts @@ -109,6 +109,7 @@ export type SysPrivateSetting = { type?: string; config?: any; }; + acmeWalkFromAuthoritative?: boolean; //http请求超时时间 httpRequestTimeout?: number; diff --git a/packages/ui/certd-client/src/store/settings/index.tsx b/packages/ui/certd-client/src/store/settings/index.tsx index 7384bc917..050455452 100644 --- a/packages/ui/certd-client/src/store/settings/index.tsx +++ b/packages/ui/certd-client/src/store/settings/index.tsx @@ -303,8 +303,18 @@ export const useSettingStore = defineStore({ } }; const { closable = false } = opts; + let title = "URL地址未绑定,是否绑定此地址?"; + let okButtonText = "不,回到原来的地址"; + let okButtonDanger = false; + let forceBack = true; + if (closable) { + title = "绑定URL"; + okButtonText = "确定"; + okButtonDanger = false; + forceBack = false; + } const modalRef: any = Modal.warning({ - title: "URL地址未绑定,是否绑定此地址?", + title: title, width: 500, keyboard: false, closable, @@ -320,6 +330,7 @@ export const useSettingStore = defineStore({ 绑定到地址1 +
各类通知里面会以地址1作为URL显示
绑定地址2: @@ -334,12 +345,14 @@ export const useSettingStore = defineStore({ }, onOk: async () => { // await this.doBindUrl(); - window.location.href = bindUrl; + if (forceBack) { + window.location.href = bindUrl; + } }, okButtonProps: { - danger: true, + danger: okButtonDanger, }, - okText: "不,回到原来的地址", + okText: okButtonText, // cancelText: "不,回到原来的地址", // onOk: () => { // window.location.href = bindUrl; diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue index 04ba8edb1..98d4bd4aa 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue @@ -27,6 +27,7 @@ {{ t("certd.sys.setting.bindUrl") }} +
diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/pipeline.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/pipeline.vue index 35d7d7046..b3bef3af4 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/pipeline.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/pipeline.vue @@ -53,6 +53,13 @@
{{ t("certd.sys.setting.pipelineMaxRunningCountHelper") }}
+ +
+ +
+
{{ t("certd.sys.setting.acmeWalkFromAuthoritativeHelper") }}
+
+ {{ t("certd.saveButton") }} @@ -76,7 +83,9 @@ defineOptions({ const formState = reactive>({ public: {}, - private: {}, + private: { + acmeWalkFromAuthoritative: true, + }, }); async function loadSysSettings() {