Compare commits

..

43 Commits

Author SHA1 Message Date
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
xiaojunnuo cdea411136 v1.41.1 2026-06-06 02:36:50 +08:00
xiaojunnuo fdb000ee7c build: prepare to build 2026-06-06 02:33:06 +08:00
xiaojunnuo 4a0be1c29d build: prepare to build 2026-06-06 02:31:59 +08:00
xiaojunnuo 892d22e225 chore: 1 2026-06-06 02:30:57 +08:00
xiaojunnuo 4958a48b92 build: prepare to build 2026-06-06 02:21:11 +08:00
xiaojunnuo 28bbea85f0 chore: 1 2026-06-06 02:20:09 +08:00
xiaojunnuo 73b3a29cfc build: prepare to build 2026-06-06 02:15:39 +08:00
xiaojunnuo 77b8024453 perf(volcengine-vke): 火山VKE集群证书支持两种类型的证书保密字典 2026-06-06 02:12:47 +08:00
xiaojunnuo 1175e1164b refactor(ui): 统一使用useMounted钩子简化页面初始化逻辑 2026-06-06 00:50:59 +08:00
xiaojunnuo 5546af518e perf: 优化列表页面请求两次的问题 2026-06-05 00:23:08 +08:00
xiaojunnuo 99fd3083f2 perf: 流水线、监控站点支持导出 2026-06-04 23:43:25 +08:00
xiaojunnuo c0df8be832 perf(settings): 新增NO_PROXY代理排除配置 2026-06-04 23:24:29 +08:00
xiaojunnuo 73cab6a6ee chore(auth): 清理已使用的邀请码缓存 2026-06-04 18:28:15 +08:00
xiaojunnuo 7a71e45799 perf: 优化邀请注册流程 2026-06-04 18:22:21 +08:00
xiaojunnuo fdb1d1e6dd build: publish 2026-06-04 12:32:41 +08:00
xiaojunnuo 6dd4d6adeb build: trigger build image 2026-06-04 12:32:29 +08:00
161 changed files with 2500 additions and 534 deletions
+7 -2
View File
@@ -1,4 +1,4 @@
./packages/core/lego
./packages/core/lego
# IntelliJ project files
node_modules/
npm-debug.log
@@ -34,4 +34,9 @@ test.js
/logs
.pnpm-lock.yaml
pnpm-lock.yaml
.studio/
.studio/
# Certd 推广报告,仅本地使用
/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、完成开发后无需测试,通知用户自己去测试
+79 -1
View File
@@ -25,7 +25,7 @@ version: 1.0.0
## 实现流程
1. 先在 `packages/ui/certd-client/src/views` 下找 1-2 个相近 Fast Crud 页面,沿用它们的导入、布局、命名和权限写法。
2.`index.vue` 中使用 `fs-crud ref="crudRef" v-bind="crudBinding"`,并在 `onMounted` / `onActivated` 时调用 `crudExpose.doRefresh()`
2.`index.vue` 中使用 `fs-crud ref="crudRef" v-bind="crudBinding"`,并在 `onMounted` `onActivated` 时调用 `crudExpose.doRefresh()`;两个生命周期同时存在时只保留一个刷新入口,避免首次进入页面请求两次
3.`crud.tsx` 中配置 `request.pageRequest``columns``search``form``rowHandle``actionbar``toolbar` 等,接口分页参数和返回值按现有页面适配。
4. 操作按钮优先放在 Fast Crud 的 `rowHandle.buttons``actionbar.buttons` 中;审核、保存设置、批量操作等复杂交互可通过 `context` 调用 `index.vue` 中的方法。
5. 金额、状态、时间、枚举等字段优先复用项目已有组件、字典和格式化工具;避免在模板里重复堆格式化逻辑。
@@ -77,6 +77,84 @@ container:{}, //容器配置 ,对应fs-container
- 有固定操作栏、统计区、说明区时,这些区域应 `flex: none`,把剩余空间交给表格区域。
- 修改嵌入式 Fast Crud 页面后,要检查空数据、少量数据和多页数据时表格高度、分页器和空状态是否仍在预期区域内。
## 列表导出
- 列表需要导出时,优先使用 Fast Crud 工具栏导出能力,不要另写一套导出按钮或后端接口,除非数据必须跨权限、跨分页或异步生成文件。
- 导出当前搜索条件下的数据时,在 `toolbar.export` 中设置 `dataFrom: "search"`,并显式打开导出按钮。
- 导出列必须输出 Excel 可读的纯文本或数字;不要直接导出对象、数组、VNode、进度条组件、开关组件、时间戳毫秒值等。
- 有隐藏但业务上需要导出的字段时,把字段定义为普通列并设置 `column.show: false`,再在 `columnFilter` 中对该字段返回 `true`。例如证书域名这类只用于导出的辅助列。
- 嵌套字段可以使用 `lastVars.certDomains` 这类 key;导出格式化时用安全取值函数读取嵌套值。
- `dataFormatter` 中统一格式化特殊字段:时间字段转 `YYYY-MM-DD HH:mm:ss`,日期类有效期转业务文案或 `YYYY-MM-DD`,枚举/开关转字典 label,数组转逗号分隔字符串,对象转明确的业务摘要。
```typescript
import { ColumnProps, DataFormatterContext } from "@fast-crud/fast-crud";
import dayjs from "dayjs";
function getRecordValue(row: any, key: string) {
return key.split(".").reduce((target, item) => target?.[item], row);
}
function formatListValue(value: any) {
if (Array.isArray(value)) {
return value.join(",");
}
return value ?? "";
}
function exportColumnFilter(col: ColumnProps) {
if (!col.key || ["_index", "_selection", "rowHandle"].includes(col.key)) {
return false;
}
if (col.key === "lastVars.certDomains") {
return true;
}
return col.show !== false;
}
function exportDataFormatter(opts: DataFormatterContext) {
const { row, originalRow, col, exportCol } = opts;
const key = col.key;
const value = getRecordValue(originalRow, key);
if (key === "lastVars.certDomains") {
row[key] = formatListValue(value);
} else if (key.includes("Time") && value) {
row[key] = dayjs(value).format("YYYY-MM-DD HH:mm:ss");
}
if (col.width) {
exportCol.width = col.width / 10;
}
}
return {
crudOptions: {
toolbar: {
buttons: {
export: { show: true },
},
export: {
dataFrom: "search",
columnFilter: exportColumnFilter,
dataFormatter: exportDataFormatter,
},
},
columns: {
"lastVars.certDomains": {
title: "证书域名",
type: "text",
column: {
show: false,
width: 260,
ellipsis: true,
},
form: { show: false },
},
},
},
};
```
## 内置 CRUD 按钮
只要在 `request` 中配置了 `addRequest``editRequest``delRequest`Fast Crud 会自动在 `rowHandle` 渲染新增、编辑、删除按钮并完成对应操作,**不需要手写 `openDeleteConfirm``openEditDialog` 等方法**。
+37
View File
@@ -3,6 +3,43 @@
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
+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官方激活码购买平台
专业版、商业版特权对比
+52
View File
@@ -3,6 +3,58 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
* 修复阿里云证书订单orderid 选择出错的问题 ([41254d1](https://github.com/certd/certd/commit/41254d10b748a2d3e6ba43c7e11411650c748d1b))
* **monitor:** 修复开放接口自动创建证书流水线重复触发和等待时间不足的问题 ([91d5c90](https://github.com/certd/certd/commit/91d5c90eb0eaf65c81dddbd2d4d4b404cb8b4d07))
* **pipeline:** 修复批量随机修改定时没有生效的bug ([2e19dda](https://github.com/certd/certd/commit/2e19dda72e70b525a7c269e18e963a5ee602f59f))
### Features
* 商业版支持邀请返佣功能 ([f9a310b](https://github.com/certd/certd/commit/f9a310b6c3bbf30f221482a0c59e9c30080bdfc8))
* 商业版支持邀请推广功能 ([f1d2a10](https://github.com/certd/certd/commit/f1d2a1033a0f8d3dbd91fc9793e07bd0b858b539))
* 新增管理员针对用户流水线和证书监控管理功能 ([0211552](https://github.com/certd/certd/commit/021155278e7375f8487b0531ed1b5ad52512f007))
* 新增套餐激活码功能,通过CDK兑换套餐 ([81d6289](https://github.com/certd/certd/commit/81d6289a8631b073b49f24dee4b14bb1c8f31071))
* 新增推广等级激励功能 ([5096df5](https://github.com/certd/certd/commit/5096df5cc0d8f0ad8aa327b8e2a900ba23714bd8))
* 新增证书申请参数模版管理,开放接口支持使用证书参数模版和指定证书申请参数 ([f8b71a0](https://github.com/certd/certd/commit/f8b71a0e612fad527cf49136335e0b46f0f379cd))
* 支持dns-persist-01持久化验证方式申请证书,优化Acme账号的存储方式 ([67b05e2](https://github.com/certd/certd/commit/67b05e2d75e96b9f855e1ca0b3d0d8d03b92d8e6))
### Performance Improvements
* 插件全局配置支持下拉选项自定义映射功能 ([c637985](https://github.com/certd/certd/commit/c637985575b09196b04cce37ac14fbe68c029bde))
* 商业版提现增加收款二维码上传 ([83a5a21](https://github.com/certd/certd/commit/83a5a21f956e50942541f1532f3a8dcaa5821d34))
* **aliyun-apig:** 优化阿里云API网关部署插件的查询及翻页 ([3e4b7f3](https://github.com/certd/certd/commit/3e4b7f30ac6f3c976c8274bdf256c69b8a2c46db))
* **trade:** 优化商品购买页面的规格展示和折扣计算,支持订单取消 ([6624769](https://github.com/certd/certd/commit/66247690326ce2789900fc9110c08b3502cea655))
## [1.40.5](https://github.com/certd/certd/compare/v1.40.4...v1.40.5) (2026-05-26)
### Bug Fixes
View File
+1 -1
View File
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.41.0"
"version": "1.41.3"
}
+14
View File
@@ -3,6 +3,20 @@
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
# [1.41.0](https://github.com/publishlab/node-acme-client/compare/v1.40.5...v1.41.0) (2026-06-04)
### Bug Fixes
+3 -3
View File
@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.41.0",
"version": "1.41.3",
"type": "module",
"module": "./dist/index.js",
"main": "./dist/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.41.0",
"@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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
@@ -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")
+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)
**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
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
**Note:** Version bump only for package @certd/basic
+1 -1
View File
@@ -1 +1 @@
12:22
23:53
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.41.0",
"version": "1.41.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -52,5 +52,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
@@ -1,8 +1,9 @@
import { expect } from "chai";
import { createAxiosService, HttpClient, setGlobalHeaders } from "./util.request.js";
import { createAgent, createAxiosService, getGlobalAgents, HttpClient, isNoProxyMatched, setGlobalHeaders, setGlobalProxy } from "./util.request.js";
import { ILogger } from "./util.log.js";
const testLogger = {
debug() {},
info() {},
error() {},
} as unknown as ILogger;
@@ -10,6 +11,9 @@ const testLogger = {
describe("util.request", () => {
afterEach(() => {
setGlobalHeaders({});
setGlobalProxy({});
delete process.env.NO_PROXY;
delete process.env.no_proxy;
});
it("should merge global headers without overriding request headers", async () => {
@@ -50,4 +54,122 @@ describe("util.request", () => {
request: "request",
});
});
it("should set no_proxy environment variables", () => {
setGlobalProxy({
httpProxy: "http://127.0.0.1:1080",
httpsProxy: "http://127.0.0.1:1080",
noProxy: "localhost,*.internal.example.com",
});
expect(process.env.NO_PROXY).to.equal("localhost,*.internal.example.com");
expect(process.env.no_proxy).to.equal("localhost,*.internal.example.com");
});
it("should normalize multiline no_proxy environment variables", () => {
setGlobalProxy({
noProxy: "localhost\n127.0.0.1, 192.168.*\n*.internal.example.com",
});
expect(process.env.NO_PROXY).to.equal("localhost,127.0.0.1,192.168.*,*.internal.example.com");
expect(process.env.no_proxy).to.equal("localhost,127.0.0.1,192.168.*,*.internal.example.com");
});
it("should not change environment variables when creating agents", () => {
process.env.HTTP_PROXY = "http://old-http-proxy";
process.env.HTTPS_PROXY = "http://old-https-proxy";
process.env.NO_PROXY = "old.local";
createAgent({
httpProxy: "http://127.0.0.1:1080",
httpsProxy: "http://127.0.0.1:1081",
});
expect(process.env.HTTP_PROXY).to.equal("http://old-http-proxy");
expect(process.env.HTTPS_PROXY).to.equal("http://old-https-proxy");
expect(process.env.NO_PROXY).to.equal("old.local");
});
it("should bypass global proxy when request host matches no_proxy", async () => {
setGlobalProxy({
httpProxy: "http://127.0.0.1:1080",
httpsProxy: "http://127.0.0.1:1080",
noProxy: "localhost,.internal.example.com",
});
const globalAgents = getGlobalAgents();
const http = createAxiosService({ logger: testLogger }) as HttpClient;
const res = await http.request({
url: "https://api.internal.example.com",
method: "get",
logReq: false,
logRes: false,
adapter: async config => {
return {
config,
data: {
usesGlobalHttpAgent: config.httpAgent === globalAgents.httpAgent,
usesGlobalHttpsAgent: config.httpsAgent === globalAgents.httpsAgent,
},
headers: {},
status: 200,
statusText: "OK",
};
},
});
expect(res).to.deep.equal({
usesGlobalHttpAgent: false,
usesGlobalHttpsAgent: false,
});
});
it("should bypass custom request proxy when request host matches no_proxy", async () => {
setGlobalProxy({
noProxy: ".internal.example.com",
});
const http = createAxiosService({ logger: testLogger }) as HttpClient;
const res = await http.request({
url: "https://api.internal.example.com",
method: "get",
httpProxy: "http://127.0.0.1:1080",
logReq: false,
logRes: false,
adapter: async config => {
return {
config,
data: {
httpAgent: config.httpAgent?.constructor?.name,
httpsAgent: config.httpsAgent?.constructor?.name,
},
headers: {},
status: 200,
statusText: "OK",
};
},
});
expect(res).to.deep.equal({
httpAgent: "Agent",
httpsAgent: "Agent",
});
});
it("should match no_proxy rules", () => {
expect(isNoProxyMatched("*", { hostname: "api.example.com", port: "" })).to.equal(true);
expect(isNoProxyMatched("api.example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
expect(isNoProxyMatched("example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
expect(isNoProxyMatched(".example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
expect(isNoProxyMatched("*.example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
expect(isNoProxyMatched("127.0.0.1", { hostname: "127.0.0.1", port: "" })).to.equal(true);
expect(isNoProxyMatched("192.168.*", { hostname: "192.168.1.10", port: "" })).to.equal(true);
expect(isNoProxyMatched("192.168.*", { hostname: "192.169.1.10", port: "" })).to.equal(false);
expect(isNoProxyMatched("[::1]", { hostname: "::1", port: "" })).to.equal(true);
expect(isNoProxyMatched("[::1]:8443", { hostname: "::1", port: "8443" })).to.equal(true);
expect(isNoProxyMatched("api.example.com:8443", { hostname: "api.example.com", port: "8443" })).to.equal(true);
expect(isNoProxyMatched("api.example.com:8443", { hostname: "api.example.com", port: "443" })).to.equal(false);
expect(isNoProxyMatched("127.0.0.1", { hostname: "127.0.0.2", port: "" })).to.equal(false);
expect(isNoProxyMatched(".example.com", { hostname: "example.org", port: "" })).to.equal(false);
});
});
+174 -28
View File
@@ -82,11 +82,24 @@ export class HttpError extends Error {
export const HttpCommonError = HttpError;
let defaultAgents = createAgent();
const directAgents = createAgent();
let defaultProxyOptions: GlobalProxyOptions = {};
let defaultHeaders: Record<string, string> = {};
export function setGlobalProxy(opts: { httpProxy?: string; httpsProxy?: string }) {
export type GlobalProxyOptions = {
httpProxy?: string;
httpsProxy?: string;
noProxy?: string;
};
export function setGlobalProxy(opts: GlobalProxyOptions) {
logger.info("setGlobalProxy:", opts);
defaultAgents = createAgent(opts);
defaultProxyOptions = { ...opts };
defaultAgents = createAgent({
httpProxy: opts.httpProxy,
httpsProxy: opts.httpsProxy,
});
setProxyEnvironment(opts);
}
export function getGlobalAgents() {
@@ -137,21 +150,25 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
if (config.timeout == null) {
config.timeout = 15000;
}
let agents = defaultAgents;
if (config.skipSslVerify || config.httpProxy) {
let rejectUnauthorized = true;
const bypassProxy = shouldBypassProxy(config, defaultProxyOptions.noProxy);
const useCustomProxy = !!config.httpProxy && !bypassProxy;
let agents = bypassProxy ? directAgents : defaultAgents;
if (bypassProxy) {
logger.info("命中no_proxy配置,跳过代理:", config.url);
}
if (config.skipSslVerify || useCustomProxy) {
const agentOptions: any = {};
if (config.skipSslVerify) {
logger.info("忽略接口请求的SSL校验");
rejectUnauthorized = false;
agentOptions.rejectUnauthorized = false;
}
const proxy: any = {};
if (config.httpProxy) {
if (useCustomProxy) {
logger.info("使用自定义http代理:", config.httpProxy);
proxy.httpProxy = config.httpProxy;
proxy.httpsProxy = config.httpProxy;
agentOptions.httpProxy = config.httpProxy;
agentOptions.httpsProxy = config.httpProxy;
}
agents = createAgent({ rejectUnauthorized, ...proxy } as any);
agents = createAgent(agentOptions);
}
delete config.skipSslVerify;
@@ -354,7 +371,7 @@ export type CreateAgentOptions = {
httpsProxy?: string;
} & nodeHttp.AgentOptions;
export function createAgent(opts: CreateAgentOptions = {}) {
opts = merge(
const { httpProxy, httpsProxy, ...agentOptions } = merge(
{
autoSelectFamily: true,
autoSelectFamilyAttemptTimeout: 1000,
@@ -364,29 +381,19 @@ export function createAgent(opts: CreateAgentOptions = {}) {
);
let httpAgent, httpsAgent;
const httpProxy = opts.httpProxy;
if (httpProxy) {
process.env.HTTP_PROXY = httpProxy;
process.env.http_proxy = httpProxy;
logger.info("use httpProxy:", httpProxy);
httpAgent = new HttpProxyAgent(httpProxy, opts as any);
merge(httpAgent.options, opts);
httpAgent = new HttpProxyAgent(httpProxy, agentOptions as any);
merge(httpAgent.options, agentOptions);
} else {
process.env.HTTP_PROXY = "";
process.env.http_proxy = "";
httpAgent = new nodeHttp.Agent(opts);
httpAgent = new nodeHttp.Agent(agentOptions);
}
const httpsProxy = opts.httpsProxy;
if (httpsProxy) {
process.env.HTTPS_PROXY = httpsProxy;
process.env.https_proxy = httpsProxy;
logger.info("use httpsProxy:", httpsProxy);
httpsAgent = new HttpsProxyAgent(httpsProxy, opts as any);
merge(httpsAgent.options, opts);
httpsAgent = new HttpsProxyAgent(httpsProxy, agentOptions as any);
merge(httpsAgent.options, agentOptions);
} else {
process.env.HTTPS_PROXY = "";
process.env.https_proxy = "";
httpsAgent = new https.Agent(opts);
httpsAgent = new https.Agent(agentOptions);
}
return {
httpAgent,
@@ -394,6 +401,145 @@ export function createAgent(opts: CreateAgentOptions = {}) {
};
}
function setProxyEnvironment(opts: GlobalProxyOptions = {}) {
setEnvValue("HTTP_PROXY", opts.httpProxy);
setEnvValue("http_proxy", opts.httpProxy);
setEnvValue("HTTPS_PROXY", opts.httpsProxy);
setEnvValue("https_proxy", opts.httpsProxy);
const noProxy = normalizeNoProxyText(opts.noProxy);
setEnvValue("NO_PROXY", noProxy);
setEnvValue("no_proxy", noProxy);
}
function setEnvValue(key: string, value?: string) {
process.env[key] = value || "";
}
function shouldBypassProxy(config: AxiosRequestConfig, noProxy?: string) {
if (!noProxy) {
return false;
}
const target = getRequestTarget(config);
if (!target) {
return false;
}
return splitNoProxyRules(noProxy).some(item => isNoProxyMatched(item, target));
}
function getRequestTarget(config: AxiosRequestConfig) {
try {
const baseURL = config.baseURL || undefined;
const url = new URL(config.url || "", baseURL);
return {
hostname: normalizeHost(url.hostname),
port: url.port,
};
} catch (e) {
return null;
}
}
export function isNoProxyMatched(rule: string, target: { hostname: string; port: string }) {
if (rule === "*") {
return true;
}
const normalizedRule = normalizeNoProxyRule(rule);
if (!normalizedRule.host) {
return false;
}
if (normalizedRule.port && normalizedRule.port !== target.port) {
return false;
}
const host = normalizeHost(target.hostname);
if (normalizedRule.host.includes("*")) {
return wildcardHostMatched(normalizedRule.host, host);
}
if (normalizedRule.host.startsWith("*.")) {
const suffix = normalizedRule.host.substring(1);
return host.endsWith(suffix);
}
if (normalizedRule.host.startsWith(".")) {
return host === normalizedRule.host.substring(1) || host.endsWith(normalizedRule.host);
}
return host === normalizedRule.host || host.endsWith(`.${normalizedRule.host}`);
}
function normalizeNoProxyRule(rule: string) {
let value = rule.trim().toLowerCase();
if (value.includes("://")) {
try {
const url = new URL(value);
return {
host: normalizeHost(url.hostname),
port: url.port,
};
} catch (e) {
return {
host: "",
port: "",
};
}
}
let port = "";
if (value.startsWith("[")) {
const closeIndex = value.indexOf("]");
const host = value.substring(1, closeIndex);
const rest = value.substring(closeIndex + 1);
if (rest.startsWith(":")) {
port = rest.substring(1);
}
return {
host: normalizeHost(host),
port,
};
}
const colonCount = (value.match(/:/g) || []).length;
const portIndex = value.lastIndexOf(":");
if (colonCount === 1 && portIndex > -1) {
port = value.substring(portIndex + 1);
value = value.substring(0, portIndex);
}
return {
host: normalizeHost(value),
port,
};
}
function normalizeHost(host: string) {
let value = host.trim().toLowerCase();
if (value.startsWith("[") && value.endsWith("]")) {
value = value.substring(1, value.length - 1);
}
return value;
}
function wildcardHostMatched(rule: string, host: string) {
const pattern = rule.split("*").map(escapeRegExp).join(".*");
return new RegExp(`^${pattern}$`).test(host);
}
function escapeRegExp(value: string) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function normalizeNoProxyText(noProxy?: string) {
return splitNoProxyRules(noProxy).join(",");
}
function splitNoProxyRules(noProxy?: string) {
if (!noProxy) {
return [];
}
return noProxy
.split(/[,\s]+/)
.map(item => item.trim())
.filter(Boolean);
}
export async function download(req: { http: HttpClient; config: HttpRequestConfig; savePath: string; logger: ILogger }) {
const { http, config, savePath, logger } = req;
return safePromise((resolve, reject) => {
+14
View File
@@ -3,6 +3,20 @@
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
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
### Bug Fixes
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.41.0",
"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.0",
"@certd/plus-core": "^1.41.0",
"@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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
@@ -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> = {
+12
View File
@@ -3,6 +3,18 @@
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
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
**Note:** Version bump only for package @certd/lib-huawei
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.41.0",
"version": "1.41.3",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -27,5 +27,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
+12
View File
@@ -3,6 +3,18 @@
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
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
**Note:** Version bump only for package @certd/lib-iframe
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.41.0",
"version": "1.41.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -34,5 +34,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
+12
View File
@@ -3,6 +3,18 @@
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
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
**Note:** Version bump only for package @certd/jdcloud
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.41.0",
"version": "1.41.3",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -59,5 +59,5 @@
"fetch"
]
},
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
+12
View File
@@ -3,6 +3,18 @@
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
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
**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.0",
"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.0",
"@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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
+14
View File
@@ -3,6 +3,20 @@
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
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
### Bug Fixes
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.41.0",
"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.0",
"@certd/basic": "^1.41.0",
"@certd/pipeline": "^1.41.0",
"@certd/plugin-lib": "^1.41.0",
"@certd/plus-core": "^1.41.0",
"@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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
@@ -81,6 +81,7 @@ export class SysPrivateSettings extends BaseSettings {
httpsProxy? = '';
httpProxy? = '';
noProxy? = '';
commonHeaders?: string = '';
reverseProxies?: Record<string, string> = {};
@@ -165,6 +165,7 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
const opts = {
httpProxy: privateSetting.httpProxy,
httpsProxy: privateSetting.httpsProxy,
noProxy: privateSetting.noProxy,
};
setGlobalProxy(opts);
setGlobalHeaders(this.parseKeyValueText(privateSetting.commonHeaders));
@@ -3,6 +3,18 @@
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
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
**Note:** Version bump only for package @certd/midway-flyway-js
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.41.0",
"version": "1.41.3",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -49,5 +49,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
+12
View File
@@ -3,6 +3,18 @@
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
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
**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.0",
"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.0",
"@certd/basic": "^1.41.0",
"@certd/pipeline": "^1.41.0",
"@certd/plugin-lib": "^1.41.0",
"@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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
+14
View File
@@ -3,6 +3,20 @@
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
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
**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.0",
"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.0",
"@certd/basic": "^1.41.0",
"@certd/pipeline": "^1.41.0",
"@certd/plus-core": "^1.41.0",
"@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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cc38ccd0e9eddbd31e0b0401516788e5d9c16d05"
}
@@ -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
+25
View File
@@ -3,6 +3,31 @@
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
* 流水线、监控站点支持导出 ([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))
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
### Bug Fixes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.41.0",
"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.0",
"@certd/pipeline": "^1.41.0",
"@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",
@@ -82,6 +82,7 @@ export default {
pipelineContent: "Pipeline Content",
scheduledTaskCount: "Scheduled Task Count",
deployTaskCount: "Deployment Task Count",
certDomains: "Certificate Domains",
remainingValidity: "Remaining Validity",
effectiveTime: "Effective time",
expiryTime: "Expiry Time",
@@ -41,6 +41,7 @@ export default {
pi: {
validTime: "Piepline Valid Time",
validTimeHelper: "Not filled in means permanent validity",
permanentValid: "Permanent",
},
types: {
certApply: "Cert Apply",
@@ -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 *",
@@ -111,6 +111,9 @@ export default {
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
saveThenTestTitle: "Save first, then click test",
httpsProxyHelper: "Usually both proxies are the same, save first then test",
noProxy: "Proxy Bypass",
noProxyPlaceholder: "localhost,127.0.0.1,.example.com,192.168.*",
noProxyHelper: "Configure NO_PROXY. Separate entries with commas, spaces, or line breaks; matched requests bypass the proxy. \nExample: localhost,127.0.0.1,.example.com,192.168.*",
dualStackNetwork: "Dual Stack Network",
ipv4Priority: "IPv4 Priority",
ipv6Priority: "IPv6 Priority",
@@ -18,6 +18,7 @@ export default {
subdomainConfirmTitle: "子域名确认",
subdomainConfirmContent: "检测到{domain}为子域名,只有托管子域名和免费二级子域名才需要在此处维护,否则会导致申请证书失败,请确认是否继续?",
importFromProvider: "从域名提供商导入",
importFromResolveRecords: "从解析记录导入",
syncExpirationDate: "同步域名过期时间",
syncTaskSubmitted: "同步任务已提交",
syncExpirationProgress: "同步域名过期时间进度",
@@ -86,6 +86,7 @@ export default {
pipelineContent: "流水线内容",
scheduledTaskCount: "定时任务数",
deployTaskCount: "部署任务数",
certDomains: "证书域名",
remainingValidity: "到期剩余",
effectiveTime: "生效时间",
expiryTime: "过期时间",
@@ -41,6 +41,7 @@ export default {
pi: {
validTime: "流水线有效期",
validTimeHelper: "不填则为永久有效",
permanentValid: "永久有效",
},
types: {
certApply: "证书申请",
@@ -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: "托管子域名",
@@ -108,6 +108,9 @@ export default {
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
saveThenTestTitle: "保存后,再点击测试",
httpsProxyHelper: "一般这两个代理填一样的,保存后再测试",
noProxy: "代理排除",
noProxyPlaceholder: "localhost,127.0.0.1,.example.com,192.168.*",
noProxyHelper: "配置NO_PROXY,多个地址可用英文逗号、空格或换行分隔,命中的请求将不走代理\n例如:localhost,127.0.0.1,.example.com,192.168.*",
dualStackNetwork: "双栈网络",
ipv4Priority: "IPV4优先",
ipv6Priority: "IPV6优先",
@@ -107,6 +107,9 @@ function install(app: App, options: any = {}) {
scroll: {
x: 960,
},
rowSelection: {
fixed: "left",
},
size: "small",
pagination: false,
onResizeColumn: (w: number, col: any) => {
@@ -82,6 +82,7 @@ export const certdResources = [
isMenu: true,
icon: "ion:duplicate-outline",
auth: true,
keepAlive: true,
},
},
{
@@ -282,6 +283,7 @@ export const certdResources = [
meta: {
icon: "ion:barcode-outline",
auth: true,
keepAlive: true,
isMenu: true,
},
},
@@ -350,6 +352,7 @@ export const certdResources = [
},
icon: "ion:gift-outline",
auth: true,
keepAlive: true,
},
},
{
@@ -389,7 +392,7 @@ export const certdResources = [
meta: {
show: () => {
const settingStore = useSettingStore();
return settingStore.isComm;
return settingStore.isInviteCommissionEnabled;
},
icon: "ion:gift-outline",
auth: true,
@@ -112,7 +112,7 @@ export const sysResources = [
},
{
title: "certd.sysResources.headerMenus",
name: "HeaderMenus",
name: "SettingsHeaderMenus",
path: "/sys/settings/header-menus",
component: "/sys/settings/header-menus/index.vue",
meta: {
@@ -128,7 +128,7 @@ export const sysResources = [
},
{
title: "certd.sysResources.sysAccess",
name: "SysAccess",
name: "SysAccessManager",
path: "/sys/access",
component: "/sys/access/index.vue",
meta: {
@@ -311,7 +311,7 @@ export const sysResources = [
},
icon: "ion:bag-check",
permission: "sys:settings:edit",
keepAlive: true,
keepAlive: false,
auth: true,
},
},
@@ -105,6 +105,7 @@ export type InviteSetting = {
export type SysPrivateSetting = {
httpProxy?: string;
httpsProxy?: string;
noProxy?: string;
commonHeaders?: string;
reverseProxies?: any;
dnsResultOrder?: string;
@@ -15,6 +15,7 @@ import { resetAllStores, useAccessStore } from "/@/vben/stores";
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
import { request } from "/@/api/service";
import { inviteUtils } from "/@/utils/util.invite";
interface UserState {
userInfo: Nullable<UserInfoRes>;
@@ -66,6 +67,9 @@ export const useUserStore = defineStore({
},
async register(user: RegisterReq) {
await UserApi.register(user);
if (user.inviteCode) {
inviteUtils.clear();
}
notification.success({
message: "注册成功,请登录",
});
@@ -85,6 +89,9 @@ export const useUserStore = defineStore({
let loginRes: any = null;
if (loginType === "sms") {
loginRes = await UserApi.loginBySms(params as SmsLoginReq);
if ((params as SmsLoginReq).inviteCode) {
inviteUtils.clear();
}
} else {
loginRes = await UserApi.login(params as LoginReq);
}
@@ -136,12 +143,12 @@ export const useUserStore = defineStore({
} catch (e) {
console.error("注销登录请求失败:", e);
}
// 第三方登录注销
this.oauthLogout();
}
this.resetState();
resetAllStores();
// 第三方登录注销
await this.oauthLogout();
goLogin && router.push("/login");
mitter.emit("app.logout");
},
@@ -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;
}
}
}
@@ -0,0 +1,24 @@
import { onActivated, onMounted } from "vue";
/**
*
* - KeepAlive onActivated init
* - onMounted onActivated
*/
export function useMounted(init: () => void | Promise<void>) {
let activated = false;
onActivated(() => {
activated = true;
init();
});
onMounted(() => {
// 让 onActivated 有机会先执行;组件未被 KeepAlive 缓存时 onActivated 不会触发,由这里兜底。
setTimeout(() => {
if (!activated) {
init();
}
});
});
}
@@ -41,6 +41,10 @@ export const inviteUtils = {
}
},
clear() {
localStorage.removeItem(INVITE_STORAGE_KEY);
},
captureFromLocation() {
const hashQuery = window.location.hash?.split("?")[1] || "";
const search = window.location.search?.replace(/^\?/, "") || "";
@@ -11,7 +11,8 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted } from "vue";
import { defineComponent } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { createAccessApi } from "/@/views/certd/access/api";
@@ -23,14 +24,7 @@ export default defineComponent({
const { t } = useI18n();
const api = createAccessApi("user");
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api, permission: { isProjectPermission: true } } });
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
return {
crudBinding,
@@ -11,7 +11,8 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted } from "vue";
import { defineComponent } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { createAddonApi } from "./api";
@@ -26,14 +27,7 @@ export default defineComponent({
createCrudOptions,
context: { api, permission: { isProjectPermission: true } },
});
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
return {
crudBinding,
@@ -11,7 +11,8 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted } from "vue";
import { defineComponent } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
@@ -24,14 +25,7 @@ export default defineComponent({
permission: { isProjectPermission: true },
},
});
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
return {
crudBinding,
@@ -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: "CertApplyTemplate",
@@ -25,12 +25,5 @@ const { crudBinding, crudRef, crudExpose } = useFs({
permission: { isProjectPermission: true },
},
});
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
</script>
@@ -25,7 +25,7 @@ const context: any = {
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
onMounted(() => {
crudExpose.doRefresh();
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
@@ -62,7 +62,7 @@ const handleBatchDelete = () => {
//
onMounted(() => {
crudExpose.doRefresh();
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
@@ -62,7 +62,7 @@ const handleBatchDelete = () => {
//
onMounted(() => {
crudExpose.doRefresh();
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
@@ -14,13 +14,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 createCrudOptions from "./crud";
import { useCrudPermission } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
import { useI18n } from "/src/locales";
const { t } = useI18n();
@@ -52,13 +52,6 @@ const handleBatchDelete = () => {
message.error(t("certd.pleaseSelectRecords"));
}
};
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
</script>
<style lang="less"></style>
@@ -300,7 +300,7 @@ async function handleTabChange() {
}
async function refreshInvitePage(autoOpenAgreement = true) {
await settingStore.initOnce();
await settingStore.init();
enabled.value = settingStore.isInviteCommissionEnabled;
loaded.value = true;
if (!enabled.value) {
@@ -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";
import { useI18n } from "/src/locales";
const { t } = useI18n();
@@ -22,12 +22,5 @@ defineOptions({
name: "CertStore",
});
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: { isProjectPermission: true } } });
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
</script>
@@ -22,8 +22,8 @@
<script lang="ts" setup>
import { useFs } from "@fast-crud/fast-crud";
import { onActivated, onMounted } from "vue";
import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
import { useI18n } from "/src/locales";
const { t } = useI18n();
defineOptions({
@@ -37,12 +37,5 @@ const context: any = {
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
const handleBatchDelete = context.handleBatchDelete;
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
</script>
@@ -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>
@@ -27,8 +27,8 @@
<script lang="ts" setup>
import { useFs } from "@fast-crud/fast-crud";
import { onActivated, onMounted } from "vue";
import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
import { useI18n } from "/src/locales";
const { t } = useI18n();
defineOptions({
@@ -42,12 +42,5 @@ const context: any = {
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
const handleBatchDelete = context.handleBatchDelete;
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
</script>
@@ -5,9 +5,9 @@
</template>
<script lang="ts" setup>
import { onActivated, onMounted, ref, Ref } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({
name: "SiteIpCertMonitor",
@@ -21,12 +21,5 @@ const { crudBinding, crudRef, crudExpose } = useFs({
props,
},
});
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
</script>
@@ -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);
}
},
});
};
}
@@ -11,7 +11,8 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted } from "vue";
import { defineComponent } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { createNotificationApi } from "./api";
@@ -23,14 +24,7 @@ export default defineComponent({
const api = createNotificationApi();
notificationProvide(api);
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api, permission: { isProjectPermission: true } } });
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
return {
crudBinding,
@@ -11,21 +11,14 @@
</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";
import { OPEN_API_DOC } from "/@/views/certd/open/openkey/api";
defineOptions({
name: "OpenKey",
});
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: { isProjectPermission: true } } });
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
</script>
@@ -1,4 +1,4 @@
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, useUi } from "@fast-crud/fast-crud";
import { AddReq, ColumnProps, CreateCrudOptionsProps, CreateCrudOptionsRet, DataFormatterContext, DelReq, dict, EditReq, UserPageQuery, UserPageRes, useUi } from "@fast-crud/fast-crud";
import { Modal, notification } from "ant-design-vue";
import dayjs from "dayjs";
import { computed, ref } from "vue";
@@ -75,6 +75,17 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
const projectStore = useProjectStore();
const { myProjectDict } = useDicts();
const DEFAULT_WILL_EXPIRE_DAYS = settingStore.sysPublic.defaultWillExpireDays || settingStore.sysPublic.defaultCertRenewDays || 15;
const pipelineTypeDictData = [
{ value: "cert", label: t("certd.types.certApply") },
{ value: "cert_upload", label: t("certd.types.certUpload") },
{ value: "custom", label: t("certd.types.custom") },
{ value: "template", label: t("certd.types.template") },
{ value: "cert_auto", label: t("certd.types.certApply") },
];
const disabledDictData = [
{ value: false, label: t("certd.fields.enabledLabel") },
{ value: true, label: t("certd.fields.disabledLabel") },
];
function onDialogOpen(opt: any) {
const searchForm = crudExpose.getSearchValidatedFormData();
@@ -84,6 +95,79 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
};
}
function getRecordValue(row: any, key: string) {
return key.split(".").reduce((target, item) => target?.[item], row);
}
function findDictLabel(data: any[], value: any) {
return data.find(item => item.value === value)?.label ?? value;
}
function formatValidTime(value: any) {
if (!value || value <= 0) {
return t("certd.pi.permanentValid");
}
if (value < Date.now()) {
return t("certd.hasExpired");
}
return dayjs(value).format("YYYY-MM-DD");
}
function formatRemainingValidity(lastVars: any) {
const expiresTime = lastVars?.certExpiresTime;
if (!expiresTime) {
return "-";
}
const leftDays = dayjs(expiresTime).diff(dayjs(), "day");
if (leftDays < 0) {
return t("certd.hasExpired");
}
return `${leftDays}${t("certd.days")}`;
}
function formatListValue(value: any) {
if (Array.isArray(value)) {
return value.join(",");
}
return value ?? "";
}
function exportColumnFilter(col: ColumnProps) {
if (!col.key || ["_index", "_selection", "rowHandle"].includes(col.key)) {
return false;
}
if (col.key === "lastVars.certDomains") {
return true;
}
return col.show !== false;
}
function exportDataFormatter(opts: DataFormatterContext) {
const { row, originalRow, col, exportCol } = opts;
const key = col.key;
const value = getRecordValue(originalRow, key);
if (key === "validTime") {
row[key] = formatValidTime(value);
} else if (key === "lastVars") {
row[key] = formatRemainingValidity(value);
} else if (key === "lastVars.certDomains") {
row[key] = formatListValue(value);
} else if (key === "status") {
row[key] = statusUtil.get(value)?.label ?? value;
} else if (key === "disabled") {
row[key] = findDictLabel(disabledDictData, value);
} else if (key === "type") {
row[key] = findDictLabel(pipelineTypeDictData, value);
} else if (key.includes("Time") && value) {
row[key] = dayjs(value).format("YYYY-MM-DD HH:mm:ss");
}
if (col.width) {
exportCol.width = col.width / 10;
}
}
return {
crudOptions: {
request: {
@@ -178,6 +262,18 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
confirmMessage: t("certd.table.confirmDeleteMessage"),
},
},
toolbar: {
buttons: {
export: {
show: true,
},
},
export: {
dataFrom: "search",
columnFilter: exportColumnFilter,
dataFormatter: exportDataFormatter,
},
},
tabs: {
name: "groupId",
show: true,
@@ -419,6 +515,19 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
width: 150,
},
},
"lastVars.certDomains": {
title: t("certd.fields.certDomains"),
type: "text",
form: {
show: false,
},
column: {
width: 260,
show: false,
ellipsis: true,
showTitle: true,
},
},
"lastVars.certEffectiveTime": {
title: t("certd.fields.effectiveTime"),
search: {
@@ -503,10 +612,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
},
},
dict: dict({
data: [
{ value: false, label: t("certd.fields.enabledLabel") },
{ value: true, label: t("certd.fields.disabledLabel") },
],
data: disabledDictData,
}),
form: {
value: false,
@@ -563,13 +669,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
col: { span: 2 },
},
dict: dict({
data: [
{ value: "cert", label: t("certd.types.certApply") },
{ value: "cert_upload", label: t("certd.types.certUpload") },
{ value: "custom", label: t("certd.types.custom") },
{ value: "template", label: t("certd.types.template") },
{ value: "cert_auto", label: t("certd.types.certApply") },
],
data: pipelineTypeDictData,
}),
form: {
show: false,
@@ -650,7 +750,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
align: "center",
cellRender({ value }) {
if (!value || value <= 0) {
return "-";
return t("certd.pi.permanentValid");
}
if (value < Date.now()) {
return t("certd.hasExpired");
@@ -11,7 +11,8 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted } from "vue";
import { defineComponent } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
@@ -24,14 +25,7 @@ export default defineComponent({
permission: { isProjectPermission: true },
},
});
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
return {
crudBinding,
@@ -129,7 +129,7 @@ const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context
//
onMounted(() => {
crudExpose.doRefresh();
// crudExpose.doRefresh();
});
onActivated(async () => {
@@ -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;
@@ -37,7 +37,7 @@ import { useCrudPermission } from "/@/plugin/permission";
const { t } = useI18n();
defineOptions({
name: "CnameRecord",
name: "SubDomain",
});
const context: any = {
permission: {
@@ -68,7 +68,7 @@ const handleBatchDelete = () => {
//
onMounted(() => {
crudExpose.doRefresh();
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
@@ -14,9 +14,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";
import { useI18n } from "/src/locales";
defineOptions({
name: "PipelineTemplate",
@@ -28,11 +28,5 @@ const { crudBinding, crudRef, crudExpose } = useFs({
},
});
const { t } = useI18n();
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
</script>
@@ -28,6 +28,7 @@
<script lang="ts" setup>
import { onActivated, onMounted, Ref, ref } 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";
@@ -117,13 +118,12 @@ onMounted(async () => {
return;
}
await loadProjectDetail();
await crudExpose.doRefresh();
if (migrate === "true") {
openTransferDialog();
}
});
onActivated(async () => {
useMounted(async () => {
await crudExpose.doRefresh();
});
</script>
@@ -53,7 +53,7 @@ const handleBatchDelete = () => {
//
onMounted(() => {
crudExpose.doRefresh();
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
@@ -15,14 +15,14 @@
</template>
<script lang="ts" setup>
import { computed, onActivated, onMounted, ref } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import { computed, ref } from "vue";
import createCrudOptions from "./crud";
import { mySuiteApi, SuiteDetail } from "/@/views/certd/suite/mine/api";
import SuiteCard from "/@/views/framework/home/dashboard/suite-card.vue";
import { useMounted } from "/@/use/use-mounted";
defineOptions({
name: "MySuites",
name: "MySuite",
});
const detail = ref<SuiteDetail>({});
const currentSuite = computed(() => {
@@ -39,11 +39,8 @@ async function loadSuiteDetail() {
}
//
onMounted(async () => {
useMounted(async () => {
await loadSuiteDetail();
await crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
</script>
@@ -19,7 +19,7 @@ const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
//
onMounted(() => {
crudExpose.doRefresh();
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
@@ -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 {
@@ -26,13 +26,14 @@ export async function OauthToken(type: string, validationCode: string) {
});
}
export async function AutoRegister(type: string, code: string) {
export async function AutoRegister(type: string, code: string, inviteCode?: string) {
return await request({
url: apiPrefix + `/autoRegister`,
method: "post",
data: {
validationCode: code,
type,
inviteCode,
},
});
}
@@ -29,6 +29,7 @@ import { useRoute, useRouter } from "vue-router";
import { useUserStore } from "/@/store/user";
import { notification } from "ant-design-vue";
import { useSettingStore } from "/@/store/settings";
import { inviteUtils } from "/@/utils/util.invite";
const route = useRoute();
const router = useRouter();
@@ -99,7 +100,11 @@ async function goBindUser() {
async function autoRegister() {
//
const res = await api.AutoRegister(oauthType, bindCode.value);
const inviteCode = inviteUtils.get();
const res = await api.AutoRegister(oauthType, bindCode.value, inviteCode);
if (inviteCode) {
inviteUtils.clear();
}
//
userStore.onLoginSuccess(res);
//
@@ -215,6 +215,8 @@ export default defineComponent({
const handleFinish = async (values: any) => {
try {
//
userStore.resetState();
await userStore.register(
toRaw({
type: registerType.value,
@@ -11,7 +11,8 @@
</template>
<script lang="ts">
import { defineComponent, onActivated, onMounted } from "vue";
import { defineComponent } from "vue";
import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "../../certd/access/crud";
import { createAccessApi } from "/@/views/certd/access/api";
@@ -21,15 +22,7 @@ export default defineComponent({
setup() {
const api = createAccessApi("sys");
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } });
//
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh();
});
useMounted(() => crudExpose.doRefresh());
return {
crudBinding,

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