mirror of
https://github.com/certd/certd.git
synced 2026-05-03 20:17:26 +08:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c1d92ff4b | |||
| 0a0f1e90e1 | |||
| 80092823db | |||
| 146098d9ce | |||
| 519743dbdb | |||
| 7ab661ecd7 | |||
| bb46cb08f7 | |||
| 028932c04a | |||
| 73e6480853 | |||
| aa176b081a | |||
| 267243e71b | |||
| 33fbef8380 | |||
| 45a128a050 | |||
| 2ddc668954 | |||
| 898bc9b9f2 | |||
| d8e5928523 | |||
| 36808a953e | |||
| 39d3f79026 | |||
| 6463e1ca22 | |||
| c985a13544 | |||
| ad76c5177c | |||
| 64b3184b28 | |||
| 2f1ad7201f | |||
| cd23ee2055 | |||
| e00830bebc | |||
| 00e6d580c2 | |||
| 9c7b419e8f | |||
| 95edc0d303 | |||
| 5991b1e37c | |||
| 1aa50cf53a | |||
| eab66e2d19 | |||
| 5b504f094f | |||
| 1460cb9ac1 | |||
| 53782cbf49 | |||
| 0ea22dddf0 |
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../.trae/skills
|
||||||
Vendored
+10
@@ -65,6 +65,16 @@
|
|||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"internalConsoleOptions": "neverOpen"
|
"internalConsoleOptions": "neverOpen"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "server-new",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceFolder}/packages/ui/certd-server",
|
||||||
|
"runtimeExecutable": "pnpm",
|
||||||
|
"runtimeArgs": ["dev-new"],
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"internalConsoleOptions": "neverOpen"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "server-local-plus",
|
"name": "server-local-plus",
|
||||||
"type": "node",
|
"type": "node",
|
||||||
|
|||||||
@@ -0,0 +1,211 @@
|
|||||||
|
# Certd 开发 Agent 上下文
|
||||||
|
|
||||||
|
这个文件是给在本仓库工作的开发 agent 看的常驻项目说明。后续会话进入仓库后,应先读取它,再按任务需要查看具体代码,避免每次都重新全量扫描项目。
|
||||||
|
|
||||||
|
## 项目用途
|
||||||
|
|
||||||
|
Certd 是一个支持私有化部署的 SSL/TLS 证书自动化管理平台。它提供 Web 管理台和后端服务,用于证书申请、续期、部署、监控、通知和开放 API 集成。
|
||||||
|
|
||||||
|
它不只是一个简单的 ACME 客户端。项目的核心产品模型是“证书流水线”:
|
||||||
|
|
||||||
|
- 通过 ACME 申请证书
|
||||||
|
- 支持 DNS-01、HTTP-01、CNAME 代理以及各类服务商集成来完成域名验证
|
||||||
|
- 支持将证书转换或导出为 pem、pfx、der、jks、p7b 等格式
|
||||||
|
- 支持把证书部署到主机、Nginx、Kubernetes、CDN、云厂商、面板等目标
|
||||||
|
- 支持通知用户,并监控站点证书过期时间
|
||||||
|
|
||||||
|
由于系统会保存证书、云厂商凭据、SSH 信息、API Key 等敏感数据,产品定位上强烈建议私有化/本地部署。
|
||||||
|
|
||||||
|
## 仓库结构
|
||||||
|
|
||||||
|
这是一个 pnpm + lerna 的 monorepo。
|
||||||
|
|
||||||
|
- `package.json`:根脚本和 workspace 元信息
|
||||||
|
- `pnpm-workspace.yaml`:workspace 包匹配规则
|
||||||
|
- `lerna.json`:lerna-lite 配置
|
||||||
|
- `docs/`:VitePress 文档站
|
||||||
|
- `docker/`:Docker 安装和运行相关文件
|
||||||
|
- `packages/core/acme-client/`:ACME 协议客户端,风格接近 node-acme-client
|
||||||
|
- `packages/core/basic/`:共享基础工具和基础设施
|
||||||
|
- `packages/core/pipeline/`:流水线核心、注册表、装饰器、插件模型、上下文、服务、通知等
|
||||||
|
- `packages/libs/`:共享集成与辅助库,例如 server、Huawei、JDCloud、Kubernetes、iframe
|
||||||
|
- `packages/plugins/plugin-lib/`:通用插件辅助能力和证书相关共享代码
|
||||||
|
- `packages/plugins/plugin-cert/`:证书流水线插件包
|
||||||
|
- `packages/pro/`:商业版/专业版相关包
|
||||||
|
- `packages/ui/certd-server/`:后端服务
|
||||||
|
- `packages/ui/certd-client/`:前端 Web 管理台
|
||||||
|
|
||||||
|
## 后端
|
||||||
|
|
||||||
|
主要后端包:`packages/ui/certd-server`。
|
||||||
|
|
||||||
|
技术栈:
|
||||||
|
|
||||||
|
- Node.js、ESM、TypeScript
|
||||||
|
- MidwayJS 3
|
||||||
|
- Koa
|
||||||
|
- TypeORM
|
||||||
|
- 默认使用 better-sqlite3,同时支持 PostgreSQL 和 MySQL
|
||||||
|
- 通过 `@certd/midway-flyway-js` 使用类似 Flyway 的 SQL 迁移机制
|
||||||
|
|
||||||
|
重要位置:
|
||||||
|
|
||||||
|
- `packages/ui/certd-server/src/config/config.default.ts`:默认服务、静态文件、数据库、定时任务、认证、上传、Swagger 配置
|
||||||
|
- `packages/ui/certd-server/src/config/`:环境与配置加载逻辑
|
||||||
|
- `packages/ui/certd-server/src/configuration.ts`:Midway 应用配置、中间件注册、组件导入
|
||||||
|
- `packages/ui/certd-server/src/modules/`:业务模块,例如 pipeline、cert、cron、monitor、login、open API、sys、plugin、cname、notification
|
||||||
|
- `packages/ui/certd-server/src/controller/`:按 API 领域划分的控制器
|
||||||
|
- `packages/ui/certd-server/src/plugins/`:后端内置的具体服务商、部署、通知等插件
|
||||||
|
- `packages/ui/certd-server/db/migration/`:数据库迁移 SQL
|
||||||
|
- `packages/ui/certd-server/data/`:本地运行数据,例如 SQLite 数据库和生成文件
|
||||||
|
- `packages/ui/certd-server/logs/`:运行日志
|
||||||
|
|
||||||
|
已观察到的默认开发配置:
|
||||||
|
|
||||||
|
- HTTP 端口:`7001`
|
||||||
|
- HTTPS 端口:`7002`
|
||||||
|
- 默认 SQLite 数据库:`./data/db.sqlite`
|
||||||
|
- 默认文件根目录:`./data/files`
|
||||||
|
|
||||||
|
常用脚本:
|
||||||
|
|
||||||
|
- 根目录 `pnpm run start:server`:以生产模式启动后端包
|
||||||
|
- 后端 `pnpm run dev`:启动 Midway watch/dev 服务
|
||||||
|
- 后端 `pnpm run test`:运行后端 mocha 测试
|
||||||
|
- 后端 `pnpm run build`:构建后端并导出插件元数据
|
||||||
|
|
||||||
|
## 前端
|
||||||
|
|
||||||
|
主要前端包:`packages/ui/certd-client`。
|
||||||
|
|
||||||
|
技术栈:
|
||||||
|
|
||||||
|
- Vue 3
|
||||||
|
- Vite
|
||||||
|
- TypeScript
|
||||||
|
- Ant Design Vue
|
||||||
|
- Fast Crud
|
||||||
|
- Pinia
|
||||||
|
- vue-router
|
||||||
|
- vue-i18n
|
||||||
|
- Tailwind/Windi 相关样式工具
|
||||||
|
|
||||||
|
重要位置:
|
||||||
|
|
||||||
|
- `packages/ui/certd-client/src/main.ts`:前端启动入口
|
||||||
|
- `packages/ui/certd-client/src/App.vue`:根组件
|
||||||
|
- `packages/ui/certd-client/src/api/`:API 调用封装
|
||||||
|
- `packages/ui/certd-client/src/router/`:路由
|
||||||
|
- `packages/ui/certd-client/src/store/`:Pinia store
|
||||||
|
- `packages/ui/certd-client/src/views/certd/`:核心产品页面,例如流水线、证书、授权、监控、通知、开放 API、项目、支付、插件
|
||||||
|
- `packages/ui/certd-client/src/components/`:共享 UI 组件
|
||||||
|
- `packages/ui/certd-client/src/locales/`:国际化
|
||||||
|
|
||||||
|
常用脚本:
|
||||||
|
|
||||||
|
- 前端 `pnpm dev`:启动 Vite 开发服务
|
||||||
|
- 前端 `pnpm build`:生产构建
|
||||||
|
- 前端 `pnpm tsc`:类型检查
|
||||||
|
- 前端 `pnpm test:unit`:Vitest 单元测试
|
||||||
|
|
||||||
|
## 流水线与插件模型
|
||||||
|
|
||||||
|
项目最关键的架构概念是证书流水线。
|
||||||
|
|
||||||
|
可以从 `packages/core/pipeline/src/index.ts` 入手,它导出:
|
||||||
|
|
||||||
|
- `core`
|
||||||
|
- `dt`
|
||||||
|
- `access`
|
||||||
|
- `registry`
|
||||||
|
- `plugin`
|
||||||
|
- `context`
|
||||||
|
- `decorator`
|
||||||
|
- `service`
|
||||||
|
- `notification`
|
||||||
|
|
||||||
|
插件是核心能力,不是边缘功能。新增服务商、DNS 验证、证书部署、通知方式等能力,通常应该放在插件包里,或放在 `packages/ui/certd-server/src/plugins/<plugin-name>/` 下。
|
||||||
|
|
||||||
|
后端已看到的插件类型包括:
|
||||||
|
|
||||||
|
- DNS 和注册商服务商:Aliyun、Tencent、Cloudflare、Huawei、JDCloud、AWS、Azure、Google、GoDaddy、Namesilo、Xinnet、West、UCloud、Qiniu、Upyun、Volcengine 等
|
||||||
|
- 部署目标:host、Kubernetes、Nginx Proxy Manager、APISIX、Proxmox、QNAP、Dokploy、GoEdge、各类 CDN、各类面板
|
||||||
|
- 系统/产品插件:notification、captcha、oauth、admin、plus/pro、demo/template
|
||||||
|
|
||||||
|
当修改证书申请、验证、部署或通知行为时,先判断改动属于哪里:
|
||||||
|
|
||||||
|
- ACME client 代码
|
||||||
|
- pipeline 核心抽象
|
||||||
|
- 后端 module/service/entity/controller
|
||||||
|
- 某个具体插件实现
|
||||||
|
- 前端 view/form/schema
|
||||||
|
|
||||||
|
如果只是某个服务商或部署目标的问题,不要轻易修改共享 pipeline/core 行为,除非确实是可复用的公共能力。
|
||||||
|
|
||||||
|
## 数据与迁移
|
||||||
|
|
||||||
|
后端使用 TypeORM 实体加 SQL 迁移。
|
||||||
|
|
||||||
|
重点查看:
|
||||||
|
|
||||||
|
- `packages/ui/certd-server/src/modules/**/entity/*.ts`
|
||||||
|
- `packages/ui/certd-server/db/migration/*.sql`
|
||||||
|
|
||||||
|
默认配置中 `synchronize: false`,所以涉及表结构变更时,通常应该添加或更新迁移脚本,而不是依赖 TypeORM 自动同步。
|
||||||
|
|
||||||
|
## 开发注意事项
|
||||||
|
|
||||||
|
- 中文 README 在部分 PowerShell 环境中可能显示乱码;`README_en.md` 可读性更好,且包含同样的高层项目说明。
|
||||||
|
- 初次整理时观察到当前分支为 `v2-dev`。
|
||||||
|
- 根包管理器是 pnpm,不要引入 npm/yarn lockfile。
|
||||||
|
- 优先沿用现有模块、插件、服务模式,再考虑新增抽象。
|
||||||
|
- `packages/ui/certd-server/data/`、`logs/`、生成的 metadata/dist 等通常视为运行时或构建产物,除非任务明确要求处理它们。
|
||||||
|
- 注意本地数据和配置里可能包含凭据、证书材料等敏感信息。
|
||||||
|
|
||||||
|
## 插件开发技能
|
||||||
|
|
||||||
|
仓库内置了 Certd 插件开发技能,供 Trae 和 Codex 共用:
|
||||||
|
|
||||||
|
- Trae 入口:`.trae/skills`
|
||||||
|
- Codex 入口:`.codex/skills`
|
||||||
|
|
||||||
|
其中 `.codex/skills` 是指向 `.trae/skills` 的目录链接,不要复制出第二份技能内容。更新技能时只维护 `.trae/skills` 下的原始文件,Codex 会通过 `.codex/skills` 读取同一份内容。
|
||||||
|
|
||||||
|
当前技能包括:
|
||||||
|
|
||||||
|
- `access-plugin-dev`:开发 Access 授权插件
|
||||||
|
- `dns-provider-dev`:开发 DNS Provider 插件
|
||||||
|
- `task-plugin-dev`:开发 Task 部署任务插件
|
||||||
|
- `plugin-converter`:将插件转换为 YAML 配置
|
||||||
|
|
||||||
|
做插件相关任务时,先读取对应技能目录下的 `SKILL.md`,再进入具体实现。若用户在插件开发中指出更好的做法,应总结并更新对应技能。
|
||||||
|
|
||||||
|
## 快速定向命令
|
||||||
|
|
||||||
|
进入项目后,优先使用这些有目标的读取命令,而不是立刻全仓库扫描:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-Content -Encoding utf8 package.json
|
||||||
|
Get-Content -Encoding utf8 pnpm-workspace.yaml
|
||||||
|
Get-Content -Encoding utf8 lerna.json
|
||||||
|
Get-Content -Encoding utf8 README_en.md -TotalCount 180
|
||||||
|
Get-Content -Encoding utf8 packages\ui\certd-server\package.json
|
||||||
|
Get-Content -Encoding utf8 packages\ui\certd-client\package.json
|
||||||
|
Get-ChildItem packages\ui\certd-server\src\modules
|
||||||
|
Get-ChildItem packages\ui\certd-server\src\plugins
|
||||||
|
Get-ChildItem packages\ui\certd-client\src\views\certd
|
||||||
|
```
|
||||||
|
|
||||||
|
## 本仓库 Agent 工作方式
|
||||||
|
|
||||||
|
- 先读本文件,再按用户任务查看相关 package/module。
|
||||||
|
- 在 PowerShell 中读取中文、Markdown、locale、文档类文件时,显式使用 `Get-Content -Encoding utf8`;如果仍然显示乱码,再先执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()` 后重试。
|
||||||
|
- 做后端任务时,先定位 `packages/ui/certd-server/src/modules` 下的模块,以及相关 entity/service/controller。
|
||||||
|
- 做前端任务时,先定位 `packages/ui/certd-client/src/views/certd` 下的页面,再找对应 `src/api`。
|
||||||
|
- 做服务商、DNS、部署、通知相关任务时,先看 `packages/ui/certd-server/src/plugins`,再看 `packages/plugins/plugin-lib` 里的共享辅助能力。
|
||||||
|
- 做数据库结构变更时,添加或更新迁移脚本,不要依赖 TypeORM 自动同步。
|
||||||
|
- 实现新功能或修复行为缺陷前,先补对应单元测试,并先运行测试确认它处于失败状态;再实现功能或修复代码,反复运行聚焦单元测试直到通过。若某项改动确实不适合先写单元测试,应在回复中说明原因和替代验证方式。
|
||||||
|
- 后补单元测试时,应先基于对正确行为的实际预期编写测试,而不是为了迎合现有实现改写预期;如果运行后出现红灯,且通过测试需要修改已有实现,应先向用户确认这是确实的 bug,还是原本需求/既有行为就是如此;确认后再修改原始实现,避免把测试补充变成未经确认的行为改动。
|
||||||
|
- 后端纯单元测试用例放在 `src` 目录内,并尽量与被测文件相邻,例如 `src/utils/random.test.ts`;对应 `test:unit` 只跑 `src/**/*.test.ts`,构建/打包配置应排除这些 `*.test.ts` 文件。
|
||||||
|
- 前端 TS/Vue/locale 等文件改动后,优先只对本次改动文件运行项目现有自动格式化/修复,例如 `corepack pnpm --dir packages\ui\certd-client exec prettier --write <files>` 和 `corepack pnpm --dir packages\ui\certd-client exec eslint --fix <files>`;不要为了格式化无关文件而扩大 diff。项目保留了 `tslint` 依赖,但当前主要使用 ESLint + Prettier。
|
||||||
|
- 优先对改动包运行聚焦的测试或类型检查;只有跨包影响明显时再考虑全 monorepo 构建。
|
||||||
@@ -3,6 +3,21 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 调整手机版首页标题被挤开的bug ([eab66e2](https://github.com/certd/certd/commit/eab66e2d1988635985745f2d1b227b958969ee00))
|
||||||
|
* 修复腾讯云clb部署报缺少sslmode参数的bug ([2f1ad72](https://github.com/certd/certd/commit/2f1ad7201f5ed9e00368a28b9e40907d4b415852))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 524错误时重试3次 ([00e6d58](https://github.com/certd/certd/commit/00e6d580c2f54af70fe96a214aff87c4b96426c2))
|
||||||
|
* 阿里云证书订单支持获取2.0的订单 ([64b3184](https://github.com/certd/certd/commit/64b3184b286fee996002d857b0de588452abdadd))
|
||||||
|
* 优化流水线执行时的状态保存性能 ([e00830b](https://github.com/certd/certd/commit/e00830bebcfe6344499e490bc174de96f9fb22d6))
|
||||||
|
* 增加权威NS检查开关,某些用户服务器禁止向黑名单NS服务器发请求 ([1aa50cf](https://github.com/certd/certd/commit/1aa50cf53a0deab752f35ec973912e41ab8161b6))
|
||||||
|
* 支持页脚自定义 ([c985a13](https://github.com/certd/certd/commit/c985a13544aa31b0eb0783f9a3193a7e8bdc6ed6))
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -3,6 +3,47 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 调整手机版首页标题被挤开的bug ([eab66e2](https://github.com/certd/certd/commit/eab66e2d1988635985745f2d1b227b958969ee00))
|
||||||
|
* 修复腾讯云clb部署报缺少sslmode参数的bug ([2f1ad72](https://github.com/certd/certd/commit/2f1ad7201f5ed9e00368a28b9e40907d4b415852))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 524错误时重试3次 ([00e6d58](https://github.com/certd/certd/commit/00e6d580c2f54af70fe96a214aff87c4b96426c2))
|
||||||
|
* 阿里云证书订单支持获取2.0的订单 ([64b3184](https://github.com/certd/certd/commit/64b3184b286fee996002d857b0de588452abdadd))
|
||||||
|
* 优化流水线执行时的状态保存性能 ([e00830b](https://github.com/certd/certd/commit/e00830bebcfe6344499e490bc174de96f9fb22d6))
|
||||||
|
* 增加权威NS检查开关,某些用户服务器禁止向黑名单NS服务器发请求 ([1aa50cf](https://github.com/certd/certd/commit/1aa50cf53a0deab752f35ec973912e41ab8161b6))
|
||||||
|
* 支持页脚自定义 ([c985a13](https://github.com/certd/certd/commit/c985a13544aa31b0eb0783f9a3193a7e8bdc6ed6))
|
||||||
|
|
||||||
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复列表页面底部滚动条与表格之间有空白间隙的bug ([71cfcad](https://github.com/certd/certd/commit/71cfcad2a15aac0badd85a10c4012a1e713654d1))
|
||||||
|
* 修复流水线未编辑模式下也提示未保存的bug ([64a3503](https://github.com/certd/certd/commit/64a350364d820725b5e69d22ac2416809092f97d))
|
||||||
|
* 修复商业版设置了公共eab,创建流水线仍然会显示需要配置eab的bug ([24dff05](https://github.com/certd/certd/commit/24dff05f6427dadec1e40350214c0167e1d6a73d))
|
||||||
|
* 修复站点监控某些情况下获取不到证书的bug ([a2bbc7e](https://github.com/certd/certd/commit/a2bbc7e27298821d75a36abac6ec05d86dcf51f4))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 支持google dns插件 ([edc7bfc](https://github.com/certd/certd/commit/edc7bfc23043c2c6ef5f3564392f8aac6661c4bf))
|
||||||
|
* 阿里云waf支持云产品接入方式应用的证书部署 ([2f7514a](https://github.com/certd/certd/commit/2f7514a2e7d89a34f833401a983149e667da911b))
|
||||||
|
* 模版创建流水线支持随机时间 ([575415b](https://github.com/certd/certd/commit/575415b93a3e10e1c6e5644f71ddc711ea6f8adc))
|
||||||
|
* 商业版支持配置证书申请插件参数 ([7ac789c](https://github.com/certd/certd/commit/7ac789c9c7e91cdf08dfdae1bb49186552e370e3))
|
||||||
|
* 添加全新的未登录首页和路由配置 ([d1988dc](https://github.com/certd/certd/commit/d1988dc982440472ecf61847ccad76e4c96a80fb))
|
||||||
|
* 添加Azure DNS插件支持及文档 ([1f1d687](https://github.com/certd/certd/commit/1f1d6873172d71fadaa5a0005e1d6f3f528096fc))
|
||||||
|
* 添加HiPMDnsmgr DNS提供商的支持 @WUHINS ([296dcab](https://github.com/certd/certd/commit/296dcab4c7c26cb3f9da1ff748cc6a6b7d83edda))
|
||||||
|
* 为DNS解析器添加超时配置,避免查询时间过长 ([cc5154e](https://github.com/certd/certd/commit/cc5154e04e87f648111119b4eeb4e3cb4dd6cc41))
|
||||||
|
* 优化权威域名服务器查询超时时长 ([77db5ec](https://github.com/certd/certd/commit/77db5ecd12c51293e4de178e43ca0067bc70b46d))
|
||||||
|
* 支持部署到nginx-proxy-manager ([2e6e9ed](https://github.com/certd/certd/commit/2e6e9ed9255bcf178edb0eb00d93a7f13c214430))
|
||||||
|
* 支持一键安装脚本 ([dc969dd](https://github.com/certd/certd/commit/dc969dd7edb6934a29d6657afefe6f8af056741c))
|
||||||
|
* 支持主动修改绑定url地址 ([11b7cfe](https://github.com/certd/certd/commit/11b7cfe5cb7e88e6ebd68d53acb4e5b556550ca9))
|
||||||
|
* apisix支持v2 ([23b4658](https://github.com/certd/certd/commit/23b465867244b199bab9b61863a5ca43644834a9))
|
||||||
|
* **technitium:** 添加Technitium DNS Server插件支持 ([edeb817](https://github.com/certd/certd/commit/edeb817c39597e4fa73a17ff4ca3f712f0320fec))
|
||||||
|
|
||||||
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
## [1.39.10](https://github.com/certd/certd/compare/v1.39.9...v1.39.10) (2026-04-11)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# 任务插件
|
# 任务插件
|
||||||
共 `131` 款任务插件
|
共 `132` 款任务插件
|
||||||
## 1. 证书申请
|
## 1. 证书申请
|
||||||
|
|
||||||
| 序号 | 名称 | 说明 |
|
| 序号 | 名称 | 说明 |
|
||||||
|-----|-----|-----|
|
|-----|-----|-----|
|
||||||
| 1.| **证书申请(JS版)** | 免费通配符域名证书申请,支持多个域名打到同一个证书上 |
|
| 1.| **证书申请(JS版)** | 免费通配符域名证书申请,支持多个域名打到同一个证书上 |
|
||||||
| 2.| **已有证书托管** | 手动上传自定义证书后,自动部署(每次证书有更新,都需要手动上传一次) |
|
| 2.| **已有证书托管** | 手动上传自定义证书后,自动部署(每次证书有更新,都需要手动上传一次) |
|
||||||
| 3.| **获取阿里云订阅证书** | 从阿里云拉取订阅模式的商用证书 |
|
| 3.| **获取阿里云订阅证书** | 从阿里云拉取订阅模式的商用证书(支持 API 1.0 和 2.0) |
|
||||||
| 4.| **证书申请(Lego)** | 支持海量DNS解析提供商,推荐使用,一样的免费通配符域名证书申请,支持多个域名打到同一个证书上 |
|
| 4.| **证书申请(Lego)** | 支持海量DNS解析提供商,推荐使用,一样的免费通配符域名证书申请,支持多个域名打到同一个证书上 |
|
||||||
## 2. 主机
|
## 2. 主机
|
||||||
|
|
||||||
@@ -134,8 +134,9 @@
|
|||||||
| 4.| **火山引擎-部署证书至DCDN** | 部署至火山引擎全站加速 |
|
| 4.| **火山引擎-部署证书至DCDN** | 部署至火山引擎全站加速 |
|
||||||
| 5.| **火山引擎-部署证书至Live** | 部署至火山引擎视频直播 |
|
| 5.| **火山引擎-部署证书至Live** | 部署至火山引擎视频直播 |
|
||||||
| 6.| **火山引擎-部署证书至TOS自定义域名** | 仅限TOS自定义域名,加速域名请选择火山引擎的CDN插件 |
|
| 6.| **火山引擎-部署证书至TOS自定义域名** | 仅限TOS自定义域名,加速域名请选择火山引擎的CDN插件 |
|
||||||
| 7.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播 |
|
| 7.| **火山引擎-替换VKE证书** | 替换火山引擎VKE集群中的TLS Secret证书 |
|
||||||
| 8.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
|
| 8.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播 |
|
||||||
|
| 9.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
|
||||||
## 9. 京东云
|
## 9. 京东云
|
||||||
|
|
||||||
| 序号 | 名称 | 说明 |
|
| 序号 | 名称 | 说明 |
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npmClient": "pnpm",
|
"npmClient": "pnpm",
|
||||||
"version": "1.39.11"
|
"version": "1.39.12"
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -25,8 +25,8 @@
|
|||||||
"commitAll": "git add . && git commit -m \"build: publish\" && git push && pnpm run commitPro",
|
"commitAll": "git add . && git commit -m \"build: publish\" && git push && pnpm run commitPro",
|
||||||
"commitPro": "cd ./packages/pro/ && git add . && git commit -m \"build: publish\" && git push",
|
"commitPro": "cd ./packages/pro/ && git add . && git commit -m \"build: publish\" && git push",
|
||||||
"copylogs": "copyfiles \"CHANGELOG.md\" ./docs/guide/changelogs/",
|
"copylogs": "copyfiles \"CHANGELOG.md\" ./docs/guide/changelogs/",
|
||||||
"prepublishOnly1": "pnpm run check && lerna run build ",
|
"prepublishOnly1": "pnpm run test:unit && pnpm run check && lerna run build ",
|
||||||
"prepublishOnly2": "pnpm run check && pnpm run before-build && lerna run build && pnpm run plugin-doc-gen",
|
"prepublishOnly2": "pnpm run test:unit && pnpm run check && pnpm run before-build && lerna run build && pnpm run plugin-doc-gen",
|
||||||
"before-build": "pnpm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
|
"before-build": "pnpm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
|
||||||
"deploy1": "node --experimental-json-modules ./scripts/deploy.js ",
|
"deploy1": "node --experimental-json-modules ./scripts/deploy.js ",
|
||||||
"check": "node --experimental-json-modules ./scripts/publish-check.js",
|
"check": "node --experimental-json-modules ./scripts/publish-check.js",
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
"docs:dev": "vitepress dev docs",
|
"docs:dev": "vitepress dev docs",
|
||||||
"docs:build": "pnpm run copylogs && vitepress build docs",
|
"docs:build": "pnpm run copylogs && vitepress build docs",
|
||||||
"docs:preview": "vitepress preview docs",
|
"docs:preview": "vitepress preview docs",
|
||||||
|
"test:unit": "pnpm -r --workspace-concurrency=1 run test:unit",
|
||||||
"pub": "echo 1",
|
"pub": "echo 1",
|
||||||
"dev": "pnpm run -r --parallel compile ",
|
"dev": "pnpm run -r --parallel compile ",
|
||||||
"pub_all":"pnpm run -r --parallel pub ",
|
"pub_all":"pnpm run -r --parallel pub ",
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/publishlab/node-acme-client/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 增加权威NS检查开关,某些用户服务器禁止向黑名单NS服务器发请求 ([1aa50cf](https://github.com/publishlab/node-acme-client/commit/1aa50cf53a0deab752f35ec973912e41ab8161b6))
|
||||||
|
|
||||||
## [1.39.11](https://github.com/publishlab/node-acme-client/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/publishlab/node-acme-client/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "Simple and unopinionated ACME client",
|
"description": "Simple and unopinionated ACME client",
|
||||||
"private": false,
|
"private": false,
|
||||||
"author": "nmorsman",
|
"author": "nmorsman",
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "scr/index.js",
|
"module": "scr/index.js",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.39.11",
|
"@certd/basic": "^1.39.12",
|
||||||
"@peculiar/x509": "^1.11.0",
|
"@peculiar/x509": "^1.11.0",
|
||||||
"asn1js": "^3.0.5",
|
"asn1js": "^3.0.5",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
@@ -52,6 +52,7 @@
|
|||||||
"lint-types": "tsd",
|
"lint-types": "tsd",
|
||||||
"prepublishOnly": "npm run build-docs",
|
"prepublishOnly": "npm run build-docs",
|
||||||
"test": "mocha -t 60000 \"test/setup.js\" \"test/**/*.spec.js\"",
|
"test": "mocha -t 60000 \"test/setup.js\" \"test/**/*.spec.js\"",
|
||||||
|
"test:unit": "echo no unit tests",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "echo '1'"
|
"compile": "echo '1'"
|
||||||
},
|
},
|
||||||
@@ -70,5 +71,5 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -494,7 +494,7 @@ class AcmeClient {
|
|||||||
throw new Error('Unable to verify ACME challenge, URL not found');
|
throw new Error('Unable to verify ACME challenge, URL not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const {challenges} = createChallengeFn({logger:this.logger});
|
const {challenges} = createChallengeFn({logger:this.logger,walkFromAuthoritative: this.opts.walkFromAuthoritative});
|
||||||
|
|
||||||
const verify = challenges
|
const verify = challenges
|
||||||
if (typeof verify[challenge.type] === 'undefined') {
|
if (typeof verify[challenge.type] === 'undefined') {
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ async function resolveDomainBySoaRecord(recordName, logger = log) {
|
|||||||
|
|
||||||
async function getAuthoritativeDnsResolver(recordName, logger = log) {
|
async function getAuthoritativeDnsResolver(recordName, logger = log) {
|
||||||
logger(`获取域名${recordName}的权威NS服务器: `);
|
logger(`获取域名${recordName}的权威NS服务器: `);
|
||||||
const resolver = new dns.Resolver({ timeout: 10000,maxTimeout: 60000 });
|
const resolver = new dns.Resolver({timeout: 2000,tries: 2});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* Resolve root domain by SOA */
|
/* Resolve root domain by SOA */
|
||||||
@@ -352,3 +352,5 @@ export {
|
|||||||
resolveDomainBySoaRecord
|
resolveDomainBySoaRecord
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,23 @@
|
|||||||
|
|
||||||
import dnsSdk from "dns"
|
import dnsSdk from "dns"
|
||||||
import https from 'https'
|
import https from 'https'
|
||||||
import {log as defaultLog} from './logger.js'
|
import { log as defaultLog } from './logger.js'
|
||||||
import axios from './axios.js'
|
import axios from './axios.js'
|
||||||
import * as util from './util.js'
|
import * as util from './util.js'
|
||||||
import {isAlpnCertificateAuthorizationValid} from './crypto/index.js'
|
import { isAlpnCertificateAuthorizationValid } from './crypto/index.js'
|
||||||
import {utils} from '@certd/basic'
|
import { utils } from '@certd/basic'
|
||||||
|
|
||||||
const dns = dnsSdk.promises
|
const dns = dnsSdk.promises
|
||||||
|
|
||||||
|
let walkFromAuthoritative = true
|
||||||
|
export function setWalkFromAuthoritative(value = true) {
|
||||||
|
walkFromAuthoritative = value
|
||||||
|
}
|
||||||
|
|
||||||
export function createChallengeFn(opts = {}){
|
export function createChallengeFn(opts = {}) {
|
||||||
const logger = opts?.logger || {info:defaultLog,error:defaultLog,warn:defaultLog,debug:defaultLog}
|
const logger = opts?.logger || { info: defaultLog, error: defaultLog, warn: defaultLog, debug: defaultLog }
|
||||||
|
|
||||||
const log = function(...args){
|
const log = function (...args) {
|
||||||
logger.info(...args)
|
logger.info(...args)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -31,201 +35,212 @@ export function createChallengeFn(opts = {}){
|
|||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function verifyHttpChallenge(authz, challenge, keyAuthorization, suffix = `/.well-known/acme-challenge/${challenge.token}`) {
|
async function verifyHttpChallenge(authz, challenge, keyAuthorization, suffix = `/.well-known/acme-challenge/${challenge.token}`) {
|
||||||
|
|
||||||
async function doQuery(challengeUrl){
|
async function doQuery(challengeUrl) {
|
||||||
log(`正在测试请求 ${challengeUrl} `)
|
log(`正在测试请求 ${challengeUrl} `)
|
||||||
// const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
|
// const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
|
||||||
// const challengeUrl = `https://${authz.identifier.value}:${httpsPort}${suffix}`;
|
// const challengeUrl = `https://${authz.identifier.value}:${httpsPort}${suffix}`;
|
||||||
|
|
||||||
/* May redirect to HTTPS with invalid/self-signed cert - https://letsencrypt.org/docs/challenge-types/#http-01-challenge */
|
/* May redirect to HTTPS with invalid/self-signed cert - https://letsencrypt.org/docs/challenge-types/#http-01-challenge */
|
||||||
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
||||||
|
|
||||||
|
log(`Sending HTTP query to ${authz.identifier.value}, suffix: ${suffix}, port: ${httpPort}`);
|
||||||
|
let data = ""
|
||||||
|
try {
|
||||||
|
const resp = await axios.get(challengeUrl, { httpsAgent });
|
||||||
|
data = (resp.data || '').replace(/\s+$/, '');
|
||||||
|
} catch (e) {
|
||||||
|
log(`[error] HTTP request error from ${authz.identifier.value}`, e.message);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data || (data !== keyAuthorization)) {
|
||||||
|
log(`[error] Authorization not found in HTTP response from ${authz.identifier.value}`);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
log(`Sending HTTP query to ${authz.identifier.value}, suffix: ${suffix}, port: ${httpPort}`);
|
|
||||||
let data = ""
|
|
||||||
try{
|
|
||||||
const resp = await axios.get(challengeUrl, { httpsAgent });
|
|
||||||
data = (resp.data || '').replace(/\s+$/, '');
|
|
||||||
}catch (e) {
|
|
||||||
log(`[error] HTTP request error from ${authz.identifier.value}`,e.message);
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data || (data !== keyAuthorization)) {
|
const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
|
||||||
log(`[error] Authorization not found in HTTP response from ${authz.identifier.value}`);
|
let host = authz.identifier.value;
|
||||||
return false
|
if (utils.domain.isIpv6(host)) {
|
||||||
|
host = `[${host}]`;
|
||||||
}
|
}
|
||||||
return true
|
const challengeUrl = `http://${host}:${httpPort}${suffix}`;
|
||||||
|
|
||||||
}
|
if (!await doQuery(challengeUrl)) {
|
||||||
|
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
|
||||||
const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
|
const httpsChallengeUrl = `https://${host}:${httpsPort}${suffix}`;
|
||||||
let host = authz.identifier.value;
|
const res = await doQuery(httpsChallengeUrl)
|
||||||
if(utils.domain.isIpv6(host)){
|
if (!res) {
|
||||||
host = `[${host}]`;
|
throw new Error(`[error] 验证失败,请检查以上测试url是否可以正常访问`);
|
||||||
}
|
}
|
||||||
const challengeUrl = `http://${host}:${httpPort}${suffix}`;
|
|
||||||
|
|
||||||
if (!await doQuery(challengeUrl)) {
|
|
||||||
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
|
|
||||||
const httpsChallengeUrl = `https://${host}:${httpsPort}${suffix}`;
|
|
||||||
const res = await doQuery(httpsChallengeUrl)
|
|
||||||
if (!res) {
|
|
||||||
throw new Error(`[error] 验证失败,请检查以上测试url是否可以正常访问`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk DNS until TXT records are found
|
||||||
|
*/
|
||||||
|
|
||||||
log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`);
|
async function walkDnsChallengeRecord(recordName, resolver = dns, deep = 0) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
let records = [];
|
||||||
* Walk DNS until TXT records are found
|
|
||||||
*/
|
|
||||||
|
|
||||||
async function walkDnsChallengeRecord(recordName, resolver = dns,deep = 0) {
|
const isAuthoritative = resolver === dns
|
||||||
|
/* Resolve TXT records */
|
||||||
let records = [];
|
try {
|
||||||
|
log(`检查域名 ${recordName} 的TXT记录(from ${isAuthoritative ? '本地DNS' : '权威DNS服务器'})`);
|
||||||
/* Resolve TXT records */
|
const txtRecords = await resolver.resolveTxt(recordName);
|
||||||
try {
|
if (txtRecords && txtRecords.length) {
|
||||||
log(`检查域名 ${recordName} 的TXT记录`);
|
log(`找到 ${txtRecords.length} 条 TXT记录( ${recordName})`);
|
||||||
const txtRecords = await resolver.resolveTxt(recordName);
|
log(`TXT records: ${JSON.stringify(txtRecords)}`);
|
||||||
if (txtRecords && txtRecords.length) {
|
records = records.concat(...txtRecords);
|
||||||
log(`找到 ${txtRecords.length} 条 TXT记录( ${recordName})`);
|
}
|
||||||
log(`TXT records: ${JSON.stringify(txtRecords)}`);
|
} catch (e) {
|
||||||
records = records.concat(...txtRecords);
|
log(`解析 TXT 记录出错, ${recordName} :${e.message}`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
log(`解析 TXT 记录出错, ${recordName} :${e.message}`);
|
/* Resolve CNAME record first */
|
||||||
|
try {
|
||||||
|
log(`检查是否存在CNAME映射: ${recordName}`);
|
||||||
|
const cnameRecords = await resolver.resolveCname(recordName);
|
||||||
|
|
||||||
|
if (cnameRecords.length) {
|
||||||
|
const cnameRecord = cnameRecords[0];
|
||||||
|
log(`已找到${recordName}的CNAME记录,将检查: ${cnameRecord}`);
|
||||||
|
let res = await walkTxtRecord(cnameRecord, deep + 1);
|
||||||
|
if (res && res.length) {
|
||||||
|
log(`从CNAME中找到TXT记录: ${JSON.stringify(res)}`);
|
||||||
|
records = records.concat(...res);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log(`没有CNAME映射(${recordName})`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(`检查CNAME出错(${recordName}) :${e.message}`);
|
||||||
|
}
|
||||||
|
return records
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolve CNAME record first */
|
async function walkTxtRecord(recordName, deep = 0) {
|
||||||
try {
|
if (deep > 5) {
|
||||||
log(`检查是否存在CNAME映射: ${recordName}`);
|
log(`walkTxtRecord too deep (#${deep}) , skip walk`)
|
||||||
const cnameRecords = await resolver.resolveCname(recordName);
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
if (cnameRecords.length) {
|
const txtRecords = []
|
||||||
const cnameRecord = cnameRecords[0];
|
try {
|
||||||
log(`已找到${recordName}的CNAME记录,将检查: ${cnameRecord}`);
|
/* Default DNS resolver first */
|
||||||
let res= await walkTxtRecord(cnameRecord,deep+1);
|
log('从本地DNS服务器获取TXT解析记录');
|
||||||
if (res && res.length) {
|
const res = await walkDnsChallengeRecord(recordName, dns, deep);
|
||||||
log(`从CNAME中找到TXT记录: ${JSON.stringify(res)}`);
|
if (res && res.length > 0) {
|
||||||
records = records.concat(...res);
|
for (const item of res) {
|
||||||
|
txtRecords.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
log(`本地获取TXT解析记录失败:${e.message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (walkFromAuthoritative !==false) {
|
||||||
|
try {
|
||||||
|
/* Authoritative DNS resolver */
|
||||||
|
log(`从域名权威服务器获取TXT解析记录`);
|
||||||
|
const authoritativeResolver = await util.getAuthoritativeDnsResolver(recordName, log);
|
||||||
|
const res = await walkDnsChallengeRecord(recordName, authoritativeResolver, deep);
|
||||||
|
if (res && res.length > 0) {
|
||||||
|
for (const item of res) {
|
||||||
|
txtRecords.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log(`权威服务器获取TXT解析记录失败:${e.message}`)
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
log(`没有CNAME映射(${recordName})`);
|
log(`跳过从权威服务器获取TXT解析记录`);
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log(`检查CNAME出错(${recordName}) :${e.message}`);
|
|
||||||
}
|
|
||||||
return records
|
|
||||||
}
|
|
||||||
|
|
||||||
async function walkTxtRecord(recordName,deep = 0) {
|
|
||||||
if(deep >5){
|
|
||||||
log(`walkTxtRecord too deep (#${deep}) , skip walk`)
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const txtRecords = []
|
|
||||||
try {
|
|
||||||
/* Default DNS resolver first */
|
|
||||||
log('从本地DNS服务器获取TXT解析记录');
|
|
||||||
const res = await walkDnsChallengeRecord(recordName,dns,deep);
|
|
||||||
if (res && res.length > 0) {
|
|
||||||
for (const item of res) {
|
|
||||||
txtRecords.push(item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
log(`本地获取TXT解析记录失败:${e.message}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
try{
|
if (txtRecords.length === 0) {
|
||||||
/* Authoritative DNS resolver */
|
throw new Error(`没有找到TXT解析记录(${recordName})`);
|
||||||
log(`从域名权威服务器获取TXT解析记录`);
|
|
||||||
const authoritativeResolver = await util.getAuthoritativeDnsResolver(recordName,log);
|
|
||||||
const res = await walkDnsChallengeRecord(recordName, authoritativeResolver,deep);
|
|
||||||
if (res && res.length > 0) {
|
|
||||||
for (const item of res) {
|
|
||||||
txtRecords.push(item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}catch (e) {
|
return txtRecords;
|
||||||
log(`权威服务器获取TXT解析记录失败:${e.message}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (txtRecords.length === 0) {
|
/**
|
||||||
throw new Error(`没有找到TXT解析记录(${recordName})`);
|
* Verify ACME DNS challenge
|
||||||
}
|
*
|
||||||
return txtRecords;
|
* https://datatracker.ietf.org/doc/html/rfc8555#section-8.4
|
||||||
}
|
*
|
||||||
|
* @param {object} authz Identifier authorization
|
||||||
|
* @param {object} challenge Authorization challenge
|
||||||
|
* @param {string} keyAuthorization Challenge key authorization
|
||||||
|
* @param {string} [prefix] DNS prefix
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
async function verifyDnsChallenge(authz, challenge, keyAuthorization, prefix = '_acme-challenge.') {
|
||||||
* Verify ACME DNS challenge
|
const recordName = `${prefix}${authz.identifier.value}`;
|
||||||
*
|
log(`本地校验TXT记录): ${recordName}`);
|
||||||
* https://datatracker.ietf.org/doc/html/rfc8555#section-8.4
|
let recordValues = await walkTxtRecord(recordName, 0, walkFromAuthoritative);
|
||||||
*
|
//去重
|
||||||
* @param {object} authz Identifier authorization
|
recordValues = [...new Set(recordValues)];
|
||||||
* @param {object} challenge Authorization challenge
|
log(`DNS查询成功, 找到 ${recordValues.length} 条TXT记录:${recordValues}`);
|
||||||
* @param {string} keyAuthorization Challenge key authorization
|
if (!recordValues.length || !recordValues.includes(keyAuthorization)) {
|
||||||
* @param {string} [prefix] DNS prefix
|
const err = `没有找到需要的DNS TXT记录: ${recordName},期望:${keyAuthorization},结果:${recordValues}`
|
||||||
* @returns {Promise<boolean>}
|
throw new Error(err);
|
||||||
*/
|
}
|
||||||
|
|
||||||
async function verifyDnsChallenge(authz, challenge, keyAuthorization, prefix = '_acme-challenge.') {
|
log(`关键授权匹配成功(${challenge.type}/${recordName}):${keyAuthorization},校验成功, ACME challenge verified`);
|
||||||
const recordName = `${prefix}${authz.identifier.value}`;
|
return true;
|
||||||
log(`本地校验TXT记录): ${recordName}`);
|
|
||||||
let recordValues = await walkTxtRecord(recordName);
|
|
||||||
//去重
|
|
||||||
recordValues = [...new Set(recordValues)];
|
|
||||||
log(`DNS查询成功, 找到 ${recordValues.length} 条TXT记录:${recordValues}`);
|
|
||||||
if (!recordValues.length || !recordValues.includes(keyAuthorization)) {
|
|
||||||
const err = `没有找到需要的DNS TXT记录: ${recordName},期望:${keyAuthorization},结果:${recordValues}`
|
|
||||||
throw new Error(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`关键授权匹配成功(${challenge.type}/${recordName}):${keyAuthorization},校验成功, ACME challenge verified`);
|
/**
|
||||||
return true;
|
* Verify ACME TLS ALPN challenge
|
||||||
}
|
*
|
||||||
|
* https://datatracker.ietf.org/doc/html/rfc8737
|
||||||
|
*
|
||||||
|
* @param {object} authz Identifier authorization
|
||||||
|
* @param {object} challenge Authorization challenge
|
||||||
|
* @param {string} keyAuthorization Challenge key authorization
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
async function verifyTlsAlpnChallenge(authz, challenge, keyAuthorization) {
|
||||||
* Verify ACME TLS ALPN challenge
|
const tlsAlpnPort = axios.defaults.acmeSettings.tlsAlpnChallengePort || 443;
|
||||||
*
|
const host = authz.identifier.value;
|
||||||
* https://datatracker.ietf.org/doc/html/rfc8737
|
log(`Establishing TLS connection with host: ${host}:${tlsAlpnPort}`);
|
||||||
*
|
|
||||||
* @param {object} authz Identifier authorization
|
|
||||||
* @param {object} challenge Authorization challenge
|
|
||||||
* @param {string} keyAuthorization Challenge key authorization
|
|
||||||
* @returns {Promise<boolean>}
|
|
||||||
*/
|
|
||||||
|
|
||||||
async function verifyTlsAlpnChallenge(authz, challenge, keyAuthorization) {
|
const certificate = await util.retrieveTlsAlpnCertificate(host, tlsAlpnPort);
|
||||||
const tlsAlpnPort = axios.defaults.acmeSettings.tlsAlpnChallengePort || 443;
|
log('Certificate received from server successfully, matching key authorization in ALPN');
|
||||||
const host = authz.identifier.value;
|
|
||||||
log(`Establishing TLS connection with host: ${host}:${tlsAlpnPort}`);
|
|
||||||
|
|
||||||
const certificate = await util.retrieveTlsAlpnCertificate(host, tlsAlpnPort);
|
if (!isAlpnCertificateAuthorizationValid(certificate, keyAuthorization)) {
|
||||||
log('Certificate received from server successfully, matching key authorization in ALPN');
|
throw new Error(`Authorization not found in certificate from ${authz.identifier.value}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isAlpnCertificateAuthorizationValid(certificate, keyAuthorization)) {
|
log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`);
|
||||||
throw new Error(`Authorization not found in certificate from ${authz.identifier.value}`);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
challenges:{
|
challenges: {
|
||||||
'http-01': verifyHttpChallenge,
|
'http-01': verifyHttpChallenge,
|
||||||
'dns-01': verifyDnsChallenge,
|
'dns-01': verifyDnsChallenge,
|
||||||
'tls-alpn-01': verifyTlsAlpnChallenge,
|
'tls-alpn-01': verifyTlsAlpnChallenge,
|
||||||
},
|
},
|
||||||
walkTxtRecord,
|
walkTxtRecord,
|
||||||
|
walkDnsChallengeRecord,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// createChallengeFn({logger:{info:console.log}}).walkDnsChallengeRecord("handsfree.work")
|
||||||
|
|||||||
+3
-1
@@ -219,4 +219,6 @@ export function getAuthoritativeDnsResolver(record:string): Promise<any>;
|
|||||||
|
|
||||||
export const CancelError: typeof CancelError;
|
export const CancelError: typeof CancelError;
|
||||||
|
|
||||||
export function resolveDomainBySoaRecord(domain: string): Promise<string>;
|
export function resolveDomainBySoaRecord(domain: string): Promise<string>;
|
||||||
|
|
||||||
|
export function setWalkFromAuthoritative(value = true): void;
|
||||||
@@ -3,6 +3,12 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 524错误时重试3次 ([00e6d58](https://github.com/certd/certd/commit/00e6d580c2f54af70fe96a214aff87c4b96426c2))
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/basic
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
13:28
|
23:06
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/basic",
|
"name": "@certd/basic",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"dev-build": "npm run build",
|
"dev-build": "npm run build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test": "mocha --loader=ts-node/esm",
|
"test": "mocha --loader=ts-node/esm",
|
||||||
|
"test:unit": "mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch"
|
||||||
},
|
},
|
||||||
@@ -42,10 +43,12 @@
|
|||||||
"eslint": "^8.41.0",
|
"eslint": "^8.41.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"mocha": "^10.2.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/// <reference types="mocha" />
|
||||||
|
|
||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { amountUtils } from "./util.amount.js";
|
||||||
|
|
||||||
|
describe("amountUtils", () => {
|
||||||
|
describe("toCent", () => {
|
||||||
|
it("converts yuan values to cents", () => {
|
||||||
|
expect(amountUtils.toCent(1)).to.equal(100);
|
||||||
|
expect(amountUtils.toCent(12.34)).to.equal(1234);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rounds to the nearest cent", () => {
|
||||||
|
expect(amountUtils.toCent(1.235)).to.equal(124);
|
||||||
|
expect(amountUtils.toCent(1.234)).to.equal(123);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("toYuan", () => {
|
||||||
|
it("converts cent values to yuan", () => {
|
||||||
|
expect(amountUtils.toYuan(100)).to.equal(1);
|
||||||
|
expect(amountUtils.toYuan(1234)).to.equal(12.34);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rounds yuan values to two decimal places", () => {
|
||||||
|
expect(amountUtils.toYuan(1235)).to.equal(12.35);
|
||||||
|
expect(amountUtils.toYuan(1)).to.equal(0.01);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/// <reference types="mocha" />
|
||||||
|
|
||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { domainUtils } from "./util.domain.js";
|
||||||
|
|
||||||
|
describe("domainUtils", () => {
|
||||||
|
describe("match", () => {
|
||||||
|
it("matches exact domains", () => {
|
||||||
|
expect(domainUtils.match("example.com", ["example.com"])).to.equal(true);
|
||||||
|
expect(domainUtils.match("api.example.com", ["example.com"])).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("matches wildcard domains by suffix", () => {
|
||||||
|
expect(domainUtils.match("api.example.com", ["*.example.com"])).to.equal(true);
|
||||||
|
expect(domainUtils.match("deep.api.example.com", ["*.example.com"])).to.equal(false);
|
||||||
|
expect(domainUtils.match("example.com", ["*.example.com"])).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requires every target domain to match", () => {
|
||||||
|
expect(domainUtils.match(["api.example.com", "admin.example.com"], ["*.example.com"])).to.equal(true);
|
||||||
|
expect(domainUtils.match(["api.example.com", "other.com"], ["*.example.com"])).to.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isIp", () => {
|
||||||
|
it("detects valid IPv4 addresses", () => {
|
||||||
|
expect(domainUtils.isIpv4("127.0.0.1")).to.equal(true);
|
||||||
|
expect(domainUtils.isIpv4("255.255.255.255")).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects invalid IPv4 addresses", () => {
|
||||||
|
expect(domainUtils.isIpv4("999.1.1.1")).to.equal(false);
|
||||||
|
expect(domainUtils.isIpv4("1.2.3")).to.equal(false);
|
||||||
|
expect(domainUtils.isIpv4("example.com")).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("detects IPv6 addresses", () => {
|
||||||
|
expect(domainUtils.isIpv6("2001:db8::1")).to.equal(true);
|
||||||
|
expect(domainUtils.isIp("2001:db8::1")).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -51,7 +51,10 @@ function isIpv4(d: string) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const isIPv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
const isIPv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||||
return isIPv4Regex.test(d);
|
if (!isIPv4Regex.test(d)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return d.split(".").every(item => Number(item) <= 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIpv6(d: string) {
|
function isIpv6(d: string) {
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/// <reference types="mocha" />
|
||||||
|
|
||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { optionsUtils } from "./util.options.js";
|
||||||
|
|
||||||
|
describe("optionsUtils", () => {
|
||||||
|
describe("groupByDomain", () => {
|
||||||
|
it("splits options by domain match", () => {
|
||||||
|
const matchedOption = { value: "matched", domain: "api.example.com" };
|
||||||
|
const wildcardMatchedOption = { value: "wildcard", domain: "admin.example.com" };
|
||||||
|
const unmatchedOption = { value: "unmatched", domain: "other.com" };
|
||||||
|
|
||||||
|
const result = optionsUtils.groupByDomain([matchedOption, wildcardMatchedOption, unmatchedOption], ["api.example.com", "*.example.com"]);
|
||||||
|
|
||||||
|
expect(result.matched).to.deep.equal([matchedOption, wildcardMatchedOption]);
|
||||||
|
expect(result.notMatched).to.deep.equal([unmatchedOption]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("treats options without matching domains as not matched", () => {
|
||||||
|
const optionWithoutDomain = { value: "empty" };
|
||||||
|
|
||||||
|
const result = optionsUtils.groupByDomain([optionWithoutDomain], ["example.com"]);
|
||||||
|
|
||||||
|
expect(result.matched).to.deep.equal([]);
|
||||||
|
expect(result.notMatched).to.deep.equal([optionWithoutDomain]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("buildGroupOptions", () => {
|
||||||
|
it("builds disabled group labels around matched and unmatched options", () => {
|
||||||
|
const matchedOption = { value: "matched", domain: "api.example.com" };
|
||||||
|
const unmatchedOption = { value: "unmatched", domain: "other.com" };
|
||||||
|
|
||||||
|
const result = optionsUtils.buildGroupOptions([matchedOption, unmatchedOption], ["api.example.com"]);
|
||||||
|
|
||||||
|
expect(result).to.deep.equal([{ value: "matched", disabled: true, label: "----已匹配----" }, matchedOption, { value: "unmatched", disabled: true, label: "----未匹配----" }, unmatchedOption]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -111,8 +111,13 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
|
|||||||
if (config.logData == null) {
|
if (config.logData == null) {
|
||||||
config.logData = false;
|
config.logData = false;
|
||||||
}
|
}
|
||||||
|
if (config.logReq == null) {
|
||||||
|
config.logReq = true;
|
||||||
|
}
|
||||||
|
|
||||||
logger.info(`http request:${config.url},method:${config.method}`);
|
if (config.logReq !== false) {
|
||||||
|
logger.info(`http request:${config.url},method:${config.method}`);
|
||||||
|
}
|
||||||
if (config.logParams !== false && config.params) {
|
if (config.logParams !== false && config.params) {
|
||||||
logger.info(`params:${JSON.stringify(config.params)}`);
|
logger.info(`params:${JSON.stringify(config.params)}`);
|
||||||
}
|
}
|
||||||
@@ -151,10 +156,11 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
|
|||||||
|
|
||||||
config.retry = merge(
|
config.retry = merge(
|
||||||
{
|
{
|
||||||
status: [421],
|
status: [421, 524],
|
||||||
count: 0,
|
count: 0,
|
||||||
max: 3,
|
max: 3,
|
||||||
delay: 1000,
|
delay: 2000,
|
||||||
|
includes: ["[524]"],
|
||||||
},
|
},
|
||||||
config.retry
|
config.retry
|
||||||
);
|
);
|
||||||
@@ -273,7 +279,19 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
|
|||||||
const originalRequest = error.config || {};
|
const originalRequest = error.config || {};
|
||||||
// logger.info(`config`, originalRequest);
|
// logger.info(`config`, originalRequest);
|
||||||
const retry = originalRequest.retry || {};
|
const retry = originalRequest.retry || {};
|
||||||
if (retry.status && retry.status.includes(status)) {
|
|
||||||
|
const isRetryStatus = retry.status && retry.status.includes(status);
|
||||||
|
let isRetryMessage = false;
|
||||||
|
if (retry.includes) {
|
||||||
|
for (const item of retry.includes) {
|
||||||
|
if (error.message?.includes(item)) {
|
||||||
|
isRetryMessage = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRetryStatus || isRetryMessage) {
|
||||||
if (retry.max > 0 && retry.count < retry.max) {
|
if (retry.max > 0 && retry.count < retry.max) {
|
||||||
// 重试次数增加
|
// 重试次数增加
|
||||||
retry.count++;
|
retry.count++;
|
||||||
@@ -301,6 +319,7 @@ export type HttpClientResponse<R> = any;
|
|||||||
export type HttpRequestConfig<D = any> = {
|
export type HttpRequestConfig<D = any> = {
|
||||||
skipSslVerify?: boolean;
|
skipSslVerify?: boolean;
|
||||||
skipCheckRes?: boolean;
|
skipCheckRes?: boolean;
|
||||||
|
logReq?: boolean;
|
||||||
logParams?: boolean;
|
logParams?: boolean;
|
||||||
logRes?: boolean;
|
logRes?: boolean;
|
||||||
logData?: boolean;
|
logData?: boolean;
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/// <reference types="mocha" />
|
||||||
|
|
||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { stringUtils } from "./util.string.js";
|
||||||
|
|
||||||
|
describe("stringUtils", () => {
|
||||||
|
describe("maxLength", () => {
|
||||||
|
it("returns an empty string for empty input", () => {
|
||||||
|
expect(stringUtils.maxLength()).to.equal("");
|
||||||
|
expect(stringUtils.maxLength("")).to.equal("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the original string when it is within the limit", () => {
|
||||||
|
expect(stringUtils.maxLength("certd", 5)).to.equal("certd");
|
||||||
|
expect(stringUtils.maxLength("certd", 6)).to.equal("certd");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("truncates strings longer than the limit and appends ellipsis", () => {
|
||||||
|
expect(stringUtils.maxLength("certificate", 4)).to.equal("cert...");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("appendTimeSuffix", () => {
|
||||||
|
it("returns an empty string for empty input", () => {
|
||||||
|
expect(stringUtils.appendTimeSuffix()).to.equal("");
|
||||||
|
expect(stringUtils.appendTimeSuffix("")).to.equal("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("appends a millisecond timestamp suffix", () => {
|
||||||
|
const result = stringUtils.appendTimeSuffix("certd");
|
||||||
|
|
||||||
|
expect(result).to.match(/^certd-\d{17}$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"inlineSourceMap":false,
|
"inlineSourceMap": false,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
@@ -22,21 +22,11 @@
|
|||||||
"composite": false,
|
"composite": false,
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"typeRoots": [ "./typings", "./node_modules/@types"],
|
"typeRoots": ["./typings", "./node_modules/@types"],
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": false,
|
"isolatedModules": false,
|
||||||
"lib": ["ESNext", "DOM"],
|
"lib": ["ESNext", "DOM"]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src/**/*.ts", "src/**/*.json"],
|
||||||
"src/**/*.ts",
|
"exclude": ["*.js", "*.ts", "*.spec.ts", "dist", "node_modules", "src/**/*.test.ts", "test"]
|
||||||
"src/**/*.json"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"*.js",
|
|
||||||
"*.ts",
|
|
||||||
"*.spec.ts",
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"test"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,13 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 524错误时重试3次 ([00e6d58](https://github.com/certd/certd/commit/00e6d580c2f54af70fe96a214aff87c4b96426c2))
|
||||||
|
* 优化流水线执行时的状态保存性能 ([e00830b](https://github.com/certd/certd/commit/e00830bebcfe6344499e490bc174de96f9fb22d6))
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/pipeline",
|
"name": "@certd/pipeline",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -14,12 +14,13 @@
|
|||||||
"build3": "rollup -c",
|
"build3": "rollup -c",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test": "mocha --loader=ts-node/esm",
|
"test": "mocha --loader=ts-node/esm",
|
||||||
|
"test:unit": "mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.39.11",
|
"@certd/basic": "^1.39.12",
|
||||||
"@certd/plus-core": "^1.39.11",
|
"@certd/plus-core": "^1.39.12",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.1.13"
|
||||||
@@ -42,8 +43,9 @@
|
|||||||
"mocha": "^10.2.0",
|
"mocha": "^10.2.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export type ExecutorOptions = {
|
|||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
storage: IStorage;
|
storage: IStorage;
|
||||||
onChanged: (history: RunHistory) => Promise<void>;
|
onChanged: (history: RunHistory) => Promise<void>;
|
||||||
|
onFinished: (history: RunHistory) => Promise<void>;
|
||||||
accessService: IAccessService;
|
accessService: IAccessService;
|
||||||
emailService: IEmailService;
|
emailService: IEmailService;
|
||||||
notificationService: INotificationService;
|
notificationService: INotificationService;
|
||||||
@@ -47,16 +48,19 @@ export class Executor {
|
|||||||
lastRuntime!: RunHistory;
|
lastRuntime!: RunHistory;
|
||||||
options: ExecutorOptions;
|
options: ExecutorOptions;
|
||||||
abort: AbortController = new AbortController();
|
abort: AbortController = new AbortController();
|
||||||
|
|
||||||
_inited = false;
|
_inited = false;
|
||||||
|
|
||||||
onChanged: (history: RunHistory) => Promise<void>;
|
onChanged: (history: RunHistory) => Promise<void>;
|
||||||
|
onFinished: (history: RunHistory) => Promise<void>;
|
||||||
constructor(options: ExecutorOptions) {
|
constructor(options: ExecutorOptions) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.pipeline = cloneDeep(options.pipeline);
|
this.pipeline = cloneDeep(options.pipeline);
|
||||||
this.onChanged = async (history: RunHistory) => {
|
this.onChanged = async (history: RunHistory) => {
|
||||||
await options.onChanged(history);
|
await options.onChanged(history);
|
||||||
};
|
};
|
||||||
|
this.onFinished = async (history: RunHistory) => {
|
||||||
|
await options.onFinished(history);
|
||||||
|
};
|
||||||
this.pipeline.userId = options.user.id;
|
this.pipeline.userId = options.user.id;
|
||||||
this.contextFactory = new ContextFactory(options.storage);
|
this.contextFactory = new ContextFactory(options.storage);
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
@@ -77,7 +81,7 @@ export class Executor {
|
|||||||
async cancel() {
|
async cancel() {
|
||||||
this.abort.abort();
|
this.abort.abort();
|
||||||
this.runtime?.cancel(this.pipeline);
|
this.runtime?.cancel(this.pipeline);
|
||||||
await this.onChanged(this.runtime);
|
await this.onFinished(this.runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(runtimeId: any = 0, triggerType: string) {
|
async run(runtimeId: any = 0, triggerType: string) {
|
||||||
@@ -111,7 +115,7 @@ export class Executor {
|
|||||||
this.logger.error("pipeline 执行失败", e);
|
this.logger.error("pipeline 执行失败", e);
|
||||||
} finally {
|
} finally {
|
||||||
clearInterval(intervalFlushLogId);
|
clearInterval(intervalFlushLogId);
|
||||||
await this.onChanged(this.runtime);
|
await this.onFinished(this.runtime);
|
||||||
//保存之前移除logs
|
//保存之前移除logs
|
||||||
const lastRuntime: any = {
|
const lastRuntime: any = {
|
||||||
...this.runtime,
|
...this.runtime,
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export type Notification = {
|
|||||||
options?: EmailOptions;
|
options?: EmailOptions;
|
||||||
notificationId: number;
|
notificationId: number;
|
||||||
title: string;
|
title: string;
|
||||||
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Pipeline = Runnable & {
|
export type Pipeline = Runnable & {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/// <reference types="mocha" />
|
||||||
|
|
||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { PluginGroup, pluginGroups } from "./group.js";
|
||||||
|
|
||||||
|
describe("PluginGroup", () => {
|
||||||
|
it("initializes a group with defaults", () => {
|
||||||
|
const group = new PluginGroup("custom", "Custom");
|
||||||
|
|
||||||
|
expect(group.key).to.equal("custom");
|
||||||
|
expect(group.title).to.equal("Custom");
|
||||||
|
expect(group.order).to.equal(0);
|
||||||
|
expect(group.icon).to.equal("");
|
||||||
|
expect(group.plugins).to.deep.equal([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exposes built-in groups with stable keys", () => {
|
||||||
|
expect(pluginGroups.cert.key).to.equal("cert");
|
||||||
|
expect(pluginGroups.host.key).to.equal("host");
|
||||||
|
expect(pluginGroups.other.order).to.equal(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"inlineSourceMap":false,
|
"inlineSourceMap": false,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
@@ -22,22 +22,11 @@
|
|||||||
"composite": false,
|
"composite": false,
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
// "sourceMap": true,
|
// "sourceMap": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": false,
|
"isolatedModules": false,
|
||||||
"lib": ["ESNext", "DOM"],
|
"lib": ["ESNext", "DOM"]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.json"],
|
||||||
"src/**/*.ts",
|
"exclude": ["*.js", "*.ts", "*.spec.ts", "dist", "node_modules", "src/**/*.test.ts", "test"]
|
||||||
"src/**/*.d.ts",
|
|
||||||
"src/**/*.json"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"*.js",
|
|
||||||
"*.ts",
|
|
||||||
"*.spec.ts",
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"test"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-huawei
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-huawei",
|
"name": "@certd/lib-huawei",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
"types": "./dist/d/index.d.ts",
|
"types": "./dist/d/index.d.ts",
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
"build": "npm run before-build && rollup -c ",
|
"build": "npm run before-build && rollup -c ",
|
||||||
"dev-build": "npm run build",
|
"dev-build": "npm run build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"test:unit": "echo no unit tests",
|
||||||
"pub": "npm publish"
|
"pub": "npm publish"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -24,5 +25,5 @@
|
|||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-iframe
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-iframe",
|
"name": "@certd/lib-iframe",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"build3": "rollup -c",
|
"build3": "rollup -c",
|
||||||
"build2": "vue-tsc --noEmit && vite build",
|
"build2": "vue-tsc --noEmit && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"test:unit": "echo no unit tests",
|
||||||
"pub": "npm publish"
|
"pub": "npm publish"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -31,5 +32,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/jdcloud
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/jdcloud",
|
"name": "@certd/jdcloud",
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"description": "jdcloud openApi sdk",
|
"description": "jdcloud openApi sdk",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c ",
|
"build": "rollup -c ",
|
||||||
"dev-build": "npm run build",
|
"dev-build": "npm run build",
|
||||||
|
"test:unit": "echo no unit tests",
|
||||||
"pub": "npm publish"
|
"pub": "npm publish"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
@@ -56,5 +57,5 @@
|
|||||||
"fetch"
|
"fetch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-k8s
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-k8s",
|
"name": "@certd/lib-k8s",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -14,11 +14,12 @@
|
|||||||
"build3": "rollup -c",
|
"build3": "rollup -c",
|
||||||
"build2": "vue-tsc --noEmit && vite build",
|
"build2": "vue-tsc --noEmit && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"test:unit": "echo no unit tests",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.39.11",
|
"@certd/basic": "^1.39.12",
|
||||||
"@kubernetes/client-node": "0.21.0"
|
"@kubernetes/client-node": "0.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -33,5 +34,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,13 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 增加权威NS检查开关,某些用户服务器禁止向黑名单NS服务器发请求 ([1aa50cf](https://github.com/certd/certd/commit/1aa50cf53a0deab752f35ec973912e41ab8161b6))
|
||||||
|
* 支持页脚自定义 ([c985a13](https://github.com/certd/certd/commit/c985a13544aa31b0eb0783f9a3193a7e8bdc6ed6))
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-server",
|
"name": "@certd/lib-server",
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"build": "npm run before-build && tsc --skipLibCheck",
|
"build": "npm run before-build && tsc --skipLibCheck",
|
||||||
"dev-build": "npm run build",
|
"dev-build": "npm run build",
|
||||||
"test": "midway-bin test --ts -V",
|
"test": "midway-bin test --ts -V",
|
||||||
|
"test:unit": "mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||||
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
|
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
|
||||||
"cov": "midway-bin cov --ts",
|
"cov": "midway-bin cov --ts",
|
||||||
"lint": "mwts check",
|
"lint": "mwts check",
|
||||||
@@ -28,11 +29,11 @@
|
|||||||
],
|
],
|
||||||
"license": "AGPL",
|
"license": "AGPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.39.11",
|
"@certd/acme-client": "^1.39.12",
|
||||||
"@certd/basic": "^1.39.11",
|
"@certd/basic": "^1.39.12",
|
||||||
"@certd/pipeline": "^1.39.11",
|
"@certd/pipeline": "^1.39.12",
|
||||||
"@certd/plugin-lib": "^1.39.11",
|
"@certd/plugin-lib": "^1.39.12",
|
||||||
"@certd/plus-core": "^1.39.11",
|
"@certd/plus-core": "^1.39.12",
|
||||||
"@midwayjs/cache": "3.14.0",
|
"@midwayjs/cache": "3.14.0",
|
||||||
"@midwayjs/core": "3.20.11",
|
"@midwayjs/core": "3.20.11",
|
||||||
"@midwayjs/i18n": "3.20.13",
|
"@midwayjs/i18n": "3.20.13",
|
||||||
@@ -52,17 +53,20 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.3",
|
||||||
|
"@types/mocha": "^10.0.1",
|
||||||
"@types/node": "^18",
|
"@types/node": "^18",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"eslint": "^8.24.0",
|
"eslint": "^8.24.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"mocha": "^10.2.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/// <reference types="mocha" />
|
||||||
|
/// <reference types="node" />
|
||||||
|
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
|
import { Constants } from "./constants.js";
|
||||||
|
import { ParamException } from "./exception/param-exception.js";
|
||||||
|
import { Result } from "./result.js";
|
||||||
|
|
||||||
|
describe("lib-server basic helpers", () => {
|
||||||
|
it("builds success and error results", () => {
|
||||||
|
const success = Result.success("ok", { id: 1 });
|
||||||
|
assert.ok(success instanceof Result);
|
||||||
|
assert.equal(success.code, 0);
|
||||||
|
assert.equal(success.message, "ok");
|
||||||
|
assert.deepEqual(success.data, { id: 1 });
|
||||||
|
|
||||||
|
const error = Result.error(400, "bad request");
|
||||||
|
assert.ok(error instanceof Result);
|
||||||
|
assert.equal(error.code, 400);
|
||||||
|
assert.equal(error.message, "bad request");
|
||||||
|
assert.equal(error.data, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses default param exception metadata", () => {
|
||||||
|
const error = new ParamException(undefined);
|
||||||
|
|
||||||
|
assert.equal(error.name, "ParamException");
|
||||||
|
assert.equal(error.code, Constants.res.param.code);
|
||||||
|
assert.equal(error.message, Constants.res.param.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -29,6 +29,7 @@ export class SysPublicSettings extends BaseSettings {
|
|||||||
managerOtherUserPipeline = false;
|
managerOtherUserPipeline = false;
|
||||||
icpNo?: string;
|
icpNo?: string;
|
||||||
mpsNo?: string;
|
mpsNo?: string;
|
||||||
|
customFooter?: string;
|
||||||
robots?: boolean = true;
|
robots?: boolean = true;
|
||||||
aiChatEnabled = true;
|
aiChatEnabled = true;
|
||||||
|
|
||||||
@@ -92,6 +93,9 @@ export class SysPrivateSettings extends BaseSettings {
|
|||||||
environmentVars?: string = '';
|
environmentVars?: string = '';
|
||||||
|
|
||||||
|
|
||||||
|
acmeWalkFromAuthoritative?: boolean = true;
|
||||||
|
|
||||||
|
|
||||||
sms?: {
|
sms?: {
|
||||||
type?: string;
|
type?: string;
|
||||||
config?: any;
|
config?: any;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Repository } from 'typeorm';
|
|||||||
import { SysSettingsEntity } from '../entity/sys-settings.js';
|
import { SysSettingsEntity } from '../entity/sys-settings.js';
|
||||||
import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, SysSecret, SysSecretBackup } from './models.js';
|
import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, SysSecret, SysSecretBackup } from './models.js';
|
||||||
|
|
||||||
import { getAllSslProviderDomains, setSslProviderReverseProxies } from '@certd/acme-client';
|
import { getAllSslProviderDomains, setSslProviderReverseProxies, setWalkFromAuthoritative } from '@certd/acme-client';
|
||||||
import { cache, logger, mergeUtils, setGlobalProxy } from '@certd/basic';
|
import { cache, logger, mergeUtils, setGlobalProxy } from '@certd/basic';
|
||||||
import { isPlus } from '@certd/plus-core';
|
import { isPlus } from '@certd/plus-core';
|
||||||
import * as dns from 'node:dns';
|
import * as dns from 'node:dns';
|
||||||
@@ -180,6 +180,9 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
|||||||
|
|
||||||
//加载环境变量
|
//加载环境变量
|
||||||
this.setEnvironmentVars(privateSetting.environmentVars);
|
this.setEnvironmentVars(privateSetting.environmentVars);
|
||||||
|
|
||||||
|
setWalkFromAuthoritative(privateSetting.acmeWalkFromAuthoritative);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setEnvironmentVars(vars: string) {
|
setEnvironmentVars(vars: string) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"inlineSourceMap":false,
|
"inlineSourceMap": false,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
@@ -17,25 +17,15 @@
|
|||||||
"pretty": true,
|
"pretty": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"typeRoots": [ "./typings", "./node_modules/@types"],
|
"typeRoots": ["./typings", "./node_modules/@types"],
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"lib": ["ESNext", "DOM"],
|
"lib": ["ESNext", "DOM"]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.json"],
|
||||||
"src/**/*.ts",
|
"exclude": ["*.js", "*.ts", "dist", "node_modules", "src/**/*.test.ts", "test"]
|
||||||
"src/**/*.d.ts",
|
|
||||||
"src/**/*.json"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"*.js",
|
|
||||||
"*.ts",
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"test"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/midway-flyway-js",
|
"name": "@certd/midway-flyway-js",
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"dev-build": "npm run build",
|
"dev-build": "npm run build",
|
||||||
"test": "midway-bin test --ts -V",
|
"test": "midway-bin test --ts -V",
|
||||||
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
|
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
|
||||||
|
"test:unit": "echo no unit tests",
|
||||||
"cov": "midway-bin cov --ts",
|
"cov": "midway-bin cov --ts",
|
||||||
"prepublish": "npm run build",
|
"prepublish": "npm run build",
|
||||||
"pub": "npm publish"
|
"pub": "npm publish"
|
||||||
@@ -46,5 +47,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/plugin-cert
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-cert",
|
"name": "@certd/plugin-cert",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -13,14 +13,15 @@
|
|||||||
"build3": "rollup -c",
|
"build3": "rollup -c",
|
||||||
"build2": "vue-tsc --noEmit && vite build",
|
"build2": "vue-tsc --noEmit && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"test:unit": "echo no unit tests",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.39.11",
|
"@certd/acme-client": "^1.39.12",
|
||||||
"@certd/basic": "^1.39.11",
|
"@certd/basic": "^1.39.12",
|
||||||
"@certd/pipeline": "^1.39.11",
|
"@certd/pipeline": "^1.39.12",
|
||||||
"@certd/plugin-lib": "^1.39.11",
|
"@certd/plugin-lib": "^1.39.12",
|
||||||
"psl": "^1.9.0",
|
"psl": "^1.9.0",
|
||||||
"punycode.js": "^2.3.1"
|
"punycode.js": "^2.3.1"
|
||||||
},
|
},
|
||||||
@@ -38,5 +39,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-lib",
|
"name": "@certd/plugin-lib",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"build3": "rollup -c",
|
"build3": "rollup -c",
|
||||||
"build2": "vue-tsc --noEmit && vite build",
|
"build2": "vue-tsc --noEmit && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"test:unit": "mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch"
|
||||||
},
|
},
|
||||||
@@ -22,10 +23,10 @@
|
|||||||
"@alicloud/pop-core": "^1.7.10",
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"@alicloud/tea-util": "^1.4.11",
|
"@alicloud/tea-util": "^1.4.11",
|
||||||
"@aws-sdk/client-s3": "^3.964.0",
|
"@aws-sdk/client-s3": "^3.964.0",
|
||||||
"@certd/acme-client": "^1.39.11",
|
"@certd/acme-client": "^1.39.12",
|
||||||
"@certd/basic": "^1.39.11",
|
"@certd/basic": "^1.39.12",
|
||||||
"@certd/pipeline": "^1.39.11",
|
"@certd/pipeline": "^1.39.12",
|
||||||
"@certd/plus-core": "^1.39.11",
|
"@certd/plus-core": "^1.39.12",
|
||||||
"@kubernetes/client-node": "0.21.0",
|
"@kubernetes/client-node": "0.21.0",
|
||||||
"ali-oss": "^6.22.0",
|
"ali-oss": "^6.22.0",
|
||||||
"basic-ftp": "^5.0.5",
|
"basic-ftp": "^5.0.5",
|
||||||
@@ -54,8 +55,9 @@
|
|||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.1.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "112a565bf74c50e16c27a2c0a716588f84f39789"
|
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/// <reference types="mocha" />
|
||||||
|
|
||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "./util.js";
|
||||||
|
|
||||||
|
describe("plugin-lib common util", () => {
|
||||||
|
it("builds cert domain getter input define with defaults", () => {
|
||||||
|
const define = createCertDomainGetterInputDefine();
|
||||||
|
|
||||||
|
expect(define.title).to.equal("当前证书域名");
|
||||||
|
expect(define.component.name).to.equal("cert-domains-getter");
|
||||||
|
expect(define.required).to.equal(true);
|
||||||
|
expect(define.template).to.equal(false);
|
||||||
|
expect(define.mergeScript).to.contain("form.cert");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows overriding cert input key and props", () => {
|
||||||
|
const define = createCertDomainGetterInputDefine({
|
||||||
|
certInputKey: "customCert",
|
||||||
|
props: {
|
||||||
|
title: "自定义域名",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(define.title).to.equal("自定义域名");
|
||||||
|
expect(define.required).to.equal(false);
|
||||||
|
expect(define.mergeScript).to.contain("form.customCert");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("builds remote select input define with expected component options", () => {
|
||||||
|
const define = createRemoteSelectInputDefine({
|
||||||
|
title: "选择资源",
|
||||||
|
action: "ListResource",
|
||||||
|
typeName: "resource",
|
||||||
|
multi: false,
|
||||||
|
search: true,
|
||||||
|
watches: ["region"],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(define.title).to.equal("选择资源");
|
||||||
|
expect(define.required).to.equal(true);
|
||||||
|
expect(define.component).to.include({
|
||||||
|
name: "remote-select",
|
||||||
|
vModel: "value",
|
||||||
|
action: "ListResource",
|
||||||
|
typeName: "resource",
|
||||||
|
mode: "default",
|
||||||
|
multi: false,
|
||||||
|
search: true,
|
||||||
|
});
|
||||||
|
expect(define.component.watches).to.deep.equal(["certDomains", "accessId", "region"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"inlineSourceMap":false,
|
"inlineSourceMap": false,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
@@ -16,27 +16,17 @@
|
|||||||
"pretty": true,
|
"pretty": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"typeRoots": [ "./typings", "./node_modules/@types"],
|
"typeRoots": ["./typings", "./node_modules/@types"],
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
// "sourceMap": true,
|
// "sourceMap": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": false,
|
"isolatedModules": false,
|
||||||
"lib": ["ESNext", "DOM"],
|
"lib": ["ESNext", "DOM"]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.json"],
|
||||||
"src/**/*.ts",
|
"exclude": ["*.js", "*.ts", "dist", "node_modules", "src/**/*.test.ts", "test"]
|
||||||
"src/**/*.d.ts",
|
|
||||||
"src/**/*.json"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"*.js",
|
|
||||||
"*.ts",
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"test"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 调整手机版首页标题被挤开的bug ([eab66e2](https://github.com/certd/certd/commit/eab66e2d1988635985745f2d1b227b958969ee00))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 524错误时重试3次 ([00e6d58](https://github.com/certd/certd/commit/00e6d580c2f54af70fe96a214aff87c4b96426c2))
|
||||||
|
* 增加权威NS检查开关,某些用户服务器禁止向黑名单NS服务器发请求 ([1aa50cf](https://github.com/certd/certd/commit/1aa50cf53a0deab752f35ec973912e41ab8161b6))
|
||||||
|
* 支持页脚自定义 ([c985a13](https://github.com/certd/certd/commit/c985a13544aa31b0eb0783f9a3193a7e8bdc6ed6))
|
||||||
|
|
||||||
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
## [1.39.11](https://github.com/certd/certd/compare/v1.39.10...v1.39.11) (2026-04-26)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/ui-client",
|
"name": "@certd/ui-client",
|
||||||
"version": "1.39.11",
|
"version": "1.39.12",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --open",
|
"dev": "vite --open",
|
||||||
@@ -12,7 +12,8 @@
|
|||||||
"debug:force": "vite --force --mode debug",
|
"debug:force": "vite --force --mode debug",
|
||||||
"build": "cross-env NODE_OPTIONS=--max-old-space-size=40960 vite build ",
|
"build": "cross-env NODE_OPTIONS=--max-old-space-size=40960 vite build ",
|
||||||
"dev-build": "echo 1",
|
"dev-build": "echo 1",
|
||||||
"test:unit": "vitest",
|
"test:unit": "echo no unit tests",
|
||||||
|
"test:vue": "vitest run",
|
||||||
"serve": "vite preview",
|
"serve": "vite preview",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"pretty-quick": "pretty-quick",
|
"pretty-quick": "pretty-quick",
|
||||||
@@ -106,8 +107,8 @@
|
|||||||
"zod-defaults": "^0.1.3"
|
"zod-defaults": "^0.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@certd/lib-iframe": "^1.39.11",
|
"@certd/lib-iframe": "^1.39.12",
|
||||||
"@certd/pipeline": "^1.39.11",
|
"@certd/pipeline": "^1.39.12",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@types/chai": "^4.3.12",
|
"@types/chai": "^4.3.12",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.2 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -1,17 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<a-input :value="valueRef" placeholder="请输入图片验证码" autocomplete="off" @update:value="onChange">
|
<a-input :value="valueRef" :placeholder="t('certd.captcha.inputImageCode')" autocomplete="off" @update:value="onChange">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<fs-icon icon="ion:image-outline"></fs-icon>
|
<fs-icon icon="ion:image-outline"></fs-icon>
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<div class="input-right pointer" title="点击刷新">
|
<div class="input-right pointer" :title="t('certd.captcha.refresh')">
|
||||||
<img class="image-code" :src="imageCodeSrc" @click="resetImageCode" />
|
<img class="image-code" :src="imageCodeSrc" @click="resetImageCode" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: any;
|
modelValue: any;
|
||||||
@@ -20,6 +21,7 @@ const props = defineProps<{
|
|||||||
defineOptions({
|
defineOptions({
|
||||||
name: "ImageCaptcha",
|
name: "ImageCaptcha",
|
||||||
});
|
});
|
||||||
|
const { t } = useI18n();
|
||||||
const emit = defineEmits(["update:modelValue", "change"]);
|
const emit = defineEmits(["update:modelValue", "change"]);
|
||||||
|
|
||||||
const valueRef = ref("");
|
const valueRef = ref("");
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
<div class="sweep-animation"></div>
|
<div class="sweep-animation"></div>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-icon">✓</div>
|
<div class="box-icon">✓</div>
|
||||||
<span v-if="modelValue == null" class="status-text">点击进行验证</span>
|
<span v-if="modelValue == null" class="status-text">{{ t("certd.captcha.clickToVerify") }}</span>
|
||||||
<span v-else class="status-text">验证成功</span>
|
<span v-else class="status-text">{{ t("certd.captcha.verifySuccess") }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -13,8 +13,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { ref, Ref, watch } from "vue";
|
import { ref, Ref, watch } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
import { loadScript } from "vue-plugin-load-script";
|
import { loadScript } from "vue-plugin-load-script";
|
||||||
|
const { t } = useI18n();
|
||||||
const loaded = ref(false);
|
const loaded = ref(false);
|
||||||
async function loadCaptchaScript() {
|
async function loadCaptchaScript() {
|
||||||
// 加载验证码js
|
// 加载验证码js
|
||||||
@@ -56,7 +58,7 @@ function callback(res: { ret: number; ticket: string; randstr: string; errorCode
|
|||||||
|
|
||||||
if (res.errorCode && res.errorCode > 0) {
|
if (res.errorCode && res.errorCode > 0) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: `验证码验证失败:${res.errorMessage || res.errorCode}`,
|
message: t("certd.captcha.verifyFailed", { message: res.errorMessage || res.errorCode }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,13 +85,13 @@ function loadErrorCallback(error: any) {
|
|||||||
// errorMessage: "jsload_error",
|
// errorMessage: "jsload_error",
|
||||||
// });
|
// });
|
||||||
notification.error({
|
notification.error({
|
||||||
message: `验证码加载失败:${error?.message || error}`,
|
message: t("certd.captcha.loadFailed", { message: error?.message || error }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function triggerCaptcha() {
|
async function triggerCaptcha() {
|
||||||
if (!loaded.value) {
|
if (!loaded.value) {
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "验证码还未加载完成,请稍后再试",
|
message: t("certd.captcha.notLoaded"),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-11
@@ -4,7 +4,7 @@
|
|||||||
<!-- <td class="domain">-->
|
<!-- <td class="domain">-->
|
||||||
<!-- {{ props.domain }}-->
|
<!-- {{ props.domain }}-->
|
||||||
<!-- </td>-->
|
<!-- </td>-->
|
||||||
<td class="host-record" :title="'域名:' + props.domain">
|
<td class="host-record" :title="t('certd.verifyPlan.domainTitle', { domain: props.domain })">
|
||||||
<fs-copyable v-model="cnameRecord.hostRecord"></fs-copyable>
|
<fs-copyable v-model="cnameRecord.hostRecord"></fs-copyable>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center">CNAME</td>
|
<td style="text-align: center">CNAME</td>
|
||||||
@@ -16,17 +16,17 @@
|
|||||||
<a-tooltip v-if="cnameRecord.error" :title="cnameRecord.error">
|
<a-tooltip v-if="cnameRecord.error" :title="cnameRecord.error">
|
||||||
<fs-icon class="ml-5 color-red" icon="ion:warning-outline"></fs-icon>
|
<fs-icon class="ml-5 color-red" icon="ion:warning-outline"></fs-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip v-if="cnameRecord.status === 'valid'" title="重置校验状态,重新校验">
|
<a-tooltip v-if="cnameRecord.status === 'valid'" :title="t('certd.verifyPlan.resetStatusTooltip')">
|
||||||
<fs-icon class="ml-2 color-yellow text-md pointer" icon="solar:undo-left-square-bold" @click="resetStatus"></fs-icon>
|
<fs-icon class="ml-2 color-yellow text-md pointer" icon="solar:undo-left-square-bold" @click="resetStatus"></fs-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</td>
|
</td>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<template v-if="cnameRecord.status !== 'valid'">
|
<template v-if="cnameRecord.status !== 'valid'">
|
||||||
<a-button type="primary" size="small" :loading="loading" @click="doVerify">点击验证</a-button>
|
<a-button type="primary" size="small" :loading="loading" @click="doVerify">{{ t("certd.verifyPlan.clickToValidate") }}</a-button>
|
||||||
<cname-tip :record="cnameRecord"></cname-tip>
|
<cname-tip :record="cnameRecord"></cname-tip>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-else class="helper" title="后续自动申请证书需要">不要删除CNAME</div>
|
<div v-else class="helper" :title="t('certd.verifyPlan.keepCnameTitle')">{{ t("certd.verifyPlan.keepCname") }}</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -35,18 +35,20 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { CnameRecord, GetByDomain } from "/@/components/plugins/cert/domains-verify-plan-editor/api";
|
import { CnameRecord, GetByDomain } from "/@/components/plugins/cert/domains-verify-plan-editor/api";
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { dict } from "@fast-crud/fast-crud";
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
import * as api from "./api.js";
|
import * as api from "./api.js";
|
||||||
import CnameTip from "./cname-tip.vue";
|
import CnameTip from "./cname-tip.vue";
|
||||||
import { Modal } from "ant-design-vue";
|
import { Modal } from "ant-design-vue";
|
||||||
import { utils } from "/@/utils/index.js";
|
import { utils } from "/@/utils/index.js";
|
||||||
|
const { t } = useI18n();
|
||||||
const statusDict = dict({
|
const statusDict = dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "待设置CNAME", value: "cname", color: "warning" },
|
{ label: t("certd.verifyPlan.status.pendingCname"), value: "cname", color: "warning" },
|
||||||
{ label: "验证中", value: "validating", color: "blue" },
|
{ label: t("certd.verifyPlan.status.validating"), value: "validating", color: "blue" },
|
||||||
{ label: "验证成功", value: "valid", color: "green" },
|
{ label: t("certd.verifyPlan.status.valid"), value: "valid", color: "green" },
|
||||||
{ label: "验证失败", value: "failed", color: "red" },
|
{ label: t("certd.verifyPlan.status.failed"), value: "failed", color: "red" },
|
||||||
{ label: "验证超时", value: "timeout", color: "red" },
|
{ label: t("certd.verifyPlan.status.timeout"), value: "timeout", color: "red" },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -125,8 +127,8 @@ async function doVerify() {
|
|||||||
|
|
||||||
async function resetStatus() {
|
async function resetStatus() {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "重置状态",
|
title: t("certd.verifyPlan.resetStatus"),
|
||||||
content: "确定要重置校验状态吗?",
|
content: t("certd.verifyPlan.confirmResetStatus"),
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await api.ResetStatus(cnameRecord.value.id);
|
await api.ResetStatus(cnameRecord.value.id);
|
||||||
await loadRecord();
|
await loadRecord();
|
||||||
|
|||||||
+9
-7
@@ -2,17 +2,17 @@
|
|||||||
<a-tooltip :overlay-style="{ maxWidth: '400px' }">
|
<a-tooltip :overlay-style="{ maxWidth: '400px' }">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div>
|
<div>
|
||||||
<div>多试几次,如果仍然无法验证通过,请按如下步骤排查问题:</div>
|
<div>{{ t("certd.verifyPlan.cnameTip.intro") }}</div>
|
||||||
<div>1. 解析记录应该添加在{{ record.domain }}域名下</div>
|
<div>{{ t("certd.verifyPlan.cnameTip.step1", { domain: record.domain }) }}</div>
|
||||||
<div>2. 要添加的是CNAME类型的记录,不是TXT</div>
|
<div>{{ t("certd.verifyPlan.cnameTip.step2") }}</div>
|
||||||
<div>3. 核对记录值是否是:{{ record.recordValue }}</div>
|
<div>{{ t("certd.verifyPlan.cnameTip.step3", { value: record.recordValue }) }}</div>
|
||||||
<div>
|
<div>
|
||||||
4. 在验证中状态下,运行下面的命令,查看cname和txt解析是否正确
|
{{ t("certd.verifyPlan.cnameTip.step4") }}
|
||||||
<fs-copyable :style="{ color: '#52c41a' }" :model-value="nslookupCmd"></fs-copyable>
|
<fs-copyable :style="{ color: '#52c41a' }" :model-value="nslookupCmd"></fs-copyable>
|
||||||
或者
|
{{ t("certd.verifyPlan.cnameTip.or") }}
|
||||||
<fs-copyable :style="{ color: '#52c41a' }" :model-value="digCmd"></fs-copyable>
|
<fs-copyable :style="{ color: '#52c41a' }" :model-value="digCmd"></fs-copyable>
|
||||||
</div>
|
</div>
|
||||||
<div>5. 如果以上检查都没有问题,则可能是DNS解析生效时间比较慢,某些提供商延迟可能高达几个小时</div>
|
<div>{{ t("certd.verifyPlan.cnameTip.step5") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<fs-icon class="ml-5 pointer" icon="mingcute:question-line"></fs-icon>
|
<fs-icon class="ml-5 pointer" icon="mingcute:question-line"></fs-icon>
|
||||||
@@ -21,9 +21,11 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
record: any;
|
record: any;
|
||||||
}>();
|
}>();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const nslookupCmd = computed(() => {
|
const nslookupCmd = computed(() => {
|
||||||
return `nslookup -q=txt _acme-challenge.${props.record.domain}`;
|
return `nslookup -q=txt _acme-challenge.${props.record.domain}`;
|
||||||
|
|||||||
+8
-5
@@ -2,11 +2,11 @@
|
|||||||
<table class="cname-verify-plan">
|
<table class="cname-verify-plan">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width: 160px">主机记录</td>
|
<td style="width: 160px">{{ t("certd.verifyPlan.hostRecord") }}</td>
|
||||||
<td style="width: 100px; text-align: center">记录类型</td>
|
<td style="width: 100px; text-align: center">{{ t("certd.verifyPlan.recordType") }}</td>
|
||||||
<td style="width: 250px">请设置CNAME记录(验证成功以后不要删除)</td>
|
<td style="width: 250px">{{ t("certd.verifyPlan.setCnameRecord") }}</td>
|
||||||
<td style="width: 120px" class="center">状态</td>
|
<td style="width: 120px" class="center">{{ t("certd.status") }}</td>
|
||||||
<td style="width: 90px" class="center">操作</td>
|
<td style="width: 90px" class="center">{{ t("certd.verifyPlan.operation") }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<template v-for="key in domains" :key="key">
|
<template v-for="key in domains" :key="key">
|
||||||
@@ -19,11 +19,14 @@
|
|||||||
import { CnameRecord } from "/@/components/plugins/cert/domains-verify-plan-editor/api";
|
import { CnameRecord } from "/@/components/plugins/cert/domains-verify-plan-editor/api";
|
||||||
import CnameRecordInfo from "/@/components/plugins/cert/domains-verify-plan-editor/cname-record-info.vue";
|
import CnameRecordInfo from "/@/components/plugins/cert/domains-verify-plan-editor/cname-record-info.vue";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "CnameVerifyPlan",
|
name: "CnameVerifyPlan",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue", "change"]);
|
const emit = defineEmits(["update:modelValue", "change"]);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|||||||
+8
-5
@@ -2,10 +2,10 @@
|
|||||||
<table class="http-verify-plan">
|
<table class="http-verify-plan">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width: 160px">网站域名</td>
|
<td style="width: 160px">{{ t("certd.verifyPlan.websiteDomain") }}</td>
|
||||||
<td style="width: 100px; text-align: center">上传方式</td>
|
<td style="width: 100px; text-align: center">{{ t("certd.verifyPlan.uploadMethod") }}</td>
|
||||||
<td style="width: 150px">上传授权</td>
|
<td style="width: 150px">{{ t("certd.verifyPlan.uploadAccess") }}</td>
|
||||||
<td style="width: 200px">网站根目录路径</td>
|
<td style="width: 200px">{{ t("certd.verifyPlan.websiteRootPath") }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody v-if="records" class="http-record-body">
|
<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>
|
<access-selector v-model="item.httpUploaderAccess" :type="item.httpUploaderType" @change="onRecordChange"></access-selector>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a-input v-model:value="item.httpUploadRootDir" placeholder="网站根目录,如:/www/wwwroot" @change="onRecordChange"></a-input>
|
<a-input v-model:value="item.httpUploadRootDir" :placeholder="t('certd.verifyPlan.websiteRootPlaceholder')" @change="onRecordChange"></a-input>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Ref, ref, watch, nextTick } from "vue";
|
import { Ref, ref, watch, nextTick } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { HttpRecord } from "/@/components/plugins/cert/domains-verify-plan-editor/type";
|
import { HttpRecord } from "/@/components/plugins/cert/domains-verify-plan-editor/type";
|
||||||
import { dict } from "@fast-crud/fast-crud";
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
import { Dicts } from "/@/components/plugins/lib/dicts";
|
import { Dicts } from "/@/components/plugins/lib/dicts";
|
||||||
@@ -39,6 +40,8 @@ defineOptions({
|
|||||||
name: "HttpVerifyPlan",
|
name: "HttpVerifyPlan",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const emit = defineEmits(["update:modelValue", "change"]);
|
const emit = defineEmits(["update:modelValue", "change"]);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|||||||
+15
-12
@@ -5,7 +5,7 @@
|
|||||||
<div class="plan-box bg-white dark:bg-neutral-700">
|
<div class="plan-box bg-white dark:bg-neutral-700">
|
||||||
<div class="fullscreen-button pointer flex-center" @click="fullscreen = !fullscreen">
|
<div class="fullscreen-button pointer flex-center" @click="fullscreen = !fullscreen">
|
||||||
<span v-if="!fullscreen" style="font-size: 10px" class="flex-center">
|
<span v-if="!fullscreen" style="font-size: 10px" class="flex-center">
|
||||||
这里可以放大
|
{{ t("certd.verifyPlan.expandTip") }}
|
||||||
<fs-icon icon="ion:arrow-forward-outline"></fs-icon>
|
<fs-icon icon="ion:arrow-forward-outline"></fs-icon>
|
||||||
</span>
|
</span>
|
||||||
<fs-icon :icon="fullscreen ? 'material-symbols:fullscreen-exit' : 'material-symbols:fullscreen'"></fs-icon>
|
<fs-icon :icon="fullscreen ? 'material-symbols:fullscreen-exit' : 'material-symbols:fullscreen'"></fs-icon>
|
||||||
@@ -13,9 +13,9 @@
|
|||||||
<table class="plan-table">
|
<table class="plan-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="min-width: 100px">主域名</th>
|
<th style="min-width: 100px">{{ t("certd.verifyPlan.mainDomain") }}</th>
|
||||||
<th>验证方式</th>
|
<th>{{ t("certd.verifyPlan.challengeType") }}</th>
|
||||||
<th>验证计划</th>
|
<th>{{ t("certd.verifyPlan.challengePlan") }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -30,13 +30,13 @@
|
|||||||
<div class="plan">
|
<div class="plan">
|
||||||
<div v-if="item.type === 'dns'" class="plan-dns">
|
<div v-if="item.type === 'dns'" class="plan-dns">
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<span class="label">DNS类型:</span>
|
<span class="label">{{ t("certd.verifyPlan.dnsType") }}:</span>
|
||||||
<span class="input">
|
<span class="input">
|
||||||
<fs-dict-select
|
<fs-dict-select
|
||||||
v-model:value="item.dnsProviderType"
|
v-model:value="item.dnsProviderType"
|
||||||
size="small"
|
size="small"
|
||||||
:dict="dnsProviderTypeDict"
|
:dict="dnsProviderTypeDict"
|
||||||
placeholder="DNS提供商"
|
:placeholder="t('certd.verifyPlan.dnsProvider')"
|
||||||
@change="onPlanChanged"
|
@change="onPlanChanged"
|
||||||
@selected-change="onDnsProviderChange(item, $event)"
|
@selected-change="onDnsProviderChange(item, $event)"
|
||||||
></fs-dict-select>
|
></fs-dict-select>
|
||||||
@@ -44,9 +44,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
<span class="label">DNS授权:</span>
|
<span class="label">{{ t("certd.verifyPlan.dnsAccess") }}:</span>
|
||||||
<span class="input">
|
<span class="input">
|
||||||
<access-selector v-model="item.dnsProviderAccessId" size="small" :type="item.dnsProviderAccessType || item.dnsProviderType" placeholder="请选择" @change="onPlanChanged"></access-selector>
|
<access-selector v-model="item.dnsProviderAccessId" size="small" :type="item.dnsProviderAccessType || item.dnsProviderType" :placeholder="t('certd.verifyPlan.pleaseSelect')" @change="onPlanChanged"></access-selector>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="item.type === 'http'" class="plan-http">
|
<div v-if="item.type === 'http'" class="plan-http">
|
||||||
<http-verify-plan v-model="item.httpVerifyPlan" @change="onPlanChanged" />
|
<http-verify-plan v-model="item.httpVerifyPlan" @change="onPlanChanged" />
|
||||||
<div class="helper">证书颁发机构将请求 https://yourdomain/.well-known/acme-challenge/xxxxxx 来验证域名所有权。</div>
|
<div class="helper">{{ t("certd.verifyPlan.httpHelper") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { dict, FsDictSelect } from "@fast-crud/fast-crud";
|
import { dict, FsDictSelect } from "@fast-crud/fast-crud";
|
||||||
import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
||||||
import CnameVerifyPlan from "./cname-verify-plan.vue";
|
import CnameVerifyPlan from "./cname-verify-plan.vue";
|
||||||
@@ -84,17 +85,19 @@ defineOptions({
|
|||||||
name: "DomainsVerifyPlanEditor",
|
name: "DomainsVerifyPlanEditor",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const challengeTypeOptions = ref<any[]>([
|
const challengeTypeOptions = ref<any[]>([
|
||||||
{
|
{
|
||||||
label: "DNS验证",
|
label: t("certd.verifyPlan.dnsChallenge"),
|
||||||
value: "dns",
|
value: "dns",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "CNAME验证",
|
label: t("certd.verifyPlan.cnameChallenge"),
|
||||||
value: "cname",
|
value: "cname",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "HTTP验证",
|
label: t("certd.verifyPlan.httpChallenge"),
|
||||||
value: "http",
|
value: "http",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
+7
-6
@@ -1,5 +1,6 @@
|
|||||||
import Validator from "async-validator";
|
import Validator from "async-validator";
|
||||||
import { DomainsVerifyPlanInput } from "./type";
|
import { DomainsVerifyPlanInput } from "./type";
|
||||||
|
import { $t } from "/@/locales";
|
||||||
|
|
||||||
function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
|
function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@@ -13,7 +14,7 @@ function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
|
|||||||
for (const subDomain of subDomains) {
|
for (const subDomain of subDomains) {
|
||||||
const plan = value[domain].cnameVerifyPlan[subDomain];
|
const plan = value[domain].cnameVerifyPlan[subDomain];
|
||||||
if (plan.status !== "valid") {
|
if (plan.status !== "valid") {
|
||||||
throw new Error(`域名${subDomain}的CNAME未验证通过,请先设置CNAME记录,点击验证按钮`);
|
throw new Error($t("certd.verifyPlan.errors.cnameNotValid", { domain: subDomain }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +23,7 @@ function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
|
|||||||
for (const item of domains) {
|
for (const item of domains) {
|
||||||
//如果有通配符域名则不允许使用http校验
|
//如果有通配符域名则不允许使用http校验
|
||||||
if (item.startsWith("*.")) {
|
if (item.startsWith("*.")) {
|
||||||
throw new Error(`域名${item}为通配符域名,不支持HTTP校验`);
|
throw new Error($t("certd.verifyPlan.errors.wildcardNotSupportHttp", { domain: item }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,19 +32,19 @@ function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
|
|||||||
for (const subDomain of subDomains) {
|
for (const subDomain of subDomains) {
|
||||||
const plan = value[domain].httpVerifyPlan[subDomain];
|
const plan = value[domain].httpVerifyPlan[subDomain];
|
||||||
if (!plan.httpUploaderType) {
|
if (!plan.httpUploaderType) {
|
||||||
throw new Error(`域名${subDomain}的上传方式必须填写`);
|
throw new Error($t("certd.verifyPlan.errors.uploadMethodRequired", { domain: subDomain }));
|
||||||
}
|
}
|
||||||
if (!plan.httpUploaderAccess) {
|
if (!plan.httpUploaderAccess) {
|
||||||
throw new Error(`域名${subDomain}的上传授权信息必须填写`);
|
throw new Error($t("certd.verifyPlan.errors.uploadAccessRequired", { domain: subDomain }));
|
||||||
}
|
}
|
||||||
if (!plan.httpUploadRootDir) {
|
if (!plan.httpUploadRootDir) {
|
||||||
throw new Error(`域名${subDomain}的网站根路径必须填写`);
|
throw new Error($t("certd.verifyPlan.errors.websiteRootRequired", { domain: subDomain }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type === "dns") {
|
} else if (type === "dns") {
|
||||||
if (!value[domain].dnsProviderType || !value[domain].dnsProviderAccessId) {
|
if (!value[domain].dnsProviderType || !value[domain].dnsProviderAccessId) {
|
||||||
throw new Error(`DNS模式下,域名${domain}的DNS类型和授权信息必须填写`);
|
throw new Error($t("certd.verifyPlan.errors.dnsProviderRequired", { domain }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="api-test">
|
<div class="api-test">
|
||||||
<div>
|
<div>
|
||||||
<fs-button :loading="loading" type="primary" text="测试" icon="ion:refresh-outline" @click="doTest"></fs-button>
|
<fs-button :loading="loading" type="primary" :text="t('certd.pluginCommon.test')" icon="ion:refresh-outline" @click="doTest"></fs-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="helper" :class="{ error: hasError }">
|
<div class="helper" :class="{ error: hasError }">
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||||
import { ref, inject } from "vue";
|
import { ref, inject } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { Form } from "ant-design-vue";
|
import { Form } from "ant-design-vue";
|
||||||
import { getInputFromForm } from "./utils";
|
import { getInputFromForm } from "./utils";
|
||||||
|
|
||||||
@@ -19,6 +20,8 @@ defineOptions({
|
|||||||
name: "ApiTest",
|
name: "ApiTest",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const fromType: any = inject("getFromType");
|
const fromType: any = inject("getFromType");
|
||||||
const getScope: any = inject("get:scope");
|
const getScope: any = inject("get:scope");
|
||||||
const getPluginType: any = inject("get:plugin:type", () => {
|
const getPluginType: any = inject("get:plugin:type", () => {
|
||||||
@@ -61,14 +64,14 @@ const doTest = async () => {
|
|||||||
{
|
{
|
||||||
onError(err: any) {
|
onError(err: any) {
|
||||||
hasError.value = true;
|
hasError.value = true;
|
||||||
message.value = `错误:${err.message}`;
|
message.value = t("certd.pluginCommon.errorWithMessage", { message: err.message });
|
||||||
},
|
},
|
||||||
showErrorNotify: false,
|
showErrorNotify: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
message.value = "测试请求成功";
|
message.value = t("certd.pluginCommon.testRequestSuccess");
|
||||||
if (res) {
|
if (res) {
|
||||||
message.value += `,返回:${JSON.stringify(res)}`;
|
message.value += t("certd.pluginCommon.responseSuffix", { response: JSON.stringify(res) });
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject, ref, watch } from "vue";
|
import { inject, ref, watch } from "vue";
|
||||||
|
import { useI18n } from "/@/locales";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "CertDomainsGetter",
|
name: "CertDomainsGetter",
|
||||||
@@ -24,6 +25,7 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const pipeline: any = inject("pipeline");
|
const pipeline: any = inject("pipeline");
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
function findStepFromPipeline(targetStepId: string) {
|
function findStepFromPipeline(targetStepId: string) {
|
||||||
for (const stage of pipeline.value.stages) {
|
for (const stage of pipeline.value.stages) {
|
||||||
@@ -40,7 +42,7 @@ function findStepFromPipeline(targetStepId: string) {
|
|||||||
const errorRef = ref("");
|
const errorRef = ref("");
|
||||||
function getStepIdFromInputKey(inputKey: string) {
|
function getStepIdFromInputKey(inputKey: string) {
|
||||||
if (!inputKey) {
|
if (!inputKey) {
|
||||||
errorRef.value = "请先选择域名证书";
|
errorRef.value = t("certd.pluginCommon.selectCertFirst");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return inputKey.split(".")[1];
|
return inputKey.split(".")[1];
|
||||||
@@ -49,7 +51,7 @@ function getDomainFromPipeline(inputKey: string) {
|
|||||||
let targetStepId = getStepIdFromInputKey(inputKey);
|
let targetStepId = getStepIdFromInputKey(inputKey);
|
||||||
let certStep = findStepFromPipeline(targetStepId);
|
let certStep = findStepFromPipeline(targetStepId);
|
||||||
if (!certStep) {
|
if (!certStep) {
|
||||||
errorRef.value = "找不到目标步骤,请先选择域名证书";
|
errorRef.value = t("certd.pluginCommon.targetStepNotFound");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +60,7 @@ function getDomainFromPipeline(inputKey: string) {
|
|||||||
targetStepId = getStepIdFromInputKey(firstLevelValue);
|
targetStepId = getStepIdFromInputKey(firstLevelValue);
|
||||||
certStep = findStepFromPipeline(targetStepId);
|
certStep = findStepFromPipeline(targetStepId);
|
||||||
if (!certStep) {
|
if (!certStep) {
|
||||||
errorRef.value = "找不到目标步骤,请先选择域名证书";
|
errorRef.value = t("certd.pluginCommon.targetStepNotFound");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,12 @@
|
|||||||
<template v-if="search">
|
<template v-if="search">
|
||||||
<div class="flex w-full items-center justify-between flex-wrap" style="padding: 4px 8px">
|
<div class="flex w-full items-center justify-between flex-wrap" style="padding: 4px 8px">
|
||||||
<div class="flex-1 flex flex-row items-center">
|
<div class="flex-1 flex flex-row items-center">
|
||||||
<a-input ref="inputRef" v-model:value="searchKeyRef" class="flex-1" allow-clear placeholder="这里可以搜索域名(数据来自“设置->域名管理”),您也可以直接在上面输入框输入" @keydown.enter="doSearch" />
|
<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"> 查询 </fs-button>
|
<fs-button type="primary" class="m-1" :loading="loading" icon="mingcute:search-2-line" @click="doSearch">{{ t("certd.pluginCommon.search") }}</fs-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="manager flex flex-row items-center">
|
<div class="manager flex flex-row items-center">
|
||||||
<fs-button type="primary" class="m-1" icon="mingcute:vip-1-line" @click="openDomainImportDialog">导入域名</fs-button>
|
<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">管理域名</fs-button>
|
<fs-button class="m-1" type="primary" icon="carbon:gui-management" @click="openDomainManager">{{ t("certd.pluginCommon.manageDomain") }}</fs-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasError" class="helper p-2" :class="{ error: hasError }">
|
<div v-if="hasError" class="helper p-2" :class="{ error: hasError }">
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-select>
|
</a-select>
|
||||||
<div class="ml-5">
|
<div class="ml-5">
|
||||||
<fs-button :loading="loading" title="刷新我的域名列表" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
<fs-button :loading="loading" :title="t('certd.pluginCommon.refreshMyDomains')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="helper" :class="{ error: hasError }">
|
<div class="helper" :class="{ error: hasError }">
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, defineComponent, onMounted, ref, Ref, useAttrs } from "vue";
|
import { computed, defineComponent, onMounted, ref, Ref, useAttrs } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { Dicts } from "../lib/dicts";
|
import { Dicts } from "../lib/dicts";
|
||||||
import { request } from "/@/api/service";
|
import { request } from "/@/api/service";
|
||||||
@@ -67,6 +68,8 @@ defineOptions({
|
|||||||
name: "DomainSelector",
|
name: "DomainSelector",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const VNodes = defineComponent({
|
const VNodes = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
vnodes: {
|
vnodes: {
|
||||||
|
|||||||
@@ -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 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>
|
</a-auto-complete>
|
||||||
<div class="ml-5">
|
<div class="ml-5">
|
||||||
<fs-button :loading="loading" title="刷新选项" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
<fs-button :loading="loading" :title="t('certd.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="helper" :class="{ error: hasError }">
|
<div class="helper" :class="{ error: hasError }">
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||||
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { PluginDefine } from "@certd/pipeline";
|
import { PluginDefine } from "@certd/pipeline";
|
||||||
import { getInputFromForm } from "./utils";
|
import { getInputFromForm } from "./utils";
|
||||||
|
|
||||||
@@ -22,6 +23,8 @@ defineOptions({
|
|||||||
name: "RemoteAutoComplete",
|
name: "RemoteAutoComplete",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps<
|
const props = defineProps<
|
||||||
{
|
{
|
||||||
watches?: string[];
|
watches?: string[];
|
||||||
@@ -93,16 +96,16 @@ const getOptions = async () => {
|
|||||||
{
|
{
|
||||||
onError(err: any) {
|
onError(err: any) {
|
||||||
hasError.value = true;
|
hasError.value = true;
|
||||||
message.value = `获取选项出错:${err.message}`;
|
message.value = t("certd.pluginCommon.getOptionsError", { message: err.message });
|
||||||
},
|
},
|
||||||
showErrorNotify: false,
|
showErrorNotify: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const list = res?.list || res || [];
|
const list = res?.list || res || [];
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
message.value = "获取数据成功,请从下拉框中选择";
|
message.value = t("certd.pluginCommon.getDataSuccessSelect");
|
||||||
} else {
|
} else {
|
||||||
message.value = "获取数据成功,没有数据";
|
message.value = t("certd.pluginCommon.getDataSuccessEmpty");
|
||||||
}
|
}
|
||||||
optionsRef.value = list;
|
optionsRef.value = list;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { doRequest } from "/@/components/plugins/lib";
|
import { doRequest } from "/@/components/plugins/lib";
|
||||||
import { inject, ref, useAttrs } from "vue";
|
import { inject, ref, useAttrs } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { useFormWrapper } from "@fast-crud/fast-crud";
|
import { useFormWrapper } from "@fast-crud/fast-crud";
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { getInputFromForm } from "./utils";
|
import { getInputFromForm } from "./utils";
|
||||||
@@ -15,6 +16,7 @@ defineOptions({
|
|||||||
name: "RemoteInput",
|
name: "RemoteInput",
|
||||||
});
|
});
|
||||||
const { openCrudFormDialog } = useFormWrapper();
|
const { openCrudFormDialog } = useFormWrapper();
|
||||||
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string;
|
modelValue: string;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -53,7 +55,7 @@ async function openDialog() {
|
|||||||
saveRemind: false,
|
saveRemind: false,
|
||||||
},
|
},
|
||||||
afterSubmit() {
|
afterSubmit() {
|
||||||
notification.success({ message: "操作成功" });
|
notification.success({ message: t("certd.operationSuccess") });
|
||||||
},
|
},
|
||||||
async doSubmit({ form }: any) {
|
async doSubmit({ form }: any) {
|
||||||
return await doPluginFormSubmit(form);
|
return await doPluginFormSubmit(form);
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
<template #dropdownRender="{ menuNode: menu }">
|
<template #dropdownRender="{ menuNode: menu }">
|
||||||
<template v-if="search">
|
<template v-if="search">
|
||||||
<div class="flex w-full" style="padding: 4px 8px">
|
<div class="flex w-full" style="padding: 4px 8px">
|
||||||
<a-input ref="inputRef" v-model:value="searchKeyRef" class="flex-1" allow-clear placeholder="查询关键字" @keydown.enter="doSearch" />
|
<a-input ref="inputRef" v-model:value="searchKeyRef" class="flex-1" allow-clear :placeholder="t('certd.pluginCommon.searchKeyword')" @keydown.enter="doSearch" />
|
||||||
<a-button class="ml-2" :loading="loading" type="text" @click="doSearch">
|
<a-button class="ml-2" :loading="loading" type="text" @click="doSearch">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<search-outlined />
|
<search-outlined />
|
||||||
</template>
|
</template>
|
||||||
查询
|
{{ t("certd.pluginCommon.search") }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasError" class="helper p-2" :class="{ error: hasError }">
|
<div v-if="hasError" class="helper p-2" :class="{ error: hasError }">
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-select>
|
</a-select>
|
||||||
<div class="ml-5 flex flex-row no-wrap">
|
<div class="ml-5 flex flex-row no-wrap">
|
||||||
<fs-button :loading="loading" title="刷新选项" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
<fs-button :loading="loading" :title="t('certd.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
||||||
<UploadCert v-if="uploadCert" class="ml-5" v-bind="uploadCert" @submit="refreshOptions"></UploadCert>
|
<UploadCert v-if="uploadCert" class="ml-5" v-bind="uploadCert" @submit="refreshOptions"></UploadCert>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||||
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { PluginDefine } from "@certd/pipeline";
|
import { PluginDefine } from "@certd/pipeline";
|
||||||
import { getInputFromForm } from "./utils";
|
import { getInputFromForm } from "./utils";
|
||||||
import UploadCert from "./upload-cert.vue";
|
import UploadCert from "./upload-cert.vue";
|
||||||
@@ -47,6 +48,8 @@ defineOptions({
|
|||||||
name: "RemoteSelect",
|
name: "RemoteSelect",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const selectRef = ref(null);
|
const selectRef = ref(null);
|
||||||
|
|
||||||
const VNodes = defineComponent({
|
const VNodes = defineComponent({
|
||||||
@@ -161,7 +164,7 @@ const getOptions = async () => {
|
|||||||
{
|
{
|
||||||
onError(err: any) {
|
onError(err: any) {
|
||||||
hasError.value = true;
|
hasError.value = true;
|
||||||
message.value = `获取选项出错:${err.message}`;
|
message.value = t("certd.pluginCommon.getOptionsError", { message: err.message });
|
||||||
optionsRef.value = [];
|
optionsRef.value = [];
|
||||||
},
|
},
|
||||||
showErrorNotify: false,
|
showErrorNotify: false,
|
||||||
@@ -169,9 +172,9 @@ const getOptions = async () => {
|
|||||||
);
|
);
|
||||||
let list = res?.list || res || [];
|
let list = res?.list || res || [];
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
message.value = "获取数据成功,请从下拉框中选择";
|
message.value = t("certd.pluginCommon.getDataSuccessSelect");
|
||||||
} else {
|
} else {
|
||||||
message.value = "获取数据成功,没有数据";
|
message.value = t("certd.pluginCommon.getDataSuccessEmpty");
|
||||||
}
|
}
|
||||||
list = list.map((item: any) => {
|
list = list.map((item: any) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="flex flex-row">
|
<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>
|
<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">
|
<div class="ml-5">
|
||||||
<fs-button :loading="loading" title="刷新选项" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
<fs-button :loading="loading" :title="t('certd.pluginCommon.refreshOptions')" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="helper" :class="{ error: hasError }">
|
<div class="helper" :class="{ error: hasError }">
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||||
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { PluginDefine } from "@certd/pipeline";
|
import { PluginDefine } from "@certd/pipeline";
|
||||||
import { getInputFromForm } from "./utils";
|
import { getInputFromForm } from "./utils";
|
||||||
|
|
||||||
@@ -21,6 +22,8 @@ defineOptions({
|
|||||||
name: "RemoteTreeSelect",
|
name: "RemoteTreeSelect",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps<
|
const props = defineProps<
|
||||||
{
|
{
|
||||||
watches: string[];
|
watches: string[];
|
||||||
@@ -104,16 +107,16 @@ const getOptions = async () => {
|
|||||||
{
|
{
|
||||||
onError(err: any) {
|
onError(err: any) {
|
||||||
hasError.value = true;
|
hasError.value = true;
|
||||||
message.value = `获取选项出错:${err.message}`;
|
message.value = t("certd.pluginCommon.getOptionsError", { message: err.message });
|
||||||
},
|
},
|
||||||
showErrorNotify: false,
|
showErrorNotify: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const list = res?.list || res || [];
|
const list = res?.list || res || [];
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
message.value = "获取数据成功,请从下拉框中选择";
|
message.value = t("certd.pluginCommon.getDataSuccessSelect");
|
||||||
} else {
|
} else {
|
||||||
message.value = "获取数据成功,没有数据";
|
message.value = t("certd.pluginCommon.getDataSuccessEmpty");
|
||||||
}
|
}
|
||||||
optionsRef.value = list;
|
optionsRef.value = list;
|
||||||
pagerRef.value.total = list.length;
|
pagerRef.value.total = list.length;
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="upload-cert">
|
<div class="upload-cert">
|
||||||
<fs-button v-model:loading="loading" type="primary" text="上传" v-bind="props.button" @click="openUploadCertDialog"></fs-button>
|
<fs-button v-model:loading="loading" type="primary" :text="t('certd.pluginCommon.upload')" v-bind="props.button" @click="openUploadCertDialog"></fs-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import { useFormDialog } from "../../../use/use-dialog";
|
import { useFormDialog } from "../../../use/use-dialog";
|
||||||
import { computed, inject, ref } from "vue";
|
import { computed, inject, ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { doRequest } from "../lib";
|
import { doRequest } from "../lib";
|
||||||
import { getInputFromForm } from "./utils";
|
import { getInputFromForm } from "./utils";
|
||||||
import { UploadCertProps } from "./types";
|
import { UploadCertProps } from "./types";
|
||||||
@@ -14,6 +15,7 @@ import { merge } from "lodash-es";
|
|||||||
|
|
||||||
const props = defineProps<UploadCertProps>();
|
const props = defineProps<UploadCertProps>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const emit = defineEmits(["submit"]);
|
const emit = defineEmits(["submit"]);
|
||||||
const { openFormDialog } = useFormDialog();
|
const { openFormDialog } = useFormDialog();
|
||||||
@@ -28,18 +30,18 @@ const getScope: any = inject("get:scope", () => {
|
|||||||
const getPluginType: any = inject("get:plugin:type", () => {
|
const getPluginType: any = inject("get:plugin:type", () => {
|
||||||
return "plugin";
|
return "plugin";
|
||||||
});
|
});
|
||||||
const title = computed(() => props.title || "上传证书");
|
const title = computed(() => props.title || t("certd.pluginCommon.uploadCert"));
|
||||||
function openUploadCertDialog() {
|
function openUploadCertDialog() {
|
||||||
const columns = merge(
|
const columns = merge(
|
||||||
{
|
{
|
||||||
certName: {
|
certName: {
|
||||||
title: "证书名称",
|
title: t("certd.pluginCommon.certName"),
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
name: "a-input",
|
name: "a-input",
|
||||||
vModel: "value",
|
vModel: "value",
|
||||||
},
|
},
|
||||||
helper: "上传后证书显示名称",
|
helper: t("certd.pluginCommon.certNameHelper"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -49,7 +51,7 @@ function openUploadCertDialog() {
|
|||||||
title: title.value,
|
title: title.value,
|
||||||
columns: {
|
columns: {
|
||||||
certName: {
|
certName: {
|
||||||
title: "证书名称",
|
title: t("certd.pluginCommon.certName"),
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
name: "a-input",
|
name: "a-input",
|
||||||
@@ -84,7 +86,7 @@ function openUploadCertDialog() {
|
|||||||
showErrorNotify: true,
|
showErrorNotify: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
message.success("上传成功");
|
message.success(t("certd.pluginCommon.uploadSuccess"));
|
||||||
emit("submit");
|
emit("submit");
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import { dict } from "@fast-crud/fast-crud";
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
|
import { $t } from "/@/locales";
|
||||||
|
|
||||||
export const Dicts = {
|
export const Dicts = {
|
||||||
sslProviderDict: dict({
|
sslProviderDict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ value: "letsencrypt", label: "Let‘s Encrypt" },
|
{ value: "letsencrypt", label: "Let's Encrypt" },
|
||||||
{ value: "zerossl", label: "ZeroSSL" },
|
{ value: "zerossl", label: "ZeroSSL" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
challengeTypeDict: dict({
|
challengeTypeDict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ value: "dns", label: "DNS校验", color: "green" },
|
{ value: "dns", label: $t("certd.verifyPlan.dnsChallenge"), color: "green" },
|
||||||
{ value: "cname", label: "CNAME代理校验", color: "blue" },
|
{ value: "cname", label: $t("certd.verifyPlan.cnameProxyChallenge"), color: "blue" },
|
||||||
{ value: "http", label: "HTTP校验", color: "yellow" },
|
{ value: "http", label: $t("certd.verifyPlan.httpChallenge"), color: "yellow" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
dnsProviderTypeDict: dict({
|
dnsProviderTypeDict: dict({
|
||||||
@@ -22,17 +23,17 @@ export const Dicts = {
|
|||||||
{ label: "SFTP", value: "sftp" },
|
{ label: "SFTP", value: "sftp" },
|
||||||
{ label: "SCP", value: "scp" },
|
{ label: "SCP", value: "scp" },
|
||||||
{ label: "FTP", value: "ftp" },
|
{ label: "FTP", value: "ftp" },
|
||||||
{ label: "阿里云OSS", value: "alioss" },
|
{ label: $t("certd.verifyPlan.uploader.aliyunOss"), value: "alioss" },
|
||||||
{ label: "腾讯云COS", value: "tencentcos" },
|
{ label: $t("certd.verifyPlan.uploader.tencentCos"), value: "tencentcos" },
|
||||||
{ label: "七牛OSS", value: "qiniuoss" },
|
{ label: $t("certd.verifyPlan.uploader.qiniuOss"), value: "qiniuoss" },
|
||||||
{ label: "S3/Minio", value: "s3" },
|
{ label: "S3/Minio", value: "s3" },
|
||||||
{ label: "SSH(已废弃,请选择SFTP方式)", value: "ssh", disabled: true },
|
{ label: $t("certd.verifyPlan.uploader.sshDeprecated"), value: "ssh", disabled: true },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
domainFromTypeDict: dict({
|
domainFromTypeDict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ value: "manual", label: "手动" },
|
{ value: "manual", label: $t("certd.verifyPlan.domainFrom.manual") },
|
||||||
{ value: "auto", label: "自动" },
|
{ value: "auto", label: $t("certd.verifyPlan.domainFrom.auto") },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,5 +20,6 @@ export async function getTodayVipOrderCount() {
|
|||||||
return await request({
|
return await request({
|
||||||
url: "/sys/plus/getTodayVipOrderCount",
|
url: "/sys/plus/getTodayVipOrderCount",
|
||||||
method: "post",
|
method: "post",
|
||||||
|
showErrorNotify: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mt-10 vip-active-modal">
|
<div class="mt-10 vip-active-modal">
|
||||||
<div v-if="todayOrderCount.enabled" class="order-count hidden md:flex">
|
|
||||||
<div v-for="(stage, index) in todayOrderCount.stages" :key="index" class="status-item" :class="{ 'status-show': TodayVipOrderCountRef.current === index }">
|
|
||||||
<div class="background">
|
|
||||||
<img :src="stage.bg" alt="" />
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col order-count-text weight-bold">
|
|
||||||
<div class="count-text ml-4 flex items-center">
|
|
||||||
<fs-icon icon="noto:fire" class="fs-20 mr-2"></fs-icon>
|
|
||||||
<template v-if="stage.vipTotal > 0">
|
|
||||||
<span> 已有 </span>
|
|
||||||
<span class="count-number color-red font-bold text-2xl ml-1 mr-1"> {{ stage.vipTotal }} </span> 位小伙伴赞助,
|
|
||||||
<span>
|
|
||||||
{{ stage.title }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<span> 今日赞助 </span>
|
|
||||||
<span class="count-number color-red font-bold text-2xl ml-1 mr-1"> {{ stage.orderCount }} </span> 人,
|
|
||||||
<span>
|
|
||||||
{{ stage.title }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="productInfo.notice" class="mt-10">
|
<div v-if="productInfo.notice" class="mt-10">
|
||||||
<a-alert type="error" :message="productInfo.notice"></a-alert>
|
<a-alert type="error" :message="productInfo.notice"></a-alert>
|
||||||
</div>
|
</div>
|
||||||
@@ -248,73 +222,35 @@ const vipTypeDefine: any = {
|
|||||||
const TodayVipOrderCountRef: Ref = ref({ enabled: false, current: 0, stages: [] });
|
const TodayVipOrderCountRef: Ref = ref({ enabled: false, current: 0, stages: [] });
|
||||||
|
|
||||||
async function getTodayVipOrderCount() {
|
async function getTodayVipOrderCount() {
|
||||||
const res = await api.getTodayVipOrderCount();
|
try {
|
||||||
if (res) {
|
const res = await api.getTodayVipOrderCount();
|
||||||
TodayVipOrderCountRef.value = res;
|
if (res) {
|
||||||
TodayVipOrderCountRef.value.current = 0;
|
TodayVipOrderCountRef.value = res;
|
||||||
|
TodayVipOrderCountRef.value.current = 0;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const todayOrderCount = computed(() => {
|
const todayOrderCount = computed(() => {
|
||||||
const countInfo = TodayVipOrderCountRef.value;
|
const countInfo = TodayVipOrderCountRef.value;
|
||||||
const enabled = countInfo?.enabled || false;
|
|
||||||
const orderCount = countInfo?.orderCount || 0;
|
|
||||||
for (const stage of countInfo?.stages) {
|
|
||||||
stage.orderCount = stage.countGe || 0;
|
|
||||||
}
|
|
||||||
const lastStage = countInfo?.stages?.[countInfo?.stages?.length - 1] || {};
|
|
||||||
lastStage.orderCount = orderCount;
|
|
||||||
|
|
||||||
const vipTotal = countInfo?.vipTotal || 0;
|
const vipTotal = countInfo?.vipTotal || 0;
|
||||||
const showVipTotal = countInfo?.showVipTotal || false;
|
const showVipTotal = countInfo?.showVipTotal || false;
|
||||||
const userTotal = countInfo?.userTotal || 0;
|
const userTotal = countInfo?.userTotal || 0;
|
||||||
const stages: any = [];
|
|
||||||
stages.push({
|
|
||||||
title: countInfo.title,
|
|
||||||
vipTotal: countInfo?.vipTotal || 0,
|
|
||||||
orderCount: orderCount,
|
|
||||||
bg: lastStage.bg,
|
|
||||||
showVipTotal: showVipTotal,
|
|
||||||
});
|
|
||||||
if (lastStage.orderCount > 0) {
|
|
||||||
stages.push(lastStage);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
enabled: enabled,
|
|
||||||
stages: stages,
|
|
||||||
showVipTotal: showVipTotal,
|
showVipTotal: showVipTotal,
|
||||||
vipTotal: vipTotal,
|
vipTotal: vipTotal,
|
||||||
userTotal: userTotal,
|
userTotal: userTotal,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
async function scrollOrderCount() {
|
|
||||||
const stages = todayOrderCount.value.stages;
|
|
||||||
if (stages.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let index = 0;
|
|
||||||
const doScroll = () => {
|
|
||||||
TodayVipOrderCountRef.value.current = index;
|
|
||||||
index++;
|
|
||||||
if (index >= stages.length) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
doScroll();
|
|
||||||
scrollOrderCountIntervalRef.value = setInterval(doScroll, 7000);
|
|
||||||
}
|
|
||||||
|
|
||||||
const scrollOrderCountIntervalRef: Ref = ref(null);
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getTodayVipOrderCount();
|
await getTodayVipOrderCount();
|
||||||
await nextTick();
|
|
||||||
await scrollOrderCount();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {});
|
||||||
clearInterval(scrollOrderCountIntervalRef.value);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span v-if="!settingStore.isComm">
|
<span v-if="!settingStore.isComm">
|
||||||
<span>Powered by</span>
|
<span>Powered by</span>
|
||||||
<a> handsfree.work </a>
|
<a href="https://certd.docmirror.cn/" target="_blank"> handfree.work </a>
|
||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -21,7 +21,12 @@
|
|||||||
|
|
||||||
<span v-if="sysPublic.mpsNo">
|
<span v-if="sysPublic.mpsNo">
|
||||||
<a href="http://www.beian.gov.cn/portal/registerSystemInfo" target="_blank">{{ sysPublic.mpsNo }}</a>
|
<a href="http://www.beian.gov.cn/portal/registerSystemInfo" target="_blank">{{ sysPublic.mpsNo }}</a>
|
||||||
|
<a-divider type="vertical" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<template v-if="sysPublic.customFooter && settingStore.isPlus">
|
||||||
|
<div v-html="sysPublic.customFooter"></div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-5">v{{ version }}</div>
|
<div class="ml-5">v{{ version }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<span v-if="!settingStore.isComm">
|
<span v-if="!settingStore.isComm">
|
||||||
<span>Powered by</span>
|
<span>Powered by</span>
|
||||||
<a> handfree.work </a>
|
<a href="https://certd.docmirror.cn/" target="_blank"> handfree.work </a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<template v-if="siteInfo.licenseTo">
|
<template v-if="siteInfo.licenseTo">
|
||||||
|
|||||||
@@ -1,861 +1,46 @@
|
|||||||
//注意: @符号是保留字符,需要用{'@'}替换, AI请务必提醒我修改
|
import common from "./certd/common";
|
||||||
|
import navigation from "./certd/navigation";
|
||||||
|
import dashboard from "./certd/dashboard";
|
||||||
|
import pipeline from "./certd/pipeline";
|
||||||
|
import history from "./certd/history";
|
||||||
|
import monitor from "./certd/monitor";
|
||||||
|
import certdomain from "./certd/cert-domain";
|
||||||
|
import cname from "./certd/cname";
|
||||||
|
import access from "./certd/access";
|
||||||
|
import open from "./certd/open";
|
||||||
|
import mine from "./certd/mine";
|
||||||
|
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 tutorial from "./certd/tutorial";
|
||||||
|
import cron from "./certd/cron";
|
||||||
|
|
||||||
|
// Note: @ is reserved in locale messages; use {'@'} when needed.
|
||||||
export default {
|
export default {
|
||||||
app: {
|
...common,
|
||||||
crud: {
|
...navigation,
|
||||||
i18n: {
|
...dashboard,
|
||||||
name: "name",
|
...pipeline,
|
||||||
city: "city",
|
...history,
|
||||||
status: "status",
|
...monitor,
|
||||||
},
|
...certdomain,
|
||||||
},
|
...cname,
|
||||||
},
|
...access,
|
||||||
fs: {
|
...open,
|
||||||
rowHandle: {
|
...mine,
|
||||||
title: "Operation",
|
...notification,
|
||||||
},
|
...addon,
|
||||||
},
|
...suite,
|
||||||
pipelinePage: {
|
...project,
|
||||||
addMore: "Add More Pipelines",
|
...syssettings,
|
||||||
aliyunSubscriptionPipeline: "Aliyun Subscription Pipeline",
|
...sysplugin,
|
||||||
legoCertPipeline: "Lego Certificate Pipeline",
|
...sysauthority,
|
||||||
customPipeline: "Custom Pipeline",
|
...syscname,
|
||||||
batchAddPipeline: "Add Pipeline Use Template",
|
...tutorial,
|
||||||
myPipelinesDesc: "Pipeline Mode: Apply -> Deploy -> Schedule",
|
...cron,
|
||||||
},
|
|
||||||
order: {
|
|
||||||
confirmTitle: "Order Confirmation",
|
|
||||||
package: "Package",
|
|
||||||
description: "Description",
|
|
||||||
specifications: "Specifications",
|
|
||||||
pipeline: "Pipeline",
|
|
||||||
domain: "Domain",
|
|
||||||
deployTimes: "Deployments",
|
|
||||||
monitorCount: "DomainMonitors",
|
|
||||||
duration: "Duration",
|
|
||||||
price: "Price",
|
|
||||||
paymentMethod: "Payment Method",
|
|
||||||
free: "Free",
|
|
||||||
unit: {
|
|
||||||
pieces: "pieces",
|
|
||||||
count: "count",
|
|
||||||
times: "times",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
framework: {
|
|
||||||
title: "Framework",
|
|
||||||
home: "Home",
|
|
||||||
},
|
|
||||||
helpDocLink: "Help Docs",
|
|
||||||
title: "Certificate Automation",
|
|
||||||
pipeline: "Pipeline",
|
|
||||||
pipelineEdit: "Edit Pipeline",
|
|
||||||
history: "Execution History",
|
|
||||||
certStore: "Certificate Repository",
|
|
||||||
siteMonitor: "Site Certificate Monitor",
|
|
||||||
settings: "Settings",
|
|
||||||
accessManager: "Access Management",
|
|
||||||
cnameRecord: "CNAME Record Management",
|
|
||||||
subDomain: "Subdomain Delegation Settings",
|
|
||||||
pipelineGroup: "Pipeline Group Management",
|
|
||||||
openKey: "Open API Key",
|
|
||||||
notification: "Notification Settings",
|
|
||||||
siteMonitorSetting: "Site Monitor Settings",
|
|
||||||
userSecurity: "Security Settings",
|
|
||||||
userProfile: "Account Info",
|
|
||||||
userGrant: "Grant Delegation",
|
|
||||||
suite: "Suite",
|
|
||||||
mySuite: "My Suite",
|
|
||||||
suiteBuy: "Suite Purchase",
|
|
||||||
myTrade: "My Orders",
|
|
||||||
paymentReturn: "Payment Return",
|
|
||||||
hasExpired: "Expired",
|
|
||||||
user: {
|
|
||||||
greeting: "Hello",
|
|
||||||
profile: "Account Info",
|
|
||||||
logout: "Logout",
|
|
||||||
setting: {
|
|
||||||
grantSetting: "Grant Settings",
|
|
||||||
saveSuccess: "Save Success",
|
|
||||||
allowAdminViewCerts: "Allow Admin view and download Certs",
|
|
||||||
allowAdminViewCertsHelper: "Allow admin view and download all certificates",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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",
|
|
||||||
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",
|
|
||||||
supportedTasks: "Overview of Supported Deployment Tasks",
|
|
||||||
changeLog: "Change Log",
|
|
||||||
},
|
|
||||||
steps: {
|
|
||||||
createPipeline: "Create Certificate Pipeline",
|
|
||||||
addTask: "Add Deployment Task",
|
|
||||||
scheduledRun: "Scheduled Run",
|
|
||||||
},
|
|
||||||
customPipeline: "Custom Pipeline",
|
|
||||||
createCertdPipeline: "Create Certificate Pipeline",
|
|
||||||
commercialCertHosting: "Commercial Certificate Hosting",
|
|
||||||
tooltip: {
|
|
||||||
manualUploadOwnCert: "Manually upload your own certificate for automatic deployment",
|
|
||||||
noAutoApplyCommercialCert: "Does not automatically apply for commercial certificates",
|
|
||||||
manualUploadOnUpdate: "Must manually upload once when the certificate is updated",
|
|
||||||
},
|
|
||||||
table: {
|
|
||||||
confirmDeleteTitle: "Are you sure you want to delete?",
|
|
||||||
confirmDeleteMessage: "This will delete all data related to the pipeline, including execution history, certificate files, and certificate repository records.",
|
|
||||||
},
|
|
||||||
play: {
|
|
||||||
runPipeline: "Run Pipeline",
|
|
||||||
confirm: "Confirm",
|
|
||||||
confirmTrigger: "Are you sure you want to trigger the run?",
|
|
||||||
pipelineStarted: "Pipeline has started running",
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
editPipeline: "Edit Pipeline",
|
|
||||||
editConfigGroup: "Modify Configuration/Group",
|
|
||||||
viewCertificate: "View Certificate",
|
|
||||||
downloadCertificate: "Download Certificate",
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
userId: "User ID",
|
|
||||||
pipelineName: "Pipeline Name",
|
|
||||||
keyword: "Keyword",
|
|
||||||
required: "This field is required",
|
|
||||||
pipelineContent: "Pipeline Content",
|
|
||||||
scheduledTaskCount: "Scheduled Task Count",
|
|
||||||
deployTaskCount: "Deployment Task Count",
|
|
||||||
remainingValidity: "Remaining Validity",
|
|
||||||
effectiveTime: "Effective time",
|
|
||||||
expiryTime: "Expiry Time",
|
|
||||||
status: "Status",
|
|
||||||
lastRun: "Last Run",
|
|
||||||
enabled: "Enabled",
|
|
||||||
enabledLabel: "Enabled",
|
|
||||||
disabledLabel: "Disabled",
|
|
||||||
group: "Group",
|
|
||||||
type: "Type",
|
|
||||||
order: "Order Number",
|
|
||||||
keepHistoryCount: "History Record Retention Count",
|
|
||||||
keepHistoryHelper: "Number of history records to keep; excess will be deleted",
|
|
||||||
createTime: "Creation Time",
|
|
||||||
updateTime: "Update Time",
|
|
||||||
triggerType: "Trigger Type",
|
|
||||||
pipelineId: "Pipeline Id",
|
|
||||||
nextRunTime: "Next Run Time",
|
|
||||||
projectName: "Project",
|
|
||||||
adminId: "Admin",
|
|
||||||
},
|
|
||||||
|
|
||||||
pi: {
|
|
||||||
validTime: "Piepline Valid Time",
|
|
||||||
validTimeHelper: "Not filled in means permanent validity",
|
|
||||||
},
|
|
||||||
types: {
|
|
||||||
certApply: "Cert Apply",
|
|
||||||
certUpload: "Cert Upload",
|
|
||||||
custom: "Custom",
|
|
||||||
template: "Template",
|
|
||||||
},
|
|
||||||
myPipelines: "My Pipelines",
|
|
||||||
selectedCount: "Selected {count} items",
|
|
||||||
batchDelete: "Batch Delete",
|
|
||||||
batchForceRerun: "Force Rerun",
|
|
||||||
batchRerun: "Rerun",
|
|
||||||
applyCertificate: "Apply for Certificate",
|
|
||||||
pipelineExecutionRecords: "Pipeline Execution Records",
|
|
||||||
confirm: "Confirm",
|
|
||||||
confirmBatchDeleteContent: "Are you sure you want to batch delete these {count} records?",
|
|
||||||
deleteSuccess: "Delete successful",
|
|
||||||
pleaseSelectRecords: "Please select records first",
|
|
||||||
triggerTypes: {
|
|
||||||
manual: "Manual Execution",
|
|
||||||
timer: "Scheduled Execution",
|
|
||||||
},
|
|
||||||
sysResources: {
|
|
||||||
sysRoot: "System Management",
|
|
||||||
sysConsole: "Console",
|
|
||||||
sysSettings: "System Settings",
|
|
||||||
cnameSetting: "CNAME Service Settings",
|
|
||||||
emailSetting: "Email Server Settings",
|
|
||||||
siteSetting: "Site Personalization",
|
|
||||||
headerMenus: "Top Menu Settings",
|
|
||||||
sysAccess: "System-level Authorization",
|
|
||||||
sysPlugin: "Plugin Management",
|
|
||||||
sysPluginEdit: "Edit Plugin",
|
|
||||||
sysPluginConfig: "Certificate Plugin Configuration",
|
|
||||||
accountBind: "Account Binding",
|
|
||||||
permissionManager: "Permission Management",
|
|
||||||
roleManager: "Role Management",
|
|
||||||
userManager: "User Management",
|
|
||||||
suiteManager: "Suite Management",
|
|
||||||
suiteSetting: "Suite Settings",
|
|
||||||
orderManager: "Order Management",
|
|
||||||
userSuites: "User Suites",
|
|
||||||
netTest: "Network Test",
|
|
||||||
|
|
||||||
enterpriseSetting: "Enterprise Settings",
|
|
||||||
projectManager: "Project Management",
|
|
||||||
projectUserManager: "Project User Management",
|
|
||||||
myProjectManager: "My Projects",
|
|
||||||
myProjectDetail: "Project Detail",
|
|
||||||
projectJoin: "Join Project",
|
|
||||||
currentProject: "Current Project",
|
|
||||||
projectMemberManager: "Project Member",
|
|
||||||
domainMonitorSetting: "Domain Monitor Settings",
|
|
||||||
},
|
|
||||||
certificateRepo: {
|
|
||||||
title: "Certificate Repository",
|
|
||||||
sub: "Certificates generated from pipeline",
|
|
||||||
},
|
|
||||||
|
|
||||||
certificateNotGenerated: "Certificate not yet generated, please run the pipeline first",
|
|
||||||
viewCertificateTitle: "View Certificate",
|
|
||||||
close: "Close",
|
|
||||||
viewCert: {
|
|
||||||
title: "View Certificate",
|
|
||||||
},
|
|
||||||
download: {
|
|
||||||
title: "Download Certificate",
|
|
||||||
},
|
|
||||||
source: "Source Code",
|
|
||||||
github: "GitHub",
|
|
||||||
gitee: "Gitee",
|
|
||||||
cron: {
|
|
||||||
clearTip: "Clear Selection",
|
|
||||||
nextTrigger: "Next Trigger Time",
|
|
||||||
tip: "Please set a valid cron expression first",
|
|
||||||
},
|
|
||||||
cronForm: {
|
|
||||||
title: "Scheduled Script",
|
|
||||||
helper: "Click the button above to select the time for daily execution.\nIt is recommended to run once a day. Tasks will be skipped if the certificate is not expiring.",
|
|
||||||
required: "This field is required",
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
title: "Recipient Email",
|
|
||||||
helper: "Enter your recipient email addresses. Multiple addresses are supported.",
|
|
||||||
required: "This field is required",
|
|
||||||
},
|
|
||||||
plugin: {
|
|
||||||
selectTitle: "Certificate Apply Plugin",
|
|
||||||
jsAcme: "JS-ACME: Easy to use, powerful features [Recommended]",
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
pipelineForm: {
|
|
||||||
createTitle: "Create Certificate Pipeline",
|
|
||||||
moreParams: "More Parameters",
|
|
||||||
triggerCronTitle: "Scheduled Trigger",
|
|
||||||
triggerCronHelper:
|
|
||||||
"Click the button above to choose a daily execution time.\nIt is recommended to trigger once per day. The task will be skipped if the certificate has not expired and will not be executed repeatedly.",
|
|
||||||
notificationTitle: "Failure Notification",
|
|
||||||
notificationWhen: "Notification Timing",
|
|
||||||
notificationHelper: "Get real-time alerts when the task fails",
|
|
||||||
groupIdTitle: "Pipeline Group",
|
|
||||||
|
|
||||||
addToMonitorEnabled: "Add to Cert Monitor",
|
|
||||||
addToMonitorDomains: "Add to Monitor Domains",
|
|
||||||
|
|
||||||
webhookEnabled: "Webhook Enabled",
|
|
||||||
webhookEnabledHelper: "Support webhook trigger pipeline, please check webhook address in trigger source",
|
|
||||||
},
|
|
||||||
notificationDefault: "Use Default Notification",
|
|
||||||
checkStatus: {
|
|
||||||
success: "Success",
|
|
||||||
checking: "Checking",
|
|
||||||
error: "Error",
|
|
||||||
},
|
|
||||||
domainList: {
|
|
||||||
title: "Domain List",
|
|
||||||
helper: "Format: domain:port:name:remark, one per line. Port and name are optional.\nExamples:\nwww.baidu.com:443:Baidu:remarkText\nwww.taobao.com::Taobao\nwww.google.com",
|
|
||||||
required: "Please enter domains to import",
|
|
||||||
placeholder: "www.baidu.com:443:Baidu\nwww.taobao.com::Taobao\nwww.google.com\n",
|
|
||||||
},
|
|
||||||
accountInfo: "Account Information",
|
|
||||||
securitySettings: "Security & Settings",
|
|
||||||
confirmDisable2FA: "Are you sure you want to disable two-factor authentication login?",
|
|
||||||
disabledSuccess: "Disabled successfully",
|
|
||||||
saveSuccess: "Saved successfully",
|
|
||||||
twoFactorAuth: "2FA Two-Factor Authentication Login",
|
|
||||||
rebind: "Rebind",
|
|
||||||
twoFactorAuthHelper: "Enable or disable two-factor authentication login",
|
|
||||||
bindDevice: "Bind Device",
|
|
||||||
step1: "1. Install any authenticator app, for example:",
|
|
||||||
tooltipGoogleServiceError: "If you get a Google service not found error, you can install KK Google Assistant",
|
|
||||||
step2: "2. Scan the QR code to add the account",
|
|
||||||
step3: "3. Enter the verification code",
|
|
||||||
inputVerifyCode: "Please enter the verification code",
|
|
||||||
cancel: "Cancel",
|
|
||||||
authorizationManagement: "Authorization Management",
|
|
||||||
manageThirdPartyAuth: "Manage third-party system authorization information",
|
|
||||||
name: "Name",
|
|
||||||
pleaseEnterName: "Please enter the name",
|
|
||||||
nameHelper: "Fill in as you like, useful to distinguish when multiple authorizations of the same type exist",
|
|
||||||
level: "Level",
|
|
||||||
system: "System",
|
|
||||||
usera: "User",
|
|
||||||
nickName: "Nickname",
|
|
||||||
max50Chars: "Maximum 50 characters",
|
|
||||||
myInfo: "My Information",
|
|
||||||
save: "Save",
|
|
||||||
editSchedule: "Edit Schedule",
|
|
||||||
timerTrigger: "Timer Trigger",
|
|
||||||
schedule: "Schedule",
|
|
||||||
selectCron: "Please select a schedule Cron",
|
|
||||||
batchEditSchedule: "Batch Edit Schedule",
|
|
||||||
editTrigger: "Edit Trigger",
|
|
||||||
triggerName: "Trigger Name",
|
|
||||||
requiredField: "This field is required",
|
|
||||||
type: "Type",
|
|
||||||
enterName: "Please enter a name",
|
|
||||||
confirmDeleteTrigger: "Are you sure you want to delete this trigger?",
|
|
||||||
notificationType: "Notification Type",
|
|
||||||
selectNotificationType: "Please select a notification type",
|
|
||||||
notificationName: "Notification Name",
|
|
||||||
helperNotificationName: "Fill freely, helps to distinguish when multiple notifications of the same type exist",
|
|
||||||
isDefault: "Is Default",
|
|
||||||
yes: "Yes",
|
|
||||||
no: "No",
|
|
||||||
selectIsDefault: "Please select if default",
|
|
||||||
prompt: "Prompt",
|
|
||||||
confirmSetDefaultNotification: "Are you sure to set as default notification?",
|
|
||||||
test: "Test",
|
|
||||||
scope: "Scope",
|
|
||||||
scopeOpenApiOnly: "Open API Only",
|
|
||||||
scopeFullAccount: "Full Account Permissions",
|
|
||||||
required: "This field is required",
|
|
||||||
scopeHelper: "Open API only allows access to open APIs; full account permissions allow access to all APIs",
|
|
||||||
add: "Generate New Key",
|
|
||||||
gen: {
|
|
||||||
text: "API Test",
|
|
||||||
title: "x-certd-token",
|
|
||||||
okText: "Confirm",
|
|
||||||
contentPart1: "Test the x-certd-token below, you can use it within 3 minutes to test ",
|
|
||||||
openApi: "Open API",
|
|
||||||
contentPart2: " request testing",
|
|
||||||
},
|
|
||||||
pending_cname_setup: "Pending CNAME setup",
|
|
||||||
validating: "Validating",
|
|
||||||
validation_successful: "Validation successful",
|
|
||||||
validation_failed: "Validation failed",
|
|
||||||
validation_timed_out: "Validation timed out",
|
|
||||||
proxied_domain: "Proxied Domain",
|
|
||||||
host_record: "Host Record",
|
|
||||||
please_set_cname: "Please set CNAME",
|
|
||||||
cname_service: "CNAME Service",
|
|
||||||
default_public_cname: "Default public CNAME service, you can also ",
|
|
||||||
customize_cname: "Customize CNAME Service",
|
|
||||||
public_cname: "Public CNAME",
|
|
||||||
custom_cname: "Custom CNAME",
|
|
||||||
validate: "Validate",
|
|
||||||
validation_started: "Validation started, please wait patiently",
|
|
||||||
click_to_validate: "Click to Validate",
|
|
||||||
all: "All",
|
|
||||||
cname_feature_guide: "CNAME feature principle and usage guide",
|
|
||||||
batch_delete: "Batch Delete",
|
|
||||||
confirm_delete_count: "Are you sure to delete these {count} records in batch?",
|
|
||||||
delete_successful: "Delete successful",
|
|
||||||
please_select_records: "Please select records first",
|
|
||||||
edit_notification: "Edit Notification",
|
|
||||||
other_notification_method: "Other Notification Method",
|
|
||||||
trigger_time: "Trigger Time",
|
|
||||||
start_time: "At Start",
|
|
||||||
success_time: "On Success",
|
|
||||||
fail_to_success_time: "Fail to Success",
|
|
||||||
fail_time: "On Failure",
|
|
||||||
helper_suggest_fail_only: "It is recommended to select only 'On Failure' and 'Fail to Success'",
|
|
||||||
notification_config: "Notification Configuration",
|
|
||||||
please_select_notification: "Please select a notification method",
|
|
||||||
please_select_type: "Please select type",
|
|
||||||
please_select_trigger_time: "Please select notification trigger time",
|
|
||||||
please_select_notification_config: "Please select notification configuration",
|
|
||||||
confirm_delete_trigger: "Are you sure you want to delete this trigger?",
|
|
||||||
gift_package: "Gift Package",
|
|
||||||
package_name: "Package Name",
|
|
||||||
click_to_select: "Click to select",
|
|
||||||
please_select_package: "Please select a package",
|
|
||||||
package: "Package",
|
|
||||||
addon_package: "Addon Package",
|
|
||||||
domain_count: "Domain Count",
|
|
||||||
unit_count: "pcs",
|
|
||||||
field_required: "This field is required",
|
|
||||||
pipeline_count: "Pipeline Count",
|
|
||||||
unit_item: "items",
|
|
||||||
deploy_count: "Deploy Count",
|
|
||||||
unit_times: "times",
|
|
||||||
monitor_count: "Certificate Monitor Count",
|
|
||||||
duration: "Duration",
|
|
||||||
status: "Status",
|
|
||||||
active_time: "Activation Time",
|
|
||||||
expires_time: "Expiration Time",
|
|
||||||
is_present: "Is Present",
|
|
||||||
is_present_yes: "Yes",
|
|
||||||
is_present_no: "No",
|
|
||||||
basicInfo: "Basic Information",
|
|
||||||
titlea: "Title",
|
|
||||||
disabled: "Disabled",
|
|
||||||
ordera: "Order",
|
|
||||||
supportBuy: "Support Purchase",
|
|
||||||
intro: "Introduction",
|
|
||||||
packageContent: "Package Content",
|
|
||||||
maxDomainCount: "Max Domain Count",
|
|
||||||
maxPipelineCount: "Max Pipeline Count",
|
|
||||||
maxDeployCount: "Max Deploy Count",
|
|
||||||
maxMonitorCount: "Max Monitor Count",
|
|
||||||
price: "Price",
|
|
||||||
durationPrices: "Duration Prices",
|
|
||||||
packageName: "Package Name",
|
|
||||||
addon: "Addon",
|
|
||||||
typeHelper: "Suite: Only the most recently purchased one is active at a time\nAddon: Multiple can be purchased, effective immediately without affecting the suite\nThe quantities of suite and addon can be accumulated",
|
|
||||||
domainCount: "Domain Count",
|
|
||||||
pipelineCount: "Pipeline Count",
|
|
||||||
unitPipeline: "pipelines",
|
|
||||||
deployCount: "Deployment Count",
|
|
||||||
unitDeploy: "times",
|
|
||||||
monitorCount: "Certificate Monitor Count",
|
|
||||||
unitCount: "pcs",
|
|
||||||
durationPriceTitle: "Duration and Price",
|
|
||||||
selectDuration: "Select Duration",
|
|
||||||
supportPurchase: "Support Purchase",
|
|
||||||
cannotPurchase: "Cannot Purchase",
|
|
||||||
shelfStatus: "Shelf Status",
|
|
||||||
onShelf: "On Shelf",
|
|
||||||
offShelf: "Off Shelf",
|
|
||||||
orderHelper: "Smaller values appear first",
|
|
||||||
description: "Description",
|
|
||||||
createTime: "Creation Time",
|
|
||||||
updateTime: "Update Time",
|
|
||||||
mainDomain: "Main Domain",
|
|
||||||
edit: "Edit",
|
|
||||||
groupName: "Group Name",
|
|
||||||
enterGroupName: "Please enter group name",
|
|
||||||
subdomainHosting: "Subdomain Hosting",
|
|
||||||
subdomainHostingHint: "When your domain has subdomain hosting set, you need to create records here, otherwise certificate application will fail",
|
|
||||||
batchDeleteConfirm: "Are you sure to batch delete these {count} records?",
|
|
||||||
selectRecordFirst: "Please select records first",
|
|
||||||
subdomainHosted: "Hosted Subdomain",
|
|
||||||
subdomainHelpText: "If you don't understand what subdomain hosting is,Do not set it randomly, as it may result in the inability to apply for the certificate. please refer to the documentation ",
|
|
||||||
subdomainHelpSupportStart: "Supports * wildcard, indicating that all subdomains of the domain are hosted (free subdomains)",
|
|
||||||
subdomainManagement: "Subdomain Management",
|
|
||||||
isDisabled: "Is Disabled",
|
|
||||||
enabled: "Enabled",
|
|
||||||
uploadCustomCert: "Upload Custom Certificate",
|
|
||||||
sourcee: "Source",
|
|
||||||
sourcePipeline: "Cert Apply Pipeline",
|
|
||||||
sourceManualUpload: "Hosted Pipeline",
|
|
||||||
domains: "Domains",
|
|
||||||
enterDomain: "Please enter domain",
|
|
||||||
validDays: "Valid Days",
|
|
||||||
expires: " expires",
|
|
||||||
days: " days",
|
|
||||||
effectiveTime: "Effective Time",
|
|
||||||
expireTime: "Expiration Time",
|
|
||||||
certIssuer: "Certificate Issuer",
|
|
||||||
applyTime: "Application Time",
|
|
||||||
relatedPipeline: "Related Pipeline",
|
|
||||||
statusSuccess: "Success",
|
|
||||||
statusChecking: "Checking",
|
|
||||||
statusError: "Error",
|
|
||||||
actionImportBatch: "Batch Import",
|
|
||||||
actionSyncIp: "Sync IP",
|
|
||||||
modalTitleSyncIp: "Sync IP",
|
|
||||||
modalContentSyncIp: "Are you sure to sync IP?",
|
|
||||||
notificationSyncComplete: "Sync Complete",
|
|
||||||
actionCheckAll: "Check All",
|
|
||||||
modalTitleConfirm: "Confirm",
|
|
||||||
modalContentCheckAll: "Confirm to trigger checking all IP site's certificates?",
|
|
||||||
notificationCheckSubmitted: "Check task submitted",
|
|
||||||
notificationCheckDescription: "Please refresh later to see results",
|
|
||||||
tooltipCheckNow: "Check Now",
|
|
||||||
notificationCheckSubmittedPleaseRefresh: "Check task submitted, please refresh later",
|
|
||||||
columnId: "ID",
|
|
||||||
columnIp: "IP",
|
|
||||||
helperIpCname: "Supports entering CNAME domain name or source site domain name",
|
|
||||||
ruleIpRequired: "Please enter IP",
|
|
||||||
columnCertDomains: "Certificate Domains",
|
|
||||||
columnCertProvider: "Issuer",
|
|
||||||
columnCertStatus: "Certificate Status",
|
|
||||||
statusNormal: "Normal",
|
|
||||||
statusExpired: "Expired",
|
|
||||||
columnCertExpiresTime: "Certificate Expiration Time",
|
|
||||||
expired: "expired",
|
|
||||||
columnCheckStatus: "Check Status",
|
|
||||||
columnLastCheckTime: "Last Check Time",
|
|
||||||
columnSource: "Source",
|
|
||||||
sourceSync: "Sync",
|
|
||||||
sourceManual: "Manual",
|
|
||||||
sourceImport: "Import",
|
|
||||||
columnDisabled: "Enabled/Disabled",
|
|
||||||
columnRemark: "Remark",
|
|
||||||
pluginFile: "Plugin File",
|
|
||||||
selectPluginFile: "Select plugin file",
|
|
||||||
overrideSameName: "Override same name",
|
|
||||||
override: "Override",
|
|
||||||
noOverride: "No override",
|
|
||||||
overrideHelper: "If a plugin with the same name exists, override it directly",
|
|
||||||
importPlugin: "Import Plugin",
|
|
||||||
operationSuccess: "Operation successful",
|
|
||||||
customPlugin: "Custom Plugin",
|
|
||||||
import: "Import",
|
|
||||||
export: "Export",
|
|
||||||
pluginType: "Plugin Type",
|
|
||||||
auth: "Authorization",
|
|
||||||
dns: "DNS",
|
|
||||||
deployPlugin: "Deploy Plugin",
|
|
||||||
icon: "Icon",
|
|
||||||
pluginName: "Plugin Name",
|
|
||||||
pluginNameHelper: "Must be English letters or digits, camelCase with type prefix\nExample: AliyunDeployToCDN\nDo not modify name once plugin is used",
|
|
||||||
pluginNameRuleMsg: "Must be English letters or digits, camelCase with type prefix",
|
|
||||||
author: "Author",
|
|
||||||
authorHelper: "Used as prefix when uploading to plugin store, e.g., greper/pluginName",
|
|
||||||
authorRuleMsg: "Must be English letters or digits",
|
|
||||||
titleHelper: "Plugin name in Chinese",
|
|
||||||
descriptionHelper: "Description of the plugin",
|
|
||||||
builtIn: "Built-in",
|
|
||||||
custom: "Custom",
|
|
||||||
store: "Store",
|
|
||||||
version: "Version",
|
|
||||||
pluginDependencies: "Plugin Dependencies",
|
|
||||||
pluginDependenciesHelper: "Dependencies to install first in format: [author/]pluginName[:version]",
|
|
||||||
editableRunStrategy: "Editable Run Strategy",
|
|
||||||
editable: "Editable",
|
|
||||||
notEditable: "Not Editable",
|
|
||||||
runStrategy: "Run Strategy",
|
|
||||||
normalRun: "Normal Run",
|
|
||||||
skipOnSuccess: "Skip on success (Deploy task)",
|
|
||||||
defaultRunStrategyHelper: "Default run strategy",
|
|
||||||
enableDisable: "Enable/Disable",
|
|
||||||
clickToToggle: "Click to toggle enable/disable",
|
|
||||||
confirmToggle: "Are you sure to",
|
|
||||||
disable: "disable",
|
|
||||||
enable: "enable",
|
|
||||||
pluginGroup: "Plugin Group",
|
|
||||||
icpRegistrationNumber: "ICP Registration Number",
|
|
||||||
icpPlaceholder: "Guangdong ICP xxxxxxx Number",
|
|
||||||
publicSecurityRegistrationNumber: "Public Security Registration Number",
|
|
||||||
publicSecurityPlaceholder: "Beijing Public Security xxxxxxx Number",
|
|
||||||
enableAssistant: "Enable Assistant",
|
|
||||||
allowCrawlers: "Allow Crawlers",
|
|
||||||
httpProxy: "HTTP Proxy",
|
|
||||||
httpProxyPlaceholder: "http://192.168.1.2:18010/",
|
|
||||||
httpProxyHelper: "Configure when some websites are blocked",
|
|
||||||
httpsProxy: "HTTPS Proxy",
|
|
||||||
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
|
||||||
saveThenTestTitle: "Save first, then click test",
|
|
||||||
testButton: "Test",
|
|
||||||
httpsProxyHelper: "Usually both proxies are the same, save first then test",
|
|
||||||
dualStackNetwork: "Dual Stack Network",
|
|
||||||
default: "Default",
|
|
||||||
ipv4Priority: "IPv4 Priority",
|
|
||||||
ipv6Priority: "IPv6 Priority",
|
|
||||||
dualStackNetworkHelper: "If IPv6 priority is selected, enable IPv6 in docker-compose.yaml",
|
|
||||||
enableCommonCnameService: "Enable Public CNAME Service",
|
|
||||||
commonCnameHelper: "Allow use of public CNAME service. If disabled and no <a href='#/sys/cname/provider'>custom CNAME service</a> is set, CNAME proxy certificate application will not work.",
|
|
||||||
enableCommonSelfServicePasswordRetrieval: "Enable self-service password recovery",
|
|
||||||
saveButton: "Save",
|
|
||||||
stopSuccess: "Stopped successfully",
|
|
||||||
google: "Google",
|
|
||||||
baidu: "Baidu",
|
|
||||||
success: "Success",
|
|
||||||
testFailed: "Test Failed",
|
|
||||||
testCompleted: "Test Completed",
|
|
||||||
manageOtherUserPipeline: "Manage other users' pipelines",
|
|
||||||
limitUserPipelineCount: "Limit user pipeline count",
|
|
||||||
limitUserPipelineCountHelper: "0 means no limit",
|
|
||||||
enableSelfRegistration: "Enable self-registration",
|
|
||||||
enableUserValidityPeriod: "Enable user validity period",
|
|
||||||
userValidityPeriodHelper: "Users can use normally within validity; pipelines disabled after expiry",
|
|
||||||
enableUsernameRegistration: "Enable username registration",
|
|
||||||
enableEmailRegistration: "Enable email registration",
|
|
||||||
proFeature: "Pro feature",
|
|
||||||
emailServerSetup: "Set up email server",
|
|
||||||
enableSmsLoginRegister: "Enable SMS login and registration",
|
|
||||||
commFeature: "Commercial feature",
|
|
||||||
smsProvider: "SMS provider",
|
|
||||||
aliyunSms: "Aliyun SMS",
|
|
||||||
tencentSms: "Tencent SMS",
|
|
||||||
yfySms: "YFY SMS",
|
|
||||||
smsTest: "SMS test",
|
|
||||||
testMobilePlaceholder: "Enter test mobile number",
|
|
||||||
saveThenTest: "Save first then test",
|
|
||||||
enterTestMobile: "Please enter test mobile number",
|
|
||||||
sendSuccess: "Sent successfully",
|
|
||||||
atLeastOneLoginRequired: "At least one of password login or SMS login must be enabled",
|
|
||||||
fieldRequired: "This field is required",
|
|
||||||
siteHide: "Site Hide",
|
|
||||||
enableSiteHide: "Enable Site Hide",
|
|
||||||
siteHideDescription: "You can disable site accessibility normally and enable it when needed to enhance site security",
|
|
||||||
helpDoc: "Help Document",
|
|
||||||
randomAddress: "Random Address",
|
|
||||||
siteHideUrlHelper: "After the site is hidden, you need to visit this URL to unlock to access normally",
|
|
||||||
fullUnlockUrl: "Full Unlock URL",
|
|
||||||
saveThisUrl: "Please save this URL carefully",
|
|
||||||
unlockPassword: "Unlock Password",
|
|
||||||
unlockPasswordHelper: "Password needed to unlock the hide; set on first time or reset when filled",
|
|
||||||
autoHideTime: "Auto Hide Time",
|
|
||||||
autoHideTimeHelper: "Minutes without requests before auto hiding",
|
|
||||||
hideOpenApi: "Hide Open API",
|
|
||||||
hideOpenApiHelper: "Whether to hide open APIs; whether to hide /api/v1 prefixed endpoints",
|
|
||||||
hideSiteImmediately: "Hide Site Immediately",
|
|
||||||
hideImmediately: "Hide Immediately",
|
|
||||||
confirmHideSiteTitle: "Are you sure to hide the site immediately?",
|
|
||||||
confirmHideSiteContent: "After hiding, the site will be inaccessible. Please operate cautiously.",
|
|
||||||
siteHiddenSuccess: "Site has been hidden",
|
|
||||||
emailServerSettings: "Email Server Settings",
|
|
||||||
setEmailSendingServer: "Set the email sending server",
|
|
||||||
useCustomEmailServer: "Use Custom Email Server",
|
|
||||||
smtpDomain: "SMTP Domain",
|
|
||||||
pleaseEnterSmtpDomain: "Please enter SMTP domain or IP",
|
|
||||||
smtpPort: "SMTP Port",
|
|
||||||
pleaseEnterSmtpPort: "Please enter SMTP port",
|
|
||||||
username: "Username",
|
|
||||||
pleaseEnterUsername: "Please enter username",
|
|
||||||
password: "Password",
|
|
||||||
pleaseEnterPassword: "Please enter password",
|
|
||||||
qqEmailAuthCodeHelper: "If using QQ email, get an authorization code in QQ email settings as the password",
|
|
||||||
senderEmailHelper: "You can use the format: Name<Email> to set the sender name, e.g.: autossl<certd{'@'}example.com>",
|
|
||||||
senderEmail: "Sender Email",
|
|
||||||
pleaseEnterSenderEmail: "Please enter sender email",
|
|
||||||
useSsl: "Use SSL",
|
|
||||||
sslPortNote: "SSL and non-SSL SMTP ports are different, please adjust port accordingly",
|
|
||||||
ignoreCertValidation: "Ignore Certificate Validation",
|
|
||||||
useOfficialEmailServer: "Use Official Email Server",
|
|
||||||
useOfficialEmailServerHelper: "Send emails directly using the official server to avoid complicated setup",
|
|
||||||
testReceiverEmail: "Test Receiver Email",
|
|
||||||
pleaseEnterTestReceiverEmail: "Please enter test receiver email",
|
|
||||||
saveBeforeTest: "Save before testing",
|
|
||||||
sendFailHelpDoc: "Failed to send??? ",
|
|
||||||
emailConfigHelpDoc: "Email configuration help document",
|
|
||||||
tryOfficialEmailServer: "You can also try using the official email server ↗↗↗↗↗↗↗↗",
|
|
||||||
pluginManagement: "Plugin Management",
|
|
||||||
pluginBetaWarning: "Custom plugins are in BETA and may have breaking changes in future",
|
|
||||||
pleaseSelectRecord: "Please select records first",
|
|
||||||
permissionManagement: "Permission Management",
|
|
||||||
adda: "Add",
|
|
||||||
rootNode: "Root Node",
|
|
||||||
permissionName: "Permission Name",
|
|
||||||
enterPermissionName: "Please enter permission name",
|
|
||||||
permissionCode: "Permission Code",
|
|
||||||
enterPermissionCode: "Please enter permission code",
|
|
||||||
max100Chars: "Maximum 100 characters",
|
|
||||||
examplePermissionCode: "e.g.: sys:user:view",
|
|
||||||
sortOrder: "Sort Order",
|
|
||||||
sortRequired: "Sort order is required",
|
|
||||||
parentNode: "Parent Node",
|
|
||||||
roleManagement: "Role Management",
|
|
||||||
assignPermissions: "Assign Permissions",
|
|
||||||
roleName: "Role Name",
|
|
||||||
enterRoleName: "Please enter role name",
|
|
||||||
unlockLogin: "Unlock Login",
|
|
||||||
notice: "Notice",
|
|
||||||
confirmUnlock: "Are you sure you want to unlock this user's login?",
|
|
||||||
unlockSuccess: "Unlock successful",
|
|
||||||
enterUsername: "Please enter username",
|
|
||||||
modifyPasswordIfFilled: "Fill in to change the password",
|
|
||||||
emaila: "Email",
|
|
||||||
mobile: "Mobile",
|
|
||||||
avatar: "Avatar",
|
|
||||||
validTime: "Valid Time",
|
|
||||||
remark: "Remark",
|
|
||||||
roles: "Roles",
|
|
||||||
cnameTitle: "CNAME Service Configuration",
|
|
||||||
cnameDescription:
|
|
||||||
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
|
|
||||||
cnameLinkText: "CNAME principle and usage instructions",
|
|
||||||
confirmTitle: "Confirm",
|
|
||||||
confirmDeleteBatch: "Are you sure you want to delete these {count} records?",
|
|
||||||
selectRecordsFirst: "Please select records first",
|
|
||||||
cnameDomain: "CNAME Domain",
|
|
||||||
cnameDomainPlaceholder: "cname.handsfree.work",
|
|
||||||
cnameDomainHelper:
|
|
||||||
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
|
|
||||||
cnameDomainPattern: "Domain name cannot contain *",
|
|
||||||
dnsProvider: "DNS Provider",
|
|
||||||
dnsProviderAuthorization: "DNS Provider Authorization",
|
|
||||||
setDefault: "Set Default",
|
|
||||||
confirmSetDefault: "Are you sure to set as default?",
|
|
||||||
setAsDefault: "Set as Default",
|
|
||||||
disabledLabel: "Disabled",
|
|
||||||
confirmToggleStatus: "Are you sure to {action}?",
|
|
||||||
template: {
|
|
||||||
title: "Pipeline Template",
|
|
||||||
edit: "Pipeline Template Edit",
|
|
||||||
importCreate: "Pipeline Batch Create",
|
|
||||||
// intro: "可根据模版批量创建流水线",
|
|
||||||
intro: "Batch create pipeline based on template",
|
|
||||||
createTemplate: "Create Template",
|
|
||||||
useTemplate: "Use This Template",
|
|
||||||
batchCreate: "Batch Create Pipeline",
|
|
||||||
singleCreate: "Create Single Pipeline",
|
|
||||||
templateName: "Template Name",
|
|
||||||
enterTemplateName: "Please enter template name",
|
|
||||||
copyPipelineConfig: "Copy this pipeline configuration as template source",
|
|
||||||
pipeline: "Pipeline",
|
|
||||||
},
|
|
||||||
|
|
||||||
addonType: "Type",
|
|
||||||
addonName: "Name",
|
|
||||||
addonNameHelper: "Fill freely, helps to distinguish when multiple same type exist",
|
|
||||||
addonTypeSelect: "Select type",
|
|
||||||
dates: {
|
|
||||||
years: "{count} years",
|
|
||||||
months: "{count} months",
|
|
||||||
},
|
|
||||||
sys: {
|
|
||||||
setting: {
|
|
||||||
baseSetting: "Base Settings",
|
|
||||||
registerSetting: "Register Settings",
|
|
||||||
safeSetting: "Safe Settings",
|
|
||||||
paymentSetting: "Payment Settings",
|
|
||||||
captchaSetting: "Captcha Setting",
|
|
||||||
pipelineSetting: "Pipeline Settings",
|
|
||||||
oauthSetting: "Login Settings",
|
|
||||||
networkSetting: "Network Settings",
|
|
||||||
adminModeSetting: "Admin Mode Settings",
|
|
||||||
adminModeHelper: "enterprise mode : allow to create and manage pipelines, roles, users, etc.\n saas mode : only allow to create and manage pipelines",
|
|
||||||
enterpriseMode: "Enterprise Mode",
|
|
||||||
saasMode: "SaaS Mode",
|
|
||||||
|
|
||||||
showRunStrategy: "Show RunStrategy",
|
|
||||||
showRunStrategyHelper: "Allow modify the run strategy of the task",
|
|
||||||
|
|
||||||
captchaEnabled: "Enable Login Captcha",
|
|
||||||
captchaHelper: "Whether to enable captcha verification for login",
|
|
||||||
captchaType: "Captcha Setting",
|
|
||||||
captchaTest: "Captcha Test",
|
|
||||||
// 保存后再点击测试,请务必测试通过了,再开启登录验证码
|
|
||||||
captchaTestHelper: "Save and click test, please make sure the test is passed before enabling login captcha",
|
|
||||||
|
|
||||||
pipelineValidTimeEnabled: "Enable Pipeline Valid Time",
|
|
||||||
pipelineValidTimeEnabledHelper: "Whether to enable the valid time of the pipeline",
|
|
||||||
certDomainAddToMonitorEnabled: "Add Domain to Certificate Monitor",
|
|
||||||
certDomainAddToMonitorEnabledHelper: "Whether to add the domain to the certificate monitor",
|
|
||||||
fixedCertExpireDays: "Fixed Cert Expire Days",
|
|
||||||
fixedCertExpireDaysHelper: "Fixed cert expiration days, helpful for table list progress bar display",
|
|
||||||
fixedCertExpireDaysRecommend: "Recommend 90",
|
|
||||||
|
|
||||||
enableOauth: "Enable OAuth2 Login",
|
|
||||||
oauthEnabledHelper: "Whether to enable OAuth2 login",
|
|
||||||
oauthProviders: "OAuth2 Login Providers",
|
|
||||||
oauthType: "OAuth2 Login Type",
|
|
||||||
oauthConfig: "OAuth2 Login Config",
|
|
||||||
oauthProviderSelectorPlaceholder: "Not Configured",
|
|
||||||
oauthCallback: "Callback URL",
|
|
||||||
oauthCallbackHelper: "Copy this URL to the callback address of the OAuth2 login provider",
|
|
||||||
oauthCallbackCopy: "Copy Callback URL",
|
|
||||||
oauthAutoRegister: "Auto Register User",
|
|
||||||
oauthAutoRegisterCheckedText: "Auto Register",
|
|
||||||
oauthAutoRegisterUnCheckedText: "User Select",
|
|
||||||
oauthAutoRegisterHelper: "Whether to auto register user when login",
|
|
||||||
oauthAutoRedirect: "Auto Redirect to OAuth2 Login",
|
|
||||||
oauthAutoRedirectHelper: "Whether to auto redirect to OAuth2 login when login (using the first enabled OAuth2 login type)",
|
|
||||||
oauthOnly: "OAuth2 Login Only",
|
|
||||||
oauthOnlyHelper: "Whether to only allow OAuth2 login, disable password login",
|
|
||||||
enablePasskey: "Enable Passkey Login",
|
|
||||||
passkeyHostnameNotSame: "Passkey hostname must be the same as the main domain",
|
|
||||||
passkeyEnabledHelper:
|
|
||||||
"1、Site must enable https \n2、Domain name must not change, otherwise the registered passkey will be invalid \n3、Domain name must be the same as the main domain, otherwise the registered passkey will be invalid",
|
|
||||||
|
|
||||||
email: {
|
|
||||||
templates: "Email Templates",
|
|
||||||
templateType: "Template Type",
|
|
||||||
templateProvider: "Template Config",
|
|
||||||
|
|
||||||
templateSetting: "Email Template Setting",
|
|
||||||
serverSetting: "Email Server Setting",
|
|
||||||
sendTest: "Send Test",
|
|
||||||
|
|
||||||
templateProviderSelectorPlaceholder: "Not Configured",
|
|
||||||
},
|
|
||||||
notice: "System Notice",
|
|
||||||
noticeHelper: "System notice, will be displayed on the login page",
|
|
||||||
noticePlaceholder: "System notice",
|
|
||||||
|
|
||||||
reverseProxy: "Reverse Proxy List",
|
|
||||||
reverseProxyHelper: "Reverse proxy for ACME address, used when applying for certificate",
|
|
||||||
reverseProxyPlaceholder: "http://le.px.handfree.work",
|
|
||||||
reverseProxyEmpty: "No reverse proxy list configured",
|
|
||||||
environmentVars: "Environment Variables",
|
|
||||||
environmentVarsHelper: "configure the runtime environment variables, one per line, format: KEY=VALUE",
|
|
||||||
|
|
||||||
bindUrl: "Bind URL",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
modal: {
|
|
||||||
close: "Close",
|
|
||||||
viewCertificateTitle: "View Certificate",
|
|
||||||
},
|
|
||||||
domain: {
|
|
||||||
domainManager: "Domain Manager",
|
|
||||||
domainDescription: "used to auto apply for certificate", //管理域名的校验方式,用于申请证书时自动选择验证方式
|
|
||||||
domain: "Domain",
|
|
||||||
challengeType: "Challenge Type",
|
|
||||||
dnsProviderType: "DNS Provider Type",
|
|
||||||
dnsProviderAccess: "DNS Provider Access",
|
|
||||||
httpUploaderType: "HTTP Uploader Type",
|
|
||||||
httpUploaderAccess: "HTTP Uploader Access",
|
|
||||||
httpUploadRootDir: "HTTP Upload Root Dir",
|
|
||||||
disabled: "Disabled",
|
|
||||||
challengeSetting: "Challenge Setting",
|
|
||||||
gotoCnameTip: "Please go to CNAME Record Page",
|
|
||||||
fromType: "From Type",
|
|
||||||
expirationDate: "Expiration Date",
|
|
||||||
},
|
|
||||||
ent: {
|
|
||||||
projectName: "Project Name",
|
|
||||||
projectDescription: "Project Description",
|
|
||||||
projectDetailManager: "Project Detail",
|
|
||||||
projectDetailDescription: "Manage Project Members",
|
|
||||||
projectPermission: "Permission",
|
|
||||||
permission: {
|
|
||||||
read: "Read",
|
|
||||||
write: "Write",
|
|
||||||
admin: "Admin",
|
|
||||||
},
|
|
||||||
projectMemberStatus: "Member Status",
|
|
||||||
|
|
||||||
isSystem: "Is System Project",
|
|
||||||
isSystemHelper: "System-level projects allow running admin plugins",
|
|
||||||
},
|
|
||||||
project: {
|
|
||||||
noProjectJoined: "You haven't joined any projects yet",
|
|
||||||
applyToJoin: "Please apply to join a project to start using",
|
|
||||||
systemProjects: "System Project List",
|
|
||||||
createdAt: "Created At",
|
|
||||||
applyJoin: "Apply to Join",
|
|
||||||
noSystemProjects: "No system projects available",
|
|
||||||
fetchFailed: "Failed to fetch project list",
|
|
||||||
applySuccess: "Application successful, waiting for admin approval",
|
|
||||||
applyFailed: "Application failed, please try again later",
|
|
||||||
leave: "Leave Project",
|
|
||||||
leaveSuccess: "Leave project successful",
|
|
||||||
leaveFailed: "Leave project failed, please try again later",
|
|
||||||
applyJoinConfirm: "Are you sure you want to apply to join this project?",
|
|
||||||
leaveConfirm: "Are you sure you want to leave this project?",
|
|
||||||
viewDetail: "View Detail",
|
|
||||||
projectManage: "Project Manage",
|
|
||||||
},
|
|
||||||
addonSelector: {
|
|
||||||
select: "Select",
|
|
||||||
placeholder: "select please",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export default {
|
||||||
|
authorizationManagement: "Authorization Management",
|
||||||
|
manageThirdPartyAuth: "Manage third-party system authorization information",
|
||||||
|
nameHelper: "Fill in as you like, useful to distinguish when multiple authorizations of the same type exist",
|
||||||
|
level: "Level",
|
||||||
|
system: "System",
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export default {
|
||||||
|
addonType: "Type",
|
||||||
|
addonName: "Name",
|
||||||
|
addonNameHelper: "Fill freely, helps to distinguish when multiple same type exist",
|
||||||
|
addonTypeSelect: "Select type",
|
||||||
|
addonSelector: {
|
||||||
|
select: "Select",
|
||||||
|
placeholder: "select please",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
export default {
|
||||||
|
domain: {
|
||||||
|
domainManager: "Domain Manager",
|
||||||
|
domainDescription: "used to auto apply for certificate", //管理域名的校验方式,用于申请证书时自动选择验证方式
|
||||||
|
domain: "Domain",
|
||||||
|
challengeType: "Challenge Type",
|
||||||
|
dnsProviderType: "DNS Provider Type",
|
||||||
|
dnsProviderAccess: "DNS Provider Access",
|
||||||
|
httpUploaderType: "HTTP Uploader Type",
|
||||||
|
httpUploaderAccess: "HTTP Uploader Access",
|
||||||
|
httpUploadRootDir: "HTTP Upload Root Dir",
|
||||||
|
disabled: "Disabled",
|
||||||
|
challengeSetting: "Challenge Setting",
|
||||||
|
gotoCnameTip: "Please go to CNAME Record Page",
|
||||||
|
fromType: "From Type",
|
||||||
|
expirationDate: "Expiration Date",
|
||||||
|
cnameManagedInCnamePage: "For CNAME mode, please manage records on the CNAME Records page.",
|
||||||
|
subdomainConfirmTitle: "Subdomain Confirmation",
|
||||||
|
subdomainConfirmContent: "{domain} appears to be a subdomain. Only delegated subdomains and free second-level subdomains need to be maintained here. Otherwise certificate application may fail. Continue?",
|
||||||
|
importFromProvider: "Import from Domain Provider",
|
||||||
|
syncExpirationDate: "Sync Domain Expiration Time",
|
||||||
|
syncTaskSubmitted: "Sync task submitted",
|
||||||
|
expirationMonitorSetting: "Domain Expiration Monitor Settings",
|
||||||
|
subdomainDnsHelper: "Note: In DNS validation mode, subdomains do not need to be maintained here, otherwise certificate application may be affected (except delegated subdomains or free second-level domains).",
|
||||||
|
path: "Path",
|
||||||
|
addImportTask: "Add Import Task",
|
||||||
|
refresh: "Refresh",
|
||||||
|
progress: "Progress",
|
||||||
|
operation: "Operation",
|
||||||
|
total: "Total",
|
||||||
|
skipped: "Skipped",
|
||||||
|
failed: "Failed",
|
||||||
|
notExecuted: "Not executed",
|
||||||
|
execute: "Run",
|
||||||
|
delete: "Delete",
|
||||||
|
confirmDelete: "Confirm deletion?",
|
||||||
|
domainProvider: "Domain Provider",
|
||||||
|
domainProviderAccessType: "Domain Provider Access Type",
|
||||||
|
domainProviderAccess: "Domain Provider Access",
|
||||||
|
},
|
||||||
|
verifyPlan: {
|
||||||
|
expandTip: "Click to enlarge",
|
||||||
|
mainDomain: "Main Domain",
|
||||||
|
challengeType: "Challenge Method",
|
||||||
|
challengePlan: "Challenge Plan",
|
||||||
|
dnsType: "DNS Type",
|
||||||
|
dnsProvider: "DNS Provider",
|
||||||
|
dnsAccess: "DNS Access",
|
||||||
|
pleaseSelect: "Please select",
|
||||||
|
httpHelper: "The CA will request https://yourdomain/.well-known/acme-challenge/xxxxxx to verify domain ownership.",
|
||||||
|
dnsChallenge: "DNS Challenge",
|
||||||
|
cnameChallenge: "CNAME Challenge",
|
||||||
|
cnameProxyChallenge: "CNAME Proxy Challenge",
|
||||||
|
httpChallenge: "HTTP Challenge",
|
||||||
|
domainTitle: "Domain: {domain}",
|
||||||
|
resetStatusTooltip: "Reset validation status and validate again",
|
||||||
|
clickToValidate: "Validate",
|
||||||
|
keepCnameTitle: "Required for future automatic certificate applications",
|
||||||
|
keepCname: "Do not delete CNAME",
|
||||||
|
resetStatus: "Reset Status",
|
||||||
|
confirmResetStatus: "Are you sure you want to reset the validation status?",
|
||||||
|
hostRecord: "Host Record",
|
||||||
|
recordType: "Record Type",
|
||||||
|
setCnameRecord: "Set the CNAME record. Do not delete it after validation succeeds.",
|
||||||
|
operation: "Operation",
|
||||||
|
websiteDomain: "Website Domain",
|
||||||
|
uploadMethod: "Upload Method",
|
||||||
|
uploadAccess: "Upload Access",
|
||||||
|
websiteRootPath: "Website Root Path",
|
||||||
|
websiteRootPlaceholder: "Website root path, e.g. /www/wwwroot",
|
||||||
|
status: {
|
||||||
|
pendingCname: "Pending CNAME Setup",
|
||||||
|
validating: "Validating",
|
||||||
|
valid: "Validation Successful",
|
||||||
|
failed: "Validation Failed",
|
||||||
|
timeout: "Validation Timed Out",
|
||||||
|
},
|
||||||
|
cnameTip: {
|
||||||
|
intro: "Try a few more times. If validation still fails, troubleshoot with these steps:",
|
||||||
|
step1: "1. The DNS record should be added under {domain}.",
|
||||||
|
step2: "2. Add a CNAME record, not a TXT record.",
|
||||||
|
step3: "3. Check whether the record value is: {value}",
|
||||||
|
step4: "4. While validating, run the command below to check whether CNAME and TXT resolution are correct.",
|
||||||
|
or: "or",
|
||||||
|
step5: "5. If all checks pass, DNS propagation may be slow. Some providers can take several hours.",
|
||||||
|
},
|
||||||
|
errors: {
|
||||||
|
cnameNotValid: "The CNAME for domain {domain} has not been validated. Set the CNAME record first, then click Validate.",
|
||||||
|
wildcardNotSupportHttp: "Domain {domain} is a wildcard domain and does not support HTTP validation.",
|
||||||
|
uploadMethodRequired: "The upload method for domain {domain} is required.",
|
||||||
|
uploadAccessRequired: "The upload access for domain {domain} is required.",
|
||||||
|
websiteRootRequired: "The website root path for domain {domain} is required.",
|
||||||
|
dnsProviderRequired: "In DNS mode, the DNS type and access information for domain {domain} are required.",
|
||||||
|
},
|
||||||
|
uploader: {
|
||||||
|
aliyunOss: "Alibaba Cloud OSS",
|
||||||
|
tencentCos: "Tencent Cloud COS",
|
||||||
|
qiniuOss: "Qiniu OSS",
|
||||||
|
sshDeprecated: "SSH (deprecated, use SFTP instead)",
|
||||||
|
},
|
||||||
|
domainFrom: {
|
||||||
|
manual: "Manual",
|
||||||
|
auto: "Automatic",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
export default {
|
||||||
|
pending_cname_setup: "Pending CNAME setup",
|
||||||
|
validating: "Validating",
|
||||||
|
validation_successful: "Validation successful",
|
||||||
|
validation_failed: "Validation failed",
|
||||||
|
validation_timed_out: "Validation timed out",
|
||||||
|
proxied_domain: "Proxied Domain",
|
||||||
|
host_record: "Host Record",
|
||||||
|
please_set_cname: "Please set CNAME",
|
||||||
|
cname_service: "CNAME Service",
|
||||||
|
default_public_cname: "Default public CNAME service, you can also ",
|
||||||
|
customize_cname: "Customize CNAME Service",
|
||||||
|
public_cname: "Public CNAME",
|
||||||
|
custom_cname: "Custom CNAME",
|
||||||
|
validate: "Validate",
|
||||||
|
validation_started: "Validation started, please wait patiently",
|
||||||
|
click_to_validate: "Click to Validate",
|
||||||
|
cname_feature_guide: "CNAME feature principle and usage guide",
|
||||||
|
mainDomain: "Main Domain",
|
||||||
|
cnameRecord: "CNAME Record Management",
|
||||||
|
importRecords: "Import CNAME Records",
|
||||||
|
batchImport: "Batch Import",
|
||||||
|
exportRecordsTip: "After exporting CNAME records, you can batch import CNAME DNS records to the domain registrar.",
|
||||||
|
batchExport: "Batch Export",
|
||||||
|
resetStatus: "Reset Status",
|
||||||
|
confirmResetStatus: "Are you sure you want to reset the validation status?",
|
||||||
|
resetStatusTooltip: "Reset validation status and validate again",
|
||||||
|
cname: {
|
||||||
|
importRecords: "Import CNAME Records",
|
||||||
|
batchImport: "Batch Import",
|
||||||
|
exportRecordsTip: "After exporting CNAME records, you can batch import CNAME DNS records to the domain registrar.",
|
||||||
|
batchExport: "Batch Export",
|
||||||
|
resetStatus: "Reset Status",
|
||||||
|
confirmResetStatus: "Are you sure you want to reset the validation status?",
|
||||||
|
resetStatusTooltip: "Reset validation status and validate again",
|
||||||
|
domainList: "Domain List",
|
||||||
|
domainListHelper: "One domain per line for batch import\nRemove *. from wildcard domains\nExisting records will be skipped automatically",
|
||||||
|
cnameService: "CNAME Service",
|
||||||
|
importTaskSubmitted: "Import task submitted",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
export default {
|
||||||
|
usera: "User",
|
||||||
|
disabledSuccess: "Disabled successfully",
|
||||||
|
app: {
|
||||||
|
crud: {
|
||||||
|
i18n: {
|
||||||
|
name: "name",
|
||||||
|
city: "city",
|
||||||
|
status: "status",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fs: {
|
||||||
|
rowHandle: {
|
||||||
|
title: "Operation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
close: "Close",
|
||||||
|
confirm: "Confirm",
|
||||||
|
save: "Save",
|
||||||
|
add: "Generate New Key",
|
||||||
|
edit: "Edit",
|
||||||
|
deleteSuccess: "Delete successful",
|
||||||
|
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",
|
||||||
|
name: "Name",
|
||||||
|
pleaseEnterName: "Please enter the name",
|
||||||
|
type: "Type",
|
||||||
|
status: "Status",
|
||||||
|
description: "Description",
|
||||||
|
createTime: "Creation Time",
|
||||||
|
updateTime: "Update Time",
|
||||||
|
basicInfo: "Basic Information",
|
||||||
|
disabled: "Disabled",
|
||||||
|
enabled: "Enabled",
|
||||||
|
default: "Default",
|
||||||
|
success: "Success",
|
||||||
|
test: "Test",
|
||||||
|
testButton: "Test",
|
||||||
|
operationSuccess: "Operation successful",
|
||||||
|
batch_delete: "Batch Delete",
|
||||||
|
confirm_delete_count: "Are you sure to delete these {count} records in batch?",
|
||||||
|
delete_successful: "Delete successful",
|
||||||
|
please_select_records: "Please select records first",
|
||||||
|
groupName: "Group Name",
|
||||||
|
enterGroupName: "Please enter group name",
|
||||||
|
batchDeleteConfirm: "Are you sure to batch delete these {count} records?",
|
||||||
|
selectRecordFirst: "Please select records first",
|
||||||
|
confirmTitle: "Confirm",
|
||||||
|
confirmDeleteBatch: "Are you sure you want to delete these {count} records?",
|
||||||
|
selectRecordsFirst: "Please select records first",
|
||||||
|
setDefault: "Set Default",
|
||||||
|
confirmSetDefault: "Are you sure to set as default?",
|
||||||
|
setAsDefault: "Set as Default",
|
||||||
|
disabledLabel: "Disabled",
|
||||||
|
confirmToggleStatus: "Are you sure to {action}?",
|
||||||
|
batchDelete: "Batch Delete",
|
||||||
|
sourcee: "Source",
|
||||||
|
clickToToggle: "Click to toggle enable/disable",
|
||||||
|
nickName: "Nickname",
|
||||||
|
avatar: "Avatar",
|
||||||
|
expires: " expires",
|
||||||
|
days: " days",
|
||||||
|
isDefault: "Is Default",
|
||||||
|
selectIsDefault: "Please select if default",
|
||||||
|
isDisabled: "Is Disabled",
|
||||||
|
fields: {
|
||||||
|
userId: "User ID",
|
||||||
|
pipelineName: "Pipeline Name",
|
||||||
|
keyword: "Keyword",
|
||||||
|
required: "This field is required",
|
||||||
|
pipelineContent: "Pipeline Content",
|
||||||
|
scheduledTaskCount: "Scheduled Task Count",
|
||||||
|
deployTaskCount: "Deployment Task Count",
|
||||||
|
remainingValidity: "Remaining Validity",
|
||||||
|
effectiveTime: "Effective time",
|
||||||
|
expiryTime: "Expiry Time",
|
||||||
|
status: "Status",
|
||||||
|
lastRun: "Last Run",
|
||||||
|
enabled: "Enabled",
|
||||||
|
enabledLabel: "Enabled",
|
||||||
|
disabledLabel: "Disabled",
|
||||||
|
group: "Group",
|
||||||
|
type: "Type",
|
||||||
|
order: "Order Number",
|
||||||
|
keepHistoryCount: "History Record Retention Count",
|
||||||
|
keepHistoryHelper: "Number of history records to keep; excess will be deleted",
|
||||||
|
createTime: "Creation Time",
|
||||||
|
updateTime: "Update Time",
|
||||||
|
triggerType: "Trigger Type",
|
||||||
|
pipelineId: "Pipeline Id",
|
||||||
|
nextRunTime: "Next Run Time",
|
||||||
|
projectName: "Project",
|
||||||
|
adminId: "Admin",
|
||||||
|
},
|
||||||
|
enterName: "Please enter a name",
|
||||||
|
prompt: "Prompt",
|
||||||
|
domainCount: "Domain Count",
|
||||||
|
import: "Import",
|
||||||
|
export: "Export",
|
||||||
|
disable: "disable",
|
||||||
|
enable: "enable",
|
||||||
|
username: "Username",
|
||||||
|
password: "Password",
|
||||||
|
expired: "expired",
|
||||||
|
helpDocLink: "Help Docs",
|
||||||
|
suite: "Suite",
|
||||||
|
helpDoc: "Help Document",
|
||||||
|
pluginCommon: {
|
||||||
|
test: "Test",
|
||||||
|
errorWithMessage: "Error: {message}",
|
||||||
|
testRequestSuccess: "Test request succeeded",
|
||||||
|
responseSuffix: ", response: {response}",
|
||||||
|
searchKeyword: "Search keyword",
|
||||||
|
search: "Search",
|
||||||
|
refreshOptions: "Refresh options",
|
||||||
|
getOptionsError: "Failed to get options: {message}",
|
||||||
|
getDataSuccessSelect: "Data loaded. Please select from the dropdown.",
|
||||||
|
getDataSuccessEmpty: "Data loaded. No data found.",
|
||||||
|
domainSearchPlaceholder: "Search domains here (from Settings -> Domain Management), or type directly in the input above",
|
||||||
|
importDomain: "Import Domains",
|
||||||
|
manageDomain: "Manage Domains",
|
||||||
|
refreshMyDomains: "Refresh my domain list",
|
||||||
|
upload: "Upload",
|
||||||
|
uploadCert: "Upload Certificate",
|
||||||
|
certName: "Certificate Name",
|
||||||
|
certNameHelper: "Display name after upload",
|
||||||
|
uploadSuccess: "Upload successful",
|
||||||
|
selectCertFirst: "Please select a domain certificate first",
|
||||||
|
targetStepNotFound: "Target step not found. Please select a domain certificate first.",
|
||||||
|
},
|
||||||
|
captcha: {
|
||||||
|
inputImageCode: "Please enter the image captcha",
|
||||||
|
refresh: "Refresh captcha",
|
||||||
|
clickToVerify: "Click to verify",
|
||||||
|
verifySuccess: "Verification successful",
|
||||||
|
verifyFailed: "Verification failed. Please try again.",
|
||||||
|
loadFailed: "Failed to load captcha",
|
||||||
|
notLoaded: "Captcha is not loaded yet",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export default {
|
||||||
|
cron: {
|
||||||
|
clearTip: "Clear Selection",
|
||||||
|
nextTrigger: "Next Trigger Time",
|
||||||
|
tip: "Please set a valid cron expression first",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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",
|
||||||
|
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",
|
||||||
|
supportedTasks: "Overview of Supported Deployment Tasks",
|
||||||
|
changeLog: "Change Log",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
pipelineExecutionRecords: "Pipeline Execution Records",
|
||||||
|
confirmBatchDeleteContent: "Are you sure you want to batch delete these {count} records?",
|
||||||
|
pleaseSelectRecords: "Please select records first",
|
||||||
|
triggerTypes: {
|
||||||
|
manual: "Manual Execution",
|
||||||
|
timer: "Scheduled Execution",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
export default {
|
||||||
|
accountInfo: "Account Information",
|
||||||
|
securitySettings: "Security & Settings",
|
||||||
|
confirmDisable2FA: "Are you sure you want to disable two-factor authentication login?",
|
||||||
|
twoFactorAuth: "2FA Two-Factor Authentication Login",
|
||||||
|
rebind: "Rebind",
|
||||||
|
twoFactorAuthHelper: "Enable or disable two-factor authentication login",
|
||||||
|
bindDevice: "Bind Device",
|
||||||
|
step1: "1. Install any authenticator app, for example:",
|
||||||
|
tooltipGoogleServiceError: "If you get a Google service not found error, you can install KK Google Assistant",
|
||||||
|
step2: "2. Scan the QR code to add the account",
|
||||||
|
step3: "3. Enter the verification code",
|
||||||
|
inputVerifyCode: "Please enter the verification code",
|
||||||
|
myInfo: "My Information",
|
||||||
|
user: {
|
||||||
|
greeting: "Hello",
|
||||||
|
profile: "Account Info",
|
||||||
|
logout: "Logout",
|
||||||
|
setting: {
|
||||||
|
grantSetting: "Grant Settings",
|
||||||
|
saveSuccess: "Save Success",
|
||||||
|
allowAdminViewCerts: "Allow Admin view and download Certs",
|
||||||
|
allowAdminViewCertsHelper: "Allow admin view and download all certificates",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
export default {
|
||||||
|
certificateRepo: {
|
||||||
|
title: "Certificate Repository",
|
||||||
|
sub: "Certificates generated from pipeline",
|
||||||
|
},
|
||||||
|
actionImportBatch: "Batch Import",
|
||||||
|
actionSyncIp: "Sync IP",
|
||||||
|
modalTitleSyncIp: "Sync IP",
|
||||||
|
modalContentSyncIp: "Are you sure to sync IP?",
|
||||||
|
notificationSyncComplete: "Sync Complete",
|
||||||
|
actionCheckAll: "Check All",
|
||||||
|
modalTitleConfirm: "Confirm",
|
||||||
|
modalContentCheckAll: "Confirm to trigger checking all IP site's certificates?",
|
||||||
|
notificationCheckSubmitted: "Check task submitted",
|
||||||
|
notificationCheckDescription: "Please refresh later to see results",
|
||||||
|
tooltipCheckNow: "Check Now",
|
||||||
|
notificationCheckSubmittedPleaseRefresh: "Check task submitted, please refresh later",
|
||||||
|
columnId: "ID",
|
||||||
|
columnIp: "IP",
|
||||||
|
helperIpCname: "Supports entering CNAME domain name or source site domain name",
|
||||||
|
ruleIpRequired: "Please enter IP",
|
||||||
|
columnCertDomains: "Certificate Domains",
|
||||||
|
columnCertProvider: "Issuer",
|
||||||
|
columnCertStatus: "Certificate Status",
|
||||||
|
statusNormal: "Normal",
|
||||||
|
statusExpired: "Expired",
|
||||||
|
columnCertExpiresTime: "Certificate Expiration Time",
|
||||||
|
columnCheckStatus: "Check Status",
|
||||||
|
columnLastCheckTime: "Last Check Time",
|
||||||
|
columnSource: "Source",
|
||||||
|
sourceSync: "Sync",
|
||||||
|
sourceManual: "Manual",
|
||||||
|
sourceImport: "Import",
|
||||||
|
columnDisabled: "Enabled/Disabled",
|
||||||
|
columnRemark: "Remark",
|
||||||
|
certificateNotGenerated: "Certificate not yet generated, please run the pipeline first",
|
||||||
|
viewCertificateTitle: "View Certificate",
|
||||||
|
viewCert: {
|
||||||
|
title: "View Certificate",
|
||||||
|
},
|
||||||
|
download: {
|
||||||
|
title: "Download Certificate",
|
||||||
|
},
|
||||||
|
checkStatus: {
|
||||||
|
success: "Success",
|
||||||
|
checking: "Checking",
|
||||||
|
error: "Error",
|
||||||
|
},
|
||||||
|
uploadCustomCert: "Upload Custom Certificate",
|
||||||
|
sourcePipeline: "Cert Apply Pipeline",
|
||||||
|
sourceManualUpload: "Hosted Pipeline",
|
||||||
|
domains: "Domains",
|
||||||
|
enterDomain: "Please enter domain",
|
||||||
|
validDays: "Valid Days",
|
||||||
|
effectiveTime: "Effective Time",
|
||||||
|
expireTime: "Expiration Time",
|
||||||
|
certIssuer: "Certificate Issuer",
|
||||||
|
applyTime: "Application Time",
|
||||||
|
relatedPipeline: "Related Pipeline",
|
||||||
|
statusSuccess: "Success",
|
||||||
|
statusChecking: "Checking",
|
||||||
|
statusError: "Error",
|
||||||
|
modal: {
|
||||||
|
close: "Close",
|
||||||
|
viewCertificateTitle: "View Certificate",
|
||||||
|
},
|
||||||
|
domainList: {
|
||||||
|
title: "Domain List",
|
||||||
|
helper: "Format: domain:port:name:remark, one per line. Port and name are optional.\nExamples:\nwww.baidu.com:443:Baidu:remarkText\nwww.taobao.com::Taobao\nwww.google.com",
|
||||||
|
required: "Please enter domains to import",
|
||||||
|
placeholder: "www.baidu.com:443:Baidu\nwww.taobao.com::Taobao\nwww.google.com\n",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
export default {
|
||||||
|
framework: {
|
||||||
|
title: "Framework",
|
||||||
|
home: "Home",
|
||||||
|
},
|
||||||
|
title: "Certificate Automation",
|
||||||
|
pipeline: "Pipeline",
|
||||||
|
pipelineEdit: "Edit Pipeline",
|
||||||
|
history: "Execution History",
|
||||||
|
certStore: "Certificate Repository",
|
||||||
|
siteMonitor: "Site Certificate Monitor",
|
||||||
|
settings: "Settings",
|
||||||
|
accessManager: "Access Management",
|
||||||
|
subDomain: "Subdomain Delegation Settings",
|
||||||
|
pipelineGroup: "Pipeline Group Management",
|
||||||
|
openKey: "Open API Key",
|
||||||
|
notification: "Notification Settings",
|
||||||
|
siteMonitorSetting: "Site Monitor Settings",
|
||||||
|
userSecurity: "Security Settings",
|
||||||
|
userProfile: "Account Info",
|
||||||
|
userGrant: "Grant Delegation",
|
||||||
|
mySuite: "My Suite",
|
||||||
|
suiteBuy: "Suite Purchase",
|
||||||
|
myTrade: "My Orders",
|
||||||
|
paymentReturn: "Payment Return",
|
||||||
|
source: "Source Code",
|
||||||
|
github: "GitHub",
|
||||||
|
gitee: "Gitee",
|
||||||
|
sysResources: {
|
||||||
|
sysRoot: "System Management",
|
||||||
|
sysConsole: "Console",
|
||||||
|
sysSettings: "System Settings",
|
||||||
|
cnameSetting: "CNAME Service Settings",
|
||||||
|
emailSetting: "Email Server Settings",
|
||||||
|
siteSetting: "Site Personalization",
|
||||||
|
headerMenus: "Top Menu Settings",
|
||||||
|
sysAccess: "System-level Authorization",
|
||||||
|
sysPlugin: "Plugin Management",
|
||||||
|
sysPluginEdit: "Edit Plugin",
|
||||||
|
sysPluginConfig: "Certificate Plugin Configuration",
|
||||||
|
accountBind: "Account Binding",
|
||||||
|
permissionManager: "Permission Management",
|
||||||
|
roleManager: "Role Management",
|
||||||
|
userManager: "User Management",
|
||||||
|
suiteManager: "Suite Management",
|
||||||
|
suiteSetting: "Suite Settings",
|
||||||
|
orderManager: "Order Management",
|
||||||
|
userSuites: "User Suites",
|
||||||
|
netTest: "Network Test",
|
||||||
|
|
||||||
|
enterpriseSetting: "Enterprise Settings",
|
||||||
|
projectManager: "Project Management",
|
||||||
|
projectUserManager: "Project User Management",
|
||||||
|
myProjectManager: "My Projects",
|
||||||
|
myProjectDetail: "Project Detail",
|
||||||
|
projectJoin: "Join Project",
|
||||||
|
currentProject: "Current Project",
|
||||||
|
projectMemberManager: "Project Member",
|
||||||
|
domainMonitorSetting: "Domain Monitor Settings",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
export default {
|
||||||
|
email: {
|
||||||
|
title: "Recipient Email",
|
||||||
|
helper: "Enter your recipient email addresses. Multiple addresses are supported.",
|
||||||
|
required: "This field is required",
|
||||||
|
},
|
||||||
|
notificationDefault: "Use Default Notification",
|
||||||
|
notificationType: "Notification Type",
|
||||||
|
selectNotificationType: "Please select a notification type",
|
||||||
|
confirmSetDefaultNotification: "Are you sure to set as default notification?",
|
||||||
|
edit_notification: "Edit Notification",
|
||||||
|
other_notification_method: "Other Notification Method",
|
||||||
|
notificationName: "Notification Name",
|
||||||
|
helperNotificationName: "Fill freely, helps to distinguish when multiple notifications of the same type exist",
|
||||||
|
};
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export default {
|
||||||
|
scope: "Scope",
|
||||||
|
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",
|
||||||
|
gen: {
|
||||||
|
text: "API Test",
|
||||||
|
title: "x-certd-token",
|
||||||
|
okText: "Confirm",
|
||||||
|
contentPart1: "Test the x-certd-token below, you can use it within 3 minutes to test ",
|
||||||
|
openApi: "Open API",
|
||||||
|
contentPart2: " request testing",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
export default {
|
||||||
|
hasExpired: "Expired",
|
||||||
|
subdomainHosting: "Subdomain Hosting",
|
||||||
|
subdomainHostingHint: "When your domain has subdomain hosting set, you need to create records here, otherwise certificate application will fail",
|
||||||
|
subdomainHosted: "Hosted Subdomain",
|
||||||
|
subdomainHelpText: "If you don't understand what subdomain hosting is,Do not set it randomly, as it may result in the inability to apply for the certificate. please refer to the documentation ",
|
||||||
|
subdomainHelpSupportStart: "Supports * wildcard, indicating that all subdomains of the domain are hosted (free subdomains)",
|
||||||
|
subdomainManagement: "Subdomain Management",
|
||||||
|
pipelinePage: {
|
||||||
|
addMore: "Add More Pipelines",
|
||||||
|
aliyunSubscriptionPipeline: "Aliyun Subscription Pipeline",
|
||||||
|
legoCertPipeline: "Lego Certificate Pipeline",
|
||||||
|
customPipeline: "Custom Pipeline",
|
||||||
|
batchAddPipeline: "Add Pipeline Use Template",
|
||||||
|
myPipelinesDesc: "Pipeline Mode: Apply -> Deploy -> Schedule",
|
||||||
|
},
|
||||||
|
customPipeline: "Custom Pipeline",
|
||||||
|
createCertdPipeline: "Create Certificate Pipeline",
|
||||||
|
commercialCertHosting: "Commercial Certificate Hosting",
|
||||||
|
tooltip: {
|
||||||
|
manualUploadOwnCert: "Manually upload your own certificate for automatic deployment",
|
||||||
|
noAutoApplyCommercialCert: "Does not automatically apply for commercial certificates",
|
||||||
|
manualUploadOnUpdate: "Must manually upload once when the certificate is updated",
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
confirmDeleteTitle: "Are you sure you want to delete?",
|
||||||
|
confirmDeleteMessage: "This will delete all data related to the pipeline, including execution history, certificate files, and certificate repository records.",
|
||||||
|
},
|
||||||
|
play: {
|
||||||
|
runPipeline: "Run Pipeline",
|
||||||
|
confirm: "Confirm",
|
||||||
|
confirmTrigger: "Are you sure you want to trigger the run?",
|
||||||
|
pipelineStarted: "Pipeline has started running",
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
editPipeline: "Edit Pipeline",
|
||||||
|
editConfigGroup: "Modify Configuration/Group",
|
||||||
|
viewCertificate: "View Certificate",
|
||||||
|
downloadCertificate: "Download Certificate",
|
||||||
|
},
|
||||||
|
pi: {
|
||||||
|
validTime: "Piepline Valid Time",
|
||||||
|
validTimeHelper: "Not filled in means permanent validity",
|
||||||
|
},
|
||||||
|
types: {
|
||||||
|
certApply: "Cert Apply",
|
||||||
|
certUpload: "Cert Upload",
|
||||||
|
custom: "Custom",
|
||||||
|
template: "Template",
|
||||||
|
},
|
||||||
|
myPipelines: "My Pipelines",
|
||||||
|
selectedCount: "Selected {count} items",
|
||||||
|
batchForceRerun: "Force Rerun",
|
||||||
|
batchRerun: "Rerun",
|
||||||
|
applyCertificate: "Apply for Certificate",
|
||||||
|
cronForm: {
|
||||||
|
title: "Scheduled Script",
|
||||||
|
helper: "Click the button above to select the time for daily execution.\nIt is recommended to run once a day. Tasks will be skipped if the certificate is not expiring.",
|
||||||
|
required: "This field is required",
|
||||||
|
},
|
||||||
|
plugin: {
|
||||||
|
selectTitle: "Certificate Apply Plugin",
|
||||||
|
jsAcme: "JS-ACME: Easy to use, powerful features [Recommended]",
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
pipelineForm: {
|
||||||
|
createTitle: "Create Certificate Pipeline",
|
||||||
|
moreParams: "More Parameters",
|
||||||
|
triggerCronTitle: "Scheduled Trigger",
|
||||||
|
triggerCronHelper:
|
||||||
|
"Click the button above to choose a daily execution time.\nIt is recommended to trigger once per day. The task will be skipped if the certificate has not expired and will not be executed repeatedly.",
|
||||||
|
notificationTitle: "Failure Notification",
|
||||||
|
notificationWhen: "Notification Timing",
|
||||||
|
notificationHelper: "Get real-time alerts when the task fails",
|
||||||
|
groupIdTitle: "Pipeline Group",
|
||||||
|
|
||||||
|
addToMonitorEnabled: "Add to Cert Monitor",
|
||||||
|
addToMonitorDomains: "Add to Monitor Domains",
|
||||||
|
|
||||||
|
webhookEnabled: "Webhook Enabled",
|
||||||
|
webhookEnabledHelper: "Support webhook trigger pipeline, please check webhook address in trigger source",
|
||||||
|
},
|
||||||
|
template: {
|
||||||
|
title: "Pipeline Template",
|
||||||
|
edit: "Pipeline Template Edit",
|
||||||
|
importCreate: "Pipeline Batch Create",
|
||||||
|
// intro: "可根据模版批量创建流水线",
|
||||||
|
intro: "Batch create pipeline based on template",
|
||||||
|
createTemplate: "Create Template",
|
||||||
|
useTemplate: "Use This Template",
|
||||||
|
batchCreate: "Batch Create Pipeline",
|
||||||
|
singleCreate: "Create Single Pipeline",
|
||||||
|
templateName: "Template Name",
|
||||||
|
enterTemplateName: "Please enter template name",
|
||||||
|
copyPipelineConfig: "Copy this pipeline configuration as template source",
|
||||||
|
pipeline: "Pipeline",
|
||||||
|
},
|
||||||
|
editSchedule: "Edit Schedule",
|
||||||
|
webhook: "Webhook",
|
||||||
|
timerTrigger: "Timer Trigger",
|
||||||
|
schedule: "Schedule",
|
||||||
|
selectCron: "Please select a schedule Cron",
|
||||||
|
batchEditSchedule: "Batch Edit Schedule",
|
||||||
|
editTrigger: "Edit Trigger",
|
||||||
|
triggerName: "Trigger Name",
|
||||||
|
confirmDeleteTrigger: "Are you sure you want to delete this trigger?",
|
||||||
|
trigger_time: "Trigger Time",
|
||||||
|
start_time: "At Start",
|
||||||
|
success_time: "On Success",
|
||||||
|
fail_to_success_time: "Fail to Success",
|
||||||
|
fail_time: "On Failure",
|
||||||
|
helper_suggest_fail_only: "It is recommended to select only 'On Failure' and 'Fail to Success'",
|
||||||
|
notification_config: "Notification Configuration",
|
||||||
|
please_select_notification: "Please select a notification method",
|
||||||
|
please_select_type: "Please select type",
|
||||||
|
please_select_trigger_time: "Please select notification trigger time",
|
||||||
|
please_select_notification_config: "Please select notification configuration",
|
||||||
|
confirm_delete_trigger: "Are you sure you want to delete this trigger?",
|
||||||
|
dates: {
|
||||||
|
years: "{count} years",
|
||||||
|
months: "{count} months",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
export default {
|
||||||
|
ent: {
|
||||||
|
projectName: "Project Name",
|
||||||
|
projectDescription: "Project Description",
|
||||||
|
projectDetailManager: "Project Detail",
|
||||||
|
projectDetailDescription: "Manage Project Members",
|
||||||
|
projectPermission: "Permission",
|
||||||
|
permission: {
|
||||||
|
read: "Read",
|
||||||
|
write: "Write",
|
||||||
|
admin: "Admin",
|
||||||
|
},
|
||||||
|
projectMemberStatus: "Member Status",
|
||||||
|
|
||||||
|
isSystem: "Is System Project",
|
||||||
|
isSystemHelper: "System-level projects allow running admin plugins",
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
noProjectJoined: "You haven't joined any projects yet",
|
||||||
|
applyToJoin: "Please apply to join a project to start using",
|
||||||
|
projectList: "Project List",
|
||||||
|
systemProjects: "System Project List",
|
||||||
|
createdAt: "Created At",
|
||||||
|
applyJoin: "Apply to Join",
|
||||||
|
noSystemProjects: "No system projects available",
|
||||||
|
fetchFailed: "Failed to fetch project list",
|
||||||
|
applySuccess: "Application successful, waiting for admin approval",
|
||||||
|
applyFailed: "Application failed, please try again later",
|
||||||
|
leave: "Leave Project",
|
||||||
|
leaveSuccess: "Leave project successful",
|
||||||
|
leaveFailed: "Leave project failed, please try again later",
|
||||||
|
applyJoinConfirm: "Are you sure you want to apply to join this project?",
|
||||||
|
leaveConfirm: "Are you sure you want to leave this project?",
|
||||||
|
viewDetail: "View Detail",
|
||||||
|
projectManage: "Project Manage",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
export default {
|
||||||
|
order: {
|
||||||
|
confirmTitle: "Order Confirmation",
|
||||||
|
package: "Package",
|
||||||
|
description: "Description",
|
||||||
|
specifications: "Specifications",
|
||||||
|
pipeline: "Pipeline",
|
||||||
|
domain: "Domain",
|
||||||
|
deployTimes: "Deployments",
|
||||||
|
monitorCount: "DomainMonitors",
|
||||||
|
duration: "Duration",
|
||||||
|
price: "Price",
|
||||||
|
paymentMethod: "Payment Method",
|
||||||
|
free: "Free",
|
||||||
|
unit: {
|
||||||
|
pieces: "pieces",
|
||||||
|
count: "count",
|
||||||
|
times: "times",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gift_package: "Gift Package",
|
||||||
|
package_name: "Package Name",
|
||||||
|
click_to_select: "Click to select",
|
||||||
|
please_select_package: "Please select a package",
|
||||||
|
package: "Package",
|
||||||
|
addon_package: "Addon Package",
|
||||||
|
domain_count: "Domain Count",
|
||||||
|
unit_count: "pcs",
|
||||||
|
pipeline_count: "Pipeline Count",
|
||||||
|
unit_item: "items",
|
||||||
|
deploy_count: "Deploy Count",
|
||||||
|
unit_times: "times",
|
||||||
|
monitor_count: "Certificate Monitor Count",
|
||||||
|
duration: "Duration",
|
||||||
|
active_time: "Activation Time",
|
||||||
|
expires_time: "Expiration Time",
|
||||||
|
is_present: "Is Present",
|
||||||
|
is_present_yes: "Yes",
|
||||||
|
is_present_no: "No",
|
||||||
|
ordera: "Order",
|
||||||
|
supportBuy: "Support Purchase",
|
||||||
|
intro: "Introduction",
|
||||||
|
packageContent: "Package Content",
|
||||||
|
maxDomainCount: "Max Domain Count",
|
||||||
|
maxPipelineCount: "Max Pipeline Count",
|
||||||
|
maxDeployCount: "Max Deploy Count",
|
||||||
|
maxMonitorCount: "Max Monitor Count",
|
||||||
|
price: "Price",
|
||||||
|
durationPrices: "Duration Prices",
|
||||||
|
packageName: "Package Name",
|
||||||
|
addon: "Addon",
|
||||||
|
typeHelper: "Suite: Only the most recently purchased one is active at a time\nAddon: Multiple can be purchased, effective immediately without affecting the suite\nThe quantities of suite and addon can be accumulated",
|
||||||
|
pipelineCount: "Pipeline Count",
|
||||||
|
unitPipeline: "pipelines",
|
||||||
|
deployCount: "Deployment Count",
|
||||||
|
unitDeploy: "times",
|
||||||
|
monitorCount: "Certificate Monitor Count",
|
||||||
|
unitCount: "pcs",
|
||||||
|
durationPriceTitle: "Duration and Price",
|
||||||
|
selectDuration: "Select Duration",
|
||||||
|
supportPurchase: "Support Purchase",
|
||||||
|
cannotPurchase: "Cannot Purchase",
|
||||||
|
shelfStatus: "Shelf Status",
|
||||||
|
onShelf: "On Shelf",
|
||||||
|
offShelf: "Off Shelf",
|
||||||
|
orderHelper: "Smaller values appear first",
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
export default {
|
||||||
|
max50Chars: "Maximum 50 characters",
|
||||||
|
permissionManagement: "Permission Management",
|
||||||
|
adda: "Add",
|
||||||
|
rootNode: "Root Node",
|
||||||
|
permissionName: "Permission Name",
|
||||||
|
enterPermissionName: "Please enter permission name",
|
||||||
|
permissionCode: "Permission Code",
|
||||||
|
enterPermissionCode: "Please enter permission code",
|
||||||
|
max100Chars: "Maximum 100 characters",
|
||||||
|
examplePermissionCode: "e.g.: sys:user:view",
|
||||||
|
sortOrder: "Sort Order",
|
||||||
|
sortRequired: "Sort order is required",
|
||||||
|
parentNode: "Parent Node",
|
||||||
|
roleManagement: "Role Management",
|
||||||
|
assignPermissions: "Assign Permissions",
|
||||||
|
roleName: "Role Name",
|
||||||
|
enterRoleName: "Please enter role name",
|
||||||
|
unlockLogin: "Unlock Login",
|
||||||
|
notice: "Notice",
|
||||||
|
confirmUnlock: "Are you sure you want to unlock this user's login?",
|
||||||
|
unlockSuccess: "Unlock successful",
|
||||||
|
enterUsername: "Please enter username",
|
||||||
|
modifyPasswordIfFilled: "Fill in to change the password",
|
||||||
|
emaila: "Email",
|
||||||
|
mobile: "Mobile",
|
||||||
|
validTime: "Valid Time",
|
||||||
|
remark: "Remark",
|
||||||
|
roles: "Roles",
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export default {
|
||||||
|
cnameTitle: "CNAME Service Configuration",
|
||||||
|
cnameDescription:
|
||||||
|
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
|
||||||
|
cnameLinkText: "CNAME principle and usage instructions",
|
||||||
|
cnameDomain: "CNAME Domain",
|
||||||
|
cnameDomainPlaceholder: "cname.handsfree.work",
|
||||||
|
cnameDomainHelper:
|
||||||
|
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
|
||||||
|
cnameDomainPattern: "Domain name cannot contain *",
|
||||||
|
dnsProvider: "DNS Provider",
|
||||||
|
dnsProviderAuthorization: "DNS Provider Authorization",
|
||||||
|
};
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
export default {
|
||||||
|
titlea: "Title",
|
||||||
|
pluginFile: "Plugin File",
|
||||||
|
selectPluginFile: "Select plugin file",
|
||||||
|
overrideSameName: "Override same name",
|
||||||
|
override: "Override",
|
||||||
|
noOverride: "No override",
|
||||||
|
overrideHelper: "If a plugin with the same name exists, override it directly",
|
||||||
|
importPlugin: "Import Plugin",
|
||||||
|
customPlugin: "Custom Plugin",
|
||||||
|
pluginType: "Plugin Type",
|
||||||
|
auth: "Authorization",
|
||||||
|
dns: "DNS",
|
||||||
|
deployPlugin: "Deploy Plugin",
|
||||||
|
icon: "Icon",
|
||||||
|
pluginName: "Plugin Name",
|
||||||
|
pluginNameHelper: "Must be English letters or digits, camelCase with type prefix\nExample: AliyunDeployToCDN\nDo not modify name once plugin is used",
|
||||||
|
pluginNameRuleMsg: "Must be English letters or digits, camelCase with type prefix",
|
||||||
|
author: "Author",
|
||||||
|
authorHelper: "Used as prefix when uploading to plugin store, e.g., greper/pluginName",
|
||||||
|
authorRuleMsg: "Must be English letters or digits",
|
||||||
|
titleHelper: "Plugin name in Chinese",
|
||||||
|
descriptionHelper: "Description of the plugin",
|
||||||
|
builtIn: "Built-in",
|
||||||
|
custom: "Custom",
|
||||||
|
store: "Store",
|
||||||
|
version: "Version",
|
||||||
|
pluginDependencies: "Plugin Dependencies",
|
||||||
|
pluginDependenciesHelper: "Dependencies to install first in format: [author/]pluginName[:version]",
|
||||||
|
editableRunStrategy: "Editable Run Strategy",
|
||||||
|
editable: "Editable",
|
||||||
|
notEditable: "Not Editable",
|
||||||
|
runStrategy: "Run Strategy",
|
||||||
|
normalRun: "Normal Run",
|
||||||
|
skipOnSuccess: "Skip on success (Deploy task)",
|
||||||
|
defaultRunStrategyHelper: "Default run strategy",
|
||||||
|
enableDisable: "Enable/Disable",
|
||||||
|
confirmToggle: "Are you sure to",
|
||||||
|
pluginGroup: "Plugin Group",
|
||||||
|
pluginManagement: "Plugin Management",
|
||||||
|
pluginBetaWarning: "Custom plugins are in BETA and may have breaking changes in future",
|
||||||
|
pleaseSelectRecord: "Please select records first",
|
||||||
|
};
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
export default {
|
||||||
|
sys: {
|
||||||
|
setting: {
|
||||||
|
baseSetting: "Base Settings",
|
||||||
|
registerSetting: "Register Settings",
|
||||||
|
safeSetting: "Safe Settings",
|
||||||
|
paymentSetting: "Payment Settings",
|
||||||
|
captchaSetting: "Captcha Setting",
|
||||||
|
pipelineSetting: "Pipeline Settings",
|
||||||
|
oauthSetting: "Login Settings",
|
||||||
|
networkSetting: "Network Settings",
|
||||||
|
adminModeSetting: "Admin Mode Settings",
|
||||||
|
adminModeHelper: "enterprise mode : allow to create and manage pipelines, roles, users, etc.\n saas mode : only allow to create and manage pipelines",
|
||||||
|
enterpriseMode: "Enterprise Mode",
|
||||||
|
saasMode: "SaaS Mode",
|
||||||
|
|
||||||
|
showRunStrategy: "Show RunStrategy",
|
||||||
|
showRunStrategyHelper: "Allow modify the run strategy of the task",
|
||||||
|
|
||||||
|
captchaEnabled: "Enable Login Captcha",
|
||||||
|
captchaHelper: "Whether to enable captcha verification for login",
|
||||||
|
captchaType: "Captcha Setting",
|
||||||
|
captchaTest: "Captcha Test",
|
||||||
|
// 保存后再点击测试,请务必测试通过了,再开启登录验证码
|
||||||
|
captchaTestHelper: "Save and click test, please make sure the test is passed before enabling login captcha",
|
||||||
|
|
||||||
|
pipelineValidTimeEnabled: "Enable Pipeline Valid Time",
|
||||||
|
pipelineValidTimeEnabledHelper: "Whether to enable the valid time of the pipeline",
|
||||||
|
certDomainAddToMonitorEnabled: "Add Domain to Certificate Monitor",
|
||||||
|
certDomainAddToMonitorEnabledHelper: "Whether to add the domain to the certificate monitor",
|
||||||
|
|
||||||
|
defaultCertRenewDays: "Default Certificate Renew Days",
|
||||||
|
defaultCertRenewDaysHelper: "Default certificate renewal days, helpful for table list progress bar display",
|
||||||
|
defaultCertRenewDaysRecommend: "Recommend 15",
|
||||||
|
|
||||||
|
pipelineMaxRunningCount: "Max Running Count",
|
||||||
|
pipelineMaxRunningCountHelper: "Max running count of the pipeline",
|
||||||
|
pipelineMaxRunningCountRecommend: "Recommend 5-15, default 10",
|
||||||
|
|
||||||
|
acmeWalkFromAuthoritative: "Check TXT Record from Authoritative NS",
|
||||||
|
acmeWalkFromAuthoritativeHelper: "Apply certificate when whether to check the TXT record from authoritative NS server first",
|
||||||
|
|
||||||
|
fixedCertExpireDays: "Fixed Cert Expire Days",
|
||||||
|
fixedCertExpireDaysHelper: "Fixed cert expiration days, helpful for table list progress bar display",
|
||||||
|
fixedCertExpireDaysRecommend: "Recommend 90",
|
||||||
|
|
||||||
|
enableOauth: "Enable OAuth2 Login",
|
||||||
|
oauthEnabledHelper: "Whether to enable OAuth2 login",
|
||||||
|
oauthProviders: "OAuth2 Login Providers",
|
||||||
|
oauthType: "OAuth2 Login Type",
|
||||||
|
oauthConfig: "OAuth2 Login Config",
|
||||||
|
oauthProviderSelectorPlaceholder: "Not Configured",
|
||||||
|
oauthCallback: "Callback URL",
|
||||||
|
oauthCallbackHelper: "Copy this URL to the callback address of the OAuth2 login provider",
|
||||||
|
oauthCallbackCopy: "Copy Callback URL",
|
||||||
|
oauthAutoRegister: "Auto Register User",
|
||||||
|
oauthAutoRegisterCheckedText: "Auto Register",
|
||||||
|
oauthAutoRegisterUnCheckedText: "User Select",
|
||||||
|
oauthAutoRegisterHelper: "Whether to auto register user when login",
|
||||||
|
oauthAutoRedirect: "Auto Redirect to OAuth2 Login",
|
||||||
|
oauthAutoRedirectHelper: "Whether to auto redirect to OAuth2 login when login (using the first enabled OAuth2 login type)",
|
||||||
|
oauthOnly: "OAuth2 Login Only",
|
||||||
|
oauthOnlyHelper: "Whether to only allow OAuth2 login, disable password login",
|
||||||
|
enablePasskey: "Enable Passkey Login",
|
||||||
|
passkeyHostnameNotSame: "Passkey hostname must be the same as the main domain",
|
||||||
|
passkeyEnabledHelper:
|
||||||
|
"1、Site must enable https \n2、Domain name must not change, otherwise the registered passkey will be invalid \n3、Domain name must be the same as the main domain, otherwise the registered passkey will be invalid",
|
||||||
|
|
||||||
|
email: {
|
||||||
|
templates: "Email Templates",
|
||||||
|
templateType: "Template Type",
|
||||||
|
templateProvider: "Template Config",
|
||||||
|
|
||||||
|
templateSetting: "Email Template Setting",
|
||||||
|
serverSetting: "Email Server Setting",
|
||||||
|
sendTest: "Send Test",
|
||||||
|
|
||||||
|
templateProviderSelectorPlaceholder: "Not Configured",
|
||||||
|
},
|
||||||
|
notice: "System Notice",
|
||||||
|
noticeHelper: "System notice, will be displayed on the login page",
|
||||||
|
noticePlaceholder: "System notice",
|
||||||
|
customFooter: "Custom Footer",
|
||||||
|
customFooterHelper: "Custom site footer, displayed at the bottom of the page, support HTML format",
|
||||||
|
customFooterPlaceholder: "Custom footer content",
|
||||||
|
|
||||||
|
reverseProxy: "Reverse Proxy List",
|
||||||
|
reverseProxyHelper: "Reverse proxy for ACME address, used when applying for certificate",
|
||||||
|
reverseProxyPlaceholder: "http://le.px.handfree.work",
|
||||||
|
reverseProxyEmpty: "No reverse proxy list configured",
|
||||||
|
environmentVars: "Environment Variables",
|
||||||
|
environmentVarsHelper: "configure the runtime environment variables, one per line, format: KEY=VALUE",
|
||||||
|
|
||||||
|
bindUrl: "Bind URL",
|
||||||
|
bindUrlHelper: "Bind URL, used as your site URL in notifications",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icpRegistrationNumber: "ICP Registration Number",
|
||||||
|
icpPlaceholder: "Guangdong ICP xxxxxxx Number",
|
||||||
|
publicSecurityRegistrationNumber: "Public Security Registration Number",
|
||||||
|
publicSecurityPlaceholder: "Beijing Public Security xxxxxxx Number",
|
||||||
|
enableAssistant: "Enable Assistant",
|
||||||
|
allowCrawlers: "Allow Crawlers",
|
||||||
|
httpProxy: "HTTP Proxy",
|
||||||
|
httpProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||||
|
httpProxyHelper: "Configure when some websites are blocked",
|
||||||
|
httpsProxy: "HTTPS Proxy",
|
||||||
|
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||||
|
saveThenTestTitle: "Save first, then click test",
|
||||||
|
httpsProxyHelper: "Usually both proxies are the same, save first then test",
|
||||||
|
dualStackNetwork: "Dual Stack Network",
|
||||||
|
ipv4Priority: "IPv4 Priority",
|
||||||
|
ipv6Priority: "IPv6 Priority",
|
||||||
|
dualStackNetworkHelper: "If IPv6 priority is selected, enable IPv6 in docker-compose.yaml",
|
||||||
|
enableCommonCnameService: "Enable Public CNAME Service",
|
||||||
|
commonCnameHelper: "Allow use of public CNAME service. If disabled and no <a href='#/sys/cname/provider'>custom CNAME service</a> is set, CNAME proxy certificate application will not work.",
|
||||||
|
enableCommonSelfServicePasswordRetrieval: "Enable self-service password recovery",
|
||||||
|
saveButton: "Save",
|
||||||
|
stopSuccess: "Stopped successfully",
|
||||||
|
google: "Google",
|
||||||
|
baidu: "Baidu",
|
||||||
|
testFailed: "Test Failed",
|
||||||
|
testCompleted: "Test Completed",
|
||||||
|
manageOtherUserPipeline: "Manage other users' pipelines",
|
||||||
|
limitUserPipelineCount: "Limit user pipeline count",
|
||||||
|
limitUserPipelineCountHelper: "0 means no limit",
|
||||||
|
enableSelfRegistration: "Enable self-registration",
|
||||||
|
enableUserValidityPeriod: "Enable user validity period",
|
||||||
|
userValidityPeriodHelper: "Users can use normally within validity; pipelines disabled after expiry",
|
||||||
|
enableUsernameRegistration: "Enable username registration",
|
||||||
|
enableEmailRegistration: "Enable email registration",
|
||||||
|
proFeature: "Pro feature",
|
||||||
|
emailServerSetup: "Set up email server",
|
||||||
|
enableSmsLoginRegister: "Enable SMS login and registration",
|
||||||
|
commFeature: "Commercial feature",
|
||||||
|
smsProvider: "SMS provider",
|
||||||
|
aliyunSms: "Aliyun SMS",
|
||||||
|
tencentSms: "Tencent SMS",
|
||||||
|
yfySms: "YFY SMS",
|
||||||
|
smsTest: "SMS test",
|
||||||
|
testMobilePlaceholder: "Enter test mobile number",
|
||||||
|
saveThenTest: "Save first then test",
|
||||||
|
enterTestMobile: "Please enter test mobile number",
|
||||||
|
sendSuccess: "Sent successfully",
|
||||||
|
atLeastOneLoginRequired: "At least one of password login or SMS login must be enabled",
|
||||||
|
siteHide: "Site Hide",
|
||||||
|
enableSiteHide: "Enable Site Hide",
|
||||||
|
siteHideDescription: "You can disable site accessibility normally and enable it when needed to enhance site security",
|
||||||
|
randomAddress: "Random Address",
|
||||||
|
siteHideUrlHelper: "After the site is hidden, you need to visit this URL to unlock to access normally",
|
||||||
|
fullUnlockUrl: "Full Unlock URL",
|
||||||
|
saveThisUrl: "Please save this URL carefully",
|
||||||
|
unlockPassword: "Unlock Password",
|
||||||
|
unlockPasswordHelper: "Password needed to unlock the hide; set on first time or reset when filled",
|
||||||
|
autoHideTime: "Auto Hide Time",
|
||||||
|
autoHideTimeHelper: "Minutes without requests before auto hiding",
|
||||||
|
hideOpenApi: "Hide Open API",
|
||||||
|
hideOpenApiHelper: "Whether to hide open APIs; whether to hide /api/v1 prefixed endpoints",
|
||||||
|
hideSiteImmediately: "Hide Site Immediately",
|
||||||
|
hideImmediately: "Hide Immediately",
|
||||||
|
confirmHideSiteTitle: "Are you sure to hide the site immediately?",
|
||||||
|
confirmHideSiteContent: "After hiding, the site will be inaccessible. Please operate cautiously.",
|
||||||
|
siteHiddenSuccess: "Site has been hidden",
|
||||||
|
emailServerSettings: "Email Server Settings",
|
||||||
|
setEmailSendingServer: "Set the email sending server",
|
||||||
|
useCustomEmailServer: "Use Custom Email Server",
|
||||||
|
smtpDomain: "SMTP Domain",
|
||||||
|
pleaseEnterSmtpDomain: "Please enter SMTP domain or IP",
|
||||||
|
smtpPort: "SMTP Port",
|
||||||
|
pleaseEnterSmtpPort: "Please enter SMTP port",
|
||||||
|
pleaseEnterUsername: "Please enter username",
|
||||||
|
pleaseEnterPassword: "Please enter password",
|
||||||
|
qqEmailAuthCodeHelper: "If using QQ email, get an authorization code in QQ email settings as the password",
|
||||||
|
senderEmailHelper: "You can use the format: Name<Email> to set the sender name, e.g.: autossl<certd{'@'}example.com>",
|
||||||
|
senderEmail: "Sender Email",
|
||||||
|
pleaseEnterSenderEmail: "Please enter sender email",
|
||||||
|
useSsl: "Use SSL",
|
||||||
|
sslPortNote: "SSL and non-SSL SMTP ports are different, please adjust port accordingly",
|
||||||
|
ignoreCertValidation: "Ignore Certificate Validation",
|
||||||
|
useOfficialEmailServer: "Use Official Email Server",
|
||||||
|
useOfficialEmailServerHelper: "Send emails directly using the official server to avoid complicated setup",
|
||||||
|
testReceiverEmail: "Test Receiver Email",
|
||||||
|
pleaseEnterTestReceiverEmail: "Please enter test receiver email",
|
||||||
|
saveBeforeTest: "Save before testing",
|
||||||
|
sendFailHelpDoc: "Failed to send??? ",
|
||||||
|
emailConfigHelpDoc: "Email configuration help document",
|
||||||
|
tryOfficialEmailServer: "You can also try using the official email server ↗↗↗↗↗↗↗↗",
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export default {
|
||||||
|
steps: {
|
||||||
|
createPipeline: "Create Certificate Pipeline",
|
||||||
|
addTask: "Add Deployment Task",
|
||||||
|
scheduledRun: "Scheduled Run",
|
||||||
|
},
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user