diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index ad7960146..d30e9df40 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -43,7 +43,7 @@ jobs: with: time: '10' # for 60 seconds - name: deploy-certd-demo - uses: tyrrrz/action-http-request@master + uses: tyrrrz/action-http-request@prime with: # 通过webhook 触发 certd-demo来部署 url: ${{ secrets.WEBHOOK_CERTD_DEMO }} diff --git a/.github/workflows/release-image.yml b/.github/workflows/release-image.yml index 436cedb13..37af2be98 100644 --- a/.github/workflows/release-image.yml +++ b/.github/workflows/release-image.yml @@ -118,7 +118,7 @@ jobs: # greper/certd-agent:latest # greper/certd-agent:${{steps.get_certd_version.outputs.result}} - name: deploy-certd-doc - uses: tyrrrz/action-http-request@master + uses: tyrrrz/action-http-request@prime with: url: ${{ secrets.WEBHOOK_CERTD_DOC }} method: POST diff --git a/.trae/skills/access-plugin-dev/SKILL.md b/.trae/skills/access-plugin-dev/SKILL.md new file mode 100644 index 000000000..35742f1d7 --- /dev/null +++ b/.trae/skills/access-plugin-dev/SKILL.md @@ -0,0 +1,301 @@ +# Access 插件开发技能 + +## 什么是 Access 插件 + +Access 插件是 Certd 系统中用于存储用户第三方应用授权数据的插件,例如用户名密码、accessSecret 或 accessToken 等。同时,它还负责对接实现第三方的 API 接口,供其他插件调用使用。 + +## 开发步骤 + +### 1. 导入必要的依赖 + +```typescript +import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline'; +import { DomainRecord } from '@certd/plugin-lib'; +``` + +### 2. 使用 @IsAccess 注解注册插件 + +```typescript +@IsAccess({ + name: 'demo', // 插件唯一标识 + title: '授权插件示例', // 插件标题 + icon: 'clarity:plugin-line', // 插件图标 + desc: '这是一个示例授权插件,用于演示如何实现一个授权插件', // 插件描述 +}) +export class DemoAccess extends BaseAccess { + // 插件实现... +} +``` + +### 3. 定义授权属性 + +使用 `@AccessInput` 注解定义授权属性: + +```typescript +@AccessInput({ + title: '授权方式', + value: 'apiKey', // 默认值 + component: { + name: "a-select", // 基于 antdv 的输入组件 + vModel: "value", // v-model 绑定的属性名 + options: [ // 组件参数 + { label: "API密钥(推荐)", value: "apiKey" }, + { label: "账号密码", value: "account" }, + ], + placeholder: 'demoKeyId', + }, + required: true, +}) +apiType = ''; + +@AccessInput({ + title: '密钥Id', + component: { + name:"a-input", + allowClear: true, + placeholder: 'demoKeyId', + }, + required: true, +}) +demoKeyId = ''; + +@AccessInput({ + title: '密钥',//标题 + required: true, //text组件可以省略 + encrypt: true, //该属性是否需要加密 +}) +demoKeySecret = ''; +``` + +### 4. 实现测试方法 + +```typescript +@AccessInput({ + title: "测试", + component: { + name: "api-test", + action: "TestRequest" + }, + helper: "点击测试接口是否正常" +}) +testRequest = true; + +/** + * 会通过上面的testRequest参数在ui界面上生成测试按钮,供用户测试接口调用是否正常 + */ +async onTestRequest() { + await this.GetDomainList({}); + return "ok" +} +``` + +### 5. 实现 API 方法 + +```typescript +/** + * 获api接口示例 取域名列表, + */ +async GetDomainList(req: PageSearch): Promise> { + //输出日志必须使用ctx.logger + this.ctx.logger.info(`获取域名列表,req:${JSON.stringify(req)}`); + const pager = new Pager(req); + const resp = await this.doRequest({ + action: "ListDomains", + data: { + domain: req.searchKey, + offset: pager.getOffset(), + limit: pager.pageSize, + } + }); + const total = resp?.TotalCount || 0; + let list = resp?.DomainList?.map((item) => { + item.domain = item.Domain; + item.id = item.DomainId; + return item; + }) + return { + total, + list + }; +} + +/** + * 通用api调用方法, 具体如何构造请求体,需参考对应应用的API文档 + */ +async doRequest(req: { action: string, data?: any }) { + /** + this.ctx中包含很多有用的工具类 + type AccessContext = { + http: HttpClient; + logger: ILogger; + utils: typeof utils; + accessService: IAccessService; + } + */ + const res = await this.ctx.http.request({ + url: "https://api.demo.cn/api/", + method: "POST", + data: { + Action: req.action, + Body: req.data + } + }); + + if (res.Code !== 0) { + //异常处理 + throw new Error(res.Message || "请求失败"); + } + return res.Resp; +} +``` + +## 注意事项 + +1. **插件命名**:插件名称应简洁明了,反映其功能。 +2. **属性加密**:对于敏感信息(如密钥),应设置 `encrypt: true`。 +3. **日志输出**:必须使用 `this.ctx.logger` 输出日志,而不是 `console`。 +4. **错误处理**:API 调用失败时应抛出明确的错误信息。 +5. **测试方法**:实现 `onTestRequest` 方法,以便用户可以测试授权是否正常。 + +## 完整示例 + +```typescript +import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline'; +import { DomainRecord } from '@certd/plugin-lib'; + +/** + * 这个注解将注册一个授权配置 + * 在certd的后台管理系统中,用户可以选择添加此类型的授权 + */ +@IsAccess({ + name: 'demo', + title: '授权插件示例', + icon: 'clarity:plugin-line', //插件图标 + desc: '这是一个示例授权插件,用于演示如何实现一个授权插件', +}) +export class DemoAccess extends BaseAccess { + + /** + * 授权属性配置 + */ + @AccessInput({ + title: '授权方式', + value: 'apiKey', //默认值 + component: { + name: "a-select", //基于antdv的输入组件 + vModel: "value", // v-model绑定的属性名 + options: [ //组件参数 + { + label: "API密钥(推荐)", + value: "apiKey" + }, + { + label: "账号密码", + value: "account" + }, + ], + placeholder: 'demoKeyId', + }, + required: true, + }) + apiType = ''; + + /** + * 授权属性配置 + */ + @AccessInput({ + title: '密钥Id', + component: { + name:"a-input", + allowClear: true, + placeholder: 'demoKeyId', + }, + required: true, + }) + demoKeyId = ''; + + @AccessInput({ + title: '密钥',//标题 + required: true, //text组件可以省略 + encrypt: true, //该属性是否需要加密 + }) + demoKeySecret = ''; + + + @AccessInput({ + title: "测试", + component: { + name: "api-test", + action: "TestRequest" + }, + helper: "点击测试接口是否正常" + }) + testRequest = true; + + /** + * 会通过上面的testRequest参数在ui界面上生成测试按钮,供用户测试接口调用是否正常 + */ + async onTestRequest() { + await this.GetDomainList({}); + return "ok" + } + + /** + * 获api接口示例 取域名列表, + */ + async GetDomainList(req: PageSearch): Promise> { + //输出日志必须使用ctx.logger + this.ctx.logger.info(`获取域名列表,req:${JSON.stringify(req)}`); + const pager = new Pager(req); + const resp = await this.doRequest({ + action: "ListDomains", + data: { + domain: req.searchKey, + offset: pager.getOffset(), + limit: pager.pageSize, + } + }); + const total = resp?.TotalCount || 0; + let list = resp?.DomainList?.map((item) => { + item.domain = item.Domain; + item.id = item.DomainId; + return item; + }) + return { + total, + list + }; + } + + // 还可以继续编写API + + /** + * 通用api调用方法, 具体如何构造请求体,需参考对应应用的API文档 + */ + async doRequest(req: { action: string, data?: any }) { + /** + this.ctx中包含很多有用的工具类 + type AccessContext = { + http: HttpClient; + logger: ILogger; + utils: typeof utils; + accessService: IAccessService; + } + */ + const res = await this.ctx.http.request({ + url: "https://api.demo.cn/api/", + method: "POST", + data: { + Action: req.action, + Body: req.data + } + }); + + if (res.Code !== 0) { + //异常处理 + throw new Error(res.Message || "请求失败"); + } + return res.Resp; + } +} +``` \ No newline at end of file diff --git a/.trae/skills/access-plugin-dev/examples/input.md b/.trae/skills/access-plugin-dev/examples/input.md new file mode 100644 index 000000000..c3540a858 --- /dev/null +++ b/.trae/skills/access-plugin-dev/examples/input.md @@ -0,0 +1 @@ +我需要开发一个 Access 插件,用于存储和管理第三方应用的授权信息。请指导我如何实现。 \ No newline at end of file diff --git a/.trae/skills/access-plugin-dev/examples/output.md b/.trae/skills/access-plugin-dev/examples/output.md new file mode 100644 index 000000000..92209b335 --- /dev/null +++ b/.trae/skills/access-plugin-dev/examples/output.md @@ -0,0 +1,145 @@ +# Access 插件开发指南 + +## 开发步骤 + +### 1. 导入必要的依赖 + +```typescript +import { AccessInput, BaseAccess, IsAccess, Pager, PageRes, PageSearch } from '@certd/pipeline'; +import { DomainRecord } from '@certd/plugin-lib'; +``` + +### 2. 使用 @IsAccess 注解注册插件 + +```typescript +@IsAccess({ + name: 'demo', // 插件唯一标识 + title: '授权插件示例', // 插件标题 + icon: 'clarity:plugin-line', // 插件图标 + desc: '这是一个示例授权插件,用于演示如何实现一个授权插件', // 插件描述 +}) +export class DemoAccess extends BaseAccess { + // 插件实现... +} +``` + +### 3. 定义授权属性 + +使用 `@AccessInput` 注解定义授权属性: + +```typescript +@AccessInput({ + title: '授权方式', + value: 'apiKey', // 默认值 + component: { + name: "a-select", // 基于 antdv 的输入组件 + vModel: "value", // v-model 绑定的属性名 + options: [ // 组件参数 + { label: "API密钥(推荐)", value: "apiKey" }, + { label: "账号密码", value: "account" }, + ], + placeholder: 'demoKeyId', + }, + required: true, +}) +apiType = ''; + +@AccessInput({ + title: '密钥Id', + component: { + name:"a-input", + allowClear: true, + placeholder: 'demoKeyId', + }, + required: true, +}) +demoKeyId = ''; + +@AccessInput({ + title: '密钥',//标题 + required: true, //text组件可以省略 + encrypt: true, //该属性是否需要加密 +}) +demoKeySecret = ''; +``` + +### 4. 实现测试方法 + +```typescript +@AccessInput({ + title: "测试", + component: { + name: "api-test", + action: "TestRequest" + }, + helper: "点击测试接口是否正常" +}) +testRequest = true; + +/** + * 会通过上面的testRequest参数在ui界面上生成测试按钮,供用户测试接口调用是否正常 + */ +async onTestRequest() { + await this.GetDomainList({}); + return "ok" +} +``` + +### 5. 实现 API 方法 + +```typescript +/** + * 获api接口示例 取域名列表, + */ +async GetDomainList(req: PageSearch): Promise> { + //输出日志必须使用ctx.logger + this.ctx.logger.info(`获取域名列表,req:${JSON.stringify(req)}`); + const pager = new Pager(req); + const resp = await this.doRequest({ + action: "ListDomains", + data: { + domain: req.searchKey, + offset: pager.getOffset(), + limit: pager.pageSize, + } + }); + const total = resp?.TotalCount || 0; + let list = resp?.DomainList?.map((item) => { + item.domain = item.Domain; + item.id = item.DomainId; + return item; + }) + return { + total, + list + }; +} + +/** + * 通用api调用方法, 具体如何构造请求体,需参考对应应用的API文档 + */ +async doRequest(req: { action: string, data?: any }) { + const res = await this.ctx.http.request({ + url: "https://api.demo.cn/api/", + method: "POST", + data: { + Action: req.action, + Body: req.data + } + }); + + if (res.Code !== 0) { + //异常处理 + throw new Error(res.Message || "请求失败"); + } + return res.Resp; +} +``` + +## 注意事项 + +1. **插件命名**:插件名称应简洁明了,反映其功能。 +2. **属性加密**:对于敏感信息(如密钥),应设置 `encrypt: true`。 +3. **日志输出**:必须使用 `this.ctx.logger` 输出日志,而不是 `console`。 +4. **错误处理**:API 调用失败时应抛出明确的错误信息。 +5. **测试方法**:实现 `onTestRequest` 方法,以便用户可以测试授权是否正常。 \ No newline at end of file diff --git a/.trae/skills/dns-provider-dev/SKILL.md b/.trae/skills/dns-provider-dev/SKILL.md index bbae279dc..180a698ad 100644 --- a/.trae/skills/dns-provider-dev/SKILL.md +++ b/.trae/skills/dns-provider-dev/SKILL.md @@ -108,8 +108,11 @@ async removeRecord(options: RemoveRecordOptions): Promise { ### 6. 实例化插件 ```typescript -//结尾实例化一下,确保插件被注册 -new DemoDnsProvider(); +// 实例化这个 provider,将其自动注册到系统中 +if (isDev()) { + // 你的实现 要去掉这个 if,不然生产环境将不会显示 + new DemoDnsProvider(); +} ``` ## 注意事项 @@ -119,12 +122,14 @@ new DemoDnsProvider(); 3. **记录结构**:定义适合对应云平台的记录数据结构,至少包含 id 字段用于删除记录。 4. **日志输出**:使用 `this.logger` 输出日志,而不是 `console`。 5. **错误处理**:API 调用失败时应抛出明确的错误信息。 +6. **实例化**:生产环境中应移除 `if (isDev())` 条件,确保插件在生产环境中也能被注册。 ## 完整示例 ```typescript import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert'; import { DemoAccess } from './access.js'; +import { isDev } from '../../utils/env.js'; type DemoRecord = { // 这里定义 Record 记录的数据结构,跟对应云平台接口返回值一样即可,一般是拿到 id 就行,用于删除 txt 解析记录,清理申请痕迹 @@ -200,5 +205,8 @@ export class DemoDnsProvider extends AbstractDnsProvider { } // 实例化这个 provider,将其自动注册到系统中 -new DemoDnsProvider(); +if (isDev()) { + // 你的实现 要去掉这个 if,不然生产环境将不会显示 + new DemoDnsProvider(); +} ``` \ No newline at end of file diff --git a/.trae/skills/dns-provider-dev/examples/input.md b/.trae/skills/dns-provider-dev/examples/input.md new file mode 100644 index 000000000..2d483459a --- /dev/null +++ b/.trae/skills/dns-provider-dev/examples/input.md @@ -0,0 +1 @@ +我需要开发一个 DNS Provider 插件,用于在 ACME 申请证书时添加 TXT 解析记录。请指导我如何实现。 \ No newline at end of file diff --git a/.trae/skills/dns-provider-dev/examples/output.md b/.trae/skills/dns-provider-dev/examples/output.md new file mode 100644 index 000000000..deb7de392 --- /dev/null +++ b/.trae/skills/dns-provider-dev/examples/output.md @@ -0,0 +1,121 @@ +# DNS Provider 插件开发指南 + +## 开发步骤 + +### 1. 导入必要的依赖 + +```typescript +import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert'; +import { DemoAccess } from './access.js'; +import { isDev } from '../../utils/env.js'; +``` + +### 2. 定义记录数据结构 + +```typescript +type DemoRecord = { + // 这里定义 Record 记录的数据结构,跟对应云平台接口返回值一样即可,一般是拿到 id 就行,用于删除 txt 解析记录,清理申请痕迹 + // id:string +}; +``` + +### 3. 使用 @IsDnsProvider 注解注册插件 + +```typescript +// 这里通过 IsDnsProvider 注册一个 dnsProvider +@IsDnsProvider({ + name: 'demo', // 插件唯一标识 + title: 'Dns提供商Demo', // 插件标题 + desc: 'dns provider示例', // 插件描述 + icon: 'clarity:plugin-line', // 插件图标 + // 这里是对应的云平台的 access 类型名称 + accessType: 'demo', + order: 99, // 排序 +}) +export class DemoDnsProvider extends AbstractDnsProvider { + access!: DemoAccess; + + async onInstance() { + this.access = this.ctx.access as DemoAccess; + // 也可以通过 ctx 成员变量传递 context + this.logger.debug('access', this.access); + // 初始化的操作 + // ... + } + + // 插件实现... +} +``` + +### 4. 实现 createRecord 方法 + +```typescript +/** + * 创建 dns 解析记录,用于验证域名所有权 + */ +async createRecord(options: CreateRecordOptions): Promise { + /** + * options 参数说明 + * fullRecord: '_acme-challenge.example.com', + * value: 一串 uuid + * type: 'TXT', + * domain: 'example.com' + */ + const { fullRecord, value, type, domain } = options; + this.logger.info('添加域名解析:', fullRecord, value, type, domain); + + // 调用创建 dns 解析记录的对应的云端接口,创建 txt 类型的 dns 解析记录 + // 请根据实际接口情况调用,例如: + // const createDnsRecordUrl = "xxx" + // const record = this.http.post(createDnsRecordUrl,{ + // // 授权参数 + // // 创建 dns 解析记录的参数 + // }) + // // 返回本次创建的 dns 解析记录,这个记录会在删除的时候用到 + // return record +} +``` + +### 5. 实现 removeRecord 方法 + +```typescript +/** + * 删除 dns 解析记录,清理申请痕迹 + * @param options + */ +async removeRecord(options: RemoveRecordOptions): Promise { + const { fullRecord, value, domain } = options.recordReq; + const record = options.recordRes; + this.logger.info('删除域名解析:', domain, fullRecord, value, record); + // 这里调用删除 txt dns 解析记录接口 + // 请根据实际接口情况调用,例如: + + // const deleteDnsRecordUrl = "xxx" + // const res = this.http.delete(deleteDnsRecordUrl,{ + // // 授权参数 + // // 删除 dns 解析记录的参数 + // }) + + + this.logger.info('删除域名解析成功:', fullRecord, value); +} +``` + +### 6. 实例化插件 + +```typescript +// 实例化这个 provider,将其自动注册到系统中 +if (isDev()) { + // 你的实现 要去掉这个 if,不然生产环境将不会显示 + new DemoDnsProvider(); +} +``` + +## 注意事项 + +1. **插件命名**:插件名称应简洁明了,反映其功能。 +2. **accessType**:必须指定对应的云平台的 access 类型名称。 +3. **记录结构**:定义适合对应云平台的记录数据结构,至少包含 id 字段用于删除记录。 +4. **日志输出**:使用 `this.logger` 输出日志,而不是 `console`。 +5. **错误处理**:API 调用失败时应抛出明确的错误信息。 +6. **实例化**:生产环境中应移除 `if (isDev())` 条件,确保插件在生产环境中也能被注册。 \ No newline at end of file diff --git a/.trae/skills/plugin-converter/SKILL.md b/.trae/skills/plugin-converter/SKILL.md new file mode 100644 index 000000000..650e50d75 --- /dev/null +++ b/.trae/skills/plugin-converter/SKILL.md @@ -0,0 +1,201 @@ +# 插件转换工具技能 + +## 什么是插件转换工具 + +插件转换工具是一个用于将 Certd 插件转换为 YAML 配置文件的命令行工具。它可以分析单个插件文件,识别插件类型,并生成对应的 YAML 配置,方便插件的注册和管理。 + +## 工具位置 + +`trae/skills/convert-plugin-to-yaml.js` + +## 功能特性 + +- **单个插件转换**:支持指定单个插件文件进行转换,而不是扫描整个目录 +- **自动类型识别**:自动识别插件类型(Access、Task、DNS Provider、Notification、Addon) +- **详细日志输出**:提供详细的转换过程日志,便于调试 +- **YAML 配置生成**:生成标准的 YAML 配置文件 +- **配置文件保存**:自动将生成的配置保存到 `./metadata` 目录 +- **可复用函数**:导出了可复用的函数,便于其他模块调用 + +## 使用方法 + +### 基本用法 + +```bash +node trae/skills/convert-plugin-to-yaml.js <插件文件路径> +``` + +### 示例 + +```bash +# 转换 Access 插件 +node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js + +# 转换 Task 插件 +node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js + +# 转换 DNS Provider 插件 +node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.js +``` + +## 转换过程 + +1. **加载插件模块**:使用 `import()` 动态加载指定的插件文件 +2. **分析插件定义**:检查模块导出的对象,寻找带有 `define` 属性的插件 +3. **识别插件类型**:根据插件的继承关系或属性识别插件类型 +4. **生成 YAML 配置**:基于插件定义生成标准的 YAML 配置 +5. **保存配置文件**:将生成的配置保存到 `./metadata` 目录 + +## 输出说明 + +### 命令行输出 + +执行转换命令后,工具会输出以下信息: + +- 插件加载状态 +- 插件导出的对象列表 +- 插件类型识别结果 +- 生成的 YAML 配置内容 +- 配置文件保存路径 + +### 配置文件命名规则 + +生成的配置文件命名规则为: + +``` +<插件类型>[_<子类型>]_<插件名称>.yaml +``` + +例如: +- `access_demo.yaml`(Access 插件) +- `deploy_DemoTest.yaml`(Task 插件) +- `dnsProvider_demo.yaml`(DNS Provider 插件) + +## 插件类型识别逻辑 + +工具通过以下逻辑识别插件类型: + +1. **DNS Provider**:如果插件定义中包含 `accessType` 属性 +2. **Task**:如果插件继承自 `AbstractTaskPlugin` +3. **Notification**:如果插件继承自 `BaseNotification` +4. **Access**:如果插件继承自 `BaseAccess` +5. **Addon**:如果插件继承自 `BaseAddon` + +## 注意事项 + +1. **文件路径**:插件文件路径可以是相对路径或绝对路径 +2. **文件格式**:仅支持 `.js` 文件,不支持 `.ts` 文件(需要先编译) +3. **依赖安装**:执行前确保已安装所有必要的依赖 +4. **配置目录**:如果 `./metadata` 目录不存在,工具会自动创建 +5. **错误处理**:如果插件加载失败或识别失败,工具会输出错误信息但不会终止执行 + +## 代码结构 + +### 主要函数 + +1. **isPrototypeOf(value, cls)**:检查对象是否是指定类的原型 +2. **loadSingleModule(filePath)**:加载单个插件模块 +3. **convertSinglePlugin(pluginPath)**:分析单个插件并生成 YAML 配置 +4. **main()**:主函数,处理命令行参数并执行转换 + +### 导出函数 + +工具导出了以下函数,便于其他模块调用: + +```javascript +export { + convertSinglePlugin, // 转换单个插件 + loadSingleModule, // 加载单个模块 + isPrototypeOf // 检查原型关系 +}; +``` + +## 应用场景 + +1. **插件开发**:在开发新插件时,快速生成配置文件 +2. **插件调试**:查看插件的内部定义和配置 +3. **插件管理**:批量转换现有插件为标准配置格式 +4. **自动化构建**:集成到构建流程中,自动生成插件配置 + +## 示例输出 + +### 转换 Access 插件示例 + +```bash +$ node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js +开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/access.js +插件模块导出了 1 个对象: DemoAccess +处理插件: DemoAccess +插件类型: access +脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/access.js + +生成的 YAML 配置: +name: demo +title: 授权插件示例 +desc: 这是一个示例授权插件,用于演示如何实现一个授权插件 +icon: clarity:plugin-line +pluginType: access +type: builtIn +scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/access.js + +YAML 配置已保存到: ./metadata/access_demo.yaml +插件转换完成! +``` + +### 转换 Task 插件示例 + +```bash +$ node trae/skills/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js +开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js +插件模块导出了 1 个对象: DemoTest +处理插件: DemoTest +插件类型: deploy +脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js + +生成的 YAML 配置: +name: DemoTest +title: Demo-测试插件 +desc: "" +icon: clarity:plugin-line +group: other +default: + strategy: + runStrategy: SkipWhenSucceed +pluginType: deploy +type: builtIn +scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js + +YAML 配置已保存到: ./metadata/deploy_DemoTest.yaml +插件转换完成! +``` + +## 故障排除 + +### 常见问题 + +1. **模块加载失败** + - 原因:插件文件依赖未安装或路径错误 + - 解决:确保已安装所有依赖,检查文件路径是否正确 + +2. **插件类型识别失败** + - 原因:插件未正确继承基类或缺少必要的属性 + - 解决:检查插件代码,确保正确继承对应的基类 + +3. **YAML 配置生成失败** + - 原因:插件定义格式不正确 + - 解决:检查插件的 `define` 属性格式是否正确 + +4. **配置文件保存失败** + - 原因:权限不足或磁盘空间不足 + - 解决:确保有足够的权限和磁盘空间 + +### 调试建议 + +- **查看详细日志**:工具会输出详细的转换过程日志,仔细查看日志信息 +- **检查插件代码**:确保插件代码符合 Certd 插件开发规范 +- **尝试简化插件**:如果转换失败,尝试创建一个最小化的插件示例进行测试 +- **检查依赖版本**:确保使用的依赖版本与 Certd 兼容 + +## 总结 + +插件转换工具是一个方便实用的工具,它可以帮助开发者快速生成插件的 YAML 配置文件,简化插件的注册和管理过程。通过命令行参数指定单个插件文件,工具会自动完成类型识别、配置生成和保存等操作,大大提高了插件开发和管理的效率。 \ No newline at end of file diff --git a/.trae/skills/plugin-converter/examples/input.md b/.trae/skills/plugin-converter/examples/input.md new file mode 100644 index 000000000..fe075cb3a --- /dev/null +++ b/.trae/skills/plugin-converter/examples/input.md @@ -0,0 +1 @@ +我需要将一个插件转换为 YAML 配置文件。请指导我如何使用插件转换工具。 \ No newline at end of file diff --git a/.trae/skills/plugin-converter/examples/output.md b/.trae/skills/plugin-converter/examples/output.md new file mode 100644 index 000000000..2f252f73b --- /dev/null +++ b/.trae/skills/plugin-converter/examples/output.md @@ -0,0 +1,95 @@ +# 插件转换工具使用指南 + +## 工具说明 + +插件转换工具用于将单个 Certd 插件转换为 YAML 配置文件,方便插件的注册和管理。 + +## 工具位置 + +`.trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js` + +## 使用方法 + +### 基本用法 + +```bash +node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js <插件文件路径> +``` + +### 示例 + +#### 转换 Access 插件 + +```bash +node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js +``` + +#### 转换 Task 插件 + +```bash +node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/plugins/plugin-test.js +``` + +#### 转换 DNS Provider 插件 + +```bash +node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.js +``` + +## 转换过程 + +1. **加载插件模块**:使用 `import()` 动态加载指定的插件文件 +2. **分析插件定义**:检查模块导出的对象,寻找带有 `define` 属性的插件 +3. **识别插件类型**:根据插件的继承关系或属性识别插件类型 +4. **生成 YAML 配置**:基于插件定义生成标准的 YAML 配置 +5. **保存配置文件**:将生成的配置保存到 `./metadata` 目录 + +## 输出说明 + +### 命令行输出 + +执行转换命令后,工具会输出以下信息: + +- 插件加载状态 +- 插件导出的对象列表 +- 插件类型识别结果 +- 生成的 YAML 配置内容 +- 配置文件保存路径 + +### 配置文件命名规则 + +生成的配置文件命名规则为: + +``` +<插件类型>[_<子类型>]_<插件名称>.yaml +``` + +例如: +- `access_demo.yaml`(Access 插件) +- `deploy_DemoTest.yaml`(Task 插件) +- `dnsProvider_demo.yaml`(DNS Provider 插件) + +## 示例输出 + +### 转换 Access 插件示例 + +```bash +$ node .trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js packages/ui/certd-server/src/plugins/plugin-demo/access.js +开始转换插件: packages/ui/certd-server/src/plugins/plugin-demo/access.js +插件模块导出了 1 个对象: DemoAccess +处理插件: DemoAccess +插件类型: access +脚本路径: packages/ui/certd-server/src/plugins/plugin-demo/access.js + +生成的 YAML 配置: +name: demo +title: 授权插件示例 +desc: 这是一个示例授权插件,用于演示如何实现一个授权插件 +icon: clarity:plugin-line +pluginType: access +type: builtIn +scriptFilePath: packages/ui/certd-server/src/plugins/plugin-demo/access.js + +YAML 配置已保存到: ./metadata/access_demo.yaml +插件转换完成! +``` \ No newline at end of file diff --git a/.trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js b/.trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js new file mode 100644 index 000000000..0bb6251cc --- /dev/null +++ b/.trae/skills/plugin-converter/resources/convert-plugin-to-yaml.js @@ -0,0 +1,160 @@ +// 转换单个插件为 YAML 配置的技能脚本 + +import path from "path"; +import fs from "fs"; +import { pathToFileURL } from "node:url"; +import * as yaml from "js-yaml"; +import { AbstractTaskPlugin, BaseAccess, BaseNotification} from "@certd/pipeline"; +import { BaseAddon} from "@certd/lib-server"; + +/** + * 检查对象是否是指定类的原型 + */ +function isPrototypeOf(value, cls) { + return cls.prototype.isPrototypeOf(value.prototype); +} + +/** + * 加载单个插件模块 + */ +async function loadSingleModule(filePath) { + try { + // 转换为 file:// URL(Windows 必需) + const moduleUrl = pathToFileURL(filePath).href; + const module = await import(moduleUrl); + return module.default || module; + } catch (err) { + console.error(`加载模块 ${filePath} 失败:`, err); + return null; + } +} + +/** + * 分析单个插件并生成 YAML 配置 + */ +async function convertSinglePlugin(pluginPath) { + console.log(`开始转换插件: ${pluginPath}`); + + // 加载插件模块 + const module = await loadSingleModule(pluginPath); + if (!module) { + console.error("加载插件失败,退出"); + return; + } + + // 处理模块中的所有导出 + const entry = Object.entries(module); + if (entry.length === 0) { + console.error("插件模块没有导出任何内容"); + return; + } + + console.log(`插件模块导出了 ${entry.length} 个对象: ${entry.map(([name]) => name).join(", ")}`); + + // 处理每个导出的对象 + for (const [name, value] of entry) { + // 检查是否是插件(有 define 属性) + if (!value.define) { + console.log(`跳过非插件对象: ${name}`); + continue; + } + + console.log(`处理插件: ${name}`); + + // 构建插件定义 + const pluginDefine = { + ...value.define + }; + + let subType = ""; + + // 确定插件类型 + if (pluginDefine.accessType) { + pluginDefine.pluginType = "dnsProvider"; + } else if (isPrototypeOf(value, AbstractTaskPlugin)) { + pluginDefine.pluginType = "deploy"; + } else if (isPrototypeOf(value, BaseNotification)) { + pluginDefine.pluginType = "notification"; + } else if (isPrototypeOf(value, BaseAccess)) { + pluginDefine.pluginType = "access"; + } else if (isPrototypeOf(value, BaseAddon)) { + pluginDefine.pluginType = "addon"; + subType = "_" + (pluginDefine.addonType || ""); + } else { + console.log(`[warning] 未知的插件类型:${pluginDefine.name}`); + continue; + } + + pluginDefine.type = "builtIn"; + + // 计算脚本文件路径 + const relativePath = path.relative(process.cwd(), pluginPath); + const scriptFilePath = relativePath.replace(/\\/g, "/").replace(/\.js$/, ".js"); + pluginDefine.scriptFilePath = scriptFilePath; + + console.log(`插件类型: ${pluginDefine.pluginType}`); + console.log(`脚本路径: ${scriptFilePath}`); + + // 生成 YAML 配置 + const yamlContent = yaml.dump(pluginDefine); + console.log("\n生成的 YAML 配置:"); + console.log(yamlContent); + + // 可选:保存到文件 + const outputDir = "./metadata"; + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + const outputFileName = `${pluginDefine.pluginType}${subType}_${pluginDefine.name}.yaml`; + const outputPath = path.join(outputDir, outputFileName); + + fs.writeFileSync(outputPath, yamlContent, 'utf8'); + console.log(`\nYAML 配置已保存到: ${outputPath}`); + + return pluginDefine; + } + + console.error("未找到有效的插件定义"); +} + +/** + * 主函数 + */ +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("请指定插件文件路径"); + console.log("用法: node convert-plugin-to-yaml.js <插件文件路径>"); + process.exit(1); + } + + const pluginPath = args[0]; + + if (!fs.existsSync(pluginPath)) { + console.error(`插件文件不存在: ${pluginPath}`); + process.exit(1); + } + + try { + await convertSinglePlugin(pluginPath); + console.log("\n插件转换完成!"); + } catch (error) { + console.error("转换过程中出错:", error); + process.exit(1); + } +} + +// 如果直接运行此脚本 +if (import.meta.url === pathToFileURL(process.argv[1]).href) { + main(); +} + +// 导出函数,以便其他模块使用 +export { + convertSinglePlugin, + loadSingleModule, + isPrototypeOf +}; + diff --git a/.trae/skills/task-plugin-dev/SKILL.md b/.trae/skills/task-plugin-dev/SKILL.md new file mode 100644 index 000000000..84d16e690 --- /dev/null +++ b/.trae/skills/task-plugin-dev/SKILL.md @@ -0,0 +1,388 @@ +# Task 插件开发技能 + +## 什么是 Task 插件 + +Task 插件是 Certd 系统中的部署任务插件,它继承自 `AbstractTaskPlugin` 类,被流水线调用 `execute` 方法,将证书部署到对应的应用上。 + +## 开发步骤 + +### 1. 导入必要的依赖 + +```typescript +import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { CertInfo, CertReader } from '@certd/plugin-cert'; +import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib'; +import { optionsUtils } from '@certd/basic'; +import { CertApplyPluginNames} from '@certd/plugin-cert'; +``` + +### 2. 使用 @IsTaskPlugin 注解注册插件 + +```typescript +@IsTaskPlugin({ + // 命名规范,插件类型+功能,大写字母开头,驼峰命名 + name: 'DemoTest', + title: 'Demo-测试插件', // 插件标题 + icon: 'clarity:plugin-line', // 插件图标 + // 插件分组 + group: pluginGroups.other.key, + default: { + // 默认值配置照抄即可 + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +// 类名规范,跟上面插件名称(name)一致 +export class DemoTest extends AbstractTaskPlugin { + // 插件实现... +} +``` + +### 3. 定义任务输入参数 + +使用 `@TaskInput` 注解定义任务输入参数: + +```typescript +// 测试参数 +@TaskInput({ + title: '属性示例', + value: '默认值', + component: { + // 前端组件配置,具体配置见组件文档 https://www.antdv.com/components/input-cn + name: 'a-input', + vModel: 'value', // 双向绑定组件的 props 名称 + }, + helper: '帮助说明,[链接](https://certd.docmirror.cn)', + required: false, // 是否必填 +}) +text!: string; + +// 证书选择,此项必须要有 +@TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: [...CertApplyPluginNames], + }, + // required: true, // 必填 +}) +cert!: CertInfo; + +@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) +// 前端可以展示,当前申请的证书域名列表 +certDomains!: string[]; + +// 授权选择框 +@TaskInput({ + title: 'demo授权', + helper: 'demoAccess授权', + component: { + name: 'access-selector', + type: 'demo', // 固定授权类型 + }, + // rules: [{ required: true, message: '此项必填' }], + // required: true, // 必填 +}) +accessId!: string; +``` + +### 4. 实现插件方法 + +#### 4.1 插件实例化时执行的方法 + +```typescript +// 插件实例化时执行的方法 +async onInstance() {} +``` + +#### 4.2 插件执行方法 + +```typescript +// 插件执行方法 +async execute(): Promise { + const { select, text, cert, accessId } = this; + + try { + const access = await this.getAccess(accessId); + this.logger.debug('access', access); + } catch (e) { + this.logger.error('获取授权失败', e); + } + + try { + const certReader = new CertReader(cert); + this.logger.debug('certReader', certReader); + } catch (e) { + this.logger.error('读取crt失败', e); + } + + this.logger.info('DemoTestPlugin execute'); + this.logger.info('text:', text); + this.logger.info('select:', select); + this.logger.info('switch:', this.switch); + this.logger.info('授权id:', accessId); + + // 具体的部署逻辑 + // ... +} +``` + +#### 4.3 后端获取选项方法 + +```typescript +@TaskInput( + createRemoteSelectInputDefine({ + title: '从后端获取选项', + helper: '选择时可以从后端获取选项', + action: DemoTest.prototype.onGetSiteList.name, + // 当以下参数变化时,触发获取选项 + watches: ['certDomains', 'accessId'], + required: true, + }) +) +siteName!: string | string[]; + +// 从后端获取选项的方法 +async onGetSiteList(req: PageSearch) { + if (!this.accessId) { + throw new Error('请选择Access授权'); + } + + // @ts-ignore + const access = await this.getAccess(this.accessId); + + // const siteRes = await access.GetDomainList(req); + // 以下是模拟数据 + const siteRes = [ + { id: 1, siteName: 'site1.com' }, + { id: 2, siteName: 'site2.com' }, + { id: 3, siteName: 'site2.com' }, + ]; + // 转换为前端所需要的格式 + const options = siteRes.map((item: any) => { + return { + value: item.siteName, + label: item.siteName, + domain: item.siteName, + }; + }); + // 将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择 + return optionsUtils.buildGroupOptions(options, this.certDomains); +} +``` + +## 注意事项 + +1. **插件命名**:插件名称应遵循命名规范,大写字母开头,驼峰命名。 +2. **类名规范**:类名应与插件名称(name)一致。 +3. **证书选择**:必须包含证书选择参数,用于获取前置任务输出的域名证书。 +4. **日志输出**:使用 `this.logger` 输出日志,而不是 `console`。 +5. **错误处理**:执行过程中的错误应被捕获并记录。 +6. **授权获取**:使用 `this.getAccess(accessId)` 获取授权信息。 + +## 完整示例 + +```typescript +import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { CertInfo, CertReader } from '@certd/plugin-cert'; +import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib'; +import { optionsUtils } from '@certd/basic'; +import { CertApplyPluginNames} from '@certd/plugin-cert'; +@IsTaskPlugin({ + //命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名 + name: 'DemoTest', + title: 'Demo-测试插件', + icon: 'clarity:plugin-line', + //插件分组 + group: pluginGroups.other.key, + default: { + //默认值配置照抄即可 + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +//类名规范,跟上面插件名称(name)一致 +export class DemoTest extends AbstractTaskPlugin { + //测试参数 + @TaskInput({ + title: '属性示例', + value: '默认值', + component: { + //前端组件配置,具体配置见组件文档 https://www.antdv.com/components/input-cn + name: 'a-input', + vModel: 'value', //双向绑定组件的props名称 + }, + helper: '帮助说明,[链接](https://certd.docmirror.cn)', + required: false, //是否必填 + }) + text!: string; + + //测试参数 + @TaskInput({ + title: '选择框', + component: { + //前端组件配置,具体配置见组件文档 https://www.antdv.com/components/select-cn + name: 'a-auto-complete', + vModel: 'value', + options: [ + //选项列表 + { label: '动态显', value: 'show' }, + { label: '动态隐', value: 'hide' }, + ], + }, + }) + select!: string; + + @TaskInput({ + title: '动态显隐', + helper: '我会根据选择框的值进行显隐', + show: true, //动态计算的值会覆盖它 + //动态计算脚本, mergeScript返回的对象会合并当前配置,此处演示 show的值会被动态计算结果覆盖,show的值根据用户选择的select的值决定 + mergeScript: ` + return { + show: ctx.compute(({form})=>{ + return form.select === 'show'; + }) + } + `, + }) + showText!: string; + + //测试参数 + @TaskInput({ + title: '多选框', + component: { + //前端组件配置,具体配置见组件文档 https://www.antdv.com/components/select-cn + name: 'a-select', + vModel: 'value', + mode: 'tags', + multiple: true, + options: [ + { value: '1', label: '选项1' }, + { value: '2', label: '选项2' }, + ], + }, + }) + multiSelect!: string; + + //测试参数 + @TaskInput({ + title: 'switch', + component: { + //前端组件配置,具体配置见组件文档 https://www.antdv.com/components/switch-cn + name: 'a-switch', + vModel: 'checked', + }, + }) + switch!: boolean; + //证书选择,此项必须要有 + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: [...CertApplyPluginNames], + }, + // required: true, // 必填 + }) + cert!: CertInfo; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + //前端可以展示,当前申请的证书域名列表 + certDomains!: string[]; + + //授权选择框 + @TaskInput({ + title: 'demo授权', + helper: 'demoAccess授权', + component: { + name: 'access-selector', + type: 'demo', //固定授权类型 + }, + // rules: [{ required: true, message: '此项必填' }], + // required: true, //必填 + }) + accessId!: string; + + @TaskInput( + createRemoteSelectInputDefine({ + title: '从后端获取选项', + helper: '选择时可以从后端获取选项', + action: DemoTest.prototype.onGetSiteList.name, + //当以下参数变化时,触发获取选项 + watches: ['certDomains', 'accessId'], + required: true, + }) + ) + siteName!: string | string[]; + + //插件实例化时执行的方法 + async onInstance() {} + + //插件执行方法 + async execute(): Promise { + const { select, text, cert, accessId } = this; + + try { + const access = await this.getAccess(accessId); + this.logger.debug('access', access); + } catch (e) { + this.logger.error('获取授权失败', e); + } + + try { + const certReader = new CertReader(cert); + this.logger.debug('certReader', certReader); + } catch (e) { + this.logger.error('读取crt失败', e); + } + + this.logger.info('DemoTestPlugin execute'); + this.logger.info('text:', text); + this.logger.info('select:', select); + this.logger.info('switch:', this.switch); + this.logger.info('授权id:', accessId); + + // const res = await this.http.request({ + // url: 'https://api.demo.com', + // method: 'GET', + // }); + // if (res.code !== 0) { + // //检查res是否报错,你需要抛异常,来结束插件执行,否则会判定为执行成功,下次执行时会跳过本任务 + // throw new Error(res.message); + // } + // this.logger.info('部署成功:', res); + } + + //此方法演示,如何让前端在添加插件时可以从后端获取选项,这里是后端返回选项的方法 + async onGetSiteList(req: PageSearch) { + if (!this.accessId) { + throw new Error('请选择Access授权'); + } + + // @ts-ignore + const access = await this.getAccess(this.accessId); + + // const siteRes = await access.GetDomainList(req); + //以下是模拟数据 + const siteRes = [ + { id: 1, siteName: 'site1.com' }, + { id: 2, siteName: 'site2.com' }, + { id: 3, siteName: 'site2.com' }, + ]; + //转换为前端所需要的格式 + const options = siteRes.map((item: any) => { + return { + value: item.siteName, + label: item.siteName, + domain: item.siteName, + }; + }); + //将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择 + return optionsUtils.buildGroupOptions(options, this.certDomains); + } +} +``` \ No newline at end of file diff --git a/.trae/skills/task-plugin-dev/examples/input.md b/.trae/skills/task-plugin-dev/examples/input.md new file mode 100644 index 000000000..622920d6d --- /dev/null +++ b/.trae/skills/task-plugin-dev/examples/input.md @@ -0,0 +1 @@ +我需要开发一个 Task 插件,用于将申请的证书部署到指定的应用系统中。请指导我如何实现。 \ No newline at end of file diff --git a/.trae/skills/task-plugin-dev/examples/output.md b/.trae/skills/task-plugin-dev/examples/output.md new file mode 100644 index 000000000..28ebb03e0 --- /dev/null +++ b/.trae/skills/task-plugin-dev/examples/output.md @@ -0,0 +1,129 @@ +# Task 插件开发指南 + +## 开发步骤 + +### 1. 导入必要的依赖 + +```typescript +import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline'; +import { CertInfo, CertReader } from '@certd/plugin-cert'; +import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib'; +import { optionsUtils } from '@certd/basic'; +import { CertApplyPluginNames} from '@certd/plugin-cert'; +``` + +### 2. 使用 @IsTaskPlugin 注解注册插件 + +```typescript +@IsTaskPlugin({ + // 命名规范,插件类型+功能,大写字母开头,驼峰命名 + name: 'DemoTest', + title: 'Demo-测试插件', // 插件标题 + icon: 'clarity:plugin-line', // 插件图标 + // 插件分组 + group: pluginGroups.other.key, + default: { + // 默认值配置照抄即可 + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +// 类名规范,跟上面插件名称(name)一致 +export class DemoTest extends AbstractTaskPlugin { + // 插件实现... +} +``` + +### 3. 定义任务输入参数 + +使用 `@TaskInput` 注解定义任务输入参数: + +```typescript +// 测试参数 +@TaskInput({ + title: '属性示例', + value: '默认值', + component: { + //前端组件配置,具体配置见组件文档 https://www.antdv.com/components/input-cn + name: 'a-input', + vModel: 'value', //双向绑定组件的props名称 + }, + helper: '帮助说明,[链接](https://certd.docmirror.cn)', + required: false, //是否必填 +}) +text!: string; + +//证书选择,此项必须要有 +@TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: [...CertApplyPluginNames], + }, + // required: true, // 必填 +}) +cert!: CertInfo; + +@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) +//前端可以展示,当前申请的证书域名列表 +certDomains!: string[]; + +//授权选择框 +@TaskInput({ + title: 'demo授权', + helper: 'demoAccess授权', + component: { + name: 'access-selector', + type: 'demo', //固定授权类型 + }, + // rules: [{ required: true, message: '此项必填' }], + // required: true, //必填 +}) +accessId!: string; +``` + +### 4. 实现插件方法 + +```typescript +//插件实例化时执行的方法 +async onInstance() {} + +//插件执行方法 +async execute(): Promise { + const { select, text, cert, accessId } = this; + + try { + const access = await this.getAccess(accessId); + this.logger.debug('access', access); + } catch (e) { + this.logger.error('获取授权失败', e); + } + + try { + const certReader = new CertReader(cert); + this.logger.debug('certReader', certReader); + } catch (e) { + this.logger.error('读取crt失败', e); + } + + this.logger.info('DemoTestPlugin execute'); + this.logger.info('text:', text); + this.logger.info('select:', select); + this.logger.info('switch:', this.switch); + this.logger.info('授权id:', accessId); + + // 具体的部署逻辑 + // ... +} +``` + +## 注意事项 + +1. **插件命名**:插件名称应遵循命名规范,大写字母开头,驼峰命名。 +2. **类名规范**:类名应与插件名称(name)一致。 +3. **证书选择**:必须包含证书选择参数,用于获取前置任务输出的域名证书。 +4. **日志输出**:使用 `this.logger` 输出日志,而不是 `console`。 +5. **错误处理**:执行过程中的错误应被捕获并记录。 +6. **授权获取**:使用 `this.getAccess(accessId)` 获取授权信息。 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c06cb30a..941af97f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,34 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +### Bug Fixes + +* 修复部署到openwrt错误的bug ([2e3d0cc](https://github.com/certd/certd/commit/2e3d0cc57c16c48ad435bc8fde729bacaedde9f5)) +* 修复发件邮箱无法输入的bug ([27b0348](https://github.com/certd/certd/commit/27b0348e1d3d752f418f851965d6afbc26c0160c)) +* 修复复制流水线保存后丢失分组和排序号的问题 ([bc32648](https://github.com/certd/certd/commit/bc326489abc1d50a0930b4f47aa2d62d3a486798)) +* 修复获取群辉deviceid报错的bug ([79be392](https://github.com/certd/certd/commit/79be392775a2c91848dd5a66a2618adc4e4b48f6)) +* 修复京东云域名申请证书报错的bug ([d9c0130](https://github.com/certd/certd/commit/d9c0130b59997144a3c274d456635b800135e43f)) +* 修复偶尔下载证书报未授权的错误 ([316537e](https://github.com/certd/certd/commit/316537eb4dcbe5ec57784e8bf95ee3cdfd21dce7)) +* 修复dcdn多个域名同时部署时 可能会出现证书名称重复的bug ([78c2ced](https://github.com/certd/certd/commit/78c2ced43b1a73d142b0ed783b162b97f545ab06)) +* 优化dcdn部署上传多次证书 偶尔报 The CertName already exists的问题 ([72f850f](https://github.com/certd/certd/commit/72f850f675b500d12ebff2338d1b99d6fab476e1)) +* **cert-plugin:** 优化又拍云客户端错误处理逻辑,当域名已绑定证书时不再抛出异常。 ([92c9ac3](https://github.com/certd/certd/commit/92c9ac382692e6c84140ff787759ab6d39ccbe96)) +* esxi部署失败的bug ([1e44115](https://github.com/certd/certd/commit/1e441154617e6516a9a3610412bf597128c62696)) + +### Features + +* 支持企业级管理模式,项目管理,细分权限 ([3734083](https://github.com/certd/certd/commit/37340838b6a61a94b86bfa13cf5da88b26f1315a)) + +### Performance Improvements + +* 【破坏性更新】错误返回信息msg字段名统一改成message,与成功的返回结构一致 ([51ab6d6](https://github.com/certd/certd/commit/51ab6d6da1bb551b55b3a6a4a9a945c8d6ace806)) +* 当域名管理中没有域名时,创建流水线时不展开域名选择框 ([bb0afe1](https://github.com/certd/certd/commit/bb0afe1fa7b0fc52fde051d24fbe6be69d52f4cc)) +* 任务步骤页面增加串行执行提示说明 ([787f6ef](https://github.com/certd/certd/commit/787f6ef52893d8dc912ee2a7a5b8ce2b73c108c9)) +* 站点监控支持指定ip地址检查 ([83d81b6](https://github.com/certd/certd/commit/83d81b64b3adb375366039e07c87d1ad79121c13)) +* AI开发插件 skills 定义初步 ([1f68fad](https://github.com/certd/certd/commit/1f68faddb97a978c5a5e731a8895b4bb0587ad83)) +* http请求增加建立连接超时配置 ([3c85602](https://github.com/certd/certd/commit/3c85602ab1fc1953cdc06a6cd75a971d14119179)) + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) ### Bug Fixes diff --git a/docs/guide/changelogs/CHANGELOG.md b/docs/guide/changelogs/CHANGELOG.md index 2c06cb30a..941af97f9 100644 --- a/docs/guide/changelogs/CHANGELOG.md +++ b/docs/guide/changelogs/CHANGELOG.md @@ -3,6 +3,34 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +### Bug Fixes + +* 修复部署到openwrt错误的bug ([2e3d0cc](https://github.com/certd/certd/commit/2e3d0cc57c16c48ad435bc8fde729bacaedde9f5)) +* 修复发件邮箱无法输入的bug ([27b0348](https://github.com/certd/certd/commit/27b0348e1d3d752f418f851965d6afbc26c0160c)) +* 修复复制流水线保存后丢失分组和排序号的问题 ([bc32648](https://github.com/certd/certd/commit/bc326489abc1d50a0930b4f47aa2d62d3a486798)) +* 修复获取群辉deviceid报错的bug ([79be392](https://github.com/certd/certd/commit/79be392775a2c91848dd5a66a2618adc4e4b48f6)) +* 修复京东云域名申请证书报错的bug ([d9c0130](https://github.com/certd/certd/commit/d9c0130b59997144a3c274d456635b800135e43f)) +* 修复偶尔下载证书报未授权的错误 ([316537e](https://github.com/certd/certd/commit/316537eb4dcbe5ec57784e8bf95ee3cdfd21dce7)) +* 修复dcdn多个域名同时部署时 可能会出现证书名称重复的bug ([78c2ced](https://github.com/certd/certd/commit/78c2ced43b1a73d142b0ed783b162b97f545ab06)) +* 优化dcdn部署上传多次证书 偶尔报 The CertName already exists的问题 ([72f850f](https://github.com/certd/certd/commit/72f850f675b500d12ebff2338d1b99d6fab476e1)) +* **cert-plugin:** 优化又拍云客户端错误处理逻辑,当域名已绑定证书时不再抛出异常。 ([92c9ac3](https://github.com/certd/certd/commit/92c9ac382692e6c84140ff787759ab6d39ccbe96)) +* esxi部署失败的bug ([1e44115](https://github.com/certd/certd/commit/1e441154617e6516a9a3610412bf597128c62696)) + +### Features + +* 支持企业级管理模式,项目管理,细分权限 ([3734083](https://github.com/certd/certd/commit/37340838b6a61a94b86bfa13cf5da88b26f1315a)) + +### Performance Improvements + +* 【破坏性更新】错误返回信息msg字段名统一改成message,与成功的返回结构一致 ([51ab6d6](https://github.com/certd/certd/commit/51ab6d6da1bb551b55b3a6a4a9a945c8d6ace806)) +* 当域名管理中没有域名时,创建流水线时不展开域名选择框 ([bb0afe1](https://github.com/certd/certd/commit/bb0afe1fa7b0fc52fde051d24fbe6be69d52f4cc)) +* 任务步骤页面增加串行执行提示说明 ([787f6ef](https://github.com/certd/certd/commit/787f6ef52893d8dc912ee2a7a5b8ce2b73c108c9)) +* 站点监控支持指定ip地址检查 ([83d81b6](https://github.com/certd/certd/commit/83d81b64b3adb375366039e07c87d1ad79121c13)) +* AI开发插件 skills 定义初步 ([1f68fad](https://github.com/certd/certd/commit/1f68faddb97a978c5a5e731a8895b4bb0587ad83)) +* http请求增加建立连接超时配置 ([3c85602](https://github.com/certd/certd/commit/3c85602ab1fc1953cdc06a6cd75a971d14119179)) + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) ### Bug Fixes diff --git a/docs/guide/install/1panel/index.md b/docs/guide/install/1panel/index.md index dfdba6840..b301b42a6 100644 --- a/docs/guide/install/1panel/index.md +++ b/docs/guide/install/1panel/index.md @@ -16,7 +16,6 @@ https://1panel.cn/docs/installation/online_installation/ ![](./images/store-1.png) - ![](./images/store-2.png) #### 1.2 访问测试: @@ -40,6 +39,9 @@ admin/123456 1. 打开`docker-compose.yaml`,整个内容复制下来 https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml +::: tip +默认使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](./install/database.md) +::: 2. 然后到 `1Panel->容器->编排->新建编排` 输入名称,粘贴`docker-compose.yaml`原文内容 @@ -49,7 +51,10 @@ admin/123456 ![](./images/2.png) > 默认使用sqlite数据库,数据保存在`/data/certd`目录下,您可以手动备份该目录 -> certd还支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database) + + + + #### 2.2 访问测试 diff --git a/docs/guide/install/baota/index.md b/docs/guide/install/baota/index.md index bf173b930..57bb90c16 100644 --- a/docs/guide/install/baota/index.md +++ b/docs/guide/install/baota/index.md @@ -30,7 +30,9 @@ 点击确定,等待启动完成 ![](./images/2.png) -> certd默认使用sqlite数据库,另外支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database) +::: tip +默认安装使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](./install/database.md) +::: ## 二、访问应用 diff --git a/docs/guide/install/docker/index.md b/docs/guide/install/docker/index.md index 927504f44..341a16953 100644 --- a/docs/guide/install/docker/index.md +++ b/docs/guide/install/docker/index.md @@ -42,8 +42,9 @@ docker compose up -d > 如果提示 没有docker compose命令,请安装docker-compose > https://docs.docker.com/compose/install/linux/ -> certd默认使用sqlite数据库,另外还支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database) - +::: tip +默认安装使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](./install/database.md) +::: ### 3. 访问测试 diff --git a/docs/guide/start.md b/docs/guide/start.md index 49ccd510d..cdca811aa 100644 --- a/docs/guide/start.md +++ b/docs/guide/start.md @@ -28,6 +28,11 @@ https://certd.handsfree.work/ 2. [Docker方式部署](./install/docker/) 3. [源码方式部署](./install/source/) +::: tip +默认安装使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](./install/database.md) +::: + + ### 2. 访问测试 diff --git a/lerna.json b/lerna.json index c8a058702..a01dc07e0 100644 --- a/lerna.json +++ b/lerna.json @@ -9,5 +9,5 @@ } }, "npmClient": "pnpm", - "version": "1.38.12" + "version": "1.39.0" } diff --git a/package.json b/package.json index 798617d7b..e8d0d1c68 100644 --- a/package.json +++ b/package.json @@ -15,25 +15,25 @@ }, "scripts": { "start": "lerna bootstrap --hoist", - "start:server": "cd ./packages/ui/certd-server && npm start", + "start:server": "cd ./packages/ui/certd-server && pnpm start", "devb": "lerna run dev-build", "i-all": "lerna link && lerna exec npm install ", - "publish": "npm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && npm run afterpublishOnly ", - "afterpublishOnly": "npm run copylogs && time /t >trigger/build.trigger && git add ./trigger/build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && npm run commitAll", + "publish": "pnpm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && pnpm run afterpublishOnly ", + "afterpublishOnly": "pnpm run copylogs && time /t >trigger/build.trigger && git add ./trigger/build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && pnpm run commitAll", "transform-sql": "cd ./packages/ui/certd-server/db/ && node --experimental-json-modules transform.js", - "plugin-doc-gen": "cd ./packages/ui/certd-server/ && npm run export-metadata", - "commitAll": "git add . && git commit -m \"build: publish\" && git push && npm run commitPro", + "plugin-doc-gen": "cd ./packages/ui/certd-server/ && pnpm run export-metadata", + "commitAll": "git add . && git commit -m \"build: publish\" && git push && pnpm run commitPro", "commitPro": "cd ./packages/pro/ && git add . && git commit -m \"build: publish\" && git push", "copylogs": "copyfiles \"CHANGELOG.md\" ./docs/guide/changelogs/", - "prepublishOnly1": "npm run check && lerna run build ", - "prepublishOnly2": "npm run check && npm run before-build && lerna run build && npm run plugin-doc-gen", - "before-build": "npm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"", + "prepublishOnly1": "pnpm run check && lerna run build ", + "prepublishOnly2": "pnpm run check && pnpm run before-build && lerna run build && pnpm run plugin-doc-gen", + "before-build": "pnpm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"", "deploy1": "node --experimental-json-modules ./scripts/deploy.js ", "check": "node --experimental-json-modules ./scripts/publish-check.js", "init": "lerna run build", "init:dev": "lerna run build", "docs:dev": "vitepress dev docs", - "docs:build": "npm run copylogs && vitepress build docs", + "docs:build": "pnpm run copylogs && vitepress build docs", "docs:preview": "vitepress preview docs", "pub": "echo 1", "dev": "pnpm run -r --parallel compile ", diff --git a/packages/core/acme-client/CHANGELOG.md b/packages/core/acme-client/CHANGELOG.md index a66e74b69..f5a0a0a03 100644 --- a/packages/core/acme-client/CHANGELOG.md +++ b/packages/core/acme-client/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/publishlab/node-acme-client/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/acme-client + ## [1.38.12](https://github.com/publishlab/node-acme-client/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/acme-client diff --git a/packages/core/acme-client/package.json b/packages/core/acme-client/package.json index 924f60a81..090cf549f 100644 --- a/packages/core/acme-client/package.json +++ b/packages/core/acme-client/package.json @@ -3,7 +3,7 @@ "description": "Simple and unopinionated ACME client", "private": false, "author": "nmorsman", - "version": "1.38.12", + "version": "1.39.0", "type": "module", "module": "scr/index.js", "main": "src/index.js", @@ -18,7 +18,7 @@ "types" ], "dependencies": { - "@certd/basic": "^1.38.12", + "@certd/basic": "^1.39.0", "@peculiar/x509": "^1.11.0", "asn1js": "^3.0.5", "axios": "^1.9.0", @@ -70,5 +70,5 @@ "bugs": { "url": "https://github.com/publishlab/node-acme-client/issues" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/core/basic/CHANGELOG.md b/packages/core/basic/CHANGELOG.md index cb5160ce3..fedba406a 100644 --- a/packages/core/basic/CHANGELOG.md +++ b/packages/core/basic/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +### Bug Fixes + +* esxi部署失败的bug ([1e44115](https://github.com/certd/certd/commit/1e441154617e6516a9a3610412bf597128c62696)) + +### Performance Improvements + +* http请求增加建立连接超时配置 ([3c85602](https://github.com/certd/certd/commit/3c85602ab1fc1953cdc06a6cd75a971d14119179)) + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/basic diff --git a/packages/core/basic/build.md b/packages/core/basic/build.md index 70bd44af2..20c2a34a5 100644 --- a/packages/core/basic/build.md +++ b/packages/core/basic/build.md @@ -1 +1 @@ -23:18 +01:15 diff --git a/packages/core/basic/package.json b/packages/core/basic/package.json index 233194c38..2ee9578fe 100644 --- a/packages/core/basic/package.json +++ b/packages/core/basic/package.json @@ -1,7 +1,7 @@ { "name": "@certd/basic", "private": false, - "version": "1.38.12", + "version": "1.39.0", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -47,5 +47,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/core/basic/src/utils/util.request.ts b/packages/core/basic/src/utils/util.request.ts index 4a46e77b5..7fdc45097 100644 --- a/packages/core/basic/src/utils/util.request.ts +++ b/packages/core/basic/src/utils/util.request.ts @@ -323,6 +323,7 @@ export function createAgent(opts: CreateAgentOptions = {}) { { autoSelectFamily: true, autoSelectFamilyAttemptTimeout: 1000, + connectTimeout: 5000, // 连接建立超时 }, opts ); diff --git a/packages/core/pipeline/CHANGELOG.md b/packages/core/pipeline/CHANGELOG.md index 06b396f9d..6c38e66bc 100644 --- a/packages/core/pipeline/CHANGELOG.md +++ b/packages/core/pipeline/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/pipeline + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/pipeline diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json index 7fc05d669..e529c1402 100644 --- a/packages/core/pipeline/package.json +++ b/packages/core/pipeline/package.json @@ -1,7 +1,7 @@ { "name": "@certd/pipeline", "private": false, - "version": "1.38.12", + "version": "1.39.0", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -18,8 +18,8 @@ "compile": "tsc --skipLibCheck --watch" }, "dependencies": { - "@certd/basic": "^1.38.12", - "@certd/plus-core": "^1.38.12", + "@certd/basic": "^1.39.0", + "@certd/plus-core": "^1.39.0", "dayjs": "^1.11.7", "lodash-es": "^4.17.21", "reflect-metadata": "^0.1.13" @@ -45,5 +45,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/core/pipeline/src/plugin/api.ts b/packages/core/pipeline/src/plugin/api.ts index a27ceaac2..27f06be7c 100644 --- a/packages/core/pipeline/src/plugin/api.ts +++ b/packages/core/pipeline/src/plugin/api.ts @@ -123,6 +123,9 @@ export type TaskInstanceContext = { //用户信息 user: UserInfo; + //项目id + projectId?: number; + emitter: TaskEmitter; //service 容器 @@ -167,9 +170,7 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin { } if (this.ctx?.define?.onlyAdmin) { - if (!this.isAdmin()) { - throw new Error("只有管理员才能运行此任务"); - } + this.checkAdmin(); } } @@ -281,6 +282,12 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin { return this.ctx.user.role === "admin"; } + checkAdmin() { + if (!this.isAdmin()) { + throw new Error("只有“管理员”或“系统级项目”才有权限运行此插件任务"); + } + } + getStepFromPipeline(stepId: string) { let found: any = null; RunnableCollection.each(this.ctx.pipeline.stages, step => { diff --git a/packages/libs/lib-huawei/CHANGELOG.md b/packages/libs/lib-huawei/CHANGELOG.md index 9925124b2..10dcb08df 100644 --- a/packages/libs/lib-huawei/CHANGELOG.md +++ b/packages/libs/lib-huawei/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/lib-huawei + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/lib-huawei diff --git a/packages/libs/lib-huawei/package.json b/packages/libs/lib-huawei/package.json index f2151ef93..8dbc45088 100644 --- a/packages/libs/lib-huawei/package.json +++ b/packages/libs/lib-huawei/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-huawei", "private": false, - "version": "1.38.12", + "version": "1.39.0", "main": "./dist/bundle.js", "module": "./dist/bundle.js", "types": "./dist/d/index.d.ts", @@ -24,5 +24,5 @@ "prettier": "^2.8.8", "tslib": "^2.8.1" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/libs/lib-iframe/CHANGELOG.md b/packages/libs/lib-iframe/CHANGELOG.md index ddd2b03fe..bff016b71 100644 --- a/packages/libs/lib-iframe/CHANGELOG.md +++ b/packages/libs/lib-iframe/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/lib-iframe + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/lib-iframe diff --git a/packages/libs/lib-iframe/package.json b/packages/libs/lib-iframe/package.json index 3c55e93f8..417f6a052 100644 --- a/packages/libs/lib-iframe/package.json +++ b/packages/libs/lib-iframe/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-iframe", "private": false, - "version": "1.38.12", + "version": "1.39.0", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -31,5 +31,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/libs/lib-jdcloud/CHANGELOG.md b/packages/libs/lib-jdcloud/CHANGELOG.md index 16ea78683..9c2f67398 100644 --- a/packages/libs/lib-jdcloud/CHANGELOG.md +++ b/packages/libs/lib-jdcloud/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/jdcloud + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/jdcloud diff --git a/packages/libs/lib-jdcloud/package.json b/packages/libs/lib-jdcloud/package.json index a3d4af1f0..21ae368f0 100644 --- a/packages/libs/lib-jdcloud/package.json +++ b/packages/libs/lib-jdcloud/package.json @@ -1,6 +1,6 @@ { "name": "@certd/jdcloud", - "version": "1.38.12", + "version": "1.39.0", "description": "jdcloud openApi sdk", "main": "./dist/bundle.js", "module": "./dist/bundle.js", @@ -56,5 +56,5 @@ "fetch" ] }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/libs/lib-k8s/CHANGELOG.md b/packages/libs/lib-k8s/CHANGELOG.md index 93ea5de77..6f0b7733b 100644 --- a/packages/libs/lib-k8s/CHANGELOG.md +++ b/packages/libs/lib-k8s/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/lib-k8s + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/lib-k8s diff --git a/packages/libs/lib-k8s/package.json b/packages/libs/lib-k8s/package.json index 0d70949e8..9f5c4622d 100644 --- a/packages/libs/lib-k8s/package.json +++ b/packages/libs/lib-k8s/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-k8s", "private": false, - "version": "1.38.12", + "version": "1.39.0", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -17,7 +17,7 @@ "pub": "npm publish" }, "dependencies": { - "@certd/basic": "^1.38.12", + "@certd/basic": "^1.39.0", "@kubernetes/client-node": "0.21.0" }, "devDependencies": { @@ -32,5 +32,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/libs/lib-server/CHANGELOG.md b/packages/libs/lib-server/CHANGELOG.md index d5d3458b3..1cba6f03f 100644 --- a/packages/libs/lib-server/CHANGELOG.md +++ b/packages/libs/lib-server/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +### Performance Improvements + +* 【破坏性更新】错误返回信息msg字段名统一改成message,与成功的返回结构一致 ([51ab6d6](https://github.com/certd/certd/commit/51ab6d6da1bb551b55b3a6a4a9a945c8d6ace806)) + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/lib-server diff --git a/packages/libs/lib-server/package.json b/packages/libs/lib-server/package.json index 7c19870dc..e1ab382a6 100644 --- a/packages/libs/lib-server/package.json +++ b/packages/libs/lib-server/package.json @@ -1,6 +1,6 @@ { "name": "@certd/lib-server", - "version": "1.38.12", + "version": "1.39.0", "description": "midway with flyway, sql upgrade way ", "private": false, "type": "module", @@ -28,11 +28,11 @@ ], "license": "AGPL", "dependencies": { - "@certd/acme-client": "^1.38.12", - "@certd/basic": "^1.38.12", - "@certd/pipeline": "^1.38.12", - "@certd/plugin-lib": "^1.38.12", - "@certd/plus-core": "^1.38.12", + "@certd/acme-client": "^1.39.0", + "@certd/basic": "^1.39.0", + "@certd/pipeline": "^1.39.0", + "@certd/plugin-lib": "^1.39.0", + "@certd/plus-core": "^1.39.0", "@midwayjs/cache": "3.14.0", "@midwayjs/core": "3.20.11", "@midwayjs/i18n": "3.20.13", @@ -64,5 +64,5 @@ "typeorm": "^0.3.11", "typescript": "^5.4.2" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/libs/lib-server/src/basic/base-controller.ts b/packages/libs/lib-server/src/basic/base-controller.ts index 158eec008..e09f30b32 100644 --- a/packages/libs/lib-server/src/basic/base-controller.ts +++ b/packages/libs/lib-server/src/basic/base-controller.ts @@ -1,11 +1,17 @@ -import { Inject } from '@midwayjs/core'; +import { ApplicationContext, Inject } from '@midwayjs/core'; +import type {IMidwayContainer} from '@midwayjs/core'; import * as koa from '@midwayjs/koa'; import { Constants } from './constants.js'; +import { isEnterprise } from './mode.js'; + export abstract class BaseController { @Inject() ctx: koa.Context; + @ApplicationContext() + applicationContext: IMidwayContainer; + /** * 成功返回 * @param data 返回数据 @@ -28,7 +34,7 @@ export abstract class BaseController { fail(msg: string, code?: any) { return { code: code ? code : Constants.res.error.code, - msg: msg ? msg : Constants.res.error.code, + message: msg ? msg : Constants.res.error.code, }; } @@ -55,4 +61,67 @@ export abstract class BaseController { } } + async getProjectId(permission:string) { + if (!isEnterprise()) { + return null + } + let projectIdStr = this.ctx.headers["project-id"] as string; + if (!projectIdStr){ + projectIdStr = this.ctx.request.query["projectId"] as string; + } + if (!projectIdStr) { + //这里必须抛异常,否则可能会有权限问题 + throw new Error("projectId 不能为空") + } + const userId = this.getUserId() + const projectId = parseInt(projectIdStr) + await this.checkProjectPermission(userId, projectId,permission) + return projectId; + } + + async getProjectUserId(permission:string){ + let userId = this.getUserId() + const projectId = await this.getProjectId(permission) + if(projectId){ + userId = -1 // 企业管理模式下,用户id固定-1 + } + return { + projectId,userId + } + } + async getProjectUserIdRead(){ + return await this.getProjectUserId("read") + } + async getProjectUserIdWrite(){ + return await this.getProjectUserId("write") + } + async getProjectUserIdAdmin(){ + return await this.getProjectUserId("admin") + } + + async checkProjectPermission(userId: number, projectId: number,permission:string) { + const projectService:any = await this.applicationContext.getAsync("projectService"); + await projectService.checkPermission({userId,projectId,permission}) + } + + /** + * + * @param service 检查记录是否属于某用户或某项目 + * @param id + */ + async checkOwner(service:any,id:number,permission:string,allowAdmin:boolean = false){ + let { projectId,userId } = await this.getProjectUserId(permission) + const authService:any = await this.applicationContext.getAsync("authService"); + if (projectId) { + await authService.checkProjectId(service, id, projectId); + }else{ + if(allowAdmin){ + await authService.checkUserIdButAllowAdmin(this.ctx, service, id); + }else{ + await authService.checkUserId( service, id, userId); + } + } + return {projectId,userId} + } + } diff --git a/packages/libs/lib-server/src/basic/base-service.ts b/packages/libs/lib-server/src/basic/base-service.ts index eed4ecc32..00e9e5fd4 100644 --- a/packages/libs/lib-server/src/basic/base-service.ts +++ b/packages/libs/lib-server/src/basic/base-service.ts @@ -206,30 +206,41 @@ export abstract class BaseService { return await qb.getMany(); } - async checkUserId(id: any = 0, userId: number, userKey = 'userId') { - const res = await this.getRepository().findOne({ + async checkUserId(ids: number | number[] = 0, userId: number, userKey = 'userId') { + if (ids == null) { + throw new ValidateException('id不能为空'); + } + if (userId == null) { + throw new ValidateException('userId不能为空'); + } + if (!Array.isArray(ids)) { + ids = [ids]; + } + const res = await this.getRepository().find({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore select: { [userKey]: true }, where: { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - id, + id: In(ids), + [userKey]: userId, }, }); - if (!res || res[userKey] === userId) { + if (!res || res.length === ids.length) { return; } throw new PermissionException('权限不足'); } - async batchDelete(ids: number[], userId: number) { - if(userId >0){ + async batchDelete(ids: number[], userId: number,projectId?:number) { + if(userId!=null){ const list = await this.getRepository().find({ where: { // @ts-ignore id: In(ids), userId, + projectId, }, }) // @ts-ignore @@ -242,4 +253,19 @@ export abstract class BaseService { async findOne(options: FindOneOptions) { return await this.getRepository().findOne(options); } + } + +export function checkUserProjectParam(userId: number, projectId: number) { + if (projectId != null ){ + if( userId !==-1) { + throw new ValidateException('userId projectId 错误'); + } + return true + }else{ + if( userId != null) { + return true + } + throw new ValidateException('userId不能为空'); + } +} \ No newline at end of file diff --git a/packages/libs/lib-server/src/basic/exception/base-exception.ts b/packages/libs/lib-server/src/basic/exception/base-exception.ts index 3ee44d788..65959654d 100644 --- a/packages/libs/lib-server/src/basic/exception/base-exception.ts +++ b/packages/libs/lib-server/src/basic/exception/base-exception.ts @@ -4,7 +4,7 @@ export class BaseException extends Error { code: number; data?:any - constructor(name, code, message,data?:any) { + constructor(name: string, code: number, message: string ,data?:any) { super(message); this.name = name; this.code = code; diff --git a/packages/libs/lib-server/src/basic/index.ts b/packages/libs/lib-server/src/basic/index.ts index 184d3339e..d012ec35c 100644 --- a/packages/libs/lib-server/src/basic/index.ts +++ b/packages/libs/lib-server/src/basic/index.ts @@ -5,3 +5,4 @@ export * from './enum-item.js'; export * from './exception/index.js'; export * from './result.js'; export * from './base-service.js'; +export * from "./mode.js" \ No newline at end of file diff --git a/packages/libs/lib-server/src/basic/mode.ts b/packages/libs/lib-server/src/basic/mode.ts new file mode 100644 index 000000000..2f8318150 --- /dev/null +++ b/packages/libs/lib-server/src/basic/mode.ts @@ -0,0 +1,12 @@ +let adminMode = "saas" + +export function setAdminMode(mode:string = "saas"){ + adminMode = mode +} +export function getAdminMode(){ + return adminMode +} + +export function isEnterprise(){ + return adminMode === "enterprise" +} \ No newline at end of file diff --git a/packages/libs/lib-server/src/basic/result.ts b/packages/libs/lib-server/src/basic/result.ts index ca42e8d14..0d1dcc375 100644 --- a/packages/libs/lib-server/src/basic/result.ts +++ b/packages/libs/lib-server/src/basic/result.ts @@ -1,19 +1,19 @@ export class Result { code: number; - msg: string; + message: string; data: T; - constructor(code, msg, data?) { + constructor(code, message, data?) { this.code = code; - this.msg = msg; + this.message = message; this.data = data; } - static error(code = 1, msg, data?: any) { - return new Result(code, msg, data); + static error(code = 1, message, data?: any) { + return new Result(code, message, data); } - static success(msg, data?) { - return new Result(0, msg, data); + static success(message, data?) { + return new Result(0, message, data); } } diff --git a/packages/libs/lib-server/src/system/settings/service/models.ts b/packages/libs/lib-server/src/system/settings/service/models.ts index 892fc2546..9c6f3e7cd 100644 --- a/packages/libs/lib-server/src/system/settings/service/models.ts +++ b/packages/libs/lib-server/src/system/settings/service/models.ts @@ -65,6 +65,8 @@ export class SysPublicSettings extends BaseSettings { }> = {}; notice?: string; + + adminMode?: "enterprise" | "saas" = "saas"; } export class SysPrivateSettings extends BaseSettings { diff --git a/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts b/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts index 5cb56673d..5ae977b12 100644 --- a/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts +++ b/packages/libs/lib-server/src/system/settings/service/sys-settings-service.ts @@ -7,9 +7,10 @@ import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, Sy import { getAllSslProviderDomains, setSslProviderReverseProxies } from '@certd/acme-client'; import { cache, logger, mergeUtils, setGlobalProxy } from '@certd/basic'; import * as dns from 'node:dns'; -import { BaseService } from '../../../basic/index.js'; +import { BaseService, setAdminMode } from '../../../basic/index.js'; import { executorQueue } from '../../basic/service/executor-queue.js'; -const {merge} = mergeUtils; +import { isComm } from '@certd/plus-core'; +const { merge } = mergeUtils; /** * 设置 */ @@ -116,7 +117,15 @@ export class SysSettingsService extends BaseService { } async savePublicSettings(bean: SysPublicSettings) { + if(isComm()){ + if(bean.adminMode === 'enterprise'){ + throw new Error("商业版不支持使用企业管理模式") + } + } + await this.saveSetting(bean); + //让设置生效 + await this.reloadPublicSettings(); } async getPrivateSettings(): Promise { @@ -137,23 +146,33 @@ export class SysSettingsService extends BaseService { await this.reloadPrivateSettings(); } + async reloadSettings() { + await this.reloadPrivateSettings() + await this.reloadPublicSettings() + } + + async reloadPublicSettings() { + const publicSetting = await this.getPublicSettings() + setAdminMode(publicSetting.adminMode) + } + async reloadPrivateSettings() { - const bean = await this.getPrivateSettings(); + const privateSetting = await this.getPrivateSettings(); const opts = { - httpProxy: bean.httpProxy, - httpsProxy: bean.httpsProxy, + httpProxy: privateSetting.httpProxy, + httpsProxy: privateSetting.httpsProxy, }; setGlobalProxy(opts); - if (bean.dnsResultOrder) { - dns.setDefaultResultOrder(bean.dnsResultOrder as any); + if (privateSetting.dnsResultOrder) { + dns.setDefaultResultOrder(privateSetting.dnsResultOrder as any); } - if (bean.pipelineMaxRunningCount){ - executorQueue.setMaxRunningCount(bean.pipelineMaxRunningCount); + if (privateSetting.pipelineMaxRunningCount) { + executorQueue.setMaxRunningCount(privateSetting.pipelineMaxRunningCount); } - setSslProviderReverseProxies(bean.reverseProxies); + setSslProviderReverseProxies(privateSetting.reverseProxies); } async updateByKey(key: string, setting: any) { diff --git a/packages/libs/lib-server/src/user/access/entity/access.ts b/packages/libs/lib-server/src/user/access/entity/access.ts index d69128c72..fa8349370 100644 --- a/packages/libs/lib-server/src/user/access/entity/access.ts +++ b/packages/libs/lib-server/src/user/access/entity/access.ts @@ -8,7 +8,7 @@ export class AccessEntity { @PrimaryGeneratedColumn() id: number; @Column({ name: 'user_id', comment: '用户id' }) - userId: number; + userId: number; // 0为系统级别, -1为企业,大于1为用户 @Column({ comment: '名称', length: 100 }) name: string; @@ -21,6 +21,9 @@ export class AccessEntity { @Column({ name: 'encrypt_setting', comment: '已加密设置', length: 10240, nullable: true }) encryptSetting: string; + @Column({ name: 'project_id', comment: '项目id' }) + projectId: number; + @Column({ name: 'create_time', comment: '创建时间', diff --git a/packages/libs/lib-server/src/user/access/service/access-getter.ts b/packages/libs/lib-server/src/user/access/service/access-getter.ts index 0fd2d3f19..1bbb75ea8 100644 --- a/packages/libs/lib-server/src/user/access/service/access-getter.ts +++ b/packages/libs/lib-server/src/user/access/service/access-getter.ts @@ -2,17 +2,19 @@ import { IAccessService } from '@certd/pipeline'; export class AccessGetter implements IAccessService { userId: number; - getter: (id: any, userId?: number) => Promise; - constructor(userId: number, getter: (id: any, userId: number) => Promise) { + projectId?: number; + getter: (id: any, userId?: number, projectId?: number) => Promise; + constructor(userId: number, projectId: number, getter: (id: any, userId: number, projectId?: number) => Promise) { this.userId = userId; + this.projectId = projectId; this.getter = getter; } async getById(id: any) { - return await this.getter(id, this.userId); + return await this.getter(id, this.userId, this.projectId); } async getCommonById(id: any) { - return await this.getter(id, 0); + return await this.getter(id, 0,null); } } diff --git a/packages/libs/lib-server/src/user/access/service/access-service.ts b/packages/libs/lib-server/src/user/access/service/access-service.ts index 756e72d1d..f3e1ee29d 100644 --- a/packages/libs/lib-server/src/user/access/service/access-service.ts +++ b/packages/libs/lib-server/src/user/access/service/access-service.ts @@ -129,10 +129,11 @@ export class AccessService extends BaseService { id: entity.id, name: entity.name, userId: entity.userId, + projectId: entity.projectId, }; } - async getAccessById(id: any, checkUserId: boolean, userId?: number): Promise { + async getAccessById(id: any, checkUserId: boolean, userId?: number, projectId?: number): Promise { const entity = await this.info(id); if (entity == null) { throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`); @@ -145,6 +146,9 @@ export class AccessService extends BaseService { throw new PermissionException('您对该Access授权无访问权限'); } } + if (projectId != null && projectId !== entity.projectId) { + throw new PermissionException('您对该Access授权无访问权限'); + } // const access = accessRegistry.get(entity.type); const setting = this.decryptAccessEntity(entity); @@ -152,12 +156,12 @@ export class AccessService extends BaseService { id: entity.id, ...setting, }; - const accessGetter = new AccessGetter(userId, this.getById.bind(this)); + const accessGetter = new AccessGetter(userId,projectId, this.getById.bind(this)); return await newAccess(entity.type, input,accessGetter); } - async getById(id: any, userId: number): Promise { - return await this.getAccessById(id, true, userId); + async getById(id: any, userId: number, projectId?: number): Promise { + return await this.getAccessById(id, true, userId, projectId); } decryptAccessEntity(entity: AccessEntity): any { @@ -188,23 +192,25 @@ export class AccessService extends BaseService { } - async getSimpleByIds(ids: number[], userId: any) { + async getSimpleByIds(ids: number[], userId: any, projectId?: number) { if (ids.length === 0) { return []; } - if (!userId) { + if (userId==null) { return []; } return await this.repository.find({ where: { id: In(ids), userId, + projectId, }, select: { id: true, name: true, type: true, - userId:true + userId:true, + projectId:true, }, }); diff --git a/packages/libs/lib-server/src/user/addon/entity/addon.ts b/packages/libs/lib-server/src/user/addon/entity/addon.ts index 4d16fb43b..438de2237 100644 --- a/packages/libs/lib-server/src/user/addon/entity/addon.ts +++ b/packages/libs/lib-server/src/user/addon/entity/addon.ts @@ -28,6 +28,9 @@ export class AddonEntity { @Column({ name: 'is_default', comment: '是否默认', nullable: false, default: false }) isDefault: boolean; + @Column({ name: 'project_id', comment: '项目id' }) + projectId: number; + @Column({ name: 'create_time', diff --git a/packages/libs/lib-server/src/user/addon/service/addon-service.ts b/packages/libs/lib-server/src/user/addon/service/addon-service.ts index 55928d9bf..38c9636c0 100644 --- a/packages/libs/lib-server/src/user/addon/service/addon-service.ts +++ b/packages/libs/lib-server/src/user/addon/service/addon-service.ts @@ -70,7 +70,8 @@ export class AddonService extends BaseService { name: entity.name, userId: entity.userId, addonType: entity.addonType, - type: entity.type + type: entity.type, + projectId: entity.projectId }; } @@ -84,17 +85,18 @@ export class AddonService extends BaseService { } - async getSimpleByIds(ids: number[], userId: any) { + async getSimpleByIds(ids: number[], userId: any,projectId?:number) { if (ids.length === 0) { return []; } - if (!userId) { + if (userId==null) { return []; } return await this.repository.find({ where: { id: In(ids), - userId + userId, + projectId }, select: { id: true, @@ -109,11 +111,12 @@ export class AddonService extends BaseService { } - async getDefault(userId: number, addonType: string): Promise { + async getDefault(userId: number, addonType: string,projectId?:number): Promise { const res = await this.repository.findOne({ where: { userId, - addonType + addonType, + projectId }, order: { isDefault: "DESC" @@ -133,21 +136,23 @@ export class AddonService extends BaseService { type: res.type, name: res.name, userId: res.userId, - setting + setting, + projectId: res.projectId }; } - async setDefault(id: number, userId: number, addonType: string) { + async setDefault(id: number, userId: number, addonType: string,projectId?:number) { if (!id) { throw new ValidateException("id不能为空"); } - if (!userId) { + if (userId==null) { throw new ValidateException("userId不能为空"); } await this.repository.update( { userId, - addonType + addonType, + projectId }, { isDefault: false @@ -157,7 +162,8 @@ export class AddonService extends BaseService { { id, userId, - addonType + addonType, + projectId }, { isDefault: true @@ -165,12 +171,12 @@ export class AddonService extends BaseService { ); } - async getOrCreateDefault(opts: { addonType: string, type: string, inputs: any, userId: any }) { - const { addonType, type, inputs, userId } = opts; + async getOrCreateDefault(opts: { addonType: string, type: string, inputs: any, userId: any,projectId?:number }) { + const { addonType, type, inputs, userId,projectId } = opts; const addonDefine = this.getDefineByType(type, addonType); - const defaultConfig = await this.getDefault(userId, addonType); + const defaultConfig = await this.getDefault(userId, addonType,projectId); if (defaultConfig) { return defaultConfig; } @@ -183,17 +189,19 @@ export class AddonService extends BaseService { type: type, name: addonDefine.title, setting: JSON.stringify(setting), - isDefault: true + isDefault: true, + projectId }); return this.buildAddonInstanceConfig(res); } - async getOneByType(req:{addonType:string,type:string,userId:number}) { + async getOneByType(req:{addonType:string,type:string,userId:number,projectId?:number}) { return await this.repository.findOne({ where: { addonType: req.addonType, type: req.type, - userId: req.userId + userId: req.userId, + projectId: req.projectId } }); } diff --git a/packages/libs/midway-flyway-js/CHANGELOG.md b/packages/libs/midway-flyway-js/CHANGELOG.md index 2ffeea524..e270d4acf 100644 --- a/packages/libs/midway-flyway-js/CHANGELOG.md +++ b/packages/libs/midway-flyway-js/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/midway-flyway-js + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/midway-flyway-js diff --git a/packages/libs/midway-flyway-js/package.json b/packages/libs/midway-flyway-js/package.json index 5c936eb15..f8fab7328 100644 --- a/packages/libs/midway-flyway-js/package.json +++ b/packages/libs/midway-flyway-js/package.json @@ -1,6 +1,6 @@ { "name": "@certd/midway-flyway-js", - "version": "1.38.12", + "version": "1.39.0", "description": "midway with flyway, sql upgrade way ", "private": false, "type": "module", @@ -46,5 +46,5 @@ "typeorm": "^0.3.11", "typescript": "^5.4.2" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/plugins/plugin-cert/CHANGELOG.md b/packages/plugins/plugin-cert/CHANGELOG.md index cd4065b26..0d5f8518e 100644 --- a/packages/plugins/plugin-cert/CHANGELOG.md +++ b/packages/plugins/plugin-cert/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/plugin-cert + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/plugin-cert diff --git a/packages/plugins/plugin-cert/package.json b/packages/plugins/plugin-cert/package.json index 234a3ca3f..ab117ad0f 100644 --- a/packages/plugins/plugin-cert/package.json +++ b/packages/plugins/plugin-cert/package.json @@ -1,7 +1,7 @@ { "name": "@certd/plugin-cert", "private": false, - "version": "1.38.12", + "version": "1.39.0", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -17,10 +17,10 @@ "compile": "tsc --skipLibCheck --watch" }, "dependencies": { - "@certd/acme-client": "^1.38.12", - "@certd/basic": "^1.38.12", - "@certd/pipeline": "^1.38.12", - "@certd/plugin-lib": "^1.38.12", + "@certd/acme-client": "^1.39.0", + "@certd/basic": "^1.39.0", + "@certd/pipeline": "^1.39.0", + "@certd/plugin-lib": "^1.39.0", "psl": "^1.9.0", "punycode.js": "^2.3.1" }, @@ -38,5 +38,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/plugins/plugin-lib/CHANGELOG.md b/packages/plugins/plugin-lib/CHANGELOG.md index a66e8026e..2aba3b7fd 100644 --- a/packages/plugins/plugin-lib/CHANGELOG.md +++ b/packages/plugins/plugin-lib/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +**Note:** Version bump only for package @certd/plugin-lib + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) **Note:** Version bump only for package @certd/plugin-lib diff --git a/packages/plugins/plugin-lib/package.json b/packages/plugins/plugin-lib/package.json index ae03e35ee..af28f4878 100644 --- a/packages/plugins/plugin-lib/package.json +++ b/packages/plugins/plugin-lib/package.json @@ -1,7 +1,7 @@ { "name": "@certd/plugin-lib", "private": false, - "version": "1.38.12", + "version": "1.39.0", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -22,10 +22,10 @@ "@alicloud/pop-core": "^1.7.10", "@alicloud/tea-util": "^1.4.11", "@aws-sdk/client-s3": "^3.964.0", - "@certd/acme-client": "^1.38.12", - "@certd/basic": "^1.38.12", - "@certd/pipeline": "^1.38.12", - "@certd/plus-core": "^1.38.12", + "@certd/acme-client": "^1.39.0", + "@certd/basic": "^1.39.0", + "@certd/pipeline": "^1.39.0", + "@certd/plus-core": "^1.39.0", "@kubernetes/client-node": "0.21.0", "ali-oss": "^6.22.0", "basic-ftp": "^5.0.5", @@ -57,5 +57,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2" + "gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a" } diff --git a/packages/ui/certd-client/CHANGELOG.md b/packages/ui/certd-client/CHANGELOG.md index 424c83373..7086b59a9 100644 --- a/packages/ui/certd-client/CHANGELOG.md +++ b/packages/ui/certd-client/CHANGELOG.md @@ -3,6 +3,24 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07) + +### Bug Fixes + +* 修复发件邮箱无法输入的bug ([27b0348](https://github.com/certd/certd/commit/27b0348e1d3d752f418f851965d6afbc26c0160c)) +* 修复复制流水线保存后丢失分组和排序号的问题 ([bc32648](https://github.com/certd/certd/commit/bc326489abc1d50a0930b4f47aa2d62d3a486798)) +* 修复获取群辉deviceid报错的bug ([79be392](https://github.com/certd/certd/commit/79be392775a2c91848dd5a66a2618adc4e4b48f6)) +* 修复偶尔下载证书报未授权的错误 ([316537e](https://github.com/certd/certd/commit/316537eb4dcbe5ec57784e8bf95ee3cdfd21dce7)) + +### Features + +* 支持企业级管理模式,项目管理,细分权限 ([3734083](https://github.com/certd/certd/commit/37340838b6a61a94b86bfa13cf5da88b26f1315a)) + +### Performance Improvements + +* 当域名管理中没有域名时,创建流水线时不展开域名选择框 ([bb0afe1](https://github.com/certd/certd/commit/bb0afe1fa7b0fc52fde051d24fbe6be69d52f4cc)) +* 任务步骤页面增加串行执行提示说明 ([787f6ef](https://github.com/certd/certd/commit/787f6ef52893d8dc912ee2a7a5b8ce2b73c108c9)) + ## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18) ### Bug Fixes diff --git a/packages/ui/certd-client/package.json b/packages/ui/certd-client/package.json index ee72a866a..fd290604b 100644 --- a/packages/ui/certd-client/package.json +++ b/packages/ui/certd-client/package.json @@ -1,6 +1,6 @@ { "name": "@certd/ui-client", - "version": "1.38.12", + "version": "1.39.0", "private": true, "scripts": { "dev": "vite --open", @@ -106,8 +106,8 @@ "zod-defaults": "^0.1.3" }, "devDependencies": { - "@certd/lib-iframe": "^1.38.12", - "@certd/pipeline": "^1.38.12", + "@certd/lib-iframe": "^1.39.0", + "@certd/pipeline": "^1.39.0", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@types/chai": "^4.3.12", diff --git a/packages/ui/certd-client/public/static/images/ent/admin_mode.png b/packages/ui/certd-client/public/static/images/ent/admin_mode.png new file mode 100644 index 000000000..aa42f96e7 Binary files /dev/null and b/packages/ui/certd-client/public/static/images/ent/admin_mode.png differ diff --git a/packages/ui/certd-client/src/api/service.ts b/packages/ui/certd-client/src/api/service.ts index fc4df40d4..a38d92c49 100644 --- a/packages/ui/certd-client/src/api/service.ts +++ b/packages/ui/certd-client/src/api/service.ts @@ -3,6 +3,7 @@ import { get } from "lodash-es"; import { errorLog, errorCreate } from "./tools"; import { env } from "/src/utils/util.env"; import { useUserStore } from "/@/store/user"; +import { useProjectStore } from "../store/project"; export class CodeError extends Error { code: number; @@ -135,21 +136,29 @@ function createService() { */ function createRequestFunction(service: any) { return function (config: any) { - const configDefault = { + const configDefault: any = { headers: { "Content-Type": get(config, "headers.Content-Type", "application/json"), - }, + } as any, timeout: 30000, baseURL: env.API, data: {}, + params: {}, }; + const projectStore = useProjectStore(); + const userStore = useUserStore(); const token = userStore.getToken; if (token != null) { // @ts-ignore configDefault.headers.Authorization = token; } - return service(Object.assign(configDefault, config)); + Object.assign(configDefault, config); + + if (!configDefault.params.projectId && projectStore.isEnterprise && !config.url.startsWith("/sys") && !config.url.startsWith("http")) { + configDefault.params.projectId = projectStore.currentProject?.id; + } + return service(configDefault); }; } diff --git a/packages/ui/certd-client/src/api/tools.ts b/packages/ui/certd-client/src/api/tools.ts index 193a570c1..f889197eb 100644 --- a/packages/ui/certd-client/src/api/tools.ts +++ b/packages/ui/certd-client/src/api/tools.ts @@ -19,30 +19,30 @@ export function parse(jsonString = "{}", defaultValue = {}) { /** * @description 接口请求返回 * @param {Any} data 返回值 - * @param {String} msg 状态信息 + * @param {String} message 状态信息 * @param {Number} code 状态码 */ -export function response(data = {}, msg = "", code = 0) { - return [200, { code, msg, data }]; +export function response(data = {}, message = "", code = 0) { + return [200, { code, message, data }]; } /** * @description 接口请求返回 正确返回 * @param {Any} data 返回值 - * @param {String} msg 状态信息 + * @param {String} message 状态信息 */ -export function responseSuccess(data = {}, msg = "成功") { - return response(data, msg); +export function responseSuccess(data = {}, message = "成功") { + return response(data, message); } /** * @description 接口请求返回 错误返回 * @param {Any} data 返回值 - * @param {String} msg 状态信息 + * @param {String} message 状态信息 * @param {Number} code 状态码 */ -export function responseError(data = {}, msg = "请求失败", code = 500) { - return response(data, msg, code); +export function responseError(data = {}, message = "请求失败", code = 500) { + return response(data, message, code); } /** diff --git a/packages/ui/certd-client/src/components/index.ts b/packages/ui/certd-client/src/components/index.ts index d751e9c7d..418472789 100644 --- a/packages/ui/certd-client/src/components/index.ts +++ b/packages/ui/certd-client/src/components/index.ts @@ -16,6 +16,9 @@ import { defineAsyncComponent } from "vue"; import NotificationSelector from "../views/certd/notification/notification-selector/index.vue"; import EmailSelector from "./email-selector/index.vue"; import ValidTimeFormat from "./valid-time-format.vue"; +import ProjectSelector from "./project-selector/index.vue"; +import ProjectCurrent from "./project-selector/project-current.vue"; + export default { install(app: any) { app.component( @@ -45,5 +48,7 @@ export default { app.component("ExpiresTimeText", ExpiresTimeText); app.use(vip); app.use(Plugins); + app.component("ProjectSelector", ProjectSelector); + app.component("ProjectCurrent", ProjectCurrent); }, }; diff --git a/packages/ui/certd-client/src/components/plugins/common/domain-selector.vue b/packages/ui/certd-client/src/components/plugins/common/domain-selector.vue index 589156f41..1c761873e 100644 --- a/packages/ui/certd-client/src/components/plugins/common/domain-selector.vue +++ b/packages/ui/certd-client/src/components/plugins/common/domain-selector.vue @@ -83,6 +83,7 @@ const props = defineProps<{ search?: boolean; pager?: boolean; value?: any[]; + open?: boolean; }>(); const emit = defineEmits<{ diff --git a/packages/ui/certd-client/src/components/project-selector/api.ts b/packages/ui/certd-client/src/components/project-selector/api.ts new file mode 100644 index 000000000..f50033e89 --- /dev/null +++ b/packages/ui/certd-client/src/components/project-selector/api.ts @@ -0,0 +1,9 @@ +import { request } from "/src/api/service"; + +export async function MyProjectList() { + return await request({ + url: "/enterprise/project/list", + method: "post", + data: {}, + }); +} diff --git a/packages/ui/certd-client/src/components/project-selector/index.vue b/packages/ui/certd-client/src/components/project-selector/index.vue new file mode 100644 index 000000000..37139a6a2 --- /dev/null +++ b/packages/ui/certd-client/src/components/project-selector/index.vue @@ -0,0 +1,68 @@ + + + + diff --git a/packages/ui/certd-client/src/components/project-selector/project-current.vue b/packages/ui/certd-client/src/components/project-selector/project-current.vue new file mode 100644 index 000000000..073b65b82 --- /dev/null +++ b/packages/ui/certd-client/src/components/project-selector/project-current.vue @@ -0,0 +1,28 @@ + + + + diff --git a/packages/ui/certd-client/src/layout/layout-basic.vue b/packages/ui/certd-client/src/layout/layout-basic.vue index df15dbeac..944c7b41c 100644 --- a/packages/ui/certd-client/src/layout/layout-basic.vue +++ b/packages/ui/certd-client/src/layout/layout-basic.vue @@ -10,6 +10,7 @@ import PageFooter from "./components/footer/index.vue"; import { useRouter } from "vue-router"; import MaxKBChat from "/@/components/ai/index.vue"; import { useI18n } from "vue-i18n"; +import { useProjectStore } from "../store/project"; const { t } = useI18n(); @@ -77,10 +78,17 @@ const openChat = (q: string) => { chatBox.value.openChat({ q }); }; provide("fn:ai.open", openChat); + +const projectStore = useProjectStore(); diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts index 06a506d92..4d72810aa 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts @@ -1,4 +1,4 @@ -//注意: @符号是保留字符,需要用{'@'}替换 +//注意: @符号是保留字符,需要用{'@'}替换, AI请务必提醒我修改 export default { app: { crud: { @@ -162,6 +162,8 @@ export default { triggerType: "Trigger Type", pipelineId: "Pipeline Id", nextRunTime: "Next Run Time", + projectName: "Project", + adminId: "Admin", }, pi: { @@ -210,6 +212,15 @@ export default { orderManager: "Order Management", userSuites: "User Suites", netTest: "Network Test", + + enterpriseSetting: "Enterprise Settings", + projectManager: "Project Management", + projectUserManager: "Project User Management", + myProjectManager: "My Projects", + myProjectDetail: "Project Detail", + projectJoin: "Join Project", + currentProject: "Current Project", + projectMemberManager: "Project Member", }, certificateRepo: { title: "Certificate Repository", @@ -718,6 +729,10 @@ export default { pipelineSetting: "Pipeline Settings", oauthSetting: "OAuth2 Settings", networkSetting: "Network Settings", + adminModeSetting: "Admin Mode Settings", + adminModeHelper: "enterprise mode : allow to create and manage pipelines, roles, users, etc.\n saas mode : only allow to create and manage pipelines", + enterpriseMode: "Enterprise Mode", + saasMode: "SaaS Mode", showRunStrategy: "Show RunStrategy", showRunStrategyHelper: "Allow modify the run strategy of the task", @@ -796,6 +811,40 @@ export default { fromType: "From Type", expirationDate: "Expiration Date", }, + ent: { + projectName: "Project Name", + projectDescription: "Project Description", + projectDetailManager: "Project Detail", + projectDetailDescription: "Manage Project Members", + projectPermission: "Permission", + permission: { + read: "Read", + write: "Write", + admin: "Admin", + }, + projectMemberStatus: "Member Status", + + isSystem: "Is System Project", + isSystemHelper: "System-level projects allow running admin plugins", + }, + project: { + noProjectJoined: "You haven't joined any projects yet", + applyToJoin: "Please apply to join a project to start using", + systemProjects: "System Project List", + createdAt: "Created At", + applyJoin: "Apply to Join", + noSystemProjects: "No system projects available", + fetchFailed: "Failed to fetch project list", + applySuccess: "Application successful, waiting for admin approval", + applyFailed: "Application failed, please try again later", + leave: "Leave Project", + leaveSuccess: "Leave project successful", + leaveFailed: "Leave project failed, please try again later", + applyJoinConfirm: "Are you sure you want to apply to join this project?", + leaveConfirm: "Are you sure you want to leave this project?", + viewDetail: "View Detail", + projectManage: "Project Manage", + }, addonSelector: { select: "Select", placeholder: "select please", diff --git a/packages/ui/certd-client/src/locales/langs/en-US/monitor.ts b/packages/ui/certd-client/src/locales/langs/en-US/monitor.ts index f9543abf4..505d5f891 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/monitor.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/monitor.ts @@ -1,4 +1,6 @@ export default { + ipAddress: "IP地址", + ipAddressHelper: "填写则固定检查此IP,不从DNS获取域名的IP地址", remark: "Remark", title: "Site Certificate Monitoring", description: "Check website certificates' expiration at 0:00 daily; reminders sent 10 days before expiration (using default notification channel);", diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts index 7d02863cf..6a15e954a 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -1,4 +1,4 @@ -//注意: @符号是保留字符,需要用{'@'}替换 +//注意: @符号是保留字符,需要用{'@'}替换, AI请务必提醒我修改 export default { app: { crud: { @@ -169,6 +169,8 @@ export default { triggerType: "触发类型", pipelineId: "流水线Id", nextRunTime: "下次运行时间", + projectName: "项目", + adminId: "管理员", }, pi: { validTime: "流水线有效期", @@ -216,6 +218,14 @@ export default { orderManager: "订单管理", userSuites: "用户套餐", netTest: "网络测试", + enterpriseManager: "企业管理设置", + projectManager: "项目管理", + enterpriseSetting: "企业设置", + myProjectManager: "我的项目", + myProjectDetail: "项目详情", + projectJoin: "加入项目", + currentProject: "当前项目", + projectMemberManager: "项目成员管理", }, certificateRepo: { title: "证书仓库", @@ -720,6 +730,12 @@ export default { pipelineSetting: "流水线设置", oauthSetting: "第三方登录", networkSetting: "网络设置", + adminModeSetting: "管理模式", + adminModeHelper: "企业管理模式: 企业内部使用,通过项目来隔离权限,流水线、授权数据属于项目。\nsaas模式:供外部用户注册使用,各个用户之间数据隔离,流水线、授权数据属于用户。", + + adminMode: "管理模式", + enterpriseMode: "企业模式", + saasMode: "SaaS模式", showRunStrategy: "显示运行策略选择", showRunStrategyHelper: "任务设置中是否允许选择运行策略", @@ -810,4 +826,38 @@ export default { select: "选择", placeholder: "请选择", }, + ent: { + projectName: "项目名称", + projectDescription: "项目描述", + projectDetailManager: "项目详情", + projectDetailDescription: "管理项目成员", + projectPermission: "权限", + permission: { + read: "查看", + write: "修改", + admin: "管理员", + }, + projectMemberStatus: "成员状态", + + isSystem: "是否系统项目", + isSystemHelper: "系统级项目允许运行管理员插件", + }, + project: { + noProjectJoined: "您还没有加入任何项目", + applyToJoin: "请申请加入项目以开始使用", + projectList: "项目列表", + createdAt: "创建时间", + applyJoin: "申请加入", + noProjects: "暂无项目", + fetchFailed: "获取项目列表失败", + applySuccess: "申请成功,等待管理员审核", + applyFailed: "申请失败,请稍后重试", + leave: "退出项目", + leaveSuccess: "退出项目成功", + leaveFailed: "退出项目失败,请稍后重试", + applyJoinConfirm: "确认加入项目?", + leaveConfirm: "确认退出项目?", + viewDetail: "查看详情", + projectManage: "项目管理", + }, }; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/monitor.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/monitor.ts index f3dbfcd86..ddd609166 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/monitor.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/monitor.ts @@ -1,4 +1,6 @@ export default { + ipAddress: "IP地址", + ipAddressHelper: "填写则固定检查此IP,不从DNS获取域名的IP地址", remark: "备注", title: "站点证书监控", description: "每天0点,检查网站证书的过期时间,到期前10天时将发出提醒(使用默认通知渠道);", diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts index e4edf5782..2877bce9f 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/vip.ts @@ -55,7 +55,7 @@ export default { email_webhook_notifications: "邮件、webhook通知方式", professional_edition: "专业版", - open_source_support: "开源需要您的赞助支持", + open_source_support: "开源需要您的赞助支持,个人和企业内部使用", vip_group_priority: "可加VIP群,您的需求将优先实现", unlimited_site_certificate_monitoring: "站点证书监控无限制", more_notification_methods: "更多通知方式", @@ -66,13 +66,13 @@ export default { get_after_support: "立即赞助", business_edition: "商业版", - commercial_license: "商业授权,可对外运营", + commercial_license: "商业授权,可对外运营,提供SaaS服务", all_pro_privileges: "拥有专业版所有特权", allow_commercial_use_modify_logo_title: "允许商用,可修改logo、标题", data_statistics: "数据统计", plugin_management: "插件管理", unlimited_multi_users: "多用户无限制", - support_user_payment: "支持用户支付", + support_user_payment: "支持用户支付(购买套餐,按流水线条数、域名数量、部署次数计费)", activate: "激活", get_pro_code_after_support: "前往获取", business_contact_author: "", diff --git a/packages/ui/certd-client/src/plugin/fast-crud/index.tsx b/packages/ui/certd-client/src/plugin/fast-crud/index.tsx index 50922a486..0ffd43247 100644 --- a/packages/ui/certd-client/src/plugin/fast-crud/index.tsx +++ b/packages/ui/certd-client/src/plugin/fast-crud/index.tsx @@ -123,7 +123,6 @@ function install(app: App, options: any = {}) { if (scope.key === "__blank__") { return false; } - //不能用 !scope.value , 否则switch组件设置为关之后就消失了 const { value, key, props } = scope; return !value && key != "_index" && value != false && value != 0; diff --git a/packages/ui/certd-client/src/plugin/permission/use-crud-permission.ts b/packages/ui/certd-client/src/plugin/permission/use-crud-permission.ts index 71618bde0..f19f582c8 100644 --- a/packages/ui/certd-client/src/plugin/permission/use-crud-permission.ts +++ b/packages/ui/certd-client/src/plugin/permission/use-crud-permission.ts @@ -1,12 +1,15 @@ import { usePermission } from "/@/plugin/permission"; import { merge as LodashMerge } from "lodash-es"; +import { useProjectStore } from "/@/store/project"; export type UseCrudPermissionExtraProps = { hasActionPermission: (action: string) => boolean; }; export type UseCrudPermissionExtra = (props: UseCrudPermissionExtraProps) => any; export type UseCrudPermissionCompProps = { - prefix: string; + isProjectPermission?: boolean; + projectPermission?: string; + prefix?: string; extra?: UseCrudPermissionExtra; [key: string]: any; }; @@ -20,14 +23,31 @@ export type UseCrudPermissionProps = { export function useCrudPermission({ permission }: UseCrudPermissionProps) { const { hasPermissions } = usePermission(); - const prefix = permission instanceof Object ? permission.prefix : permission; - //根据权限显示按钮 - function hasActionPermission(action: string) { + let hasActionPermission = (action: string) => { if (!prefix) { return true; } return hasPermissions(prefix + ":" + action); + }; + + let per: UseCrudPermissionCompProps = permission as any; + if (per == null) { + per = { prefix: "" }; + } + if (typeof per === "string") { + per = { + prefix: per || "", + }; + } + let prefix = per.prefix || ""; + const isProjectPermission = per.isProjectPermission || false; + if (isProjectPermission) { + const projectStore = useProjectStore(); + prefix = ""; + hasActionPermission = function (value: string) { + return projectStore.hasPermission(value as string); + }; } function buildCrudPermission(): any { @@ -36,25 +56,45 @@ export function useCrudPermission({ permission }: UseCrudPermissionProps) { } let extra = {}; - if (permission instanceof Object) { - extra = permission.extra; - if (permission.extra && permission.extra instanceof Function) { - extra = permission.extra({ hasActionPermission }); + if (per instanceof Object) { + extra = per.extra; + if (per.extra && per.extra instanceof Function) { + extra = per.extra({ hasActionPermission }); } } + let viewPermission = "view"; + if (isProjectPermission) { + viewPermission = "read"; + } + + let addPermission = "add"; + if (isProjectPermission) { + addPermission = per.projectPermission || "write"; + } + + let editPermission = "edit"; + if (isProjectPermission) { + editPermission = per.projectPermission || "write"; + } + + let removePermission = "remove"; + if (isProjectPermission) { + removePermission = per.projectPermission || "write"; + } return LodashMerge( { actionbar: { buttons: { - add: { show: hasActionPermission("add") }, + add: { show: hasActionPermission(addPermission) }, }, }, rowHandle: { buttons: { - edit: { show: hasActionPermission("edit") }, - remove: { show: hasActionPermission("remove") }, - view: { show: hasActionPermission("view") }, + edit: { show: hasActionPermission(editPermission) }, + remove: { show: hasActionPermission(removePermission) }, + view: { show: hasActionPermission(viewPermission) }, + copy: { show: hasActionPermission(addPermission) }, }, }, }, diff --git a/packages/ui/certd-client/src/router/guard.ts b/packages/ui/certd-client/src/router/guard.ts index c18941595..87c84506f 100644 --- a/packages/ui/certd-client/src/router/guard.ts +++ b/packages/ui/certd-client/src/router/guard.ts @@ -9,6 +9,9 @@ import { useSettingStore } from "/@/store/settings"; import { usePermissionStore } from "/@/plugin/permission/store.permission"; import util from "/@/plugin/permission/util.permission"; import { useUserStore } from "/@/store/user"; +import { useProjectStore } from "../store/project"; +export const PROJECT_PATH_PREFIX = "/certd/project"; +export const SYS_PATH_PREFIX = "/sys"; function buildAccessedMenus(menus: any) { if (menus == null) { @@ -124,6 +127,20 @@ function setupAccessGuard(router: Router) { }; } return true; + } else { + // 如果是项目模式 + const projectStore = useProjectStore(); + if (projectStore.isEnterprise) { + //加载我的项目 + await projectStore.init(); + if (!projectStore.currentProject && !to.path.startsWith(PROJECT_PATH_PREFIX) && !to.path.startsWith(SYS_PATH_PREFIX)) { + //没有项目 + return { + path: `${PROJECT_PATH_PREFIX}/join`, + replace: true, + }; + } + } } }); } diff --git a/packages/ui/certd-client/src/router/source/modules/certd.ts b/packages/ui/certd-client/src/router/source/modules/certd.ts index c14e7432e..e38745825 100644 --- a/packages/ui/certd-client/src/router/source/modules/certd.ts +++ b/packages/ui/certd-client/src/router/source/modules/certd.ts @@ -1,6 +1,5 @@ +import { useProjectStore } from "/@/store/project"; import { useSettingStore } from "/@/store/settings"; -import aboutResource from "/@/router/source/modules/about"; -import i18n from "/@/locales/i18n"; export const certdResources = [ { @@ -14,6 +13,34 @@ export const certdResources = [ order: 0, }, children: [ + { + title: "certd.sysResources.myProjectManager", + name: "MyProjectManager", + path: "/certd/project", + component: "/certd/project/index.vue", + meta: { + show: () => { + const projectStore = useProjectStore(); + return projectStore.isEnterprise; + }, + isMenu: false, + icon: "ion:apps", + keepAlive: true, + auth: true, + }, + }, + { + title: "certd.sysResources.projectJoin", + name: "ProjectJoin", + path: "/certd/project/join", + component: "/certd/project/join.vue", + meta: { + isMenu: false, + show: true, + icon: "ion:apps", + auth: true, + }, + }, { title: "certd.pipeline", name: "PipelineManager", @@ -22,6 +49,7 @@ export const certdResources = [ meta: { icon: "ion:analytics-sharp", keepAlive: true, + auth: true, }, }, { @@ -31,6 +59,7 @@ export const certdResources = [ component: "/certd/pipeline/detail.vue", meta: { isMenu: false, + auth: true, }, }, { @@ -41,6 +70,7 @@ export const certdResources = [ meta: { icon: "ion:timer-outline", keepAlive: true, + auth: true, }, }, { @@ -51,6 +81,7 @@ export const certdResources = [ meta: { isMenu: true, icon: "ion:duplicate-outline", + auth: true, }, }, { @@ -60,6 +91,7 @@ export const certdResources = [ component: "/certd/pipeline/template/edit.vue", meta: { isMenu: false, + auth: true, }, }, { @@ -69,6 +101,7 @@ export const certdResources = [ component: "/certd/pipeline/template/import/index.vue", meta: { isMenu: false, + auth: true, }, }, { @@ -213,6 +246,10 @@ export const certdResources = [ icon: "mi:user-check", auth: true, isMenu: true, + show: () => { + const projectStore = useProjectStore(); + return !projectStore.isEnterprise; + }, }, }, { @@ -226,6 +263,21 @@ export const certdResources = [ isMenu: false, }, }, + { + title: "certd.sysResources.projectMemberManager", + name: "ProjectMemberManager", + path: "/certd/project/detail", + component: "/certd/project/detail/index.vue", + meta: { + show: () => { + const projectStore = useProjectStore(); + return projectStore.isEnterprise; + }, + isMenu: true, + icon: "ion:apps", + auth: true, + }, + }, ], }, { diff --git a/packages/ui/certd-client/src/router/source/modules/sys.ts b/packages/ui/certd-client/src/router/source/modules/sys.ts index 31fa225a5..161ba44cd 100644 --- a/packages/ui/certd-client/src/router/source/modules/sys.ts +++ b/packages/ui/certd-client/src/router/source/modules/sys.ts @@ -1,7 +1,4 @@ -import LayoutPass from "/@/layout/layout-pass.vue"; import { useSettingStore } from "/@/store/settings"; -import aboutResource from "/@/router/source/modules/about"; -import i18n from "/@/locales/i18n"; export const sysResources = [ { @@ -13,6 +10,7 @@ export const sysResources = [ icon: "ion:settings-outline", permission: "sys:settings:view", order: 10, + auth: true, }, children: [ { @@ -27,6 +25,7 @@ export const sysResources = [ }, icon: "ion:speedometer-outline", permission: "sys:auth:user:view", + auth: true, }, }, @@ -38,6 +37,33 @@ export const sysResources = [ meta: { icon: "ion:settings-outline", permission: "sys:settings:view", + auth: true, + }, + }, + { + title: "certd.sysResources.projectManager", + name: "ProjectManager", + path: "/sys/enterprise/project", + component: "/sys/enterprise/project/index.vue", + meta: { + show: true, + auth: true, + icon: "ion:apps", + permission: "sys:settings:edit", + keepAlive: true, + }, + }, + { + title: "certd.sysResources.projectDetail", + name: "ProjectDetail", + path: "/sys/enterprise/project/detail", + component: "/sys/enterprise/project/detail/index.vue", + meta: { + isMenu: false, + show: true, + auth: true, + icon: "ion:apps", + permission: "sys:settings:edit", }, }, { @@ -49,6 +75,7 @@ export const sysResources = [ icon: "ion:earth-outline", permission: "sys:settings:view", keepAlive: true, + auth: true, }, }, { @@ -72,6 +99,7 @@ export const sysResources = [ const settingStore = useSettingStore(); return settingStore.isComm; }, + auth: true, icon: "ion:document-text-outline", permission: "sys:settings:view", }, @@ -86,6 +114,7 @@ export const sysResources = [ const settingStore = useSettingStore(); return settingStore.isComm; }, + auth: true, icon: "ion:menu", permission: "sys:settings:view", keepAlive: true, @@ -101,6 +130,7 @@ export const sysResources = [ const settingStore = useSettingStore(); return settingStore.isComm; }, + auth: true, icon: "ion:disc-outline", permission: "sys:settings:view", keepAlive: true, @@ -115,6 +145,7 @@ export const sysResources = [ icon: "ion:extension-puzzle-outline", permission: "sys:settings:view", keepAlive: true, + auth: true, }, }, { @@ -127,6 +158,7 @@ export const sysResources = [ icon: "ion:extension-puzzle", permission: "sys:settings:view", keepAlive: true, + auth: true, }, }, { @@ -141,6 +173,7 @@ export const sysResources = [ }, icon: "ion:extension-puzzle", permission: "sys:settings:view", + auth: true, }, }, { @@ -152,6 +185,7 @@ export const sysResources = [ icon: "ion:golf-outline", permission: "sys:settings:view", keepAlive: true, + auth: true, }, }, { @@ -163,6 +197,7 @@ export const sysResources = [ icon: "ion:list-outline", permission: "sys:auth:per:view", keepAlive: true, + auth: true, }, }, { @@ -174,6 +209,7 @@ export const sysResources = [ icon: "ion:people-outline", permission: "sys:auth:role:view", keepAlive: true, + auth: true, }, }, { @@ -185,9 +221,9 @@ export const sysResources = [ icon: "ion:person-outline", permission: "sys:auth:user:view", keepAlive: true, + auth: true, }, }, - { title: "certd.sysResources.suiteManager", name: "SuiteManager", @@ -201,6 +237,7 @@ export const sysResources = [ return settingStore.isComm; }, keepAlive: true, + auth: true, }, children: [ { @@ -215,6 +252,7 @@ export const sysResources = [ }, icon: "ion:cart", permission: "sys:settings:edit", + auth: true, }, }, { @@ -230,6 +268,7 @@ export const sysResources = [ icon: "ion:bag-check", permission: "sys:settings:edit", keepAlive: true, + auth: true, }, }, { diff --git a/packages/ui/certd-client/src/store/project/api.ts b/packages/ui/certd-client/src/store/project/api.ts new file mode 100644 index 000000000..f50033e89 --- /dev/null +++ b/packages/ui/certd-client/src/store/project/api.ts @@ -0,0 +1,9 @@ +import { request } from "/src/api/service"; + +export async function MyProjectList() { + return await request({ + url: "/enterprise/project/list", + method: "post", + data: {}, + }); +} diff --git a/packages/ui/certd-client/src/store/project/index.ts b/packages/ui/certd-client/src/store/project/index.ts new file mode 100644 index 000000000..1122c9b9c --- /dev/null +++ b/packages/ui/certd-client/src/store/project/index.ts @@ -0,0 +1,145 @@ +import { defineStore } from "pinia"; +import * as api from "./api"; +import { message } from "ant-design-vue"; +import { computed, ref } from "vue"; +import { useSettingStore } from "../settings"; +import { LocalStorage } from "/@/utils/util.storage"; +import { useUserStore } from "../user"; + +export type ProjectItem = { + id: string; + name: string; + permission?: string; +}; + +export const useProjectStore = defineStore("app.project", () => { + const myProjects = ref([]); + const inited = ref(false); + const currentProjectId = ref(); // 直接调用 + + function $reset() { + myProjects.value = []; + currentProjectId.value = ""; + inited.value = false; + } + const userStore = useUserStore(); + const userId = userStore.getUserInfo?.id; + const lastProjectIdCacheKey = "currentProjectId:" + userId; + const lastProjectId = LocalStorage.get(lastProjectIdCacheKey); + currentProjectId.value = lastProjectId; + + const projects = computed(() => { + return myProjects.value; + }); + const currentProject = computed(() => { + if (currentProjectId.value) { + const project = projects.value.find(item => item.id === currentProjectId.value); + if (project) { + return project; + } + } + if (projects.value.length > 0) { + return projects.value[0]; + } + + return null; + }); + + const settingStore = useSettingStore(); + const isEnterprise = computed(() => { + return settingStore.isEnterprise; + }); + + function getSearchForm() { + if (!currentProjectId.value || !isEnterprise.value) { + return {}; + } + return { + projectId: currentProjectId.value, + }; + } + async function loadMyProjects(): Promise { + if (!isEnterprise.value) { + return []; + } + const projects = await api.MyProjectList(); + myProjects.value = projects; + if (projects.length > 0 && !currentProjectId.value) { + changeCurrentProject(projects[0].id, true); + } + } + + function changeCurrentProject(id: string, silent?: boolean) { + currentProjectId.value = id; + LocalStorage.set(lastProjectIdCacheKey, id); + if (!silent) { + message.success("切换项目成功"); + } + } + + async function reload() { + inited.value = false; + await init(); + } + + async function init() { + if (!inited.value) { + await loadMyProjects(); + inited.value = true; + } + return myProjects.value; + } + + const isRead = computed(() => { + if (!isEnterprise.value) { + return true; + } + return currentProject.value; + }); + + const isWrite = computed(() => { + if (!isEnterprise.value) { + return true; + } + return currentProject.value?.permission === "write" || currentProject.value?.permission === "admin"; + }); + + const isAdmin = computed(() => { + if (!isEnterprise.value) { + return true; + } + return currentProject.value?.permission === "admin"; + }); + + function hasPermission(value: string) { + if (!isEnterprise.value) { + return true; + } + if (value === "read") { + return isRead.value; + } else if (value === "write") { + return isWrite.value; + } else if (value === "admin") { + return isAdmin.value; + } + return false; + } + + return { + projects, + myProjects, + currentProject, + currentProjectId, + isEnterprise, + isRead, + isWrite, + isAdmin, + getSearchForm, + loadMyProjects, + changeCurrentProject, + reload, + init, + $reset, + hasPermission, + }; +}); diff --git a/packages/ui/certd-client/src/store/settings/api.basic.ts b/packages/ui/certd-client/src/store/settings/api.basic.ts index 4ea251426..67a71a65b 100644 --- a/packages/ui/certd-client/src/store/settings/api.basic.ts +++ b/packages/ui/certd-client/src/store/settings/api.basic.ts @@ -86,6 +86,9 @@ export type SysPublicSetting = { >; // 系统通知 notice?: string; + + // 管理员模式 + adminMode?: "enterprise" | "saas"; }; export type SuiteSetting = { enabled?: boolean; diff --git a/packages/ui/certd-client/src/store/settings/index.tsx b/packages/ui/certd-client/src/store/settings/index.tsx index 1c884168f..09ccb3e24 100644 --- a/packages/ui/certd-client/src/store/settings/index.tsx +++ b/packages/ui/certd-client/src/store/settings/index.tsx @@ -141,6 +141,9 @@ export const useSettingStore = defineStore({ isComm(): boolean { return this.plusInfo?.isComm && (this.plusInfo?.expireTime === -1 || this.plusInfo?.expireTime > new Date().getTime()); }, + isEnterprise(): boolean { + return this.isPlus && this.sysPublic.adminMode === "enterprise"; + }, isAgent(): boolean { return this.siteEnv?.agent?.enabled === true; }, diff --git a/packages/ui/certd-client/src/style/common.less b/packages/ui/certd-client/src/style/common.less index 96a13462c..a784bea43 100644 --- a/packages/ui/certd-client/src/style/common.less +++ b/packages/ui/certd-client/src/style/common.less @@ -6,6 +6,7 @@ @import "./antdv4.less"; @import "./certd.less"; @import "./dark.less"; +@import "./vben.less"; html, body { diff --git a/packages/ui/certd-client/src/style/dark.less b/packages/ui/certd-client/src/style/dark.less index 0b167dff0..7c690c2c4 100644 --- a/packages/ui/certd-client/src/style/dark.less +++ b/packages/ui/certd-client/src/style/dark.less @@ -1,7 +1,13 @@ -.dark{ - .fs-page-header{ +.dark { + .fs-page-header { .title { - color: #d5d5d5 !important; - } + color: #d5d5d5 !important; } + } + + + .vben-normal-menu__item.is-active{ + background-color: #3b3b3b !important; + } + } \ No newline at end of file diff --git a/packages/ui/certd-client/src/style/vben.less b/packages/ui/certd-client/src/style/vben.less new file mode 100644 index 000000000..be47330c1 --- /dev/null +++ b/packages/ui/certd-client/src/style/vben.less @@ -0,0 +1,3 @@ +.vben-normal-menu__item.is-active{ +background-color: #ebf1f6 !important; +} \ No newline at end of file diff --git a/packages/ui/certd-client/src/vben/layouts/basic/header/header.vue b/packages/ui/certd-client/src/vben/layouts/basic/header/header.vue index 4ce85ea48..926a86929 100644 --- a/packages/ui/certd-client/src/vben/layouts/basic/header/header.vue +++ b/packages/ui/certd-client/src/vben/layouts/basic/header/header.vue @@ -111,7 +111,7 @@ function clearPreferencesAndLogout() { @@ -25,13 +25,22 @@ import createCrudOptions from "./crud"; import { message, Modal } from "ant-design-vue"; import { DeleteBatch } from "./api"; import { useI18n } from "/src/locales"; +import { useCrudPermission } from "/@/plugin/permission"; const { t } = useI18n(); defineOptions({ name: "DomainManager", }); -const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions }); +const context: any = { + permission: { + isProjectPermission: true, + }, +}; + +const { hasActionPermission } = useCrudPermission({ permission: context.permission }); +context.hasActionPermission = hasActionPermission; +const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context }); const selectedRowKeys = context.selectedRowKeys; const handleBatchDelete = () => { diff --git a/packages/ui/certd-client/src/views/certd/cname/record/crud.tsx b/packages/ui/certd-client/src/views/certd/cname/record/crud.tsx index 6817749ee..2716ef614 100644 --- a/packages/ui/certd-client/src/views/certd/cname/record/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/cname/record/crud.tsx @@ -8,6 +8,7 @@ import { useSettingStore } from "/@/store/settings"; import { message, Modal } from "ant-design-vue"; import CnameTip from "/@/components/plugins/cert/domains-verify-plan-editor/cname-tip.vue"; import { useCnameImport } from "./use"; +import { useCrudPermission } from "/@/plugin/permission"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const crudBinding = crudExpose.crudBinding; const router = useRouter(); @@ -45,6 +46,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat { label: t("certd.validation_timed_out"), value: "timeout", color: "red" }, ], }); + + const { hasActionPermission } = useCrudPermission(context); return { crudOptions: { settings: { @@ -75,6 +78,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat icon: "ion:add-circle-outline", }, import: { + show: hasActionPermission("write"), title: "导入CNAME记录", type: "primary", text: "批量导入", diff --git a/packages/ui/certd-client/src/views/certd/cname/record/index.vue b/packages/ui/certd-client/src/views/certd/cname/record/index.vue index 833ba5478..5f4f49522 100644 --- a/packages/ui/certd-client/src/views/certd/cname/record/index.vue +++ b/packages/ui/certd-client/src/views/certd/cname/record/index.vue @@ -12,7 +12,7 @@ @@ -27,13 +27,20 @@ import createCrudOptions from "./crud"; import { message, Modal } from "ant-design-vue"; import { DeleteBatch } from "./api"; import { useI18n } from "/src/locales"; +import { useCrudPermission } from "/@/plugin/permission"; const { t } = useI18n(); defineOptions({ name: "CnameRecord", }); -const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions }); + +const context: any = { + permission: { isProjectPermission: true }, +}; + +const { hasActionPermission } = useCrudPermission(context); +const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context }); const selectedRowKeys = context.selectedRowKeys; const handleBatchDelete = () => { diff --git a/packages/ui/certd-client/src/views/certd/dicts.ts b/packages/ui/certd-client/src/views/certd/dicts.ts new file mode 100644 index 000000000..a091b80db --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/dicts.ts @@ -0,0 +1,93 @@ +import { dict } from "@fast-crud/fast-crud"; +import { GetMyProjectList } from "./project/api"; +import { request } from "/@/api/service"; + +const projectPermissionDict = dict({ + data: [ + { + value: "read", + label: "查看", + color: "cyan", + icon: "material-symbols:folder-eye-outline-sharp", + }, + { + value: "write", + label: "修改", + color: "green", + icon: "material-symbols:edit-square-outline-rounded", + }, + { + value: "admin", + label: "管理员", + color: "orange", + icon: "material-symbols:manage-accounts-rounded", + }, + ], +}); + +const projectMemberStatusDict = dict({ + data: [ + { + value: "pending", + label: "待审核", + color: "orange", + icon: "material-symbols:hourglass-top", + }, + { + value: "approved", + label: "已加入", + color: "green", + icon: "material-symbols:done-all", + }, + { + value: "rejected", + label: "已拒绝", + color: "red", + icon: "material-symbols:close", + }, + ], +}); + +const myProjectDict = dict({ + url: "/enterprise/project/list", + getData: async () => { + const res = await GetMyProjectList(); + return res; + }, + value: "id", + label: "name", + immediate: false, + onReady: ({ dict }) => { + for (const item of dict.data) { + item.label = item.name; + item.value = item.id; + } + }, +}); + +const userDict = dict({ + url: "/basic/user/getSimpleUsers", + value: "id", + getData: async () => { + const res = await request({ + url: "/basic/user/getSimpleUsers", + method: "POST", + }); + return res; + }, + immediate: false, + onReady: ({ dict }) => { + for (const item of dict.data) { + item.label = item.nickName || item.username || item.phoneCode + item.mobile; + } + }, +}); + +export function useDicts() { + return { + projectPermissionDict, + myProjectDict, + userDict, + projectMemberStatusDict, + }; +} diff --git a/packages/ui/certd-client/src/views/certd/history/crud.tsx b/packages/ui/certd-client/src/views/certd/history/crud.tsx index e1b7b760c..dd64a1f98 100644 --- a/packages/ui/certd-client/src/views/certd/history/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/history/crud.tsx @@ -6,6 +6,8 @@ import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, Edi import { useUserStore } from "/@/store/user"; import { useSettingStore } from "/@/store/settings"; import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status"; +import { useDicts } from "../dicts"; +import { useProjectStore } from "/@/store/project"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const router = useRouter(); @@ -31,6 +33,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat const settingStore = useSettingStore(); const selectedRowKeys: Ref = ref([]); context.selectedRowKeys = selectedRowKeys; + const { myProjectDict } = useDicts(); + const projectStore = useProjectStore(); return { crudOptions: { @@ -64,6 +68,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, }, search: { + initialForm: { + ...projectStore.getSearchForm(), + }, formItem: { labelCol: { style: { @@ -195,17 +202,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat align: "center", }, }, - createTime: { - title: t("certd.fields.createTime"), - type: "datetime", + projectId: { + title: t("certd.fields.projectName"), + type: "dict-select", + dict: myProjectDict, form: { show: false, }, - column: { - sorter: true, - width: 160, - align: "center", - }, }, updateTime: { title: t("certd.fields.updateTime"), diff --git a/packages/ui/certd-client/src/views/certd/history/index.vue b/packages/ui/certd-client/src/views/certd/history/index.vue index dea2c720d..74e298830 100644 --- a/packages/ui/certd-client/src/views/certd/history/index.vue +++ b/packages/ui/certd-client/src/views/certd/history/index.vue @@ -5,7 +5,7 @@ @@ -20,13 +20,20 @@ import createCrudOptions from "./crud"; import { message, Modal } from "ant-design-vue"; import { DeleteBatch } from "./api"; import { useI18n } from "/src/locales"; +import { useCrudPermission } from "/@/plugin/permission"; const { t } = useI18n(); defineOptions({ name: "PipelineHistory", }); -const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions }); + +const context: any = { + permission: { isProjectPermission: true }, +}; + +const { hasActionPermission } = useCrudPermission(context); +const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context }); const selectedRowKeys = context.selectedRowKeys; const handleBatchDelete = () => { diff --git a/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx index 4c691c538..69b5d44ed 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx @@ -10,6 +10,9 @@ import { notification } from "ant-design-vue"; import CertView from "/@/views/certd/pipeline/cert-view.vue"; import { useCertUpload } from "/@/views/certd/pipeline/cert-upload/use"; import { useSettingStore } from "/@/store/settings"; +import { useProjectStore } from "/@/store/project"; +import { useDicts } from "../../dicts"; +import { useUserStore } from "/@/store/user"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const { t } = useI18n(); @@ -34,9 +37,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat return res; }; const router = useRouter(); - + const { myProjectDict } = useDicts(); const settingStore = useSettingStore(); - + const projectStore = useProjectStore(); + const userStore = useUserStore(); const model = useModal(); const viewCert = async (row: any) => { const cert = await api.GetCert(row.id); @@ -63,6 +67,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat const expireStatus = route?.query?.expireStatus as string; const searchInitForm = { expiresLeft: expireStatus, + ...projectStore.getSearchForm(), }; return { crudOptions: { @@ -137,7 +142,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat notification.error({ message: t("certd.certificateNotGenerated") }); return; } - window.open("/api/monitor/cert/download?id=" + row.id); + let url = "/api/monitor/cert/download?id=" + row.id; + if (projectStore.isEnterprise) { + url += `&projectId=${projectStore.currentProject?.id}`; + } + url += `&token=${userStore.getToken}`; + window.open(url); }, }, }, @@ -344,6 +354,14 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, }, }, + projectId: { + title: t("certd.fields.projectName"), + type: "dict-select", + dict: myProjectDict, + form: { + show: false, + }, + }, }, }, }; diff --git a/packages/ui/certd-client/src/views/certd/monitor/cert/index.vue b/packages/ui/certd-client/src/views/certd/monitor/cert/index.vue index b8486669e..6c04a8314 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/cert/index.vue +++ b/packages/ui/certd-client/src/views/certd/monitor/cert/index.vue @@ -21,7 +21,7 @@ const { t } = useI18n(); defineOptions({ name: "CertStore", }); -const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} }); +const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: { isProjectPermission: true } } }); // 页面打开后获取列表数据 onMounted(() => { diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx index 9f5932806..4b1823ea6 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx @@ -13,6 +13,9 @@ import { useSiteImport } from "/@/views/certd/monitor/site/use"; import { ref } from "vue"; import GroupSelector from "../../basic/group/group-selector.vue"; import { createGroupDictRef } from "../../basic/group/api"; +import { useProjectStore } from "/@/store/project"; +import { useDicts } from "../../dicts"; +import { useCrudPermission } from "/@/plugin/permission"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const { t } = useI18n(); const api = siteInfoApi; @@ -37,7 +40,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat const res = await api.AddObj(form); return res; }; - + const { myProjectDict } = useDicts(); const settingsStore = useSettingStore(); const checkStatusDict = dict({ @@ -105,6 +108,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat return searchFrom.groupId; } } + + const projectStore = useProjectStore(); + const { hasActionPermission } = useCrudPermission({ permission: context.permission }); return { id: "siteMonitorCrud", crudOptions: { @@ -227,7 +233,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, //导入按钮 import: { - show: true, + show: hasActionPermission("write"), text: t("monitor.bulkImport"), type: "primary", async click() { @@ -289,6 +295,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat // name: "disabled", // show: true, // }, + search: { + initialForm: { + ...projectStore.getSearchForm(), + }, + }, columns: { id: { title: "ID", @@ -547,6 +558,20 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, }, }, + ipAddress: { + title: t("monitor.ipAddress"), + search: { + show: false, + }, + type: "text", + form: { + helper: t("monitor.ipAddressHelper"), + }, + column: { + width: 150, + sorter: true, + }, + }, groupId: { title: t("certd.fields.group"), type: "dict-select", @@ -709,7 +734,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }), }, column: { - width: 100, + width: 140, sorter: true, align: "center", }, @@ -732,7 +757,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat helper: t("monitor.ipSyncModeHelper"), }, column: { - width: 100, + width: 140, sorter: true, align: "center", }, @@ -754,7 +779,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat helper: t("monitor.ipIgnoreCoherenceHelper"), }, column: { - width: 100, + width: 180, sorter: true, align: "center", }, @@ -800,6 +825,14 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, }, }, + projectId: { + title: t("certd.fields.projectName"), + type: "dict-select", + dict: myProjectDict, + form: { + show: false, + }, + }, }, }, }; diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/index.vue b/packages/ui/certd-client/src/views/certd/monitor/site/index.vue index 55a7221f8..01dfe2a8b 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/index.vue +++ b/packages/ui/certd-client/src/views/certd/monitor/site/index.vue @@ -34,7 +34,12 @@ const { t } = useI18n(); defineOptions({ name: "SiteCertMonitor", }); -const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions }); +const context: any = { + permission: { + isProjectPermission: true, + }, +}; +const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context }); const handleBatchDelete = context.handleBatchDelete; diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/setting/index.vue b/packages/ui/certd-client/src/views/certd/monitor/site/setting/index.vue index 3f13c386c..5b6e1f15b 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/setting/index.vue +++ b/packages/ui/certd-client/src/views/certd/monitor/site/setting/index.vue @@ -37,7 +37,7 @@
{{ t("monitor.setting.cronTrigger") }}
- {{ t("certd.save") }} + {{ t("certd.save") }} @@ -55,6 +55,7 @@ import { utils } from "/@/utils"; import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue"; import { useI18n } from "/src/locales"; import { useSettingStore } from "/src/store/settings"; +import { useCrudPermission } from "/@/plugin/permission"; const { t } = useI18n(); @@ -73,6 +74,8 @@ async function loadUserSettings() { merge(formState, data); } +const { hasActionPermission } = useCrudPermission({ permission: { isProjectPermission: true } }); + loadUserSettings(); const doSave = async (form: any) => { await utils.sleep(300); diff --git a/packages/ui/certd-client/src/views/certd/notification/common.tsx b/packages/ui/certd-client/src/views/certd/notification/common.tsx index 9b1cec9ff..91726d8ee 100644 --- a/packages/ui/certd-client/src/views/certd/notification/common.tsx +++ b/packages/ui/certd-client/src/views/certd/notification/common.tsx @@ -5,6 +5,8 @@ import { forEach, get, merge, set } from "lodash-es"; import { Modal } from "ant-design-vue"; import { mitter } from "/@/utils/util.mitt"; import { useI18n } from "/src/locales"; +import { useProjectStore } from "/@/store/project"; +import { useDicts } from "../dicts"; export function notificationProvide(api: any) { provide("notificationApi", api); @@ -26,6 +28,8 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) { }, }; + const projectStore = useProjectStore(); + const { myProjectDict } = useDicts(); provide("getCurrentPluginDefine", () => { return currentDefine; }); @@ -247,5 +251,13 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) { }, }, } as ColumnCompositionProps, + projectId: { + title: t("certd.fields.projectName"), + type: "dict-select", + dict: myProjectDict, + form: { + show: false, + }, + }, }; } diff --git a/packages/ui/certd-client/src/views/certd/notification/crud.tsx b/packages/ui/certd-client/src/views/certd/notification/crud.tsx index 08bebc5c6..53baf1ade 100644 --- a/packages/ui/certd-client/src/views/certd/notification/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/notification/crud.tsx @@ -2,6 +2,7 @@ import { ref } from "vue"; import { getCommonColumnDefine } from "./common"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { createNotificationApi } from "/@/views/certd/notification/api"; +import { useProjectStore } from "/@/store/project"; const api = createNotificationApi(); export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const pageRequest = async (query: UserPageQuery): Promise => { @@ -26,6 +27,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat const typeRef = ref(); const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api); + const projectStore = useProjectStore(); return { crudOptions: { request: { @@ -34,6 +36,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat editRequest, delRequest, }, + search: { + initialForm: { + ...projectStore.getSearchForm(), + }, + }, form: { labelCol: { //固定label宽度 diff --git a/packages/ui/certd-client/src/views/certd/notification/index.vue b/packages/ui/certd-client/src/views/certd/notification/index.vue index 059404bbd..614e08c27 100644 --- a/packages/ui/certd-client/src/views/certd/notification/index.vue +++ b/packages/ui/certd-client/src/views/certd/notification/index.vue @@ -22,7 +22,7 @@ export default defineComponent({ setup() { const api = createNotificationApi(); notificationProvide(api); - const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } }); + const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api, permission: { isProjectPermission: true } } }); // 页面打开后获取列表数据 onMounted(() => { diff --git a/packages/ui/certd-client/src/views/certd/notification/notification-selector/modal/crud.tsx b/packages/ui/certd-client/src/views/certd/notification/notification-selector/modal/crud.tsx index 3f27ca407..944e29a39 100644 --- a/packages/ui/certd-client/src/views/certd/notification/notification-selector/modal/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/notification/notification-selector/modal/crud.tsx @@ -2,6 +2,7 @@ import { ref } from "vue"; import { getCommonColumnDefine } from "../../common"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; +import { useProjectStore } from "/@/store/project"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const { crudBinding } = crudExpose; @@ -29,6 +30,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat return res; }; + const projectStore = useProjectStore(); const selectedRowKey = ref([props.modelValue]); const onSelectChange = (changed: any) => { @@ -54,6 +56,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, search: { show: false, + initialForm: { + ...projectStore.getSearchForm(), + }, }, form: { labelCol: { diff --git a/packages/ui/certd-client/src/views/certd/open/openkey/crud.tsx b/packages/ui/certd-client/src/views/certd/open/openkey/crud.tsx index 0f7d8cbe5..66a3a9128 100644 --- a/packages/ui/certd-client/src/views/certd/open/openkey/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/open/openkey/crud.tsx @@ -3,6 +3,9 @@ import { useI18n } from "/src/locales"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { OPEN_API_DOC, openkeyApi } from "./api"; import { useModal } from "/@/use/use-modal"; +import { useProjectStore } from "/@/store/project"; +import { computed } from "vue"; +import { useDicts } from "../../dicts"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const { t } = useI18n(); @@ -26,6 +29,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat const res = await api.AddObj(form); return res; }; + const { myProjectDict } = useDicts(); + const projectStore = useProjectStore(); const model = useModal(); return { crudOptions: { @@ -37,6 +42,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, search: { show: false, + initialForm: { + ...projectStore.getSearchForm(), + }, }, form: { labelCol: { @@ -163,6 +171,22 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat sorter: true, }, }, + projectId: { + title: t("certd.fields.projectName"), + type: "dict-select", + dict: myProjectDict, + form: { + show: false, + }, + column: { + show: computed(() => { + return projectStore.isEnterprise; + }), + width: 120, + align: "center", + sorter: true, + }, + }, createTime: { title: t("certd.fields.createTime"), type: "datetime", diff --git a/packages/ui/certd-client/src/views/certd/open/openkey/index.vue b/packages/ui/certd-client/src/views/certd/open/openkey/index.vue index 89e8815f5..1c4a425ac 100644 --- a/packages/ui/certd-client/src/views/certd/open/openkey/index.vue +++ b/packages/ui/certd-client/src/views/certd/open/openkey/index.vue @@ -19,7 +19,7 @@ import { OPEN_API_DOC } from "/@/views/certd/open/openkey/api"; defineOptions({ name: "OpenKey", }); -const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} }); +const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: { isProjectPermission: true } } }); // 页面打开后获取列表数据 onMounted(() => { diff --git a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx index 2ac300a1a..ca8144cfa 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx @@ -14,13 +14,18 @@ import GroupSelector from "/@/views/certd/pipeline/group/group-selector.vue"; import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status"; import { useCertViewer } from "/@/views/certd/pipeline/use"; import { useI18n } from "/src/locales"; +import { useDicts } from "../dicts"; +import { useProjectStore } from "/@/store/project"; +import { useCrudPermission } from "/@/plugin/permission"; -export default function ({ crudExpose, context: { selectedRowKeys, openCertApplyDialog } }: CreateCrudOptionsProps): CreateCrudOptionsRet { +export default function ({ crudExpose, context: { selectedRowKeys, openCertApplyDialog, permission } }: CreateCrudOptionsProps): CreateCrudOptionsRet { const router = useRouter(); const lastResRef = ref(); const { t } = useI18n(); + const { hasActionPermission } = useCrudPermission({ permission }); + const { openUploadCreateDialog } = useCertUpload(); const pageRequest = async (query: UserPageQuery): Promise => { @@ -48,6 +53,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply delete form.lastVars; delete form.createTime; delete form.id; + delete form.webhook; let pipeline = form.content; if (typeof pipeline === "string" && pipeline.startsWith("{")) { pipeline = JSON.parse(form.content); @@ -66,13 +72,15 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply const userStore = useUserStore(); const settingStore = useSettingStore(); + const projectStore = useProjectStore(); + const { myProjectDict } = useDicts(); const DEFAULT_WILL_EXPIRE_DAYS = settingStore.sysPublic.defaultWillExpireDays || settingStore.sysPublic.defaultCertRenewDays || 15; function onDialogOpen(opt: any) { const searchForm = crudExpose.getSearchValidatedFormData(); opt.initialForm = { - ...opt.initialForm, groupId: searchForm.groupId, + ...opt.initialForm, }; } @@ -120,9 +128,11 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply click() { openCertApplyDialog({ key: "CertApply" }); }, + show: hasActionPermission("write"), }, uploadCert: { order: 2, + show: hasActionPermission("write"), text: t("certd.commercialCertHosting"), type: "primary", tooltip: { @@ -147,7 +157,9 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply }, }, search: { - col: { span: 3 }, + initialForm: { + ...projectStore.getSearchForm(), + }, }, form: { afterSubmit({ form, res, mode }) { @@ -201,6 +213,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply }, }, copy: { + show: hasActionPermission("write"), click: async context => { settingStore.checkPlus(); const { ui } = useUi(); @@ -210,7 +223,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply row = info.pipeline; row.content = JSON.parse(row.content); row.title = row.title + "_copy"; - await crudExpose.openCopy({ + await crudExpose.openAdd({ row: row, index: context.index, }); @@ -218,6 +231,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply class: "need-plus", }, config: { + show: hasActionPermission("write"), order: 1, title: t("certd.actions.editPipeline"), type: "link", @@ -228,6 +242,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply }, }, edit: { + show: hasActionPermission("write"), order: 2, title: t("certd.actions.editConfigGroup"), icon: "ant-design:setting-outlined", @@ -440,6 +455,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply type: "dict-select", search: { show: true, + col: { span: 2 }, }, dict: dict({ data: statusUtil.getOptions(), @@ -544,6 +560,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply type: "dict-select", search: { show: true, + col: { span: 2 }, }, dict: dict({ data: [ @@ -641,6 +658,22 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply }, }, }, + projectId: { + title: t("certd.fields.projectName"), + type: "dict-select", + dict: myProjectDict, + form: { + show: false, + }, + column: { + show: computed(() => { + return settingStore.isEnterprise; + }), + width: 120, + align: "center", + sorter: true, + }, + }, updateTime: { title: t("certd.fields.updateTime"), type: "datetime", diff --git a/packages/ui/certd-client/src/views/certd/pipeline/group/crud.tsx b/packages/ui/certd-client/src/views/certd/pipeline/group/crud.tsx index 43d6b4841..d83850f43 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/group/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/group/crud.tsx @@ -1,6 +1,8 @@ import { useI18n } from "/src/locales"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { pipelineGroupApi } from "./api"; +import { useProjectStore } from "/@/store/project"; +import { useDicts } from "../../dicts"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const { t } = useI18n(); @@ -25,6 +27,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat return res; }; + const projectStore = useProjectStore(); + const { myProjectDict } = useDicts(); return { crudOptions: { settings: { @@ -44,6 +48,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat editRequest, delRequest, }, + search: { + initialForm: { + ...projectStore.getSearchForm(), + }, + }, form: { labelCol: { //固定label宽度 @@ -127,6 +136,14 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat width: 400, }, }, + projectId: { + title: t("certd.fields.projectName"), + type: "dict-select", + dict: myProjectDict, + form: { + show: false, + }, + }, }, }, }; diff --git a/packages/ui/certd-client/src/views/certd/pipeline/group/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/group/index.vue index f4cdad28f..d83be9422 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/group/index.vue +++ b/packages/ui/certd-client/src/views/certd/pipeline/group/index.vue @@ -14,12 +14,16 @@ import { defineComponent, onActivated, onMounted } from "vue"; import { useFs } from "@fast-crud/fast-crud"; import createCrudOptions from "./crud"; -import { createApi } from "./api"; export default defineComponent({ name: "PipelineGroupManager", setup() { - const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} }); + const { crudBinding, crudRef, crudExpose } = useFs({ + createCrudOptions, + context: { + permission: { isProjectPermission: true }, + }, + }); // 页面打开后获取列表数据 onMounted(() => { diff --git a/packages/ui/certd-client/src/views/certd/pipeline/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/index.vue index c2c7b8fec..4b8e31131 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/index.vue +++ b/packages/ui/certd-client/src/views/certd/pipeline/index.vue @@ -13,7 +13,7 @@ -->