mirror of
https://github.com/certd/certd.git
synced 2026-06-13 12:47:32 +08:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 678b70cee8 | |||
| c0ca8da4ed | |||
| a07dcb1cf5 | |||
| cb4a86d1d5 | |||
| 0499347588 | |||
| cb08e061d2 | |||
| 9bf7ca400f | |||
| 9f75e30af3 | |||
| 3f0243ba9a | |||
| 1a9b367c9d | |||
| 73996f055b | |||
| 57ea2f317a | |||
| c66c05b442 | |||
| 6450d2bd56 | |||
| b5368d120d | |||
| a88d14a3bd | |||
| bdb3d09c09 | |||
| 667e7b185b | |||
| 8483ee0d41 | |||
| c3baaf3ac7 | |||
| c63745d1ba | |||
| 59b9ffadd0 | |||
| d131ea3790 | |||
| b849d34be5 | |||
| 58fc9a551c |
@@ -0,0 +1,55 @@
|
||||
---
|
||||
name: fast-crud-page-dev
|
||||
description: 用于开发或重构 Certd 前端列表管理、后台管理、记录查询、CRUD 表格页面,优先使用 Fast Crud(@fast-crud/fast-crud、fs-crud、useFs、createCrudOptions)实现。当用户要求列表页、管理页、审核页、记录页或表格 CRUD 页面时触发。
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Fast Crud 页面开发技能
|
||||
|
||||
## 角色定义
|
||||
|
||||
你是一名 Certd 前端列表管理页面开发专家,熟悉 Vue 3、Ant Design Vue、Fast Crud 和本仓库现有页面拆分方式。你的目标是让管理页面保持统一的表格、搜索、分页、操作列和弹窗体验。
|
||||
|
||||
## 核心规则
|
||||
|
||||
- 列表管理、后台管理、记录查询、审核记录、CRUD 表格类页面,默认优先使用 Fast Crud 实现。
|
||||
- 只有轻量只读展示、强交互自定义界面、复杂可视化或已有页面模式明确不适合 Fast Crud 时,才手写 `a-table` / 自定义列表,并在回复中说明原因。
|
||||
- 设置表单、概览卡片、向导流程等非列表主体可以保留自定义 Vue;如果同一功能同时包含设置和列表,优先拆成独立页面,或把设置放入对话框。
|
||||
|
||||
## 推荐文件拆分
|
||||
|
||||
- `api.ts`:封装接口请求,保持页面和 CRUD 配置里不直接散落 URL。
|
||||
- `crud.tsx` / `crud-*.tsx`:导出 `createCrudOptions`,集中定义请求映射、搜索项、列、表单、操作列、工具栏和字典。
|
||||
- `index.vue`:承载 `fs-page`、`fs-crud`、页面头部、弹窗和生命周期,使用 `useFs({ createCrudOptions, context })` 创建绑定。
|
||||
|
||||
## 实现流程
|
||||
|
||||
1. 先在 `packages/ui/certd-client/src/views` 下找 1-2 个相近 Fast Crud 页面,沿用它们的导入、布局、命名和权限写法。
|
||||
2. 在 `index.vue` 中使用 `fs-crud ref="crudRef" v-bind="crudBinding"`,并在 `onMounted` / `onActivated` 时调用 `crudExpose.doRefresh()`。
|
||||
3. 在 `crud.tsx` 中配置 `request.pageRequest`、`columns`、`search`、`form`、`rowHandle`、`actionbar`、`toolbar` 等,接口分页参数和返回值按现有页面适配。
|
||||
4. 操作按钮优先放在 Fast Crud 的 `rowHandle.buttons` 或 `actionbar.buttons` 中;审核、保存设置、批量操作等复杂交互可通过 `context` 调用 `index.vue` 中的方法。
|
||||
5. 金额、状态、时间、枚举等字段优先复用项目已有组件、字典和格式化工具;避免在模板里重复堆格式化逻辑。
|
||||
6. 表格查询条件使用 Fast Crud 的 `search` 配置;新增/编辑表单使用 Fast Crud 的 `form` 配置,复杂设置项可以用 Ant Design Vue 对话框承载。
|
||||
7. 删除、审核通过、拒绝等危险操作必须保留确认弹窗和错误提示,成功后刷新当前 CRUD 列表。
|
||||
8. 对话框里只做纯确认时可以使用 `Modal.confirm`;只要需要字段输入、表单校验或提交字段,统一使用 `useFormDialog` / `openFormDialog`,不要在 `Modal.confirm` 的 `content` 里手写输入框。
|
||||
|
||||
## 布局高度
|
||||
|
||||
- Fast Crud 表格依赖外部容器高度计算。虽然表格本身有默认约 200px 高度,但页面内嵌 `fs-crud` 时,为了获得稳定可用的列表区域,必须让外层容器提供明确高度或剩余高度。
|
||||
- 独立列表页通常可直接让 `fs-page` / 页面内容区撑满;如果表格嵌在 tabs、详情页、上下分区或弹窗里,要从页面根容器到 `fs-crud` 建立完整的 flex 高度链路:父容器 `display: flex; flex-direction: column; min-height: 0`,中间内容区和 tab pane 使用 `flex: 1; min-height: 0`,`fs-crud` 本身也使用 `flex: 1; min-height: 0`。
|
||||
- 有固定操作栏、统计区、说明区时,这些区域应 `flex: none`,把剩余空间交给表格区域。
|
||||
- 修改嵌入式 Fast Crud 页面后,要检查空数据、少量数据和多页数据时表格高度、分页器和空状态是否仍在预期区域内。
|
||||
|
||||
## 代码习惯
|
||||
|
||||
- 页面命名、API 命名、权限标识和路由结构要贴近同目录已有页面。
|
||||
- CRUD 配置中不要写大段业务流程;复杂逻辑放回 `index.vue` 方法或 `api.ts`。
|
||||
- 能用 `dict`、`compute`、`valueBuilder`、`valueResolve`、`component` 配置表达的表格/表单行为,不要改成手写模板。
|
||||
- 保持列表页密度和操作入口克制,不要做营销式布局、嵌套卡片或大块说明文字。
|
||||
- 如果页面有“设置 + 列表”,管理端优先拆成两个路由页面;用户端提现设置这类低频配置优先使用对话框保存。
|
||||
|
||||
## 验证方式
|
||||
|
||||
- 前端改动后,只对本次改动的 Vue / TS / TSX / locale 文件运行项目现有 Prettier / ESLint。
|
||||
- 不运行 `vue-tsc` / `pnpm tsc`,因为当前依赖组合下 `vue-tsc` 已知会抛内部错误。
|
||||
- 若只是新增或修改本 skill / 文档,不需要运行前端格式化和测试。
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **certd-server:** 调整首页缓存控制头的判断逻辑 ([0499347](https://github.com/certd/certd/commit/0499347588ee544862420ab9a5afd2546d61bc6c))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **controller:** 更换版本获取源并添加版本标准化处理 ([cb08e06](https://github.com/certd/certd/commit/cb08e061d257ba23a0fefdbfb046a8c759def828))
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 固化华为云sdk版本,避免华为云调用报错 ([59b9ffa](https://github.com/certd/certd/commit/59b9ffadd05faf3982151c48f8d83cbd97419865))
|
||||
* **email-service:** 优化商业版测试邮件内容中带的url链接 ([667e7b1](https://github.com/certd/certd/commit/667e7b185bf26558972be01720872f48352b5876))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 商业版支持限制泛域名数量 ([c63745d](https://github.com/certd/certd/commit/c63745d1ba30904428ba6b13ab0785298baa5cae))
|
||||
* **suite:** 商业版用户已购套餐支持修改 ([bdb3d09](https://github.com/certd/certd/commit/bdb3d09c09fc73e7e5e3401f6ef5588bf8ad5088))
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -34,7 +34,7 @@ Certd® 是一个免费的全自动证书管理系统,让你的网站证书永
|
||||
* **多种通知方式**: 邮件通知、webhook通知、企微、钉钉、飞书、anpush等多种通知方式
|
||||
* **私有化部署**: 数据保存本地,安装简单快捷,镜像由Github Actions构建,过程公开透明
|
||||
* **多重安全保障**: 授权加密,站点隐藏,2FA,密码防爆破等多重安全保障
|
||||
* **多数据库支持**:支持SQLite、PostgreSQL、MySQL
|
||||
* **多数据库支持**:支持SQLite、PostgreSQL、MySQL、MariaDB
|
||||
* **开放接口支持**: 提供RESTful API接口,方便集成到其他系统
|
||||
* **站点证书监控**: 定时监控网站证书的过期时间
|
||||
* **多用户管理**: 用户可以管理自己的证书流水线
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ This project not only supports automated certificate application but also automa
|
||||
* Multiple notification methods, including email, webhook, WeChat Work, DingTalk, Lark, and anpush.
|
||||
* On-premises deployment, local data storage, simple and quick installation. Images are built by Github Actions, with a transparent process.
|
||||
* Multiple security measures, including authorization encryption, site hiding, 2FA, and password brute-force protection.
|
||||
* Supports multiple databases such as SQLite, PostgreSQL, and MySQL.
|
||||
* Supports multiple databases such as SQLite, PostgreSQL, MySQL, and MariaDB.
|
||||
* Open API support.
|
||||
* Site certificate monitoring.
|
||||
* Multi-user management.
|
||||
|
||||
@@ -3,6 +3,37 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 固化华为云sdk版本,避免华为云调用报错 ([59b9ffa](https://github.com/certd/certd/commit/59b9ffadd05faf3982151c48f8d83cbd97419865))
|
||||
* **email-service:** 优化商业版测试邮件内容中带的url链接 ([667e7b1](https://github.com/certd/certd/commit/667e7b185bf26558972be01720872f48352b5876))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 商业版支持限制泛域名数量 ([c63745d](https://github.com/certd/certd/commit/c63745d1ba30904428ba6b13ab0785298baa5cae))
|
||||
* **suite:** 商业版用户已购套餐支持修改 ([bdb3d09](https://github.com/certd/certd/commit/bdb3d09c09fc73e7e5e3401f6ef5588bf8ad5088))
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复第三方登录丢失state时无法在用户信息页面绑定第三方账号的bug ([45dedf5](https://github.com/certd/certd/commit/45dedf5bc779fea852e1f33dda4f31db2765633c))
|
||||
* 修复群晖授权没有显示设备id输入框的bug ([2f172b5](https://github.com/certd/certd/commit/2f172b56e9411303ca15138d827bdb9bafdae4d1))
|
||||
* 修复自动注册后没有跳转到控制台的bug ([4681ec9](https://github.com/certd/certd/commit/4681ec90088a3eb665427b2ac4047ec5ccefd7b3))
|
||||
* 修复clogin登录丢失state问题 ([22f5cfc](https://github.com/certd/certd/commit/22f5cfcfd8462ca74128329eefb3f48b3ee0b7ea))
|
||||
* 修复clogin多选类型登录失败的bug ([9f878a3](https://github.com/certd/certd/commit/9f878a353cd49b7b10bb0a95610ad236bc920dd2))
|
||||
|
||||
### Features
|
||||
|
||||
* 彩虹登录支持选择多种登录方式 ([7aa0c7e](https://github.com/certd/certd/commit/7aa0c7e491fe660abb62e68792ff5474f19bd5b8))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 第三方登录自动注册的用户支持设置初始化密码 ([a815d02](https://github.com/certd/certd/commit/a815d0245b97efbb948b33d6fc9d49862ce06889))
|
||||
* 头像增加缓存时间 ([7015b1b](https://github.com/certd/certd/commit/7015b1b232602e5168a3eb8bee6d7f1776ae1e74))
|
||||
|
||||
## [1.39.16](https://github.com/certd/certd/compare/v1.39.15...v1.39.16) (2026-05-13)
|
||||
|
||||
**Note:** Version bump only for package root
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.40.0"
|
||||
"version": "1.40.2"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/publishlab/node-acme-client/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.40.1](https://github.com/publishlab/node-acme-client/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
# [1.40.0](https://github.com/publishlab/node-acme-client/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"type": "module",
|
||||
"module": "./dist/index.js",
|
||||
"main": "./dist/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.40.0",
|
||||
"@certd/basic": "^1.40.2",
|
||||
"@peculiar/x509": "^1.11.0",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.9.0",
|
||||
@@ -50,7 +50,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"before-build": "node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tsconfig.tsbuildinfo',{force:true});\"",
|
||||
"build": "npm run before-build && tsc --skipLibCheck",
|
||||
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"build-docs": "jsdoc2md dist/client.js > docs/client.md && jsdoc2md dist/crypto/index.js > docs/crypto.md && jsdoc2md dist/crypto/forge.js > docs/forge.md",
|
||||
"lint": "eslint \"src/**/*.ts\" \"types/**/*.ts\"",
|
||||
"lint-types": "tsd --files \"types/index.test-d.ts\"",
|
||||
@@ -76,5 +76,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "1c6dc169ac04fd09ef94404a912a15cbb17e1452"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"inlineSourceMap": false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": false,
|
||||
"noUnusedLocals": false,
|
||||
"stripInternal": true,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
@@ -1 +1 @@
|
||||
00:44
|
||||
00:35
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -9,7 +9,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
|
||||
"build": "npm run before-build && tsc --skipLibCheck",
|
||||
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"preview": "vite preview",
|
||||
"test": "mocha --loader=ts-node/esm",
|
||||
@@ -52,5 +52,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "1c6dc169ac04fd09ef94404a912a15cbb17e1452"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ export class LocalCache<V = any> {
|
||||
cache: Map<string, { value: V; expiresAt: number }>;
|
||||
constructor(opts: { clearInterval?: number } = {}) {
|
||||
this.cache = new Map();
|
||||
setInterval(() => {
|
||||
const intervalId = setInterval(() => {
|
||||
this.clearExpires();
|
||||
}, opts.clearInterval ?? 5 * 60 * 1000);
|
||||
intervalId.unref?.();
|
||||
}
|
||||
|
||||
get(key: string): V | undefined {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap": false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -9,7 +9,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
|
||||
"build": "npm run before-build && tsc --skipLibCheck",
|
||||
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"build3": "rollup -c",
|
||||
"preview": "vite preview",
|
||||
@@ -19,8 +19,8 @@
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.40.0",
|
||||
"@certd/plus-core": "^1.40.0",
|
||||
"@certd/basic": "^1.40.2",
|
||||
"@certd/plus-core": "^1.40.2",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
@@ -49,5 +49,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "1c6dc169ac04fd09ef94404a912a15cbb17e1452"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap": false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"importHelpers": true,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -27,5 +27,5 @@
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "bae5a04dcc0a679c290a9805c3ac4a6020eb6ec0"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ module.exports = {
|
||||
output: {
|
||||
file: "dist/bundle.js",
|
||||
format: "cjs",
|
||||
sourcemap: false,
|
||||
},
|
||||
plugins: [
|
||||
// 解析第三方依赖
|
||||
@@ -22,10 +23,13 @@ module.exports = {
|
||||
// ],
|
||||
}),
|
||||
Typescript({
|
||||
tsconfig: "./tsconfig.build.json",
|
||||
target: "esnext",
|
||||
rootDir: "src",
|
||||
declaration: true,
|
||||
declarationDir: "dist/d",
|
||||
sourceMap: false,
|
||||
inlineSourceMap: false,
|
||||
exclude: ["./node_modules/**", "./src/**/*.vue", "./src/**/*.spec.ts"],
|
||||
allowSyntheticDefaultImports: true,
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
"composite": false,
|
||||
"useDefineForClassFields": true,
|
||||
"strict": false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"inlineSourceMap": false,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": false,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -9,7 +9,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
|
||||
"build": "npm run before-build && tsc --skipLibCheck",
|
||||
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
@@ -34,5 +34,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "bae5a04dcc0a679c290a9805c3ac4a6020eb6ec0"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap":false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
@@ -22,7 +23,6 @@
|
||||
"composite": false,
|
||||
"useDefineForClassFields": true,
|
||||
"strict": false,
|
||||
// "sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "@certd/jdcloud",
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"description": "jdcloud openApi sdk",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "rollup -c ",
|
||||
"before-build": "node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tsconfig.tsbuildinfo',{force:true});fs.rmSync('.rollup.cache',{recursive:true,force:true});\"",
|
||||
"build": "npm run before-build && rollup -c ",
|
||||
"dev-build": "npm run build",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish"
|
||||
@@ -58,5 +59,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "bae5a04dcc0a679c290a9805c3ac4a6020eb6ec0"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ module.exports = {
|
||||
output: {
|
||||
file: "dist/bundle.js",
|
||||
format: "cjs",
|
||||
sourcemap: false,
|
||||
},
|
||||
plugins: [
|
||||
// 解析第三方依赖
|
||||
@@ -22,10 +23,13 @@ module.exports = {
|
||||
// ],
|
||||
}),
|
||||
Typescript({
|
||||
tsconfig: "./tsconfig.build.json",
|
||||
target: "esnext",
|
||||
rootDir: "src",
|
||||
declaration: true,
|
||||
declarationDir: "dist/d",
|
||||
sourceMap: false,
|
||||
inlineSourceMap: false,
|
||||
exclude: ["./node_modules/**", "./src/**/*.vue", "./src/**/*.spec.ts"],
|
||||
allowSyntheticDefaultImports: true,
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
"composite": false,
|
||||
"useDefineForClassFields": true,
|
||||
"strict": false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"inlineSourceMap": false,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": false,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -9,7 +9,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
|
||||
"build": "npm run before-build && tsc --skipLibCheck",
|
||||
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
@@ -19,7 +19,7 @@
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.40.0",
|
||||
"@certd/basic": "^1.40.2",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -36,5 +36,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "1c6dc169ac04fd09ef94404a912a15cbb17e1452"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap":false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
@@ -22,7 +23,6 @@
|
||||
"composite": false,
|
||||
"useDefineForClassFields": true,
|
||||
"strict": false,
|
||||
// "sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -9,7 +9,7 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
|
||||
"build": "npm run before-build && tsc --skipLibCheck",
|
||||
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"test": "midway-bin test --ts -V",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
@@ -29,11 +29,11 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.40.0",
|
||||
"@certd/basic": "^1.40.0",
|
||||
"@certd/pipeline": "^1.40.0",
|
||||
"@certd/plugin-lib": "^1.40.0",
|
||||
"@certd/plus-core": "^1.40.0",
|
||||
"@certd/acme-client": "^1.40.2",
|
||||
"@certd/basic": "^1.40.2",
|
||||
"@certd/pipeline": "^1.40.2",
|
||||
"@certd/plugin-lib": "^1.40.2",
|
||||
"@certd/plus-core": "^1.40.2",
|
||||
"@midwayjs/cache": "3.14.0",
|
||||
"@midwayjs/core": "3.20.11",
|
||||
"@midwayjs/i18n": "3.20.13",
|
||||
@@ -69,5 +69,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "1c6dc169ac04fd09ef94404a912a15cbb17e1452"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -253,6 +253,14 @@ export class SysSuiteSetting extends BaseSettings {
|
||||
intro?: string;
|
||||
}
|
||||
|
||||
export class SysAutoFixSetting extends BaseSettings {
|
||||
static __title__ = '自动修复记录';
|
||||
static __key__ = 'sys.auto.fix';
|
||||
static __access__ = 'private';
|
||||
|
||||
fixed: Record<string, boolean> = {};
|
||||
}
|
||||
|
||||
|
||||
export type SiteHidden = {
|
||||
enabled: boolean;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap": false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -9,7 +9,7 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
|
||||
"build": "npm run before-build && tsc --skipLibCheck",
|
||||
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"test": "midway-bin test --ts -V",
|
||||
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
|
||||
@@ -49,5 +49,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "bae5a04dcc0a679c290a9805c3ac4a6020eb6ec0"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap":false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
@@ -21,7 +22,6 @@
|
||||
"composite": false,
|
||||
"useDefineForClassFields": true,
|
||||
"strict": false,
|
||||
// "sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
|
||||
"build": "tsc --skipLibCheck",
|
||||
"build": "tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
@@ -18,10 +18,10 @@
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.40.0",
|
||||
"@certd/basic": "^1.40.0",
|
||||
"@certd/pipeline": "^1.40.0",
|
||||
"@certd/plugin-lib": "^1.40.0",
|
||||
"@certd/acme-client": "^1.40.2",
|
||||
"@certd/basic": "^1.40.2",
|
||||
"@certd/pipeline": "^1.40.2",
|
||||
"@certd/plugin-lib": "^1.40.2",
|
||||
"psl": "^1.9.0",
|
||||
"punycode.js": "^2.3.1"
|
||||
},
|
||||
@@ -41,5 +41,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "1c6dc169ac04fd09ef94404a912a15cbb17e1452"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap":false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "@certd/plugin-lib",
|
||||
"private": false,
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
|
||||
"build": "npm run before-build &&tsc --skipLibCheck",
|
||||
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
@@ -23,10 +23,10 @@
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@alicloud/tea-util": "^1.4.11",
|
||||
"@aws-sdk/client-s3": "^3.964.0",
|
||||
"@certd/acme-client": "^1.40.0",
|
||||
"@certd/basic": "^1.40.0",
|
||||
"@certd/pipeline": "^1.40.0",
|
||||
"@certd/plus-core": "^1.40.0",
|
||||
"@certd/acme-client": "^1.40.2",
|
||||
"@certd/basic": "^1.40.2",
|
||||
"@certd/pipeline": "^1.40.2",
|
||||
"@certd/plus-core": "^1.40.2",
|
||||
"@kubernetes/client-node": "0.21.0",
|
||||
"ali-oss": "^6.22.0",
|
||||
"basic-ftp": "^5.0.5",
|
||||
@@ -61,5 +61,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "1c6dc169ac04fd09ef94404a912a15cbb17e1452"
|
||||
"gitHead": "73996f055bbff996ee776e7788e5a5cb500fc197"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"inlineSourceMap": false
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"inlineSourceMap": false,
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"stripInternal": true,
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 商业版支持限制泛域名数量 ([c63745d](https://github.com/certd/certd/commit/c63745d1ba30904428ba6b13ab0785298baa5cae))
|
||||
* **suite:** 商业版用户已购套餐支持修改 ([bdb3d09](https://github.com/certd/certd/commit/bdb3d09c09fc73e7e5e3401f6ef5588bf8ad5088))
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -106,8 +106,8 @@
|
||||
"zod-defaults": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.40.0",
|
||||
"@certd/pipeline": "^1.40.0",
|
||||
"@certd/lib-iframe": "^1.40.2",
|
||||
"@certd/pipeline": "^1.40.2",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
@@ -40,6 +40,9 @@ export default {
|
||||
basicInfo: "Basic Information",
|
||||
disabled: "Disabled",
|
||||
enabled: "Enabled",
|
||||
enabledLabel: "Enabled",
|
||||
normal: "Normal",
|
||||
used_up: "Used up",
|
||||
default: "Default",
|
||||
success: "Success",
|
||||
test: "Test",
|
||||
|
||||
@@ -6,6 +6,9 @@ export default {
|
||||
specifications: "Specifications",
|
||||
pipeline: "Pipeline",
|
||||
domain: "Domain",
|
||||
totalDomain: "Total Domain Count",
|
||||
wildcardDomain: "Wildcard Domain",
|
||||
includedWildcardDomain: "Included Wildcard Domain Count",
|
||||
deployTimes: "Deployments",
|
||||
monitorCount: "DomainMonitors",
|
||||
duration: "Duration",
|
||||
@@ -24,11 +27,13 @@ export default {
|
||||
please_select_package: "Please select a package",
|
||||
package: "Package",
|
||||
addon_package: "Addon Package",
|
||||
domain_count: "Domain Count",
|
||||
domain_count: "Total Domain Count",
|
||||
wildcard_domain_count: "Wildcard Domain Count",
|
||||
unit_count: "pcs",
|
||||
pipeline_count: "Pipeline Count",
|
||||
unit_item: "items",
|
||||
deploy_count: "Deploy Count",
|
||||
deploy_count_used: "Used Deploy Count",
|
||||
unit_times: "times",
|
||||
monitor_count: "Certificate Monitor Count",
|
||||
duration: "Duration",
|
||||
@@ -42,6 +47,7 @@ export default {
|
||||
intro: "Introduction",
|
||||
packageContent: "Package Content",
|
||||
maxDomainCount: "Max Domain Count",
|
||||
maxWildcardDomainCount: "Max Wildcard Domain Count",
|
||||
maxPipelineCount: "Max Pipeline Count",
|
||||
maxDeployCount: "Max Deploy Count",
|
||||
maxMonitorCount: "Max Monitor Count",
|
||||
@@ -51,6 +57,10 @@ export default {
|
||||
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",
|
||||
wildcardDomainCount: "Wildcard Domain Count",
|
||||
wildcardDomainCountPart: "Included Wildcard Domain Count",
|
||||
wildcardDomainCountSub: "- Wildcard Domain Count",
|
||||
wildcardDomainCountHelper: "Wildcard domains are also limited by the total domain count: each wildcard domain consumes both domain count and wildcard domain count quota.",
|
||||
unitPipeline: "pipelines",
|
||||
deployCount: "Deployment Count",
|
||||
unitDeploy: "times",
|
||||
|
||||
@@ -44,6 +44,9 @@ export default {
|
||||
basicInfo: "基础信息",
|
||||
disabled: "禁用",
|
||||
enabled: "启用",
|
||||
enabledLabel: "启用",
|
||||
normal: "正常",
|
||||
used_up: "已用完",
|
||||
default: "默认",
|
||||
success: "成功",
|
||||
test: "测试",
|
||||
|
||||
@@ -6,6 +6,9 @@ export default {
|
||||
specifications: "规格",
|
||||
pipeline: "流水线",
|
||||
domain: "域名",
|
||||
totalDomain: "域名总数量",
|
||||
wildcardDomain: "泛域名",
|
||||
includedWildcardDomain: "其中泛域名数量",
|
||||
deployTimes: "部署次数",
|
||||
monitorCount: "域名监控数",
|
||||
duration: "时长",
|
||||
@@ -24,11 +27,13 @@ export default {
|
||||
please_select_package: "请选择套餐",
|
||||
package: "套餐",
|
||||
addon_package: "加量包",
|
||||
domain_count: "域名数量",
|
||||
domain_count: "域名总数量",
|
||||
wildcard_domain_count: "泛域名数量",
|
||||
unit_count: "个",
|
||||
pipeline_count: "流水线数量",
|
||||
unit_item: "条",
|
||||
deploy_count: "部署次数",
|
||||
deploy_count_used: "已用部署次数",
|
||||
unit_times: "次",
|
||||
monitor_count: "证书监控数量",
|
||||
duration: "时长",
|
||||
@@ -42,6 +47,7 @@ export default {
|
||||
intro: "介绍",
|
||||
packageContent: "套餐内容",
|
||||
maxDomainCount: "最大域名数",
|
||||
maxWildcardDomainCount: "最大泛域名数",
|
||||
maxPipelineCount: "最大流水线数",
|
||||
maxDeployCount: "最大部署数",
|
||||
maxMonitorCount: "最大监控数",
|
||||
@@ -51,6 +57,10 @@ export default {
|
||||
addon: "加量包",
|
||||
typeHelper: "套餐:同一时间只有最新购买的一个生效\n加量包:可购买多个,购买后立即生效,不影响套餐\n套餐和加量包数量可叠加",
|
||||
pipelineCount: "流水线数量",
|
||||
wildcardDomainCount: "泛域名数量",
|
||||
wildcardDomainCountPart: "其中泛域名数量",
|
||||
wildcardDomainCountSub: "- 泛域名数量",
|
||||
wildcardDomainCountHelper: "泛域名数量受域名总数量限制:泛域名会同时占用域名总数量和泛域名数量额度;注意:如果域名总数有限制,泛域名数量不要设置为无限制。",
|
||||
unitPipeline: "条",
|
||||
deployCount: "部署次数",
|
||||
unitDeploy: "次",
|
||||
|
||||
@@ -13,7 +13,6 @@ export function useReference(formItem: any) {
|
||||
const ctx = {
|
||||
compute: (opts: any) => {
|
||||
const func = (context: any) => {
|
||||
debugger;
|
||||
let form = context.form || {};
|
||||
form = form.input || form.body || form; // form.access去掉,历史原因,access的mergeScript会处理form.access
|
||||
return opts({
|
||||
|
||||
@@ -56,6 +56,10 @@ export function findStep(pipeline: any, id: string) {
|
||||
|
||||
export async function checkPipelineLimit() {
|
||||
const settingsStore = useSettingStore();
|
||||
if (settingsStore.isEnterprise) {
|
||||
//企业模式不限制
|
||||
return;
|
||||
}
|
||||
if (settingsStore.isComm && settingsStore.suiteSetting.enabled) {
|
||||
//检查数量是否超限
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ export type SuiteDetail = {
|
||||
expiresTime?: number;
|
||||
pipelineCount?: SuiteValue;
|
||||
domainCount?: SuiteValue;
|
||||
wildcardDomainCount?: SuiteValue;
|
||||
deployCount?: SuiteValue;
|
||||
monitorCount?: SuiteValue;
|
||||
};
|
||||
|
||||
@@ -147,7 +147,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
},
|
||||
"content.maxDomainCount": {
|
||||
title: "域名数量",
|
||||
title: "域名总数量",
|
||||
type: "text",
|
||||
form: {
|
||||
key: ["content", "maxDomainCount"],
|
||||
@@ -168,6 +168,28 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
"content.maxWildcardDomainCount": {
|
||||
title: "其中泛域名数量",
|
||||
type: "text",
|
||||
form: {
|
||||
key: ["content", "maxWildcardDomainCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
vModel: "modelValue",
|
||||
unit: "个",
|
||||
},
|
||||
rules: [{ required: true, message: "此项必填" }],
|
||||
},
|
||||
column: {
|
||||
width: 120,
|
||||
component: {
|
||||
name: SuiteValue,
|
||||
vModel: "modelValue",
|
||||
unit: "个",
|
||||
},
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
"content.maxPipelineCount": {
|
||||
title: "流水线数量",
|
||||
type: "text",
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
<span class="label">{{ $t("certd.order.specifications") }}:</span>
|
||||
<span class="flex-o flex-wrap">
|
||||
<span class="flex-o"> {{ $t("certd.order.pipeline") }}<suite-value class="ml-5" :model-value="product.content.maxPipelineCount" :unit="$t('certd.order.unit.pieces')" />; </span>
|
||||
<span class="flex-o"> {{ $t("certd.order.domain") }}<suite-value class="ml-5" :model-value="product.content.maxDomainCount" :unit="$t('certd.order.unit.count')" />; </span>
|
||||
<span class="flex-o"> {{ $t("certd.order.totalDomain") }}<suite-value class="ml-5" :model-value="product.content.maxDomainCount" :unit="$t('certd.order.unit.count')" />; </span>
|
||||
<span class="flex-o" style="padding-left: 2em">
|
||||
- {{ $t("certd.order.includedWildcardDomain") }}<suite-value class="ml-5" :model-value="product.content.maxWildcardDomainCount" :unit="$t('certd.order.unit.count')" />;
|
||||
</span>
|
||||
<span class="flex-o"> {{ $t("certd.order.deployTimes") }}<suite-value class="ml-5" :model-value="product.content.maxDeployCount" :unit="$t('certd.order.unit.times')" />; </span>
|
||||
<span class="flex-o"> {{ $t("certd.order.monitorCount") }}<suite-value class="ml-5" :model-value="product.content.maxMonitorCount" :unit="$t('certd.order.unit.times')" />; </span>
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<a-card :title="product.title" class="product-card">
|
||||
<template #extra>
|
||||
<fs-values-format v-model="product.type" :dict="productTypeDictRef"></fs-values-format>
|
||||
<fs-values-format :model-value="product.type" :dict="productTypeDictRef"></fs-values-format>
|
||||
</template>
|
||||
|
||||
<div class="product-intro">{{ product.intro || "暂无介绍" }}</div>
|
||||
@@ -12,9 +12,13 @@
|
||||
<suite-value :model-value="product.content.maxPipelineCount" unit="条" />
|
||||
</div>
|
||||
<div class="flex-between mt-5">
|
||||
<div class="flex-o"><fs-icon icon="ant-design:check-outlined" class="color-green mr-5" />域名数量:</div>
|
||||
<div class="flex-o"><fs-icon icon="ant-design:check-outlined" class="color-green mr-5" />域名总数量:</div>
|
||||
<suite-value :model-value="product.content.maxDomainCount" unit="个" />
|
||||
</div>
|
||||
<div class="flex-between mt-5">
|
||||
<div class="flex-o" style="padding-left: 2em">- 其中泛域名数量:</div>
|
||||
<suite-value :model-value="product.content.maxWildcardDomainCount" unit="个" />
|
||||
</div>
|
||||
<div class="flex-between mt-5">
|
||||
<div class="flex-o">
|
||||
<fs-icon icon="ant-design:check-outlined" class="color-green mr-5" />
|
||||
|
||||
@@ -204,6 +204,7 @@ const hasNewVersion = computed(() => {
|
||||
return isNewVersion(version.value, latestVersion.value);
|
||||
});
|
||||
async function loadLatestVersion() {
|
||||
// version.value = settingsStore.app.version; //前端有缓存 可能不准确
|
||||
latestVersion.value = await api.GetLatestVersion();
|
||||
console.log("latestVersion", latestVersion.value);
|
||||
|
||||
|
||||
@@ -20,10 +20,14 @@
|
||||
<div class="flex-between mt-5">
|
||||
<div class="flex-o">
|
||||
<fs-icon icon="ant-design:check-outlined" class="color-green mr-5" />
|
||||
域名数量:
|
||||
域名总数量:
|
||||
</div>
|
||||
<suite-value :model-value="detail.domainCount.max" :used="detail.domainCount.used" unit="个" />
|
||||
</div>
|
||||
<div class="flex-between mt-5">
|
||||
<div class="flex-o ml-20">- 其中泛域名数量:</div>
|
||||
<suite-value :model-value="detail.wildcardDomainCount.max" :used="detail.wildcardDomainCount.used" unit="个" />
|
||||
</div>
|
||||
<div class="flex-between mt-5">
|
||||
<div class="flex-o">
|
||||
<fs-icon icon="ant-design:check-outlined" class="color-green mr-5" />
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
content: {
|
||||
header: t("certd.packageContent"),
|
||||
columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"],
|
||||
columns: ["content.maxDomainCount", "content.maxWildcardDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"],
|
||||
},
|
||||
price: {
|
||||
header: t("certd.price"),
|
||||
@@ -154,6 +154,28 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
},
|
||||
},
|
||||
"content.maxWildcardDomainCount": {
|
||||
title: t("certd.wildcardDomainCountPart"),
|
||||
type: "text",
|
||||
form: {
|
||||
key: ["content", "maxWildcardDomainCount"],
|
||||
helper: t("certd.wildcardDomainCountHelper"),
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
vModel: "modelValue",
|
||||
unit: t("certd.unitCount"),
|
||||
},
|
||||
rules: [{ required: true, message: t("certd.requiredField") }],
|
||||
},
|
||||
column: {
|
||||
width: 120,
|
||||
component: {
|
||||
name: SuiteValue,
|
||||
vModel: "modelValue",
|
||||
unit: t("certd.unitCount"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"content.maxPipelineCount": {
|
||||
title: t("certd.pipelineCount"),
|
||||
type: "text",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<div style="height: 400px">
|
||||
<ProductManager @refreshed="onTableRefresh"></ProductManager>
|
||||
</div>
|
||||
<div class="helper">泛域名数量受域名总数量限制:泛域名会同时占用域名总数量和泛域名数量额度。</div>
|
||||
<div class="helper">不建议设置免费套餐,可以在下方配置注册赠送套餐,或者在用户套餐管理中手动赠送套餐</div>
|
||||
</a-form-item>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { sysUserSuiteApi } from "./api";
|
||||
import { useRouter } from "vue-router";
|
||||
import SuiteValueEdit from "/@/views/sys/suite/product/suite-value-edit.vue";
|
||||
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
||||
import DurationValue from "/@/views/sys/suite/product/duration-value.vue";
|
||||
@@ -33,8 +32,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
return res;
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
@@ -71,7 +68,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
buttons: {
|
||||
view: { show: true },
|
||||
copy: { show: false },
|
||||
edit: { show: false },
|
||||
edit: { show: true },
|
||||
remove: { show: true },
|
||||
// continue:{
|
||||
// text:"续期",
|
||||
@@ -107,7 +104,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
show: true,
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
rules: [{ required: true, message: t("certd.field_required") }],
|
||||
},
|
||||
column: {
|
||||
width: 200,
|
||||
@@ -127,7 +124,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
label: "nickName",
|
||||
}),
|
||||
form: {
|
||||
show: true,
|
||||
component: {
|
||||
disabled: true,
|
||||
crossPage: true,
|
||||
multiple: false,
|
||||
select: {
|
||||
@@ -183,7 +182,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
],
|
||||
}),
|
||||
form: {
|
||||
show: false,
|
||||
show: true,
|
||||
component: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
column: {
|
||||
width: 80,
|
||||
@@ -204,7 +206,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
title: t("certd.domain_count"),
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
key: ["content", "maxDomainCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
@@ -223,11 +224,32 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
"content.maxWildcardDomainCount": {
|
||||
title: t("certd.wildcardDomainCountPart"),
|
||||
type: "text",
|
||||
form: {
|
||||
key: ["content", "maxWildcardDomainCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
vModel: "modelValue",
|
||||
unit: t("certd.unit_count"),
|
||||
},
|
||||
rules: [{ required: true, message: t("certd.field_required") }],
|
||||
},
|
||||
column: {
|
||||
width: 120,
|
||||
component: {
|
||||
name: SuiteValue,
|
||||
vModel: "modelValue",
|
||||
unit: t("certd.unit_count"),
|
||||
},
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
"content.maxPipelineCount": {
|
||||
title: t("certd.pipeline_count"),
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
key: ["content", "maxPipelineCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
@@ -250,7 +272,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
title: t("certd.deploy_count"),
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
key: ["content", "maxDeployCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
@@ -276,7 +297,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
title: t("certd.monitor_count"),
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
key: ["content", "maxMonitorCount"],
|
||||
component: {
|
||||
name: SuiteValueEdit,
|
||||
@@ -298,7 +318,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
duration: {
|
||||
title: t("certd.duration"),
|
||||
type: "text",
|
||||
form: { show: false },
|
||||
form: {
|
||||
rules: [{ required: true, message: t("certd.field_required") }],
|
||||
},
|
||||
column: {
|
||||
component: {
|
||||
name: DurationValue,
|
||||
@@ -342,7 +364,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
title: t("certd.expires_time"),
|
||||
type: "date",
|
||||
form: {
|
||||
show: false,
|
||||
valueBuilder: ({ value }) => {
|
||||
return dayjs(value);
|
||||
},
|
||||
valueResolve: ({ value }) => {
|
||||
//今天的最后一秒
|
||||
return dayjs(value).endOf("day").valueOf();
|
||||
},
|
||||
},
|
||||
column: {
|
||||
width: 150,
|
||||
@@ -367,13 +395,56 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
}),
|
||||
form: {
|
||||
value: true,
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
deployCountUsed: {
|
||||
title: t("certd.deploy_count_used"),
|
||||
type: "number",
|
||||
form: {
|
||||
value: 0,
|
||||
rules: [{ required: true, message: t("certd.field_required") }],
|
||||
},
|
||||
column: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
title: t("certd.disabled"),
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
data: [
|
||||
{ label: t("certd.enabledLabel"), value: false, color: "success" },
|
||||
{ label: t("certd.disabledLabel"), value: true, color: "error" },
|
||||
],
|
||||
}),
|
||||
form: {
|
||||
value: false,
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
isEmpty: {
|
||||
title: t("certd.status"),
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
data: [
|
||||
{ label: t("certd.normal"), value: false, color: "success" },
|
||||
{ label: t("certd.used_up"), value: true, color: "gray" },
|
||||
],
|
||||
}),
|
||||
form: {
|
||||
value: false,
|
||||
},
|
||||
column: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
createTime: {
|
||||
title: t("certd.createTime"),
|
||||
type: "datetime",
|
||||
|
||||
@@ -3,6 +3,27 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **certd-server:** 调整首页缓存控制头的判断逻辑 ([0499347](https://github.com/certd/certd/commit/0499347588ee544862420ab9a5afd2546d61bc6c))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **controller:** 更换版本获取源并添加版本标准化处理 ([cb08e06](https://github.com/certd/certd/commit/cb08e061d257ba23a0fefdbfb046a8c759def828))
|
||||
|
||||
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 固化华为云sdk版本,避免华为云调用报错 ([59b9ffa](https://github.com/certd/certd/commit/59b9ffadd05faf3982151c48f8d83cbd97419865))
|
||||
* **email-service:** 优化商业版测试邮件内容中带的url链接 ([667e7b1](https://github.com/certd/certd/commit/667e7b185bf26558972be01720872f48352b5876))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 商业版支持限制泛域名数量 ([c63745d](https://github.com/certd/certd/commit/c63745d1ba30904428ba6b13ab0785298baa5cae))
|
||||
|
||||
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE cd_cert_info ADD COLUMN wildcard_domain_count bigint NOT NULL DEFAULT 0;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE cd_cert_info ADD COLUMN wildcard_domain_count bigint NOT NULL DEFAULT 0;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE cd_cert_info ADD COLUMN wildcard_domain_count integer NOT NULL DEFAULT 0;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-server",
|
||||
"version": "1.40.0",
|
||||
"version": "1.40.2",
|
||||
"description": "fast-server base midway",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -24,7 +24,7 @@
|
||||
"lint": "mwts check",
|
||||
"lint:fix": "mwts fix",
|
||||
"ci": "pnpm run cov",
|
||||
"build-only": "cross-env NODE_ENV=production mwtsc --cleanOutDir --skipLibCheck",
|
||||
"build-only": "cross-env NODE_ENV=production mwtsc -p tsconfig.build.json --cleanOutDir --skipLibCheck",
|
||||
"build": "pnpm run build-only && pnpm run export-metadata",
|
||||
"export-metadata": "node export-plugin-yaml.js",
|
||||
"export-metadata-only": "node export-plugin-yaml.js docoff",
|
||||
@@ -53,26 +53,26 @@
|
||||
"@aws-sdk/client-sts": "^3.990.0",
|
||||
"@azure/arm-dns": "^5.1.0",
|
||||
"@azure/identity": "^4.13.1",
|
||||
"@certd/acme-client": "^1.40.0",
|
||||
"@certd/basic": "^1.40.0",
|
||||
"@certd/commercial-core": "^1.40.0",
|
||||
"@certd/acme-client": "^1.40.2",
|
||||
"@certd/basic": "^1.40.2",
|
||||
"@certd/commercial-core": "^1.40.2",
|
||||
"@certd/cv4pve-api-javascript": "^8.4.2",
|
||||
"@certd/jdcloud": "^1.40.0",
|
||||
"@certd/lib-huawei": "^1.40.0",
|
||||
"@certd/lib-k8s": "^1.40.0",
|
||||
"@certd/lib-server": "^1.40.0",
|
||||
"@certd/midway-flyway-js": "^1.40.0",
|
||||
"@certd/pipeline": "^1.40.0",
|
||||
"@certd/plugin-cert": "^1.40.0",
|
||||
"@certd/plugin-lib": "^1.40.0",
|
||||
"@certd/plugin-plus": "^1.40.0",
|
||||
"@certd/plus-core": "^1.40.0",
|
||||
"@certd/jdcloud": "^1.40.2",
|
||||
"@certd/lib-huawei": "^1.40.2",
|
||||
"@certd/lib-k8s": "^1.40.2",
|
||||
"@certd/lib-server": "^1.40.2",
|
||||
"@certd/midway-flyway-js": "^1.40.2",
|
||||
"@certd/pipeline": "^1.40.2",
|
||||
"@certd/plugin-cert": "^1.40.2",
|
||||
"@certd/plugin-lib": "^1.40.2",
|
||||
"@certd/plugin-plus": "^1.40.2",
|
||||
"@certd/plus-core": "^1.40.2",
|
||||
"@google-cloud/dns": "^5.3.1",
|
||||
"@google-cloud/publicca": "^1.3.0",
|
||||
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.185",
|
||||
"@huaweicloud/huaweicloud-sdk-core": "^3.1.185",
|
||||
"@huaweicloud/huaweicloud-sdk-elb": "^3.1.185",
|
||||
"@huaweicloud/huaweicloud-sdk-iam": "^3.1.185",
|
||||
"@huaweicloud/huaweicloud-sdk-cdn": "3.1.185",
|
||||
"@huaweicloud/huaweicloud-sdk-core": "3.1.185",
|
||||
"@huaweicloud/huaweicloud-sdk-elb": "3.1.185",
|
||||
"@huaweicloud/huaweicloud-sdk-iam": "3.1.185",
|
||||
"@koa/cors": "^5.0.0",
|
||||
"@midwayjs/bootstrap": "3.20.11",
|
||||
"@midwayjs/cache": "3.14.0",
|
||||
|
||||
@@ -124,7 +124,9 @@ export class MainConfiguration {
|
||||
|
||||
this.app.getMiddleware().insertFirst(async (ctx: IMidwayKoaContext, next: NextFunction) => {
|
||||
await next();
|
||||
if (shouldSetDefaultNoCache(ctx.path, ctx.response.get('Cache-Control'))) {
|
||||
const path = ctx.path;
|
||||
// 如果是首页则强制设置为不缓存
|
||||
if (path === '/' || path === '/index.html' || shouldSetDefaultNoCache(path, ctx.response.get('Cache-Control')) ) {
|
||||
ctx.response.set('Cache-Control', 'public,max-age=0');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/// <reference types="mocha" />
|
||||
/// <reference types="node" />
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { normalizeReleaseVersion } from "./app-controller.js";
|
||||
|
||||
describe("AppController.normalizeReleaseVersion", () => {
|
||||
it("normalizes AtomGit release tag names", () => {
|
||||
assert.equal(normalizeReleaseVersion({ tag_name: "v1.40.0" }), "1.40.0");
|
||||
assert.equal(normalizeReleaseVersion({ tag_name: "1.40.0" }), "1.40.0");
|
||||
});
|
||||
|
||||
it("falls back to release name when tag_name is empty", () => {
|
||||
assert.equal(normalizeReleaseVersion({ name: "v1.40.0" }), "1.40.0");
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,11 @@ import { BaseController, Constants, FileService, SysSettingsService, SysSiteInfo
|
||||
import { http, logger } from '@certd/basic';
|
||||
import { isComm } from '@certd/plus-core';
|
||||
|
||||
export function normalizeReleaseVersion(release: { tag_name?: string; name?: string }) {
|
||||
const version = release?.tag_name || release?.name || '';
|
||||
return version.replace(/^v/i, '');
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
@Provide()
|
||||
@@ -17,12 +22,12 @@ export class AppController extends BaseController {
|
||||
async latest(): Promise<any> {
|
||||
try {
|
||||
const res = await http.request({
|
||||
url: 'https://registry.npmmirror.com/@certd/pipeline',
|
||||
url: 'https://api.atomgit.com/api/v5/repos/certd/certd/releases/latest',
|
||||
method: 'get',
|
||||
logRes: false,
|
||||
timeout: 5000,
|
||||
});
|
||||
const latest = res['dist-tags'].latest;
|
||||
const latest = normalizeReleaseVersion(res);
|
||||
return this.ok(latest);
|
||||
} catch (e: any) {
|
||||
logger.error(e);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AutoLoadPlugins } from "./auto-load-plugins.js";
|
||||
import { AutoCron } from "./auto-cron.js";
|
||||
import { AutoMitterRegister } from "./auto-mitter-register.js";
|
||||
import { AutoPipelineEmitterRegister } from "./auto-pipeline-emitter-register.js";
|
||||
import { AutoFix } from "./auto-fix.js";
|
||||
import { AutoFix } from "./fix/auto-fix.js";
|
||||
import { AutoPrint } from "./auto-print.js";
|
||||
|
||||
@Autoload()
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
import assert from "assert";
|
||||
import esmock from "esmock";
|
||||
import { AutoFix, buildEabAccountKeyValue, buildLegacyGoogleAccountConfigWhere, buildOauthBoundType, parseStorageValue } from "./auto-fix.js";
|
||||
|
||||
function createAutoFix(options: { pluginConfigService?: any; accessService?: any; storageService?: any; sysSettingsService?: any; oauthBoundService?: any }) {
|
||||
const autoFix = new AutoFix();
|
||||
autoFix.pluginConfigService = options.pluginConfigService;
|
||||
autoFix.accessService = options.accessService;
|
||||
autoFix.storageService = options.storageService;
|
||||
autoFix.sysSettingsService = options.sysSettingsService;
|
||||
autoFix.oauthBoundService = options.oauthBoundService;
|
||||
return autoFix;
|
||||
}
|
||||
|
||||
describe("AutoFix", () => {
|
||||
it("parses legacy storage values", () => {
|
||||
const config = parseStorageValue(
|
||||
JSON.stringify({
|
||||
value: {
|
||||
key: "legacy-private-key",
|
||||
accountUrl: "https://example.com/acct/1",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(config.key, "legacy-private-key");
|
||||
});
|
||||
|
||||
it("builds the EAB account key payload", () => {
|
||||
const payload = JSON.parse(buildEabAccountKeyValue("kid-1", "private-key"));
|
||||
|
||||
assert.deepEqual(payload, {
|
||||
kid: "kid-1",
|
||||
privateKey: "private-key",
|
||||
});
|
||||
});
|
||||
|
||||
it("builds legacy Google account config query by exact email key only", () => {
|
||||
assert.deepEqual(buildLegacyGoogleAccountConfigWhere("user@example.com"), {
|
||||
userId: 1,
|
||||
scope: "user",
|
||||
namespace: "1",
|
||||
key: "acme.config.google.user@example.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("builds OAuth subtype bound type", () => {
|
||||
assert.equal(buildOauthBoundType("clogin", "alipay"), "clogin:alipay");
|
||||
assert.equal(buildOauthBoundType("github"), "github");
|
||||
});
|
||||
|
||||
it("finds legacy Google account config by exact email key only", async () => {
|
||||
let findOneWhere: any;
|
||||
let findCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
async find() {
|
||||
findCalled = true;
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const config = await autoFix.getLegacyGoogleAccountConfig("user@example.com");
|
||||
|
||||
assert.equal(config.privateKey, "legacy-private-key");
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.equal(findCalled, false);
|
||||
});
|
||||
|
||||
it("does not query legacy Google account config without email", async () => {
|
||||
let repositoryCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: {
|
||||
getRepository() {
|
||||
repositoryCalled = true;
|
||||
return {};
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const config = await autoFix.getLegacyGoogleAccountConfig();
|
||||
|
||||
assert.equal(config, null);
|
||||
assert.equal(repositoryCalled, false);
|
||||
});
|
||||
|
||||
it("skips Google common EAB account key fix outside commercial edition", async () => {
|
||||
let pluginConfigCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: {
|
||||
async getPluginConfig() {
|
||||
pluginConfigCalled = true;
|
||||
return null;
|
||||
},
|
||||
} as any,
|
||||
accessService: null as any,
|
||||
storageService: null as any,
|
||||
sysSettingsService: {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {},
|
||||
};
|
||||
},
|
||||
},
|
||||
oauthBoundService: {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
async update() {
|
||||
return { affected: 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await autoFix.init();
|
||||
|
||||
assert.equal(pluginConfigCalled, false);
|
||||
});
|
||||
|
||||
it("fixes Google common EAB account key in commercial edition", async () => {
|
||||
const { AutoFix: MockedAutoFix } = await esmock("./auto-fix.js", {
|
||||
"@certd/plus-core": {
|
||||
isComm: () => true,
|
||||
},
|
||||
});
|
||||
let getAccessByIdArgs: any[] = [];
|
||||
let findOneWhere: any;
|
||||
let updateAccessParam: any;
|
||||
const autoFix = new MockedAutoFix();
|
||||
autoFix.pluginConfigService = {
|
||||
async getPluginConfig(options: any) {
|
||||
assert.deepEqual(options, {
|
||||
name: "CertApply",
|
||||
type: "builtIn",
|
||||
});
|
||||
return {
|
||||
sysSetting: {
|
||||
input: {
|
||||
googleCommonEabAccessId: 12,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
autoFix.accessService = {
|
||||
async getAccessById(...args: any[]) {
|
||||
getAccessByIdArgs = args;
|
||||
return {
|
||||
kid: "kid-1",
|
||||
email: "user@example.com",
|
||||
};
|
||||
},
|
||||
async updateAccess(param: any) {
|
||||
updateAccessParam = param;
|
||||
},
|
||||
};
|
||||
autoFix.storageService = {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
await autoFix.fixGoogleCommonEabAccountKey();
|
||||
|
||||
assert.deepEqual(getAccessByIdArgs, [12, false]);
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.deepEqual(updateAccessParam, {
|
||||
id: 12,
|
||||
eabType: "google",
|
||||
accountKey: buildEabAccountKeyValue("kid-1", "legacy-private-key"),
|
||||
});
|
||||
});
|
||||
|
||||
it("fixes legacy OAuth bound type from string addon loginType and converts loginType to array", async () => {
|
||||
const updates: any[] = [];
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: null as any,
|
||||
sysSettingsService: {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {
|
||||
clogin: {
|
||||
addonId: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
oauthBoundService: {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne(entity: any, options: any) {
|
||||
assert.equal(entity.name, "AddonEntity");
|
||||
assert.deepEqual(options, { where: { id: 1 } });
|
||||
return {
|
||||
id: 1,
|
||||
setting: JSON.stringify({
|
||||
loginType: "alipay",
|
||||
}),
|
||||
};
|
||||
},
|
||||
async update(entity: any, where: any, value: any) {
|
||||
updates.push({ entity: entity.name, where, value });
|
||||
return { affected: entity.name === "OauthBoundEntity" ? 1 : 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await autoFix.fixOauthSubtypeBoundType();
|
||||
|
||||
assert.deepEqual(updates[0], {
|
||||
entity: "OauthBoundEntity",
|
||||
where: { type: "clogin" },
|
||||
value: { type: "clogin:alipay" },
|
||||
});
|
||||
assert.equal(updates[1].entity, "AddonEntity");
|
||||
assert.deepEqual(updates[1].where, { id: 1 });
|
||||
assert.deepEqual(JSON.parse(updates[1].value.setting).loginType, ["alipay"]);
|
||||
});
|
||||
|
||||
it("skips OAuth subtype fix when addon loginType is already not legacy string", async () => {
|
||||
let updateCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: null as any,
|
||||
sysSettingsService: {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {
|
||||
clogin: {
|
||||
addonId: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
oauthBoundService: {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne() {
|
||||
return {
|
||||
id: 1,
|
||||
setting: JSON.stringify({
|
||||
loginType: ["alipay", "github"],
|
||||
}),
|
||||
};
|
||||
},
|
||||
async update() {
|
||||
updateCalled = true;
|
||||
return { affected: 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await autoFix.fixOauthSubtypeBoundType();
|
||||
|
||||
assert.equal(updateCalled, false);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import assert from "assert";
|
||||
import { AutoFix } from "./auto-fix.js";
|
||||
|
||||
describe("AutoFix", () => {
|
||||
it("runs unfinished fix tasks in order and marks them fixed", async () => {
|
||||
const calls: string[] = [];
|
||||
let savedSetting: any;
|
||||
const autoFix = new AutoFix();
|
||||
autoFix.sysSettingsService = {
|
||||
async getSetting() {
|
||||
return {
|
||||
fixed: {
|
||||
"oauth-subtype-bound-type": true,
|
||||
},
|
||||
};
|
||||
},
|
||||
async saveSetting(setting: any) {
|
||||
savedSetting = {
|
||||
fixed: { ...setting.fixed },
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
autoFix.googleCommonEabAccountKeyFix = {
|
||||
async init() {
|
||||
calls.push("google");
|
||||
},
|
||||
} as any;
|
||||
autoFix.oauthSubtypeBoundTypeFix = {
|
||||
async init() {
|
||||
calls.push("oauth");
|
||||
},
|
||||
} as any;
|
||||
autoFix.certInfoWildcardDomainCountFix = {
|
||||
async init() {
|
||||
calls.push("cert");
|
||||
},
|
||||
} as any;
|
||||
autoFix.suiteContentWildcardDomainCountFix = {
|
||||
async init() {
|
||||
calls.push("suite");
|
||||
},
|
||||
} as any;
|
||||
|
||||
await autoFix.init();
|
||||
|
||||
assert.deepEqual(calls, ["google", "cert", "suite"]);
|
||||
assert.equal(savedSetting.fixed["google-common-eab-account-key"], true);
|
||||
assert.equal(savedSetting.fixed["oauth-subtype-bound-type"], true);
|
||||
assert.equal(savedSetting.fixed["cert-info-wildcard-domain-count"], true);
|
||||
assert.equal(savedSetting.fixed["suite-content-wildcard-domain-count"], true);
|
||||
});
|
||||
|
||||
it("initializes missing fixed map", async () => {
|
||||
const autoFix = new AutoFix();
|
||||
autoFix.sysSettingsService = {
|
||||
async getSetting() {
|
||||
return {};
|
||||
},
|
||||
async saveSetting() {},
|
||||
} as any;
|
||||
autoFix.googleCommonEabAccountKeyFix = { async init() {} } as any;
|
||||
autoFix.oauthSubtypeBoundTypeFix = { async init() {} } as any;
|
||||
autoFix.certInfoWildcardDomainCountFix = { async init() {} } as any;
|
||||
autoFix.suiteContentWildcardDomainCountFix = { async init() {} } as any;
|
||||
|
||||
await autoFix.init();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { SysAutoFixSetting, SysSettingsService } from "@certd/lib-server";
|
||||
import { GoogleCommonEabAccountKeyFix } from "./google-common-eab-account-key-fix.js";
|
||||
import { OauthSubtypeBoundTypeFix } from "./oauth-subtype-bound-type-fix.js";
|
||||
import { CertInfoWildcardDomainCountFix } from "./cert-info-wildcard-domain-count-fix.js";
|
||||
import { SuiteContentWildcardDomainCountFix } from "./suite-content-wildcard-domain-count-fix.js";
|
||||
|
||||
type AutoFixTask = {
|
||||
key: string;
|
||||
fix: {
|
||||
init(): Promise<void>;
|
||||
};
|
||||
};
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoFix {
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
googleCommonEabAccountKeyFix: GoogleCommonEabAccountKeyFix;
|
||||
|
||||
@Inject()
|
||||
oauthSubtypeBoundTypeFix: OauthSubtypeBoundTypeFix;
|
||||
|
||||
@Inject()
|
||||
certInfoWildcardDomainCountFix: CertInfoWildcardDomainCountFix;
|
||||
|
||||
@Inject()
|
||||
suiteContentWildcardDomainCountFix: SuiteContentWildcardDomainCountFix;
|
||||
|
||||
async init() {
|
||||
const setting = await this.sysSettingsService.getSetting<SysAutoFixSetting>(SysAutoFixSetting);
|
||||
setting.fixed = setting.fixed || {};
|
||||
const tasks: AutoFixTask[] = [
|
||||
{
|
||||
key: "google-common-eab-account-key",
|
||||
fix: this.googleCommonEabAccountKeyFix,
|
||||
},
|
||||
{
|
||||
key: "oauth-subtype-bound-type",
|
||||
fix: this.oauthSubtypeBoundTypeFix,
|
||||
},
|
||||
{
|
||||
key: "cert-info-wildcard-domain-count",
|
||||
fix: this.certInfoWildcardDomainCountFix,
|
||||
},
|
||||
{
|
||||
key: "suite-content-wildcard-domain-count",
|
||||
fix: this.suiteContentWildcardDomainCountFix,
|
||||
},
|
||||
];
|
||||
|
||||
for (const task of tasks) {
|
||||
if (setting.fixed?.[task.key]) {
|
||||
continue;
|
||||
}
|
||||
await task.fix.init();
|
||||
setting.fixed[task.key] = true;
|
||||
await this.sysSettingsService.saveSetting(setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import assert from "assert";
|
||||
import { CertInfoWildcardDomainCountFix } from "./cert-info-wildcard-domain-count-fix.js";
|
||||
|
||||
describe("CertInfoWildcardDomainCountFix", () => {
|
||||
it("fixes cert info wildcard domain count only when value changed", async () => {
|
||||
const updated: any[] = [];
|
||||
const rows = [
|
||||
{ id: 1, domains: "*.a.com,a.com, *.b.com ", wildcardDomainCount: 0 },
|
||||
{ id: 2, domains: "c.com", wildcardDomainCount: 0 },
|
||||
{ id: 3, domains: "*.d.com", wildcardDomainCount: 1 },
|
||||
];
|
||||
const fix = new CertInfoWildcardDomainCountFix();
|
||||
fix.certInfoService = {
|
||||
countWildcardDomains(domains: string[]) {
|
||||
return domains.filter(item => item.trim().toLowerCase().startsWith("*.")).length;
|
||||
},
|
||||
async find() {
|
||||
return rows;
|
||||
},
|
||||
async update(value: any) {
|
||||
updated.push(value);
|
||||
const row = rows.find(item => item.id === value.id);
|
||||
Object.assign(row, value);
|
||||
},
|
||||
} as any;
|
||||
|
||||
await fix.init();
|
||||
await fix.init();
|
||||
|
||||
assert.deepEqual(updated, [
|
||||
{
|
||||
id: 1,
|
||||
wildcardDomainCount: 2,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { CertInfoService } from "../../monitor/service/cert-info-service.js";
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class CertInfoWildcardDomainCountFix {
|
||||
@Inject()
|
||||
certInfoService: CertInfoService;
|
||||
|
||||
async init() {
|
||||
if (!this.certInfoService) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const list = await this.certInfoService.find({
|
||||
select: {
|
||||
id: true,
|
||||
domains: true,
|
||||
wildcardDomainCount: true,
|
||||
},
|
||||
});
|
||||
let fixedCount = 0;
|
||||
for (const item of list) {
|
||||
if (!item.domains) {
|
||||
continue;
|
||||
}
|
||||
const wildcardDomainCount = this.certInfoService.countWildcardDomains(item.domains.split(","));
|
||||
if ((item.wildcardDomainCount ?? 0) === wildcardDomainCount) {
|
||||
continue;
|
||||
}
|
||||
await this.certInfoService.update({
|
||||
id: item.id,
|
||||
wildcardDomainCount,
|
||||
});
|
||||
fixedCount++;
|
||||
}
|
||||
if (fixedCount > 0) {
|
||||
logger.info(`已修复证书泛域名数量历史数据,数量=${fixedCount}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
logger.error("修复证书泛域名数量历史数据失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
import assert from "assert";
|
||||
import esmock from "esmock";
|
||||
import { buildEabAccountKeyValue, buildLegacyGoogleAccountConfigWhere, GoogleCommonEabAccountKeyFix, parseStorageValue } from "./google-common-eab-account-key-fix.js";
|
||||
|
||||
describe("GoogleCommonEabAccountKeyFix", () => {
|
||||
it("parses legacy storage values", () => {
|
||||
const config = parseStorageValue(
|
||||
JSON.stringify({
|
||||
value: {
|
||||
key: "legacy-private-key",
|
||||
accountUrl: "https://example.com/acct/1",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(config.key, "legacy-private-key");
|
||||
});
|
||||
|
||||
it("builds the EAB account key payload", () => {
|
||||
const payload = JSON.parse(buildEabAccountKeyValue("kid-1", "private-key"));
|
||||
|
||||
assert.deepEqual(payload, {
|
||||
kid: "kid-1",
|
||||
privateKey: "private-key",
|
||||
});
|
||||
});
|
||||
|
||||
it("builds legacy Google account config query by exact email key only", () => {
|
||||
assert.deepEqual(buildLegacyGoogleAccountConfigWhere("user@example.com"), {
|
||||
userId: 1,
|
||||
scope: "user",
|
||||
namespace: "1",
|
||||
key: "acme.config.google.user@example.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("finds legacy Google account config by exact email key only", async () => {
|
||||
let findOneWhere: any;
|
||||
let findCalled = false;
|
||||
const fix = new GoogleCommonEabAccountKeyFix();
|
||||
fix.storageService = {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
async find() {
|
||||
findCalled = true;
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
|
||||
const config = await fix.getLegacyGoogleAccountConfig("user@example.com");
|
||||
|
||||
assert.equal(config.privateKey, "legacy-private-key");
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.equal(findCalled, false);
|
||||
});
|
||||
|
||||
it("does not query legacy Google account config without email", async () => {
|
||||
let repositoryCalled = false;
|
||||
const fix = new GoogleCommonEabAccountKeyFix();
|
||||
fix.storageService = {
|
||||
getRepository() {
|
||||
repositoryCalled = true;
|
||||
return {};
|
||||
},
|
||||
} as any;
|
||||
|
||||
const config = await fix.getLegacyGoogleAccountConfig();
|
||||
|
||||
assert.equal(config, null);
|
||||
assert.equal(repositoryCalled, false);
|
||||
});
|
||||
|
||||
it("skips Google common EAB account key fix outside commercial edition", async () => {
|
||||
const { GoogleCommonEabAccountKeyFix: MockedFix } = await esmock("./google-common-eab-account-key-fix.js", {
|
||||
"@certd/plus-core": {
|
||||
isComm: () => false,
|
||||
},
|
||||
});
|
||||
let pluginConfigCalled = false;
|
||||
const fix = new MockedFix();
|
||||
fix.pluginConfigService = {
|
||||
async getPluginConfig() {
|
||||
pluginConfigCalled = true;
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
await fix.init();
|
||||
|
||||
assert.equal(pluginConfigCalled, false);
|
||||
});
|
||||
|
||||
it("fixes Google common EAB account key in commercial edition", async () => {
|
||||
const { GoogleCommonEabAccountKeyFix: MockedFix } = await esmock("./google-common-eab-account-key-fix.js", {
|
||||
"@certd/plus-core": {
|
||||
isComm: () => true,
|
||||
},
|
||||
});
|
||||
let getAccessByIdArgs: any[] = [];
|
||||
let findOneWhere: any;
|
||||
let updateAccessParam: any;
|
||||
const fix = new MockedFix();
|
||||
fix.pluginConfigService = {
|
||||
async getPluginConfig(options: any) {
|
||||
assert.deepEqual(options, {
|
||||
name: "CertApply",
|
||||
type: "builtIn",
|
||||
});
|
||||
return {
|
||||
sysSetting: {
|
||||
input: {
|
||||
googleCommonEabAccessId: 12,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
fix.accessService = {
|
||||
async getAccessById(...args: any[]) {
|
||||
getAccessByIdArgs = args;
|
||||
return {
|
||||
kid: "kid-1",
|
||||
email: "user@example.com",
|
||||
};
|
||||
},
|
||||
async updateAccess(param: any) {
|
||||
updateAccessParam = param;
|
||||
},
|
||||
};
|
||||
fix.storageService = {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
await fix.init();
|
||||
|
||||
assert.deepEqual(getAccessByIdArgs, [12, false]);
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.deepEqual(updateAccessParam, {
|
||||
id: 12,
|
||||
eabType: "google",
|
||||
accountKey: buildEabAccountKeyValue("kid-1", "legacy-private-key"),
|
||||
});
|
||||
});
|
||||
});
|
||||
+5
-71
@@ -1,11 +1,9 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { AccessService, AddonEntity, SysSettingsService } from "@certd/lib-server";
|
||||
import { AccessService } from "@certd/lib-server";
|
||||
import { isComm } from "@certd/plus-core";
|
||||
import { PluginConfigService } from "../plugin/service/plugin-config-service.js";
|
||||
import { StorageService } from "../pipeline/service/storage-service.js";
|
||||
import { OauthBoundService } from "../login/service/oauth-bound-service.js";
|
||||
import { OauthBoundEntity } from "../login/entity/oauth-bound.js";
|
||||
import { PluginConfigService } from "../../plugin/service/plugin-config-service.js";
|
||||
import { StorageService } from "../../pipeline/service/storage-service.js";
|
||||
|
||||
export function parseStorageValue(value?: string) {
|
||||
if (!value) {
|
||||
@@ -35,13 +33,9 @@ export function buildLegacyGoogleAccountConfigWhere(email: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildOauthBoundType(type: string, subtype?: string) {
|
||||
return subtype ? `${type}:${subtype}` : type;
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoFix {
|
||||
export class GoogleCommonEabAccountKeyFix {
|
||||
@Inject()
|
||||
pluginConfigService: PluginConfigService;
|
||||
|
||||
@@ -51,67 +45,7 @@ export class AutoFix {
|
||||
@Inject()
|
||||
storageService: StorageService;
|
||||
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
oauthBoundService: OauthBoundService;
|
||||
|
||||
async init() {
|
||||
await this.fixGoogleCommonEabAccountKey();
|
||||
await this.fixOauthSubtypeBoundType();
|
||||
}
|
||||
|
||||
async fixOauthSubtypeBoundType() {
|
||||
try {
|
||||
const publicSettings = await this.sysSettingsService.getPublicSettings();
|
||||
const oauthProviders = publicSettings.oauthProviders || {};
|
||||
await this.oauthBoundService.transaction(async manager => {
|
||||
for (const [type, provider] of Object.entries(oauthProviders)) {
|
||||
if (!provider.addonId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const addonEntity = await manager.findOne(AddonEntity, { where: { id: provider.addonId } });
|
||||
const legacyLoginType = this.getLegacyAddonLoginType(addonEntity?.setting);
|
||||
if (!legacyLoginType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newType = buildOauthBoundType(type, legacyLoginType);
|
||||
const res = await manager.update(OauthBoundEntity, { type }, { type: newType });
|
||||
if (res.affected) {
|
||||
logger.info(`已修复OAuth绑定历史数据,${type} -> ${newType},数量=${res.affected}`);
|
||||
}
|
||||
await this.convertLegacyAddonLoginTypeToArray(addonEntity, legacyLoginType, manager);
|
||||
}
|
||||
});
|
||||
} catch (e: any) {
|
||||
logger.error("修复OAuth subtype绑定历史数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private getLegacyAddonLoginType(settingValue?: string) {
|
||||
if (!settingValue) {
|
||||
return null;
|
||||
}
|
||||
const setting = JSON.parse(settingValue);
|
||||
return typeof setting.loginType === "string" && setting.loginType ? setting.loginType : null;
|
||||
}
|
||||
|
||||
private async convertLegacyAddonLoginTypeToArray(addonEntity: AddonEntity | null, loginType: string, manager: any) {
|
||||
if (!addonEntity?.setting) {
|
||||
return;
|
||||
}
|
||||
const setting = JSON.parse(addonEntity.setting);
|
||||
if (typeof setting.loginType !== "string") {
|
||||
return;
|
||||
}
|
||||
setting.loginType = [loginType];
|
||||
await manager.update(AddonEntity, { id: addonEntity.id }, { setting: JSON.stringify(setting) });
|
||||
}
|
||||
|
||||
async fixGoogleCommonEabAccountKey() {
|
||||
if (!isComm()) {
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +97,7 @@ export class AutoFix {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
parseStorageValue(value?: string) {
|
||||
return parseStorageValue(value);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import assert from "assert";
|
||||
import { buildOauthBoundType, OauthSubtypeBoundTypeFix } from "./oauth-subtype-bound-type-fix.js";
|
||||
|
||||
describe("OauthSubtypeBoundTypeFix", () => {
|
||||
it("builds OAuth subtype bound type", () => {
|
||||
assert.equal(buildOauthBoundType("clogin", "alipay"), "clogin:alipay");
|
||||
assert.equal(buildOauthBoundType("github"), "github");
|
||||
});
|
||||
|
||||
it("fixes legacy OAuth bound type from string addon loginType and converts loginType to array", async () => {
|
||||
const updates: any[] = [];
|
||||
const fix = new OauthSubtypeBoundTypeFix();
|
||||
fix.sysSettingsService = {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {
|
||||
clogin: {
|
||||
addonId: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
fix.oauthBoundService = {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne(entity: any, options: any) {
|
||||
assert.equal(entity.name, "AddonEntity");
|
||||
assert.deepEqual(options, { where: { id: 1 } });
|
||||
return {
|
||||
id: 1,
|
||||
setting: JSON.stringify({
|
||||
loginType: "alipay",
|
||||
}),
|
||||
};
|
||||
},
|
||||
async update(entity: any, where: any, value: any) {
|
||||
updates.push({ entity: entity.name, where, value });
|
||||
return { affected: entity.name === "OauthBoundEntity" ? 1 : 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
} as any;
|
||||
|
||||
await fix.init();
|
||||
|
||||
assert.deepEqual(updates[0], {
|
||||
entity: "OauthBoundEntity",
|
||||
where: { type: "clogin" },
|
||||
value: { type: "clogin:alipay" },
|
||||
});
|
||||
assert.equal(updates[1].entity, "AddonEntity");
|
||||
assert.deepEqual(updates[1].where, { id: 1 });
|
||||
assert.deepEqual(JSON.parse(updates[1].value.setting).loginType, ["alipay"]);
|
||||
});
|
||||
|
||||
it("skips OAuth subtype fix when addon loginType is already not legacy string", async () => {
|
||||
let updateCalled = false;
|
||||
const fix = new OauthSubtypeBoundTypeFix();
|
||||
fix.sysSettingsService = {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {
|
||||
clogin: {
|
||||
addonId: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
fix.oauthBoundService = {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne() {
|
||||
return {
|
||||
id: 1,
|
||||
setting: JSON.stringify({
|
||||
loginType: ["alipay", "github"],
|
||||
}),
|
||||
};
|
||||
},
|
||||
async update() {
|
||||
updateCalled = true;
|
||||
return { affected: 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
} as any;
|
||||
|
||||
await fix.init();
|
||||
|
||||
assert.equal(updateCalled, false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { AddonEntity, SysSettingsService } from "@certd/lib-server";
|
||||
import { OauthBoundService } from "../../login/service/oauth-bound-service.js";
|
||||
import { OauthBoundEntity } from "../../login/entity/oauth-bound.js";
|
||||
|
||||
export function buildOauthBoundType(type: string, subtype?: string) {
|
||||
return subtype ? `${type}:${subtype}` : type;
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class OauthSubtypeBoundTypeFix {
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
oauthBoundService: OauthBoundService;
|
||||
|
||||
async init() {
|
||||
try {
|
||||
const publicSettings = await this.sysSettingsService.getPublicSettings();
|
||||
const oauthProviders = publicSettings.oauthProviders || {};
|
||||
await this.oauthBoundService.transaction(async manager => {
|
||||
for (const [type, provider] of Object.entries(oauthProviders)) {
|
||||
if (!provider.addonId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const addonEntity = await manager.findOne(AddonEntity, { where: { id: provider.addonId } });
|
||||
const legacyLoginType = this.getLegacyAddonLoginType(addonEntity?.setting);
|
||||
if (!legacyLoginType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newType = buildOauthBoundType(type, legacyLoginType);
|
||||
const res = await manager.update(OauthBoundEntity, { type }, { type: newType });
|
||||
if (res.affected) {
|
||||
logger.info(`已修复OAuth绑定历史数据,${type} -> ${newType},数量=${res.affected}`);
|
||||
}
|
||||
await this.convertLegacyAddonLoginTypeToArray(addonEntity, legacyLoginType, manager);
|
||||
}
|
||||
});
|
||||
} catch (e: any) {
|
||||
logger.error("修复OAuth subtype绑定历史数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private getLegacyAddonLoginType(settingValue?: string) {
|
||||
if (!settingValue) {
|
||||
return null;
|
||||
}
|
||||
const setting = JSON.parse(settingValue);
|
||||
return typeof setting.loginType === "string" && setting.loginType ? setting.loginType : null;
|
||||
}
|
||||
|
||||
private async convertLegacyAddonLoginTypeToArray(addonEntity: AddonEntity | null, loginType: string, manager: any) {
|
||||
if (!addonEntity?.setting) {
|
||||
return;
|
||||
}
|
||||
const setting = JSON.parse(addonEntity.setting);
|
||||
if (typeof setting.loginType !== "string") {
|
||||
return;
|
||||
}
|
||||
setting.loginType = [loginType];
|
||||
await manager.update(AddonEntity, { id: addonEntity.id }, { setting: JSON.stringify(setting) });
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import assert from "assert";
|
||||
import { fixSuiteContentWildcardDomainCount, SuiteContentWildcardDomainCountFix } from "./suite-content-wildcard-domain-count-fix.js";
|
||||
|
||||
describe("SuiteContentWildcardDomainCountFix", () => {
|
||||
it("fills missing suite wildcard domain count from total domain count", () => {
|
||||
const fixed = fixSuiteContentWildcardDomainCount(
|
||||
JSON.stringify({
|
||||
maxDomainCount: 10,
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(JSON.parse(fixed).maxWildcardDomainCount, 10);
|
||||
assert.equal(
|
||||
JSON.parse(
|
||||
fixSuiteContentWildcardDomainCount(
|
||||
JSON.stringify({
|
||||
maxDomainCount: -1,
|
||||
})
|
||||
)
|
||||
).maxWildcardDomainCount,
|
||||
-1
|
||||
);
|
||||
assert.equal(
|
||||
fixSuiteContentWildcardDomainCount(
|
||||
JSON.stringify({
|
||||
maxDomainCount: 10,
|
||||
maxWildcardDomainCount: 3,
|
||||
})
|
||||
),
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
it("fixes suite content wildcard domain count in product and user suite tables", async () => {
|
||||
const rows = {
|
||||
cd_product: [
|
||||
{ id: 1, content: JSON.stringify({ maxDomainCount: 1 }) },
|
||||
{ id: 2, content: JSON.stringify({ maxDomainCount: 1, maxWildcardDomainCount: 0 }) },
|
||||
],
|
||||
cd_user_suite: [{ id: 3, content: JSON.stringify({ maxDomainCount: 2 }) }],
|
||||
};
|
||||
const updates: any[] = [];
|
||||
const entityManager = {
|
||||
async query(sql: string) {
|
||||
const table = sql.includes("cd_product") ? "cd_product" : "cd_user_suite";
|
||||
return rows[table];
|
||||
},
|
||||
async update(tableName: string, where: any, value: any) {
|
||||
updates.push({ tableName, where, value });
|
||||
const row = rows[tableName].find((item: any) => item.id === where.id);
|
||||
Object.assign(row, value);
|
||||
},
|
||||
};
|
||||
const fix = new SuiteContentWildcardDomainCountFix();
|
||||
fix.dataSourceManager = {
|
||||
getDataSource() {
|
||||
return {
|
||||
manager: entityManager,
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
|
||||
await fix.init();
|
||||
await fix.init();
|
||||
|
||||
assert.equal(updates.length, 2);
|
||||
assert.equal(updates[0].tableName, "cd_product");
|
||||
assert.equal(JSON.parse(updates[0].value.content).maxWildcardDomainCount, 1);
|
||||
assert.equal(updates[1].tableName, "cd_user_suite");
|
||||
assert.equal(JSON.parse(updates[1].value.content).maxWildcardDomainCount, 2);
|
||||
});
|
||||
});
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
||||
|
||||
export function fixSuiteContentWildcardDomainCount(contentValue?: string) {
|
||||
if (!contentValue) {
|
||||
return null;
|
||||
}
|
||||
const content = JSON.parse(contentValue);
|
||||
if (content.maxWildcardDomainCount != null) {
|
||||
return null;
|
||||
}
|
||||
content.maxWildcardDomainCount = content.maxDomainCount == null || content.maxDomainCount === -1 ? -1 : content.maxDomainCount;
|
||||
return JSON.stringify(content);
|
||||
}
|
||||
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class SuiteContentWildcardDomainCountFix {
|
||||
@Inject()
|
||||
dataSourceManager: TypeORMDataSourceManager;
|
||||
|
||||
async init() {
|
||||
if (!this.dataSourceManager) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const entityManager = this.dataSourceManager.getDataSource("default").manager;
|
||||
let fixedCount = 0;
|
||||
fixedCount += await this.fixSuiteContentWildcardDomainCountByTable(entityManager, "cd_product");
|
||||
fixedCount += await this.fixSuiteContentWildcardDomainCountByTable(entityManager, "cd_user_suite");
|
||||
if (fixedCount > 0) {
|
||||
logger.info(`已修复套餐最大泛域名数量历史数据,数量=${fixedCount}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
logger.error("修复套餐最大泛域名数量历史数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private async fixSuiteContentWildcardDomainCountByTable(entityManager: any, tableName: string) {
|
||||
const list = await entityManager.query(`select id, content from ${tableName}`);
|
||||
let fixedCount = 0;
|
||||
for (const item of list) {
|
||||
const content = fixSuiteContentWildcardDomainCount(item.content);
|
||||
if (!content) {
|
||||
continue;
|
||||
}
|
||||
await entityManager.update(tableName, { id: item.id }, { content });
|
||||
fixedCount++;
|
||||
}
|
||||
return fixedCount;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { isComm, isPlus } from '@certd/plus-core';
|
||||
import nodemailer from 'nodemailer';
|
||||
import { SendMailOptions } from 'nodemailer';
|
||||
import { UserSettingsService } from '../../mine/service/user-settings-service.js';
|
||||
import { AddonService, PlusService, SysEmailConf, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
|
||||
import { AddonService, PlusService, SysEmailConf, SysInstallInfo, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
|
||||
import { getEmailSettings } from '../../sys/settings/fix.js';
|
||||
import { UserEmailSetting } from "../../mine/service/models.js";
|
||||
import { AddonGetterService } from '../../pipeline/service/addon-getter-service.js';
|
||||
@@ -131,12 +131,21 @@ export class EmailService implements IEmailService {
|
||||
data: {
|
||||
title: '测试邮件,from certd',
|
||||
content: '测试邮件,from certd',
|
||||
url: "https://certd.handfree.work",
|
||||
url: await this.getTestEmailUrl(),
|
||||
},
|
||||
receivers: [receiver],
|
||||
});
|
||||
}
|
||||
|
||||
private async getTestEmailUrl() {
|
||||
const defaultUrl = "https://certd.docmirror.cn";
|
||||
if (!isComm()) {
|
||||
return defaultUrl;
|
||||
}
|
||||
const installInfo = await this.sysSettingsService.getSetting<SysInstallInfo>(SysInstallInfo);
|
||||
return installInfo?.bindUrl || installInfo?.bindUrl2 || defaultUrl;
|
||||
}
|
||||
|
||||
async list(userId: any) {
|
||||
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId,null, UserEmailSetting)
|
||||
return userEmailSetting.list;
|
||||
@@ -157,7 +166,6 @@ export class EmailService implements IEmailService {
|
||||
await this.settingsService.saveSetting(userId, null, userEmailSetting)
|
||||
}
|
||||
|
||||
|
||||
async sendByTemplate(req: EmailSendByTemplateReq) {
|
||||
let content = null
|
||||
const emailConf = await this.sysSettingsService.getSetting<SysEmailConf>(SysEmailConf);
|
||||
|
||||
@@ -18,6 +18,9 @@ export class CertInfoEntity {
|
||||
@Column({ name: 'domain_count', comment: '域名数量' })
|
||||
domainCount: number;
|
||||
|
||||
@Column({ name: 'wildcard_domain_count', comment: '泛域名数量', default: 0 })
|
||||
wildcardDomainCount: number;
|
||||
|
||||
@Column({ name: 'pipeline_id', comment: '关联流水线id' })
|
||||
pipelineId: number;
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import assert from "assert";
|
||||
import { CertInfoService } from "./cert-info-service.js";
|
||||
|
||||
describe("CertInfoService", () => {
|
||||
it("counts wildcard domains by normalized prefix", () => {
|
||||
const service = new CertInfoService();
|
||||
|
||||
assert.equal(service.countWildcardDomains(["*.a.com", "a.com", " *.B.com "]), 2);
|
||||
});
|
||||
|
||||
it("saves wildcard domain count when updating pipeline domains", async () => {
|
||||
const service = new CertInfoService();
|
||||
let saved: any;
|
||||
service.repository = {
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
} as any;
|
||||
service.addOrUpdate = async (bean: any) => {
|
||||
saved = bean;
|
||||
return bean;
|
||||
};
|
||||
|
||||
await service.updateDomains(1, 2, null, ["*.a.com", "a.com", "*.b.com"], "pipeline");
|
||||
|
||||
assert.equal(saved.domainCount, 3);
|
||||
assert.equal(saved.wildcardDomainCount, 2);
|
||||
});
|
||||
});
|
||||
@@ -40,6 +40,22 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
});
|
||||
}
|
||||
|
||||
async getUserWildcardDomainCount(userId: number) {
|
||||
if (userId == null) {
|
||||
throw new Error('userId is required');
|
||||
}
|
||||
return await this.repository.sum('wildcardDomainCount', {
|
||||
userId,
|
||||
});
|
||||
}
|
||||
|
||||
countWildcardDomains(domains?: string[]) {
|
||||
if (!domains) {
|
||||
return 0;
|
||||
}
|
||||
return domains.filter(domain => domain?.trim().toLowerCase().startsWith("*.")).length;
|
||||
}
|
||||
|
||||
async updateDomains(pipelineId: number, userId: number, projectId: number, domains: string[],fromType?:string) {
|
||||
const found = await this.repository.findOne({
|
||||
where: {
|
||||
@@ -67,10 +83,12 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
bean.domain = '';
|
||||
bean.domains = '';
|
||||
bean.domainCount = 0;
|
||||
bean.wildcardDomainCount = 0;
|
||||
} else {
|
||||
bean.domain = domains[0];
|
||||
bean.domains = domains.join(',');
|
||||
bean.domainCount = domains.length;
|
||||
bean.wildcardDomainCount = this.countWildcardDomains(domains);
|
||||
}
|
||||
|
||||
await this.addOrUpdate(bean);
|
||||
@@ -171,6 +189,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
bean.domains = domains.join(',');
|
||||
bean.domain = domains[0];
|
||||
bean.domainCount = domains.length;
|
||||
bean.wildcardDomainCount = this.countWildcardDomains(domains);
|
||||
bean.effectiveTime = certReader.effective;
|
||||
bean.expiresTime = certReader.expires;
|
||||
bean.certProvider = certReader.detail.issuer.commonName;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core";
|
||||
import {BaseService, Constants, NeedSuiteException, NeedVIPException, SysSettingsService} from "@certd/lib-server";
|
||||
import {BaseService, Constants, isEnterprise, NeedSuiteException, NeedVIPException, SysSettingsService} from "@certd/lib-server";
|
||||
import {InjectEntityModel} from "@midwayjs/typeorm";
|
||||
import {In, Repository} from "typeorm";
|
||||
import {SiteInfoEntity} from "../entity/site-info.js";
|
||||
@@ -59,25 +59,35 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async add(data: SiteInfoEntity) {
|
||||
if (data.userId == null) {
|
||||
throw new Error("userId is required");
|
||||
async checkMonitorLimit(userId: number) {
|
||||
if (isEnterprise()) {
|
||||
//企业模式不限制
|
||||
return;
|
||||
}
|
||||
|
||||
if (isComm()) {
|
||||
const suiteSetting = await this.userSuiteService.getSuiteSetting();
|
||||
if (suiteSetting.enabled) {
|
||||
const userSuite = await this.userSuiteService.getMySuiteDetail(data.userId);
|
||||
const userSuite = await this.userSuiteService.getMySuiteDetail(userId);
|
||||
if (userSuite.monitorCount.max != -1 && userSuite.monitorCount.max <= userSuite.monitorCount.used) {
|
||||
throw new NeedSuiteException("站点监控数量已达上限,请购买或升级套餐");
|
||||
}
|
||||
}
|
||||
} else if (!isPlus()) {
|
||||
const count = await this.getUserMonitorCount(data.userId);
|
||||
const count = await this.getUserMonitorCount(userId);
|
||||
if (count >= 1) {
|
||||
throw new NeedVIPException("站点监控数量已达上限,请升级专业版");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async add(data: SiteInfoEntity) {
|
||||
if (data.userId == null) {
|
||||
throw new Error("userId is required");
|
||||
}
|
||||
|
||||
await this.checkMonitorLimit(data.userId);
|
||||
|
||||
data.disabled = false;
|
||||
|
||||
const found = await this.repository.findOne({
|
||||
|
||||
@@ -53,6 +53,7 @@ import { executorQueue } from "@certd/lib-server";
|
||||
import parser from "cron-parser";
|
||||
import { ProjectService } from "../../sys/enterprise/service/project-service.js";
|
||||
import { CertApplyStepInputPatch, updateCertApplyStepInputs } from "./pipeline-batch-update.js";
|
||||
import { calcNextSuiteCountUsed } from "./pipeline-suite-limit.js";
|
||||
const runningTasks: Map<string | number, Executor> = new Map();
|
||||
|
||||
|
||||
@@ -76,7 +77,6 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
historyService: HistoryService;
|
||||
@Inject()
|
||||
historyLogService: HistoryLogService;
|
||||
|
||||
@Inject()
|
||||
pluginConfigGetter: PluginConfigGetter;
|
||||
|
||||
@@ -287,10 +287,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
});
|
||||
}
|
||||
|
||||
if (!isUpdate) {
|
||||
//如果是添加,校验数量
|
||||
await this.checkMaxPipelineCount(bean, pipeline, domains);
|
||||
}
|
||||
await this.checkMaxPipelineCount(bean, pipeline, domains, old);
|
||||
|
||||
if (!bean.status) {
|
||||
bean.status = ResultType.none;
|
||||
@@ -345,7 +342,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
return bean
|
||||
}
|
||||
|
||||
private async checkMaxPipelineCount(bean: PipelineEntity, pipeline: Pipeline, domains: string[]) {
|
||||
private async checkMaxPipelineCount(bean: PipelineEntity, pipeline: Pipeline, domains: string[], old?: PipelineEntity) {
|
||||
// if (!isPlus()) {
|
||||
// const count = await this.repository.count();
|
||||
// if (count >= freeCount) {
|
||||
@@ -363,15 +360,31 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
const suiteSetting = await this.userSuiteService.getSuiteSetting();
|
||||
if (suiteSetting.enabled) {
|
||||
const userSuite = await this.userSuiteService.getMySuiteDetail(bean.userId);
|
||||
if (userSuite?.pipelineCount.max != -1 && userSuite?.pipelineCount.used + 1 > userSuite?.pipelineCount.max) {
|
||||
if (!old && userSuite?.pipelineCount.max != -1 && userSuite?.pipelineCount.used + 1 > userSuite?.pipelineCount.max) {
|
||||
throw new NeedSuiteException(`对不起,您最多只能创建${userSuite?.pipelineCount.max}条流水线,请购买或升级套餐`);
|
||||
}
|
||||
|
||||
if (userSuite.domainCount.max != -1 && userSuite.domainCount.used + domains.length > userSuite.domainCount.max) {
|
||||
let oldDomainCount = 0;
|
||||
let oldWildcardDomainCount = 0;
|
||||
if (old?.id) {
|
||||
const oldCertInfo = await this.certInfoService.getByPipelineId(old.id);
|
||||
oldDomainCount = oldCertInfo?.domainCount ?? 0;
|
||||
oldWildcardDomainCount = oldCertInfo?.wildcardDomainCount ?? 0;
|
||||
}
|
||||
|
||||
const nextDomainCountUsed = calcNextSuiteCountUsed(userSuite.domainCount.used, oldDomainCount, domains.length);
|
||||
if (userSuite.domainCount.max != -1 && nextDomainCountUsed > userSuite.domainCount.max) {
|
||||
throw new NeedSuiteException(`对不起,您最多只能添加${userSuite.domainCount.max}个域名,请购买或升级套餐`);
|
||||
}
|
||||
|
||||
const suiteWildcardDomainCount = userSuite.wildcardDomainCount;
|
||||
const wildcardDomainCount = this.certInfoService.countWildcardDomains(domains);
|
||||
const nextWildcardDomainCountUsed = calcNextSuiteCountUsed(suiteWildcardDomainCount.used, oldWildcardDomainCount, wildcardDomainCount);
|
||||
if (suiteWildcardDomainCount.max != -1 && nextWildcardDomainCountUsed > suiteWildcardDomainCount.max) {
|
||||
throw new NeedSuiteException(`对不起,您最多只能添加${suiteWildcardDomainCount.max}个泛域名,请购买或升级套餐`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (!old) {
|
||||
//非商业版校验用户最大流水线数量
|
||||
const userId = bean.userId;
|
||||
const userIsAdmin = await this.userService.isAdmin(userId);
|
||||
@@ -1332,7 +1345,6 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
}
|
||||
|
||||
async createAutoPipeline(req: { domains: string[]; email: string; userId: number, projectId?: number, from: string }) {
|
||||
|
||||
const randomHour = Math.floor(Math.random() * 6);
|
||||
const randomMin = Math.floor(Math.random() * 60);
|
||||
const randomCron = `0 ${randomMin} ${randomHour} * * *`;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import assert from "assert";
|
||||
import { calcNextSuiteCountUsed } from "./pipeline-suite-limit.js";
|
||||
|
||||
describe("Pipeline suite limits", () => {
|
||||
it("calculates next usage by subtracting current pipeline usage on update", () => {
|
||||
assert.equal(calcNextSuiteCountUsed(2, 1, 1), 2);
|
||||
assert.equal(calcNextSuiteCountUsed(2, 1, 2), 3);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
export function calcNextSuiteCountUsed(used: number, oldCount: number, newCount: number) {
|
||||
return (used ?? 0) - (oldCount ?? 0) + (newCount ?? 0);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user