Compare commits

..

54 Commits

Author SHA1 Message Date
xiaojunnuo
ff1d7b115a v1.27.8 2024-11-26 02:07:06 +08:00
xiaojunnuo
98bd5149e9 build: prepare to build 2024-11-26 02:04:39 +08:00
xiaojunnuo
4efa2e0c6a build: prepare to build 2024-11-26 02:03:00 +08:00
xiaojunnuo
f805036054 build: prepare to build 2024-11-26 02:02:34 +08:00
xiaojunnuo
3c723c4325 chore: 2024-11-26 02:02:18 +08:00
xiaojunnuo
14a83f6b52 build: publish 2024-11-26 01:43:35 +08:00
xiaojunnuo
ff0686670c build: trigger build image 2024-11-26 01:43:14 +08:00
xiaojunnuo
3198d07553 v1.27.7 2024-11-26 01:41:21 +08:00
xiaojunnuo
c7e2896326 build: prepare to build 2024-11-26 01:39:40 +08:00
xiaojunnuo
0db5381a8b chore: 2024-11-26 01:39:19 +08:00
xiaojunnuo
cb86151deb chore: 2024-11-26 01:21:49 +08:00
xiaojunnuo
d6c7326467 chore: 2024-11-26 01:14:16 +08:00
xiaojunnuo
92c6c45e77 chore: 2024-11-25 23:48:04 +08:00
xiaojunnuo
c6fff4950d chore: 2024-11-25 23:24:12 +08:00
xiaojunnuo
81a8123725 perf: 谷歌EAB绑定邮箱改成必填 2024-11-25 18:26:23 +08:00
xiaojunnuo
d0d3e74d55 chore: 2024-11-25 11:38:49 +08:00
xiaojunnuo
b54ae272eb perf: 通知渠道支持测试按钮 2024-11-25 11:35:16 +08:00
xiaojunnuo
3af6d96e6e fix: 修复CNAME时子域名级数超出限制的问题 2024-11-25 10:57:27 +08:00
xiaojunnuo
f38b33ea39 chore: 2024-11-25 09:51:45 +08:00
xiaojunnuo
dd2b0a1595 chore: 2024-11-25 00:53:36 +08:00
xiaojunnuo
c96fcb7afc perf: 支持部署到阿里云WAF 2024-11-24 01:40:42 +08:00
xiaojunnuo
b805a29259 perf: 支持企业微信群聊机器人通知 2024-11-23 23:58:31 +08:00
xiaojunnuo
5450246f06 chore: 2024-11-23 00:25:30 +08:00
xiaojunnuo
d9a00eeaf7 perf: 通知管理 2024-11-22 17:12:39 +08:00
xiaojunnuo
131ed13df1 docs: 2024-11-22 10:05:21 +08:00
xiaojunnuo
5f8d70028a chore: 2024-11-20 18:36:38 +08:00
xiaojunnuo
c222b702c3 chore: 2024-11-20 18:12:10 +08:00
xiaojunnuo
de43391e4c perf: 华为云密钥获取提示及访问链接 2024-11-20 13:58:41 +08:00
xiaojunnuo
547c0b8399 docs: 2024-11-20 11:41:03 +08:00
xiaojunnuo
fcbb5e46a1 perf: 优化插件开发,dnsProvider无需写http logger 变量 2024-11-20 11:36:39 +08:00
xiaojunnuo
7c5166c8bb Merge branch 'v2' into v2-dev 2024-11-20 11:18:35 +08:00
xiaojunnuo
fab66606b3 fix: 修复关键字查询bug 2024-11-20 10:46:05 +08:00
Greper
1d143f7103 pref: deployment to cachefly and gcore plugin
Merge pull request #244 from origami-owo/v2
2024-11-19 18:26:39 +08:00
xiaojunnuo
4955fcd12a build: publish 2024-11-19 18:15:21 +08:00
xiaojunnuo
817e9663fa build: trigger build image 2024-11-19 18:15:02 +08:00
xiaojunnuo
85ca850453 v1.27.6 2024-11-19 18:12:55 +08:00
xiaojunnuo
3baefb2b60 build: prepare to build 2024-11-19 18:10:17 +08:00
xiaojunnuo
ffea5a0e02 build: prepare to build 2024-11-19 18:08:27 +08:00
xiaojunnuo
be55695691 chore: 2024-11-19 18:07:34 +08:00
xiaojunnuo
ea27c96362 Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2024-11-19 17:51:34 +08:00
xiaojunnuo
7a73a01999 chore: 2024-11-19 17:49:59 +08:00
xiaojunnuo
018dee6c38 fix: 修复vip试用secret报错的bug 2024-11-19 17:43:06 +08:00
xiaojunnuo
c7cf2e6f16 Merge branch 'v2-dev' into v2 2024-11-19 11:20:49 +08:00
xiaojunnuo
9ab9a6e8b0 perf: 当步骤全部都禁用时,任务本身显示删除线 2024-11-19 11:19:40 +08:00
xiaojunnuo
67ccff3e86 build: publish 2024-11-18 22:03:19 +08:00
xiaojunnuo
40c09ce26a build: trigger build image 2024-11-18 22:02:58 +08:00
origami
d6a2e4aee9 Merge branch 'certd:v2' into v2 2024-11-16 23:32:28 +08:00
xiaojunnuo
d8a134fe7e Merge branch 'v2-dev' into v2 2024-11-16 00:50:30 +08:00
Greper
16748a75d5 fix: .env 读取 \r 问题
fix: .env 读取 \r 问题

Merge pull request #243 from ltxhhz/v2
2024-11-15 23:11:45 +08:00
ltxhhz
0e33dfa019 fix: .env 读取 \r 问题 2024-11-15 20:34:38 +08:00
origami
fc9ac23725 Merge branch 'certd:v2' into v2 2024-11-02 01:53:20 +08:00
origami-owo
0f426b9c19 fix 2024-11-02 01:38:50 +08:00
origami-owo
a7d4710702 add gcore 2024-11-02 01:33:51 +08:00
origami-owo
6946279f03 add deploy plugin cachefly 2024-10-31 13:36:49 +08:00
132 changed files with 2435 additions and 263 deletions

View File

