Merge branch 'refs/heads/v2-dev' into v2

pull/229/head
xiaojunnuo 2024-10-23 10:43:37 +08:00
commit de62abf0e7
64 changed files with 591 additions and 135 deletions

View File

@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes
* 申请证书没有使用到系统设置的http代理的bug ([3db216f](https://github.com/certd/certd/commit/3db216f515ba404cb4330fdab452971b22a50f08))
* 修复移动任务后出现空阶段的bug ([4ea3edd](https://github.com/certd/certd/commit/4ea3edd59e93ca4f5b2e43b20dd4ef33909caddb))
* 修复google证书*.xx.com与xx.com同时申请时报错的bug ([f8b99b8](https://github.com/certd/certd/commit/f8b99b81a23e7e9fd5e05ebd5caf355c41d67a90))
* 允许七牛云cdn插件输入.号开头的通配符域名 ([18ee87d](https://github.com/certd/certd/commit/18ee87daff6eafc2201b58e28d85aafd3cb7a5b9))
### Performance Improvements
* 申请证书启用新的反代地址 ([a705182](https://github.com/certd/certd/commit/a705182b85e51157883e48f23463263793bf3c12))
* 优化日志颜色 ([1291e98](https://github.com/certd/certd/commit/1291e98e821c5b1810aab7f0aebe3f5f5cd44a20))
* 优化证书申请速度和成功率反代地址优化google基本可以稳定请求。增加请求重试。 ([41d9c3a](https://github.com/certd/certd/commit/41d9c3ac8398def541e65351cbe920d4a927182d))
* 优化pfx密码密码输入框让浏览器不自动填写密码 ([ffeede3](https://github.com/certd/certd/commit/ffeede38afa70c5ff6f2015516bead23d2c4df87))
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
### Bug Fixes ### Bug Fixes

View File

@ -1 +1 @@
12:34 10:38

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,5 @@
# 如何强制重新执行任务
## 强制重新执行任务
![](./images/rerun.png)

View File

@ -28,7 +28,7 @@ features:
- title: 多域名、泛域名打到一个证书上 - title: 多域名、泛域名打到一个证书上
details: 支持通配符域名/泛域名,支持多个域名打到一个证书上 details: 支持通配符域名/泛域名,支持多个域名打到一个证书上
- title: 多证书格式支持 - title: 多证书格式支持
details: 支持pem、pfx、der等多种证书格式 details: 支持pem、pfx、der等多种证书格式支持Google、Letsencrypt、ZeroSSL证书颁发机构
- title: 支持私有化部署 - title: 支持私有化部署
details: 保障数据安全 details: 保障数据安全
- title: 多数据库支持 - title: 多数据库支持

View File

@ -9,5 +9,5 @@
} }
}, },
"npmClient": "pnpm", "npmClient": "pnpm",
"version": "1.26.10" "version": "1.26.11"
} }

View File

@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/publishlab/node-acme-client/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes
* 申请证书没有使用到系统设置的http代理的bug ([3db216f](https://github.com/publishlab/node-acme-client/commit/3db216f515ba404cb4330fdab452971b22a50f08))
* 修复google证书*.xx.com与xx.com同时申请时报错的bug ([f8b99b8](https://github.com/publishlab/node-acme-client/commit/f8b99b81a23e7e9fd5e05ebd5caf355c41d67a90))
### Performance Improvements
* 优化证书申请速度和成功率反代地址优化google基本可以稳定请求。增加请求重试。 ([41d9c3a](https://github.com/publishlab/node-acme-client/commit/41d9c3ac8398def541e65351cbe920d4a927182d))
## [1.26.10](https://github.com/publishlab/node-acme-client/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/publishlab/node-acme-client/compare/v1.26.9...v1.26.10) (2024-10-20)
**Note:** Version bump only for package @certd/acme-client **Note:** Version bump only for package @certd/acme-client

View File

@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client", "description": "Simple and unopinionated ACME client",
"private": false, "private": false,
"author": "nmorsman", "author": "nmorsman",
"version": "1.26.10", "version": "1.26.11",
"main": "src/index.js", "main": "src/index.js",
"types": "types/index.d.ts", "types": "types/index.d.ts",
"license": "MIT", "license": "MIT",
@ -20,6 +20,7 @@
"asn1js": "^3.0.5", "asn1js": "^3.0.5",
"axios": "^1.7.2", "axios": "^1.7.2",
"debug": "^4.3.5", "debug": "^4.3.5",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.5", "https-proxy-agent": "^7.0.5",
"node-forge": "^1.3.1" "node-forge": "^1.3.1"
}, },
@ -59,5 +60,5 @@
"bugs": { "bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues" "url": "https://github.com/publishlab/node-acme-client/issues"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -0,0 +1,101 @@
const nodeHttp = require('node:http');
const https = require('node:https');
const { HttpProxyAgent } = require('http-proxy-agent');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { log } = require('./logger');
function createAgent(opts = {}) {
let httpAgent;
let
httpsAgent;
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
if (httpProxy) {
log(`acme use httpProxy:${httpProxy}`);
httpAgent = new HttpProxyAgent(httpProxy, opts);
}
else {
httpAgent = new nodeHttp.Agent(opts);
}
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
if (httpsProxy) {
log(`acme use httpsProxy:${httpsProxy}`);
httpsAgent = new HttpsProxyAgent(httpsProxy, opts);
}
else {
httpsAgent = new https.Agent(opts);
}
return {
httpAgent,
httpsAgent,
};
}
let defaultAgents = createAgent();
function getGlobalAgents() {
return defaultAgents;
}
function setGlobalProxy(opts) {
log('acme setGlobalProxy:', opts);
if (opts.httpProxy) {
process.env.HTTP_PROXY = opts.httpProxy;
}
if (opts.httpsProxy) {
process.env.HTTPS_PROXY = opts.httpsProxy;
}
defaultAgents = createAgent();
}
class HttpError extends Error {
constructor(error) {
super(error || error.message);
if (!error) {
return;
}
if (error.message.indexOf('ssl3_get_record:wrong version number') >= 0) {
this.message = 'http协议错误服务端要求http协议请检查是否使用了https请求';
}
this.name = error.name;
this.code = error.code;
this.cause = error.cause;
if (error.response) {
this.status = error.response.status;
this.statusText = error.response.statusText;
this.response = {
data: error.response.data,
};
}
let url = '';
if (error.config) {
this.request = {
baseURL: error.config.baseURL,
url: error.config.url,
method: error.config.method,
params: error.config.params,
data: error.config.data,
};
url = error.config.baseURL + error.config.url;
}
if (url) {
this.message = `${this.message}:${url}`;
}
delete error.response;
delete error.config;
delete error.request;
// logger.error(error);
}
}
module.exports = {
setGlobalProxy,
createAgent,
getGlobalAgents,
HttpError,
};

View File

@ -30,6 +30,7 @@ class AcmeApi {
} }
} }
} }
console.log(locationUrl, mapping);
return locationUrl; return locationUrl;
} }

View File

@ -182,12 +182,19 @@ module.exports = async (client, userOpts) => {
authorizations.forEach((authz) => { authorizations.forEach((authz) => {
const d = authz.identifier.value; const d = authz.identifier.value;
log(`authorization:domain = ${d}, value = ${JSON.stringify(authz)}`);
if (authz.status === 'valid') {
log(`[auto] [${d}] Authorization already has valid status, no need to complete challenges`);
return;
}
let setd = false; let setd = false;
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (const group of domainSets) { for (const group of domainSets) {
if (!group[d]) { if (!group[d]) {
group[d] = authz; group[d] = authz;
setd = true; setd = true;
break;
} }
} }
if (!setd) { if (!setd) {
@ -197,6 +204,8 @@ module.exports = async (client, userOpts) => {
} }
}); });
// log(`domainSets:${JSON.stringify(domainSets)}`);
const allChallengePromises = []; const allChallengePromises = [];
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (const domainSet of domainSets) { for (const domainSet of domainSets) {
@ -233,28 +242,52 @@ module.exports = async (client, userOpts) => {
return Promise.all(results); return Promise.all(results);
} }
log(`开始challenge${allChallengePromises.length}`); try {
let i = 0; log(`开始challenge${allChallengePromises.length}`);
// eslint-disable-next-line no-restricted-syntax let i = 0;
for (const challengePromises of allChallengePromises) { // eslint-disable-next-line no-restricted-syntax
i += 1; for (const challengePromises of allChallengePromises) {
log(`开始第${i}`); i += 1;
if (opts.signal && opts.signal.aborted) { log(`开始第${i}`);
throw new Error('用户取消'); if (opts.signal && opts.signal.aborted) {
} throw new Error('用户取消');
}
try {
// eslint-disable-next-line no-await-in-loop
await runPromisePa(challengePromises);
}
catch (e) {
log(`证书申请失败${e.message}`);
throw e;
}
finally {
log(`清理challenge痕迹length:${clearTasks.length}`);
try { try {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await runPromisePa(challengePromises);
}
catch (e) {
log(`证书申请失败${e.message}`);
throw e;
}
finally {
if (client.opts.sslProvider !== 'google') {
// letsencrypt 如果同时检出两个TXT记录会以第一个为准就会校验失败所以需要提前删除
// zerossl 此方式测试无问题
log(`清理challenge痕迹length:${clearTasks.length}`);
try {
// eslint-disable-next-line no-await-in-loop
await runAllPromise(clearTasks);
}
catch (e) {
log('清理challenge失败');
log(e);
}
}
}
}
}
finally {
if (client.opts.sslProvider === 'google') {
// google 相同的域名txt记录是一样的不能提前删除否则校验失败报错如下
// Error: The TXT record retrieved from _acme-challenge.bbc.handsfree.work.
// at the time the challenge was validated did not contain JshHVu7dt_DT6uYILWhokHefFVad2Q6Mw1L-fNZFcq8
// (the base64url-encoded SHA-256 digest of RlJZNBR0LWnxNK_xd2zqtYVvCiNJOKJ3J1NmCjU_9BjaUJgL3k-qSpIhQ-uF4FBS.NRyqT8fRiq6THzzrvkgzgR5Xai2LsA2SyGLAq_wT3qc).
// See https://tools.ietf.org/html/rfc8555#section-8.4 for more information.
log(`清理challenge痕迹length:${clearTasks.length}`);
try {
// eslint-disable-next-line no-await-in-loop
await runAllPromise(clearTasks); await runAllPromise(clearTasks);
} }
catch (e) { catch (e) {
@ -263,6 +296,7 @@ module.exports = async (client, userOpts) => {
} }
} }
} }
log('challenge结束'); log('challenge结束');
// log('[auto] Waiting for challenge valid status'); // log('[auto] Waiting for challenge valid status');

View File

@ -1,11 +1,11 @@
/** /**
* Axios instance * Axios instance
*/ */
const axios = require('axios'); const axios = require('axios');
const { parseRetryAfterHeader } = require('./util'); const { parseRetryAfterHeader } = require('./util');
const { log } = require('./logger'); const { log } = require('./logger');
const pkg = require('./../package.json'); const pkg = require('./../package.json');
const Agents = require('./agents');
const { AxiosError } = axios; const { AxiosError } = axios;
@ -24,8 +24,8 @@ instance.defaults.acmeSettings = {
httpsChallengePort: 443, httpsChallengePort: 443,
tlsAlpnChallengePort: 443, tlsAlpnChallengePort: 443,
retryMaxAttempts: 5, retryMaxAttempts: 3,
retryDefaultDelay: 5, retryDefaultDelay: 3,
}; };
// instance.defaults.proxy = { // instance.defaults.proxy = {
// host: '192.168.34.139', // host: '192.168.34.139',
@ -56,19 +56,26 @@ function isRetryableError(error) {
/* https://github.com/axios/axios/blob/main/lib/core/settle.js */ /* https://github.com/axios/axios/blob/main/lib/core/settle.js */
function validateStatus(response) { function validateStatus(response) {
const validator = response.config.retryValidateStatus; if (!response) {
return new Error('Response is undefined');
}
let validator = null;
if (response.config) {
validator = response.config.retryValidateStatus;
}
if (!response.status || !validator || validator(response.status)) { if (!response.status || !validator || validator(response.status)) {
return response; return response;
} }
throw new AxiosError( const err = new AxiosError(
`Request failed with status code ${response.status}`, `Request failed with status code ${response.status}`,
(Math.floor(response.status / 100) === 4) ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE, (Math.floor(response.status / 100) === 4) ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE,
response.config, response.config,
response.request, response.request,
response, response,
); );
throw new Agents.HttpError(err);
} }
/* Pass all responses through the error interceptor */ /* Pass all responses through the error interceptor */
@ -76,8 +83,17 @@ instance.interceptors.request.use((config) => {
if (!('retryValidateStatus' in config)) { if (!('retryValidateStatus' in config)) {
config.retryValidateStatus = config.validateStatus; config.retryValidateStatus = config.validateStatus;
} }
config.validateStatus = () => false; config.validateStatus = () => false;
const agents = Agents.getGlobalAgents();
// if (config.skipSslVerify) {
// logger.info('跳过SSL验证');
// agents = createAgent({ rejectUnauthorized: false } as any);
// }
// delete config.skipSslVerify;
config.httpsAgent = agents.httpsAgent;
config.httpAgent = agents.httpAgent;
config.proxy = false; // 必须 否则还会走一层代理,
return config; return config;
}); });
@ -86,7 +102,7 @@ instance.interceptors.response.use(null, async (error) => {
const { config, response } = error; const { config, response } = error;
if (!config) { if (!config) {
return Promise.reject(error); return Promise.reject(new Agents.HttpError(error));
} }
/* Pick up errors we want to retry */ /* Pick up errors we want to retry */
@ -115,6 +131,9 @@ instance.interceptors.response.use(null, async (error) => {
} }
} }
if (!response) {
return Promise.reject(new Agents.HttpError(error));
}
/* Validate and return response */ /* Validate and return response */
return validateStatus(response); return validateStatus(response);
}); });

View File

@ -558,6 +558,7 @@ class AcmeClient {
const verifyFn = async (abort) => { const verifyFn = async (abort) => {
if (this.opts.signal && this.opts.signal.aborted) { if (this.opts.signal && this.opts.signal.aborted) {
abort();
throw new Error('用户取消'); throw new Error('用户取消');
} }

View File

@ -3,21 +3,9 @@
*/ */
const { createHmac, createSign, constants: { RSA_PKCS1_PADDING } } = require('crypto'); const { createHmac, createSign, constants: { RSA_PKCS1_PADDING } } = require('crypto');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { getJwk } = require('./crypto'); const { getJwk } = require('./crypto');
const { log } = require('./logger'); const { log } = require('./logger');
const axios1 = require('./axios'); const axios = require('./axios');
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
let httpsAgent = null;
if (httpsProxy) {
httpsAgent = new HttpsProxyAgent(httpsProxy);
log(`use https_proxy:${httpsProxy}`);
}
const axios = axios1.create({
proxy: false,
httpsAgent,
});
/** /**
* ACME HTTP client * ACME HTTP client

View File

@ -39,6 +39,7 @@ exports.forge = require('./crypto/forge');
*/ */
exports.axios = require('./axios'); exports.axios = require('./axios');
exports.agents = require('./agents');
/** /**
* Logger * Logger

View File

@ -22,7 +22,7 @@ exports.setLogger = (fn) => {
* @param {string} msg Message * @param {string} msg Message
*/ */
exports.log = (msg) => { exports.log = (...msg) => {
debug(msg); debug(...msg);
logger(msg); logger(...msg);
}; };

View File

@ -37,6 +37,7 @@ export type UrlMapping={
*/ */
export interface ClientOptions { export interface ClientOptions {
sslProvider:string;
directoryUrl: string; directoryUrl: string;
accountKey: PrivateKeyBuffer | PrivateKeyString; accountKey: PrivateKeyBuffer | PrivateKeyString;
accountUrl?: string; accountUrl?: string;
@ -192,6 +193,7 @@ export const forge: CryptoLegacyInterface;
export const axios: AxiosInstance; export const axios: AxiosInstance;
export const agents: any;
/** /**
* Logger * Logger
*/ */

View File

@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes
* 申请证书没有使用到系统设置的http代理的bug ([3db216f](https://github.com/certd/certd/commit/3db216f515ba404cb4330fdab452971b22a50f08))
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
**Note:** Version bump only for package @certd/basic **Note:** Version bump only for package @certd/basic

View File

@ -1 +1 @@
12:31 10:35

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/basic", "name": "@certd/basic",
"private": false, "private": false,
"version": "1.26.10", "version": "1.26.11",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -64,5 +64,5 @@
"vite": "^4.3.8", "vite": "^4.3.8",
"vue-tsc": "^1.6.5" "vue-tsc": "^1.6.5"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -5,6 +5,7 @@ import { HttpProxyAgent } from 'http-proxy-agent';
import { HttpsProxyAgent } from 'https-proxy-agent'; import { HttpsProxyAgent } from 'https-proxy-agent';
import nodeHttp from 'http'; import nodeHttp from 'http';
import * as https from 'node:https'; import * as https from 'node:https';
import { merge } from 'lodash-es';
export class HttpError extends Error { export class HttpError extends Error {
status?: number; status?: number;
statusText?: string; statusText?: string;
@ -35,6 +36,14 @@ export class HttpError extends Error {
params: error.config?.params, params: error.config?.params,
data: error.config?.data, data: error.config?.data,
}; };
let url = error.config?.url;
if (error.config?.baseURL) {
url = error.config?.baseURL + url;
}
if (url) {
this.message = `${this.message} : url=${url}`;
}
this.response = { this.response = {
data: error.response?.data, data: error.response?.data,
}; };
@ -62,6 +71,10 @@ export function setGlobalProxy(opts: { httpProxy?: string; httpsProxy?: string }
defaultAgents = createAgent(); defaultAgents = createAgent();
} }
export function getGlobalAgents() {
return defaultAgents;
}
/** /**
* @description * @description
*/ */
@ -72,7 +85,10 @@ export function createAxiosService({ logger }: { logger: Logger }) {
// 请求拦截 // 请求拦截
service.interceptors.request.use( service.interceptors.request.use(
(config: any) => { (config: any) => {
logger.info(`http request:${config.url}method:${config.method}params:${JSON.stringify(config.params)}`); logger.info(`http request:${config.url}method:${config.method}`);
if (config.logParams !== false) {
logger.info(`params:${JSON.stringify(config.params)}`);
}
if (config.timeout == null) { if (config.timeout == null) {
config.timeout = 15000; config.timeout = 15000;
} }
@ -84,6 +100,11 @@ export function createAxiosService({ logger }: { logger: Logger }) {
delete config.skipSslVerify; delete config.skipSslVerify;
config.httpsAgent = agents.httpsAgent; config.httpsAgent = agents.httpsAgent;
config.httpAgent = agents.httpAgent; config.httpAgent = agents.httpAgent;
// const agent = new https.Agent({
// rejectUnauthorized: false // 允许自签名证书
// });
// config.httpsAgent = agent;
config.proxy = false; //必须 否则还会走一层代理, config.proxy = false; //必须 否则还会走一层代理,
return config; return config;
}, },
@ -96,7 +117,11 @@ export function createAxiosService({ logger }: { logger: Logger }) {
// 响应拦截 // 响应拦截
service.interceptors.response.use( service.interceptors.response.use(
(response: any) => { (response: any) => {
logger.info('http response:', JSON.stringify(response?.data)); if (response?.config?.logRes !== false) {
logger.info(`http response : status=${response?.status},data=${JSON.stringify(response?.data)}`);
} else {
logger.info('http response status:', response?.status);
}
return response.data; return response.data;
}, },
(error: any) => { (error: any) => {
@ -160,6 +185,8 @@ export type HttpClientResponse<R> = any;
export type HttpRequestConfig<D> = { export type HttpRequestConfig<D> = {
skipSslVerify?: boolean; skipSslVerify?: boolean;
skipCheckRes?: boolean; skipCheckRes?: boolean;
logParams?: boolean;
logRes?: boolean;
} & AxiosRequestConfig<D>; } & AxiosRequestConfig<D>;
export type HttpClient = { export type HttpClient = {
request<D = any, R = any>(config: HttpRequestConfig<D>): Promise<HttpClientResponse<R>>; request<D = any, R = any>(config: HttpRequestConfig<D>): Promise<HttpClientResponse<R>>;
@ -171,6 +198,7 @@ export function createAgent(opts: nodeHttp.AgentOptions = {}) {
if (httpProxy) { if (httpProxy) {
logger.info('use httpProxy:', httpProxy); logger.info('use httpProxy:', httpProxy);
httpAgent = new HttpProxyAgent(httpProxy, opts as any); httpAgent = new HttpProxyAgent(httpProxy, opts as any);
merge(httpAgent.options, opts);
} else { } else {
httpAgent = new nodeHttp.Agent(opts); httpAgent = new nodeHttp.Agent(opts);
} }
@ -178,6 +206,7 @@ export function createAgent(opts: nodeHttp.AgentOptions = {}) {
if (httpsProxy) { if (httpsProxy) {
logger.info('use httpsProxy:', httpsProxy); logger.info('use httpsProxy:', httpsProxy);
httpsAgent = new HttpsProxyAgent(httpsProxy, opts as any); httpsAgent = new HttpsProxyAgent(httpsProxy, opts as any);
merge(httpsAgent.options, opts);
} else { } else {
httpsAgent = new https.Agent(opts); httpsAgent = new https.Agent(opts);
} }

View File

@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Performance Improvements
* 申请证书启用新的反代地址 ([a705182](https://github.com/certd/certd/commit/a705182b85e51157883e48f23463263793bf3c12))
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
### Bug Fixes ### Bug Fixes

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/pipeline", "name": "@certd/pipeline",
"private": false, "private": false,
"version": "1.26.10", "version": "1.26.11",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -15,8 +15,8 @@
"test": "mocha --loader=ts-node/esm" "test": "mocha --loader=ts-node/esm"
}, },
"dependencies": { "dependencies": {
"@certd/basic": "^1.26.10", "@certd/basic": "^1.26.11",
"@certd/plus-core": "^1.26.10", "@certd/plus-core": "^1.26.11",
"axios": "^1.7.2", "axios": "^1.7.2",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"fix-path": "^4.0.0", "fix-path": "^4.0.0",
@ -66,5 +66,5 @@
"vite": "^4.3.8", "vite": "^4.3.8",
"vue-tsc": "^1.6.5" "vue-tsc": "^1.6.5"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -128,6 +128,10 @@ export class Executor {
this.runtime.skip(runnable); this.runtime.skip(runnable);
return resultType; return resultType;
} }
if (resultType == ResultType.disabled) {
this.runtime.disabled(runnable);
return resultType;
}
this.runtime.success(runnable); this.runtime.success(runnable);
return ResultType.success; return ResultType.success;
} catch (e: any) { } catch (e: any) {
@ -164,12 +168,14 @@ export class Executor {
let resList: ResultType[] = []; let resList: ResultType[] = [];
if (stage.concurrency === ConcurrencyStrategy.Parallel) { if (stage.concurrency === ConcurrencyStrategy.Parallel) {
//并行
const pList = []; const pList = [];
for (const item of runnerList) { for (const item of runnerList) {
pList.push(item()); pList.push(item());
} }
resList = await Promise.all(pList); resList = await Promise.all(pList);
} else { } else {
//串行
for (let i = 0; i < runnerList.length; i++) { for (let i = 0; i < runnerList.length; i++) {
const runner = runnerList[i]; const runner = runnerList[i];
resList[i] = await runner(); resList[i] = await runner();
@ -181,6 +187,7 @@ export class Executor {
compositionResultType(resList: ResultType[]): ResultType { compositionResultType(resList: ResultType[]): ResultType {
let hasSuccess = false; let hasSuccess = false;
let hasSkip = false; let hasSkip = false;
let hasDisabled = false;
for (const type of resList) { for (const type of resList) {
if (type === ResultType.error) { if (type === ResultType.error) {
return ResultType.error; return ResultType.error;
@ -188,8 +195,14 @@ export class Executor {
hasSuccess = true; hasSuccess = true;
} else if (type === ResultType.skip) { } else if (type === ResultType.skip) {
hasSkip = true; hasSkip = true;
} else if (type === ResultType.disabled) {
hasDisabled = true;
} }
} }
if (!hasSuccess && !hasSkip && hasDisabled) {
//全是disabled
return ResultType.disabled;
}
if (!hasSuccess && hasSkip) { if (!hasSuccess && hasSkip) {
//全是跳过 //全是跳过
return ResultType.skip; return ResultType.skip;

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
**Note:** Version bump only for package @certd/lib-huawei
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
**Note:** Version bump only for package @certd/lib-huawei **Note:** Version bump only for package @certd/lib-huawei

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-huawei", "name": "@certd/lib-huawei",
"private": false, "private": false,
"version": "1.26.10", "version": "1.26.11",
"main": "./dist/bundle.js", "main": "./dist/bundle.js",
"module": "./dist/bundle.js", "module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts", "types": "./dist/d/index.d.ts",
@ -17,5 +17,5 @@
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"rollup": "^3.7.4" "rollup": "^3.7.4"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
**Note:** Version bump only for package @certd/lib-iframe
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
**Note:** Version bump only for package @certd/lib-iframe **Note:** Version bump only for package @certd/lib-iframe

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-iframe", "name": "@certd/lib-iframe",
"private": false, "private": false,
"version": "1.26.10", "version": "1.26.11",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -39,5 +39,5 @@
"tslib": "^2.5.2", "tslib": "^2.5.2",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
**Note:** Version bump only for package @certd/lib-jdcloud
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
**Note:** Version bump only for package @certd/lib-jdcloud **Note:** Version bump only for package @certd/lib-jdcloud

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-jdcloud", "name": "@certd/lib-jdcloud",
"private": false, "private": false,
"version": "1.26.10", "version": "1.26.11",
"main": "./dist/bundle.mjs", "main": "./dist/bundle.mjs",
"module": "./dist/bundle.mjs", "module": "./dist/bundle.mjs",
"types": "./dist/d/index.d.ts", "types": "./dist/d/index.d.ts",
@ -27,5 +27,5 @@
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"rollup": "^3.7.4" "rollup": "^3.7.4"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
**Note:** Version bump only for package @certd/lib-k8s
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
**Note:** Version bump only for package @certd/lib-k8s **Note:** Version bump only for package @certd/lib-k8s

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-k8s", "name": "@certd/lib-k8s",
"private": false, "private": false,
"version": "1.26.10", "version": "1.26.11",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -18,7 +18,7 @@
"@kubernetes/client-node": "0.21.0" "@kubernetes/client-node": "0.21.0"
}, },
"devDependencies": { "devDependencies": {
"@certd/pipeline": "^1.26.10", "@certd/pipeline": "^1.26.11",
"@rollup/plugin-commonjs": "^23.0.4", "@rollup/plugin-commonjs": "^23.0.4",
"@rollup/plugin-json": "^6.0.0", "@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-node-resolve": "^15.0.1",
@ -40,5 +40,5 @@
"tslib": "^2.5.2", "tslib": "^2.5.2",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes
* 申请证书没有使用到系统设置的http代理的bug ([3db216f](https://github.com/certd/certd/commit/3db216f515ba404cb4330fdab452971b22a50f08))
### Performance Improvements
* 申请证书启用新的反代地址 ([a705182](https://github.com/certd/certd/commit/a705182b85e51157883e48f23463263793bf3c12))
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
**Note:** Version bump only for package @certd/lib-server **Note:** Version bump only for package @certd/lib-server

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/lib-server", "name": "@certd/lib-server",
"version": "1.26.10", "version": "1.26.11",
"description": "midway with flyway, sql upgrade way ", "description": "midway with flyway, sql upgrade way ",
"private": false, "private": false,
"type": "module", "type": "module",
@ -26,8 +26,9 @@
], ],
"license": "AGPL", "license": "AGPL",
"dependencies": { "dependencies": {
"@certd/basic": "^1.26.10", "@certd/acme-client": "^1.26.11",
"@certd/pipeline": "^1.26.10", "@certd/basic": "^1.26.11",
"@certd/pipeline": "^1.26.11",
"@midwayjs/cache": "~3.14.0", "@midwayjs/cache": "~3.14.0",
"@midwayjs/core": "~3.17.1", "@midwayjs/core": "~3.17.1",
"@midwayjs/i18n": "~3.17.3", "@midwayjs/i18n": "~3.17.3",
@ -68,5 +69,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -7,7 +7,7 @@ import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, Sy
import * as _ from 'lodash-es'; import * as _ from 'lodash-es';
import { BaseService } from '../../../basic/index.js'; import { BaseService } from '../../../basic/index.js';
import { logger, setGlobalProxy } from '@certd/basic'; import { logger, setGlobalProxy } from '@certd/basic';
import { agents } from '@certd/acme-client';
/** /**
* *
*/ */
@ -23,7 +23,6 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
getRepository() { getRepository() {
return this.repository; return this.repository;
} }
async getById(id: any): Promise<SysSettingsEntity | null> { async getById(id: any): Promise<SysSettingsEntity | null> {
const entity = await this.info(id); const entity = await this.info(id);
if (!entity) { if (!entity) {
@ -129,10 +128,12 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
async reloadPrivateSettings() { async reloadPrivateSettings() {
const bean = await this.getPrivateSettings(); const bean = await this.getPrivateSettings();
if (bean.httpProxy || bean.httpsProxy) { if (bean.httpProxy || bean.httpsProxy) {
setGlobalProxy({ const opts = {
httpProxy: bean.httpProxy, httpProxy: bean.httpProxy,
httpsProxy: bean.httpsProxy, httpsProxy: bean.httpsProxy,
}); };
setGlobalProxy(opts);
agents.setGlobalProxy(opts);
} }
} }
@ -149,10 +150,10 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
async backupSecret() { async backupSecret() {
const settings = await this.getSettingByKey(SysSecretBackup.__key__); const settings = await this.getSettingByKey(SysSecretBackup.__key__);
const privateSettings = await this.getPrivateSettings();
const installInfo = await this.getSetting<SysInstallInfo>(SysInstallInfo);
if (settings == null) { if (settings == null) {
const backup = new SysSecretBackup(); const backup = new SysSecretBackup();
const privateSettings = await this.getPrivateSettings();
const installInfo = await this.getSetting<SysInstallInfo>(SysInstallInfo);
if (installInfo.siteId == null || privateSettings.encryptSecret == null) { if (installInfo.siteId == null || privateSettings.encryptSecret == null) {
logger.error('备份密钥失败siteId或encryptSecret为空'); logger.error('备份密钥失败siteId或encryptSecret为空');
return; return;
@ -161,6 +162,14 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
backup.encryptSecret = privateSettings.encryptSecret; backup.encryptSecret = privateSettings.encryptSecret;
await this.saveSetting(backup); await this.saveSetting(backup);
logger.info('备份密钥成功'); logger.info('备份密钥成功');
} else {
//校验是否有变化
if (settings.siteId !== installInfo.siteId) {
throw new Error(`siteId与备份不一致可能是数据异常请检查backup=${settings.siteId}, current=${installInfo.siteId}`);
}
if (settings.encryptSecret !== privateSettings.encryptSecret) {
throw new Error('encryptSecret与备份不一致可能是数据异常请检查');
}
} }
} }
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
**Note:** Version bump only for package @certd/midway-flyway-js **Note:** Version bump only for package @certd/midway-flyway-js

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/midway-flyway-js", "name": "@certd/midway-flyway-js",
"version": "1.26.10", "version": "1.26.11",
"description": "midway with flyway, sql upgrade way ", "description": "midway with flyway, sql upgrade way ",
"private": false, "private": false,
"type": "module", "type": "module",
@ -56,5 +56,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Performance Improvements
* 申请证书启用新的反代地址 ([a705182](https://github.com/certd/certd/commit/a705182b85e51157883e48f23463263793bf3c12))
* 优化证书申请速度和成功率反代地址优化google基本可以稳定请求。增加请求重试。 ([41d9c3a](https://github.com/certd/certd/commit/41d9c3ac8398def541e65351cbe920d4a927182d))
* 优化pfx密码密码输入框让浏览器不自动填写密码 ([ffeede3](https://github.com/certd/certd/commit/ffeede38afa70c5ff6f2015516bead23d2c4df87))
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
### Bug Fixes ### Bug Fixes

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-cert", "name": "@certd/plugin-cert",
"private": false, "private": false,
"version": "1.26.10", "version": "1.26.11",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -15,9 +15,9 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@certd/acme-client": "^1.26.10", "@certd/acme-client": "^1.26.11",
"@certd/basic": "^1.26.10", "@certd/basic": "^1.26.11",
"@certd/pipeline": "^1.26.10", "@certd/pipeline": "^1.26.11",
"@google-cloud/publicca": "^1.3.0", "@google-cloud/publicca": "^1.3.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"jszip": "^3.10.1", "jszip": "^3.10.1",
@ -57,5 +57,5 @@
"vite": "^3.1.0", "vite": "^3.1.0",
"vue-tsc": "^0.38.9" "vue-tsc": "^0.38.9"
}, },
"gitHead": "617cc13e29e6a325ac6fc0202499398390d25997" "gitHead": "3a78cb9929fd63bb72f0e00f4389e775c926c789"
} }

View File

@ -89,12 +89,15 @@ export class AcmeService {
} }
async getAcmeClient(email: string, isTest = false): Promise<acme.Client> { async getAcmeClient(email: string, isTest = false): Promise<acme.Client> {
const mappings = {};
if (this.sslProvider === "letsencrypt") {
mappings["acme-v02.api.letsencrypt.org"] = this.options.reverseProxy || "le.px.certd.handfree.work";
} else if (this.sslProvider === "google") {
mappings["dv.acme-v02.api.pki.goog"] = this.options.reverseProxy || "gg.px.certd.handfree.work";
}
const urlMapping: UrlMapping = { const urlMapping: UrlMapping = {
enabled: false, enabled: false,
mappings: { mappings,
"acme-v02.api.letsencrypt.org": this.options.reverseProxy || "letsencrypt.proxy.handsfree.work",
"dv.acme-v02.api.pki.goog": this.options.reverseProxy || "google.proxy.handsfree.work",
},
}; };
const conf = await this.getAccountConfig(email, urlMapping); const conf = await this.getAccountConfig(email, urlMapping);
if (conf.key == null) { if (conf.key == null) {
@ -119,6 +122,7 @@ export class AcmeService {
} }
} }
const client = new acme.Client({ const client = new acme.Client({
sslProvider: this.sslProvider,
directoryUrl: directoryUrl, directoryUrl: directoryUrl,
accountKey: conf.key, accountKey: conf.key,
accountUrl: conf.accountUrl, accountUrl: conf.accountUrl,
@ -172,7 +176,7 @@ export class AcmeService {
this.logger.info(`Would create TXT record "${fullRecord}" with value "${recordValue}"`); this.logger.info(`Would create TXT record "${fullRecord}" with value "${recordValue}"`);
let domain = parseDomain(fullDomain); let domain = parseDomain(fullDomain);
this.logger.info("解析到域名domain=", domain); this.logger.info("解析到域名domain=" + domain);
if (domainsVerifyPlan) { if (domainsVerifyPlan) {
//按照计划执行 //按照计划执行

View File

@ -50,7 +50,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
@TaskInput({ @TaskInput({
title: "PFX证书密码", title: "PFX证书密码",
component: { component: {
name: "a-input-password", name: "input-password",
vModel: "value", vModel: "value",
}, },
required: false, required: false,
@ -227,7 +227,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
* "successNotify": true, * "successNotify": true,
* "pfxPassword": "123456" * "pfxPassword": "123456"
*/ */
const checkInputChanges = ["domains", "sslProvider", "privateKeyType", "dnsProviderType", "dnsProviderAccess", "pfxPassword"]; const checkInputChanges = ["domains", "sslProvider", "privateKeyType", "dnsProviderType", "pfxPassword"];
const oldInput = JSON.stringify(pick(this.lastStatus?.input, checkInputChanges)); const oldInput = JSON.stringify(pick(this.lastStatus?.input, checkInputChanges));
const thisInput = JSON.stringify(pick(this, checkInputChanges)); const thisInput = JSON.stringify(pick(this, checkInputChanges));
inputChanged = oldInput !== thisInput; inputChanged = oldInput !== thisInput;

View File

@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes
* 修复移动任务后出现空阶段的bug ([4ea3edd](https://github.com/certd/certd/commit/4ea3edd59e93ca4f5b2e43b20dd4ef33909caddb))
* 允许七牛云cdn插件输入.号开头的通配符域名 ([18ee87d](https://github.com/certd/certd/commit/18ee87daff6eafc2201b58e28d85aafd3cb7a5b9))
### Performance Improvements
* 优化日志颜色 ([1291e98](https://github.com/certd/certd/commit/1291e98e821c5b1810aab7f0aebe3f5f5cd44a20))
* 优化证书申请速度和成功率反代地址优化google基本可以稳定请求。增加请求重试。 ([41d9c3a](https://github.com/certd/certd/commit/41d9c3ac8398def541e65351cbe920d4a927182d))
* 优化pfx密码密码输入框让浏览器不自动填写密码 ([ffeede3](https://github.com/certd/certd/commit/ffeede38afa70c5ff6f2015516bead23d2c4df87))
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
### Bug Fixes ### Bug Fixes

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/ui-client", "name": "@certd/ui-client",
"version": "1.26.10", "version": "1.26.11",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite --open", "dev": "vite --open",
@ -11,6 +11,7 @@
"debug:force": "vite --force --mode debug", "debug:force": "vite --force --mode debug",
"build": " vite build ", "build": " vite build ",
"dev-build": "echo 1", "dev-build": "echo 1",
"test:unit": "vitest",
"serve": "vite preview", "serve": "vite preview",
"preview": "vite preview", "preview": "vite preview",
"pretty-quick": "pretty-quick", "pretty-quick": "pretty-quick",
@ -61,8 +62,8 @@
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@certd/lib-iframe": "^1.26.10", "@certd/lib-iframe": "^1.26.11",
"@certd/pipeline": "^1.26.10", "@certd/pipeline": "^1.26.11",
"@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12", "@types/chai": "^4.3.12",
@ -77,7 +78,7 @@
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/compiler-sfc": "^3.4.21", "@vue/compiler-sfc": "^3.4.21",
"@vue/eslint-config-typescript": "^13.0.0", "@vue/eslint-config-typescript": "^13.0.0",
"@vue/test-utils": "^2.4.5", "@vue/test-utils": "^2.4.6",
"autoprefixer": "^10.4.18", "autoprefixer": "^10.4.18",
"caller-path": "^4.0.0", "caller-path": "^4.0.0",
"chai": "^5.1.0", "chai": "^5.1.0",
@ -113,6 +114,7 @@
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.2", "vite-plugin-html": "^3.2.2",
"vite-plugin-windicss": "^1.9.3", "vite-plugin-windicss": "^1.9.3",
"vitest": "^2.1.2",
"vue-eslint-parser": "^9.4.2", "vue-eslint-parser": "^9.4.2",
"vue-tsc": "^1.8.8" "vue-tsc": "^1.8.8"
}, },

View File

@ -53,7 +53,9 @@ function createService() {
// @ts-ignore // @ts-ignore
response.config.onError(new Error(errorMessage)); response.config.onError(new Error(errorMessage));
} }
errorCreate(`${errorMessage}: ${response.config.url}`); //@ts-ignore
const showErrorNotify = response?.config?.showErrorNotify;
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify);
return dataAxios; return dataAxios;
} }
} }
@ -97,7 +99,7 @@ function createService() {
default: default:
break; break;
} }
errorLog(error); errorLog(error, error?.response?.config?.showErrorNotify);
if (status === 401) { if (status === 401) {
const userStore = useUserStore(); const userStore = useUserStore();
userStore.logout(); userStore.logout();

View File

@ -48,7 +48,7 @@ export function responseError(data = {}, msg = "请求失败", code = 500) {
* @description * @description
* @param {Error} error * @param {Error} error
*/ */
export function errorLog(error: any) { export function errorLog(error: any, notify = true) {
// 打印到控制台 // 打印到控制台
console.error("errorLog", error); console.error("errorLog", error);
let message = error.message; let message = error.message;
@ -58,17 +58,22 @@ export function errorLog(error: any) {
if (message.indexOf("ssl3_get_record:wrong version number") >= 0) { if (message.indexOf("ssl3_get_record:wrong version number") >= 0) {
message = "http协议错误服务端要求http协议请检查是否使用了https请求"; message = "http协议错误服务端要求http协议请检查是否使用了https请求";
} }
// 显示提示 if (notify) {
uiContext.get().notification.error({ message }); // 显示提示
uiContext.get().notification.error({ message });
}
} }
/** /**
* @description * @description
* @param {String} msg * @param {String} msg
*/ */
export function errorCreate(msg: string) { export function errorCreate(msg: string, notify = true) {
const err = new Error(msg); const err = new Error(msg);
console.error("errorCreate", err); console.error("errorCreate", err);
uiContext.get().notification.error({ message: err.message }); if (notify) {
uiContext.get().notification.error({ message: err.message });
}
throw err; throw err;
} }

View File

@ -3,8 +3,10 @@ import { request } from "/src/api/service";
const apiPrefix = "/cname/record"; const apiPrefix = "/cname/record";
export type CnameRecord = { export type CnameRecord = {
id: number; id?: number;
status: string; status?: string;
hostRecord?: string;
recordValue?: string;
}; };
export async function GetList() { export async function GetList() {

View File

@ -7,6 +7,7 @@
<td class="host-record" :title="'域名:' + props.domain"> <td class="host-record" :title="'域名:' + props.domain">
<fs-copyable v-model="cnameRecord.hostRecord"></fs-copyable> <fs-copyable v-model="cnameRecord.hostRecord"></fs-copyable>
</td> </td>
<td style="text-align: center">CNAME</td>
<td class="record-value"> <td class="record-value">
<fs-copyable v-model="cnameRecord.recordValue"></fs-copyable> <fs-copyable v-model="cnameRecord.recordValue"></fs-copyable>
</td> </td>
@ -44,10 +45,12 @@ const props = defineProps<{
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
change: { change: [
id: number | null; {
status: string | null; id: number | null;
}; status: string | null;
}
];
}>(); }>();
const cnameRecord = ref<CnameRecord | null>(null); const cnameRecord = ref<CnameRecord | null>(null);

View File

@ -3,6 +3,7 @@
<thead> <thead>
<tr> <tr>
<td style="width: 160px">主机记录</td> <td style="width: 160px">主机记录</td>
<td style="width: 100px; text-align: center">记录类型</td>
<td style="width: 250px">请设置CNAME记录验证成功以后不要删除</td> <td style="width: 250px">请设置CNAME记录验证成功以后不要删除</td>
<td style="width: 120px" class="center">状态</td> <td style="width: 120px" class="center">状态</td>
<td style="width: 80px" class="center">操作</td> <td style="width: 80px" class="center">操作</td>

View File

@ -0,0 +1,28 @@
<template>
<a-input class="cd-input-password" :class="{ show: showRef }">
<template #suffix>
<fs-icon class="pointer" :icon="computedIcon" @click="showRef = !showRef" />
</template>
</a-input>
</template>
<script lang="ts" setup>
import { computed, ref } from "vue";
const showRef = ref(false);
const computedIcon = computed(() => {
return showRef.value ? "ion:eye-outline" : "ion:eye-off-outline";
});
</script>
<style lang="less">
.cd-input-password {
text-security: disc;
-webkit-text-security: disc;
.fs-iconify {
font-size: 16px;
}
&.show {
text-security: none;
-webkit-text-security: none;
}
}
</style>

View File

@ -43,6 +43,7 @@ const attrs = useAttrs();
const optionsRef = ref([]); const optionsRef = ref([]);
const message = ref(""); const message = ref("");
const getOptions = async () => { const getOptions = async () => {
message.value = "";
const res = await doRequest( const res = await doRequest(
{ {
type: props.type, type: props.type,
@ -53,10 +54,10 @@ const getOptions = async () => {
{ {
onError(err: any) { onError(err: any) {
message.value = `获取选项出错:${err.message}`; message.value = `获取选项出错:${err.message}`;
} },
showErrorNotify: false
} }
); );
message.value = "";
return res; return res;
}; };

View File

@ -5,6 +5,7 @@ import OutputSelector from "/@/components/plugins/common/output-selector/index.v
import DnsProviderSelector from "/@/components/plugins/cert/dns-provider-selector/index.vue"; import DnsProviderSelector from "/@/components/plugins/cert/dns-provider-selector/index.vue";
import DomainsVerifyPlanEditor from "/@/components/plugins/cert/domains-verify-plan-editor/index.vue"; import DomainsVerifyPlanEditor from "/@/components/plugins/cert/domains-verify-plan-editor/index.vue";
import AccessSelector from "/@/views/certd/access/access-selector/index.vue"; import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
import InputPassword from "./common/input-password.vue";
export * from "./cert/index.js"; export * from "./cert/index.js";
export default { export default {
install(app: any) { install(app: any) {
@ -16,5 +17,6 @@ export default {
app.component("SynologyDeviceIdGetter", SynologyIdDeviceGetter); app.component("SynologyDeviceIdGetter", SynologyIdDeviceGetter);
app.component("RemoteSelect", RemoteSelect); app.component("RemoteSelect", RemoteSelect);
app.component("CertDomainsGetter", CertDomainsGetter); app.component("CertDomainsGetter", CertDomainsGetter);
app.component("InputPassword", InputPassword);
} }
}; };

View File

@ -0,0 +1,44 @@
import { describe, expect, it } from "vitest";
import { isDomain } from "/@/plugin/validator";
describe("domain_validator", () => {
it("ok", () => {
const value = ["a.cc.com", "*.zz.com", "a.cc.com"];
const v = isDomain({}, value);
expect(v).to.be.true;
});
it("allowDotStart", () => {
let value = ["&.cc.com"];
function test() {
return isDomain({ allowDotStart: true }, value);
}
expect(test).to.throw(Error, "域名有误:&.cc.com请输入正确的域名");
value = ["a,cc.com"];
expect(test).to.throw(Error, "域名有误a,cc.com请输入正确的域名");
value = ["&cc.com"];
expect(test).to.throw(Error, "域名有误:&cc.com请输入正确的域名");
value = [".cc.com"];
expect(test()).to.be.true;
});
it("default", () => {
let value = ["&.cc.com"];
function test() {
return isDomain({ allowDotStart: false }, value);
}
expect(test).to.throw(Error, "域名有误:&.cc.com请输入正确的域名");
value = ["&cc.com"];
expect(test).to.throw(Error, "域名有误:&cc.com请输入正确的域名");
value = ["a,cc.com"];
expect(test).to.throw(Error, "域名有误a,cc.com请输入正确的域名");
value = [".cc.com"];
expect(test).to.throw(Error, "域名有误:.cc.com请输入正确的域名");
});
});

View File

@ -1,6 +1,6 @@
import Validator from "async-validator"; import Validator from "async-validator";
// 自定义验证器函数 // 自定义验证器函数
function isDomain(rule, value) { export function isDomain(rule: any, value: any) {
if (value == null) { if (value == null) {
return true; return true;
} }
@ -8,9 +8,14 @@ function isDomain(rule, value) {
if (typeof value === "string") { if (typeof value === "string") {
domains = value.split(","); domains = value.split(",");
} }
const allowDotStart = rule.allowDotStart ? "\\.|" : "";
const exp = `^(?:${allowDotStart}\\*\\.|[0-9a-zA-Z\u4e00-\u9fa5-]+\\.)+[0-9a-zA-Z\u4e00-\u9fa5-]+$`;
const compiled = new RegExp(exp);
for (const domain of domains) { for (const domain of domains) {
//域名可以是泛域名,中文域名,数字域名,英文域名,域名中可以包含-和. ,可以_开头 //域名可以是泛域名,中文域名,数字域名,英文域名,域名中可以包含-和. ,可以_开头
if (!/^(?:\*\.|[0-9a-zA-Z\u4e00-\u9fa5-]+\.)+[0-9a-zA-Z\u4e00-\u9fa5-]+$/.test(domain)) {
if (!compiled.test(domain)) {
throw new Error(`域名有误:${domain},请输入正确的域名`); throw new Error(`域名有误:${domain},请输入正确的域名`);
} }
} }

View File

@ -1,7 +1,10 @@
export const headerResource = [ export const headerResource = [
{ {
title: "文档", title: "文档",
path: "https://certd.docmirror.cn" path: "https://certd.docmirror.cn",
meta: {
icon: "ion:document-text-outline"
},
}, },
{ {
title: "源码", title: "源码",

View File

@ -170,6 +170,9 @@ h1, h2, h3, h4, h5, h6 {
color: #1890ff; color: #1890ff;
} }
.iconify{
//font-size: 16px;
}
.icon-box { .icon-box {
display: inline-flex; display: inline-flex;

View File

@ -13,7 +13,8 @@ import { useSettingStore } from "/@/store/modules/settings";
import _ from "lodash-es"; import _ from "lodash-es";
import { useModal } from "/@/use/use-modal"; import { useModal } from "/@/use/use-modal";
import CertView from "./cert-view.vue"; import CertView from "./cert-view.vue";
import { eachRunnable, eachStages } from "./utils"; import { eachStages } from "./utils";
export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();

View File

@ -20,7 +20,7 @@ import createCrudOptions from "./crud";
import { useExpose } from "@fast-crud/fast-crud"; import { useExpose } from "@fast-crud/fast-crud";
import PiCertdForm from "./certd-form/index.vue"; import PiCertdForm from "./certd-form/index.vue";
export default defineComponent({ export default defineComponent({
name: "PipelineManager", name: "PipelineManager1",
components: { PiCertdForm }, components: { PiCertdForm },
setup() { setup() {
const certdFormRef = ref(); const certdFormRef = ref();

View File

@ -8,7 +8,11 @@
<pi-status-show :status="item.node.status?.result" type="icon"></pi-status-show> <pi-status-show :status="item.node.status?.result" type="icon"></pi-status-show>
</div> </div>
</template> </template>
<pre class="pi-task-view-logs" style="overflow: auto"><template v-for="(text, index) of item.logs" :key="index">{{ text }}</template></pre> <div class="pi-task-view-logs" style="overflow: auto">
<template v-for="(logItem, index) of item.logs" :key="index">
<span :class="logItem.color"> {{ logItem.time }}</span> <span>{{ logItem.content }}</span>
</template>
</div>
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
</a-modal> </a-modal>
@ -57,7 +61,20 @@ export default {
if (currentHistory?.value?.logs != null) { if (currentHistory?.value?.logs != null) {
node.logs = computed(() => { node.logs = computed(() => {
if (currentHistory?.value?.logs && currentHistory.value?.logs[node.node.id] != null) { if (currentHistory?.value?.logs && currentHistory.value?.logs[node.node.id] != null) {
return currentHistory.value?.logs[node.node.id]; const logs = currentHistory.value?.logs[node.node.id];
const list = [];
for (let log of logs) {
const index = log.indexOf("]", 27) + 1;
const time = log.substring(0, index);
const content = log.substring(index);
const color = time.includes("ERROR") ? "red" : time.includes("WARN") ? "yellow" : "green";
list.push({
time,
content,
color
});
}
return list;
} }
return []; return [];
}); });
@ -92,6 +109,7 @@ export default {
.pi-task-view { .pi-task-view {
.tab-title { .tab-title {
display: flex; display: flex;
.tab-title-text { .tab-title-text {
display: inline-block; display: inline-block;
width: 180px; width: 180px;
@ -104,11 +122,26 @@ export default {
.pi-task-view-logs { .pi-task-view-logs {
background-color: #000c17; background-color: #000c17;
color: #fafafa; color: #e9e9e9;
font-family: monospace;
padding: 5px;
min-height: 300px; min-height: 300px;
max-height: 580px; max-height: 580px;
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
> div {
padding: 0;
margin: 0;
}
.green {
color: rgba(0, 255, 0, 0.8);
}
.yellow {
color: yellow;
}
.red {
color: red;
}
} }
} }
</style> </style>

View File

@ -101,8 +101,9 @@
<fs-icon <fs-icon
v-if="!editMode" v-if="!editMode"
class="pointer color-blue ml-2" class="pointer color-blue ml-2"
title="完全重新运行此步骤" style="font-size: 16px"
icon="SyncOutlined" title="强制重新执行此步骤"
icon="icon-park-outline:replay-music"
@click="run(item.id)" @click="run(item.id)"
></fs-icon> ></fs-icon>
</div> </div>
@ -263,7 +264,7 @@ import _ from "lodash-es";
import { message, Modal, notification } from "ant-design-vue"; import { message, Modal, notification } from "ant-design-vue";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { PipelineDetail, PipelineOptions, PluginGroups, RunHistory } from "./type"; import { PipelineDetail, PipelineOptions, PluginGroups, RunHistory } from "./type";
import type { Runnable } from "@certd/pipeline"; import type { Runnable, Stage } from "@certd/pipeline";
import PiHistoryTimelineItem from "/@/views/certd/pipeline/pipeline/component/history-timeline-item.vue"; import PiHistoryTimelineItem from "/@/views/certd/pipeline/pipeline/component/history-timeline-item.vue";
import { FsIcon } from "@fast-crud/fast-crud"; import { FsIcon } from "@fast-crud/fast-crud";
import { useSettingStore } from "/@/store/modules/settings"; import { useSettingStore } from "/@/store/modules/settings";
@ -632,6 +633,12 @@ export default defineComponent({
} }
pipeline.value.version++; pipeline.value.version++;
currentPipeline.value = pipeline.value; currentPipeline.value = pipeline.value;
//
_.remove(pipeline.value.stages, (item: Stage) => {
return item.tasks.length === 0;
});
await props.options.doSave(pipeline.value); await props.options.doSave(pipeline.value);
} }
toggleEditMode(false); toggleEditMode(false);

View File

@ -4,6 +4,7 @@ export type StatusEnumItem = {
color: string; color: string;
icon: string; icon: string;
spin?: boolean; spin?: boolean;
iconSpin?: boolean;
}; };
export type StatusEnumType = { export type StatusEnumType = {
[key: string]: StatusEnumItem; [key: string]: StatusEnumItem;
@ -34,13 +35,13 @@ const StatusEnum: StatusEnumType = {
label: "运行中", label: "运行中",
color: "blue", color: "blue",
spin: true, spin: true,
iconSpin: true,
icon: "ant-design:sync-outlined" icon: "ant-design:sync-outlined"
}, },
canceled: { canceled: {
value: "canceled", value: "canceled",
label: "已取消", label: "已取消",
color: "yellow", color: "yellow",
spin: true,
icon: "ant-design:minus-circle-twotone" icon: "ant-design:minus-circle-twotone"
}, },
none: { none: {

View File

@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.11](https://github.com/certd/certd/compare/v1.26.10...v1.26.11) (2024-10-23)
### Bug Fixes
* 允许七牛云cdn插件输入.号开头的通配符域名 ([18ee87d](https://github.com/certd/certd/commit/18ee87daff6eafc2201b58e28d85aafd3cb7a5b9))
### Performance Improvements
* 优化证书申请速度和成功率反代地址优化google基本可以稳定请求。增加请求重试。 ([41d9c3a](https://github.com/certd/certd/commit/41d9c3ac8398def541e65351cbe920d4a927182d))
## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20) ## [1.26.10](https://github.com/certd/certd/compare/v1.26.9...v1.26.10) (2024-10-20)
### Bug Fixes ### Bug Fixes

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/ui-server", "name": "@certd/ui-server",
"version": "1.26.10", "version": "1.26.11",
"description": "fast-server base midway", "description": "fast-server base midway",
"private": true, "private": true,
"type": "module", "type": "module",
@ -27,17 +27,17 @@
}, },
"dependencies": { "dependencies": {
"@alicloud/pop-core": "^1.7.10", "@alicloud/pop-core": "^1.7.10",
"@certd/acme-client": "^1.26.10", "@certd/acme-client": "^1.26.11",
"@certd/commercial-core": "^1.26.10", "@certd/commercial-core": "^1.26.11",
"@certd/lib-huawei": "^1.26.10", "@certd/lib-huawei": "^1.26.11",
"@certd/lib-jdcloud": "^1.26.10", "@certd/lib-jdcloud": "^1.26.11",
"@certd/lib-k8s": "^1.26.10", "@certd/lib-k8s": "^1.26.11",
"@certd/lib-server": "^1.26.10", "@certd/lib-server": "^1.26.11",
"@certd/midway-flyway-js": "^1.26.10", "@certd/midway-flyway-js": "^1.26.11",
"@certd/pipeline": "^1.26.10", "@certd/pipeline": "^1.26.11",
"@certd/plugin-cert": "^1.26.10", "@certd/plugin-cert": "^1.26.11",
"@certd/plugin-plus": "^1.26.10", "@certd/plugin-plus": "^1.26.11",
"@certd/plus-core": "^1.26.10", "@certd/plus-core": "^1.26.11",
"@koa/cors": "^5.0.0", "@koa/cors": "^5.0.0",
"@midwayjs/bootstrap": "~3.17.1", "@midwayjs/bootstrap": "~3.17.1",
"@midwayjs/cache": "~3.14.0", "@midwayjs/cache": "~3.14.0",

View File

@ -37,8 +37,12 @@ export class CloudflareDnsProvider extends AbstractDnsProvider<CloudflareRecord>
} }
async getZoneId(domain: string) { async getZoneId(domain: string) {
this.logger.info('获取zoneId:', domain);
const url = `https://api.cloudflare.com/client/v4/zones?name=${domain}`; const url = `https://api.cloudflare.com/client/v4/zones?name=${domain}`;
const res = await this.doRequestApi(url, null, 'get'); const res = await this.doRequestApi(url, null, 'get');
if (res.result.length === 0) {
throw new Error(`未找到域名${domain}的zoneId`);
}
return res.result[0].id; return res.result[0].id;
} }

View File

@ -7,7 +7,7 @@ import { CertInfo } from '@certd/plugin-cert';
title: '部署证书至七牛CDN', title: '部署证书至七牛CDN',
icon: 'svg:icon-qiniuyun', icon: 'svg:icon-qiniuyun',
group: pluginGroups.cdn.key, group: pluginGroups.cdn.key,
desc: '自动部署域名证书至七牛云CDN七牛云OSS', desc: '自动部署域名证书至七牛云CDN',
default: { default: {
strategy: { strategy: {
runStrategy: RunStrategy.SkipWhenSucceed, runStrategy: RunStrategy.SkipWhenSucceed,
@ -25,7 +25,7 @@ export class QiniuDeployCertToCDN extends AbstractTaskPlugin {
open: false, open: false,
tokenSeparators: [',', ' ', '', '、', '|'], tokenSeparators: [',', ' ', '', '、', '|'],
}, },
rules: [{ type: 'domains' }], rules: [{ type: 'domains', allowDotStart: true }],
required: true, required: true,
}) })
domainName!: string | string[]; domainName!: string | string[];