Compare commits

..

34 Commits

Author SHA1 Message Date
xiaojunnuo e913fe509c chore: 优化代码格式 2026-06-14 20:59:38 +08:00
xiaojunnuo a3a215b7ae perf(plugin): 增加 Dynadot DNS and access 插件
新增Dynadot域名解析提供商插件,包含API授权配置和DNS记录增删查功能
2026-06-14 17:23:24 +08:00
xiaojunnuo 56f2949ac5 chore: 1 2026-06-14 15:07:38 +08:00
xiaojunnuo c1b5a35f90 fix: 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug 2026-06-14 14:57:47 +08:00
xiaojunnuo 48ab1fbffe build: release 2026-06-12 00:17:31 +08:00
xiaojunnuo 5f078273b3 build: publish 2026-06-11 23:57:37 +08:00
xiaojunnuo 636338f9ed build: trigger build image 2026-06-11 23:57:24 +08:00
xiaojunnuo 6cbd629777 v1.41.3 2026-06-11 23:56:12 +08:00
xiaojunnuo 5a07dce759 build: prepare to build 2026-06-11 23:53:30 +08:00
xiaojunnuo 15484bc119 perf: 首页夜间模式主图切换为黑色背景 2026-06-11 23:51:45 +08:00
xiaojunnuo d6cd9d136d fix: 修复litessl无法申请证书,报authorization must be pending 错误的问题 2026-06-11 23:40:44 +08:00
xiaojunnuo c76815756b chore: 1 2026-06-11 01:17:48 +08:00
xiaojunnuo eef93250ac build: release 2026-06-11 00:25:37 +08:00
xiaojunnuo a1c6cf0477 build: release 2026-06-11 00:20:14 +08:00
xiaojunnuo 14a0ccac93 chore: popularize agents.md 2026-06-11 00:07:02 +08:00
xiaojunnuo 9439743b7e build: publish 2026-06-10 23:39:06 +08:00
xiaojunnuo acbac6a9c3 build: trigger build image 2026-06-10 23:38:54 +08:00
xiaojunnuo cc38ccd0e9 v1.41.2 2026-06-10 23:37:43 +08:00
xiaojunnuo 7d0cf846ac build: prepare to build 2026-06-10 23:34:12 +08:00
xiaojunnuo f9541fab70 perf: 新增站点证书监控从DNS解析记录批量导入功能
本次提交新增了从DNS解析记录批量导入站点监控的完整功能:
1. 扩展Registrable类型新增icon字段支持
2. 新增DNS解析记录获取接口和基础实现
3. 为阿里云、腾讯云、Cloudflare等DNS提供商添加解析记录分页获取支持
4. 新增站点监控导入任务管理功能,支持保存、启动、删除导入任务
5. 新增中文/英文多语言支持
6. 优化暗黑模式表格样式
7. 修复ACME账户访问修复逻辑中项目ID可选的问题
8. 优化HiPM DNS提供商的域名获取逻辑
2026-06-10 23:32:39 +08:00
xiaojunnuo 8d9870e9c6 Merge branch 'v2' into v2-dev 2026-06-09 23:10:45 +08:00
HINS 0f3f8519e0 perf: 优化 HiPM DNSMgr 插件,添加域名查询双层策略 (#744) @WUHINS
- 新增 getDomainId() 方法,首选 keyword 直接查询(O(1))
- 列表匹配作为降级方案(向后兼容)
- 性能提升 99%,减少 99% 数据传输
- 与 ddns-go hipmdnsmgr.go 实现保持一致
2026-06-09 23:10:15 +08:00
xiaojunnuo 016ae865b1 fix(cert-plugin): 修复DNS提供商授权无法回显的bug 2026-06-09 23:08:45 +08:00
xiaojunnuo 3e9953a74a chore: 1 2026-06-08 16:54:33 +08:00
xiaojunnuo 1fc80d2b93 chore: 1 2026-06-08 16:52:36 +08:00
xiaojunnuo b55fe2ef19 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-06-08 16:48:39 +08:00
xiaojunnuo 71030b7e27 chore: handsfree.work 域名 修改 2026-06-08 16:48:30 +08:00
greper 5e8bdac008 Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749)
This reverts commit 56b8c689ec.
2026-06-08 11:43:10 +08:00
Steven Zhu 56b8c689ec perf: 添加AWS Rate Limit应对措施 (#748)
* Parse PEM chain and import certificate chain

Split the PEM in certInfo.crt into a leaf certificate and intermediate chain (using a lookbehind regex), trim the blocks, and pass the chain to ImportCertificateCommand only when present. Replace console.log with this.logger.info and log the returned CertificateArn. This ensures the leaf cert is uploaded separately from its chain and avoids sending an empty CertificateChain.

* Add AWS retry & CloudFront deployment wait

Introduce robust retry and polling helpers to handle AWS throttling and CloudFront propagation. Added AwsClient.withRetry (exponential backoff, handles common throttling errors, default 5 attempts/base 2s) and waitForDistributionDeployed (polls until distribution Status is "Deployed", default 10min timeout/15s interval). Update deploy-to-cloudfront plugin to use withRetry for Get/UpdateDistribution and importCertificate, pass AwsClient into uploadToACM, and wait for each distribution to finish deploying before continuing to avoid PreconditionFailed errors. Improves reliability when facing rate limits and global CloudFront propagation delays; adds informative logging for retry and deployment status.
2026-06-08 10:29:11 +08:00
Steven Zhu 454912d314 fix: Parse PEM chain and import certificate chain (#747)
Split the PEM in certInfo.crt into a leaf certificate and intermediate chain (using a lookbehind regex), trim the blocks, and pass the chain to ImportCertificateCommand only when present. Replace console.log with this.logger.info and log the returned CertificateArn. This ensures the leaf cert is uploaded separately from its chain and avoids sending an empty CertificateChain.
2026-06-08 10:28:39 +08:00
xiaojunnuo 61e3f5761c build: release 2026-06-06 03:06:48 +08:00
xiaojunnuo 2908569841 chore: 1 2026-06-06 03:02:47 +08:00
xiaojunnuo 775226b49f build: publish 2026-06-06 02:38:20 +08:00
xiaojunnuo e3dacb5b3f build: trigger build image 2026-06-06 02:38:08 +08:00
119 changed files with 1722 additions and 328 deletions
+3 -2
View File
@@ -37,5 +37,6 @@ pnpm-lock.yaml
.studio/
# Certd 推广报告,仅本地使用
/popularize/
/popularize/reports/
output/
.uploads/
-13
View File
@@ -1,13 +0,0 @@
你是一名资深nodejs工程师,擅长开发Certd开源系统的任务插件。
certd是一款全自动证书申请部署管理工具,基于流水线的方式,通过里面申请证书插件申请证书,然后将证书传递给下一个部署任务插件,不同的部署任务插件将证书部署到用户的各个应用系统当中。
certd插件分成以下几种类型:
Access:存储用户的第三放应用的授权数据,比如用户名密码,accessSecret 或 accessToken等。同时它里面的方法还负责对接第三方的api接口
Task 部署任务插件,它继承AbstractTaskPlugin类,被流水线调用execute方法,将证书部署到对应的应用上
DnsProvider: DNS提供商插件,它用于在ACME申请证书时给域名添加txt解析记录。
注意事项:
1、使用技能:在开始工作前,请阅读并加载.trae/skills下面的技能,根据skills进行相应的插件开发
2、迭代技能:当开发过程用户提醒你更好的做法时,你需要总结经验,更新相应的skills,让skills越来越完善,能够在以后得新插件开发中具备指导意义。
3、一般调用的api接口文档会比较复杂,你不知道接口是什么时,请务必询问用户,让用户提供API接口文档
4、完成开发后无需测试,通知用户自己去测试
+27
View File
@@ -3,6 +3,33 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
### Bug Fixes
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/certd/certd/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
### Performance Improvements
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Bug Fixes
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
* Parse PEM chain and import certificate chain ([#747](https://github.com/certd/certd/issues/747)) ([454912d](https://github.com/certd/certd/commit/454912d31407d350cbd170953ccbd0564e74fd6c))
### Performance Improvements
* 添加AWS Rate Limit应对措施 ([#748](https://github.com/certd/certd/issues/748)) ([56b8c68](https://github.com/certd/certd/commit/56b8c689ec2b5cff49010a8c765483dd36803e9d))
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
* 优化 HiPM DNSMgr 插件,添加域名查询双层策略 ([#744](https://github.com/certd/certd/issues/744)) @WUHINS ([0f3f851](https://github.com/certd/certd/commit/0f3f8519e04d95cb848e28b98a3d4fcbed481fce))
### Reverts
* Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749) ([5e8bdac](https://github.com/certd/certd/commit/5e8bdac00850bed4f5f2a272bee42c490730ec21)), closes [#748](https://github.com/certd/certd/issues/748) [#749](https://github.com/certd/certd/issues/749)
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements
+4 -1
View File
@@ -179,7 +179,10 @@ https://certd.handfree.work/
2. 您的需求我们将优先实现,并且可能将作为专业版功能提供
3. 获得专业版功能
[50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product)
> [50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product) https://app.handfree.work/subject/#/app/certd/product
> handfree.work是Certd官方激活码购买平台
专业版、商业版特权对比
+37 -1
View File
@@ -1,9 +1,45 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
### Bug Fixes
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/certd/certd/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
### Performance Improvements
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Bug Fixes
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
* Parse PEM chain and import certificate chain ([#747](https://github.com/certd/certd/issues/747)) ([454912d](https://github.com/certd/certd/commit/454912d31407d350cbd170953ccbd0564e74fd6c))
### Performance Improvements
* 添加AWS Rate Limit应对措施 ([#748](https://github.com/certd/certd/issues/748)) ([56b8c68](https://github.com/certd/certd/commit/56b8c689ec2b5cff49010a8c765483dd36803e9d))
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
* 优化 HiPM DNSMgr 插件,添加域名查询双层策略 ([#744](https://github.com/certd/certd/issues/744)) @WUHINS ([0f3f851](https://github.com/certd/certd/commit/0f3f8519e04d95cb848e28b98a3d4fcbed481fce))
### Reverts
* Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749) ([5e8bdac](https://github.com/certd/certd/commit/5e8bdac00850bed4f5f2a272bee42c490730ec21)), closes [#748](https://github.com/certd/certd/issues/748) [#749](https://github.com/certd/certd/issues/749)
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
* 优化列表页面请求两次的问题 ([5546af5](https://github.com/certd/certd/commit/5546af518e92c765513787ccaf8e856be789bcf9))
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
* **volcengine-vke:** 火山VKE集群证书支持两种类型的证书保密字典 ([77b8024](https://github.com/certd/certd/commit/77b802445322d576d54d194f7c505da49e0e824c))
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
### Bug Fixes
View File
+1 -1
View File
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.41.1"
"version": "1.41.3"
}
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/publishlab/node-acme-client/compare/v1.41.2...v1.41.3) (2026-06-11)
### Bug Fixes
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/publishlab/node-acme-client/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
## [1.41.2](https://github.com/publishlab/node-acme-client/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/acme-client
## [1.41.1](https://github.com/publishlab/node-acme-client/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/acme-client
+3 -3
View File
@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.41.1",
"version": "1.41.3",
"type": "module",
"module": "./dist/index.js",
"main": "./dist/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.41.1",
"@certd/basic": "^1.41.3",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.9.0",
@@ -76,5 +76,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
@@ -0,0 +1,69 @@
import assert from "node:assert/strict";
import auto from "./auto.js";
import { createCsr, createPrivateRsaKey } from "./crypto/index.js";
declare const describe: any;
declare const it: any;
describe("auto challenge status polling", () => {
it("polls the authorization URL after completing a challenge", async () => {
const [, csr] = await createCsr({ commonName: "example.com" }, await createPrivateRsaKey());
const challenge = {
type: "dns-01",
url: "https://ca.example/chall/1",
token: "token",
};
const authz = {
status: "pending",
identifier: { type: "dns", value: "example.com" },
url: "https://ca.example/authz/1",
challenges: [challenge],
};
const order = {
status: "pending",
url: "https://ca.example/order/1",
finalize: "https://ca.example/order/1/finalize",
authorizations: [authz.url],
};
const polledUrls: string[] = [];
const originalSetTimeout = globalThis.setTimeout;
(globalThis as any).setTimeout = (fn: (...args: any[]) => void) => originalSetTimeout(fn, 0);
try {
const certificate = await auto(
{
logger: { info: () => {} },
sslProvider: "litessl",
getAccountUrl: () => "https://ca.example/acct/1",
createOrder: async () => order,
getAuthorizations: async () => [authz],
getChallengeKeyAuthorization: async () => "key-authorization",
verifyChallenge: async () => {},
completeChallenge: async () => ({ ...challenge, status: "processing" }),
waitForValidStatus: async (item: { url: string }) => {
polledUrls.push(item.url);
return { ...item, status: "valid" };
},
finalizeOrder: async () => ({ ...order, status: "valid", certificate: "https://ca.example/cert/1" }),
getCertificate: async () => "CERTIFICATE",
} as any,
{
csr,
termsOfServiceAgreed: true,
waitDnsDiffuseTime: 0,
challengeCreateFn: async (_authz: any, keyAuthorizationGetter: (challenge: any) => Promise<string>) => ({
challenge,
keyAuthorization: await keyAuthorizationGetter(challenge),
}),
challengeRemoveFn: async () => {},
}
);
assert.equal(certificate, "CERTIFICATE");
assert.deepEqual(polledUrls, [authz.url]);
} finally {
(globalThis as any).setTimeout = originalSetTimeout;
}
});
});
+1 -1
View File
@@ -172,7 +172,7 @@ export default async (client, userOpts) => {
}
challengeCompleted = true;
log(`[auto] [${d}] 等待返回valid状态`);
await client.waitForValidStatus(challenge,d);
await client.waitForValidStatus(authz,d);
});
+1 -1
View File
@@ -263,4 +263,4 @@ export function createChallengeFn(opts = {}) {
// createChallengeFn({logger:{info:console.log}}).walkDnsChallengeRecord("handsfree.work")
// createChallengeFn({logger:{info:console.log}}).walkDnsChallengeRecord("handfree.work")
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/basic
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Performance Improvements
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements
+1 -1
View File
@@ -1 +1 @@
02:33
23:53
+4 -2
View File
@@ -1,7 +1,9 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.41.1",
"version": "1.41.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -52,5 +54,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/pipeline
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Performance Improvements
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/pipeline
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.41.1",
"version": "1.41.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -19,8 +19,8 @@
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.41.1",
"@certd/plus-core": "^1.41.1",
"@certd/basic": "^1.41.3",
"@certd/plus-core": "^1.41.3",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13"
@@ -49,5 +49,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
@@ -7,6 +7,7 @@ export type Registrable = {
group?: string;
deprecated?: string;
order?: number;
icon?: string;
};
export type TargetGetter<T> = () => Promise<T>;
export type RegistryItem<T> = {
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/lib-huawei
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/lib-huawei
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/lib-huawei
+4 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.41.1",
"version": "1.41.3",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -12,7 +12,8 @@
"dev-build": "npm run build",
"preview": "vite preview",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish"
"pub": "npm publish",
"compile": "npm run build"
},
"dependencies": {
"axios": "^1.9.0",
@@ -27,5 +28,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/lib-iframe
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/lib-iframe
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/lib-iframe
+4 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.41.1",
"version": "1.41.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -15,7 +15,8 @@
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish"
"pub": "npm publish",
"compile": "npm run build"
},
"dependencies": {
"nanoid": "^4.0.0"
@@ -34,5 +35,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/jdcloud
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/jdcloud
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/jdcloud
+7 -3
View File
@@ -1,16 +1,20 @@
{
"name": "@certd/jdcloud",
"version": "1.41.1",
"version": "1.41.3",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
"scripts": {
"before-build": "node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tsconfig.tsbuildinfo',{force:true});fs.rmSync('.rollup.cache',{recursive:true,force:true});\"",
"build": "npm run before-build && rollup -c ",
"dev-build": "npm run build",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish"
"pub": "npm publish",
"compile": "npm run build"
},
"author": "",
"license": "Apache",
@@ -59,5 +63,5 @@
"fetch"
]
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/lib-k8s
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/lib-k8s
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/lib-k8s
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.41.1",
"version": "1.41.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -19,7 +19,7 @@
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.41.1",
"@certd/basic": "^1.41.3",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -36,5 +36,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/lib-server
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/lib-server
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.41.1",
"version": "1.41.3",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -29,11 +29,11 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.41.1",
"@certd/basic": "^1.41.1",
"@certd/pipeline": "^1.41.1",
"@certd/plugin-lib": "^1.41.1",
"@certd/plus-core": "^1.41.1",
"@certd/acme-client": "^1.41.3",
"@certd/basic": "^1.41.3",
"@certd/pipeline": "^1.41.3",
"@certd/plugin-lib": "^1.41.3",
"@certd/plus-core": "^1.41.3",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
@@ -69,5 +69,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/midway-flyway-js
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.41.1",
"version": "1.41.3",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -16,7 +16,8 @@
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"cov": "midway-bin cov --ts",
"prepublish": "npm run build",
"pub": "npm publish"
"pub": "npm publish",
"compile": "npm run build"
},
"keywords": [],
"author": "greper",
@@ -49,5 +50,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/plugin-cert
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/plugin-cert
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/plugin-cert
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.41.1",
"version": "1.41.3",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -18,10 +18,10 @@
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/acme-client": "^1.41.1",
"@certd/basic": "^1.41.1",
"@certd/pipeline": "^1.41.1",
"@certd/plugin-lib": "^1.41.1",
"@certd/acme-client": "^1.41.3",
"@certd/basic": "^1.41.3",
"@certd/pipeline": "^1.41.3",
"@certd/plugin-lib": "^1.41.3",
"psl": "^1.9.0",
"punycode.js": "^2.3.1"
},
@@ -41,5 +41,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/plugin-lib
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Performance Improvements
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/plugin-lib
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.41.1",
"version": "1.41.3",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -23,10 +23,10 @@
"@alicloud/pop-core": "^1.7.10",
"@alicloud/tea-util": "^1.4.11",
"@aws-sdk/client-s3": "^3.964.0",
"@certd/acme-client": "^1.41.1",
"@certd/basic": "^1.41.1",
"@certd/pipeline": "^1.41.1",
"@certd/plus-core": "^1.41.1",
"@certd/acme-client": "^1.41.3",
"@certd/basic": "^1.41.3",
"@certd/pipeline": "^1.41.3",
"@certd/plus-core": "^1.41.3",
"@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.22.0",
"basic-ftp": "^5.0.5",
@@ -61,5 +61,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
"gitHead": "6cbd62977731a3b72c42b5f88c49500631da0a46"
}
@@ -34,6 +34,14 @@ export type DomainRecord = {
domain: string;
};
export type DnsResolveRecord = {
id: string;
hostRecord: string;
fullRecord: string;
type: string;
value: string;
};
export interface IDnsProvider<T = any> {
onInstance(): Promise<void>;
@@ -59,6 +67,8 @@ export interface IDnsProvider<T = any> {
usePunyCode(): boolean;
getDomainListPage(pager: PageSearch): Promise<PageRes<DomainRecord>>;
getRecordListPage?(domain: string, pager: PageSearch): Promise<PageRes<DnsResolveRecord>>;
}
export interface ISubDomainsGetter {
@@ -1,7 +1,7 @@
import { HttpClient, ILogger } from "@certd/basic";
import { IAccessService, PageRes, PageSearch } from "@certd/pipeline";
import punycode from "punycode.js";
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js";
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DnsResolveRecord, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js";
import { dnsProviderRegistry } from "./registry.js";
export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
ctx!: DnsProviderContext;
@@ -49,6 +49,10 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
throw new Error("Method not implemented.");
}
async getRecordListPage(domain: string, req: PageSearch): Promise<PageRes<DnsResolveRecord>> {
throw new Error("Method not implemented.");
}
}
export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> {
+2 -2
View File
@@ -4,8 +4,8 @@ VITE_APP_PM_ENABLED=true
VITE_APP_TITLE=Certd
VITE_APP_SLOGAN=让你的证书永不过期
VITE_APP_COPYRIGHT_YEAR=2021-2026
VITE_APP_COPYRIGHT_NAME=handsfree.work
VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work
VITE_APP_COPYRIGHT_NAME=handfree.work
VITE_APP_COPYRIGHT_URL=https://certd.handfree.work
VITE_APP_LOGO=static/images/logo/logo.svg
VITE_APP_LOGIN_LOGO=static/images/logo/rect-black.svg
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
### Performance Improvements
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Bug Fixes
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
### Performance Improvements
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.41.1",
"version": "1.41.3",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -106,8 +106,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.41.1",
"@certd/pipeline": "^1.41.1",
"@certd/lib-iframe": "^1.41.3",
"@certd/pipeline": "^1.41.3",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

@@ -14,7 +14,7 @@ export default {
default: undefined,
},
},
emits: ["update:modelValue", "selected-change"],
emits: ["update:modelValue", "selected-change", "change"],
setup(props: any, ctx: any) {
const options = ref<any[]>([]);
@@ -33,7 +33,8 @@ export default {
// if (props.modelValue == null && options.value.length > 0) {
// ctx.emit("update:modelValue", options.value[0].value);
// }
onSelectedChange(props.modelValue);
//selected-changeoption
onSelectedChange(props.modelValue, true);
}
onCreate();
@@ -41,9 +42,12 @@ export default {
ctx.emit("update:modelValue", value);
onSelectedChange(value);
}
function onSelectedChange(value: any) {
function onSelectedChange(value: any, isFirst: boolean = false) {
if (value) {
const option = options.value.find(item => item.value == value);
if (!isFirst) {
ctx.emit("change", value);
}
if (option) {
ctx.emit("selected-change", option);
return;
@@ -18,6 +18,7 @@ export default {
subdomainConfirmTitle: "Subdomain Confirmation",
subdomainConfirmContent: "{domain} appears to be a subdomain. Only delegated subdomains and free second-level subdomains need to be maintained here. Otherwise certificate application may fail. Continue?",
importFromProvider: "Import from Domain Provider",
importFromResolveRecords: "Import from DNS Records",
syncExpirationDate: "Sync Domain Expiration Time",
syncTaskSubmitted: "Sync task submitted",
syncExpirationProgress: "Sync Domain Expiration Progress",
@@ -4,7 +4,7 @@ export default {
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
cnameLinkText: "CNAME principle and usage instructions",
cnameDomain: "CNAME Domain",
cnameDomainPlaceholder: "cname.handsfree.work",
cnameDomainPlaceholder: "cname.handfree.work",
cnameDomainHelper:
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
cnameDomainPattern: "Domain name cannot contain *",
@@ -18,6 +18,7 @@ export default {
subdomainConfirmTitle: "子域名确认",
subdomainConfirmContent: "检测到{domain}为子域名,只有托管子域名和免费二级子域名才需要在此处维护,否则会导致申请证书失败,请确认是否继续?",
importFromProvider: "从域名提供商导入",
importFromResolveRecords: "从解析记录导入",
syncExpirationDate: "同步域名过期时间",
syncTaskSubmitted: "同步任务已提交",
syncExpirationProgress: "同步域名过期时间进度",
@@ -3,7 +3,7 @@ export default {
cnameDescription: "此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。",
cnameLinkText: "CNAME功能原理及使用说明",
cnameDomain: "CNAME域名",
cnameDomainPlaceholder: "cname.handsfree.work",
cnameDomainPlaceholder: "cname.handfree.work",
cnameDomainHelper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名",
cnameDomainPattern: "域名不能使用星号",
cnameProviderSubdomain: "托管子域名",
@@ -8,4 +8,45 @@
.vben-normal-menu__item.is-active {
background-color: #3b3b3b !important;
}
.cd-table {
th,
td {
border-bottom: 1px solid #303030;
border-left: 1px solid #303030;
}
th {
background: #1f1f1f;
color: rgba(255, 255, 255, 0.85);
border-top: 1px solid #303030;
&:last-child {
border-right: 1px solid #303030;
}
}
td {
&:last-child {
border-right: 1px solid #303030;
}
}
td.position-sticky-right {
background-color: #141414;
}
.position-sticky-right::before {
background: #303030;
}
tr.hover-color:hover td {
background: #262626;
}
.status-active {
background: #1f3a23;
color: #81c784;
}
}
}
@@ -109,7 +109,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
},
rowHandle: {
fixed: "right",
width: 120,
width: 200,
buttons: {
edit: {
click: ({ row }) => openForm(row),
@@ -11,9 +11,9 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({
name: "DnsPersistRecord",
@@ -24,10 +24,8 @@ const context: any = {
};
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
//
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -19,13 +19,14 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n();
@@ -61,10 +62,7 @@ const handleBatchDelete = () => {
};
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -21,13 +21,14 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n();
@@ -61,10 +62,7 @@ const handleBatchDelete = () => {
};
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -149,7 +149,7 @@
</template>
<script lang="ts" setup>
import { computed, nextTick, onActivated, onMounted, reactive, ref } from "vue";
import { computed, nextTick, reactive, ref } from "vue";
import { FsIcon, useFs } from "@fast-crud/fast-crud";
import { notification } from "ant-design-vue";
import { useRouter } from "vue-router";
@@ -158,6 +158,7 @@ import createInviteesCrudOptions from "./crud-invitees";
import createLogsCrudOptions from "./crud-logs";
import { useSettingStore } from "/@/store/settings";
import { util } from "/@/utils";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ name: "InviteCommission" });
@@ -314,16 +315,10 @@ async function refreshInvitePage(autoOpenAgreement = true) {
await refreshActiveList();
}
onMounted(async () => {
//
useMounted(async () => {
await refreshInvitePage(true);
});
onActivated(async () => {
if (!loaded.value) {
return;
}
await refreshInvitePage();
});
</script>
<style lang="less">
@@ -72,6 +72,36 @@ export const siteInfoApi = {
});
},
async ImportTaskSave(body: any) {
return await request({
url: apiPrefix + "/import/save",
method: "post",
data: body,
});
},
async ImportTaskStatus() {
return await request({
url: apiPrefix + "/import/status",
method: "post",
});
},
async ImportTaskDelete(key: string) {
return await request({
url: apiPrefix + "/import/delete",
method: "post",
data: { key },
});
},
async ImportTaskStart(key: string) {
return await request({
url: apiPrefix + "/import/start",
method: "post",
data: { key },
});
},
async DisabledChange(id: number, disabled: boolean) {
return await request({
url: apiPrefix + "/disabledChange",
@@ -9,7 +9,7 @@ import { useSettingStore } from "/@/store/settings";
import { mySuiteApi } from "/@/views/certd/suite/mine/api";
import { mitter } from "/@/utils/util.mitt";
import { useSiteIpMonitor } from "./ip/use";
import { useSiteImport } from "/@/views/certd/monitor/site/use";
import { useSiteImport, useSiteImportTaskManage } from "/@/views/certd/monitor/site/use";
import { ref } from "vue";
import GroupSelector from "../../basic/group/group-selector.vue";
import { createGroupDictRef } from "../../basic/group/api";
@@ -53,6 +53,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const { openSiteIpMonitorDialog } = useSiteIpMonitor();
const { openSiteImportDialog } = useSiteImport();
const openSiteImportTaskManageDialog = useSiteImportTaskManage();
const certValidDaysRef = ref(10);
@@ -200,6 +201,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
actionbar: {
buttons: {
add: {
icon: "ion:add-circle-outline",
async click() {
if (!settingsStore.isPlus) {
// 非plus
@@ -236,6 +238,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: hasActionPermission("write"),
text: t("monitor.bulkImport"),
type: "primary",
icon: "ion:cloud-upload-outline",
async click() {
const defaultGroupId = getDefaultGroupId();
openSiteImportDialog({
@@ -246,10 +249,27 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
});
},
},
importFromProvider: {
show: hasActionPermission("write"),
title: t("certd.domain.importFromResolveRecords"),
text: t("certd.domain.importFromResolveRecords"),
type: "primary",
needPlus: true,
color: "gold",
icon: "mingcute:vip-1-line",
click: async () => {
await openSiteImportTaskManageDialog({
afterSubmit: () => {
crudExpose.doRefresh();
},
});
},
},
checkAll: {
show: true,
text: t("monitor.checkAll"),
type: "primary",
icon: "ion:play-circle-outline",
click() {
checkAll();
},
@@ -0,0 +1,139 @@
<template>
<div class="site-info-import-task-status min-h-[300px]">
<div class="action mb-5">
<fs-button type="primary" icon="mingcute:vip-1-line" @click="addTask">{{ t("certd.domain.addImportTask") }}</fs-button>
<fs-button type="primary" icon="ion:refresh-outline" class="ml-2" @click="loadImportTaskStatus">{{ t("certd.domain.refresh") }}</fs-button>
</div>
<div class="table-container overflow-auto mb-10">
<table class="cd-table border-gray-300 w-full">
<thead>
<tr>
<th class="w-[220px]">{{ t("certd.sourcee") }}</th>
<th class="">{{ t("certd.domain.progress") }}</th>
<th class="w-[220px]">{{ t("certd.domain.operation") }}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.key">
<td class="ellipsis">
<span class="flex items-center pointer" @click="editTask(item)">
<span class="flex-1 ellipsis flex items-center">
<fs-icon :icon="item.icon" class="mr-2"></fs-icon>
{{ item.title }}
</span>
<fs-icon icon="ant-design:edit-outlined" class="ml-2" />
</span>
</td>
<td>
<div v-if="item.task">
<div>
<a-tag color="blue">{{ t("certd.domain.total") }}{{ item.task?.total }}</a-tag>
<a-tag color="success" class="ml-2">{{ t("certd.success") }}{{ item.task?.successCount }}</a-tag>
<a-tag type="info" class="ml-2">{{ t("certd.domain.skipped") }}{{ item.task?.skipCount }}</a-tag>
<a-tooltip v-if="item.task?.errors.length > 0">
<template #title>
<div v-for="error in item.task?.errors" :key="error">{{ error }}</div>
</template>
<a-tag color="red" class="ml-2">{{ t("certd.domain.failed") }}{{ item.task?.errors.length }}</a-tag>
</a-tooltip>
</div>
<a-progress :percent="item.task?.progress" size="small" status="active" />
</div>
<div v-else>{{ t("certd.domain.notExecuted") }}</div>
</td>
<td>
<fs-button type="primary" icon="ion:play-outline" :disabled="item.task?.status === 'running'" @click="startTask(item)">{{ t("certd.domain.execute") }}</fs-button>
<fs-button type="primary" class="ml-2" danger icon="ion:trash-outline" @click="deleteTask(item)">{{ t("certd.domain.delete") }}</fs-button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import { Modal } from "ant-design-vue";
import { onMounted, onUnmounted, ref } from "vue";
import * as api from "./api";
import { useSiteImportTask } from "./use";
import { useSettingStore } from "/@/store/settings";
import { useI18n } from "/@/locales";
defineOptions({
name: "SiteInfoImportTaskStatus",
});
const list = ref([]);
const { t } = useI18n();
async function loadImportTaskStatus() {
const res = await api.siteInfoApi.ImportTaskStatus();
list.value = res || [];
}
async function startTask(item: any) {
settingStore.checkPlus();
await api.siteInfoApi.ImportTaskStart(item.key);
await loadImportTaskStatus();
}
async function deleteTask(item: any) {
Modal.confirm({
title: t("certd.domain.confirmDelete"),
okText: t("common.confirm"),
okType: "danger",
onOk: async () => {
await api.siteInfoApi.ImportTaskDelete(item.key);
await loadImportTaskStatus();
},
});
}
const openSiteImportTaskDialog = useSiteImportTask();
const settingStore = useSettingStore();
async function addTask() {
settingStore.checkPlus();
await openSiteImportTaskDialog({
afterSubmit: async (res?: any) => {
if (res) {
await api.siteInfoApi.ImportTaskStart(res.key);
}
await loadImportTaskStatus();
},
});
}
async function editTask(item: any) {
settingStore.checkPlus();
await openSiteImportTaskDialog({
afterSubmit: async () => {
await loadImportTaskStatus();
},
form: item,
});
}
const checkIntervalRef = ref();
onMounted(async () => {
await loadImportTaskStatus();
checkIntervalRef.value = setInterval(async () => {
await loadImportTaskStatus();
}, 3000);
});
onUnmounted(() => {
clearInterval(checkIntervalRef.value);
});
</script>
<style lang="less">
.site-info-import-task-status {
.table-container {
height: 50vh;
}
.ant-progress {
margin-bottom: 0px;
}
}
</style>
@@ -1,7 +1,11 @@
import { useFormWrapper } from "@fast-crud/fast-crud";
import { useFormWrapper, compute } from "@fast-crud/fast-crud";
import { siteInfoApi } from "./api";
import { useI18n } from "/src/locales";
import { useI18n } from "/@/locales";
import { useSettingStore } from "/@/store/settings";
import { useFormDialog } from "/@/use/use-dialog";
import GroupSelector from "../../basic/group/group-selector.vue";
import SiteInfoImportTaskStatus from "./import.vue";
export function useSiteImport() {
const { t } = useI18n();
const { openCrudFormDialog } = useFormWrapper();
@@ -13,7 +17,7 @@ export function useSiteImport() {
columns: {
text: {
type: "textarea",
title: t("certd.domainList.title"), // 域名列表
title: t("certd.domainList.title"),
form: {
helper: t("certd.domainList.helper"),
rules: [{ required: true, message: t("certd.domainList.required") }],
@@ -21,9 +25,7 @@ export function useSiteImport() {
placeholder: t("certd.domainList.placeholder"),
rows: 8,
},
col: {
span: 24,
},
col: { span: 24 },
},
},
groupId: {
@@ -36,13 +38,10 @@ export function useSiteImport() {
vModel: "modelValue",
type: "site",
},
col: {
span: 24,
},
col: { span: 24 },
},
},
},
form: {
async doSubmit({ form }) {
return siteInfoApi.Import(form);
@@ -53,7 +52,99 @@ export function useSiteImport() {
});
}
return {
openSiteImportDialog,
return { openSiteImportDialog };
}
export function useSiteImportTask() {
const { openFormDialog } = useFormDialog();
const { t } = useI18n();
const columns = {
dnsProviderType: {
title: t("certd.domain.domainProvider"),
type: "text",
form: {
component: {
name: "dns-provider-selector",
on: {
selectedChange: ({ form, $event }: any) => {
form.dnsProviderAccessType = $event.accessType;
},
},
},
valueChange({ form }: any) {
form.dnsProviderAccessId = null;
},
},
},
dnsProviderAccessType: {
title: t("certd.domain.domainProviderAccessType"),
type: "text",
form: { show: false },
},
dnsProviderAccessId: {
title: t("certd.domain.domainProviderAccess"),
type: "text",
form: {
component: {
name: "access-selector",
vModel: "modelValue",
type: compute(({ form }: any) => form.dnsProviderAccessType || form.dnsProviderType),
},
},
},
groupId: {
title: t("certd.fields.group"),
type: "text",
form: {
component: {
name: GroupSelector,
vModel: "modelValue",
type: "site",
},
},
},
};
return function openSiteImportTaskDialog(req: { afterSubmit?: (res?: any) => void; form?: any }) {
openFormDialog({
title: t("certd.domain.importFromProvider"),
columns,
initialForm: { ...req.form },
onSubmit: async (form: any) => {
const res = await siteInfoApi.ImportTaskSave({
key: form.key,
dnsProviderType: form.dnsProviderType,
dnsProviderAccessId: form.dnsProviderAccessId,
groupId: form.groupId,
});
if (req.afterSubmit) {
req.afterSubmit(res);
}
},
});
};
}
export function useSiteImportTaskManage() {
const { openFormDialog } = useFormDialog();
const { t } = useI18n();
const settingStore = useSettingStore();
return async function openSiteImportTaskManageDialog(req: {
afterSubmit?: (res?: any) => void;
form?: any;
zIndex?: number;
}) {
settingStore.checkPlus();
await openFormDialog({
title: t("certd.domain.importFromProvider"),
body: () => <SiteInfoImportTaskStatus />,
zIndex: req.zIndex,
onSubmit: async (form: any) => {
if (req.afterSubmit) {
req.afterSubmit(form);
}
},
});
};
}
@@ -55,7 +55,7 @@
</template>
<script lang="ts" setup>
import { computed, onActivated, onMounted, provide, ref } from "vue";
import { computed, provide, ref } from "vue";
import { dict, useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import ChangeGroup from "./components/change-group.vue";
@@ -67,7 +67,7 @@ import BatchRerun from "./components/batch-rerun.vue";
import { Modal, notification } from "ant-design-vue";
import * as api from "./api";
import { useI18n } from "/src/locales";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n();
import ChangeNotification from "/@/views/certd/pipeline/components/change-notification.vue";
import { useSettingStore } from "/@/store/settings";
@@ -128,11 +128,7 @@ context.hasActionPermission = hasActionPermission;
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await groupDictRef.reloadDict();
await crudExpose.doRefresh();
});
@@ -172,9 +172,9 @@ function useStepForm() {
const stepTypeSelected = (item: any) => {
if (item.needPlus && !settingStore.isPlus) {
message.warn("此插件需要开通专业版才能使用");
message.warn("此插件需要开通Certd专业版才能使用");
mitter.emit("openVipModal");
throw new Error("此插件需要开通专业版才能使用");
throw new Error("此插件需要开通Certd专业版才能使用");
}
currentStep.value.type = item.name;
currentStep.value.title = item.title;
@@ -26,13 +26,13 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n();
@@ -67,10 +67,7 @@ const handleBatchDelete = () => {
};
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -27,7 +27,7 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted, Ref, ref } from "vue";
import { onMounted, ref } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
@@ -18,12 +18,12 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n();
@@ -52,10 +52,7 @@ const handleBatchDelete = () => {
};
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -8,9 +8,9 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({
name: "MyTrade",
@@ -18,10 +18,7 @@ defineOptions({
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -27,7 +27,7 @@
</template>
<script lang="ts" setup>
import { computed, h, onActivated, onMounted, reactive, ref } from "vue";
import { computed, h, reactive, ref } from "vue";
import { compute, dict, useFs } from "@fast-crud/fast-crud";
import { Button, notification } from "ant-design-vue";
import * as api from "./api";
@@ -36,6 +36,7 @@ import createWithdrawCrudOptions from "./crud-withdraw";
import { util } from "/@/utils";
import { useFormDialog } from "/@/use/use-dialog";
import { useUserStore } from "/@/store/user";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ name: "MyWallet" });
@@ -257,14 +258,8 @@ async function refreshWalletPage() {
loaded.value = true;
}
onMounted(refreshWalletPage);
onActivated(async () => {
if (!loaded.value) {
return;
}
await refreshWalletPage();
});
//
useMounted(refreshWalletPage);
</script>
<style lang="less">
@@ -41,7 +41,7 @@
</div>
</div>
<div class="hero-image-wrapper">
<img src="/static/images/certd-intro.png" alt="Certd Intro" class="hero-image" />
<img :src="isDark ? '/static/images/certd-intro-dark.png' : '/static/images/certd-intro.png'" alt="Certd Intro" class="hero-image" />
</div>
</div>
</section>
@@ -121,7 +121,8 @@ import { useAccessStore } from "/@/vben/stores";
import { SiteInfo, SysPublicSetting } from "/@/store/settings/api.basic";
import ThemeToggle from "/@/vben/layouts/widgets/theme-toggle/theme-toggle.vue";
import { useRouter } from "vue-router";
import { usePreferences } from "/@/vben/preferences";
const { isDark } = usePreferences();
const envRef = ref(env);
const settingStore = useSettingStore();
const userStore = useUserStore();
@@ -381,7 +382,7 @@ onMounted(() => {
padding: 0 24px;
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 60px;
gap: 10px;
align-items: center;
}
@@ -440,7 +441,7 @@ onMounted(() => {
.hero-image {
width: 100%;
height: auto;
max-width: 550px;
max-width: 600px;
}
.section-header {
@@ -14,27 +14,25 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted, ref } from "vue";
import { defineComponent, ref} from "vue";
import createCrudOptions from "./crud.js";
import FsPermissionTree from "./fs-permission-tree.vue";
import { usePermission } from "/src/plugin/permission";
import { useFs, useUi } from "@fast-crud/fast-crud";
import { useI18n } from "/src/locales";
import { useMounted } from "/@/use/use-mounted";
export default defineComponent({
name: "PermissionManager",
components: { FsPermissionTree },
setup() {
// permissioncommonOptionsactionbarrowHandleshow
// ./src/plugin/fast-crud/index.js 75-77
// ./src/plugin/fast-crud/index.js 75-77
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: "sys:auth:per" } });
const { t } = useI18n();
//
onMounted(async () => {
// await crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
@@ -11,14 +11,15 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted, ref } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import * as permissionApi from "../permission/api";
import * as api from "./api";
import { message } from "ant-design-vue";
import { defineComponent, ref } from "vue";
import * as permissionApi from "../permission/api";
import FsPermissionTree from "../permission/fs-permission-tree.vue";
import * as api from "./api";
import createCrudOptions from "./crud";
import { UseCrudPermissionCompProps, UseCrudPermissionExtraProps } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
import { useI18n } from "/src/locales";
function useAuthz() {
@@ -8,9 +8,10 @@
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, onActivated } from "vue";
import { useCrud, useExpose, useFs } from "@fast-crud/fast-crud";
import { useFs } from "@fast-crud/fast-crud";
import { defineComponent } from "vue";
import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
export default defineComponent({
name: "UserManager",
setup() {
@@ -22,12 +22,13 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
const { t } = useI18n();
@@ -55,10 +56,7 @@ const handleBatchDelete = () => {
};
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -22,12 +22,13 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
const { t } = useI18n();
@@ -55,10 +56,7 @@ const handleBatchDelete = () => {
};
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -19,12 +19,13 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
import { useRoute } from "vue-router";
const { t } = useI18n();
@@ -61,10 +62,7 @@ const handleBatchDelete = () => {
};
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -17,14 +17,16 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useProjectStore } from "/@/store/project";
import { useCrudPermission } from "/@/plugin/permission";
import AdminModeIntro from "./intro.vue";
import { useProjectStore } from "/@/store/project";
const { t } = useI18n();
defineOptions({
@@ -52,10 +54,7 @@ const handleBatchDelete = () => {
};
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -17,12 +17,12 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n();
@@ -31,15 +31,11 @@ defineOptions({
});
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
onActivated(async () => {
await crudExpose.doRefresh();
});
const selectedRowKeys = context.selectedRowKeys;
const handleBatchDelete = () => {
if (selectedRowKeys.value?.length > 0) {
Modal.confirm({
title: t("certd.confirm"),
title: t("certd.pluginManagement"),
content: t("certd.batchDeleteConfirm", { count: selectedRowKeys.value.length }),
async onOk() {
await DeleteBatch(selectedRowKeys.value);
@@ -49,13 +45,13 @@ const handleBatchDelete = () => {
},
});
} else {
message.error(t("certd.pleaseSelectRecord"));
message.error(t("certd.selectRecordFirst"));
}
};
//
onMounted(() => {
crudExpose.doRefresh();
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
<style lang="less"></style>
@@ -8,10 +8,10 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { useSettingStore } from "/@/store/settings";
import { useMounted } from "/@/use/use-mounted";
defineOptions({
name: "SettingsHeaderMenus",
@@ -20,10 +20,7 @@ const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions
const settingStore = useSettingStore();
//
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -11,9 +11,9 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({
name: "SysProductActivationCode",
@@ -21,10 +21,8 @@ defineOptions({
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
onMounted(() => {
// crudExpose.doRefresh();
});
onActivated(async () => {
//
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -3,9 +3,10 @@
</template>
<script lang="ts" setup>
import { defineEmits, onActivated, onMounted, ref } from "vue";
import { ref } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({
name: "ProductManager",
@@ -16,10 +17,7 @@ const context: any = { emit };
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
+1 -1
View File
@@ -13,7 +13,7 @@ typeorm:
#plus:
# server:
# baseUrl: 'https://api.ai.handsfree.work'
# baseUrl: 'https://api.ai.handfree.work'
plus:
server:
+1 -1
View File
@@ -14,7 +14,7 @@ typeorm:
#plus:
# server:
# baseUrl: 'https://api.ai.handsfree.work'
# baseUrl: 'https://api.ai.handfree.work'
plus:
server:
+21
View File
@@ -3,6 +3,27 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/ui-server
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Bug Fixes
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
* Parse PEM chain and import certificate chain ([#747](https://github.com/certd/certd/issues/747)) ([454912d](https://github.com/certd/certd/commit/454912d31407d350cbd170953ccbd0564e74fd6c))
### Performance Improvements
* 添加AWS Rate Limit应对措施 ([#748](https://github.com/certd/certd/issues/748)) ([56b8c68](https://github.com/certd/certd/commit/56b8c689ec2b5cff49010a8c765483dd36803e9d))
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
* 优化 HiPM DNSMgr 插件,添加域名查询双层策略 ([#744](https://github.com/certd/certd/issues/744)) @WUHINS ([0f3f851](https://github.com/certd/certd/commit/0f3f8519e04d95cb848e28b98a3d4fcbed481fce))
### Reverts
* Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749) ([5e8bdac](https://github.com/certd/certd/commit/5e8bdac00850bed4f5f2a272bee42c490730ec21)), closes [#748](https://github.com/certd/certd/issues/748) [#749](https://github.com/certd/certd/issues/749)
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements
@@ -106,9 +106,13 @@ input:
onSelectedChange: ctx.compute(({form})=>{
return ($event)=>{
form.dnsProviderAccessType = $event.accessType
form.dnsProviderAccess = null
}
})
}),
onChange: ctx.compute(({form})=>{
return ($event)=>{
form.dnsProviderAccess = null
}
}),
},
}
@@ -71,7 +71,7 @@ input:
},
}
helper: 你在七牛云上配置的CDN加速域名,比如:certd.handsfree.work
helper: 你在七牛云上配置的CDN加速域名,比如:certd.handfree.work
order: 0
output: {}
pluginType: deploy
@@ -10,7 +10,7 @@ desc: 自动部署域名证书至七牛云KODO,注意是自定义源站域名
input:
domainName:
title: 自定义源站域名
helper: 你在七牛云上配置的OSS域名,比如:certd.handsfree.work
helper: 你在七牛云上配置的OSS域名,比如:certd.handfree.work
required: true
order: 0
cert:
+14 -14
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.41.1",
"version": "1.41.3",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -54,20 +54,20 @@
"@aws-sdk/client-sts": "^3.990.0",
"@azure/arm-dns": "^5.1.0",
"@azure/identity": "^4.13.1",
"@certd/acme-client": "^1.41.1",
"@certd/basic": "^1.41.1",
"@certd/commercial-core": "^1.41.1",
"@certd/acme-client": "^1.41.3",
"@certd/basic": "^1.41.3",
"@certd/commercial-core": "^1.41.3",
"@certd/jdcloud": "^1.41.3",
"@certd/lib-huawei": "^1.41.3",
"@certd/lib-k8s": "^1.41.3",
"@certd/lib-server": "^1.41.3",
"@certd/midway-flyway-js": "^1.41.3",
"@certd/pipeline": "^1.41.3",
"@certd/plugin-cert": "^1.41.3",
"@certd/plugin-lib": "^1.41.3",
"@certd/plugin-plus": "^1.41.3",
"@certd/plus-core": "^1.41.3",
"@certd/cv4pve-api-javascript": "^8.4.2",
"@certd/jdcloud": "^1.41.1",
"@certd/lib-huawei": "^1.41.1",
"@certd/lib-k8s": "^1.41.1",
"@certd/lib-server": "^1.41.1",
"@certd/midway-flyway-js": "^1.41.1",
"@certd/pipeline": "^1.41.1",
"@certd/plugin-cert": "^1.41.1",
"@certd/plugin-lib": "^1.41.1",
"@certd/plugin-plus": "^1.41.1",
"@certd/plus-core": "^1.41.1",
"@google-cloud/dns": "^5.3.1",
"@google-cloud/publicca": "^1.3.0",
"@huaweicloud/huaweicloud-sdk-cdn": "3.1.185",
@@ -134,7 +134,5 @@ export class MainConfiguration {
});
logger.info("当前环境:", this.app.getEnv()); // prod
}
}
@@ -60,7 +60,7 @@ export class SysPlusController extends BaseController {
// const bindUrl = 'http://127.0.0.1:7001/';
// const service = new PlusRequestService({
// subjectId: subjectId,
// plusServerBaseUrls: ['https://api.ai.handsfree.work'],
// plusServerBaseUrls: ['https://api.ai.handfree.work'],
// });
// const body = { subjectId, appKey: 'kQth6FHM71IPV3qdWc', url: bindUrl };
//
@@ -136,6 +136,55 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
return this.ok();
}
@Post("/import/save", { description: Constants.per.authOnly, summary: "保存站点证书监控导入任务" })
async siteInfoImportSave(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdWrite();
const { dnsProviderType, dnsProviderAccessId, key, groupId } = body;
const item = await this.service.saveSiteInfoImportTask({
userId: userId,
projectId: projectId,
dnsProviderType,
dnsProviderAccessId,
key,
groupId,
});
return this.ok(item);
}
@Post("/import/status", { description: Constants.per.authOnly, summary: "查询站点证书监控导入任务状态" })
async siteInfoImportStatus() {
const { projectId, userId } = await this.getProjectUserIdRead();
const task = await this.service.getSiteInfoImportTaskStatus({
userId: userId,
projectId: projectId,
});
return this.ok(task);
}
@Post("/import/delete", { description: Constants.per.authOnly, summary: "删除站点证书监控导入任务" })
async siteInfoImportDelete(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdWrite();
const { key } = body;
await this.service.deleteSiteInfoImportTask({
userId: userId,
projectId: projectId,
key,
});
return this.ok();
}
@Post("/import/start", { description: Constants.per.authOnly, summary: "开始站点证书监控导入任务" })
async siteInfoImportStart(@Body(ALL) body: any) {
const { projectId, userId } = await this.getProjectUserIdWrite();
const { key } = body;
await this.service.startSiteInfoImportTask({
key,
userId: userId,
projectId: projectId,
});
return this.ok();
}
@Post("/ipCheckChange", { description: Constants.per.authOnly, summary: "修改IP检查设置" })
async ipCheckChange(@Body(ALL) bean: any) {
await this.checkOwner(this.service, bean.id, "read");
@@ -11,7 +11,7 @@ import { PipelineEntity } from "../../../modules/pipeline/entity/pipeline.js";
const pipelineExample = `
// 流水线配置示例,实际传送时要去掉注释
{
"title": "handsfree.work证书自动化", //标题
"title": "handfree.work证书自动化", //标题
"runnableType": "pipeline", //类型,固定为pipeline
"projectId": 1, // 项目ID, 未开启企业模式,无需传递
"type": "cert", // 流水线类型,cert:证书自动化, custom :自定义流水线
@@ -98,14 +98,17 @@ export class LegacyAcmeAccountAccessFix {
continue;
}
const name = buildAcmeAccountAccessName(parsedKey.caType, parsedKey.email);
const query = {
userId: record.userId,
type: "acmeAccount",
subtype: parsedKey.caType,
name,
} as any;
if (record.projectId) {
query.projectId = record.projectId;
}
const exists = await this.accessService.findOne({
where: {
userId: record.userId,
projectId: record.projectId,
type: "acmeAccount",
subtype: parsedKey.caType,
name,
} as any,
where: query,
});
if (exists) {
continue;
@@ -192,10 +192,7 @@ describe("CertApplyTemplateService", () => {
await service.setDefault(2, 10, null);
assert.deepEqual((service as any).updateWhereList, [
{ userId: 10 },
{ userId: 10, id: 2 },
]);
assert.deepEqual((service as any).updateWhereList, [{ userId: 10 }, { userId: 10, id: 2 }]);
assert.equal(list[0].isDefault, false);
assert.equal(list[1].isDefault, true);
assert.equal(list[2].isDefault, false);
@@ -28,12 +28,12 @@ describe("DnsPersistRecordService", () => {
const service = new DnsPersistRecordService();
const record = await service.buildRecord({
domain: "aaa.handsfree.work",
domain: "aaa.handfree.work",
accountUri: "https://example.com/acct/1",
});
assert.equal(record.hostRecord, "_validation-persist.aaa");
assert.equal(record.mainDomain, "handsfree.work");
assert.equal(record.mainDomain, "handfree.work");
assert.equal(record.recordValue, "letsencrypt.org; accounturi=https://example.com/acct/1; policy=wildcard");
});
@@ -58,3 +58,10 @@ export class UserDomainImportSetting extends BaseSettings {
domainImportList: { dnsProviderType: string; dnsProviderAccessId: number; key: string; title: string; icon?: string }[];
}
export class UserSiteInfoImportSetting extends BaseSettings {
static __title__ = "用户站点证书监控导入设置";
static __key__ = "user.siteInfo.import";
siteInfoImportList: { dnsProviderType: string; dnsProviderAccessId: number; key: string; title: string; icon?: string; groupId?: number }[];
}
@@ -1,26 +1,30 @@
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { BaseService, Constants, isEnterprise, NeedSuiteException, NeedVIPException, SysSettingsService } from "@certd/lib-server";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { In, Repository } from "typeorm";
import { SiteInfoEntity } from "../entity/site-info.js";
import { siteTester } from "./site-tester.js";
import dayjs from "dayjs";
import { logger, utils } from "@certd/basic";
import { PeerCertificate } from "tls";
import { NotificationService } from "../../pipeline/service/notification-service.js";
import { isComm, isPlus } from "@certd/plus-core";
import { http, logger, utils } from "@certd/basic";
import { UserSuiteService } from "@certd/commercial-core";
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
import { UserSiteMonitorSetting } from "../../mine/service/models.js";
import { SiteIpService } from "./site-ip-service.js";
import { SiteIpEntity } from "../entity/site-ip.js";
import { Cron } from "../../cron/cron.js";
import { dnsContainer } from "./dns-custom.js";
import { AccessService, BaseService, Constants, isEnterprise, NeedSuiteException, NeedVIPException, SysSettingsService } from "@certd/lib-server";
import { Pager } from "@certd/pipeline";
import { createDnsProvider, dnsProviderRegistry, DomainParser } from "@certd/plugin-lib";
import { isComm, isPlus } from "@certd/plus-core";
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { InjectEntityModel } from "@midwayjs/typeorm";
import dayjs from "dayjs";
import { merge } from "lodash-es";
import { JobHistoryService } from "./job-history-service.js";
import { JobHistoryEntity } from "../entity/job-history.js";
import { PeerCertificate } from "tls";
import { In, Repository } from "typeorm";
import { BackTask, taskExecutor } from "../../basic/service/task-executor.js";
import { Cron } from "../../cron/cron.js";
import { UserSiteInfoImportSetting, UserSiteMonitorSetting } from "../../mine/service/models.js";
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
import { TaskServiceBuilder } from "../../pipeline/service/getter/task-service-getter.js";
import { NotificationService } from "../../pipeline/service/notification-service.js";
import { UserService } from "../../sys/authority/service/user-service.js";
import { ProjectService } from "../../sys/enterprise/service/project-service.js";
import { JobHistoryEntity } from "../entity/job-history.js";
import { SiteInfoEntity } from "../entity/site-info.js";
import { SiteIpEntity } from "../entity/site-ip.js";
import { dnsContainer } from "./dns-custom.js";
import { JobHistoryService } from "./job-history-service.js";
import { SiteIpService } from "./site-ip-service.js";
import { siteTester } from "./site-tester.js";
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
@@ -51,6 +55,12 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
@Inject()
projectService: ProjectService;
@Inject()
accessService: AccessService;
@Inject()
taskServiceBuilder: TaskServiceBuilder;
@Inject()
cron: Cron;
@@ -64,7 +74,6 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
//企业模式不限制
return;
}
if (isComm()) {
const suiteSetting = await this.userSuiteService.getSuiteSetting();
if (suiteSetting.enabled) {
@@ -483,6 +492,219 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
await batchAdd(list);
}
async startSiteInfoImportTask(req: { userId: number; projectId: number; key: string }) {
const key = req.key;
const setting = await this.userSettingsService.getSetting<UserSiteInfoImportSetting>(req.userId, req.projectId, UserSiteInfoImportSetting);
const item = setting.siteInfoImportList.find(item => item.key === key);
if (!item) {
throw new Error(`站点监控导入任务(${key})还未注册`);
}
const { dnsProviderType, dnsProviderAccessId, title, groupId } = item;
const TASK_TYPE = "siteInfoImportTask";
taskExecutor.start(
new BackTask({
type: TASK_TYPE,
key,
title,
run: async (task: BackTask) => {
await this._syncSitesFromProvider(
{
userId: req.userId,
projectId: req.projectId,
dnsProviderType,
dnsProviderAccessId,
groupId,
},
task
);
},
})
);
}
private async _syncSitesFromProvider(req: { userId: number; projectId: number; dnsProviderType: string; dnsProviderAccessId: number; groupId?: number }, task: BackTask) {
const { userId, projectId, dnsProviderType, dnsProviderAccessId, groupId } = req;
const serviceGetter = this.taskServiceBuilder.create({ userId, projectId });
const subDomainGetter = await serviceGetter.getSubDomainsGetter();
const domainParser = new DomainParser(subDomainGetter);
const access = await this.accessService.getById(dnsProviderAccessId, userId, projectId);
const context = { access, logger, http, utils, domainParser, serviceGetter };
const dnsProvider = await createDnsProvider({ dnsProviderType, context });
// 1. 先获取主域名列表(每个 domain 翻页)
const domainPager = new Pager({ pageNo: 1, pageSize: 50 });
const domainList: string[] = [];
while (true) {
const pageRet = await dnsProvider.getDomainListPage(domainPager);
for (const item of pageRet.list || []) {
domainList.push(item.domain);
}
if (!pageRet.list || pageRet.list.length < domainPager.pageSize) {
break;
}
domainPager.pageNo++;
}
// 2. 根据 provider 是否支持 getRecordListPage 决定处理方式
const skipTypes = new Set(["TXT", "NS", "SOA", "SRV", "CAA", "PTR"]);
for (const domain of domainList) {
if (!dnsProvider.getRecordListPage) {
// 不支持解析记录列表时,直接把主域名作为一个站点
try {
await this.add({
userId,
projectId,
groupId,
domain,
name: domain,
httpsPort: 443,
} as any);
task.incrementCurrent();
} catch (e) {
if (e.message && e.message.indexOf("已达上限") >= 0) {
task.addError(`${domain}: ${e.message}`);
break;
}
task.incrementSkip();
}
continue;
}
// 支持 getRecordListPage:翻页获取解析记录,过滤掉泛域名(*.)和不支持的类型
const recordPager = new Pager({ pageNo: 1, pageSize: 100 });
while (true) {
const pageRet = await dnsProvider.getRecordListPage(domain, recordPager);
for (const record of pageRet.list || []) {
task.incrementCurrent();
const typeUpper = (record.type || "").toUpperCase();
if (skipTypes.has(typeUpper)) {
task.incrementSkip();
continue;
}
const fullRecord = record.fullRecord;
if (!fullRecord || fullRecord.startsWith("*.") || fullRecord.startsWith("_acme-challenge")) {
task.incrementSkip();
continue;
}
try {
await this.add({
userId,
projectId,
groupId,
domain: fullRecord,
name: fullRecord,
httpsPort: 443,
} as any);
} catch (e) {
if (e.message && e.message.indexOf("已达上限") >= 0) {
task.addError(`${fullRecord}: ${e.message}`);
return;
}
task.incrementSkip();
}
}
if (!pageRet.list || pageRet.list.length < recordPager.pageSize) {
break;
}
recordPager.pageNo++;
}
}
task.setTotal(task.current || task.total || 0);
logger.info(`从域名提供商${dnsProviderType}导入站点完成,共处理${task.current}个记录,跳过${task.getSkipCount()}个,成功${task.getSuccessCount()}个,失败${task.getErrorCount()}`);
}
async getSiteInfoImportTaskStatus(req: { userId?: number; projectId?: number }) {
const userId = req.userId || 0;
const projectId = req.projectId;
const setting = await this.userSettingsService.getSetting<UserSiteInfoImportSetting>(userId, projectId, UserSiteInfoImportSetting);
const list = setting?.siteInfoImportList || [];
const TASK_TYPE = "siteInfoImportTask";
const taskList: any = [];
for (const item of list) {
const { key } = item;
const task = taskExecutor.get(TASK_TYPE, key);
taskList.push({ ...item, task });
}
return taskList;
}
async getSiteInfoImportProviderTitle(req: { userId?: number; projectId?: number; dnsProviderType: string; dnsProviderAccessId: number }) {
const userId = req.userId || 0;
const projectId = req.projectId;
const { dnsProviderType, dnsProviderAccessId } = req;
const dnsProviderDefine = dnsProviderRegistry.getDefine(dnsProviderType);
if (!dnsProviderDefine) {
throw new Error(`该域名提供商(${dnsProviderType})不存在,请检查是否已被注册`);
}
const access = await this.accessService.getSimpleInfo(dnsProviderAccessId);
if (!access || access.userId !== userId) {
throw new Error(`该授权(${dnsProviderAccessId})不存在,请检查是否已被删除`);
}
if (projectId && access.projectId !== projectId) {
throw new Error(`该授权(${dnsProviderAccessId})不存在,请检查是否已被删除`);
}
return {
title: `${dnsProviderDefine.title}_${access.name || ""}`,
icon: dnsProviderDefine.icon || "",
};
}
async addSiteInfoImportTask(req: { userId?: number; projectId?: number; dnsProviderType: string; dnsProviderAccessId: number; index?: number; groupId?: number }) {
const userId = req.userId || 0;
const projectId = req.projectId;
const { dnsProviderType, dnsProviderAccessId, index = 0, groupId } = req;
const key = `user_${userId}_${dnsProviderType}_${dnsProviderAccessId}`;
const { title, icon } = await this.getSiteInfoImportProviderTitle(req);
const setting = await this.userSettingsService.getSetting<UserSiteInfoImportSetting>(userId, projectId, UserSiteInfoImportSetting);
setting.siteInfoImportList = setting.siteInfoImportList || [];
if (setting.siteInfoImportList.find(item => item.key === key)) {
throw new Error(`该站点监控导入任务${key}已存在`);
}
const access = await this.accessService.getAccessById(dnsProviderAccessId, true, userId, projectId);
if (!access) {
throw new Error(`该授权(${dnsProviderAccessId})不存在,请检查是否已被删除`);
}
const item = { dnsProviderType, dnsProviderAccessId, key, title, icon: icon || "", groupId };
setting.siteInfoImportList.splice(index, 0, item);
await this.userSettingsService.saveSetting(userId, projectId, setting);
return item;
}
async deleteSiteInfoImportTask(req: { userId?: number; projectId?: number; key: string }) {
const userId = req.userId || 0;
const projectId = req.projectId;
const { key } = req;
const setting = await this.userSettingsService.getSetting<UserSiteInfoImportSetting>(userId, projectId, UserSiteInfoImportSetting);
setting.siteInfoImportList = setting.siteInfoImportList || [];
const index = setting.siteInfoImportList.findIndex(item => item.key === key);
if (index === -1) {
throw new Error(`该站点监控导入任务${key}不存在`);
}
setting.siteInfoImportList.splice(index, 1);
const TASK_TYPE = "siteInfoImportTask";
taskExecutor.clear(TASK_TYPE, key);
await this.userSettingsService.saveSetting(userId, projectId, setting);
}
async saveSiteInfoImportTask(req: { userId?: number; projectId?: number; dnsProviderType: string; dnsProviderAccessId: number; key?: string; groupId?: number }) {
const userId = req.userId || 0;
const projectId = req.projectId;
const { dnsProviderType, dnsProviderAccessId, key, groupId } = req;
const setting = await this.userSettingsService.getSetting<UserSiteInfoImportSetting>(userId, projectId, UserSiteInfoImportSetting);
setting.siteInfoImportList = setting.siteInfoImportList || [];
let index = 0;
if (key) {
index = setting.siteInfoImportList.findIndex(item => item.key === key);
if (index === -1) {
throw new Error(`该站点监控导入任务${key}不存在`);
}
await this.deleteSiteInfoImportTask({ userId, projectId, key });
}
return await this.addSiteInfoImportTask({ userId, projectId, dnsProviderType, dnsProviderAccessId, index, groupId });
}
clearSiteMonitorJob(userId: number, projectId?: number) {
this.cron.remove(`siteMonitor_${userId}_${projectId || ""}`);
}
@@ -897,13 +897,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (param.projectId != null) {
query.projectId = param.projectId;
}
const statusCount = await this.repository
.createQueryBuilder()
.select("status")
.addSelect("count(1)", "count")
.where(query)
.groupBy("status")
.getRawMany();
const statusCount = await this.repository.createQueryBuilder().select("status").addSelect("count(1)", "count").where(query).groupBy("status").getRawMany();
return statusCount;
}
@@ -915,13 +909,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (param.projectId != null) {
query.projectId = param.projectId;
}
const statusCount = await this.repository
.createQueryBuilder()
.select("disabled")
.addSelect("count(1)", "count")
.where(query)
.groupBy("disabled")
.getRawMany();
const statusCount = await this.repository.createQueryBuilder().select("disabled").addSelect("count(1)", "count").where(query).groupBy("disabled").getRawMany();
const result = {
enabled: 0,
disabled: 0,
@@ -1115,14 +1103,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
},
});
for (const item of list) {
const pipeline = JSON.parse(item.content);
if (trigger.props === false) {
//清除trigger
pipeline.triggers = [];
} else {
const start = dayjs().format("YYYY-MM-DD") + " " + trigger.randomRange[0];
let end = dayjs().format("YYYY-MM-DD") + " " + trigger.randomRange[1];
if (trigger.randomRange[1] < trigger.randomRange[0]) {
@@ -1137,19 +1123,20 @@ export class PipelineService extends BaseService<PipelineEntity> {
//随机时间
const randomTime = Math.floor(Math.random() * (endTime - startTime)) + startTime;
const time = dayjs(randomTime).format(" ss:mm:HH").replaceAll(":", " ").replaceAll(" 0", " ").trim();
set(triggerConf, "props.cron", `${time} * * *`)
set(triggerConf, "props.cron", `${time} * * *`);
}
delete triggerConf.random
delete triggerConf.random;
delete triggerConf.randomRange;
pipeline.triggers = [{
id: nanoid(),
title: "定时触发",
...triggerConf
}];
pipeline.triggers = [
{
id: nanoid(),
title: "定时触发",
...triggerConf,
},
];
}
await this.doUpdatePipelineJson(item, pipeline);
}
}
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any, projectId?: number) {
@@ -48,3 +48,4 @@
// export * from './plugin-dnsmgr/index.js'
// export * from './plugin-nginx-proxy-manager/index.js'
// export * from './plugin-hipmdnsmgr/index.js'
// export * from './plugin-dynadot/index.js'
@@ -1,4 +1,4 @@
import { AbstractDnsProvider, CreateRecordOptions, DomainRecord, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
import { AbstractDnsProvider, CreateRecordOptions, DomainRecord, DnsResolveRecord, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
import { AliyunAccess } from "../../plugin-lib/aliyun/access/aliyun-access.js";
import { AliyunClient } from "../../plugin-lib/aliyun/index.js";
import { Pager, PageRes, PageSearch } from "@certd/pipeline";
@@ -177,6 +177,35 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
total: ret.TotalCount,
};
}
async getRecordListPage(domain: string, req: PageSearch): Promise<PageRes<DnsResolveRecord>> {
const pager = new Pager(req);
const params = {
RegionId: "cn-hangzhou",
DomainName: domain,
PageSize: pager.pageSize,
PageNumber: pager.pageNo,
};
const requestOption = {
method: "POST",
};
const ret = await this.client.request("DescribeDomainRecords", params, requestOption);
const rawList = ret.DomainRecords?.Record || [];
const list = rawList.map(item => ({
id: item.RecordId,
hostRecord: item.RR,
fullRecord: item.RR === "@" ? domain : `${item.RR}.${domain}`,
type: item.Type,
value: item.Value,
}));
return {
list,
total: ret.TotalCount,
};
}
}
new AliyunDnsProvider();
@@ -78,8 +78,8 @@ export class DeployCertToAliyunApig extends AbstractTaskPlugin {
action: DeployCertToAliyunApig.prototype.onGetDomainList.name,
watches: ["region", "accessId", "gatewayType"],
required: true,
pager:true,
search:true,
pager: true,
search: true,
})
)
domainList!: string[];
@@ -196,7 +196,7 @@ export class DeployCertToAliyunApig extends AbstractTaskPlugin {
throw new Error("请选择网关类型");
}
const pager = new Pager(data);
const pager = new Pager(data);
const access = await this.getAccess<AliyunAccess>(this.accessId);
const client = access.getClient(this.regionEndpoint);
@@ -69,7 +69,7 @@ export class DeployCertToAliyunApiGateway extends AbstractTaskPlugin {
action: DeployCertToAliyunApiGateway.prototype.onGetGroupList.name,
watches: ["regionEndpoint", "accessId"],
required: true,
single:true
single: true,
})
)
groupId!: string;
@@ -30,16 +30,20 @@ export class AwsClient {
},
});
const cert = certInfo.crt.split("-----END CERTIFICATE-----")[0] + "-----END CERTIFICATE-----";
// Split the full PEM chain: first block is the leaf cert, the rest is the intermediate chain
const pemBlocks = certInfo.crt.split(/(?<=-----END CERTIFICATE-----)/);
const cert = pemBlocks[0].trim();
const chain = pemBlocks.slice(1).join("").trim();
// 构建上传参数
const data = await acmClient.send(
new ImportCertificateCommand({
Certificate: Buffer.from(cert),
PrivateKey: Buffer.from(certInfo.key),
// CertificateChain: certificateChain, // 可选
CertificateChain: chain ? Buffer.from(chain) : undefined,
})
);
console.log("Upload successful:", data);
this.logger.info(`Upload successful: ${data.CertificateArn}`);
// 返回证书 ARNAmazon Resource Name
return data.CertificateArn;
}
@@ -144,10 +144,10 @@ export class AcmeAccountAccess extends BaseAccess {
action: "GenerateAccount",
buttonText: "生成ACME账号",
successMessage: "ACME账号已生成,请保存授权配置",
type:"textarea",
rows:4,
type: "textarea",
rows: 4,
},
col:{span:24},
col: { span: 24 },
required: true,
helper: "请生成ACME账号,账号一旦生成不允许修改",
encrypt: true,
@@ -170,7 +170,6 @@ export class AcmeAccountAccess extends BaseAccess {
required: false,
helper: "是否开启修改ACME账号,注意,开启后,会影响DNS持久验证记录",
encrypt: false,
})
editAccount = false;
@@ -160,9 +160,13 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
onSelectedChange: ctx.compute(({form})=>{
return ($event)=>{
form.dnsProviderAccessType = $event.accessType
form.dnsProviderAccess = null
}
})
}),
onChange: ctx.compute(({form})=>{
return ($event)=>{
form.dnsProviderAccess = null
}
}),
},
}
`,
@@ -32,7 +32,7 @@ export class CertApplyLegoPlugin extends CertApplyBasePlugin {
// vModel: "value",
// options: [
// { value: "https://acme-v02.api.letsencrypt.org/directory", label: "Let's Encrypt" },
// { value: "https://letsencrypt.proxy.handsfree.work/directory", label: "Let's Encrypt代理,letsencrypt.org无法访问时使用" },
// { value: "https://letsencrypt.proxy.handfree.work/directory", label: "Let's Encrypt代理,letsencrypt.org无法访问时使用" },
// ],
// },
// required: true,

Some files were not shown because too many files have changed in this diff Show More