Compare commits

...
66 Commits
Author SHA1 Message Date
xiaojunnuo 3c98b974f7 build: 更新 @fast-crud 相关依赖到 1.29.0 版本
style: 修复代码格式、添加缺失逗号和分号以符合规范

refactor: 调整 flex 布局属性和 Tailwind 配置的缩进格式

docs: 移除重复的英文翻译项 "no" 和 "required"
2026-10-13 23:50:03 +08:00
xiaojunnuo e2aff952f9 chore: 添加证书密码字段sql并更新路由标题 2026-10-13 22:31:01 +08:00
xiaojunnuo 9d9ae64aff fix: 修复批量修改证书申请参数,无法选择证书颁发机构的bug 2026-10-13 22:18:56 +08:00
xiaojunnuo 22715a430e perf: 证书申请成功通知支持批量修改
- 新增英文和中文的操作按钮国际化文案(编辑、删除、启用、禁用)
- 优化状态国际化处理逻辑,支持动态加载对应语言的状态文本
- 新增证书申请成功通知批量修改组件和API逻辑
- 修复状态文本显示逻辑,确保正确使用国际化文案
- 优化通知更新逻辑,支持替换指定触发条件的通知规则
2026-10-13 21:37:06 +08:00
xiaojunnuo 949a69f0dc fix(aliyun-cert): 阿里云订阅证书增加申请请求接口 2026-10-13 20:28:16 +08:00
xiaojunnuo 53db97b6f2 chore: i18n 优化 2026-10-12 00:18:36 +08:00
xiaojunnuo cd2dcaa859 chore: 国际化持续完善 2026-10-11 23:28:45 +08:00
xiaojunnuo d0463abc26 perf: 完善插件翻译 2026-10-11 18:24:42 +08:00
xiaojunnuo 8f1b38ad7d perf: 多语言国际化优化,插件支持国际化配置
- 新增钱包模块,包含余额、提现、收益明细等功能
- 为插件、访问授权、任务等添加英文国际化支持
- 重构页面标题为多语言文本,统一国际化展示
- 完善导航、设置等页面的多语言支持
- 修复部分代码格式问题,优化插件国际化处理逻辑
2026-10-11 15:09:52 +08:00
xiaojunnuo e11349b8cc refactor: 重构国际化文案结构,按模块拆分命名空间
调整所有国际化文案的命名空间,将零散的文案归类到对应业务模块下,优化语言文件的组织结构,便于后续维护和扩展。
2026-10-11 13:39:34 +08:00
xiaojunnuo 2394cc6dd9 chore(certd-client): 修复开关选项的字典数据顺序
调整了字典数据的顺序,确保“否”选项在“是”选项之后,符合常规交互逻辑
2026-10-11 13:23:27 +08:00
xiaojunnuo 8209c3d6e1 chore(open-api): 添加返回证书密码开关及多语言支持
在开放接口密钥配置中新增返回证书密码的开关选项,添加中英文多语言提示文本和开关选项文案,明确开关开启时响应会包含证书密码,提醒妥善保管。
2026-10-11 13:22:58 +08:00
xiaojunnuo 3e001b8ba4 perf: 开放接口支持返回证书密码,需要在openKey主动开启该设置,客户端部署证书时可以直接获取密码部署
新增证书密码字段到证书信息和开放密钥表,添加返回开关,开放接口可根据开关决定是否返回证书密码
2026-10-11 12:24:09 +08:00
xiaojunnuo 41ca198116 perf: 插件市场新增同步按钮并调整搜索区域布局 2026-10-04 14:23:51 +08:00
xiaojunnuo 6eca5dad9b chore(wangsu): 修复网宿access getCertList 不支持普通CDN的bug 2026-10-04 00:35:31 +08:00
xiaojunnuo c285522500 chore(wangsu): 新增网宿CDN Pro证书列表获取方法,更新插件说明
新增getCdnProCertList方法用于获取网宿CDN Pro的证书列表,同时更新WangsuRefreshCert插件的描述,明确仅支持普通CDN证书更新,CDN Pro证书需使用对应在线插件。
2026-10-04 00:08:10 +08:00
xiaojunnuo b12f7c54d4 build: release 2026-10-03 02:47:15 +08:00
xiaojunnuo b055bbecfc build: publish 2026-10-03 01:01:10 +08:00
xiaojunnuo a52a6ce8f6 build: trigger build image 2026-10-03 01:01:00 +08:00
xiaojunnuo fcbf1045fe v1.45.0 2026-10-03 01:00:08 +08:00
xiaojunnuo f5cfe054b7 build: prepare to build 2026-10-03 00:51:15 +08:00
xiaojunnuo 5ded5ff2b6 test: 更新测试用例的断言数据和测试描述 2026-10-03 00:51:09 +08:00
xiaojunnuo 8d31346cd3 build: prepare to build 2026-10-03 00:22:38 +08:00
xiaojunnuo 44b41b6a08 chore(certd-server): 同步迁移脚本 2026-10-02 03:17:46 +08:00
xiaojunnuo 4420472f8f perf: 宝塔站点列表支持搜索和排序 2026-10-01 21:49:39 +08:00
xiaojunnuo c4497a7018 chore: 更新DNS持久验证的支持证书类型为Google证书
更新了多个位置的提示文本,将原Let's Encrypt测试环境的说明替换为Google证书,同时调整了验证方式的说明顺序和内容,还修改了插件加载模式为metadata,优化了代码中的格式细节。
2026-10-01 10:47:28 +08:00
xiaojunnuo 03f1a7bd0e chore: 修复获取证书颁发者时未处理空directoryUrl的问题
修改两处代码,在获取DNS持久化颁发者时,当directoryUrl为空时使用accountUri作为备用,避免因缺少必要参数导致的错误
2026-10-01 10:01:41 +08:00
xiaojunnuo 97d2b50074 fix: 支持google persist校验方式 2026-09-30 18:19:23 +08:00
xiaojunnuo 2d0b85b305 fix: 缺失运行时嵌套依赖时添加清除缓存提示
当加载运行时依赖时出现嵌套模块未找到错误,添加明确的错误提示,指导用户前往插件管理页面清除缓存依赖后重试。同时新增单元测试验证该提示逻辑。
2026-09-30 16:34:11 +08:00
xiaojunnuo 28052c9387 chore: clb tencent 2026-09-30 16:22:59 +08:00
xiaojunnuo c4277350bc perf(tencent clb): 修改所有CLB地域的endpoint为就近接入 2026-09-30 15:58:13 +08:00
xiaojunnuo 97160373d6 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-09-30 15:32:57 +08:00
xiaojunnuo 0ce24618a1 docs: 修正HTTPS环境变量命名为小写格式 2026-09-29 15:06:06 +08:00
xiaojunnuo f60e63fbaf perf(monitor): 站点证书监控支持手动选中触发检查 2026-09-20 23:04:49 +08:00
xiaojunnuo 2735feb330 fix(webhook): 修复模版转义换行符,避免手动上传的证书报错
1.  将webhook插件内的replaceTemplate方法迁移到stringUtils公共工具
2.  优化模板替换处理,支持转义更多控制字符和兼容非字符串变量
3.  添加对应的单元测试用例
2026-09-20 23:04:13 +08:00
xiaojunnuo b6381d79fc chore: 批量优化插件开发相关配置与文档
1.  更新gitignore,新增.tmp_chunks忽略项
2.  切换certd插件加载模式为dev
3.  修正插件示例yaml的title注释
2026-09-20 01:26:02 +08:00
xiaojunnuo 241b2e5584 perf: 网宿插件双鉴权支持 2026-09-20 01:25:52 +08:00
xiaojunnuo bb1f232f41 fix: [BUG]使用天翼云CDN部署证书时无法匹配域名
https://github.com/certd/certd/issues/805
2026-09-19 18:05:22 +08:00
xiaojunnuo f82d3a5fcc chore(monitor): 补全站点新增/导入时的IP检查相关布尔默认值
解决批量导入等场景未传ipCheck等字段导致数据库存NULL,编辑弹窗开关过不了校验的issue #803,同时新增对应单测验证默认值和显式传参的正确性
2026-09-19 18:00:15 +08:00
xiaojunnuo bbeaf617d1 perf(open-api): openkey增加用途备注
1. 新增数据库字段存储开放接口密钥备注信息
2. 多语言添加密钥备注相关文案
3. 开放密钥接口支持备注参数传递与CRUD
4. 前端页面支持备注的展示、搜索与编辑
5. 调整插件加载模式为metadata并修复代码格式
2026-09-19 17:41:02 +08:00
xiaojunnuo ea7d14c289 fix(ctyun-cdn): 修复天翼云CDN部署插件的域名选择问题
1.  添加产品类型监听,切换产品类型时重新拉取域名列表
2.  未选择产品类型时抛出明确错误提示
3.  补全域名选项的domain字段以支持证书域名匹配分组
4.  新增单元测试覆盖上述场景
2026-09-18 01:12:36 +08:00
xiaojunnuo 3272a68ff6 style(certd client page): 调整头部布局和VIP按钮展示逻辑
优化页面头部子标题区域的对齐样式,移除非Plus版本的提示文本和条件渲染逻辑,统一展示VIP按钮
2026-09-18 00:18:13 +08:00
xiaojunnuo b5715d99ff Merge branch 'v2-dev' into v2_client 2026-09-17 23:52:49 +08:00
xiaojunnuo 3f5ba14fde ci(workflow): 修正stable-release.yml里版本号示例格式
将版本号示例的v前缀移除,和输入要求保持一致
2026-09-17 23:26:32 +08:00
xiaojunnuo 07f0d3b4d6 fix(config): 修复环境变量加载逻辑并更新配置文件
1.  重构loadDotEnv方法,支持指定加载的env文件路径,增加文件存在校验
2.  跳过注释和空行的环境变量加载,添加加载日志
3.  在默认配置中加载对应环境的env文件
4.  更新开发和生产环境的env配置项
2026-09-17 23:23:35 +08:00
xiaojunnuo 3dbbe72fc1 feat(client): 新增客户端管理功能
实现了客户端管理的完整流程:
1. 新增数据库表与迁移脚本,存储客户端信息与心跳快照
2. 实现服务端心跳上报接口与管理API
3. 新增前端页面与国际化多语言支持
4. 完善客户端在线状态判定逻辑与分页查询功能
2026-09-17 22:46:03 +08:00
xiaojunnuo 7f006d1359 refactor(plugin): 迁移同步插件逻辑到页面组件并优化上下文传递
1. 将plugin crud组件中的syncOnlinePlugins方法迁移到index.vue中
2. 把syncLoading和syncOnlinePlugins加入页面上下文传递给crud组件
3. 移除crud组件中冗余的syncLoading声明
4. 调整页面初始化调用顺序
2026-09-16 00:15:16 +08:00
xiaojunnuo d30f1c4785 refactor(plugin): 优化在线插件同步逻辑与时间格式化
1.  将自动同步相关逻辑抽离到页面组件统一管理
2.  使用dayjs替代原生Date进行时间格式化
3.  移除crud.tsx中冗余的同步配置加载代码
4.  统一维护自动同步的间隔时间常量
2026-09-15 23:52:29 +08:00
xiaojunnuo 96e0122b93 build: release 2026-09-12 20:26:56 +08:00
xiaojunnuo 7786a87b21 refactor(certd-server): 移除getReleaseMode函数,改用配置注入获取发布模式
1.  从环境配置文件中注释移除certd_release_mode的注释,启用稳定发布模式
2.  在AppController和SettingsController中注入release.mode配置替代原有的全局函数获取方式
3.  移除冗余的getReleaseMode工具函数
4.  在SettingsController中添加发布模式的日志打印
2026-09-12 20:22:18 +08:00
xiaojunnuo 34476ba14f build: publish 2026-09-12 19:24:56 +08:00
xiaojunnuo 4b942385a8 build: trigger build image 2026-09-12 19:24:46 +08:00
xiaojunnuo 14809127d6 v1.44.4 2026-09-12 19:23:35 +08:00
xiaojunnuo bd8b251d6e build: prepare to build 2026-09-12 19:17:14 +08:00
xiaojunnuo 5563abb13a fix: 修复证书插件配置无法打开的bug 2026-09-12 19:13:23 +08:00
xiaojunnuo 51e83e4c38 fix: 修复添加步骤会修改所有步骤标题的bug 2026-09-12 18:51:53 +08:00
xiaojunnuo 2b1a5296fe chore(certd-client): fix step form save data overwritten issue and add no payment tip
1. 修复步骤表单保存时因复用reactive对象导致已保存步骤被覆盖的问题,深拷贝currentStep后再传出
2. 新增无支付方式时显示暂不支持在线支付的提示
2026-09-12 18:49:24 +08:00
xiaojunnuo bded368cbb perf: 新增余额管理,管理员充值功能
1. 新增余额管理页面,支持用户余额查询与充值
2. 新增余额变更记录页面,查看用户余额明细
3. 补充多语言国际化文案
4. 修复免费订单显示文案为0元而非免费
5. 隐藏审核页面的操作按钮
6. 注释掉无用的堆日志启动代码
2026-09-12 16:06:53 +08:00
xiaojunnuo 6a649aaca0 perf(acme-account-access): AcmeAccount授权允许修改eab,重新生成account
修改了三处ACME账号表单的禁用判断条件,新增判断当editAccount为true时不禁用控件,允许在编辑已有账号时修改相关配置
2026-09-12 14:44:43 +08:00
xiaojunnuo e971145686 perf(ui): 商业版套餐购买不要求设置支付方式
1. 将套餐设置页面的"需要开启支付方式"提示改为"建议"
2. 新增支付方式为空且余额不足时的红色警告提示
3. 默认开启返利余额使用并修复支付字典加载异常处理
2026-09-12 14:28:52 +08:00
xiaojunnuo bbca449f94 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-09-12 09:32:36 +08:00
xiaojunnuo e6852c2715 docs: 更新AGENTS.md编码规范说明
refactor(plugin-fastly): 简化多行API调用代码
refactor(plugin-serv
2026-09-11 19:01:29 +08:00
xiaojunnuo 47cb523307 chore(cloudflare): 优化权限说明的换行展示
修复Cloudflare插件的权限说明文本排版,将权限项用换行分开展示,提升可读性
2026-09-10 15:13:38 +08:00
xiaojunnuo cbf46b5c48 build: release 2026-09-07 00:07:49 +08:00
xiaojunnuo 7ed3105d97 build: publish 2026-09-06 23:51:23 +08:00
xiaojunnuo 61b945c08f build: trigger build image 2026-09-06 23:51:13 +08:00
652 changed files with 9333 additions and 3065 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ on:
workflow_dispatch:
inputs:
version:
description: "版本号(如 v1.42.5)"
description: "版本号(如 1.42.5)"
required: true
type: string
+2 -1
View File
@@ -42,4 +42,5 @@ output/
.uploads/
.certd-plugin-history/
.tmp
.plugin-dev/
.plugin-dev/
.tmp_chunks/
+62
View File
@@ -23,6 +23,8 @@ version: 1.0.0
- 配置插件的唯一标识、标题、图标和描述
- 继承 `BaseAccess` 类
- 插件默认文案使用中文;需要英文时,在注解参数对象中添加同级 `i18n: { "en-US": { ... } }`,不要写成 `metadata.i18n`。`define` 本身就是插件 metadata。
- `i18n["en-US"]` 仅配置要翻译的展示字段,如 `title`、`desc`、`input`;输入字段可翻译 `title`、`helper`、组件展示文案和 `optionsMapping`。缺少英文翻译时回退默认中文。
3. **定义授权属性**
@@ -392,6 +394,66 @@ export class OAuthDemoAccess extends BaseAccess {
}
```
## 字段动态显隐(mergeScript)
多种鉴权方式共用一个授权时,用 `@AccessInput` 上的 `mergeScript` 按所选方式显隐字段:
```typescript
@AccessInput({
title: '鉴权方式',
value: 'apikey',
component: {
name: 'a-select',
vModel: 'value',
options: [
{ label: 'AccessKey签名', value: 'aksk' },
{ label: 'ApiKey', value: 'apikey' },
],
},
required: true,
})
authType = 'apikey';
@AccessInput({
title: 'accessKeyId',
component: { name: 'a-input', vModel: 'value' },
required: true,
mergeScript: `
return {
show: ctx.compute(({form})=>{
return !form.access.authType || form.access.authType === 'aksk';
})
}
`,
})
accessKeyId = '';
```
要点:
- `mergeScript` 返回的对象会深合并进本字段定义,可动态化的属性有 `show`、`required`、`helper`、
`component` 下的属性(`options`、`placeholder` 等)以及 `component.on` 事件。
- **动态判断必须写在 `ctx.compute(({form})=>{ ... })` 回调里**。`mergeScript` 本身在插件类
定义阶段就被拼成字符串,那时表单还没打开,用参数在服务端判断字段值没有意义。
- 授权表单字段值在 `form.access.xxx`(任务表单才是 `form.xxx`)。
- 历史授权没有新字段,条件里要用 `!form.access.authType || ...` 兜底走旧逻辑,保证老授权还能编辑保存。
- 联动其他字段用 `component.on` 事件回调,回调参数是 `{ form }`:
```typescript
mergeScript: `
return {
component: {
on: {
selectedChange: (scope) => {
const form = scope.form;
form.acmeAccountAccessId = null;
},
},
},
}
`,
```
## 注意事项
1. **插件命名**:插件名称应简洁明了,反映其功能。
+26 -4
View File
@@ -9,7 +9,7 @@ description: 用于通过 Certd API 开发、修改、读取、保存和恢复
本 Skill 使用 Certd HTTP API,不使用 WebSocket,也不依赖浏览器传递代码草稿。
- 前端只生成包含需求、API 地址和认证 Token 的启动提示词。
- 前端只生成包含需求、插件作者、API 地址和认证 Token 的启动提示词。
- Agent 直接调用 Certd API 读取和保存插件 YAML。
- Agent 的进度、日志和代码修改在 Codex/Trae 中查看。
- 插件开发临时文件和修改历史统一保存在 Agent 工作区的 `.plugin-dev/` 下,不保存到浏览器或 Certd 后端。
@@ -43,15 +43,13 @@ Certd 插件按来源分为三类:
## API 认证
提示词会提供 Certd API 地址和仅限 AI 插件开发接口的受限 Token。调用 API 时使用:
提示词会提供 Certd API 地址和仅限 AI 插件开发接口的受限的短时效的 Token。调用 API 时使用:
```http
Authorization: <token>
Content-Type: application/json
```
不要把 Token 写入代码、历史摘要、日志、提交信息或插件 YAML。
所有 Certd API 请求统一使用 Node.js 的 `fetch`。不要使用 PowerShell 的 `Invoke-RestMethod`、`Invoke-WebRequest` 或 .NET HTTP 客户端发送插件 YAML/JSON;它们在 Windows 上可能造成中文乱码或使完整 YAML 导入请求长时间无响应。
Node 请求须直接读取 UTF-8 文件或在 Node 内构造 JSON,并使用 `JSON.stringify`:
@@ -118,6 +116,7 @@ const response = await fetch(`${apiBase}/scoped/sys/ai/plugin/find`, {
- 修改后保存修改摘要。
- 恢复前再次备份当前版本。
- 不上传历史文件,不保存 Token、证书、私钥或真实授权值。
- 修改完一版后立即提交更新到certd,但不要发布到市场。
详细格式见 `references/local-history.md`。
@@ -125,6 +124,7 @@ const response = await fetch(`${apiBase}/scoped/sys/ai/plugin/find`, {
插件始终以完整 YAML 传递和保存,脚本源码放在顶层 `content` 字段。
- 新插件的 `author` 必须使用启动提示词中给出的插件作者,不要自行编造作者名;修改已有插件时保留原 `author`,不要改成其他作者。
- 统一使用 `await _ctx.import(...)` 引用模块。
- `"/@/..."` 表示以绝对路径引用 `server/src/` 下的模块。
- 最后返回继承目标基类的 class。
@@ -135,6 +135,28 @@ const response = await fetch(`${apiBase}/scoped/sys/ai/plugin/find`, {
需要字段格式时读取 `references/online-yaml-format.md`。
需要组件示例时读取 `references/component-examples.md`。
需要按其他字段动态显隐/必填/联动时读取 `references/merge-script.md`。
### 字段动态显隐(mergeScript)
字段定义支持 `mergeScript`:脚本 `return` 的对象会合并进本字段,用来做「选了 A 才显示 B」这类联动。
```yaml
mergeScript: |2-
return {
show: ctx.compute(({form})=>{
return form.access.authType === 'apikey';
})
}
```
- 动态逻辑必须写在 `ctx.compute(({form})=>{ ... })` 回调里;`mergeScript` 本身在插件类定义阶段就
被拼成字符串,那时表单还没打开,用参数在 node 侧判断字段值没有任何意义。
- 授权表单字段值在 `form.access.xxx`,任务表单字段值在 `form.xxx`。
- 新增可选字段时要给历史数据兜底,例如 `!form.access.authType || form.access.authType === 'aksk'`。
完整用法(能力、可动态化的属性、事件联动、常见错误、验证方式)见 `references/merge-script.md`。
## 示例插件
@@ -1,6 +1,6 @@
name: DemoAccess
icon: logos:airflow-icon
title: Demo-授权插件示例 # 模块-插件名
title: Demo-授权插件示例 # 格式:平台-功能 ,例如: 网宿-授权 ; 阿里云-授权 ; 天翼云-授权
group: null
desc: 这只是一个示例
version: 1.0.0
@@ -1,6 +1,6 @@
name: DemoDeploy
icon: logos:amp-icon
title: Demo-部署插件示例 # 模块-插件名
title: Demo-部署插件示例 # 格式:平台-功能 ,例如: 网宿-更新CDNpro证书 ; 阿里云-上传到CAS证书管理服务 ; 天翼云-更新CDN证书
group: cdn
desc: 这仅仅是一个示例
version: 1.0.0
@@ -1,6 +1,6 @@
name: DemoDnsProvider
icon: fa-solid:frog
title: Demo-Dns提供商插件示例 # 模块-插件名
title: Demo-Dns提供商插件示例 # 格式:平台-功能 ,例如: 阿里云-DNS提供商 ;腾讯云-DNS提供商
desc: 这只是一个示例
type: custom
version: 1.0.0
@@ -0,0 +1,181 @@
# mergeScript 用法
来源:前端 `packages/ui/certd-client/src/use/use-refrence.tsx`,类型定义来自 `@fast-crud/fast-crud`。
`mergeScript` 用来让输入字段的定义**依赖当前表单里的其他字段值动态变化**,例如:
选了某个鉴权方式才显示对应字段、切换类型后清空已选值、下拉选项跟着另一个字段变。
它写成字符串放在字段定义上(内置插件放在 `@AccessInput` / `@TaskInput` 里,在线插件放在 `input.<字段>` 下):
```ts
@AccessInput({
title: "API Key",
component: { name: "a-input", vModel: "value" },
mergeScript: `
return {
show: ctx.compute(({form})=>{
//access里面 要用 form.access.[字段名]
return form.access.authType === 'apikey';
//deploy插件里面要用form.[字段名]
return form.authType === 'apikey';
})
}
`,
})
apiKey = "";
```
## 执行时机
字段定义被前端加载时,对**每一个字段**执行一次:
```js
const func = new Function('ctx', formItem.mergeScript);
const merged = func(ctx); // 字段脚本只执行这一次
merge(formItem, merged); // 返回值深合并进该字段的定义
delete formItem.mergeScript; // 执行完就删掉,最终定义里不再有 mergeScript
```
要点:
- 脚本体内**必须 `return` 一个对象**,返回的对象会深合并到本字段的定义上。
- 脚本本身只在加载字段时跑一次,**拿不到、也不应该判断用户填了什么值**。
- 真正的动态逻辑写在返回对象里的 `ctx.compute(...)` 回调中,由 fast-crud 在渲染时求值。
## ctx 提供的能力
| 能力 | 说明 |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `ctx.compute(fn)` | 最常用。返回一个响应式的计算值:`fn` 里读到的表单字段会被自动追踪,字段变化时重新计算,视图跟着刷新 |
`ctx.compute` 的回调参数是一个 context 对象,常用字段:
- `form`:当前表单数据。**这是唯一常用的一个**。
- `row`:行数据。
- `mode`:表单模式 `add` / `edit` / `view`。
- `getComponentRef(key)`:取其他字段组件的 ref。
注意:Certd 在把 `form` 传进回调前做过一次兼容处理(`form.input || form.body || form`),
所以两种表单取值路径不同:
- **授权(Access)表单**:字段值在 `form.access.xxx`(字段 key 会被加上 `access.` 前缀)。
- **任务(Task)表单**:字段值直接是 `form.xxx`。
## 可以动态化的属性
返回的对象里,任何字段定义属性都可以用 `ctx.compute`,常用的有:
| 属性 | 用途 |
| -------------------- | ------------------------------------------------------------------------------------------------- |
| `show` | 显隐(最常用) |
| `required` | 动态必填,例如 `required: ctx.compute(({form}) => form.targetType === 'ingress')` |
| `disabled` | 动态禁用,例如账号已创建后不允许再改:`disabled: ctx.compute(({form}) => !!form.access?.account)` |
| `helper` | 动态帮助文案,回调里按条件 `return` 不同字符串 |
| `value` | 动态默认值 |
| `component` 下的属性 | 动态 `options`、`placeholder`、`inputKey`、`type`、`disabled` 等 |
| `component.on` | 事件处理,见下面「联动」 |
| `col` / `order` | 布局与排序 |
只写了一层 `show` 时最简形式就是本文开头的例子。
## 联动(监听事件并改动其他字段)
`component.on` 的事件回调**不**用 `ctx.compute` 包,它本身就是回调,参数是
`{ form, $event, ... }`;`form` 同样做过 `form.input || form.body || form` 的兼容处理。
事件名按组件而定,例如 `selectedChange`。
```ts
mergeScript: `
return {
component: {
on: {
selectedChange: (scope) => {
const form = scope.form;
// 切换颁发机构后清空 ACME 账号,避免账号与机构不匹配
form.acmeAccountAccessId = null;
},
},
},
}
`,
```
另一种写法是把回调也做成计算值(需要用到组件上配置的 `onSelectedChange` / `onChange` 属性):
```ts
mergeScript: `
return {
component: {
onSelectedChange: ctx.compute(({form}) => {
return ($event) => {
form.dnsProviderAccessType = $event.accessType;
};
}),
},
}
`,
```
两种写法都能用,优先参考同目录已有插件的写法,保持一致。
## 常见错误
- **不要用 node 侧变量决定条件**。`mergeScript` 是在插件类定义阶段(node 侧)拼出来的字符串,
此时表单还没打开,拿不到任何用户输入;用参数拼条件等于把逻辑写死在服务端。
条件必须原样落到 `ctx.compute` 回调里。
- **不要用 `if` 直接判断字段值**。回调必须 `return` 一个值,写成
`show: ctx.compute(({form}) => { if (...) { return true } })` 也可以,但不要漏掉 `return`。
- **不要漏掉历史数据兼容**。新增可选字段时,历史数据里没有这个字段(值为 `undefined`),
条件要按旧行为兜底,例如 `!form.access.authType || form.access.authType === 'aksk'`。
- **不要访问 `form.access` 以外的路径**去读授权字段,也不要在授权表单里按任务表单的路径取值。
- **不要依赖脚本执行时的副作用**(打印日志、改全局状态),它在每次打开表单时都会重新执行。
## YAML 写法(在线插件)
`mergeScript` 是含换行的脚本字符串,必须用 YAML 块标量:
```yaml
input:
apiKey:
title: API Key
component:
name: a-input
vModel: value
mergeScript: |2-
return {
show: ctx.compute(({form})=>{
return form.access.authType === 'apikey';
})
}
```
导出生成的缩进比较特殊(块标量内容统一缩进),**不要手工调整缩进**:
用脚本生成 YAML,并保证导出后再导入内容一致。转换成 block scalar 时注意:
- `|` / `|2-` 等块标量不需要转义换行,不要把脚本写成带 `\n` 的单行。
- 脚本里不要出现 `??` 这类容易被误判为乱码的写法,用显式 `if (x == null)` 代替。
## 验证方式
前端表单没法在插件开发流程里直接断言,可以在后端加一个 `src/**/*.test.ts`,
按前端 `useReference` 的路径执行字段上的 `mergeScript`:
```ts
const computedGetters = [];
const ctx = {
compute: getter => {
computedGetters.push(getter);
return getter;
},
asyncCompute: () => undefined,
computed: () => undefined,
};
new Function('ctx', mergeScript)(ctx);
// 用假的 form 求值,断言显隐结果
const show = computedGetters[0]({ form: { access: { authType: 'apikey' } } });
```
参考实现:`packages/ui/certd-server/src/plugins/plugin-wangsu/access-input.test.ts`。
要点是断言「同一个 show 函数换 `authType` 结果会变」,证明显隐确实是运行时算的,
而不是在 node 侧定死的。
@@ -7,7 +7,7 @@
```yaml
name: DemoTask
author: demo
title: Demo 任务
title: Demo-插件名称 # 格式:平台-功能 ,例如: 网宿-更新CDNpro证书 ; 阿里云-上传到CAS证书管理服务 ; 天翼云-更新CDN证书
desc: 插件说明
icon: clarity:plugin-line
pluginType: task
@@ -61,6 +61,8 @@ content: |
### 编辑规则
- 修改已有插件时保留 `name`、`author`、`pluginType` 和已有兼容字段。
- 新插件的 `author` 使用启动提示词中给出的插件作者,不要自行编造作者名;启动提示词未给出作者时先向用户确认。
- `fullName` 由 `author/name` 组成,作者变化会导致插件身份变化,不要随意修改。
- 只修改需求涉及的字段,避免删除未知的 YAML 字段。
- 脚本过长时仍放在同一个 `content` block scalar 中。
- 提交前检查 YAML 可解析、`content` 非空、版本和插件类型没有被意外修改。
@@ -57,4 +57,41 @@ return class DemoAccess extends BaseAccess {
- `onTestRequest` 应调用实际 API 方法并在失败时抛出异常。
- 对外 API 方法应统一处理分页、错误和返回字段。
- 使用 `this.logger` 或框架提供的 logger,禁止 `console.log`。
- 一般将API接口方法封装到Access中,其他plugin调用access的方法当做client sdk使用
- 一般将API接口方法封装到Access中,其他plugin调用access的方法当做client sdk使用
## 字段动态显隐
多种鉴权方式(例如「ApiKey」和「AccessKey签名」)共用一个授权时,
用字段上的 `mergeScript` 按所选方式显隐对应字段,只让用户看到当前方式需要的字段:
```yaml
input:
authType:
title: 鉴权方式
value: apikey
component:
name: a-select
vModel: value
options:
- { label: AccessKey签名, value: aksk }
- { label: ApiKey, value: apikey }
accessKeyId:
title: accessKeyId
component:
name: a-input
vModel: value
mergeScript: |2-
return {
show: ctx.compute(({form})=>{
return !form.access.authType || form.access.authType === 'aksk';
})
}
```
- 判断必须写在 `ctx.compute(({form})=>{ ... })` 回调里;脚本本身在插件定义阶段就拼好了,
那时表单还没打开,用参数在服务端判断字段值没有意义。
- 授权表单的字段值在 `form.access.xxx`(任务表单才是 `form.xxx`)。
- 历史授权没有 `authType` 字段,条件里要用 `!form.access.authType || ...` 兜底走旧逻辑。
完整用法见父 Skill 的 `references/merge-script.md`。
+2
View File
@@ -30,6 +30,8 @@ version: 1.0.0
- 指定对应的云平台的 access 类型名称
- 设置排序值(可选)
- 继承 `AbstractDnsProvider` 类
- 插件默认文案使用中文;需要英文时,在注解参数对象中添加同级 `i18n: { "en-US": { ... } }`,不要写成 `metadata.i18n`。`define` 本身就是插件 metadata。
- `i18n["en-US"]` 仅配置要翻译的展示字段,如 `title`、`desc`、`input`;输入字段可翻译 `title`、`helper`、组件展示文案和 `optionsMapping`。缺少英文翻译时回退默认中文。
4. **实现 onInstance 方法**
+2
View File
@@ -28,6 +28,8 @@ version: 1.0.0
- 设置插件分组
- 配置默认策略(如 `SkipWhenSucceed`)
- 确保类名与插件名称一致
- 插件默认文案使用中文;需要英文时,在注解参数对象中添加同级 `i18n: { "en-US": { ... } }`,不要写成 `metadata.i18n`。`define` 本身就是插件 metadata。
- `i18n["en-US"]` 仅配置要翻译的展示字段,如 `title`、`desc`、`group`、`input`、`output`;未配置的字段自动回退默认中文。不要翻译插件标识、组件类型、默认值或执行参数。
3. **定义任务输入参数**
+1 -1
View File
@@ -75,7 +75,7 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web
- 只有真正机械化的大批量替换、格式化或生成任务才考虑脚本/工具。若必须使用临时脚本,应放在临时目录并在同一个受控步骤内完成创建、执行、删除;不要把临时脚本落在仓库里跨多步工具调用执行。
- 不要主动运行 `pnpm install`;缺依赖、TTY、网络导致安装或测试失败时,停止尝试并说明环境问题。
- 优先沿用现有模块、插件、service、页面模式;不要为形式上的复用制造过度抽象。
- 代码可读性优先于短写法。尽量少用三元表达式,优先使用完整的 `if/else`;复杂条件、链式调用、内联对象和多层 helper 调用要拆成命名清晰的中间变量或小方法。
- 【重要重要重要】代码可读性优先于短写法。尽量少用三元表达式,优先使用完整的 `if/else`【其他技能与此条要求冲突的,以此条要求为准】;复杂条件、链式调用、内联对象和多层 helper 调用要拆成命名清晰的中间变量或小方法。
- 方法调用链不要直接塞进另一个方法参数;先用有意义的局部变量承接返回值,再传入下一步。
- 不要在单一表达式内嵌套分支、对象构造与方法调用。优先使用清晰的 `if/else` 分支;仅在确实能降低复杂度时才提取有意义的中间变量,避免为拆分而增加阅读跳转。
- 注释优先使用中文,尤其是业务规则、兼容逻辑、协议细节和隐藏风险;文件已有英文风格或引用外部术语时可保持一致。
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
### Bug Fixes
* [BUG]使用天翼云CDN部署证书时无法匹配域名 ([bb1f232](https://github.com/certd/certd/commit/bb1f232f415c522851dede979f5d609dda844fd3))
* 缺失运行时嵌套依赖时添加清除缓存提示 ([2d0b85b](https://github.com/certd/certd/commit/2d0b85b3054fc1aa82b3ab5db34a2a56308f71f2))
* 支持google persist校验方式 ([97d2b50](https://github.com/certd/certd/commit/97d2b5007409ec79ea864d437c473a2f9b5dfae4))
* **config:** 修复环境变量加载逻辑并更新配置文件 ([07f0d3b](https://github.com/certd/certd/commit/07f0d3b4d626628a0e3dfc7e5c52e8c4a98bad47))
* **ctyun-cdn:** 修复天翼云CDN部署插件的域名选择问题 ([ea7d14c](https://github.com/certd/certd/commit/ea7d14c289b8720781bf101bfef59eac7bb03765))
* **webhook:** 修复模版转义换行符,避免手动上传的证书报错 ([2735feb](https://github.com/certd/certd/commit/2735feb3302f8308708dafc8b295adcd4b91d7f0))
### Features
* **client:** 新增客户端管理功能 ([3dbbe72](https://github.com/certd/certd/commit/3dbbe72fc15c114ca6b5e8ff45cb21f258b80650))
### Performance Improvements
* 宝塔站点列表支持搜索和排序 ([4420472](https://github.com/certd/certd/commit/4420472f8fac272d30fb8ccb1cc2ee9763989001))
* 网宿插件双鉴权支持 ([241b2e5](https://github.com/certd/certd/commit/241b2e55848471e61b5d2be0c87a7dc9a0d29557))
* **monitor:** 站点证书监控支持手动选中触发检查 ([f60e63f](https://github.com/certd/certd/commit/f60e63fbaf00460d5050cd7705bd75d57dec202d))
* **open-api:** openkey增加用途备注 ([bbeaf61](https://github.com/certd/certd/commit/bbeaf617d19750515f6eca51863e74e23bc1de50))
* **tencent clb:** 修改所有CLB地域的endpoint为就近接入 ([c427735](https://github.com/certd/certd/commit/c4277350bcc8cf034cf4e76db2a6daa1842bc5d7))
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
### Bug Fixes
* 修复添加步骤会修改所有步骤标题的bug ([51e83e4](https://github.com/certd/certd/commit/51e83e4c38f7f319314fdc38ef33cf5720b2dde0))
* 修复证书插件配置无法打开的bug ([5563abb](https://github.com/certd/certd/commit/5563abb13a62140f4dee668ab768cf4ff9371ddb))
### Performance Improvements
* 新增余额管理,管理员充值功能 ([bded368](https://github.com/certd/certd/commit/bded368cbb8974b6937ae260e448c25fae5c33ad))
* **acme-account-access:** AcmeAccount授权允许修改eab,重新生成account ([6a649aa](https://github.com/certd/certd/commit/6a649aaca083ccd3b517f7d676a58220aa33b37d))
* **ui:** 商业版套餐购买不要求设置支付方式 ([e971145](https://github.com/certd/certd/commit/e9711456860d7db40914a0c295534f7bca56f83c))
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
### Bug Fixes
@@ -0,0 +1 @@
export default { setSuccess: "Settings saved", editTitle: "Edit Certificate Application Template", addTitle: "Add Certificate Application Template", name: "Template Name", default: "Default", setDefault: "Set as Default", disabled: "Disabled", createTime: "Created At", config: "Configuration" };
+47
View File
@@ -3,6 +3,53 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
### Bug Fixes
* [BUG]使用天翼云CDN部署证书时无法匹配域名 ([bb1f232](https://github.com/certd/certd/commit/bb1f232f415c522851dede979f5d609dda844fd3))
* 缺失运行时嵌套依赖时添加清除缓存提示 ([2d0b85b](https://github.com/certd/certd/commit/2d0b85b3054fc1aa82b3ab5db34a2a56308f71f2))
* 支持google persist校验方式 ([97d2b50](https://github.com/certd/certd/commit/97d2b5007409ec79ea864d437c473a2f9b5dfae4))
* **config:** 修复环境变量加载逻辑并更新配置文件 ([07f0d3b](https://github.com/certd/certd/commit/07f0d3b4d626628a0e3dfc7e5c52e8c4a98bad47))
* **ctyun-cdn:** 修复天翼云CDN部署插件的域名选择问题 ([ea7d14c](https://github.com/certd/certd/commit/ea7d14c289b8720781bf101bfef59eac7bb03765))
* **webhook:** 修复模版转义换行符,避免手动上传的证书报错 ([2735feb](https://github.com/certd/certd/commit/2735feb3302f8308708dafc8b295adcd4b91d7f0))
### Features
* **client:** 新增客户端管理功能 ([3dbbe72](https://github.com/certd/certd/commit/3dbbe72fc15c114ca6b5e8ff45cb21f258b80650))
### Performance Improvements
* 宝塔站点列表支持搜索和排序 ([4420472](https://github.com/certd/certd/commit/4420472f8fac272d30fb8ccb1cc2ee9763989001))
* 网宿插件双鉴权支持 ([241b2e5](https://github.com/certd/certd/commit/241b2e55848471e61b5d2be0c87a7dc9a0d29557))
* **monitor:** 站点证书监控支持手动选中触发检查 ([f60e63f](https://github.com/certd/certd/commit/f60e63fbaf00460d5050cd7705bd75d57dec202d))
* **open-api:** openkey增加用途备注 ([bbeaf61](https://github.com/certd/certd/commit/bbeaf617d19750515f6eca51863e74e23bc1de50))
* **tencent clb:** 修改所有CLB地域的endpoint为就近接入 ([c427735](https://github.com/certd/certd/commit/c4277350bcc8cf034cf4e76db2a6daa1842bc5d7))
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
### Bug Fixes
* 修复添加步骤会修改所有步骤标题的bug ([51e83e4](https://github.com/certd/certd/commit/51e83e4c38f7f319314fdc38ef33cf5720b2dde0))
* 修复证书插件配置无法打开的bug ([5563abb](https://github.com/certd/certd/commit/5563abb13a62140f4dee668ab768cf4ff9371ddb))
### Performance Improvements
* 新增余额管理,管理员充值功能 ([bded368](https://github.com/certd/certd/commit/bded368cbb8974b6937ae260e448c25fae5c33ad))
* **acme-account-access:** AcmeAccount授权允许修改eab,重新生成account ([6a649aa](https://github.com/certd/certd/commit/6a649aaca083ccd3b517f7d676a58220aa33b37d))
* **ui:** 商业版套餐购买不要求设置支付方式 ([e971145](https://github.com/certd/certd/commit/e9711456860d7db40914a0c295534f7bca56f83c))
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
### Bug Fixes
* **plugin-service:** 修复同步自己开发的云端插件不显示已安装状态问题 ([dac94b6](https://github.com/certd/certd/commit/dac94b64f0ceb7176e2cf07ce220be0aa5ed98d2))
* **plugin:** 修复插件配置保存与加载的相关问题 ([bd94334](https://github.com/certd/certd/commit/bd9433478a43d5d64ce6370386d0fe0aaff07d2e))
### Performance Improvements
* **plugin:** add Fastly custom TLS certificates integration ([#797](https://github.com/certd/certd/issues/797)) [@szhu25](https://github.com/szhu25) ([2a2e963](https://github.com/certd/certd/commit/2a2e963914c4dfc192cbe0a4bbb9c115e626da01))
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
### Bug Fixes
+2 -2
View File
@@ -5,8 +5,8 @@
`Certd`默认启用https,监听7002端口
如果你想关闭https,或者修改端口,可以在环境变量中配置
```shell
CERTD_HTTPS_ENABLE=true
CERTD_HTTPS_port=7002
certd_https_enabled=true
certd_https_port=7002
```
+1 -1
View File
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.44.3"
"version": "1.45.0"
}
+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.45.0](https://github.com/publishlab/node-acme-client/compare/v1.44.4...v1.45.0) (2026-10-02)
### Bug Fixes
* 支持google persist校验方式 ([97d2b50](https://github.com/publishlab/node-acme-client/commit/97d2b5007409ec79ea864d437c473a2f9b5dfae4))
## [1.44.4](https://github.com/publishlab/node-acme-client/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/acme-client
## [1.44.3](https://github.com/publishlab/node-acme-client/compare/v1.44.2...v1.44.3) (2026-09-06)
**Note:** Version bump only for package @certd/acme-client
+4 -3
View File
@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.44.3",
"version": "1.45.0",
"type": "module",
"module": "./dist/index.js",
"main": "./dist/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.44.3",
"@certd/basic": "^1.45.0",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.9.0",
@@ -27,6 +27,7 @@
"https-proxy-agent": "^7.0.5",
"lodash-es": "^4.17.21",
"node-forge": "^1.3.1",
"psl": "^1.15.0",
"punycode.js": "^2.3.1"
},
"devDependencies": {
@@ -76,5 +77,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
@@ -0,0 +1,21 @@
// @ts-ignore
import psl from "psl";
export function getDnsPersistIssuer(directoryUrl?: string) {
if (!directoryUrl) {
return "letsencrypt.org";
}
let directory: URL;
try {
directory = new URL(directoryUrl);
} catch {
throw new Error(`ACME Directory URL无效,无法生成DNS持久验证Issuer:${directoryUrl}`);
}
const issuer = psl.get(directory.hostname);
if (!issuer) {
throw new Error(`无法从ACME Directory URL获取可注册主域名:${directory.hostname}`);
}
return issuer;
}
+1
View File
@@ -79,3 +79,4 @@ export * from './verify.js'
export * from './error.js'
export * from './util.js'
export * from './dns-persist.js'
+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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
### Bug Fixes
* **webhook:** 修复模版转义换行符,避免手动上传的证书报错 ([2735feb](https://github.com/certd/certd/commit/2735feb3302f8308708dafc8b295adcd4b91d7f0))
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/basic
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**Note:** Version bump only for package @certd/basic
+1 -1
View File
@@ -1 +1 @@
23:46
00:51
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.44.3",
"version": "1.45.0",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -54,5 +54,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
@@ -1,6 +1,21 @@
import dayjs from "dayjs";
export const stringUtils = {
replaceTemplate(target: string, body: Record<string, unknown>, urlEncode = false) {
let bodyStr = target;
for (const key of Object.keys(body)) {
const rawValue = body[key] == null ? "" : String(body[key]);
let value = urlEncode ? encodeURIComponent(rawValue) : rawValue;
value = value.replace(/[\u0000-\u001f]/g, char => {
const escapes: Record<string, string> = { "\b": "\\b", "\f": "\\f", "\n": "\\n", "\r": "\\r", "\t": "\\t" };
return escapes[char] ?? `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`;
});
bodyStr = bodyStr.replaceAll(`\${${key}}`, value);
bodyStr = bodyStr.replaceAll(`{${key}}`, value);
}
return bodyStr;
},
maxLength(str?: string, length = 100) {
if (str) {
return str.length > length ? str.slice(0, length) + "..." : str;
+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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
### Bug Fixes
* 缺失运行时嵌套依赖时添加清除缓存提示 ([2d0b85b](https://github.com/certd/certd/commit/2d0b85b3054fc1aa82b3ab5db34a2a56308f71f2))
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/pipeline
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**Note:** Version bump only for package @certd/pipeline
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.44.3",
"version": "1.45.0",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -21,8 +21,8 @@
"lint": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/basic": "^1.44.3",
"@certd/plus-core": "^1.44.3",
"@certd/basic": "^1.45.0",
"@certd/plus-core": "^1.45.0",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.2.2"
@@ -51,5 +51,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
+1
View File
@@ -19,6 +19,7 @@ export type AccessInputDefine = FormItemProps & {
encrypt?: boolean;
};
export type AccessDefine = Registrable & {
i18n?: Record<string, any>;
icon?: string;
subtype?: string;
dependPlugins?: Record<string, string>;
@@ -44,6 +44,7 @@ export type NotificationDefine = Registrable & {
input?: {
[key: string]: NotificationInputDefine;
};
i18n?: Record<string, any>;
};
export type NotificationInstanceConfig = {
+2
View File
@@ -43,6 +43,7 @@ export type TaskInputDefine = {
} & FormItemProps;
export type PluginDefine = Registrable & {
i18n?: Record<string, any>;
default?: any;
group?: string;
icon?: string;
@@ -97,6 +98,7 @@ export type CertTargetItem = {
export type PipelineEvent<T = any> = {
cert: T;
certPassword?: string;
runnableId: string;
pipelineId: number;
};
@@ -64,6 +64,28 @@ describe("RuntimeDepsService", () => {
assert.equal(mod.value, 42);
});
it("logs a cache clearing hint when a nested runtime dependency is missing", async () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-missing-nested-"));
const packageDir = path.join(rootDir, "node_modules", "runtime-only");
fs.mkdirSync(packageDir, { recursive: true });
fs.writeFileSync(path.join(rootDir, "package.json"), JSON.stringify({ name: "runtime-root", type: "module" }), "utf8");
fs.writeFileSync(path.join(packageDir, "package.json"), JSON.stringify({ name: "runtime-only", main: "index.js" }), "utf8");
fs.writeFileSync(path.join(packageDir, "index.js"), "module.exports = require('missing-nested');\n", "utf8");
const logs: string[] = [];
const logger = {
info() {},
warn() {},
error(message: string, ...args: any[]) {
logs.push([message, ...args.map(item => item?.message || String(item))].join(" "));
},
};
const service = new RuntimeDepsService({ rootDir }, null);
await assert.rejects(() => service.importRuntime("runtime-only", logger));
assert.ok(logs.some(message => message.includes("Cannot find module 'missing-nested'")));
assert.ok(logs.some(message => message.includes("插件管理页面->清除缓存依赖->重启certd")));
});
it("installs configured lazy dependency when import target is missing", async () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-lazy-"));
const service = new RuntimeDepsService({ rootDir, lazyDependencies: { "lazy-pkg": "^1.2.3" } }, null);
+11 -1
View File
@@ -251,6 +251,7 @@ export class RuntimeDepsService {
}
let resolved: string;
let fromRuntimeDeps = false;
try {
//尝试加载本项目依赖
resolved = this.resolveProjectSpecifier(specifier).resolved;
@@ -258,13 +259,22 @@ export class RuntimeDepsService {
if (!this.isModuleNotFoundError(error)) {
throw error;
}
fromRuntimeDeps = true;
resolved = await this.resolveLazyOrInstallSpecifier(specifier, logger);
}
if (!resolved) {
throw new Error(`依赖未安装成功: ${specifier}`);
}
return await import(pathToFileURL(resolved).href);
try {
return await import(pathToFileURL(resolved).href);
} catch (error: any) {
if (fromRuntimeDeps && this.isModuleNotFoundError(error)) {
logger.error?.("动态依赖加载失败:", error);
logger.error?.("动态依赖加载失败,请前往插件管理页面->清除缓存依赖->重启certd后重试");
}
throw error;
}
}
//动态获取模块解析
+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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
**Note:** Version bump only for package @certd/lib-huawei
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/lib-huawei
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**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.44.3",
"version": "1.45.0",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -31,5 +31,5 @@
"prettier": "3.3.3",
"tslib": "^2.8.1"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
+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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
**Note:** Version bump only for package @certd/lib-iframe
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/lib-iframe
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**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.44.3",
"version": "1.45.0",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -37,5 +37,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
+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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
**Note:** Version bump only for package @certd/jdcloud
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/jdcloud
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**Note:** Version bump only for package @certd/jdcloud
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.44.3",
"version": "1.45.0",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -63,5 +63,5 @@
"fetch"
]
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
+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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
**Note:** Version bump only for package @certd/lib-k8s
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/lib-k8s
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**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.44.3",
"version": "1.45.0",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -21,7 +21,7 @@
"lint": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/basic": "^1.44.3",
"@certd/basic": "^1.45.0",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
+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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
### Features
* **client:** 新增客户端管理功能 ([3dbbe72](https://github.com/certd/certd/commit/3dbbe72fc15c114ca6b5e8ff45cb21f258b80650))
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
### Bug Fixes
* 修复证书插件配置无法打开的bug ([5563abb](https://github.com/certd/certd/commit/5563abb13a62140f4dee668ab768cf4ff9371ddb))
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**Note:** Version bump only for package @certd/lib-server
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.44.3",
"version": "1.45.0",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -29,11 +29,11 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.44.3",
"@certd/basic": "^1.44.3",
"@certd/pipeline": "^1.44.3",
"@certd/plugin-lib": "^1.44.3",
"@certd/plus-core": "^1.44.3",
"@certd/acme-client": "^1.45.0",
"@certd/basic": "^1.45.0",
"@certd/pipeline": "^1.45.0",
"@certd/plugin-lib": "^1.45.0",
"@certd/plus-core": "^1.45.0",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
@@ -69,5 +69,5 @@
"typeorm": "^0.3.20",
"typescript": "^5.4.2"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
@@ -133,12 +133,12 @@ export class SysPrivateSettings extends BaseSettings {
// 内置项不在此配置 Directory URL:其端点由运行时按加密算法(pkType)通过 acme.getDirectoryUrl 获取,
// 例如 ZeroSSL、SSL.com 的 RSA 与 EC 证书使用不同的端点,写死单一 URL 会导致 EC 申请走错端点。
customAcmeProviders?: CustomAcmeProvider[] = [
{ sslProvider: "letsencrypt", title: "Let's Encrypt", directoryUrl: "", needEAB: false, builtIn: true },
{ sslProvider: "letsencrypt_staging", title: "Let's Encrypt测试环境", directoryUrl: "", needEAB: false, builtIn: true },
{ sslProvider: "letsencrypt", title: "Let's Encrypt(支持7天IP证书)", directoryUrl: "", needEAB: false, builtIn: true },
{ sslProvider: "google", title: "Google", directoryUrl: "", needEAB: true, builtIn: true },
{ sslProvider: "zerossl", title: "ZeroSSL", directoryUrl: "", needEAB: true, builtIn: true },
{ sslProvider: "litessl", title: "litessl", directoryUrl: "", needEAB: true, builtIn: true },
{ sslProvider: "litessl", title: "Litessl", directoryUrl: "", needEAB: true, builtIn: true },
{ sslProvider: "sslcom", title: "SSL.com", directoryUrl: "", needEAB: true, builtIn: true },
{ sslProvider: "letsencrypt_staging", title: "Let's Encrypt测试环境(只能生成测试证书)", directoryUrl: "", needEAB: false, builtIn: true },
];
removeSecret() {
@@ -174,11 +174,11 @@ export class AccessService extends BaseService<AccessEntity> {
throw new ValidateException("userId不能为空");
}
if (userId !== entity.userId) {
throw new PermissionException("您对该Access授权无访问权限");
throw new PermissionException(`您对该Access授权${entity.name}(${entity.id})无访问权限`);
}
}
if (projectId != null && projectId !== entity.projectId) {
throw new PermissionException("您对该Access授权无访问权限");
throw new PermissionException(`您对该Access授权${entity.name}(${entity.id})无访问权限`);
}
// const access = accessRegistry.get(entity.type);
@@ -26,6 +26,7 @@ export type AddonDefine = Registrable & {
};
showTest?: boolean;
icon?: string;
i18n?: Record<string, any>;
};
export type AddonInstanceConfig = {
@@ -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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**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.44.3",
"version": "1.45.0",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -52,5 +52,5 @@
"typeorm": "^0.3.20",
"typescript": "^5.4.2"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
@@ -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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
**Note:** Version bump only for package @certd/plugin-cert
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/plugin-cert
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**Note:** Version bump only for package @certd/plugin-cert
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.44.3",
"version": "1.45.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -20,7 +20,7 @@
"lint": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/plugin-lib": "^1.44.3"
"@certd/plugin-lib": "^1.45.0"
},
"devDependencies": {
"@types/chai": "^4.3.12",
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
+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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
**Note:** Version bump only for package @certd/plugin-lib
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
**Note:** Version bump only for package @certd/plugin-lib
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
**Note:** Version bump only for package @certd/plugin-lib
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.44.3",
"version": "1.45.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -17,9 +17,9 @@
"lint": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/acme-client": "^1.44.3",
"@certd/basic": "^1.44.3",
"@certd/pipeline": "^1.44.3",
"@certd/acme-client": "^1.45.0",
"@certd/basic": "^1.45.0",
"@certd/pipeline": "^1.45.0",
"dayjs": "^1.11.7",
"jszip": "^3.10.1",
"lodash-es": "^4.17.21",
@@ -45,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "67f3daae625ba734e7e390dd84acc9e3bc1930bc"
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
}
@@ -3,6 +3,7 @@ import { IAccess, IAccessService, IServiceGetter, PageRes, PageSearch, Registrab
export type DnsProviderDefine = Registrable & {
accessType: string;
i18n?: Record<string, any>;
icon?: string;
dependPlugins?: Record<string, string>;
dependPackages?: Record<string, string>;
+23
View File
@@ -3,6 +3,29 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
### Features
* **client:** 新增客户端管理功能 ([3dbbe72](https://github.com/certd/certd/commit/3dbbe72fc15c114ca6b5e8ff45cb21f258b80650))
### Performance Improvements
* **monitor:** 站点证书监控支持手动选中触发检查 ([f60e63f](https://github.com/certd/certd/commit/f60e63fbaf00460d5050cd7705bd75d57dec202d))
* **open-api:** openkey增加用途备注 ([bbeaf61](https://github.com/certd/certd/commit/bbeaf617d19750515f6eca51863e74e23bc1de50))
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
### Bug Fixes
* 修复添加步骤会修改所有步骤标题的bug ([51e83e4](https://github.com/certd/certd/commit/51e83e4c38f7f319314fdc38ef33cf5720b2dde0))
* 修复证书插件配置无法打开的bug ([5563abb](https://github.com/certd/certd/commit/5563abb13a62140f4dee668ab768cf4ff9371ddb))
### Performance Improvements
* 新增余额管理,管理员充值功能 ([bded368](https://github.com/certd/certd/commit/bded368cbb8974b6937ae260e448c25fae5c33ad))
* **ui:** 商业版套餐购买不要求设置支付方式 ([e971145](https://github.com/certd/certd/commit/e9711456860d7db40914a0c295534f7bca56f83c))
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
### Bug Fixes
@@ -13,7 +13,7 @@ const { packages } = getPackagesSync(process.cwd());
const tailwindPackages = [];
packages.forEach((pkg) => {
packages.forEach(pkg => {
// apps目录下和 @vben-core/tailwind-ui 包需要使用到 tailwindcss ui
// if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) {
tailwindPackages.push(pkg.dir);
@@ -25,96 +25,96 @@ const shadcnUiColors = {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
hover: "hsl(var(--accent-hover))",
lighter: "has(val(--accent-lighter))"
lighter: "has(val(--accent-lighter))",
},
background: {
deep: "hsl(var(--background-deep))",
DEFAULT: "hsl(var(--background))"
DEFAULT: "hsl(var(--background))",
},
border: {
DEFAULT: "hsl(var(--border))"
DEFAULT: "hsl(var(--border))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))"
foreground: "hsl(var(--card-foreground))",
},
destructive: {
...createColorsPalette("destructive"),
DEFAULT: "hsl(var(--destructive))"
DEFAULT: "hsl(var(--destructive))",
},
foreground: {
DEFAULT: "hsl(var(--foreground))"
DEFAULT: "hsl(var(--foreground))",
},
input: {
background: "hsl(var(--input-background))",
DEFAULT: "hsl(var(--input))"
DEFAULT: "hsl(var(--input))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))"
foreground: "hsl(var(--muted-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))"
foreground: "hsl(var(--popover-foreground))",
},
primary: {
...createColorsPalette("primary"),
DEFAULT: "hsl(var(--primary))"
DEFAULT: "hsl(var(--primary))",
},
ring: "hsl(var(--ring))",
secondary: {
DEFAULT: "hsl(var(--secondary))",
desc: "hsl(var(--secondary-desc))",
foreground: "hsl(var(--secondary-foreground))"
}
foreground: "hsl(var(--secondary-foreground))",
},
};
const customColors = {
green: {
...createColorsPalette("green"),
foreground: "hsl(var(--success-foreground))"
foreground: "hsl(var(--success-foreground))",
},
header: {
DEFAULT: "hsl(var(--header))"
DEFAULT: "hsl(var(--header))",
},
heavy: {
DEFAULT: "hsl(var(--heavy))",
foreground: "hsl(var(--heavy-foreground))"
foreground: "hsl(var(--heavy-foreground))",
},
main: {
DEFAULT: "hsl(var(--main))"
DEFAULT: "hsl(var(--main))",
},
overlay: {
content: "hsl(var(--overlay-content))",
DEFAULT: "hsl(var(--overlay))"
DEFAULT: "hsl(var(--overlay))",
},
red: {
...createColorsPalette("red"),
foreground: "hsl(var(--destructive-foreground))"
foreground: "hsl(var(--destructive-foreground))",
},
sidebar: {
deep: "hsl(var(--sidebar-deep))",
DEFAULT: "hsl(var(--sidebar))"
DEFAULT: "hsl(var(--sidebar))",
},
success: {
...createColorsPalette("success"),
DEFAULT: "hsl(var(--success))"
DEFAULT: "hsl(var(--success))",
},
warning: {
...createColorsPalette("warning"),
DEFAULT: "hsl(var(--warning))"
DEFAULT: "hsl(var(--warning))",
},
yellow: {
...createColorsPalette("yellow"),
foreground: "hsl(var(--warning-foreground))"
}
foreground: "hsl(var(--warning-foreground))",
},
};
export default {
content: ["./index.html", ...tailwindPackages.map((item) => path.join(item, "src/**/*.{vue,js,ts,jsx,tsx,svelte,astro,html}"))],
content: ["./index.html", ...tailwindPackages.map(item => path.join(item, "src/**/*.{vue,js,ts,jsx,tsx,svelte,astro,html}"))],
darkMode: "selector",
plugins: [animate, typographyPlugin, addDynamicIconSelectors(), enterAnimationPlugin],
prefix: "",
@@ -123,8 +123,8 @@ export default {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px"
}
"2xl": "1400px",
},
},
extend: {
animation: {
@@ -132,64 +132,67 @@ export default {
"accordion-up": "accordion-up 0.2s ease-out",
"collapsible-down": "collapsible-down 0.2s ease-in-out",
"collapsible-up": "collapsible-up 0.2s ease-in-out",
float: "float 5s linear 0ms infinite"
float: "float 5s linear 0ms infinite",
},
flex: {
2: "2 1 0%",
},
animationDuration: {
2000: "2000ms",
3000: "3000ms"
3000: "3000ms",
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
xl: "calc(var(--radius) + 4px)"
xl: "calc(var(--radius) + 4px)",
},
boxShadow: {
float: `0 6px 16px 0 rgb(0 0 0 / 8%),
0 3px 6px -4px rgb(0 0 0 / 12%),
0 9px 28px 8px rgb(0 0 0 / 5%)`
0 9px 28px 8px rgb(0 0 0 / 5%)`,
},
colors: {
...customColors,
...shadcnUiColors
...shadcnUiColors,
},
fontFamily: {
sans: [
"var(--font-family)"
"var(--font-family)",
// ...defaultTheme.fontFamily.sans
]
],
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" }
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" }
to: { height: "0" },
},
"collapsible-down": {
from: { height: "0" },
to: { height: "var(--radix-collapsible-content-height)" }
to: { height: "var(--radix-collapsible-content-height)" },
},
"collapsible-up": {
from: { height: "var(--radix-collapsible-content-height)" },
to: { height: "0" }
to: { height: "0" },
},
float: {
"0%": { transform: "translateY(0)" },
"50%": { transform: "translateY(-20px)" },
"100%": { transform: "translateY(0)" }
}
"100%": { transform: "translateY(0)" },
},
},
zIndex: {
100: "100",
1000: "1000"
}
}
1000: "1000",
},
},
},
safelist: ["dark"]
safelist: ["dark"],
};
function createColorsPalette(name) {
@@ -249,6 +252,6 @@ function createColorsPalette(name) {
// 主色文本激活态
"text-active": `hsl(var(--${name}-700))`,
// 主色文本悬浮态
"text-hover": `hsl(var(--${name}-600))`
"text-hover": `hsl(var(--${name}-600))`,
};
}
@@ -10,6 +10,6 @@ export default {
"postcss-import": {},
"postcss-preset-env": {},
tailwindcss: { config },
"tailwindcss/nesting": {}
}
"tailwindcss/nesting": {},
},
};
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.44.3",
"version": "1.45.0",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -34,11 +34,11 @@
"@aws-sdk/s3-request-presigner": "^3.964.0",
"@certd/vue-js-cron-light": "^4.0.14",
"@ctrl/tinycolor": "^4.1.0",
"@fast-crud/editor-code": "^1.28.7",
"@fast-crud/fast-crud": "^1.28.7",
"@fast-crud/fast-extends": "^1.28.7",
"@fast-crud/ui-antdv4": "^1.28.7",
"@fast-crud/ui-interface": "^1.28.7",
"@fast-crud/editor-code": "^1.29.0",
"@fast-crud/fast-crud": "^1.29.0",
"@fast-crud/fast-extends": "^1.29.0",
"@fast-crud/ui-antdv4": "^1.29.0",
"@fast-crud/ui-interface": "^1.29.0",
"@iconify/tailwind": "^1.2.0",
"@iconify/vue": "^4.1.1",
"@manypkg/get-packages": "^2.2.2",
@@ -104,8 +104,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.44.3",
"@certd/pipeline": "^1.44.3",
"@certd/lib-iframe": "^1.45.0",
"@certd/pipeline": "^1.45.0",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",
@@ -4,6 +4,7 @@ import { errorLog, errorCreate } from "./tools";
import { env } from "/src/utils/util.env";
import { useUserStore } from "/@/store/user";
import { useProjectStore } from "../store/project";
import { i18n } from "/@/locales";
export class CodeError extends Error {
code: number;
@@ -139,6 +140,7 @@ function createRequestFunction(service: any) {
const configDefault: any = {
headers: {
"Content-Type": get(config, "headers.Content-Type", "application/json"),
"Accept-Language": i18n.global.locale.value,
} as any,
timeout: 30000,
baseURL: env.API,
@@ -154,6 +156,10 @@ function createRequestFunction(service: any) {
configDefault.headers.Authorization = token;
}
Object.assign(configDefault, config);
configDefault.headers = {
...configDefault.headers,
"Accept-Language": i18n.global.locale.value,
};
if (!configDefault.params.projectId && projectStore.isEnterprise && !config.url.startsWith("/sys") && !config.url.startsWith("http")) {
configDefault.params.projectId = projectStore.currentProject?.id;
@@ -1,11 +1,11 @@
<template>
<div class="flex captcha-image-input">
<a-input :value="valueRef" :placeholder="t('certd.captcha.inputImageCode')" autocomplete="off" @update:value="onChange">
<a-input :value="valueRef" :placeholder="t('certd.common.captcha.inputImageCode')" autocomplete="off" @update:value="onChange">
<template #prefix>
<fs-icon icon="ion:image-outline"></fs-icon>
</template>
</a-input>
<div class="input-right pointer" :title="t('certd.captcha.refresh')">
<div class="input-right pointer" :title="t('certd.common.captcha.refresh')">
<img class="image-code" :src="imageCodeSrc" @click="resetImageCode" />
</div>
</div>
@@ -4,8 +4,8 @@
<div class="sweep-animation"></div>
<div class="box-content">
<div class="box-icon">✓</div>
<span v-if="modelValue == null" class="status-text">{{ t("certd.captcha.clickToVerify") }}</span>
<span v-else class="status-text">{{ t("certd.captcha.verifySuccess") }}</span>
<span v-if="modelValue == null" class="status-text">{{ t("certd.common.captcha.clickToVerify") }}</span>
<span v-else class="status-text">{{ t("certd.common.captcha.verifySuccess") }}</span>
</div>
</div>
</div>
@@ -58,7 +58,7 @@ function callback(res: { ret: number; ticket: string; randstr: string; errorCode
if (res.errorCode && res.errorCode > 0) {
notification.error({
message: t("certd.captcha.verifyFailed", { message: res.errorMessage || res.errorCode }),
message: t("certd.common.captcha.verifyFailed", { message: res.errorMessage || res.errorCode }),
});
}
@@ -85,13 +85,13 @@ function loadErrorCallback(error: any) {
// errorMessage: "jsload_error",
// });
notification.error({
message: t("certd.captcha.loadFailed", { message: error?.message || error }),
message: t("certd.common.captcha.loadFailed", { message: error?.message || error }),
});
}
async function triggerCaptcha() {
if (!loaded.value) {
notification.error({
message: t("certd.captcha.notLoaded"),
message: t("certd.common.captcha.notLoaded"),
});
return;
}
@@ -5,6 +5,7 @@
<script lang="ts">
import { ref } from "vue";
import * as api from "./api";
import { resolvePluginLocale } from "/@/store/plugin";
export default {
name: "DnsProviderSelector",
@@ -21,12 +22,13 @@ export default {
async function onCreate() {
const list = await api.GetList();
const array: any[] = [];
for (let item of list) {
for (const item of list) {
const localizedItem = resolvePluginLocale(item);
array.push({
value: item.name,
label: item.title,
icon: item.icon,
accessType: item.accessType,
value: localizedItem.name,
label: localizedItem.title,
icon: localizedItem.icon,
accessType: localizedItem.accessType,
});
}
options.value = array;
@@ -4,7 +4,7 @@
<!-- <td class="domain">-->
<!-- {{ props.domain }}-->
<!-- </td>-->
<td class="host-record" :title="t('certd.verifyPlan.domainTitle', { domain: props.domain })">
<td class="host-record" :title="t('certd.certdomain.verifyPlan.domainTitle', { domain: props.domain })">
<fs-copyable v-model="cnameRecord.hostRecord"></fs-copyable>
</td>
<td style="text-align: center">CNAME</td>
@@ -17,18 +17,18 @@
<a-tooltip v-if="cnameRecord.error" :title="cnameRecord.error">
<fs-icon class="ml-5 color-red" icon="ion:warning-outline"></fs-icon>
</a-tooltip>
<a-tooltip v-if="cnameRecord.status === 'valid'" :title="t('certd.verifyPlan.resetStatusTooltip')">
<a-tooltip v-if="cnameRecord.status === 'valid'" :title="t('certd.certdomain.verifyPlan.resetStatusTooltip')">
<fs-icon class="ml-2 color-yellow text-md pointer" icon="solar:undo-left-square-bold" @click="resetStatus"></fs-icon>
</a-tooltip>
</span>
</td>
<td class="center">
<template v-if="cnameRecord.status !== 'valid'">
<a-button type="primary" size="small" :loading="loading" @click="doVerify">{{ t("certd.verifyPlan.clickToValidate") }}</a-button>
<a-button type="primary" size="small" :loading="loading" @click="doVerify">{{ t("certd.certdomain.verifyPlan.clickToValidate") }}</a-button>
<cname-tip :record="cnameRecord"></cname-tip>
</template>
<div v-else class="helper" :title="t('certd.verifyPlan.keepCnameTitle')">{{ t("certd.verifyPlan.keepCname") }}</div>
<div v-else class="helper" :title="t('certd.certdomain.verifyPlan.keepCnameTitle')">{{ t("certd.certdomain.verifyPlan.keepCname") }}</div>
</td>
</tr>
</tbody>
@@ -46,11 +46,11 @@ import { utils } from "/@/utils/index.js";
const { t } = useI18n();
const statusDict = dict({
data: [
{ label: t("certd.verifyPlan.status.pendingCname"), value: "cname", color: "warning" },
{ label: t("certd.verifyPlan.status.validating"), value: "validating", color: "blue" },
{ label: t("certd.verifyPlan.status.valid"), value: "valid", color: "green" },
{ label: t("certd.verifyPlan.status.failed"), value: "failed", color: "red" },
{ label: t("certd.verifyPlan.status.timeout"), value: "timeout", color: "red" },
{ label: t("certd.certdomain.verifyPlan.status.pendingCname"), value: "cname", color: "warning" },
{ label: t("certd.certdomain.verifyPlan.status.validating"), value: "validating", color: "blue" },
{ label: t("certd.certdomain.verifyPlan.status.valid"), value: "valid", color: "green" },
{ label: t("certd.certdomain.verifyPlan.status.failed"), value: "failed", color: "red" },
{ label: t("certd.certdomain.verifyPlan.status.timeout"), value: "timeout", color: "red" },
],
});
@@ -129,8 +129,8 @@ async function doVerify() {
async function resetStatus() {
Modal.confirm({
title: t("certd.verifyPlan.resetStatus"),
content: t("certd.verifyPlan.confirmResetStatus"),
title: t("certd.certdomain.verifyPlan.resetStatus"),
content: t("certd.certdomain.verifyPlan.confirmResetStatus"),
onOk: async () => {
await api.ResetStatus(cnameRecord.value.id);
await loadRecord();
@@ -2,17 +2,17 @@
<a-tooltip :overlay-style="{ maxWidth: '400px' }">
<template #title>
<div>
<div>{{ t("certd.verifyPlan.cnameTip.intro") }}</div>
<div>{{ t("certd.verifyPlan.cnameTip.step1", { domain: record.domain }) }}</div>
<div>{{ t("certd.verifyPlan.cnameTip.step2") }}</div>
<div>{{ t("certd.verifyPlan.cnameTip.step3", { value: record.recordValue }) }}</div>
<div>{{ t("certd.certdomain.verifyPlan.cnameTip.intro") }}</div>
<div>{{ t("certd.certdomain.verifyPlan.cnameTip.step1", { domain: record.domain }) }}</div>
<div>{{ t("certd.certdomain.verifyPlan.cnameTip.step2") }}</div>
<div>{{ t("certd.certdomain.verifyPlan.cnameTip.step3", { value: record.recordValue }) }}</div>
<div>
{{ t("certd.verifyPlan.cnameTip.step4") }}
{{ t("certd.certdomain.verifyPlan.cnameTip.step4") }}
<fs-copyable :style="{ color: '#52c41a' }" :model-value="nslookupCmd"></fs-copyable>
{{ t("certd.verifyPlan.cnameTip.or") }}
{{ t("certd.certdomain.verifyPlan.cnameTip.or") }}
<fs-copyable :style="{ color: '#52c41a' }" :model-value="digCmd"></fs-copyable>
</div>
<div>{{ t("certd.verifyPlan.cnameTip.step5") }}</div>
<div>{{ t("certd.certdomain.verifyPlan.cnameTip.step5") }}</div>
</div>
</template>
<fs-icon class="ml-5 pointer" icon="mingcute:question-line"></fs-icon>
@@ -2,11 +2,11 @@
<table class="cname-verify-plan">
<thead>
<tr>
<td class="col-host">{{ t("certd.verifyPlan.hostRecord") }}</td>
<td class="col-type center">{{ t("certd.verifyPlan.recordType") }}</td>
<td class="col-value">{{ t("certd.verifyPlan.setCnameRecord") }}</td>
<td class="col-status center">{{ t("certd.status") }}</td>
<td class="col-action center">{{ t("certd.verifyPlan.operation") }}</td>
<td class="col-host">{{ t("certd.certdomain.verifyPlan.hostRecord") }}</td>
<td class="col-type center">{{ t("certd.certdomain.verifyPlan.recordType") }}</td>
<td class="col-value">{{ t("certd.certdomain.verifyPlan.setCnameRecord") }}</td>
<td class="col-status center">{{ t("certd.common.status") }}</td>
<td class="col-action center">{{ t("certd.certdomain.verifyPlan.operation") }}</td>
</tr>
</thead>
<template v-for="key in domains" :key="key">
@@ -2,10 +2,10 @@
<table class="http-verify-plan">
<thead>
<tr>
<td style="width: 160px">{{ t("certd.verifyPlan.websiteDomain") }}</td>
<td style="width: 100px; text-align: center">{{ t("certd.verifyPlan.uploadMethod") }}</td>
<td style="width: 150px">{{ t("certd.verifyPlan.uploadAccess") }}</td>
<td style="width: 200px">{{ t("certd.verifyPlan.websiteRootPath") }}</td>
<td style="width: 160px">{{ t("certd.certdomain.verifyPlan.websiteDomain") }}</td>
<td style="width: 100px; text-align: center">{{ t("certd.certdomain.verifyPlan.uploadMethod") }}</td>
<td style="width: 150px">{{ t("certd.certdomain.verifyPlan.uploadAccess") }}</td>
<td style="width: 200px">{{ t("certd.certdomain.verifyPlan.websiteRootPath") }}</td>
</tr>
</thead>
<tbody v-if="records" class="http-record-body">
@@ -21,7 +21,7 @@
<access-selector v-model="item.httpUploaderAccess" :type="item.httpUploaderType" @change="onRecordChange"></access-selector>
</td>
<td>
<a-input v-model:value="item.httpUploadRootDir" :placeholder="t('certd.verifyPlan.websiteRootPlaceholder')" @change="onRecordChange"></a-input>
<a-input v-model:value="item.httpUploadRootDir" :placeholder="t('certd.certdomain.verifyPlan.websiteRootPlaceholder')" @change="onRecordChange"></a-input>
</td>
</tr>
</template>
@@ -5,7 +5,7 @@
<div class="plan-box bg-white dark:bg-neutral-700">
<div class="fullscreen-button pointer flex-center" @click="fullscreen = !fullscreen">
<span v-if="!fullscreen" style="font-size: 10px" class="flex-center">
{{ t("certd.verifyPlan.expandTip") }}
{{ t("certd.certdomain.verifyPlan.expandTip") }}
<fs-icon icon="ion:arrow-forward-outline"></fs-icon>
</span>
<fs-icon :icon="fullscreen ? 'material-symbols:fullscreen-exit' : 'material-symbols:fullscreen'"></fs-icon>
@@ -13,9 +13,9 @@
<table class="plan-table">
<thead>
<tr>
<th style="min-width: 100px">{{ t("certd.verifyPlan.mainDomain") }}</th>
<th>{{ t("certd.verifyPlan.challengeType") }}</th>
<th>{{ t("certd.verifyPlan.challengePlan") }}</th>
<th style="min-width: 100px">{{ t("certd.certdomain.verifyPlan.mainDomain") }}</th>
<th>{{ t("certd.certdomain.verifyPlan.challengeType") }}</th>
<th>{{ t("certd.certdomain.verifyPlan.challengePlan") }}</th>
</tr>
</thead>
<tbody>
@@ -30,13 +30,13 @@
<div class="plan">
<div v-if="item.type === 'dns'" class="plan-dns">
<div class="form-item">
<span class="label">{{ t("certd.verifyPlan.dnsType") }}:</span>
<span class="label">{{ t("certd.certdomain.verifyPlan.dnsType") }}:</span>
<span class="input">
<fs-dict-select
v-model:value="item.dnsProviderType"
size="small"
:dict="dnsProviderTypeDict"
:placeholder="t('certd.verifyPlan.dnsProvider')"
:placeholder="t('certd.certdomain.verifyPlan.dnsProvider')"
@change="onPlanChanged"
@selected-change="onDnsProviderChange(item, $event)"
></fs-dict-select>
@@ -44,13 +44,13 @@
</div>
<a-divider type="vertical" />
<div class="form-item">
<span class="label">{{ t("certd.verifyPlan.dnsAccess") }}:</span>
<span class="label">{{ t("certd.certdomain.verifyPlan.dnsAccess") }}:</span>
<span class="input">
<access-selector
v-model="item.dnsProviderAccessId"
size="small"
:type="item.dnsProviderAccessType || item.dnsProviderType"
:placeholder="t('certd.verifyPlan.pleaseSelect')"
:placeholder="t('certd.certdomain.verifyPlan.pleaseSelect')"
@change="onPlanChanged"
></access-selector>
</span>
@@ -70,7 +70,7 @@
</div>
<div v-if="item.type === 'http'" class="plan-http">
<http-verify-plan v-model="item.httpVerifyPlan" @change="onPlanChanged" />
<div class="helper">{{ t("certd.verifyPlan.httpHelper") }}</div>
<div class="helper">{{ t("certd.certdomain.verifyPlan.httpHelper") }}</div>
</div>
</div>
</td>
@@ -105,7 +105,7 @@ const { t } = useI18n();
const challengeTypeOptions = ref<any[]>([
{
label: t("certd.verifyPlan.dnsChallenge"),
label: t("certd.certdomain.verifyPlan.dnsChallenge"),
value: "dns",
},
{
@@ -113,11 +113,11 @@ const challengeTypeOptions = ref<any[]>([
value: "dns-persist",
},
{
label: t("certd.verifyPlan.cnameChallenge"),
label: t("certd.certdomain.verifyPlan.cnameChallenge"),
value: "cname",
},
{
label: t("certd.verifyPlan.httpChallenge"),
label: t("certd.certdomain.verifyPlan.httpChallenge"),
value: "http",
},
]);
@@ -14,7 +14,7 @@ function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
for (const subDomain of subDomains) {
const plan = value[domain].cnameVerifyPlan[subDomain];
if (plan.status !== "valid") {
throw new Error($t("certd.verifyPlan.errors.cnameNotValid", { domain: subDomain }));
throw new Error($t("certd.certdomain.verifyPlan.errors.cnameNotValid", { domain: subDomain }));
}
}
}
@@ -23,7 +23,7 @@ function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
for (const item of domains) {
//如果有通配符域名则不允许使用http校验
if (item.startsWith("*.")) {
throw new Error($t("certd.verifyPlan.errors.wildcardNotSupportHttp", { domain: item }));
throw new Error($t("certd.certdomain.verifyPlan.errors.wildcardNotSupportHttp", { domain: item }));
}
}
@@ -32,19 +32,19 @@ function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
for (const subDomain of subDomains) {
const plan = value[domain].httpVerifyPlan[subDomain];
if (!plan.httpUploaderType) {
throw new Error($t("certd.verifyPlan.errors.uploadMethodRequired", { domain: subDomain }));
throw new Error($t("certd.certdomain.verifyPlan.errors.uploadMethodRequired", { domain: subDomain }));
}
if (!plan.httpUploaderAccess) {
throw new Error($t("certd.verifyPlan.errors.uploadAccessRequired", { domain: subDomain }));
throw new Error($t("certd.certdomain.verifyPlan.errors.uploadAccessRequired", { domain: subDomain }));
}
if (!plan.httpUploadRootDir) {
throw new Error($t("certd.verifyPlan.errors.websiteRootRequired", { domain: subDomain }));
throw new Error($t("certd.certdomain.verifyPlan.errors.websiteRootRequired", { domain: subDomain }));
}
}
}
} else if (type === "dns") {
if (!value[domain].dnsProviderType || !value[domain].dnsProviderAccessId) {
throw new Error($t("certd.verifyPlan.errors.dnsProviderRequired", { domain }));
throw new Error($t("certd.certdomain.verifyPlan.errors.dnsProviderRequired", { domain }));
}
} else if (type === "dns-persist") {
const subDomains = Object.keys(value[domain].dnsPersistVerifyPlan || {});
@@ -1,7 +1,7 @@
<template>
<div class="api-test">
<div>
<fs-button :loading="loading" type="primary" :text="t('certd.pluginCommon.test')" icon="ion:refresh-outline" @click="doTest"></fs-button>
<fs-button :loading="loading" type="primary" :text="t('certd.common.pluginCommon.test')" icon="ion:refresh-outline" @click="doTest"></fs-button>
</div>
<div class="helper" :class="{ error: hasError }">
@@ -64,14 +64,14 @@ const doTest = async () => {
{
onError(err: any) {
hasError.value = true;
message.value = t("certd.pluginCommon.errorWithMessage", { message: err.message });
message.value = t("certd.common.pluginCommon.errorWithMessage", { message: err.message });
},
showErrorNotify: false,
}
);
message.value = t("certd.pluginCommon.testRequestSuccess");
message.value = t("certd.common.pluginCommon.testRequestSuccess");
if (res) {
message.value += t("certd.pluginCommon.responseSuffix", { response: JSON.stringify(res) });
message.value += t("certd.common.pluginCommon.responseSuffix", { response: JSON.stringify(res) });
}
} finally {
loading.value = false;
@@ -42,7 +42,7 @@ function findStepFromPipeline(targetStepId: string) {
const errorRef = ref("");
function getStepIdFromInputKey(inputKey: string) {
if (!inputKey) {
errorRef.value = t("certd.pluginCommon.selectCertFirst");
errorRef.value = t("certd.common.pluginCommon.selectCertFirst");
return;
}
return inputKey.split(".")[1];
@@ -51,7 +51,7 @@ function getDomainFromPipeline(inputKey: string) {
let targetStepId = getStepIdFromInputKey(inputKey);
let certStep = findStepFromPipeline(targetStepId);
if (!certStep) {
errorRef.value = t("certd.pluginCommon.targetStepNotFound");
errorRef.value = t("certd.common.pluginCommon.targetStepNotFound");
return;
}
@@ -60,7 +60,7 @@ function getDomainFromPipeline(inputKey: string) {
targetStepId = getStepIdFromInputKey(firstLevelValue);
certStep = findStepFromPipeline(targetStepId);
if (!certStep) {
errorRef.value = t("certd.pluginCommon.targetStepNotFound");
errorRef.value = t("certd.common.pluginCommon.targetStepNotFound");
return;
}
}
@@ -1,4 +1,4 @@
<template>
<template>
<div class="domain-select">
<div class="flex flex-row">
<a-select
@@ -18,12 +18,12 @@
<template v-if="search">
<div class="flex w-full items-center justify-between flex-wrap" style="padding: 4px 8px">
<div class="flex-1 flex flex-row items-center">
<a-input ref="inputRef" v-model:value="searchKeyRef" class="flex-1" allow-clear :placeholder="t('certd.pluginCommon.domainSearchPlaceholder')" @keydown.enter="doSearch" />
<fs-button type="primary" class="m-1" :loading="loading" icon="mingcute:search-2-line" @click="doSearch">{{ t("certd.pluginCommon.search") }}</fs-button>
<a-input ref="inputRef" v-model:value="searchKeyRef" class="flex-1" allow-clear :placeholder="t('certd.common.pluginCommon.domainSearchPlaceholder')" @keydown.enter="doSearch" />
<fs-button type="primary" class="m-1" :loading="loading" icon="mingcute:search-2-line" @click="doSearch">{{ t("certd.common.pluginCommon.search") }}</fs-button>
</div>
<div class="manager flex flex-row items-center">
<fs-button type="primary" class="m-1" icon="mingcute:vip-1-line" @click="openDomainImportDialog">{{ t("certd.pluginCommon.importDomain") }}</fs-button>
<fs-button class="m-1" type="primary" icon="carbon:gui-management" @click="openDomainManager">{{ t("certd.pluginCommon.manageDomain") }}</fs-button>
<fs-button type="primary" class="m-1" icon="mingcute:vip-1-line" @click="openDomainImportDialog">{{ t("certd.common.pluginCommon.importDomain") }}</fs-button>
<fs-button class="m-1" type="primary" icon="carbon:gui-management" @click="openDomainManager">{{ t("certd.common.pluginCommon.manageDomain") }}</fs-button>
</div>
</div>
<div v-if="hasError" class="helper p-2" :class="{ error: hasError }">
@@ -49,7 +49,7 @@
</a-select>
<div class="ml-5 flex flex-row items-center">
<fs-button title="复制全部域名" icon="ion:copy-outline" class="domain-select-copy-icon mr-5" @click="copySelectedDomains" />
<fs-button :loading="loading" :title="t('certd.pluginCommon.refreshMyDomains')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
<fs-button :loading="loading" :title="t('certd.common.pluginCommon.refreshMyDomains')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
</div>
</div>
<div class="helper" :class="{ error: hasError }">
@@ -4,7 +4,7 @@
<a-auto-complete class="remote-auto-complete-input" :filter-option="filterOption" :options="optionsRef" :value="value" v-bind="attrs" @click="onClick" @update:value="emit('update:value', $event)">
</a-auto-complete>
<div class="ml-5">
<fs-button :loading="loading" :title="t('certd.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
<fs-button :loading="loading" :title="t('certd.common.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
</div>
</div>
<div class="helper" :class="{ error: hasError }">
@@ -96,16 +96,16 @@ const getOptions = async () => {
{
onError(err: any) {
hasError.value = true;
message.value = t("certd.pluginCommon.getOptionsError", { message: err.message });
message.value = t("certd.common.pluginCommon.getOptionsError", { message: err.message });
},
showErrorNotify: false,
}
);
const list = res?.list || res || [];
if (list.length > 0) {
message.value = t("certd.pluginCommon.getDataSuccessSelect");
message.value = t("certd.common.pluginCommon.getDataSuccessSelect");
} else {
message.value = t("certd.pluginCommon.getDataSuccessEmpty");
message.value = t("certd.common.pluginCommon.getDataSuccessEmpty");
}
optionsRef.value = list;
@@ -55,7 +55,7 @@ async function openDialog() {
saveRemind: false,
},
afterSubmit() {
notification.success({ message: t("certd.operationSuccess") });
notification.success({ message: t("certd.common.operationSuccess") });
},
async doSubmit({ form }: any) {
return await doPluginFormSubmit(form);
@@ -5,12 +5,12 @@
<template #dropdownRender="{ menuNode: menu }">
<template v-if="search">
<div class="flex w-full" style="padding: 4px 8px">
<a-input ref="inputRef" v-model:value="searchKeyRef" class="flex-1" allow-clear :placeholder="t('certd.pluginCommon.searchKeyword')" @keydown.enter="doSearch" />
<a-input ref="inputRef" v-model:value="searchKeyRef" class="flex-1" allow-clear :placeholder="t('certd.common.pluginCommon.searchKeyword')" @keydown.enter="doSearch" />
<a-button class="ml-2" :loading="loading" type="text" @click="doSearch">
<template #icon>
<search-outlined />
</template>
{{ t("certd.pluginCommon.search") }}
{{ t("certd.common.pluginCommon.search") }}
</a-button>
</div>
<div v-if="hasError" class="helper p-2" :class="{ error: hasError }">
@@ -26,7 +26,7 @@
</template>
</a-select>
<div class="ml-5 flex flex-row no-wrap">
<fs-button :loading="loading" :title="t('certd.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
<fs-button :loading="loading" :title="t('certd.common.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
<UploadCert v-if="uploadCert" class="ml-5" v-bind="uploadCert" @submit="refreshOptions"></UploadCert>
</div>
</div>
@@ -177,7 +177,7 @@ const getOptions = async () => {
{
onError(err: any) {
hasError.value = true;
message.value = t("certd.pluginCommon.getOptionsError", { message: err.message });
message.value = t("certd.common.pluginCommon.getOptionsError", { message: err.message });
optionsRef.value = [];
},
showErrorNotify: false,
@@ -185,9 +185,9 @@ const getOptions = async () => {
);
let list = res?.list || res || [];
if (list.length > 0) {
message.value = t("certd.pluginCommon.getDataSuccessSelect");
message.value = t("certd.common.pluginCommon.getDataSuccessSelect");
} else {
message.value = t("certd.pluginCommon.getDataSuccessEmpty");
message.value = t("certd.common.pluginCommon.getDataSuccessEmpty");
}
list = list.map((item: any) => {
return {
@@ -3,7 +3,7 @@
<div class="flex flex-row">
<a-tree-select class="remote-tree-select-input" :tree-data="optionsRef" :value="value" tree-checkable allow-clear v-bind="attrs" @click="onClick" @update:value="emit('update:value', $event)"> </a-tree-select>
<div class="ml-5">
<fs-button :loading="loading" :title="t('certd.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
<fs-button :loading="loading" :title="t('certd.common.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
</div>
</div>
<div class="helper" :class="{ error: hasError }">
@@ -107,16 +107,16 @@ const getOptions = async () => {
{
onError(err: any) {
hasError.value = true;
message.value = t("certd.pluginCommon.getOptionsError", { message: err.message });
message.value = t("certd.common.pluginCommon.getOptionsError", { message: err.message });
},
showErrorNotify: false,
}
);
const list = res?.list || res || [];
if (list.length > 0) {
message.value = t("certd.pluginCommon.getDataSuccessSelect");
message.value = t("certd.common.pluginCommon.getDataSuccessSelect");
} else {
message.value = t("certd.pluginCommon.getDataSuccessEmpty");
message.value = t("certd.common.pluginCommon.getDataSuccessEmpty");
}
optionsRef.value = list;
pagerRef.value.total = list.length;
@@ -1,6 +1,6 @@
<template>
<div class="upload-cert">
<fs-button v-model:loading="loading" type="primary" :text="t('certd.pluginCommon.upload')" v-bind="props.button" @click="openUploadCertDialog"></fs-button>
<fs-button v-model:loading="loading" type="primary" :text="t('certd.common.pluginCommon.upload')" v-bind="props.button" @click="openUploadCertDialog"></fs-button>
</div>
</template>
<script lang="ts" setup>
@@ -30,18 +30,18 @@ const getScope: any = inject("get:scope", () => {
const getPluginType: any = inject("get:plugin:type", () => {
return "plugin";
});
const title = computed(() => props.title || t("certd.pluginCommon.uploadCert"));
const title = computed(() => props.title || t("certd.common.pluginCommon.uploadCert"));
function openUploadCertDialog() {
const columns = merge(
{
certName: {
title: t("certd.pluginCommon.certName"),
title: t("certd.common.pluginCommon.certName"),
form: {
component: {
name: "a-input",
vModel: "value",
},
helper: t("certd.pluginCommon.certNameHelper"),
helper: t("certd.common.pluginCommon.certNameHelper"),
},
},
},
@@ -51,7 +51,7 @@ function openUploadCertDialog() {
title: title.value,
columns: {
certName: {
title: t("certd.pluginCommon.certName"),
title: t("certd.common.pluginCommon.certName"),
form: {
component: {
name: "a-input",
@@ -86,7 +86,7 @@ function openUploadCertDialog() {
showErrorNotify: true,
}
);
notification.success({ message: t("certd.pluginCommon.uploadSuccess") });
notification.success({ message: t("certd.common.pluginCommon.uploadSuccess") });
emit("submit");
} finally {
loading.value = false;
@@ -4,10 +4,10 @@ import { $t } from "/@/locales";
function createChallengeTypeDict() {
return dict({
data: [
{ value: "dns", label: $t("certd.verifyPlan.dnsChallenge"), color: "green" },
{ value: "dns", label: $t("certd.certdomain.verifyPlan.dnsChallenge"), color: "green" },
{ value: "dns-persist", label: "DNS持久验证", color: "cyan" },
{ value: "cname", label: $t("certd.verifyPlan.cnameProxyChallenge"), color: "blue" },
{ value: "http", label: $t("certd.verifyPlan.httpChallenge"), color: "yellow" },
{ value: "cname", label: $t("certd.certdomain.verifyPlan.cnameProxyChallenge"), color: "blue" },
{ value: "http", label: $t("certd.certdomain.verifyPlan.httpChallenge"), color: "yellow" },
],
});
}
@@ -18,11 +18,11 @@ function createUploaderTypeDict() {
{ label: "SFTP", value: "sftp" },
{ label: "SCP", value: "scp" },
{ label: "FTP", value: "ftp" },
{ label: $t("certd.verifyPlan.uploader.aliyunOss"), value: "alioss" },
{ label: $t("certd.verifyPlan.uploader.tencentCos"), value: "tencentcos" },
{ label: $t("certd.verifyPlan.uploader.qiniuOss"), value: "qiniuoss" },
{ label: $t("certd.certdomain.verifyPlan.uploader.aliyunOss"), value: "alioss" },
{ label: $t("certd.certdomain.verifyPlan.uploader.tencentCos"), value: "tencentcos" },
{ label: $t("certd.certdomain.verifyPlan.uploader.qiniuOss"), value: "qiniuoss" },
{ label: "S3/Minio", value: "s3" },
{ label: $t("certd.verifyPlan.uploader.sshDeprecated"), value: "ssh", disabled: true },
{ label: $t("certd.certdomain.verifyPlan.uploader.sshDeprecated"), value: "ssh", disabled: true },
],
});
}
@@ -30,8 +30,8 @@ function createUploaderTypeDict() {
function createDomainFromTypeDict() {
return dict({
data: [
{ value: "manual", label: $t("certd.verifyPlan.domainFrom.manual") },
{ value: "auto", label: $t("certd.verifyPlan.domainFrom.auto") },
{ value: "manual", label: $t("certd.certdomain.verifyPlan.domainFrom.manual") },
{ value: "auto", label: $t("certd.certdomain.verifyPlan.domainFrom.auto") },
],
});
}
@@ -33,11 +33,11 @@ const { t } = useI18n();
<div class="tutorial-button pointer" @click="open">
<template v-if="!slots.default">
<fs-icon v-if="showIcon === false" icon="ant-design:question-circle-outlined" class="mr-0.5"></fs-icon>
<div class="hidden md:block">{{ t("tutorial.title") }}</div>
<div class="hidden md:block">{{ t("certd.tutorial.title") }}</div>
</template>
<slot></slot>
<a-modal v-model:open="openedRef" class="tutorial-modal" width="90%">
<template #title>{{ t("tutorial.title") }}</template>
<template #title>{{ t("certd.tutorial.title") }}</template>
<tutorial-steps v-if="openedRef" />
<template #footer></template>
</a-modal>
@@ -18,9 +18,9 @@ import { ref } from "vue";
import { mitter } from "/@/utils/util.mitt";
const steps = ref<Step[]>([
{ title: t("certd.steps.createPipeline"), icon: <fs-icon icon="tabler:circle-number-1-filled"></fs-icon> },
{ title: t("certd.steps.addTask"), icon: <fs-icon icon="tabler:circle-number-2-filled"></fs-icon> },
{ title: t("certd.steps.scheduledRun"), icon: <fs-icon icon="tabler:circle-number-3-filled"></fs-icon> },
{ title: t("certd.tutorial.steps.createPipeline"), icon: <fs-icon icon="tabler:circle-number-1-filled"></fs-icon> },
{ title: t("certd.tutorial.steps.addTask"), icon: <fs-icon icon="tabler:circle-number-2-filled"></fs-icon> },
{ title: t("certd.tutorial.steps.scheduledRun"), icon: <fs-icon icon="tabler:circle-number-3-filled"></fs-icon> },
]);
function goPipeline() {
@@ -20,8 +20,8 @@
</div>
<div class="flex-center actions">
<fs-button class="m-10" icon="ion:arrow-back-outline" @click="prev()">{{ t("guide.buttons.prev") }}</fs-button>
<fs-button class="m-10" type="primary" icon-right="ion:arrow-forward-outline" @click="next()">{{ t("guide.buttons.next") }}</fs-button>
<fs-button class="m-10" icon="ion:arrow-back-outline" @click="prev()">{{ t("certd.guide.buttons.prev") }}</fs-button>
<fs-button class="m-10" type="primary" icon-right="ion:arrow-forward-outline" @click="next()">{{ t("certd.guide.buttons.next") }}</fs-button>
</div>
</div>
</template>
@@ -52,126 +52,126 @@ import { computed, nextTick, ref } from "vue";
const steps = ref<Step[]>([
{
title: t("guide.createCertPipeline.title"),
description: t("guide.createCertPipeline.description"),
title: t("certd.guide.createCertPipeline.title"),
description: t("certd.guide.createCertPipeline.description"),
items: [
{
title: t("guide.createCertPipeline.items.tutorialTitle"),
descriptions: [t("guide.createCertPipeline.items.tutorialDesc1"), t("guide.createCertPipeline.items.tutorialDesc2")],
title: t("certd.guide.createCertPipeline.items.tutorialTitle"),
descriptions: [t("certd.guide.createCertPipeline.items.tutorialDesc1"), t("certd.guide.createCertPipeline.items.tutorialDesc2")],
body: () => {
return <SimpleSteps></SimpleSteps>;
},
},
{
image: "/static/doc/images/1-add.png",
title: t("guide.createCertPipeline.items.createTitle"),
descriptions: [t("guide.createCertPipeline.items.createDesc")],
title: t("certd.guide.createCertPipeline.items.createTitle"),
descriptions: [t("certd.guide.createCertPipeline.items.createDesc")],
},
{
image: "/static/doc/images/3-add-success.png",
title: t("guide.createCertPipeline.items.successTitle"),
descriptions: [t("guide.createCertPipeline.items.successDesc")],
title: t("certd.guide.createCertPipeline.items.successTitle"),
descriptions: [t("certd.guide.createCertPipeline.items.successDesc")],
},
// {
// title: t("guide.createCertPipeline.items.nextTitle"),
// descriptions: [t("guide.createCertPipeline.items.nextDesc")],
// title: t("certd.guide.createCertPipeline.items.nextTitle"),
// descriptions: [t("certd.guide.createCertPipeline.items.nextDesc")],
// },
],
},
{
title: t("guide.addDeployTask.title"),
description: t("guide.addDeployTask.description"),
title: t("certd.guide.addDeployTask.title"),
description: t("certd.guide.addDeployTask.description"),
items: [
{
image: "/static/doc/images/5-1-add-host.png",
title: t("guide.addDeployTask.items.addTaskTitle"),
descriptions: [t("guide.addDeployTask.items.addTaskDesc1"), t("guide.addDeployTask.items.addTaskDesc2")],
title: t("certd.guide.addDeployTask.items.addTaskTitle"),
descriptions: [t("certd.guide.addDeployTask.items.addTaskDesc1"), t("certd.guide.addDeployTask.items.addTaskDesc2")],
},
{
image: "/static/doc/images/5-2-add-host.png",
title: t("guide.addDeployTask.items.fillParamsTitle"),
descriptions: [t("guide.addDeployTask.items.fillParamsDesc1"), t("guide.addDeployTask.items.fillParamsDesc2")],
title: t("certd.guide.addDeployTask.items.fillParamsTitle"),
descriptions: [t("certd.guide.addDeployTask.items.fillParamsDesc1"), t("certd.guide.addDeployTask.items.fillParamsDesc2")],
},
{
image: "/static/doc/images/5-3-add-host.png",
title: t("guide.addDeployTask.items.activateCertTitle"),
descriptions: [t("guide.addDeployTask.items.activateCertDesc1"), t("guide.addDeployTask.items.activateCertDesc2")],
title: t("certd.guide.addDeployTask.items.activateCertTitle"),
descriptions: [t("certd.guide.addDeployTask.items.activateCertDesc1"), t("certd.guide.addDeployTask.items.activateCertDesc2")],
},
{
image: "/static/doc/images/5-4-add-host.png",
title: t("guide.addDeployTask.items.taskSuccessTitle"),
descriptions: [t("guide.addDeployTask.items.taskSuccessDesc")],
title: t("certd.guide.addDeployTask.items.taskSuccessTitle"),
descriptions: [t("certd.guide.addDeployTask.items.taskSuccessDesc")],
},
{
image: "/static/doc/images/5-5-plugin-list.png",
title: t("guide.addDeployTask.items.pluginsTitle"),
descriptions: [t("guide.addDeployTask.items.pluginsDesc")],
title: t("certd.guide.addDeployTask.items.pluginsTitle"),
descriptions: [t("certd.guide.addDeployTask.items.pluginsDesc")],
},
],
},
{
title: t("guide.runAndTestTask.runAndTestTitle"),
description: t("guide.runAndTestTask.runAndTestDescription"),
title: t("certd.guide.runAndTestTask.runAndTestTitle"),
description: t("certd.guide.runAndTestTask.runAndTestDescription"),
items: [
{
image: "/static/doc/images/9-start.png",
title: t("guide.runAndTestTask.runTestOnce"),
descriptions: [t("guide.runAndTestTask.clickManualTriggerToTest")],
title: t("certd.guide.runAndTestTask.runTestOnce"),
descriptions: [t("certd.guide.runAndTestTask.clickManualTriggerToTest")],
},
{
image: "/static/doc/images/10-1-log.png",
title: t("guide.runAndTestTask.viewLogs"),
descriptions: [t("guide.runAndTestTask.clickTaskToViewStatusAndLogs")],
title: t("certd.guide.runAndTestTask.viewLogs"),
descriptions: [t("certd.guide.runAndTestTask.clickTaskToViewStatusAndLogs")],
},
{
image: "/static/doc/images/11-1-error.png",
title: t("guide.runAndTestTask.howToTroubleshootFailure"),
descriptions: [t("guide.runAndTestTask.viewErrorLogs")],
title: t("certd.guide.runAndTestTask.howToTroubleshootFailure"),
descriptions: [t("certd.guide.runAndTestTask.viewErrorLogs")],
},
{
image: "/static/doc/images/11-2-error.png",
title: t("guide.runAndTestTask.howToTroubleshootFailure"),
descriptions: [t("guide.runAndTestTask.viewErrorLogs"), t("guide.runAndTestTask.nginxContainerNotExistFix")],
title: t("certd.guide.runAndTestTask.howToTroubleshootFailure"),
descriptions: [t("certd.guide.runAndTestTask.viewErrorLogs"), t("certd.guide.runAndTestTask.nginxContainerNotExistFix")],
},
{
image: "/static/doc/images/12-1-log-success.png",
title: t("guide.runAndTestTask.executionSuccess"),
descriptions: [t("guide.runAndTestTask.retryAfterFix")],
title: t("certd.guide.runAndTestTask.executionSuccess"),
descriptions: [t("certd.guide.runAndTestTask.retryAfterFix")],
},
{
image: "/static/doc/images/12-2-skip-log.png",
title: t("guide.runAndTestTask.autoSkipAfterSuccess"),
descriptions: [t("guide.runAndTestTask.successSkipExplanation")],
title: t("certd.guide.runAndTestTask.autoSkipAfterSuccess"),
descriptions: [t("certd.guide.runAndTestTask.successSkipExplanation")],
},
{
image: "/static/doc/images/13-1-result.png",
title: t("guide.runAndTestTask.viewCertDeploymentSuccess"),
descriptions: [t("guide.runAndTestTask.visitNginxToSeeCert")],
title: t("certd.guide.runAndTestTask.viewCertDeploymentSuccess"),
descriptions: [t("certd.guide.runAndTestTask.visitNginxToSeeCert")],
},
{
image: "/static/doc/images/13-3-download.png",
title: t("guide.runAndTestTask.downloadCertManualDeploy"),
descriptions: [t("guide.runAndTestTask.downloadIfNoAutoDeployPlugin")],
title: t("certd.guide.runAndTestTask.downloadCertManualDeploy"),
descriptions: [t("certd.guide.runAndTestTask.downloadIfNoAutoDeployPlugin")],
},
],
},
{
title: t("guide.scheduleAndEmailTask.title"),
description: t("guide.scheduleAndEmailTask.description"),
title: t("certd.guide.scheduleAndEmailTask.title"),
description: t("certd.guide.scheduleAndEmailTask.description"),
items: [
{
image: "/static/doc/images/14-timer.png",
title: t("guide.scheduleAndEmailTask.setSchedule"),
descriptions: [t("guide.scheduleAndEmailTask.pipelineSuccessThenSchedule"), t("guide.scheduleAndEmailTask.recommendDailyRun")],
title: t("certd.guide.scheduleAndEmailTask.setSchedule"),
descriptions: [t("certd.guide.scheduleAndEmailTask.pipelineSuccessThenSchedule"), t("certd.guide.scheduleAndEmailTask.recommendDailyRun")],
},
{
image: "/static/doc/images/15-1-email.png",
title: t("guide.scheduleAndEmailTask.setEmailNotification"),
descriptions: [t("guide.scheduleAndEmailTask.suggestErrorAndRecoveryEmails")],
title: t("certd.guide.scheduleAndEmailTask.setEmailNotification"),
descriptions: [t("certd.guide.scheduleAndEmailTask.suggestErrorAndRecoveryEmails")],
},
{
title: t("guide.scheduleAndEmailTask.tutorialEndTitle"),
descriptions: [t("guide.scheduleAndEmailTask.thanksForWatching")],
title: t("certd.guide.scheduleAndEmailTask.tutorialEndTitle"),
descriptions: [t("certd.guide.scheduleAndEmailTask.thanksForWatching")],
},
],
},
@@ -48,56 +48,56 @@ const text = computed<Text>(() => {
const map = {
isComm: {
comm: {
name: t("vip.comm.name", { vipLabel }),
title: t("vip.comm.title", { expire: expireTime.value }),
name: t("certd.vip.comm.name", { vipLabel }),
title: t("certd.vip.comm.title", { expire: expireTime.value }),
},
button: {
name: t("vip.comm.name", { vipLabel }),
title: t("vip.comm.title", { expire: expireTime.value }),
name: t("certd.vip.comm.name", { vipLabel }),
title: t("certd.vip.comm.title", { expire: expireTime.value }),
},
icon: {
name: "",
title: t("vip.comm.name", { vipLabel }),
title: t("certd.vip.comm.name", { vipLabel }),
},
nav: {
name: t("vip.comm.nav", { vipLabel }),
title: t("vip.comm.title", { expire: expireTime.value }),
name: t("certd.vip.comm.nav", { vipLabel }),
title: t("certd.vip.comm.title", { expire: expireTime.value }),
},
},
isPlus: {
comm: {
name: t("vip.plus.name"),
title: t("vip.plus.title"),
name: t("certd.vip.plus.name"),
title: t("certd.vip.plus.title"),
},
button: {
name: t("vip.comm.name", { vipLabel }),
title: t("vip.comm.title", { expire: expireTime.value }),
name: t("certd.vip.comm.name", { vipLabel }),
title: t("certd.vip.comm.title", { expire: expireTime.value }),
},
icon: {
name: "",
title: t("vip.comm.name", { vipLabel }),
title: t("certd.vip.comm.name", { vipLabel }),
},
nav: {
name: t("vip.comm.nav", { vipLabel }),
title: t("vip.comm.title", { expire: expireTime.value }),
name: t("certd.vip.comm.nav", { vipLabel }),
title: t("certd.vip.comm.title", { expire: expireTime.value }),
},
},
free: {
comm: {
name: t("vip.free.comm.name"),
title: t("vip.free.comm.title"),
name: t("certd.vip.free.comm.name"),
title: t("certd.vip.free.comm.title"),
},
button: {
name: t("vip.free.button.name"),
title: t("vip.free.button.title"),
name: t("certd.vip.free.button.name"),
title: t("certd.vip.free.button.title"),
},
icon: {
name: "",
title: t("vip.free.button.name"),
title: t("certd.vip.free.button.name"),
},
nav: {
name: t("vip.free.nav.name"),
title: plusMessage || t("vip.free.nav.title"),
name: t("certd.vip.free.nav.name"),
title: plusMessage || t("certd.vip.free.nav.title"),
},
},
};
@@ -131,7 +131,7 @@ const userStore = useUserStore();
async function getVipTrial(vipType = "plus") {
const res = await api.getVipTrial(vipType);
notification.success({ message: t("vip.congratulations_vip_trial", { duration: res.duration }) });
notification.success({ message: t("certd.vip.congratulations_vip_trial", { duration: res.duration }) });
await settingStore.init();
}
@@ -139,8 +139,8 @@ function openTrialModal(vipType = "plus") {
Modal.destroyAll();
modal.confirm({
title: t("vip.trial_modal_title"),
okText: t("vip.trial_modal_ok_text"),
title: t("certd.vip.trial_modal_title"),
okText: t("certd.vip.trial_modal_ok_text"),
onOk() {
getVipTrial(vipType);
},
@@ -148,8 +148,8 @@ function openTrialModal(vipType = "plus") {
content: () => {
return (
<div class="flex-col mt-10 mb-10">
<div>{t("vip.trial_modal_thanks")}</div>
<div>{t("vip.trial_modal_click_confirm", { vipType })}</div>
<div>{t("certd.vip.trial_modal_thanks")}</div>
<div>{t("certd.vip.trial_modal_click_confirm", { vipType })}</div>
</div>
);
},
@@ -158,7 +158,7 @@ function openTrialModal(vipType = "plus") {
function openStarModal(vipType: string) {
if (settingStore.isPlus) {
notification.error({ message: t("vip.already_vip") });
notification.error({ message: t("certd.vip.already_vip") });
return;
}
Modal.destroyAll();
@@ -170,8 +170,8 @@ function openStarModal(vipType: string) {
// };
// modal.confirm({
// title: t("vip.get_7_day_pro_trial"),
// okText: t("vip.star_now"),
// title: t("certd.vip.get_7_day_pro_trial"),
// okText: t("certd.vip.star_now"),
// onOk() {
// goGithub();
// openTrialModal(vipType);
@@ -180,7 +180,7 @@ function openStarModal(vipType: string) {
// content: () => {
// return (
// <div class="flex mt-10 mb-10">
// <div>{t("vip.please_help_star")}</div>
// <div>{t("certd.vip.please_help_star")}</div>
// <img class="ml-5" src="https://img.shields.io/github/stars/certd/certd?logo=github" />
// </div>
// );
@@ -190,16 +190,16 @@ function openStarModal(vipType: string) {
function openUpgrade() {
if (!userStore.isAdmin) {
notification.info({ message: t("vip.admin_only_operation") });
notification.info({ message: t("certd.vip.admin_only_operation") });
return;
}
const placeholder = t("vip.enter_activation_code");
const placeholder = t("certd.vip.enter_activation_code");
const isPlus = settingStore.isPlus;
let title = t("vip.activate_pro_business");
let title = t("certd.vip.activate_pro_business");
if (settingStore.isComm) {
title = t("vip.renew_business");
title = t("certd.vip.renew_business");
} else if (settingStore.isPlus) {
title = t("vip.renew_pro_upgrade_business");
title = t("certd.vip.renew_pro_upgrade_business");
}
// const goBuyUrl = "https://afdian.com/a/greper"
@@ -214,10 +214,10 @@ function openUpgrade() {
function checkPerpetualPlus() {
if (settingStore.isPerpetual) {
Modal.warn({
title: t("vip.already_perpetual_plus"),
okText: t("vip.confirm"),
title: t("certd.vip.already_perpetual_plus"),
okText: t("certd.vip.confirm"),
});
throw new Error(t("vip.already_perpetual_plus"));
throw new Error(t("certd.vip.already_perpetual_plus"));
}
}
@@ -225,8 +225,8 @@ function openUpgrade() {
checkPerpetualPlus();
if (settingStore.isComm) {
Modal.warn({
title: t("vip.already_comm"),
okText: t("vip.confirm"),
title: t("certd.vip.already_comm"),
okText: t("certd.vip.confirm"),
});
return;
}
@@ -237,8 +237,8 @@ function openUpgrade() {
checkPerpetualPlus();
if (settingStore.isPlus && !settingStore.isComm) {
Modal.confirm({
title: t("vip.already_plus"),
okText: t("vip.confirm"),
title: t("certd.vip.already_plus"),
okText: t("certd.vip.confirm"),
onOk() {
window.open(goBuyCommUrl);
},
@@ -251,7 +251,7 @@ function openUpgrade() {
title,
class: "vip-modal",
maskClosable: true,
okText: t("vip.close"),
okText: t("certd.vip.close"),
width: 1180,
content: () => {
return <VipModalContent placeholder={placeholder} isPlus={isPlus} productInfo={productInfo} goBuyPlusPage={goBuyPlusPage} goBuyCommPage={goBuyCommPage} openStarModal={openStarModal} modalRef={modalRef} />;
@@ -34,7 +34,7 @@
</a-tooltip>
</span>
<span v-else>
<span class="price-text">{{ t("vip.freee") }}</span>
<span class="price-text">{{ t("certd.vip.freee") }}</span>
</span>
</div>
<div class="get-show">
@@ -44,15 +44,15 @@
<span class="color-red"> {{ todayOrderCount.vipTotal }}</span>
位伙伴支持
</span>
<a-tooltip :title="t('vip.afdian_support_vip')">
<a-tooltip :title="t('certd.vip.afdian_support_vip')">
<a-button size="small" type="primary" @click="goBuyPlusPage">
{{ t("vip.get_after_support") }}
{{ t("certd.vip.get_after_support") }}
</a-button>
</a-tooltip>
</template>
<template v-else-if="item.type === 'comm'">
<a-button size="small" type="primary" @click="goBuyCommPage">
{{ t("vip.buy") }}
{{ t("certd.vip.buy") }}
</a-button>
</template>
</div>
@@ -63,36 +63,36 @@
</div>
<div>
<a href="https://certd.docmirror.cn/guide/donate/#相关问题" target="_blank">
{{ t("vip.question") }}
{{ t("certd.vip.question") }}
</a>
</div>
<div class="mt-10">
<div class="w-100 flex-col md:flex-row">
<span>{{ t("vip.site_id") }}:</span>
<span>{{ t("certd.vip.site_id") }}:</span>
<fs-copyable v-model="computedSiteId" class="mr-2 inline-flex"></fs-copyable>
<a @click="goBindAccount">{{ t("vip.not_effective") }}</a>
<a @click="goBindAccount">{{ t("certd.vip.not_effective") }}</a>
</div>
</div>
<div v-if="isPlus" class="mt-10 flex flex-col md:flex-row">
<span class="mr-2"> {{ t("vip.current") }} {{ vipLabel }} {{ t("vip.activated_expire_time") }} {{ settingStore.expiresText }} </span>
<span class="mr-2"> {{ t("certd.vip.current") }} {{ vipLabel }} {{ t("certd.vip.activated_expire_time") }} {{ settingStore.expiresText }} </span>
<a href="https://app.handfree.work/subject/#/page/detail/1" target="_blank">
{{ t("vip.learn_more") }}
{{ t("certd.vip.learn_more") }}
</a>
</div>
<div class="mt-10">
<span class="mr-2">{{ t("vip.have_activation_code") }}</span>
<span class="mr-2">{{ t("certd.vip.have_activation_code") }}</span>
<span>
<a @click="showManualActivation">{{ t("vip.manual_activation") }}</a>
<a @click="showManualActivation">{{ t("certd.vip.manual_activation") }}</a>
</span>
</div>
<div v-if="manualActiveFlag" class="mt-10">
<div class="mt-10">
<a-input-search v-model:value="formState.code" class="w-2/6" :placeholder="placeholder" :enter-button="t('vip.activate')" @search="doActive"></a-input-search>
<a-input-search v-model:value="formState.code" class="w-2/6" :placeholder="placeholder" :enter-button="t('certd.vip.activate')" @search="doActive"></a-input-search>
</div>
<div class="mt-10">
{{ t("vip.activation_code_one_use") }}
<a @click="goAccount">{{ t("vip.bind_account") }}</a
>,{{ t("vip.transfer_vip") }}
{{ t("certd.vip.activation_code_one_use") }}
<a @click="goAccount">{{ t("certd.vip.bind_account") }}</a
>,{{ t("certd.vip.transfer_vip") }}
</div>
</div>
</div>
@@ -128,24 +128,24 @@ const formState = reactive({
async function doActive() {
if (!formState.code) {
notification.error({ message: t("vip.enterCode") });
throw new Error(t("vip.enterCode"));
notification.error({ message: t("certd.vip.enterCode") });
throw new Error(t("certd.vip.enterCode"));
}
const res = await api.doActive(formState);
if (res) {
await settingStore.init();
const vipLabel = settingStore.vipLabel;
Modal.success({
title: t("vip.successTitle"),
content: t("vip.successContent", {
title: t("certd.vip.successTitle"),
content: t("certd.vip.successContent", {
vipLabel,
expireDate: settingStore.plusInfo.expireTime === -1 ? t("vip.permanent") : dayjs(settingStore.plusInfo.expireTime).format("YYYY-MM-DD"),
expireDate: settingStore.plusInfo.expireTime === -1 ? t("certd.vip.permanent") : dayjs(settingStore.plusInfo.expireTime).format("YYYY-MM-DD"),
}),
onOk() {
if (!(settingStore.installInfo.bindUserId > 0)) {
Modal.confirm({
title: t("vip.bindAccountTitle"),
content: t("vip.bindAccountContent"),
title: t("certd.vip.bindAccountTitle"),
content: t("certd.vip.bindAccountContent"),
onOk() {
router.push("/sys/account");
},
@@ -179,39 +179,39 @@ function goBindAccount() {
const vipTypeDefine: any = {
free: {
title: t("vip.basic_edition"),
desc: t("vip.community_free_version"),
title: t("certd.vip.basic_edition"),
desc: t("certd.vip.community_free_version"),
type: "free",
icon: "lucide:package-open",
privilege: t("vip.free_privilege").split("\n"),
privilege: t("certd.vip.free_privilege").split("\n"),
},
plus: {
title: t("vip.professional_edition"),
desc: t("vip.open_source_support"),
title: t("certd.vip.professional_edition"),
desc: t("certd.vip.open_source_support"),
type: "plus",
privilege: t("vip.plus_privilege").split("\n"),
privilege: t("certd.vip.plus_privilege").split("\n"),
trial: {
title: t("vip.click_to_get_7_day_trial"),
title: t("certd.vip.click_to_get_7_day_trial"),
click: () => {
props.openStarModal("plus");
},
},
icon: "stash:thumb-up",
priceText: props.productInfo.plus.priceText || `¥${props.productInfo.plus.price}/${t("vip.years")}`,
discountText: props.productInfo.plus.discountText || `¥${props.productInfo.plus.price3}/3${t("vip.years")}`,
priceText: props.productInfo.plus.priceText || `¥${props.productInfo.plus.price}/${t("certd.vip.years")}`,
discountText: props.productInfo.plus.discountText || `¥${props.productInfo.plus.price3}/3${t("certd.vip.years")}`,
tooltip: props.productInfo.plus.tooltip,
},
comm: {
title: t("vip.business_edition"),
desc: t("vip.commercial_license"),
title: t("certd.vip.business_edition"),
desc: t("certd.vip.commercial_license"),
type: "comm",
icon: "vaadin:handshake",
privilege: t("vip.comm_privilege").split("\n"),
priceText: props.productInfo.comm.priceText || `¥${props.productInfo.comm.price}/${t("vip.years")}`,
discountText: props.productInfo.comm.discountText || `¥${props.productInfo.comm.price3}/3${t("vip.years")}`,
privilege: t("certd.vip.comm_privilege").split("\n"),
priceText: props.productInfo.comm.priceText || `¥${props.productInfo.comm.price}/${t("certd.vip.years")}`,
discountText: props.productInfo.comm.discountText || `¥${props.productInfo.comm.price3}/3${t("certd.vip.years")}`,
tooltip: props.productInfo.comm.tooltip,
trial: {
title: t("vip.click_to_get_7_day_trial"),
title: t("certd.vip.click_to_get_7_day_trial"),
click: () => {
props.openStarModal("comm");
},
@@ -1,4 +1,4 @@
<script lang="ts" setup>
<script lang="ts" setup>
import { BasicLayout, LockScreen, UserDropdown } from "/@/vben/layouts";
import { computed, onErrorCaptured, onMounted, provide, ref } from "vue";
@@ -23,14 +23,14 @@ const menus = computed(() => [
router.push("/cert/mine/user-profile");
},
icon: "fa-solid:book",
text: t("certd.accountInfo"),
text: t("certd.mine.accountInfo"),
},
{
handler: () => {
router.push("/cert/mine/security");
},
icon: "fluent:shield-keyhole-16-regular",
text: t("certd.securitySettings"),
text: t("certd.mine.securitySettings"),
},
]);
@@ -109,7 +109,7 @@ const projectStore = useProjectStore();
<vip-button class="flex-center header-btn" mode="nav" />
</div>
<div v-if="!settingStore.isComm" class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block">
<fs-button shape="circle" type="text" icon="clarity:host-solid-badged" :text="null" :tooltip="{ title: t('certd.client') }" @click="goCertdClient" />
<fs-button shape="circle" type="text" icon="clarity:host-solid-badged" :text="null" :tooltip="{ title: t('certd.navigation.client') }" @click="goCertdClient" />
</div>
<div v-if="!settingStore.isComm" class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block">
<fs-button shape="circle" type="text" icon="ion:logo-github" :text="null" @click="goGithub" />
@@ -13,34 +13,54 @@ import notification from "./certd/notification";
import addon from "./certd/addon";
import suite from "./certd/suite";
import project from "./certd/project";
import syssettings from "./certd/sys-settings";
import sysplugin from "./certd/sys-plugin";
import sysauthority from "./certd/sys-authority";
import syscname from "./certd/sys-cname";
import settings from "./certd/sys-settings";
import plugin from "./certd/sys-plugin";
import authority from "./certd/sys-authority";
import cnameSettings from "./certd/sys-cname";
import tutorial from "./certd/tutorial";
import cron from "./certd/cron";
import client from "./certd/client";
import wallet from "./certd/wallet";
import nettest from "./certd/nettest";
import audit from "./certd/audit";
import applyTemplate from "./certd/apply-template";
import dnsPersist from "./certd/dns-persist";
import auth from "./certd/auth";
import guide from "./certd/guide";
import vip from "./certd/vip";
// Note: @ is reserved in locale messages; use {'@'} when needed.
export default {
...common,
...navigation,
...dashboard,
...pipeline,
...history,
...monitor,
...certdomain,
...cname,
...access,
...open,
...mine,
...notification,
...addon,
...suite,
...project,
...syssettings,
...sysplugin,
...sysauthority,
...syscname,
...tutorial,
...cron,
common,
navigation,
dashboard,
pipeline,
history,
monitor,
certdomain,
cname,
access,
open,
mine,
notification,
addon,
suite,
project,
sys: {
settings,
plugin,
authority,
cname: cnameSettings,
},
tutorial,
cron,
client,
wallet,
nettest,
audit,
applyTemplate,
dnsPersist,
auth,
guide,
vip,
};
@@ -4,4 +4,11 @@ export default {
nameHelper: "Fill in as you like, useful to distinguish when multiple authorizations of the same type exist",
level: "Level",
system: "System",
select: "Select",
selectProvider: "Select Authorization Provider",
deleteConfirm: "If this authorization is already in use, pipelines may fail. Proceed with caution.",
name: "Name",
user: "User",
type: "Type",
selectType: "Please select a type",
};
@@ -0,0 +1,11 @@
export default {
setSuccess: "Settings saved",
editTitle: "Edit Certificate Application Template",
addTitle: "Add Certificate Application Template",
name: "Template Name",
default: "Default",
setDefault: "Set as Default",
disabled: "Disabled",
createTime: "Created At",
config: "Configuration",
};
@@ -0,0 +1,10 @@
export default {
createTime: "Operation Time",
type: "Operation Type",
action: "Action",
result: "Result",
success: "Success",
failed: "Failed",
remark: "Remark",
ipAddress: "IP Address",
};
@@ -1,58 +1,4 @@
export default {
welcomeBack: "Welcome Back",
pageTitle: "Plug-and-play Admin system",
pageDesc: "Efficient, versatile frontend template",
loginSuccess: "Login Successful",
loginSuccessDesc: "Welcome Back",
loginSubtitle: "Enter your account details to manage your projects",
selectAccount: "Quick Select Account",
username: "Username",
password: "Password",
usernameTip: "Please enter username",
passwordErrorTip: "Password is incorrect",
passwordTip: "Please enter password",
verifyRequiredTip: "Please complete the verification first",
rememberMe: "Remember Me",
createAnAccount: "Create an Account",
createAccount: "Create Account",
alreadyHaveAccount: "Already have an account?",
accountTip: "Don't have an account?",
signUp: "Sign Up",
signUpSubtitle: "Make managing your applications simple and fun",
confirmPassword: "Confirm Password",
confirmPasswordTip: "The passwords do not match",
agree: "I agree to",
privacyPolicy: "Privacy-policy",
terms: "Terms",
agreeTip: "Please agree to the Privacy Policy and Terms",
goToLogin: "Login instead",
passwordStrength: "Use 8 or more characters with a mix of letters, numbers & symbols",
forgetPassword: "Forget Password?",
forgetPasswordSubtitle: "Enter your email and we'll send you instructions to reset your password",
emailTip: "Please enter email",
emailValidErrorTip: "The email format you entered is incorrect",
sendResetLink: "Send Reset Link",
email: "Email",
qrcodeSubtitle: "Scan the QR code with your phone to login",
qrcodePrompt: "Click 'Confirm' after scanning to complete login",
qrcodeLogin: "QR Code Login",
codeSubtitle: "Enter your phone number to start managing your project",
code: "Security code",
codeTip: "Security code required {0} characters",
mobile: "Mobile",
mobileLogin: "Mobile Login",
mobileTip: "Please enter mobile number",
mobileErrortip: "The phone number format is incorrect",
sendCode: "Get Security code",
sendText: "Resend in {0}s",
thirdPartyLogin: "Or continue with",
loginAgainTitle: "Please Log In Again",
loginAgainSubTitle: "Your login session has expired. Please log in again to continue.",
layout: {
center: "Align Center",
alignLeft: "Align Left",
alignRight: "Align Right",
},
loginType: {
password: "Password Login",
sms: "Mobile Login",
@@ -110,4 +56,30 @@ export default {
userInfo: "User Info",
securitySettingTip: "2FA Setting",
securitySetting: "2FA Setting",
register: {
usernameTab: "Register with username",
emailTab: "Register with email",
mobileTab: "Register with mobile",
username: "Username",
email: "Email",
password: "Password",
confirmPassword: "Confirm Password",
captcha: "Captcha",
emailCode: "Email verification code",
mobileLogin: "Go to mobile login (login to register)",
inviteCode: "Invitation code",
inviteCodeOptional: "Invitation code (optional)",
submit: "Register",
login: "Log In",
noMethodEnabled: "No registration method is enabled",
usernameRequired: "Please enter your username",
emailRequired: "Please enter your email",
emailInvalid: "Please enter a valid email address",
passwordRequired: "Please enter your password",
confirmPasswordRequired: "Please confirm your password",
passwordMismatch: "The two passwords do not match",
smsCodeRequired: "Please enter the SMS verification code",
emailCodeRequired: "Please enter the email verification code",
captchaRequired: "Please complete the captcha",
},
};
@@ -1,5 +1,6 @@
export default {
domain: {
dnsPersistDescription: "Currently, only Google certificates support DNS persistent validation.",
domainManager: "Domain Manager",
domainDescription: "used to auto apply for certificate", //管理域名的校验方式,用于申请证书时自动选择验证方式
domain: "Domain",
@@ -19,6 +20,7 @@ export default {
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",
importFromResolveRecordsTooltip: "Import DNS records as monitoring sites",
syncExpirationDate: "Sync Domain Expiration Time",
syncTaskSubmitted: "Sync task submitted",
syncExpirationProgress: "Sync Domain Expiration Progress",
@@ -0,0 +1,18 @@
export default {
clientManager: "Client Manager",
title: "Client Manager",
description: "View online status and managed site count of connected Certd clients. Clients report a heartbeat every 10 minutes and are marked offline if no heartbeat arrives for 30 minutes.",
plusRequired: "This feature requires the Pro edition",
machineName: "Machine",
clientId: "Client ID",
version: "Version",
os: "OS",
status: "Status",
online: "Online",
offline: "Offline",
siteCount: "Sites",
httpsSiteCount: "HTTPS Sites",
syncedSiteCount: "Synced",
failedSiteCount: "Failed",
lastHeartbeatAt: "Last Heartbeat",
};
@@ -1,4 +1,6 @@
export default {
required: "Required",
no: "No",
usera: "User",
disabledSuccess: "Disabled successfully",
app: {
@@ -21,12 +23,18 @@ export default {
add: "Generate New Key",
edit: "Edit",
deleteSuccess: "Delete successful",
fastCrud: {
view: "View",
copy: "Copy",
edit: "Edit",
remove: "Delete",
addSuccess: "Added successfully",
saveSuccess: "Saved successfully",
},
saveSuccess: "Saved successfully",
cancel: "Cancel",
yes: "Yes",
no: "No",
all: "All",
required: "This field is required",
requiredField: "This field is required",
fieldRequired: "This field is required",
field_required: "This field is required",
@@ -1,50 +1,59 @@
export default {
dashboard: {
greeting: "Hello, {name}, welcome to 【{site}】",
latestVersion: "Latest version: {version}",
validUntil: "Valid until:",
tutorialTooltip: "Click to view detailed tutorial",
tutorialText: "Only 3 steps to automatically apply and deploy certificates",
alertMessage: "Certificates and credentials are sensitive. Do not use untrusted online Certd services or images. Always self-host and use official release channels:",
helpDoc: "Help Docs",
pipelineCount: "Number of Certificate Pipelines",
noPipeline: "You have no certificate pipelines yet",
enabledCount: "Enabled",
disabledCount: "Disabled",
certCount: "Number of Certificates",
noCert: "You have no certificates yet",
manageCert: "View Certificates",
siteMonitorCount: "Number of Monitored Sites",
siteAbnormalCount: "Abnormal",
siteNormalCount: "Normal",
manageSiteMonitor: "Manage Monitored Sites",
domainCount: "Number of Domains",
domainExpiringCount: "Expiring",
domainExpiredCount: "Expired",
domainNotExpiredCount: "Not Expired",
manageDomain: "Manage Domains",
certExpiringCount: "Soon-to-Expire",
certExpiredCount: "Expired",
certNoExpireCount: "Not Expired",
createNow: "Create Now",
managePipeline: "Manage Pipelines",
pipelineStatus: "Pipeline Status",
recentRun: "Recent Run Statistics",
runCount: "Run Count",
expiringCerts: "Soon-to-Expire Certificates",
totalGeneratedCertCount: "Total Certificates Generated",
singleDomainCertShortCount: "Single-domain",
multiDomainCertShortCount: "Multi-domain",
wildcardCertShortCount: "Wildcard",
singleDomainCertCount: "Single-domain Certificates Generated",
multiDomainCertCount: "Multi-domain Certificates Generated",
wildcardCertCount: "Wildcard Certificates Generated",
savedAmount: "Estimated savings",
savedAmountRule: "Estimated at one quarter of Tencent Cloud's annual certificate prices: single-domain ¥532, multi-domain ¥912, and wildcard ¥2147. Actual savings may vary.",
totalPipelineRuns: "Total Pipeline Runs",
supportedTasks: "Overview of Supported Deployment Tasks",
changeLog: "Change Log",
stableRelease: "Stable",
latestRelease: "Preview",
},
greeting: "Hello, {name}, welcome to 【{site}】",
latestVersion: "Latest version: {version}",
validUntil: "Valid until:",
tutorialTooltip: "Click to view detailed tutorial",
tutorialText: "Only 3 steps to automatically apply and deploy certificates",
alertMessage: "Certificates and credentials are sensitive. Do not use untrusted online Certd services or images. Always self-host and use official release channels:",
helpDoc: "Help Docs",
pipelineCount: "Number of Certificate Pipelines",
noPipeline: "You have no certificate pipelines yet",
enabledCount: "Enabled",
disabledCount: "Disabled",
certCount: "Number of Certificates",
noCert: "You have no certificates yet",
manageCert: "View Certificates",
siteMonitorCount: "Number of Monitored Sites",
siteAbnormalCount: "Abnormal",
siteNormalCount: "Normal",
manageSiteMonitor: "Manage Monitored Sites",
domainCount: "Number of Domains",
domainExpiringCount: "Expiring",
domainExpiredCount: "Expired",
domainNotExpiredCount: "Not Expired",
manageDomain: "Manage Domains",
certExpiringCount: "Soon-to-Expire",
certExpiredCount: "Expired",
certNoExpireCount: "Not Expired",
createNow: "Create Now",
managePipeline: "Manage Pipelines",
pipelineStatus: "Pipeline Status",
recentRun: "Recent Run Statistics",
runCount: "Run Count",
expiringCerts: "Soon-to-Expire Certificates",
totalGeneratedCertCount: "Total Certificates Generated",
singleDomainCertShortCount: "Single-domain",
multiDomainCertShortCount: "Multi-domain",
wildcardCertShortCount: "Wildcard",
singleDomainCertCount: "Single-domain Certificates Generated",
multiDomainCertCount: "Multi-domain Certificates Generated",
wildcardCertCount: "Wildcard Certificates Generated",
savedAmount: "Estimated savings",
savedAmountRule: "Estimated at one quarter of Tencent Cloud's annual certificate prices: single-domain ¥532, multi-domain ¥912, and wildcard ¥2147. Actual savings may vary.",
totalPipelineRuns: "Total Pipeline Runs",
supportedTasks: "Overview of Supported Deployment Tasks",
supportedTasksHelper: "This list is for display only. Create a pipeline to use these tasks.",
changeLog: "Change Log",
stableRelease: "Stable",
latestRelease: "Preview",
consoleTitle: "Console",
userTotal: "Total Users",
manageUsers: "Manage Users",
userGrowth: "User Growth",
newUsers: "New Users",
allPipelineTotal: "Total Pipelines",
managePipelines: "Manage Pipelines",
pipelineGrowth: "Pipeline Growth",
newPipelines: "New Pipelines",
recentRunTitle: "Recent Run Statistics",
};
@@ -0,0 +1,24 @@
export default {
pending: "Pending Setup",
created: "Created",
validating: "Validating",
valid: "Valid",
retry: "Retry",
deleteTxtTip: "Delete the TXT record at the provider",
accountUriMissing: "The ACME account authorization is missing accountUri. Please regenerate the account.",
verifySuccess: "Validation succeeded",
verifyFailed: "No matching TXT record found. Please try again later.",
submitted: "Validation submitted",
selectProviderAndAccess: "Select a DNS provider and authorization",
txtCreated: "TXT record created",
settingTitle: "Set DNS TXT Record",
manualAdd: "Add Manually",
autoAdd: "Add with Authorization",
manualTip: "Add the following TXT record in your DNS console, then click OK to validate immediately.",
autoTip: "Select a DNS provider and authorization. The system will create the TXT record and validate it in the background.",
mainDomain: "Main Domain",
txtHost: "TXT Host",
txtValue: "TXT Value",
provider: "DNS Provider",
access: "DNS Authorization",
};
@@ -7,6 +7,8 @@ export default {
settingLink: "Site Monitoring Settings",
limitInfo: "Basic edition limited to 1, professional and above unlimited, current",
checkAll: "Check All",
checkBatch: "Check Selected",
batchCheckConfirm: "Check the {count} selected sites now?",
checkNow: "Check Now",
batchDelete: "Batch Delete",
confirmTitle: "Confirm",
@@ -1,4 +1,7 @@
import monitorSite from "./monitor-site";
export default {
...monitorSite,
certificateRepo: {
title: "Certificate Repository",
sub: "Certificates generated from pipeline",
@@ -1,4 +1,9 @@
export default {
home: "Home",
login: "Login",
register: "Register",
forgotPassword: "Forgot Password",
oauthCallback: "OAuth Callback",
framework: {
title: "Framework",
home: "Home",
@@ -13,10 +18,13 @@ export default {
accessManager: "Access Management",
dnsPersistRecord: "DNS Persist Records",
certApplyTemplate: "Certificate Apply Templates",
certApplyTemplateHelper: "Preset certificate application parameters. They are filled automatically when creating a pipeline (excluding domains and validation methods).",
subDomain: "Subdomain Delegation Settings",
pipelineGroup: "Pipeline Group Management",
pipelineGroupHelper: "Manage pipeline groups",
openKey: "Open API Key",
notification: "Notification Settings",
notificationHelper: "Manage notification configurations",
siteMonitorSetting: "Site Monitor Settings",
auditLog: "Audit Log",
userSecurity: "Security Settings",
@@ -25,6 +33,7 @@ export default {
mySuite: "My Suite",
suiteBuy: "Suite Purchase",
myTrade: "My Orders",
myWallet: "My Wallet",
inviteCommission: "Incentive Plan",
paymentReturn: "Payment Return",
client: "Certd Client",
@@ -48,11 +57,15 @@ export default {
userManager: "User Management",
userDataManager: "User Data Management",
pipelineManager: "User Pipeline Management",
pipelineManagerHelper: "Manage all users' pipelines",
siteMonitorManager: "User Certificate Monitor",
siteMonitorManagerHelper: "Manage all users' certificate monitors",
suiteManager: "Suite Management",
suiteSetting: "Suite Settings",
orderManager: "Order Management",
userSuites: "User Suites",
balanceManager: "Balance Management",
balanceLog: "Balance Change Records",
inviteCommissionSetting: "Incentive Plan Settings",
inviteLevel: "Promotion Levels",
inviteUserLevel: "User Promotion Levels",
@@ -70,6 +83,8 @@ export default {
currentProject: "Current Project",
projectMemberManager: "Project Member",
auditLog: "Audit Log",
auditLogHelper: "View all users' operation records",
jobHistory: "Monitoring Execution History",
domainMonitorSetting: "Domain Monitor Settings",
},
};
@@ -0,0 +1,13 @@
export default {
title: "Network Test",
helper: "Test whether your server container network is working",
customDomain: "Custom Domain Test",
serverInfo: "Server Information",
refresh: "Refresh",
loading: "Loading...",
fetchFailed: "Failed to fetch server information",
noInfo: "No information",
localIp: "Local IP",
publicIp: "Public IP",
dnsServer: "DNS Server",
};
@@ -3,6 +3,13 @@ export default {
scopeOpenApiOnly: "Open API Only",
scopeFullAccount: "Full Account Permissions",
scopeHelper: "Open API only allows access to open APIs; full account permissions allow access to all APIs",
keyRemark: "Key Purpose",
keyRemarkHelper: "Describe the purpose of this key for easier identification",
keyRemarkMaxChars: "Maximum 512 characters",
returnCertPassword: "Return certificate password",
returnCertPasswordHelper: "When enabled, the certificate password is included in Open API certificate responses. Keep this key secure.",
yes: "Yes",
no: "No",
gen: {
text: "API Test",
title: "x-certd-token",
@@ -33,6 +33,10 @@ export default {
pipelineStarted: "Pipeline has started running",
},
actions: {
edit: "Edit",
delete: "Delete",
enable: "Enable",
disable: "Disable",
editPipeline: "Edit Pipeline",
editConfigGroup: "Modify Configuration/Group",
viewCertificate: "View Certificate",
@@ -65,6 +69,145 @@ export default {
legoAcme: "Lego-ACME: Based on Lego, supports a wide range of DNS providers, suitable for users familiar with Lego",
aliyunOrder: "Aliyun-Order: Get certificate from Aliyun certificate order",
},
editor: {
editTask: "Edit Task",
editStep: "Edit Step",
installedPlugins: "Installed Plugins",
pluginMarket: "Plugin Market",
searchPluginMarket: "Search plugin market",
searchLocalPlugins: "Search local plugins",
loadingPluginMarket: "Loading plugin market...",
noPluginFound: "No plugins found",
triggerManually: "Trigger Manually",
triggerSource: "Trigger Source",
scheduledTrigger: "Trigger Source (Scheduled)",
webhookTrigger: "Trigger Source (Webhook)",
certExpireTime: "Certificate expiration time: ",
certExpired: "Certificate expired ",
viewCertificate: "View Certificate",
downloadCertificate: "Download Certificate",
nextExecutionTime: "Next execution time: ",
noTriggerSource: "No trigger source configured; it will not run automatically",
validityPeriod: "Validity period: ",
save: "Save",
cancel: "Cancel",
addStage: "New Stage",
addTask: "Add Task",
addAfterTask: "Add After-Task",
addNotification: "Add Notification",
notificationNotSet: "Notification not set",
history: "Execution History",
historyHint: "Click a task to view logs",
taskName: "Task Name",
taskSteps: "Task Steps",
addStep: "Add Step",
copy: "Copy",
paste: "Paste",
confirm: "Confirm",
required: "This field is required",
taskNeedsStep: "At least one step is required, or delete this task using the button beside the title",
step: "Step",
endStage: "End",
confirmTitle: "Confirm",
confirmManualRun: "Are you sure you want to trigger the run manually?",
taskLog: "Task Log",
noLogs: "No logs (this node was not triggered or has no log records for this run)",
aiAnalyze: "AI Analysis",
rerun: "Rerun",
downloadLogs: "Download Logs",
close: "Close",
batchEditSteps: "Batch Edit Steps",
selectAllSteps: "Select All Steps",
batchEnableSteps: "Batch Enable Steps",
batchDisableSteps: "Batch Disable Steps",
batchDeleteSteps: "Batch Delete Steps",
exitBatchEdit: "Exit Batch Edit",
stepsSerialHelper: "Steps run sequentially. If an earlier step fails, later steps will not run.",
current: "Current",
view: "View",
confirmCancel: "Confirm Cancellation",
confirmCancelTask: "Cancel this task?",
taskCanceled: "Task canceled successfully",
selectAllTasks: "Select all tasks",
batchEnableTasks: "Enable tasks in batch",
batchDisableTasks: "Disable tasks in batch",
batchDeleteTasks: "Delete tasks in batch",
exitBatchEditTasks: "Exit batch edit",
editStageTitle: "Edit stage title",
batchEditTasks: "Edit tasks in batch",
dragSort: "Drag to reorder",
forceRerun: "Force rerun this step",
resizeStage: "Drag to resize stage",
afterLabel: "After-task",
notificationLabel: "Notification",
unsavedLeave: "This pipeline is not saved. Leave anyway?",
newPipeline: "New Pipeline",
newTask: "New Task",
newStage: "New Stage",
stageSuffix: " Stage",
saveBeforeRunTitle: "Save before running the pipeline",
saveBeforeRunContent: "Save it first?",
runUnsupported: "Running is not supported yet",
addStageAndTask: "Please add a stage and task first",
prerequisiteMissing: "The prerequisite output step {param} for {step} does not exist or is disabled",
selectPlugin: "Select plugin",
searchPlugin: "Search plugins",
searchResults: "Search results",
plusPluginRequired: "This plugin requires Certd Professional",
immediateRun: "Run immediately",
immediateRunHelper: "Run the pipeline immediately after saving",
operationSuccess: "Operation successful",
regenerate: "Regenerate",
webhookHelper: "Supports POST and GET requests (each pipeline has a unique Webhook URL)",
regenerateWebhookTitle: "Regenerate the Webhook URL?",
regenerateWebhookContent: "The old Webhook URL will become invalid",
confirmDeleteTask: "Delete this task?",
confirmDeleteStep: "Delete this step?",
confirmDeleteSelectedTasks: "Delete the selected {count} tasks?",
confirmDeleteSelectedSteps: "Delete the selected {count} steps?",
stepCopied: "Step configuration copied. You can paste it in another task editor",
allStepsCopied: "All steps copied. You can paste them in another task editor",
copyFirst: "Please copy first",
pasted: "Pasted successfully",
enterName: "Please enter a name",
pluginTypeFirst: "Please select a type first",
runStrategy: "Run strategy",
runStrategyNormal: "Run normally (required for certificate tasks unless you want this task to run every time)",
runStrategySkip: "Skip after success (choose this for other tasks)",
runStrategyDefault: "Usually keep the default",
runStrategyNormalHelp: "Run normally: runs every time; certificate tasks need this",
runStrategySkipHelp: "Skip after success: skip after one successful run and run again when the certificate changes",
triggerWebhook: "Webhook Trigger",
triggerSchedule: "Scheduled Trigger",
choose: "Select",
noLogsShort: "No logs",
statusSuccess: "success",
statusError: "error",
statusSkip: "skip",
statusStart: "running",
statusCanceled: "canceled",
statusNone: "not run",
statusDisabled: "disabled",
statusNoDeployCount: "insufficient runs",
pluginGroups: {
all: "All",
cert: "Certificate",
host: "Host",
cdn: "CDN",
panel: "Panel",
aliyun: "Alibaba Cloud",
huawei: "Huawei Cloud",
tencent: "Tencent Cloud",
volcengine: "Volcengine",
jdcloud: "JD Cloud",
ucloud: "UCloud",
baidu: "Baidu Cloud",
qiniu: "Qiniu Cloud",
aws: "Amazon Web Services",
other: "Other",
admin: "Administration",
},
},
pipelineForm: {
createTitle: "Create Certificate Pipeline",
moreParams: "More Parameters",
@@ -1,4 +1,50 @@
export default {
title: "Suite",
mySuite: "My Suite",
suiteBuy: "Suite Purchase",
myTrade: "My Orders",
inviteCommission: "Incentive Plan",
inviteSubtitle: "Invite friends and earn generous commissions",
promotionAgreement: "Promotion Agreement",
withdraw: "Withdraw",
withdrawAvailable: "Available to withdraw {amount}",
inviteCode: "Invitation Code",
orderTime: "Time",
inviteTime: "Invitation Time",
inviteeUsername: "Invited Username",
inviteLink: "Invitation Link",
myLevel: "My Level",
unset: "Not set",
commissionRate: "Commission Rate",
commissionAfterPayment: "Commission is calculated at this rate after a friend pays",
proportion: "Rate",
inviteSuccess: "Successful Invites",
incomeRecords: "Income Records",
enableFirst: "Please enable the incentive plan first",
enable: "Enable Incentive Plan",
disabled: "Incentive plan is not enabled",
totalIncome: "Total Income",
monthIncome: "This Month's Income",
promotionAmount: "Total Promotion Amount",
inviteeCount: "People Invited",
people: " people",
agreePromotion: "I have read and agree to the promotion agreement",
close: "Close",
postpone: "Later",
agreeAndEnable: "Agree and Enable",
agreeRequired: "Please check the agreement first",
enabledSuccess: "Incentive plan enabled",
purchaseSuccess: "Suite purchased successfully",
agreementPlaceholder: "Enter the promotion agreement users must accept before enabling the incentive plan",
activationExchange: "Redeem Activation Code",
activationCode: "Activation Code",
enterActivationCode: "Please enter the activation code",
enterCdkCode: "Please enter the CDK activation code",
activateSuccess: "Activated successfully",
exchangeFailed: "Redemption failed",
noSuite: "No suites available for purchase",
suiteHelperStack: "Note: Multiple suites and addons can be purchased, and their quantities are accumulated",
suiteHelperLatest: "Note: 1) Only the latest suite is active at a time; 2) Multiple addons can be purchased and take effect immediately; 3) Suite and addon quantities are accumulated",
order: {
confirmTitle: "Order Confirmation",
package: "Package",

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