@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package root
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
### Bug Fixes
* 修复关键字查询bug ([fab6660](https://github.com/certd/certd/commit/fab66606b35a540fac31fee902331ba1ffdebc16))
* 修复CNAME时子域名级数超出限制的问题 ([3af6d96](https://github.com/certd/certd/commit/3af6d96e6e353c9b2111cff81679b79c55195a0a))
### Performance Improvements
* 谷歌EAB绑定邮箱改成必填 ([81a8123](https://github.com/certd/certd/commit/81a8123725d7bf4bd6a32a64a066bd760b7b6a7f))
* 华为云密钥获取提示及访问链接 ([de43391](https://github.com/certd/certd/commit/de43391e4c12dc3ad976f8fa8787f4eb70a41e75))
* 通知管理 ([d9a00ee](https://github.com/certd/certd/commit/d9a00eeaf72735ced67c59d7983d84e3c730064a))
* 通知渠道支持测试按钮 ([b54ae27](https://github.com/certd/certd/commit/b54ae272ebc2d31b32b049d44e2299a6be7f153c))
* 优化插件开发dnsProvider无需写http logger 变量 ([fcbb5e4](https://github.com/certd/certd/commit/fcbb5e46a112174150a62648319b8224fce3b7ed))
* 支持部署到阿里云WAF ([c96fcb7](https://github.com/certd/certd/commit/c96fcb7afced979435cffa73591275008033c90d))
* 支持企业微信群聊机器人通知 ([b805a29](https://github.com/certd/certd/commit/b805a2925984144a31575b8aaa622f0c30d41b56))
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
### Bug Fixes
* .env 读取 \r 问题 ([0e33dfa](https://github.com/certd/certd/commit/0e33dfa019a55ea76193c428ec756af386adeb9d))
* 修复vip试用secret报错的bug ([018dee6](https://github.com/certd/certd/commit/018dee6c383233560f078dfd30f6c2857a7e15ee))
### Performance Improvements
* 当步骤全部都禁用时,任务本身显示删除线 ([9ab9a6e](https://github.com/certd/certd/commit/9ab9a6e8b083e19793894f23e59f29c604ec98e5))
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
### Bug Fixes

View File

@@ -1 +1 @@
21:55
01:43

View File

@@ -3,6 +3,51 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
### Bug Fixes
* 修复关键字查询bug ([fab6660](https://github.com/certd/certd/commit/fab66606b35a540fac31fee902331ba1ffdebc16))
* 修复CNAME时子域名级数超出限制的问题 ([3af6d96](https://github.com/certd/certd/commit/3af6d96e6e353c9b2111cff81679b79c55195a0a))
### Performance Improvements
* 谷歌EAB绑定邮箱改成必填 ([81a8123](https://github.com/certd/certd/commit/81a8123725d7bf4bd6a32a64a066bd760b7b6a7f))
* 华为云密钥获取提示及访问链接 ([de43391](https://github.com/certd/certd/commit/de43391e4c12dc3ad976f8fa8787f4eb70a41e75))
* 通知管理 ([d9a00ee](https://github.com/certd/certd/commit/d9a00eeaf72735ced67c59d7983d84e3c730064a))
* 通知渠道支持测试按钮 ([b54ae27](https://github.com/certd/certd/commit/b54ae272ebc2d31b32b049d44e2299a6be7f153c))
* 优化插件开发dnsProvider无需写http logger 变量 ([fcbb5e4](https://github.com/certd/certd/commit/fcbb5e46a112174150a62648319b8224fce3b7ed))
* 支持部署到阿里云WAF ([c96fcb7](https://github.com/certd/certd/commit/c96fcb7afced979435cffa73591275008033c90d))
* 支持企业微信群聊机器人通知 ([b805a29](https://github.com/certd/certd/commit/b805a2925984144a31575b8aaa622f0c30d41b56))
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
### Bug Fixes
* .env 读取 \r 问题 ([0e33dfa](https://github.com/certd/certd/commit/0e33dfa019a55ea76193c428ec756af386adeb9d))
* 修复vip试用secret报错的bug ([018dee6](https://github.com/certd/certd/commit/018dee6c383233560f078dfd30f6c2857a7e15ee))
### Performance Improvements
* 当步骤全部都禁用时,任务本身显示删除线 ([9ab9a6e](https://github.com/certd/certd/commit/9ab9a6e8b083e19793894f23e59f29c604ec98e5))
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
### Bug Fixes
* 修复1Panel面板本身证书更新导致判定执行失败的问题 ([2689e6d](https://github.com/certd/certd/commit/2689e6d6c03aba21da90d5d45232c6ba08696be1))
* 修复角色无法删除的bug ([66629a5](https://github.com/certd/certd/commit/66629a591aecc2d8364ea415c7afc3f9d0406562))
* 修复Cname情况下无法使用DNS类型的bug ([26dad39](https://github.com/certd/certd/commit/26dad399d5768b3205da099ddc11809aef7d6224))
### Performance Improvements
* 日志查看自动滚动到底部 ([4a2f7eb](https://github.com/certd/certd/commit/4a2f7ebf87b7c027cebff7cb763f8f35f6d2aa36))
* 系统设置中的代理设置优化为可全局生效环境变量中的https_proxy设置将无效 ([381a37f](https://github.com/certd/certd/commit/381a37fbaa6b61c887eda743897ae00afb825bdf))
* 新手导航在非编辑模式下不显示 ([18bfcc2](https://github.com/certd/certd/commit/18bfcc24ad0bde57bb04db8a4209861ec6b8ff1d))
* 优化腾讯云 cloudflare 重复解析记录时的返回值 ([90d1b68](https://github.com/certd/certd/commit/90d1b68bd6cf232fbe085234efe07d29b7690044))
* 支持namesilo ([80159ec](https://github.com/certd/certd/commit/80159ecca895103d0495f3217311199e66056572))
* 专业版试用,无需绑定账号 ([c7c4318](https://github.com/certd/certd/commit/c7c4318c11b65a76089787aa58939832d338a232))
## [1.27.4](https://github.com/certd/certd/compare/v1.27.3...v1.27.4) (2024-11-14)
### Bug Fixes

View File

@@ -48,4 +48,4 @@ admin/123456
## 五、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可
将备份的`db.sqlite`覆盖到原来的位置重启certd即可

View File

@@ -81,4 +81,4 @@ services:
## 五、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可
将备份的`db.sqlite`覆盖到原来的位置重启certd即可

View File

@@ -71,4 +71,4 @@ docker compose up -d
## 四、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可
将备份的`db.sqlite`覆盖到原来的位置重启certd即可

View File

@@ -42,4 +42,4 @@ kill -9 $(lsof -t -i:7001)
## 四、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可
将备份的`db.sqlite`覆盖到原来的位置重启certd即可

View File

@@ -8,4 +8,14 @@
ALIYUN_CLIENT_CONNECT_TIMEOUT=10000 # 连接超时,单位毫秒
ALIYUN_CLIENT_READ_TIMEOUT=10000 #读取数据超时,单位毫秒
```
```
## 阿里云Access权限设置
* 申请证书 `AliyunDNSFullAccess`
* 上传证书到阿里云: `AliyunYundunCertFullAccess`
* 部署证书到OSS: `AliyunYundunCertFullAccess``AliyunOSSFullAccess`
* 部署证书到CDN: `AliyunYundunCertFullAccess``AliyunCDNFullAccess`
* 部署证书到DCDN `AliyunYundunCertFullAccess``AliyunDCDNFullAccess`

View File

@@ -27,4 +27,4 @@
## 三、备份恢复
将备份的`db.sqlite`覆盖到原来的位置即可
将备份的`db.sqlite`覆盖到原来的位置重启certd即可

View File

@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.27.5"
"version": "1.27.8"
}

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/publishlab/node-acme-client/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/acme-client
## [1.27.7](https://github.com/publishlab/node-acme-client/compare/v1.27.6...v1.27.7) (2024-11-25)
**Note:** Version bump only for package @certd/acme-client
## [1.27.6](https://github.com/publishlab/node-acme-client/compare/v1.27.5...v1.27.6) (2024-11-19)
**Note:** Version bump only for package @certd/acme-client
## [1.27.5](https://github.com/publishlab/node-acme-client/compare/v1.27.4...v1.27.5) (2024-11-18)
**Note:** Version bump only for package @certd/acme-client

View File

@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.27.5",
"version": "1.27.8",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.27.5",
"@certd/basic": "^1.27.8",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.7.2",
@@ -65,5 +65,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/basic
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
**Note:** Version bump only for package @certd/basic
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
**Note:** Version bump only for package @certd/basic
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
### Performance Improvements

View File

@@ -1 +1 @@
21:57
02:04

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.27.5",
"version": "1.27.8",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -43,5 +43,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -3,6 +3,23 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/pipeline
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
### Performance Improvements
* 通知管理 ([d9a00ee](https://github.com/certd/certd/commit/d9a00eeaf72735ced67c59d7983d84e3c730064a))
* 通知渠道支持测试按钮 ([b54ae27](https://github.com/certd/certd/commit/b54ae272ebc2d31b32b049d44e2299a6be7f153c))
* 优化插件开发dnsProvider无需写http logger 变量 ([fcbb5e4](https://github.com/certd/certd/commit/fcbb5e46a112174150a62648319b8224fce3b7ed))
* 支持企业微信群聊机器人通知 ([b805a29](https://github.com/certd/certd/commit/b805a2925984144a31575b8aaa622f0c30d41b56))
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
**Note:** Version bump only for package @certd/pipeline
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
### Performance Improvements

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.27.5",
"version": "1.27.8",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -16,8 +16,8 @@
"test": "mocha --loader=ts-node/esm"
},
"dependencies": {
"@certd/basic": "^1.27.5",
"@certd/plus-core": "^1.27.5",
"@certd/basic": "^1.27.8",
"@certd/plus-core": "^1.27.8",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13"
@@ -43,5 +43,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -2,7 +2,15 @@ import { Registrable } from "../registry/index.js";
import { FormItemProps } from "../dt/index.js";
import { HttpClient, ILogger, utils } from "@certd/basic";
import * as _ from "lodash-es";
import { AccessRequestHandleReq } from "../core";
import { PluginRequestHandleReq } from "../plugin/index.js";
export type AccessRequestHandleReqInput<T = any> = {
id?: number;
title?: string;
access: T;
};
export type AccessRequestHandleReq<T = any> = PluginRequestHandleReq<AccessRequestHandleReqInput<T>>;
export type AccessInputDefine = FormItemProps & {
title: string;
@@ -33,6 +41,10 @@ export type AccessContext = {
export abstract class BaseAccess implements IAccess {
ctx!: AccessContext;
setCtx(ctx: AccessContext) {
this.ctx = ctx;
}
async onRequest(req: AccessRequestHandleReq) {
if (!req.action) {
throw new Error("action is required");

View File

@@ -56,6 +56,6 @@ export function newAccess(type: string, input: any, ctx?: AccessContext) {
utils,
};
}
access.ctx = ctx;
access.setCtx(ctx);
return access;
}

View File

@@ -7,9 +7,10 @@ import { createAxiosService, hashUtils, HttpRequestConfig, ILogger, logger, util
import { IAccessService } from "../access/index.js";
import { RegistryItem } from "../registry/index.js";
import { Decorator } from "../decorator/index.js";
import { ICnameProxyService, IEmailService, IPluginConfigService } from "../service/index.js";
import { ICnameProxyService, IEmailService, IPluginConfigService, IUrlService } from "../service/index.js";
import { FileStore } from "./file-store.js";
import { cloneDeep, forEach, merge } from "lodash-es";
import { INotificationService, NotificationBody, NotificationContext, notificationRegistry } from "../notification/index.js";
export type ExecutorOptions = {
pipeline: Pipeline;
@@ -17,10 +18,13 @@ export type ExecutorOptions = {
onChanged: (history: RunHistory) => Promise<void>;
accessService: IAccessService;
emailService: IEmailService;
notificationService: INotificationService;
cnameProxyService: ICnameProxyService;
pluginConfigService: IPluginConfigService;
urlService: IUrlService;
fileRootDir?: string;
user: UserInfo;
baseURL?: string;
};
export class Executor {
@@ -357,20 +361,22 @@ export class Executor {
if (!this.pipeline.notifications) {
return;
}
const url = await this.options.urlService.getPipelineDetailUrl(this.pipeline.id, this.runtime.id);
let subject = "";
let content = "";
const errorMessage = error?.message;
if (when === "start") {
subject = `【CertD】开始执行,【${this.pipeline.id}${this.pipeline.title}`;
content = `buildId:${this.runtime.id}`;
subject = `【Certd】开始执行,【${this.pipeline.id}${this.pipeline.title}`;
content = `buildId:${this.runtime.id}\n查看详情:${url}`;
} else if (when === "success") {
subject = `【CertD】执行成功,【${this.pipeline.id}${this.pipeline.title}`;
content = `buildId:${this.runtime.id}`;
subject = `【Certd】执行成功,【${this.pipeline.id}${this.pipeline.title}`;
content = `buildId:${this.runtime.id}\n查看详情:${url}`;
} else if (when === "turnToSuccess") {
subject = `【CertD】执行成功(错误转成功),【${this.pipeline.id}${this.pipeline.title}`;
content = `buildId:${this.runtime.id}`;
subject = `【Certd】执行成功(失败转成功),【${this.pipeline.id}${this.pipeline.title}`;
content = `buildId:${this.runtime.id}\n查看详情:${url}`;
} else if (when === "error") {
subject = `【CertD】执行失败,【${this.pipeline.id}${this.pipeline.title}`;
content = `buildId:${this.runtime.id}\nerror:${error.message}`;
subject = `【Certd】执行失败,【${this.pipeline.id}${this.pipeline.title}`;
content = `buildId:${this.runtime.id}\n查看详情:${url}\nerror:${error.message}`;
} else {
return;
}
@@ -390,6 +396,39 @@ export class Executor {
} catch (e) {
logger.error("send email error", e);
}
} else {
try {
//构建notification插件发送通知
const notifyConfig = await this.options.notificationService.getById(notification.notificationId);
const notificationPlugin = notificationRegistry.get(notifyConfig.type);
const notificationCls: any = notificationPlugin.target;
const notificationSender = new notificationCls();
const notificationCtx: NotificationContext = {
http: utils.http,
logger,
utils,
emailService: this.options.emailService,
};
//设置参数
merge(notificationSender, notifyConfig.setting);
notificationSender.setCtx(notificationCtx);
await notificationSender.onInstance();
const body: NotificationBody = {
title: subject,
content,
userId: this.pipeline.userId,
pipeline: this.pipeline,
result: this.lastRuntime.pipeline.status,
pipelineId: this.pipeline.id,
historyId: this.runtime.id,
errorMessage,
url,
};
//发送通知
await notificationSender.send(body);
} catch (e) {
logger.error("send notification error", e);
}
}
}
}

View File

@@ -1,21 +0,0 @@
import { HttpClient, ILogger, utils } from "@certd/basic";
export type PluginRequestHandleReq<T = any> = {
typeName: string;
action: string;
input: T;
data: any;
};
export type AccessRequestHandleReqInput<T = any> = {
id?: number;
title?: string;
access: T;
};
export type AccessRequestHandleContext = {
http: HttpClient;
logger: ILogger;
utils: typeof utils;
};
export type AccessRequestHandleReq<T = any> = PluginRequestHandleReq<AccessRequestHandleReqInput<T>>;

View File

@@ -3,5 +3,4 @@ export * from "./run-history.js";
export * from "./context.js";
export * from "./storage.js";
export * from "./file-store.js";
export * from "./handler.js";
export * from "./exceptions.js";

View File

@@ -18,7 +18,7 @@ export function NewRunHistory(obj: any) {
return history;
}
export class RunHistory {
id!: string;
id!: any;
pipeline!: Pipeline;
logs: {
[runnableId: string]: string[];

View File

@@ -62,7 +62,7 @@ export type FileItem = {
path: string;
};
export type Runnable = {
id: string;
id: any;
title: string;
strategy?: RunnableStrategy;
runnableType?: string; // pipeline, stage, task , step
@@ -83,6 +83,9 @@ export type Notification = {
type: NotificationType;
when: NotificationWhen[];
options: EmailOptions;
notificationId: number;
title: string;
subType: string;
};
export type Pipeline = Runnable & {

View File

@@ -6,3 +6,4 @@ export * from "./plugin/index.js";
export * from "./context/index.js";
export * from "./decorator/index.js";
export * from "./service/index.js";
export * from "./notification/index.js";

View File

@@ -0,0 +1,111 @@
import { PluginRequestHandleReq } from "../plugin";
import { Registrable } from "../registry/index.js";
import { FormItemProps, HistoryResult, Pipeline } from "../dt/index.js";
import { HttpClient, ILogger, utils } from "@certd/basic";
import * as _ from "lodash-es";
import { IEmailService } from "../service/index.js";
export type NotificationBody = {
userId: number;
title: string;
content: string;
pipeline: Pipeline;
pipelineId: number;
result?: HistoryResult;
historyId: number;
errorMessage?: string;
url?: string;
};
export type NotificationRequestHandleReqInput<T = any> = {
id?: number;
title?: string;
access: T;
};
export type NotificationRequestHandleReq<T = any> = PluginRequestHandleReq<NotificationRequestHandleReqInput<T>>;
export type NotificationInputDefine = FormItemProps & {
title: string;
required?: boolean;
encrypt?: boolean;
};
export type NotificationDefine = Registrable & {
input?: {
[key: string]: NotificationInputDefine;
};
};
export type NotificationInstanceConfig = {
id: number;
type: string;
userId: number;
setting: {
[key: string]: any;
};
};
export interface INotificationService {
getById(id: number): Promise<NotificationInstanceConfig>;
}
export interface INotification {
ctx: NotificationContext;
[key: string]: any;
}
export type NotificationContext = {
http: HttpClient;
logger: ILogger;
utils: typeof utils;
emailService: IEmailService;
};
export abstract class BaseNotification implements INotification {
ctx!: NotificationContext;
http!: HttpClient;
logger!: ILogger;
abstract send(body: NotificationBody): Promise<void>;
// eslint-disable-next-line @typescript-eslint/no-empty-function
async onInstance() {}
setCtx(ctx: NotificationContext) {
this.ctx = ctx;
this.http = ctx.http;
this.logger = ctx.logger;
}
async onRequest(req: NotificationRequestHandleReq) {
if (!req.action) {
throw new Error("action is required");
}
let methodName = req.action;
if (!req.action.startsWith("on")) {
methodName = `on${_.upperFirst(req.action)}`;
}
// @ts-ignore
const method = this[methodName];
if (method) {
// @ts-ignore
return await this[methodName](req.data);
}
throw new Error(`action ${req.action} not found`);
}
async onTestRequest() {
await this.send({
userId: 0,
title: "测试通知",
content: "测试通知",
pipeline: {
id: 1,
title: "测试流水线",
} as any,
pipelineId: 1,
historyId: 1,
url: "http://www.baidu.com",
});
}
}

View File

@@ -0,0 +1,56 @@
// src/decorator/memoryCache.decorator.ts
import { Decorator } from "../decorator/index.js";
import * as _ from "lodash-es";
import { notificationRegistry } from "./registry.js";
import { NotificationContext, NotificationDefine, NotificationInputDefine } from "./api.js";
// 提供一个唯一 key
export const NOTIFICATION_CLASS_KEY = "pipeline:notification";
export const NOTIFICATION_INPUT_KEY = "pipeline:notification:input";
export function IsNotification(define: NotificationDefine): ClassDecorator {
return (target: any) => {
target = Decorator.target(target);
const inputs: any = {};
const properties = Decorator.getClassProperties(target);
for (const property in properties) {
const input = Reflect.getMetadata(NOTIFICATION_INPUT_KEY, target, property);
if (input) {
inputs[property] = input;
}
}
_.merge(define, { input: inputs });
Reflect.defineMetadata(NOTIFICATION_CLASS_KEY, define, target);
target.define = define;
notificationRegistry.register(define.name, {
define,
target,
});
};
}
export function NotificationInput(input?: NotificationInputDefine): PropertyDecorator {
return (target, propertyKey) => {
target = Decorator.target(target, propertyKey);
// const _type = Reflect.getMetadata("design:type", target, propertyKey);
Reflect.defineMetadata(NOTIFICATION_INPUT_KEY, input, target, propertyKey);
};
}
export function newNotification(type: string, input: any, ctx: NotificationContext) {
const register = notificationRegistry.get(type);
if (register == null) {
throw new Error(`notification ${type} not found`);
}
// @ts-ignore
const plugin = new register.target();
for (const key in input) {
plugin[key] = input[key];
}
if (!ctx) {
throw new Error("ctx is required");
}
plugin.setCtx(ctx);
return plugin;
}

View File

@@ -0,0 +1,3 @@
export * from "./api.js";
export * from "./registry.js";
export * from "./decorator.js";

View File

@@ -0,0 +1,4 @@
import { createRegistry } from "../registry/index.js";
// @ts-ignore
export const notificationRegistry = createRegistry("notification");

View File

@@ -3,12 +3,20 @@ import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../dt/index.j
import { FileStore } from "../core/file-store.js";
import { IAccessService } from "../access/index.js";
import { ICnameProxyService, IEmailService } from "../service/index.js";
import { CancelError, IContext, PluginRequestHandleReq, RunnableCollection } from "../core/index.js";
import { CancelError, IContext, RunnableCollection } from "../core/index.js";
import { HttpRequestConfig, ILogger, logger, utils } from "@certd/basic";
import { HttpClient } from "@certd/basic";
import dayjs from "dayjs";
import { IPluginConfigService } from "../service/config";
import { upperFirst } from "lodash-es";
export type PluginRequestHandleReq<T = any> = {
typeName: string;
action: string;
input: T;
data: any;
};
export type UserInfo = {
role: "admin" | "user";
id: any;
@@ -102,6 +110,7 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
_result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {}, pipelinePrivateVars: {} };
ctx!: TaskInstanceContext;
logger!: ILogger;
http!: HttpClient;
accessService!: IAccessService;
clearLastStatus() {
@@ -122,6 +131,7 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
this.ctx = ctx;
this.logger = ctx.logger;
this.accessService = ctx.accessService;
this.http = ctx.http;
}
async getAccess<T = any>(accessId: string) {

View File

@@ -19,7 +19,7 @@ export type OnRegisterContext<T> = {
value: RegistryItem<T>;
};
export type OnRegister<T> = (ctx: OnRegisterContext<T>) => void;
export class Registry<T> {
export class Registry<T = any> {
type = "";
storage: {
[key: string]: RegistryItem<T>;
@@ -89,7 +89,7 @@ export class Registry<T> {
}
}
export function createRegistry<T>(type: string, onRegister?: OnRegister<T>) {
export function createRegistry<T>(type: string, onRegister?: OnRegister<T>): Registry<T> {
const pipelineregistrycacheKey = "PIPELINE_REGISTRY_CACHE";
// @ts-ignore
let cached: any = global[pipelineregistrycacheKey];

View File

@@ -1,3 +1,4 @@
export * from "./email.js";
export * from "./cname.js";
export * from "./config.js";
export * from "./url.js";

View File

@@ -0,0 +1,3 @@
export interface IUrlService {
getPipelineDetailUrl(pipelineId: number, historyId: number): Promise<string>;
}

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/lib-huawei
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
**Note:** Version bump only for package @certd/lib-huawei
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
**Note:** Version bump only for package @certd/lib-huawei
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
**Note:** Version bump only for package @certd/lib-huawei

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.27.5",
"version": "1.27.8",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -21,5 +21,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/lib-iframe
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
**Note:** Version bump only for package @certd/lib-iframe
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
**Note:** Version bump only for package @certd/lib-iframe
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
**Note:** Version bump only for package @certd/lib-iframe

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.27.5",
"version": "1.27.8",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -30,5 +30,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/lib-k8s
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
**Note:** Version bump only for package @certd/lib-k8s
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
**Note:** Version bump only for package @certd/lib-k8s
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
**Note:** Version bump only for package @certd/lib-k8s

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.27.5",
"version": "1.27.8",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -16,7 +16,7 @@
"preview": "vite preview"
},
"dependencies": {
"@certd/basic": "^1.27.5",
"@certd/basic": "^1.27.8",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -31,5 +31,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/lib-server
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
**Note:** Version bump only for package @certd/lib-server
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
### Bug Fixes
* 修复vip试用secret报错的bug ([018dee6](https://github.com/certd/certd/commit/018dee6c383233560f078dfd30f6c2857a7e15ee))
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
### Performance Improvements

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.27.5",
"version": "1.27.8",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -27,10 +27,10 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.27.5",
"@certd/basic": "^1.27.5",
"@certd/pipeline": "^1.27.5",
"@certd/plus-core": "^1.27.5",
"@certd/acme-client": "^1.27.8",
"@certd/basic": "^1.27.8",
"@certd/pipeline": "^1.27.8",
"@certd/plus-core": "^1.27.8",
"@midwayjs/cache": "~3.14.0",
"@midwayjs/core": "~3.17.1",
"@midwayjs/i18n": "~3.17.3",
@@ -61,5 +61,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -55,7 +55,9 @@ export class PlusService {
async bindUrl(url: string) {
const plusRequestService = await this.getPlusRequestService();
return await plusRequestService.bindUrl(url);
const res = await plusRequestService.bindUrl(url);
this.plusRequestService = null;
return res;
}
async register() {
@@ -64,6 +66,7 @@ export class PlusService {
if (!licenseInfo.license) {
await plusRequestService.register();
logger.info('站点注册成功');
this.plusRequestService = null;
}
}
@@ -109,8 +112,8 @@ export class PlusService {
}
async getVipTrial() {
const plusRequestService = await this.getPlusRequestService();
await this.register();
const plusRequestService = await this.getPlusRequestService();
const res = await plusRequestService.request({
url: '/activation/subject/vip/trialGet',
method: 'POST',

View File

@@ -18,6 +18,8 @@ export class SysPublicSettings extends BaseSettings {
limitUserPipelineCount = 0;
managerOtherUserPipeline = false;
icpNo?: string;
defaultCron?: string;
defaultNotification?: number;
// triggerOnStartup = false;
}

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
**Note:** Version bump only for package @certd/midway-flyway-js

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.27.5",
"version": "1.27.8",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/plugin-cert
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
### Performance Improvements
* 谷歌EAB绑定邮箱改成必填 ([81a8123](https://github.com/certd/certd/commit/81a8123725d7bf4bd6a32a64a066bd760b7b6a7f))
* 优化插件开发dnsProvider无需写http logger 变量 ([fcbb5e4](https://github.com/certd/certd/commit/fcbb5e46a112174150a62648319b8224fce3b7ed))
* 支持企业微信群聊机器人通知 ([b805a29](https://github.com/certd/certd/commit/b805a2925984144a31575b8aaa622f0c30d41b56))
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
**Note:** Version bump only for package @certd/plugin-cert
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
### Bug Fixes

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.27.5",
"version": "1.27.8",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -15,9 +15,9 @@
"preview": "vite preview"
},
"dependencies": {
"@certd/acme-client": "^1.27.5",
"@certd/basic": "^1.27.5",
"@certd/pipeline": "^1.27.5",
"@certd/acme-client": "^1.27.8",
"@certd/basic": "^1.27.8",
"@certd/pipeline": "^1.27.8",
"@google-cloud/publicca": "^1.3.0",
"dayjs": "^1.11.7",
"jszip": "^3.10.1",
@@ -40,5 +40,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "339554bdbf61c4784614747734a420e744e13bfb"
"gitHead": "3198d07553493ebb17e8239166f370ee316ebd0a"
}

View File

@@ -34,7 +34,7 @@ export class EabAccess extends BaseAccess {
},
rules: { type: "email", message: "请输入正确的邮箱" },
helper: "Google的EAB申请证书更换邮箱会导致EAB失效可以在此处绑定一个邮箱避免此问题",
required: false,
required: true,
})
email = "";
}

View File

@@ -2,12 +2,17 @@ import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, IDnsProvide
import psl from "psl";
import { dnsProviderRegistry } from "./registry.js";
import { Decorator } from "@certd/pipeline";
import { HttpClient, ILogger } from "@certd/basic";
export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
ctx!: DnsProviderContext;
http!: HttpClient;
logger!: ILogger;
setCtx(ctx: DnsProviderContext) {
this.ctx = ctx;
this.logger = ctx.logger;
this.http = ctx.http;
}
abstract createRecord(options: CreateRecordOptions): Promise<T>;

View File

@@ -6,7 +6,6 @@ import JSZip from "jszip";
import { CertConverter } from "./convert.js";
import fs from "fs";
import { pick } from "lodash-es";
import { HttpClient } from "@certd/basic";
export { CertReader };
export type { CertInfo };
@@ -92,7 +91,6 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
csrInfo!: string;
userContext!: IContext;
http!: HttpClient;
lastStatus!: Step;
@TaskOutput({
@@ -102,7 +100,6 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
async onInstance() {
this.userContext = this.ctx.userContext;
this.http = this.ctx.http;
this.lastStatus = this.ctx.lastStatus as Step;
await this.onInit();
}

View File

@@ -58,7 +58,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
},
required: true,
helper:
"DNS直接验证域名是在阿里云、腾讯云、华为云、Cloudflare、西数注册的选它。\nCNAME代理验证支持任何注册商注册的域名但第一次需要手动添加CNAME记录",
"DNS直接验证域名是在阿里云、腾讯云、华为云、Cloudflare、NameSilo、西数注册的,选它。\nCNAME代理验证支持任何注册商注册的域名但第一次需要手动添加CNAME记录",
})
challengeType!: string;

View File

@@ -96,7 +96,6 @@ export class CertApplyLegoPlugin extends CertApplyBasePlugin {
this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
this.userContext = this.ctx.userContext;
this.http = this.ctx.http;
this.lastStatus = this.ctx.lastStatus as Step;
if (this.legoEabAccessId) {
this.eab = await this.accessService.getById(this.legoEabAccessId);

View File

@@ -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.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/ui-client
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
### Performance Improvements
* 通知管理 ([d9a00ee](https://github.com/certd/certd/commit/d9a00eeaf72735ced67c59d7983d84e3c730064a))
* 通知渠道支持测试按钮 ([b54ae27](https://github.com/certd/certd/commit/b54ae272ebc2d31b32b049d44e2299a6be7f153c))
* 支持企业微信群聊机器人通知 ([b805a29](https://github.com/certd/certd/commit/b805a2925984144a31575b8aaa622f0c30d41b56))
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
### Performance Improvements
* 当步骤全部都禁用时,任务本身显示删除线 ([9ab9a6e](https://github.com/certd/certd/commit/9ab9a6e8b083e19793894f23e59f29c604ec98e5))
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.27.5",
"version": "1.27.8",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -65,8 +65,8 @@
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@certd/lib-iframe": "^1.27.5",
"@certd/pipeline": "^1.27.5",
"@certd/lib-iframe": "^1.27.8",
"@certd/pipeline": "^1.27.8",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",

View File

@@ -24,11 +24,18 @@ export type PlusInfo = {
isComm?: boolean;
};
export type SysPublicSetting = {
registerEnabled: boolean;
managerOtherUserPipeline: boolean;
registerEnabled?: boolean;
limitUserPipelineCount?: number;
managerOtherUserPipeline?: boolean;
icpNo?: string;
};
export type SysPrivateSetting = {
httpProxy?: string;
httpsProxy?: string;
dnsResultOrder?: string;
commonCnameEnabled?: boolean;
};
export type SysInstallInfo = {
siteId: string;
};

View File

@@ -13,8 +13,9 @@
@error="onError"
/>
</div>
<div class="mt-5">
<div class="mt-5 flex">
<a-input :disabled="true" :readonly="readonly" :value="modelValue" @change="onChange"></a-input>
<fs-icon icon="ion:close-circle" class="pointer fs-16 ml-5 color-gray" title="清除选择" @click="onClear"></fs-icon>
</div>
<div class="helper">下次触发时间{{ nextTime }}</div>
<div class="fs-helper">{{ errorMessage }}</div>
@@ -35,8 +36,12 @@ const props = defineProps<{
}>();
const period = ref<string>("");
if (props.modelValue == null) {
if (props.modelValue == null || props.modelValue.endsWith("* * *")) {
period.value = "day";
} else if (props.modelValue.endsWith("* *")) {
period.value = "month";
} else if (props.modelValue.endsWith("*")) {
period.value = "year";
}
const emit = defineEmits<{
"update:modelValue": any;
@@ -74,6 +79,13 @@ const onError = (error: any) => {
errorMessage.value = error;
};
const onClear = () => {
if (props.disabled) {
return;
}
onUpdate("");
};
const nextTime = computed(() => {
if (props.modelValue == null) {
return "请先设置正确的cron表达式";

View File

@@ -6,6 +6,9 @@
<div>1. 解析记录应该添加在{{ record.domain }}域名下</div>
<div>2. 要添加的是CNAME类型的记录不是TXT</div>
<div>3. 核对记录值是否是:{{ record.recordValue }}</div>
<div>
4. 运行命令 <a-tag>nslookup -qa=txt {{ record.hostRecord }}{{ record.domain }}</a-tag> 查看解析配置是否正确
</div>
</div>
</template>
<fs-icon class="ml-5 pointer" icon="mingcute:question-line"></fs-icon>

View File

@@ -11,12 +11,16 @@
</template>
<script setup lang="ts">
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
import { ref } from "vue";
import { ref, inject } from "vue";
import { Form } from "ant-design-vue";
defineOptions({
name: "ApiTest"
});
const getScope: any = inject("get:scope");
const getPluginType: any = inject("get:plugin:type");
const formItemContext = Form.useInjectFormItemContext();
const props = defineProps<{} & ComponentPropsType>();
const emit = defineEmits<{
@@ -31,16 +35,21 @@ const doTest = async () => {
return;
}
formItemContext.onFieldChange();
const { form } = getScope();
const pluginType = getPluginType();
message.value = "";
hasError.value = false;
loading.value = true;
try {
const res = await doRequest(
{
type: props.type,
typeName: props.typeName,
type: pluginType,
typeName: form.type,
action: props.action,
input: props.form
input: pluginType === "plugin" ? form.input : form
},
{
onError(err: any) {
@@ -50,9 +59,7 @@ const doTest = async () => {
showErrorNotify: false
}
);
if (res && res.length > 0) {
message.value = "测试请求成功";
}
message.value = "测试请求成功";
} finally {
loading.value = false;
}

View File

@@ -42,6 +42,9 @@ const emit = defineEmits<{
const attrs = useAttrs();
const getCurrentPluginDefine: any = inject("getCurrentPluginDefine");
const getScope: any = inject("get:scope");
const getPluginType: any = inject("get:plugin:type");
const optionsRef = ref([]);
const message = ref("");
const hasError = ref(false);
@@ -75,13 +78,16 @@ const getOptions = async () => {
hasError.value = false;
loading.value = true;
optionsRef.value = [];
const { form } = getScope();
const pluginType = getPluginType();
try {
const res = await doRequest(
{
type: props.type,
typeName: props.typeName,
type: pluginType,
typeName: form.type,
action: props.action,
input: props.form
input: pluginType === "plugin" ? form.input : form
},
{
onError(err: any) {

View File

@@ -1,9 +1,9 @@
import { request } from "/@/api/service";
export type ComponentPropsType = {
type: string;
typeName: string;
action?: string;
form: any;
type?: string;
typeName?: string;
action: string;
form?: any;
value?: any;
};
export type RequestHandleReq<T = any> = {
@@ -15,7 +15,7 @@ export type RequestHandleReq<T = any> = {
};
export async function doRequest(req: RequestHandleReq, opts: any = {}) {
const url = req.type === "access" ? "/pi/handle/access" : "/pi/handle/plugin";
const url = `/pi/handle/${req.type}`;
const { typeName, action, data, input } = req;
const res = await request({
url,

View File

@@ -10,7 +10,7 @@
</template>
<script lang="tsx" setup>
import { defineProps, ref, useAttrs } from "vue";
import { defineProps, inject, ref, useAttrs } from "vue";
import { Modal } from "ant-design-vue";
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
@@ -19,7 +19,6 @@ defineOptions({
});
const props = defineProps<ComponentPropsType>();
const emit = defineEmits<{
"update:value": any;
}>();
@@ -27,16 +26,21 @@ const emit = defineEmits<{
const attrs = useAttrs();
const otpCodeRef = ref("");
const getScope: any = inject("get:scope");
const getPluginType: any = inject("get:plugin:type");
async function loginWithOTPCode(otpCode: string) {
const { form } = getScope();
const pluginType = getPluginType();
return await doRequest({
type: props.type,
typeName: props.typeName,
type: pluginType,
typeName: form.type,
action: "LoginWithOPTCode",
data: {
otpCode
},
input: props.form
input: form
});
}

View File

@@ -49,6 +49,17 @@ export const certdResources = [
cache: true
}
},
{
title: "通知设置",
name: "NotificationManager",
path: "/certd/notification",
component: "/certd/notification/index.vue",
meta: {
icon: "ion:megaphone-outline",
auth: true,
cache: true
}
},
{
title: "CNAME记录管理",
name: "CnameRecord",

View File

@@ -198,6 +198,9 @@ h1, h2, h3, h4, h5, h6 {
.color-green {
color: green;
}
.color-gray {
color: gray;
}
.iconify{
//font-size: 16px;

View File

@@ -1,11 +1,11 @@
<template>
<div class="access-selector">
<span v-if="target.name" class="mr-5 cd-flex-inline">
<span v-if="target?.name" class="mr-5 cd-flex-inline">
<a-tag class="mr-5" color="green">{{ target.name }}</a-tag>
<fs-icon class="cd-icon-button" icon="ion:close-circle-outline" @click="clear"></fs-icon>
</span>
<span v-else class="mlr-5 text-gray">{{ placeholder }}</span>
<a-button class="ml-5" :size="size" @click="chooseForm.open">选择</a-button>
<a-button class="ml-5" :disabled="disabled" :size="size" @click="chooseForm.open">选择</a-button>
<a-form-item-rest v-if="chooseForm.show">
<a-modal v-model:open="chooseForm.show" title="选择授权提供者" width="900px" @ok="chooseForm.ok">
<div style="height: 400px; position: relative">
@@ -17,9 +17,10 @@
</template>
<script>
import { defineComponent, reactive, ref, watch } from "vue";
import { defineComponent, reactive, ref, watch, inject } from "vue";
import CertAccessModal from "./access/index.vue";
import { createAccessApi } from "../api";
import { message } from "ant-design-vue";
export default defineComponent({
name: "AccessSelector",
@@ -44,9 +45,13 @@ export default defineComponent({
from: {
type: String, //user | sys
default: "user"
},
disabled: {
type: Boolean,
default: false
}
},
emits: ["update:modelValue"],
emits: ["update:modelValue", "change", "selectedChange"],
setup(props, ctx) {
const api = createAccessApi(props.from);
@@ -55,14 +60,32 @@ export default defineComponent({
async function refreshTarget(value) {
selectedId.value = value;
if (value > 0) {
target.value = await api.GetObj(value);
target.value = await api.GetSimpleInfo(value);
}
}
function clear() {
selectedId.value = "";
target.value = null;
if (props.disabled) {
return;
}
emitValue(null);
}
async function emitValue(value) {
if (pipeline?.value && target?.value && pipeline.value.userId !== target.value.userId) {
message.error("对不起,您不能修改他人流水线的授权");
return;
}
if (value == null) {
selectedId.value = "";
target.value = null;
} else {
selectedId.value = value;
await refreshTarget(selectedId.value);
}
ctx.emit("change", selectedId.value);
ctx.emit("update:modelValue", selectedId.value);
ctx.emit("selectedChange", target.value);
}
watch(
@@ -71,7 +94,7 @@ export default defineComponent({
},
async (value) => {
selectedId.value = null;
target.value = {};
target.value = null;
if (value == null) {
return;
}
@@ -99,17 +122,18 @@ export default defineComponent({
}
);
//当不在pipeline中编辑时可能为空
const pipeline = inject("pipeline", null);
const chooseForm = reactive({
show: false,
open() {
chooseForm.show = true;
},
ok: () => {
chooseForm.show = false;
console.log("choose ok:", selectedId.value);
refreshTarget(selectedId.value);
ctx.emit("change", selectedId.value);
ctx.emit("update:modelValue", selectedId.value);
emitValue(selectedId.value);
chooseForm.show = false;
}
});

View File

@@ -43,6 +43,14 @@ export function createAccessApi(from = "user") {
});
},
async GetSimpleInfo(id: number) {
return await request({
url: apiPrefix + "/simpleInfo",
method: "post",
params: { id }
});
},
async GetSecretPlain(id: number, key: string) {
return await request({
url: apiPrefix + "/getSecretPlain",

View File

@@ -6,6 +6,9 @@ import SecretPlainGetter from "/@/views/certd/access/access-selector/access/secr
export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
provide("accessApi", api);
provide("get:plugin:type", () => {
return "access";
});
const AccessTypeDictRef = dict({
url: "/pi/access/accessTypeDict"
});
@@ -44,6 +47,13 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
//eval
useReference(column);
if (column.required) {
if (!column.rules) {
column.rules = [];
}
column.rules.push({ required: true, message: "此项必填" });
}
//设置默认值
if (column.value != null && get(form, key) == null) {
set(form, key, column.value);

View File

@@ -115,7 +115,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}
},
cnameProviderId: {
title: "CNAME提供者",
title: "CNAME服务",
type: "dict-select",
dict: dict({
url: "/cname/provider/list",

View File

@@ -138,7 +138,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
column: {
width: 300,
cellRender: ({ row, value }) => {
debugger;
return (
<router-link to={{ path: "/certd/pipeline/detail", query: { id: row.pipelineId, editMode: false, historyId: row.id } }}>{value}</router-link>
);

View File

@@ -0,0 +1,70 @@
import { request } from "/src/api/service";
export function createApi() {
const apiPrefix = "/pi/notification";
return {
async GetList(query: any) {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query
});
},
async AddObj(obj: any) {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
},
async UpdateObj(obj: any) {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
},
async DelObj(id: number) {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
},
async GetObj(id: number) {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id }
});
},
async GetSimpleInfo(id: number) {
return await request({
url: apiPrefix + "/simpleInfo",
method: "post",
params: { id }
});
},
async GetProviderDefine(type: string) {
return await request({
url: apiPrefix + "/define",
method: "post",
params: { type }
});
},
async GetProviderDefineByType(type: string) {
return await request({
url: apiPrefix + "/defineByType",
method: "post",
params: { type }
});
}
};
}

View File

@@ -0,0 +1,181 @@
import { ColumnCompositionProps, compute, dict } from "@fast-crud/fast-crud";
import { computed, provide, ref, toRef } from "vue";
import { useReference } from "/@/use/use-refrence";
import { forEach, get, merge, set } from "lodash-es";
export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
provide("notificationApi", api);
provide("get:plugin:type", () => {
return "notification";
});
const notificationTypeDictRef = dict({
url: "/pi/notification/getTypeDict"
});
const defaultPluginConfig = {
component: {
name: "a-input",
vModel: "value"
}
};
function buildDefineFields(define: any, form: any, mode: string) {
const formWrapperRef = crudExpose.getFormWrapperRef();
const columnsRef = toRef(formWrapperRef.formOptions, "columns");
for (const key in columnsRef.value) {
if (key.indexOf(".") >= 0) {
delete columnsRef.value[key];
}
}
console.log('crudBinding.value[mode + "Form"].columns', columnsRef.value);
forEach(define.input, (value: any, mapKey: any) => {
const key = "body." + mapKey;
const field = {
...value,
key
};
const column = merge({ title: key }, defaultPluginConfig, field);
//eval
useReference(column);
if (column.required) {
if (!column.rules) {
column.rules = [];
}
column.rules.push({ required: true, message: "此项必填" });
}
//设置默认值
if (column.value != null && get(form, key) == null) {
set(form, key, column.value);
}
//字段配置赋值
columnsRef.value[key] = column;
console.log("form", columnsRef.value, form);
});
}
const currentDefine = ref();
return {
id: {
title: "ID",
key: "id",
type: "number",
column: {
width: 100
},
form: {
show: false
}
},
type: {
title: "通知类型",
type: "dict-select",
dict: notificationTypeDictRef,
search: {
show: false
},
column: {
width: 200,
component: {
color: "auto"
}
},
editForm: {
component: {
disabled: false
}
},
form: {
component: {
disabled: false,
showSearch: true,
filterOption: (input: string, option: any) => {
input = input?.toLowerCase();
return option.value.toLowerCase().indexOf(input) >= 0 || option.label.toLowerCase().indexOf(input) >= 0;
}
},
rules: [{ required: true, message: "请选择通知类型" }],
valueChange: {
immediate: true,
async handle({ value, mode, form, immediate }) {
if (value == null) {
return;
}
const lastTitle = currentDefine.value?.title;
const define = await api.GetProviderDefine(value);
currentDefine.value = define;
console.log("define", define);
if (!immediate) {
form.body = {};
}
if (!form.name || form.name === lastTitle) {
form.name = define.title;
}
buildDefineFields(define, form, mode);
}
},
helper: computed(() => {
const define = currentDefine.value;
if (define == null) {
return "";
}
return define.desc;
})
}
} as ColumnCompositionProps,
name: {
title: "通知名称",
search: {
show: true
},
type: ["text"],
form: {
rules: [{ required: true, message: "请填写名称" }],
helper: "随便填,当多个相同类型的通知时,便于区分"
},
column: {
width: 200
}
},
test: {
title: "测试",
form: {
show: compute(({ form }) => {
return !!form.type;
}),
component: {
name: "api-test",
action: "TestRequest"
},
order: 999
},
column: {
show: false
}
},
setting: {
column: { show: false },
form: {
show: false,
valueBuilder({ value, form }) {
form.body = {};
if (!value) {
return;
}
const setting = JSON.parse(value);
for (const key in setting) {
form.body[key] = setting[key];
}
},
valueResolve({ form }) {
const setting = form.body;
form.setting = JSON.stringify(setting);
}
}
} as ColumnCompositionProps
};
}

View File

@@ -0,0 +1,53 @@
// @ts-ignore
import { useI18n } from "vue-i18n";
import { ref } from "vue";
import { getCommonColumnDefine } from "./common";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n();
const api = context.api;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async (req: EditReq) => {
const { form, row } = req;
form.id = row.id;
const res = await api.UpdateObj(form);
return res;
};
const delRequest = async (req: DelReq) => {
const { row } = req;
return await api.DelObj(row.id);
};
const addRequest = async (req: AddReq) => {
const { form } = req;
const res = await api.AddObj(form);
return res;
};
const typeRef = ref();
const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api);
return {
crudOptions: {
request: {
pageRequest,
addRequest,
editRequest,
delRequest
},
form: {
labelCol: {
span: 6
}
},
rowHandle: {
width: 200
},
columns: {
...commonColumnsDefine
}
}
};
}

View File

@@ -0,0 +1,39 @@
<template>
<fs-page>
<template #header>
<div class="title">
通知管理
<span class="sub">管理通知配置</span>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page>
</template>
<script lang="ts">
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: "NotificationManager",
setup() {
const api = createApi();
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } });
// 页面打开后获取列表数据
onMounted(() => {
crudExpose.doRefresh();
});
onActivated(() => {
crudExpose.doRefresh();
});
return {
crudBinding,
crudRef
};
}
});
</script>

View File

@@ -0,0 +1,149 @@
<template>
<div class="notification-selector">
<span v-if="target?.name" class="mr-5 cd-flex-inline">
<a-tag class="mr-5" color="green">{{ target.name }}</a-tag>
<fs-icon class="cd-icon-button" icon="ion:close-circle-outline" @click="clear"></fs-icon>
</span>
<span v-else class="mlr-5 text-gray">{{ placeholder }}</span>
<a-button class="ml-5" :disabled="disabled" :size="size" @click="chooseForm.open">选择</a-button>
<a-form-item-rest v-if="chooseForm.show">
<a-modal v-model:open="chooseForm.show" title="选择通知渠道" width="900px" @ok="chooseForm.ok">
<div style="height: 400px; position: relative">
<cert-notification-modal v-model="selectedId"></cert-notification-modal>
</div>
</a-modal>
</a-form-item-rest>
</div>
</template>
<script>
import { defineComponent, reactive, ref, watch, inject } from "vue";
import CertNotificationModal from "./modal/index.vue";
import { createApi } from "../api";
import { message } from "ant-design-vue";
export default defineComponent({
name: "NotificationSelector",
components: { CertNotificationModal },
props: {
modelValue: {
type: [Number, String],
default: null
},
type: {
type: String,
default: ""
},
placeholder: {
type: String,
default: "请选择"
},
size: {
type: String,
default: "middle"
},
disabled: {
type: Boolean,
default: false
}
},
emits: ["update:modelValue", "selectedChange", "change"],
setup(props, ctx) {
const api = createApi();
const target = ref({});
const selectedId = ref();
async function refreshTarget(value) {
selectedId.value = value;
if (value > 0) {
target.value = await api.GetSimpleInfo(value);
}
}
function clear() {
if (props.disabled) {
return;
}
emitValue(null);
}
async function emitValue(value) {
if (pipeline?.value && target?.value && pipeline.value.userId !== target.value.userId) {
message.error("对不起,您不能修改他人流水线的通知");
return;
}
if (value == null) {
selectedId.value = "";
target.value = null;
} else {
selectedId.value = value;
await refreshTarget(selectedId.value);
}
ctx.emit("change", selectedId.value);
ctx.emit("update:modelValue", selectedId.value);
ctx.emit("selectedChange", target.value);
}
watch(
() => {
return props.modelValue;
},
async (value) => {
selectedId.value = null;
target.value = null;
if (value == null) {
return;
}
await refreshTarget(value);
},
{
immediate: true
}
);
const providerDefine = ref({});
async function refreshProviderDefine(type) {
providerDefine.value = await api.GetProviderDefine(type);
}
// watch(
// () => {
// return props.type;
// },
// async (value) => {
// await refreshProviderDefine(value);
// },
// {
// immediate: true
// }
// );
//当不在pipeline中编辑时可能为空
const pipeline = inject("pipeline", null);
const chooseForm = reactive({
show: false,
open() {
chooseForm.show = true;
},
ok: () => {
console.log("choose ok:", selectedId.value);
emitValue(selectedId.value);
chooseForm.show = false;
}
});
return {
clear,
target,
selectedId,
providerDefine,
chooseForm
};
}
});
</script>
<style lang="less">
.notification-selector {
}
</style>

View File

@@ -0,0 +1,88 @@
// @ts-ignore
import { ref } from "vue";
import { getCommonColumnDefine } from "../../common";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { crudBinding } = crudExpose;
const { props, ctx, api } = context;
const lastResRef = ref();
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await context.api.GetList(query);
};
const editRequest = async (req: EditReq) => {
const { form, row } = req;
form.id = row.id;
const res = await context.api.UpdateObj(form);
lastResRef.value = res;
return res;
};
const delRequest = async (req: DelReq) => {
const { row } = req;
return await context.api.DelObj(row.id);
};
const addRequest = async (req: AddReq) => {
const { form } = req;
const res = await context.api.AddObj(form);
lastResRef.value = res;
return res;
};
const selectedRowKey = ref([props.modelValue]);
const onSelectChange = (changed: any) => {
selectedRowKey.value = changed;
ctx.emit("update:modelValue", changed[0]);
};
const typeRef = ref("");
context.typeRef = typeRef;
const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api);
commonColumnsDefine.type.editForm.component.disabled = true;
return {
typeRef,
crudOptions: {
request: {
pageRequest,
addRequest,
editRequest,
delRequest
},
toolbar: {
show: false
},
search: {
show: false
},
form: {
wrapper: {
width: "1050px"
}
},
rowHandle: {
width: 200
},
table: {
scroll: {
x: 800
},
rowSelection: {
type: "radio",
selectedRowKeys: selectedRowKey,
onChange: onSelectChange
},
customRow: (record: any) => {
return {
onClick: () => {
onSelectChange([record.id]);
} // 点击行
};
}
},
columns: {
...commonColumnsDefine
}
}
};
}

View File

@@ -0,0 +1,37 @@
<template>
<fs-page class="page-cert-notification-modal">
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page>
</template>
<script lang="ts">
import { defineComponent, onMounted, watch } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import { createApi } from "../../api";
export default defineComponent({
name: "CertNotificationModal",
props: {
modelValue: {}
},
emits: ["update:modelValue"],
setup(props, ctx) {
const api = createApi();
const context: any = { props, ctx, api };
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
onMounted(() => {
crudExpose.doRefresh();
});
return {
crudBinding,
crudRef
};
}
});
</script>
<style lang="less">
.page-cert-notification {
}
</style>

View File

@@ -5,6 +5,7 @@ import _, { merge } from "lodash-es";
import { useUserStore } from "/@/store/modules/user";
import { useSettingStore } from "/@/store/modules/settings";
import * as api from "../api.plugin";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
export default function (certPluginGroup: PluginGroup, formWrapperRef: any): CreateCrudOptionsRet {
const inputs: any = {};
const userStore = useUserStore();
@@ -102,30 +103,16 @@ export default function (certPluginGroup: PluginGroup, formWrapperRef: any): Cre
order: 100
}
},
emailNotify: {
title: "失败邮件通知",
type: "dict-switch",
dict: dict({
data: [
{ value: true, label: "启用" },
{ value: false, label: "不启用" }
]
}),
notification: {
title: "失败通知",
type: "text",
form: {
component: {
name: NotificationSelector,
vModel: "modelValue"
},
order: 101,
value: true,
helper: {
render: () => {
if (settingStore.isPlus) {
return "建议启用";
}
return (
<div>
<router-link to={{ path: "/sys/settings/email" }}></router-link>
</div>
);
}
}
helper: "建议设置,任务执行失败实时提醒"
}
}
}

View File

@@ -14,7 +14,7 @@ import * as _ from "lodash-es";
import { useModal } from "/@/use/use-modal";
import CertView from "./cert-view.vue";
import { eachStages } from "./utils";
import { createApi as createNotificationApi } from "../notification/api";
export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter();
const { t } = useI18n();
@@ -102,13 +102,13 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
triggers.push({ title: "定时触发", type: "timer", props: { cron: form.triggerCron } });
}
const notifications = [];
if (form.emailNotify) {
if (form.notification) {
const notify = await createNotificationApi().GetSimpleInfo(form.notification);
notifications.push({
type: "email",
type: "custom",
when: ["error", "turnToSuccess"],
options: {
receivers: [form.email]
}
notificationId: form.notification,
title: notify.name
});
}
let pipeline = {

View File

@@ -30,6 +30,7 @@ const pipelineOptions: PipelineOptions = {
return {
pipeline: {
id: detail.pipeline.id,
userId: detail.pipeline.userId,
stages: [],
triggers: [],
...JSON.parse(detail.pipeline.content || "{}")
@@ -67,7 +68,7 @@ const pipelineOptions: PipelineOptions = {
const pipelineOptionsRef: Ref<PipelineOptions> = ref(pipelineOptions);
const editMode = ref(false);
if (route.query.editMode !== "false") {
if (route.query.editMode === "true") {
editMode.value = true;
}

View File

@@ -19,6 +19,7 @@
<pi-container>
<a-form ref="notificationFormRef" class="notification-form" :model="currentNotification" :label-col="labelCol" :wrapper-col="wrapperCol">
<fs-form-item
v-if="currentNotification.type === 'email'"
v-model="currentNotification.type"
:item="{
title: '类型',
@@ -28,7 +29,10 @@
name: 'a-select',
vModel: 'value',
disabled: !editMode,
options: [{ value: 'email', label: '邮件' }]
options: [
{ value: 'email', label: '邮件' },
{ value: 'other', label: '其他通知方式' }
]
},
rules: [{ required: true, message: '此项必填' }]
}"
@@ -47,15 +51,35 @@
options: [
{ value: 'start', label: '开始时' },
{ value: 'success', label: '成功时' },
{ value: 'turnToSuccess', label: '错误转成功时' },
{ value: 'error', label: '错误时' }
{ value: 'turnToSuccess', label: '失败转成功时' },
{ value: 'error', label: '失败时' }
]
},
helper: `建议仅选择'错误时'和'错误转成功'两种即可`,
helper: `建议仅选择'失败时'和'失败转成功'两种即可`,
rules: [{ required: true, message: '此项必填' }]
}"
/>
<pi-notification-form-email
v-if="currentNotification.type === 'email'"
ref="optionsRef"
v-model:options="currentNotification.options"
></pi-notification-form-email>
<fs-form-item
v-else
v-model="currentNotification.notificationId"
:item="{
title: '通知配置',
key: 'type',
component: {
disabled: !editMode,
name: NotificationSelector,
onSelectedChange
},
helper: '请选择通知方式',
rules: [{ required: true, message: '此项必填' }]
}"
/>
<pi-notification-form-email ref="optionsRef" v-model:options="currentNotification.options"></pi-notification-form-email>
</a-form>
<template #footer>
@@ -70,14 +94,16 @@
<script lang="ts">
import { Modal } from "ant-design-vue";
import { ref } from "vue";
import { ref, Ref } from "vue";
import * as _ from "lodash-es";
import { nanoid } from "nanoid";
import PiNotificationFormEmail from "./pi-notification-form-email.vue";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
export default {
name: "PiNotificationForm",
components: { PiNotificationFormEmail },
// eslint-disable-next-line vue/no-unused-components
components: { NotificationSelector, PiNotificationFormEmail },
props: {
editMode: {
type: Boolean,
@@ -93,7 +119,7 @@ export default {
function useNotificationForm() {
const mode = ref("add");
const callback = ref();
const currentNotification = ref({ type: undefined, when: [], options: {} });
const currentNotification: Ref<any> = ref({ type: undefined, when: [], options: {}, notificationId: undefined, title: "" });
const currentPlugin = ref({});
const notificationFormRef = ref(null);
const notificationDrawerVisible = ref(false);
@@ -112,6 +138,13 @@ export default {
required: true,
message: "请选择通知时机"
}
],
notificationId: [
{
type: "number",
required: true,
message: "请选择通知配置"
}
]
});
@@ -135,7 +168,7 @@ export default {
const notificationAdd = (emit: any) => {
mode.value = "add";
const notification = { id: nanoid(), type: "email", when: ["error"] };
const notification = { id: nanoid(), type: "custom", when: ["error", "turnToSuccess"] };
notificationOpen(notification, emit);
};
@@ -150,7 +183,10 @@ export default {
};
const notificationSave = async (e: any) => {
currentNotification.value.options = await optionsRef.value.getValue();
if (optionsRef.value) {
currentNotification.value.options = await optionsRef.value.getValue();
}
console.log("currentNotificationSave", currentNotification.value);
try {
await notificationFormRef.value.validate();
@@ -177,8 +213,13 @@ export default {
const blankFn = () => {
return {};
};
function onSelectedChange(node: any) {
currentNotification.value.title = node?.name || null;
}
return {
notificationFormRef,
onSelectedChange,
mode,
notificationAdd,
notificationEdit,
@@ -201,6 +242,11 @@ export default {
labelCol: { span: 6 },
wrapperCol: { span: 16 }
};
},
computed: {
NotificationSelector() {
return NotificationSelector;
}
}
};
</script>

View File

@@ -86,13 +86,13 @@
},
rules: [{ required: true, message: '此项必填' }]
}"
:get-context-fn="blankFn"
:get-context-fn="getScopeFunc"
/>
<template v-for="(item, key) in currentPlugin.input" :key="key">
<fs-form-item v-if="item.show !== false" v-model="currentStep.input[key]" :item="item" :get-context-fn="blankFn" />
<fs-form-item v-if="item.show !== false" v-model="currentStep.input[key]" :item="item" :get-context-fn="getScopeFunc" />
</template>
<fs-form-item v-model="currentStep.strategy.runStrategy" :item="runStrategyProps" :get-context-fn="blankFn" />
<fs-form-item v-model="currentStep.strategy.runStrategy" :item="runStrategyProps" :get-context-fn="getScopeFunc" />
</a-form>
</div>
<template #footer>
@@ -222,6 +222,9 @@ export default {
provide("getCurrentPluginDefine", () => {
return currentPluginDefine;
});
provide("get:plugin:type", () => {
return "plugin";
});
function getContext() {
return {
@@ -302,8 +305,10 @@ export default {
stepDrawerClose();
};
const blankFn = () => {
return {};
const getScopeFunc = () => {
return {
form: currentStep.value
};
};
const pluginSearch = ref({
@@ -359,7 +364,7 @@ export default {
stepSave,
stepDelete,
rules,
blankFn,
getScopeFunc,
stepCopy,
fullscreen
};

View File

@@ -56,7 +56,7 @@
<a key="edit" @click="stepEdit(currentTask, element, index)">编辑</a>
<a key="edit" @click="stepCopy(currentTask, element, index)">复制</a>
<a key="remove" @click="stepDelete(currentTask, index)">删除</a>
<a key="disabled" @click="element.disabled = !!!element.disabled">{{ element.disabled ? "启用" : "禁用" }}</a>
<a key="disabled" @click="toggleDisabled(currentTask, element)">{{ element.disabled ? "启用" : "禁用" }}</a>
<fs-icon v-plus class="icon-button handle cursor-move" title="拖动排序" icon="ion:move-outline"></fs-icon>
</div>
</div>
@@ -88,6 +88,7 @@ import { CopyOutlined } from "@ant-design/icons-vue";
import VDraggable from "vuedraggable";
import { useUserStore } from "/@/store/modules/user";
import { useSettingStore } from "/@/store/modules/settings";
import { filter } from "lodash-es";
export default {
name: "PiTaskForm",
components: { CopyOutlined, PiStepForm, VDraggable },
@@ -152,7 +153,11 @@ export default {
});
};
return { stepAdd, stepEdit, stepCopy, stepDelete, stepFormRef };
const toggleDisabled = (task: any, step: any) => {
step.disabled = !!!step.disabled;
};
return { stepAdd, stepEdit, stepCopy, stepDelete, toggleDisabled, stepFormRef };
}
/**
@@ -162,7 +167,7 @@ export default {
function useTaskForm() {
const mode = ref("add");
const callback = ref();
const currentTask = ref({ title: undefined, steps: [] });
const currentTask = ref({ title: undefined, steps: [], disabled: false });
provide("currentTask", currentTask);
const taskFormRef: Ref<any> = ref(null);
const taskDrawerVisible = ref(false);
@@ -219,6 +224,15 @@ export default {
console.error("表单验证失败:", e);
return;
}
const task: any = currentTask.value;
const allDisabled = filter(task.steps, (item: any) => {
return item.disabled;
});
if (task.steps.length > 0 && task.steps.length === allDisabled.length) {
task.disabled = true;
} else {
task.disabled = false;
}
callback.value("save", currentTask.value);
taskDrawerClose();

View File

@@ -109,7 +109,7 @@
</div>
</template>
<span class="flex-o w-100">
<span class="ellipsis flex-1 task-title" :class="{ 'in-edit': editMode }">{{ task.title }}</span>
<span class="ellipsis flex-1 task-title" :class="{ 'in-edit': editMode, deleted: task.disabled }">{{ task.title }}</span>
<pi-status-show :status="task.status?.result"></pi-status-show>
</span>
</a-popover>
@@ -181,7 +181,7 @@
<div class="task">
<a-button shape="round" @click="notificationEdit(item, ii as number)">
<fs-icon icon="ion:notifications"></fs-icon>
通知 {{ item.type }}
通知 {{ item.title || item.type }}
</a-button>
</div>
</div>
@@ -199,8 +199,7 @@
<div class="task">
<a-button shape="round" @click="notificationEdit(item, index)">
<fs-icon icon="ion:notifications"></fs-icon>
通知 {{ item.type }}
通知 {{ item.title || item.type }}
</a-button>
</div>
</div>
@@ -883,6 +882,8 @@ export default defineComponent({
&.in-edit {
margin-right: 28px;
}
&.disabled {
}
}
.action {

View File

@@ -75,7 +75,8 @@ export type PluginConfigBean = {
};
export type CertApplyPluginSysInput = {
googleCommonEabAccessId: number;
googleCommonEabAccessId?: number;
zerosslCommonEabAccessId?: number;
};
export type PluginSysSetting<T> = {
sysSetting: {

View File

@@ -1,22 +1,9 @@
// @ts-ignore
import { request } from "/@/api/service";
import { SysPrivateSetting, SysPublicSetting } from "/@/api/modules/api.basic";
const apiPrefix = "/sys/settings";
export type SysSettings = { public: SysPublicSetting; private: SysPrivateSetting };
export type SysPublicSetting = {
registerEnabled?: boolean;
limitUserPipelineCount?: number;
managerOtherUserPipeline?: boolean;
icpNo?: string;
};
export type SysPrivateSetting = {
httpProxy?: string;
httpsProxy?: string;
dnsResultOrder?: string;
commonCnameEnabled?: boolean;
};
export const SettingKeys = {
SysPublic: "sys.public",
SysPrivate: "sys.private",

View File

@@ -52,6 +52,9 @@
<a-form-item label="启用公共CNAME服务" :name="['private', 'commonCnameEnabled']">
<a-switch v-model:checked="formState.private.commonCnameEnabled" />
<div class="helper">
是否可以使用公共CNAME服务如果禁用且没有设置<router-link to="/sys/cname/provider">自定义CNAME服务</router-link>则无法使用CNAME代理方式申请证书
</div>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
@@ -70,7 +73,7 @@ import { notification } from "ant-design-vue";
import { useSettingStore } from "/@/store/modules/settings";
import { merge } from "lodash-es";
import { util } from "/@/utils";
import NotificationSelector from "/src/views/certd/notification/notification-selector/index.vue";
defineOptions({
name: "SysSettings"
});

View File

@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.8](https://github.com/certd/certd/compare/v1.27.7...v1.27.8) (2024-11-25)
**Note:** Version bump only for package @certd/ui-server
## [1.27.7](https://github.com/certd/certd/compare/v1.27.6...v1.27.7) (2024-11-25)
### Bug Fixes
* 修复关键字查询bug ([fab6660](https://github.com/certd/certd/commit/fab66606b35a540fac31fee902331ba1ffdebc16))
* 修复CNAME时子域名级数超出限制的问题 ([3af6d96](https://github.com/certd/certd/commit/3af6d96e6e353c9b2111cff81679b79c55195a0a))
### Performance Improvements
* 华为云密钥获取提示及访问链接 ([de43391](https://github.com/certd/certd/commit/de43391e4c12dc3ad976f8fa8787f4eb70a41e75))
* 通知管理 ([d9a00ee](https://github.com/certd/certd/commit/d9a00eeaf72735ced67c59d7983d84e3c730064a))
* 通知渠道支持测试按钮 ([b54ae27](https://github.com/certd/certd/commit/b54ae272ebc2d31b32b049d44e2299a6be7f153c))
* 优化插件开发dnsProvider无需写http logger 变量 ([fcbb5e4](https://github.com/certd/certd/commit/fcbb5e46a112174150a62648319b8224fce3b7ed))
* 支持部署到阿里云WAF ([c96fcb7](https://github.com/certd/certd/commit/c96fcb7afced979435cffa73591275008033c90d))
* 支持企业微信群聊机器人通知 ([b805a29](https://github.com/certd/certd/commit/b805a2925984144a31575b8aaa622f0c30d41b56))
## [1.27.6](https://github.com/certd/certd/compare/v1.27.5...v1.27.6) (2024-11-19)
### Bug Fixes
* .env 读取 \r 问题 ([0e33dfa](https://github.com/certd/certd/commit/0e33dfa019a55ea76193c428ec756af386adeb9d))
## [1.27.5](https://github.com/certd/certd/compare/v1.27.4...v1.27.5) (2024-11-18)
### Bug Fixes

View File

@@ -0,0 +1,2 @@
CREATE TABLE "pi_notification" ("id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, "user_id" bigint NOT NULL, "name" varchar(100) NOT NULL, "type" varchar(100) NOT NULL, "setting" text, "create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP));

View File

@@ -0,0 +1,2 @@
CREATE TABLE "pi_notification" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer NOT NULL, "name" varchar(100) NOT NULL, "type" varchar(100) NOT NULL, "setting" text, "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP));

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.27.5",
"version": "1.27.8",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -29,17 +29,17 @@
},
"dependencies": {
"@alicloud/pop-core": "^1.7.10",
"@certd/acme-client": "^1.27.5",
"@certd/basic": "^1.27.5",
"@certd/commercial-core": "^1.27.5",
"@certd/lib-huawei": "^1.27.5",
"@certd/lib-k8s": "^1.27.5",
"@certd/lib-server": "^1.27.5",
"@certd/midway-flyway-js": "^1.27.5",
"@certd/pipeline": "^1.27.5",
"@certd/plugin-cert": "^1.27.5",
"@certd/plugin-plus": "^1.27.5",
"@certd/plus-core": "^1.27.5",
"@certd/acme-client": "^1.27.8",
"@certd/basic": "^1.27.8",
"@certd/commercial-core": "^1.27.8",
"@certd/lib-huawei": "^1.27.8",
"@certd/lib-k8s": "^1.27.8",
"@certd/lib-server": "^1.27.8",
"@certd/midway-flyway-js": "^1.27.8",
"@certd/pipeline": "^1.27.8",
"@certd/plugin-cert": "^1.27.8",
"@certd/plugin-plus": "^1.27.8",
"@certd/plus-core": "^1.27.8",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
"@koa/cors": "^5.0.0",

View File

@@ -50,7 +50,7 @@ export function mergeConfig(config: any, envType: string) {
export function loadDotEnv() {
const envStr = fs.readFileSync('.env').toString();
envStr.split('\n').forEach(line => {
const [key, value] = line.split('=');
const [key, value] = line.trim().split('=');
const oldValue = process.env[key];
if (!oldValue) {
process.env[key] = value;

View File

@@ -1,6 +1,6 @@
import { Config, Controller, Get, Inject, Provide } from '@midwayjs/core';
import { BaseController, Constants, SysHeaderMenus, SysInstallInfo, SysPublicSettings, SysSettingsService, SysSiteEnv, SysSiteInfo } from '@certd/lib-server';
import { AppKey, getPlusInfo } from '@certd/plus-core';
import { AppKey, getPlusInfo, isComm } from '@certd/plus-core';
/**
*/
@@ -49,7 +49,10 @@ export class BasicSettingsController extends BaseController {
async getAllSettings() {
const sysPublic = await this.getSysPublic();
const installInfo = await this.getInstallInfo();
const siteInfo = await this.getSiteInfo();
let siteInfo = {};
if (isComm()) {
siteInfo = await this.getSiteInfo();
}
const siteEnv = await this.getSiteEnv();
const plusInfo = await this.plusInfo();
const headerMenus = await this.getHeaderMenus();

View File

@@ -1,6 +1,7 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { Constants, CrudController } from '@certd/lib-server';
import { AccessService } from '../../modules/pipeline/service/access-service.js';
import { AuthService } from '../../modules/sys/authority/service/auth-service.js';
/**
* 授权
@@ -10,6 +11,8 @@ import { AccessService } from '../../modules/pipeline/service/access-service.js'
export class AccessController extends CrudController<AccessService> {
@Inject()
service: AccessService;
@Inject()
authService: AuthService;
getService(): AccessService {
return this.service;
@@ -84,4 +87,11 @@ export class AccessController extends CrudController<AccessService> {
}
return this.ok(dict);
}
@Post('/simpleInfo', { summary: Constants.per.authOnly })
async simpleInfo(@Query('id') id: number) {
await this.authService.checkEntityUserId(this.ctx, this.service, id);
const res = await this.service.getSimpleInfo(id);
return this.ok(res);
}
}

View File

@@ -1,11 +1,20 @@
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { Constants } from '@certd/lib-server';
import { AccessRequestHandleReq, ITaskPlugin, newAccess, pluginRegistry, PluginRequestHandleReq, TaskInstanceContext } from '@certd/pipeline';
import { BaseController } from '@certd/lib-server';
import { BaseController, Constants } from '@certd/lib-server';
import {
AccessRequestHandleReq,
ITaskPlugin,
newAccess,
newNotification,
NotificationRequestHandleReq,
pluginRegistry,
PluginRequestHandleReq,
TaskInstanceContext,
} from '@certd/pipeline';
import { AccessService } from '../../modules/pipeline/service/access-service.js';
import { EmailService } from '../../modules/basic/service/email-service.js';
import { AccessGetter } from '../../modules/pipeline/service/access-getter.js';
import { http, HttpRequestConfig, logger, mergeUtils, utils } from '@certd/basic';
import { NotificationService } from '../../modules/pipeline/service/notification-service.js';
@Provide()
@Controller('/api/pi/handle')
@@ -16,12 +25,18 @@ export class HandleController extends BaseController {
@Inject()
emailService: EmailService;
@Inject()
notificationService: NotificationService;
@Post('/access', { summary: Constants.per.authOnly })
async accessRequest(@Body(ALL) body: AccessRequestHandleReq) {
let inputAccess = body.input.access;
if (body.input.id > 0) {
const oldEntity = await this.accessService.info(body.input.id);
if (oldEntity) {
if (oldEntity.userId !== this.getUserId()) {
throw new Error('access not found');
}
const param: any = {
type: body.typeName,
setting: JSON.stringify(body.input.access),
@@ -38,6 +53,34 @@ export class HandleController extends BaseController {
return this.ok(res);
}
@Post('/notification', { summary: Constants.per.authOnly })
async notificationRequest(@Body(ALL) body: NotificationRequestHandleReq) {
const input = body.input.body;
// if (body.input.id > 0) {
// const oldEntity = await this.notificationService.info(body.input.id);
// if (oldEntity) {
// if (oldEntity.userId !== this.getUserId()) {
// throw new Error('notification not found');
// }
// const param: any = {
// type: body.typeName,
// setting: JSON.stringify(body.input.access),
// };
// }
// }
const notification = newNotification(body.typeName, input, {
http,
logger,
utils,
emailService: this.emailService,
});
const res = await notification.onRequest(body);
return this.ok(res);
}
@Post('/plugin', { summary: Constants.per.authOnly })
async pluginRequest(@Body(ALL) body: PluginRequestHandleReq) {
const userId = this.getUserId();

View File

@@ -0,0 +1,91 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { Constants, CrudController } from '@certd/lib-server';
import { NotificationService } from '../../modules/pipeline/service/notification-service.js';
import { AuthService } from '../../modules/sys/authority/service/auth-service.js';
/**
* 通知
*/
@Provide()
@Controller('/api/pi/notification')
export class NotificationController extends CrudController<NotificationService> {
@Inject()
service: NotificationService;
@Inject()
authService: AuthService;
getService(): NotificationService {
return this.service;
}
@Post('/page', { summary: Constants.per.authOnly })
async page(@Body(ALL) body) {
body.query = body.query ?? {};
delete body.query.userId;
const buildQuery = qb => {
qb.andWhere('user_id = :userId', { userId: this.getUserId() });
};
const res = await this.service.page({
query: body.query,
page: body.page,
sort: body.sort,
buildQuery,
});
return this.ok(res);
}
@Post('/list', { summary: Constants.per.authOnly })
async list(@Body(ALL) body) {
body.userId = this.getUserId();
return super.list(body);
}
@Post('/add', { summary: Constants.per.authOnly })
async add(@Body(ALL) bean) {
bean.userId = this.getUserId();
return super.add(bean);
}
@Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) {
await this.service.checkUserId(bean.id, this.getUserId());
return super.update(bean);
}
@Post('/info', { summary: Constants.per.authOnly })
async info(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
return super.info(id);
}
@Post('/delete', { summary: Constants.per.authOnly })
async delete(@Query('id') id: number) {
await this.service.checkUserId(id, this.getUserId());
return super.delete(id);
}
@Post('/define', { summary: Constants.per.authOnly })
async define(@Query('type') type: string) {
const notification = this.service.getDefineByType(type);
return this.ok(notification);
}
@Post('/getTypeDict', { summary: Constants.per.authOnly })
async getTypeDict() {
const list = this.service.getDefineList();
const dict = [];
for (const item of list) {
dict.push({
value: item.name,
label: item.title,
});
}
return this.ok(dict);
}
@Post('/simpleInfo', { summary: Constants.per.authOnly })
async simpleInfo(@Query('id') id: number) {
await this.authService.checkEntityUserId(this.ctx, this.service, id);
const res = await this.service.getSimpleInfo(id);
return this.ok(res);
}
}

View File

@@ -37,7 +37,7 @@ export class PipelineController extends CrudController<PipelineService> {
const buildQuery = qb => {
if (title) {
qb.andWhere('title like :title', { title: `%${title}%` }).orWhere('content like :content', { content: `%${title}%` });
qb.andWhere('(title like :title or content like :content)', { title: `%${title}%`, content: `%${title}%` });
}
};
if (!body.sort || !body.sort?.prop) {

View File

@@ -59,4 +59,9 @@ export class SysAccessController extends AccessController {
async getAccessTypeDict() {
return await super.getAccessTypeDict();
}
@Post('/simpleInfo', { summary: 'sys:settings:view' })
async simpleInfo(@Query('id') id: number) {
return await super.simpleInfo(id);
}
}

View File

@@ -57,6 +57,10 @@ export class EmailService implements IEmailService {
async send(email: EmailSend) {
logger.info('sendEmail', email);
if (!email.receivers || email.receivers.length === 0) {
throw new Error('收件人不能为空');
}
const emailConf = await getEmailSettings(this.sysSettingsService, this.settingsService);
if (!emailConf.host && emailConf.usePlus == null) {
@@ -64,7 +68,7 @@ export class EmailService implements IEmailService {
//自动使用plus发邮件
return await this.sendByPlus(email);
}
throw new Error('email settings 还未设置');
throw new Error('邮件服务器还未设置');
}
if (emailConf.usePlus && isPlus()) {

View File

@@ -92,7 +92,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
param.hostRecord = hostRecord;
const cnameKey = utils.id.simpleNanoId();
param.recordValue = `${param.domain}.${cnameKey}.${cnameProvider.domain}`;
const safeDomain = param.domain.replaceAll('.', '-');
param.recordValue = `${safeDomain}-${cnameKey}.${cnameProvider.domain}`;
}
async update(param: any) {

View File

@@ -0,0 +1,32 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('pi_notification')
export class NotificationEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'user_id', comment: 'UserId' })
userId: number;
@Column({ name: 'type', comment: '通知类型' })
type: string;
@Column({ name: 'name', comment: '名称' })
name: string;
@Column({ name: 'setting', comment: '通知配置', length: 10240 })
setting: string;
@Column({
name: 'create_time',
comment: '创建时间',
default: () => 'CURRENT_TIMESTAMP',
})
createTime: Date;
@Column({
name: 'update_time',
comment: '修改时间',
default: () => 'CURRENT_TIMESTAMP',
})
updateTime: Date;
}

View File

@@ -107,6 +107,18 @@ export class AccessService extends BaseService<AccessEntity> {
return await super.update(param);
}
async getSimpleInfo(id: number) {
const entity = await this.info(id);
if (entity == null) {
throw new ValidateException('该授权配置不存在,请确认是否已被删除');
}
return {
id: entity.id,
name: entity.name,
userId: entity.userId,
};
}
async getAccessById(id: any, checkUserId: boolean, userId?: number): Promise<any> {
const entity = await this.info(id);
if (entity == null) {

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