Compare commits

..

28 Commits

Author SHA1 Message Date
xiaojunnuo
c8df9e698c v1.28.3 2024-12-12 12:06:46 +08:00
xiaojunnuo
19b78a1d2f build: prepare to build 2024-12-12 12:05:16 +08:00
xiaojunnuo
8039e8baf8 perf: 支持腾讯虚拟机开关机(@wujingke) 2024-12-12 11:50:01 +08:00
xiaojunnuo
9c5142c73c chore: 2024-12-12 11:42:46 +08:00
xiaojunnuo
8e3dcdde17 chore: tke挪出来 2024-12-11 22:17:11 +08:00
xiaojunnuo
34023adafb chore: 2024-12-11 17:40:34 +08:00
xiaojunnuo
79914e8d08 chore: 2024-12-11 15:06:02 +08:00
xiaojunnuo
454fbda581 perf: 点击版本红点按钮,跳转到升级帮助页面 2024-12-11 13:59:00 +08:00
xiaojunnuo
2c32703e6b chore: 2024-12-11 12:01:06 +08:00
xiaojunnuo
b561535626 Merge branch 'v2' into v2-dev 2024-12-11 11:48:34 +08:00
xiaojunnuo
1fc684d995 chore: 2024-12-11 11:48:05 +08:00
greper
7595d9fdfd pref: 腾讯云实例开机插件( @wujingke )
pr:  #265
2024-12-11 11:44:53 +08:00
w
3bf7732a21 腾讯云实例开机插件 2024-12-11 11:40:11 +08:00
xiaojunnuo
71b5aaf8ab chore: 2024-12-11 11:38:28 +08:00
xiaojunnuo
e1e5347476 chore: 2024-12-11 11:37:52 +08:00
xiaojunnuo
cdcdb6a2d9 chore: 2024-12-11 11:36:00 +08:00
xiaojunnuo
ec79104ad2 chore: 2024-12-11 11:33:33 +08:00
xiaojunnuo
ff083ce684 perf: 通知标题优化 2024-12-11 11:30:32 +08:00
xiaojunnuo
0f051e322e docs: upgrade 2024-12-11 10:25:16 +08:00
xiaojunnuo
657a2ae032 fix: 修复没有配置eab时,报order无法读取的问题 2024-12-11 09:30:21 +08:00
xiaojunnuo
0db3570026 chore: 2024-12-10 18:30:32 +08:00
xiaojunnuo
0ae39f160a perf: 支持aws cloudfront 2024-12-10 18:28:48 +08:00
xiaojunnuo
b45977c29a fix: 修复授权被删除后,无法清空的bug 2024-12-10 17:22:43 +08:00
xiaojunnuo
b7f5740c57 fix: mysql下access.setting字段改成text 2024-12-10 00:19:35 +08:00
xiaojunnuo
21e23369d3 chore: 2024-12-09 23:08:40 +08:00
xiaojunnuo
fca598991a chore: 2024-12-09 22:56:18 +08:00
xiaojunnuo
aa5b909486 build: publish 2024-12-09 22:53:08 +08:00
xiaojunnuo
0a888cf51a build: trigger build image 2024-12-09 22:52:44 +08:00
66 changed files with 1151 additions and 179 deletions

View File

@@ -3,6 +3,21 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
### Bug Fixes
* 修复没有配置eab时报order无法读取的问题 ([657a2ae](https://github.com/certd/certd/commit/657a2ae032e6f61ac27fbdd26c7bf169c041219e))
* 修复授权被删除后无法清空的bug ([b45977c](https://github.com/certd/certd/commit/b45977c29a29084c11e496bec3415eaaebafdd74))
* mysql下access.setting字段改成text ([b7f5740](https://github.com/certd/certd/commit/b7f5740c57743914f754f3b4fdd94b59a2e8338c))
### Performance Improvements
* 点击版本红点按钮,跳转到升级帮助页面 ([454fbda](https://github.com/certd/certd/commit/454fbda581bbe22abca5b91e5086ea9d9d58a020))
* 通知标题优化 ([ff083ce](https://github.com/certd/certd/commit/ff083ce6848a8bee3c8248e4b881086ae1517c28))
* 支持腾讯虚拟机开关机([@wujingke](https://github.com/wujingke)) ([8039e8b](https://github.com/certd/certd/commit/8039e8baf83c82d03f1a6198cf61c372026b962b))
* 支持aws cloudfront ([0ae39f1](https://github.com/certd/certd/commit/0ae39f160a7c6b6696b3bf513d68aa28905810ad))
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
### Bug Fixes

View File

@@ -9,11 +9,11 @@ Certd 是一个免费全自动申请和自动部署更新SSL证书的管理系
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
* 全自动申请证书(支持所有注册商注册的域名)
* 全自动部署更新证书(目前支持部署到主机、部署到阿里云、腾讯云等,目前已支持30+部署插件)
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等,目前已支持40+部署插件)
* 支持通配符域名/泛域名支持多个域名打到一个证书上支持pem、pfx、der、jks等多种证书格式
* 邮件通知
* 邮件通知、webhook通知
* 私有化部署数据保存本地镜像由Github Actions构建过程公开透明
* 支持sqlitepostgresql数据库
* 支持SQLitePostgreSQL、MySQL数据库

View File

@@ -1 +1 @@
01:55
22:52

View File

@@ -76,8 +76,8 @@ export default defineConfig({
{ text: "源码部署", link: "/guide/install/source/" }
]
},
{ text: "演示教程", link: "/guide/tutorial.md" }
{ text: "演示教程", link: "/guide/tutorial.md" },
{ text: "版本升级", link: "/guide/install/upgrade.md" }
]
},
{

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.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
### Bug Fixes
* 修复创建流水线通知设置无效的bug ([498cf34](https://github.com/certd/certd/commit/498cf34999fddfa24ce088e2e678469fa669abb8))
* 修复流水线分组可以被所有人看见的bug ([a0e838d](https://github.com/certd/certd/commit/a0e838d1eec918e5dc92fe95dc72ac14facb930e))
### Performance Improvements
* 优化数据表索引 ([228fdf0](https://github.com/certd/certd/commit/228fdf0a0d28013f5dd156a97bbde80537e8e97e))
* 支持mysql ([7cde1fd](https://github.com/certd/certd/commit/7cde1fdc4a9ed851900d231a5460c8dbfbcd148e))
## [1.28.1](https://github.com/certd/certd/compare/v1.28.0...v1.28.1) (2024-12-08)
### Bug Fixes

View File

@@ -106,4 +106,4 @@ throw new Error("错误信息")
## 五、贡献插件送激活码
- PR要求插件功能完整代码规范
- PR通过后联系我们送您一个专业版激活码
- PR通过后联系我们送您一个半年期专业版激活码

View File

@@ -14,7 +14,7 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
* 支持通配符域名/泛域名,支持多个域名打到一个证书上
* 邮件通知
* 私有化部署,保障数据安全
* 支持sqlitepostgresql数据库
* 支持SQLite、Postgresql、MySQL数据库
## 二、一些说明

View File

@@ -8,7 +8,7 @@
```shell
# 克隆代码
git clone https://github.com/certd/certd
# git checkout v1.x.x # 1.x.x换成最新版本号当v2主干分支代码无法正常启动时可以尝试此命令
# git checkout v1.x.x # 当v2主干分支代码无法正常启动时可以尝试此命令1.x.x换成最新版本号
cd certd
# 启动服务
./start.sh
@@ -29,9 +29,15 @@ https://your_server_ip:7002
## 二、升级
```shell
# 更新代码并启动
cd certd
# 确保数据安全,备份一下数据
cp -rf ./packages/ui/certd-server/data ../certd-data-backup
git pull
# 如果提示pull失败可以尝试强制更新
# git checkout v2 -f && git pull
# 先停止旧的服务,7001是certd的默认端口
kill -9 $(lsof -t -i:7001)
# 重新编译启动

View File

@@ -0,0 +1,12 @@
# 版本升级
## 升级方法
根据不同部署方式查看升级方法
1. [Docker方式部署升级](./install/docker/#二、升级)
2. [宝塔面板方式部署升级](./install/baota/#三、如何升级)
3. [1Panel面板方式部署升级](./install/1panel/#三、升级)
4. [源码方式部署](./install/source/#二、升级)
## 升级日志
[CHANGELOG](../changelogs/CHANGELOG.md)

View File

@@ -32,5 +32,5 @@ features:
- title: 支持私有化部署
details: 保障数据安全
- title: 多数据库支持
details: 支持sqlitepostgresql数据库
details: 支持SQLite、Postgresql、MySQL数据库
---

View File

@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.28.2"
"version": "1.28.3"
}

View File

@@ -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.28.3](https://github.com/publishlab/node-acme-client/compare/v1.28.2...v1.28.3) (2024-12-12)
**Note:** Version bump only for package @certd/acme-client
## [1.28.2](https://github.com/publishlab/node-acme-client/compare/v1.28.1...v1.28.2) (2024-12-09)
### Performance Improvements

View File

@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.28.2",
"version": "1.28.3",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.28.2",
"@certd/basic": "^1.28.3",
"@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": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -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.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
**Note:** Version bump only for package @certd/basic
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/basic

View File

@@ -1 +1 @@
22:42
12:05

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.28.2",
"version": "1.28.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -43,5 +43,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
### Performance Improvements
* 通知标题优化 ([ff083ce](https://github.com/certd/certd/commit/ff083ce6848a8bee3c8248e4b881086ae1517c28))
* 支持aws cloudfront ([0ae39f1](https://github.com/certd/certd/commit/0ae39f160a7c6b6696b3bf513d68aa28905810ad))
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/pipeline

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.28.2",
"version": "1.28.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -16,8 +16,8 @@
"test": "mocha --loader=ts-node/esm"
},
"dependencies": {
"@certd/basic": "^1.28.2",
"@certd/plus-core": "^1.28.2",
"@certd/basic": "^1.28.3",
"@certd/plus-core": "^1.28.3",
"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": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -10,7 +10,8 @@ import { Decorator } from "../decorator/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";
import { INotificationService } from "../notification/index.js";
export type SysInfo = {
//系统标题
title?: string;
@@ -373,18 +374,17 @@ export class Executor {
let subject = "";
let content = "";
const errorMessage = error?.message;
const sysTitle = this.options.sysInfo?.title || "Certd";
if (when === "start") {
subject = `${sysTitle}开始执行,${this.pipeline.title}${this.pipeline.id}`;
subject = `开始执行,${this.pipeline.title}${this.pipeline.id}`;
content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`;
} else if (when === "success") {
subject = `${sysTitle}执行成功,${this.pipeline.title}${this.pipeline.id}`;
subject = `执行成功,${this.pipeline.title}${this.pipeline.id}`;
content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`;
} else if (when === "turnToSuccess") {
subject = `${sysTitle}执行成功(失败转成功),${this.pipeline.title}${this.pipeline.id}`;
subject = `执行成功(失败转成功),${this.pipeline.title}${this.pipeline.id}`;
content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}`;
} else if (when === "error") {
subject = `${sysTitle}执行失败,${this.pipeline.title}${this.pipeline.id}`;
subject = `执行失败,${this.pipeline.title}${this.pipeline.id}`;
content = `流水线ID:${this.pipeline.id}运行ID:${this.runtime.id}\n错误详情:${error.message}`;
} else {
return;
@@ -407,43 +407,24 @@ export class Executor {
}
} else {
try {
//构建notification插件发送通知
let notifyConfig: any;
if (notification.notificationId === 0) {
notifyConfig = await this.options.notificationService.getDefault();
} else {
notifyConfig = await this.options.notificationService.getById(notification.notificationId);
}
if (notifyConfig == null) {
throw new Error(`通知配置<id:${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);
await this.options.notificationService.send({
id: notification.notificationId,
useDefault: true,
useEmail: false,
logger: this.logger,
body: {
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,
},
});
} catch (e) {
logger.error("send notification error", e);
}

View File

@@ -48,9 +48,18 @@ export type NotificationInstanceConfig = {
};
};
export type NotificationSendReq = {
id?: number;
useDefault?: boolean;
useEmail?: boolean;
emailAddress?: string;
logger?: ILogger;
body: NotificationBody;
};
export interface INotificationService {
getById(id: number): Promise<NotificationInstanceConfig>;
getDefault(): Promise<NotificationInstanceConfig>;
send(req: NotificationSendReq): Promise<void>;
}
export interface INotification {

View File

@@ -1,9 +1,9 @@
// src/decorator/memoryCache.decorator.ts
import { Decorator } from "../decorator/index.js";
import * as _ from "lodash-es";
import { merge } from "lodash-es";
import { notificationRegistry } from "./registry.js";
import { BaseNotification, NotificationBody, NotificationContext, NotificationDefine, NotificationInputDefine, NotificationInstanceConfig } from "./api.js";
import { isPlus } from "@certd/plus-core";
// 提供一个唯一 key
export const NOTIFICATION_CLASS_KEY = "pipeline:notification";
@@ -47,9 +47,7 @@ export async function newNotification(type: string, input: any, ctx: Notificatio
// @ts-ignore
const plugin = new register.target();
for (const key in input) {
plugin[key] = input[key];
}
merge(plugin, input);
if (!ctx) {
throw new Error("ctx is required");
}
@@ -61,8 +59,5 @@ export async function newNotification(type: string, input: any, ctx: Notificatio
export async function sendNotification(opts: { config: NotificationInstanceConfig; ctx: NotificationContext; body: NotificationBody }) {
const notification: BaseNotification = await newNotification(opts.config.type, opts.config.setting, opts.ctx);
if (notification.define.needPlus && !isPlus()) {
opts.body.content = `${opts.body.content}\n\n注意此通知渠道已调整为专业版功能后续版本将不再支持发送请尽快修改或升级为专业版`;
}
await notification.doSend(opts.body);
}

View File

@@ -21,8 +21,9 @@ export const pluginGroups = {
huawei: new PluginGroup("huawei", "华为云", 3),
tencent: new PluginGroup("tencent", "腾讯云", 4),
qiniu: new PluginGroup("qiniu", "七牛云", 5),
host: new PluginGroup("host", "主机", 6),
cdn: new PluginGroup("cdn", "CDN", 7),
panel: new PluginGroup("panel", "面板", 8),
other: new PluginGroup("other", "其他", 9),
aws: new PluginGroup("aws", "亚马逊云", 6),
host: new PluginGroup("host", "主机", 7),
cdn: new PluginGroup("cdn", "CDN", 8),
panel: new PluginGroup("panel", "面板", 9),
other: new PluginGroup("other", "其他", 10),
};

View File

@@ -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.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
**Note:** Version bump only for package @certd/lib-huawei
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/lib-huawei

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.28.2",
"version": "1.28.3",
"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": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -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.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
**Note:** Version bump only for package @certd/lib-iframe
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/lib-iframe

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.28.2",
"version": "1.28.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -30,5 +30,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -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.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
**Note:** Version bump only for package @certd/lib-k8s
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/lib-k8s

View File

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

View File

@@ -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.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
**Note:** Version bump only for package @certd/lib-server
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/lib-server

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.28.2",
"version": "1.28.3",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -27,10 +27,10 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.28.2",
"@certd/basic": "^1.28.2",
"@certd/pipeline": "^1.28.2",
"@certd/plus-core": "^1.28.2",
"@certd/acme-client": "^1.28.3",
"@certd/basic": "^1.28.3",
"@certd/pipeline": "^1.28.3",
"@certd/plus-core": "^1.28.3",
"@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": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -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.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/midway-flyway-js

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.28.2",
"version": "1.28.3",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
### Bug Fixes
* 修复没有配置eab时报order无法读取的问题 ([657a2ae](https://github.com/certd/certd/commit/657a2ae032e6f61ac27fbdd26c7bf169c041219e))
### Performance Improvements
* 通知标题优化 ([ff083ce](https://github.com/certd/certd/commit/ff083ce6848a8bee3c8248e4b881086ae1517c28))
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/plugin-cert

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.28.2",
"version": "1.28.3",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -15,9 +15,9 @@
"preview": "vite preview"
},
"dependencies": {
"@certd/acme-client": "^1.28.2",
"@certd/basic": "^1.28.2",
"@certd/pipeline": "^1.28.2",
"@certd/acme-client": "^1.28.3",
"@certd/basic": "^1.28.3",
"@certd/pipeline": "^1.28.3",
"@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": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -1,4 +1,4 @@
import { AbstractTaskPlugin, IContext, NotificationBody, sendNotification, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import { AbstractTaskPlugin, IContext, NotificationBody, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import dayjs from "dayjs";
import type { CertInfo } from "./acme.js";
import { CertReader } from "./cert-reader.js";
@@ -295,7 +295,6 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
* 检查是否过期默认提前35天
* @param expires
* @param maxDays
* @returns {boolean}
*/
isWillExpire(expires: number, maxDays = 20) {
if (expires == null) {
@@ -312,39 +311,19 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
this.logger.info("发送证书申请成功通知");
const url = await this.ctx.urlService.getPipelineDetailUrl(this.pipeline.id, this.ctx.runtime.id);
const body: NotificationBody = {
title: `【Certd】证书申请成功【${this.pipeline.title}`,
title: `证书申请成功【${this.pipeline.title}`,
content: `域名:${this.domains.join(",")}`,
url: url,
};
try {
const defNotification = await this.ctx.notificationService.getDefault();
if (defNotification) {
this.logger.info(`通知渠道:${defNotification.name}`);
const notificationCtx = {
http: this.ctx.http,
logger: this.logger,
utils: this.ctx.utils,
emailService: this.ctx.emailService,
};
await sendNotification({
config: defNotification,
ctx: notificationCtx,
body,
});
return;
}
this.logger.warn("未配置默认通知,将发送邮件通知");
await this.sendSuccessEmail(body);
await this.ctx.notificationService.send({
useDefault: true,
useEmail: true,
emailAddress: this.email,
body,
});
} catch (e) {
this.logger.error("证书申请成功通知发送失败", e);
}
}
async sendSuccessEmail(body: NotificationBody) {
this.logger.info("发送邮件通知:" + this.email);
await this.ctx.emailService.send({
receivers: [this.email],
subject: body.title,
content: body.content,
});
}
}

View File

@@ -271,8 +271,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
this.logger.info("当前正在使用 google公共EAB授权");
eab = await this.ctx.accessService.getCommonById(this.googleCommonEabAccessId);
} else {
this.logger.error("google需要配置EAB授权或服务账号授权");
return;
throw new Error("google需要配置EAB授权或服务账号授权");
}
} else if (this.sslProvider === "zerossl") {
if (this.eabAccessId) {
@@ -282,8 +281,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
this.logger.info("当前正在使用 zerossl 公共EAB授权");
eab = await this.ctx.accessService.getCommonById(this.zerosslCommonEabAccessId);
} else {
this.logger.error("zerossl需要配置EAB授权");
return;
throw new Error("zerossl需要配置EAB授权");
}
}
this.eab = eab;

View File

@@ -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.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
**Note:** Version bump only for package @certd/plugin-lib
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
**Note:** Version bump only for package @certd/plugin-lib

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.28.2",
"version": "1.28.3",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -16,9 +16,9 @@
},
"dependencies": {
"@alicloud/pop-core": "^1.7.10",
"@certd/basic": "^1.28.2",
"@certd/pipeline": "^1.28.2",
"@certd/plugin-cert": "^1.28.2",
"@certd/basic": "^1.28.3",
"@certd/pipeline": "^1.28.3",
"@certd/plugin-cert": "^1.28.3",
"@kubernetes/client-node": "0.21.0",
"dayjs": "^1.11.7",
"iconv-lite": "^0.6.3",
@@ -44,5 +44,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a6cd532035f55a7ce122ea1229bb65f9d41e7125"
"gitHead": "e5c164065cef75a6813a4ff48b2167a966dce112"
}

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
### Bug Fixes
* 修复授权被删除后无法清空的bug ([b45977c](https://github.com/certd/certd/commit/b45977c29a29084c11e496bec3415eaaebafdd74))
### Performance Improvements
* 点击版本红点按钮,跳转到升级帮助页面 ([454fbda](https://github.com/certd/certd/commit/454fbda581bbe22abca5b91e5086ea9d9d58a020))
* 通知标题优化 ([ff083ce](https://github.com/certd/certd/commit/ff083ce6848a8bee3c8248e4b881086ae1517c28))
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
### Bug Fixes

View File

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

View File

@@ -1,7 +1,7 @@
<template>
<div class="access-selector">
<span v-if="target?.name" class="mr-5 cd-flex-inline">
<a-tag class="mr-5" color="green">{{ target.name }}</a-tag>
<span v-if="modelValue" class="mr-5 cd-flex-inline">
<a-tag class="mr-5" color="green">{{ target.name || modelValue }}</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>

View File

@@ -126,6 +126,7 @@ function clear() {
}
async function emitValue(value: any) {
target.value = optionsDictRef.dataMap[value];
if (value !== 0 && pipeline?.value && target && pipeline.value.userId !== target.value.userId) {
message.error("对不起,您不能修改他人流水线的通知");
return;

View File

@@ -182,7 +182,8 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
const downloadUrl = `${env.API}/pi/history/download?pipelineId=${row.id}&fileId=${file.id}`;
children.push(
<div>
<div>
<div class={"flex-o m-5"}>
<fs-icon icon={"ant-design:cloud-download-outlined"} class={"mr-5 fs-16"}></fs-icon>
<a href={downloadUrl} target={"_blank"}>
{file.filename}
</a>
@@ -197,7 +198,6 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
return (
<div class={"mt-3"}>
<h3></h3>
<div> {children}</div>
</div>
);

View File

@@ -15,7 +15,7 @@
<div>
<a-tag color="green" class="flex-inline pointer"> <fs-icon icon="ion:time-outline" class="mr-5"></fs-icon> {{ now }}</a-tag>
<a-badge v-if="userStore.isAdmin" :dot="hasNewVersion">
<a-tag color="blue" class="flex-inline pointer" :title="'最新版本:' + latestVersion">
<a-tag color="blue" class="flex-inline pointer" :title="'最新版本:' + latestVersion" @click="openUpgradeUrl()">
<fs-icon icon="ion:rocket-outline" class="mr-5"></fs-icon>
v{{ version }}
</a-tag>
@@ -210,6 +210,10 @@ onMounted(async () => {
await loadCount();
await loadPluginGroups();
});
function openUpgradeUrl() {
window.open("https://certd.docmirror.cn/guide/install/upgrade.html");
}
</script>
<style lang="less">

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.28.3](https://github.com/certd/certd/compare/v1.28.2...v1.28.3) (2024-12-12)
### Bug Fixes
* mysql下access.setting字段改成text ([b7f5740](https://github.com/certd/certd/commit/b7f5740c57743914f754f3b4fdd94b59a2e8338c))
### Performance Improvements
* 通知标题优化 ([ff083ce](https://github.com/certd/certd/commit/ff083ce6848a8bee3c8248e4b881086ae1517c28))
* 支持腾讯虚拟机开关机([@wujingke](https://github.com/wujingke)) ([8039e8b](https://github.com/certd/certd/commit/8039e8baf83c82d03f1a6198cf61c372026b962b))
* 支持aws cloudfront ([0ae39f1](https://github.com/certd/certd/commit/0ae39f160a7c6b6696b3bf513d68aa28905810ad))
## [1.28.2](https://github.com/certd/certd/compare/v1.28.1...v1.28.2) (2024-12-09)
### Bug Fixes

View File

@@ -0,0 +1,3 @@
ALTER TABLE `cd_access` MODIFY COLUMN `setting` text NULL AFTER `type`;
ALTER TABLE `pi_pipeline` MODIFY COLUMN `content` longtext NULL AFTER `title`;

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.28.2",
"version": "1.28.3",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -31,18 +31,21 @@
},
"dependencies": {
"@alicloud/pop-core": "^1.7.10",
"@certd/acme-client": "^1.28.2",
"@certd/basic": "^1.28.2",
"@certd/commercial-core": "^1.28.2",
"@certd/lib-huawei": "^1.28.2",
"@certd/lib-k8s": "^1.28.2",
"@certd/lib-server": "^1.28.2",
"@certd/midway-flyway-js": "^1.28.2",
"@certd/pipeline": "^1.28.2",
"@certd/plugin-cert": "^1.28.2",
"@certd/plugin-lib": "^1.28.2",
"@certd/plugin-plus": "^1.28.2",
"@certd/plus-core": "^1.28.2",
"@aws-sdk/client-acm": "^3.699.0",
"@aws-sdk/client-cloudfront": "^3.699.0",
"@aws-sdk/client-s3": "^3.705.0",
"@certd/acme-client": "^1.28.3",
"@certd/basic": "^1.28.3",
"@certd/commercial-core": "^1.28.3",
"@certd/lib-huawei": "^1.28.3",
"@certd/lib-k8s": "^1.28.3",
"@certd/lib-server": "^1.28.3",
"@certd/midway-flyway-js": "^1.28.3",
"@certd/pipeline": "^1.28.3",
"@certd/plugin-cert": "^1.28.3",
"@certd/plugin-lib": "^1.28.3",
"@certd/plugin-plus": "^1.28.3",
"@certd/plus-core": "^1.28.3",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
"@koa/cors": "^5.0.0",

View File

@@ -12,10 +12,7 @@ export class EmailController extends BaseController {
emailService: EmailService;
@Post('/test', { summary: Constants.per.authOnly })
public async test(
@Body('receiver')
receiver
) {
public async test(@Body('receiver') receiver) {
const userId = super.getUserId();
await this.emailService.test(userId, receiver);
return this.ok({});

View File

@@ -56,18 +56,6 @@ export class HandleController extends BaseController {
@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 = await newNotification(body.typeName, input, {
http,

View File

@@ -1,12 +1,13 @@
import { Inject, Provide } from '@midwayjs/core';
import { cache, isDev, randomNumber } from '@certd/basic';
import { SysSettingsService } from '@certd/lib-server';
import { SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import { SmsServiceFactory } from '../sms/factory.js';
import { ISmsService } from '../sms/api.js';
import { CodeErrorException } from '@certd/lib-server/dist/basic/exception/code-error-exception.js';
import { EmailService } from './email-service.js';
import { AccessService } from '../../pipeline/service/access-service.js';
import { AccessSysGetter } from '../../pipeline/service/access-sys-getter.js';
import { isComm } from '@certd/plus-core';
// {data: '<svg.../svg>', text: 'abcd'}
/**
@@ -99,9 +100,17 @@ export class CodeService {
throw new Error('randomStr不能为空');
}
let siteTitle = 'Certd';
if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
if (siteInfo) {
siteTitle = siteInfo.title || siteTitle;
}
}
const code = randomNumber(4);
await this.emailService.send({
subject: '【Certd】验证码',
subject: `${siteTitle}】验证码`,
content: `您的验证码是${code},请勿泄露`,
receivers: [email],
});

View File

@@ -88,10 +88,14 @@ export class EmailService implements IEmailService {
sysTitle = siteInfo.title || sysTitle;
}
}
let subject = email.subject;
if (!subject.includes(`${sysTitle}`)) {
subject = `${sysTitle}${subject}`;
}
const mailOptions = {
from: `${sysTitle} <${emailConfig.sender}>`,
to: email.receivers.join(', '), // list of receivers
subject: email.subject,
subject: subject,
text: email.content,
};
await transporter.sendMail(mailOptions);

View File

@@ -1,4 +1,4 @@
import { INotificationService } from '@certd/pipeline';
import { INotificationService, NotificationSendReq } from '@certd/pipeline';
import { NotificationService } from './notification-service.js';
export class NotificationGetter implements INotificationService {
@@ -17,4 +17,8 @@ export class NotificationGetter implements INotificationService {
async getById(id: any) {
return await this.notificationService.getById(id, this.userId);
}
async send(req: NotificationSendReq): Promise<void> {
return await this.notificationService.send(req, this.userId);
}
}

View File

@@ -1,9 +1,12 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { BaseService, ValidateException } from '@certd/lib-server';
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { BaseService, SysSettingsService, SysSiteInfo, ValidateException } from '@certd/lib-server';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { NotificationEntity } from '../entity/notification.js';
import { NotificationInstanceConfig, notificationRegistry } from '@certd/pipeline';
import { NotificationInstanceConfig, notificationRegistry, NotificationSendReq, sendNotification } from '@certd/pipeline';
import { http, utils } from '@certd/basic';
import { EmailService } from '../../basic/service/email-service.js';
import { isComm } from '@certd/plus-core';
@Provide()
@Scope(ScopeEnum.Singleton)
@@ -11,6 +14,12 @@ export class NotificationService extends BaseService<NotificationEntity> {
@InjectEntityModel(NotificationEntity)
repository: Repository<NotificationEntity>;
@Inject()
emailService: EmailService;
@Inject()
sysSettingsService: SysSettingsService;
//@ts-ignore
getRepository() {
return this.repository;
@@ -124,4 +133,58 @@ export class NotificationService extends BaseService<NotificationEntity> {
});
return this.buildNotificationInstanceConfig(res);
}
async send(req: NotificationSendReq, userId?: number) {
const logger = req.logger;
let notifyConfig: NotificationInstanceConfig = null;
if (req.id && req.id > 0) {
notifyConfig = await this.getById(req.id, userId);
if (!notifyConfig) {
logger.warn(`未找到通知配置<${req.id}>`);
}
}
if (!notifyConfig) {
if (req.id === 0 || req.useDefault) {
notifyConfig = await this.getDefault(userId);
if (!notifyConfig) {
logger.warn(`未找到默认通知配置`);
}
}
}
if (notifyConfig) {
//发送通知
logger.info('发送通知, 使用通知渠道:' + notifyConfig.name);
if (notifyConfig.type != 'email') {
//非邮件通知,需要加上站点名称
let siteTitle = 'Certd';
if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
siteTitle = siteInfo?.title || siteTitle;
}
req.body.title = `${siteTitle}${req.body.title}`;
}
await sendNotification({
config: notifyConfig,
ctx: {
http: http,
logger: logger,
utils: utils,
emailService: this.emailService,
},
body: req.body,
});
} else {
if (req.useEmail && req.emailAddress) {
logger.info('使用邮件通知');
await this.emailService.send({
receivers: [req.emailAddress],
subject: req.body.title,
content: req.body.content,
});
}
}
}
}

View File

@@ -13,3 +13,4 @@ export * from './plugin-woai/index.js';
export * from './plugin-cachefly/index.js';
export * from './plugin-gcore/index.js';
export * from './plugin-qnap/index.js';
export * from './plugin-aws/index.js';

View File

@@ -0,0 +1,66 @@
import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline';
export const AwsRegions = [
{ label: 'us-east-1', value: 'us-east-1' },
{ label: 'us-east-2', value: 'us-east-2' },
{ label: 'us-west-1', value: 'us-west-1' },
{ label: 'us-west-2', value: 'us-west-2' },
{ label: 'af-south-1', value: 'af-south-1' },
{ label: 'ap-east-1', value: 'ap-east-1' },
{ label: 'ap-northeast-1', value: 'ap-northeast-1' },
{ label: 'ap-northeast-2', value: 'ap-northeast-2' },
{ label: 'ap-northeast-3', value: 'ap-northeast-3' },
{ label: 'ap-south-1', value: 'ap-south-1' },
{ label: 'ap-south-2', value: 'ap-south-2' },
{ label: 'ap-southeast-1', value: 'ap-southeast-1' },
{ label: 'ap-southeast-2', value: 'ap-southeast-2' },
{ label: 'ap-southeast-3', value: 'ap-southeast-3' },
{ label: 'ap-southeast-4', value: 'ap-southeast-4' },
{ label: 'ap-southeast-5', value: 'ap-southeast-5' },
{ label: 'ca-central-1', value: 'ca-central-1' },
{ label: 'ca-west-1', value: 'ca-west-1' },
{ label: 'eu-central-1', value: 'eu-central-1' },
{ label: 'eu-central-2', value: 'eu-central-2' },
{ label: 'eu-north-1', value: 'eu-north-1' },
{ label: 'eu-south-1', value: 'eu-south-1' },
{ label: 'eu-south-2', value: 'eu-south-2' },
{ label: 'eu-west-1', value: 'eu-west-1' },
{ label: 'eu-west-2', value: 'eu-west-2' },
{ label: 'eu-west-3', value: 'eu-west-3' },
{ label: 'il-central-1', value: 'il-central-1' },
{ label: 'me-central-1', value: 'me-central-1' },
{ label: 'me-south-1', value: 'me-south-1' },
{ label: 'sa-east-1', value: 'sa-east-1' },
];
@IsAccess({
name: 'aws',
title: '亚马逊云aws授权',
desc: '',
icon: 'ant-design:aws-outlined',
})
export class AwsAccess extends BaseAccess {
@AccessInput({
title: 'accessKeyId',
component: {
placeholder: 'accessKeyId',
},
helper:
'右上角->安全凭证->访问密钥,[点击前往](https://us-east-1.console.aws.amazon.com/iam/home?region=ap-east-1#/security_credentials/access-key-wizard)',
required: true,
})
accessKeyId = '';
@AccessInput({
title: 'secretAccessKey',
component: {
placeholder: 'secretAccessKey',
},
required: true,
encrypt: true,
helper: '请妥善保管您的安全访问密钥。您可以在AWS管理控制台的IAM中创建新的访问密钥。',
})
secretAccessKey = '';
}
new AwsAccess();

View File

@@ -0,0 +1,2 @@
export * from './plugins/index.js';
export * from './access.js';

View File

@@ -0,0 +1,40 @@
// 导入所需的 SDK 模块
import { AwsAccess } from '../access.js';
import { CertInfo } from '@certd/plugin-cert';
type AwsAcmClientOptions = { access: AwsAccess; region: string };
export class AwsAcmClient {
options: AwsAcmClientOptions;
access: AwsAccess;
region: string;
constructor(options: AwsAcmClientOptions) {
this.options = options;
this.access = options.access;
this.region = options.region;
}
async importCertificate(certInfo: CertInfo) {
// 创建 ACM 客户端
const { ACMClient, ImportCertificateCommand } = await import('@aws-sdk/client-acm');
const acmClient = new ACMClient({
region: this.region, // 替换为您的 AWS 区域
credentials: {
accessKeyId: this.access.accessKeyId, // 从环境变量中读取
secretAccessKey: this.access.secretAccessKey,
},
});
const cert = certInfo.crt.split('-----END CERTIFICATE-----')[0] + '-----END CERTIFICATE-----';
// 构建上传参数
const data = await acmClient.send(
new ImportCertificateCommand({
Certificate: Buffer.from(cert),
PrivateKey: Buffer.from(certInfo.key),
// CertificateChain: certificateChain, // 可选
})
);
console.log('Upload successful:', data);
// 返回证书 ARNAmazon Resource Name
return data.CertificateArn;
}
}

View File

@@ -0,0 +1 @@
export * from './plugin-deploy-to-cloudfront.js';

View File

@@ -0,0 +1,162 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { AwsAccess, AwsRegions } from '../access.js';
import { AwsAcmClient } from '../libs/aws-acm-client.js';
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
@IsTaskPlugin({
name: 'AwsDeployToCloudFront',
title: '部署证书到 AWS CloudFront',
desc: '部署证书到 AWS CloudFront',
icon: 'clarity:plugin-line',
group: pluginGroups.aws.key,
needPlus: true,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class AwsDeployToCloudFront extends AbstractPlusTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'AwsUploadToACM'],
},
required: true,
})
cert!: CertInfo | string;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: '区域',
helper: '证书上传区域',
component: {
name: 'a-auto-complete',
vModel: 'value',
options: AwsRegions,
},
required: true,
})
region!: string;
@TaskInput({
title: 'Access授权',
helper: 'aws的授权',
component: {
name: 'access-selector',
type: 'aws',
},
required: true,
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: '分配ID',
helper: '请选择distributions id',
action: AwsDeployToCloudFront.prototype.onGetDistributions.name,
required: true,
})
)
distributionIds!: string[];
async onInstance() {}
async execute(): Promise<void> {
const access = await this.getAccess<AwsAccess>(this.accessId);
let certId = this.cert as string;
if (typeof this.cert !== 'string') {
//先上传
certId = await this.uploadToACM(access, this.cert);
}
//部署到CloudFront
const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await import('@aws-sdk/client-cloudfront');
const cloudFrontClient = new CloudFrontClient({
region: this.region,
credentials: {
accessKeyId: access.accessKeyId,
secretAccessKey: access.secretAccessKey,
},
});
// update-distribution
for (const distributionId of this.distributionIds) {
// get-distribution-config
const getDistributionConfigCommand = new GetDistributionConfigCommand({
Id: distributionId,
});
const configData = await cloudFrontClient.send(getDistributionConfigCommand);
const updateDistributionCommand = new UpdateDistributionCommand({
DistributionConfig: {
...configData.DistributionConfig,
ViewerCertificate: {
...configData.DistributionConfig.ViewerCertificate,
CloudFrontDefaultCertificate: false,
ACMCertificateArn: certId,
},
},
Id: distributionId,
IfMatch: configData.ETag,
});
await cloudFrontClient.send(updateDistributionCommand);
this.logger.info(`部署${distributionId}完成:`);
}
this.logger.info('部署完成');
}
private async uploadToACM(access: AwsAccess, cert: CertInfo) {
const acmClient = new AwsAcmClient({
access,
region: this.region,
});
const awsCertARN = await acmClient.importCertificate(cert);
this.logger.info('证书上传成功,id=', awsCertARN);
return awsCertARN;
}
//查找分配ID列表选项
async onGetDistributions() {
if (!this.accessId) {
throw new Error('请选择Access授权');
}
const access = await this.getAccess<AwsAccess>(this.accessId);
const { CloudFrontClient, ListDistributionsCommand } = await import('@aws-sdk/client-cloudfront');
const cloudFrontClient = new CloudFrontClient({
region: this.region,
credentials: {
accessKeyId: access.accessKeyId,
secretAccessKey: access.secretAccessKey,
},
});
// list-distributions
const listDistributionsCommand = new ListDistributionsCommand({});
const data = await cloudFrontClient.send(listDistributionsCommand);
const distributions = data.DistributionList?.Items;
if (!distributions || distributions.length === 0) {
throw new Error('找不到CloudFront分配ID您可以手动输入');
}
const options = distributions.map((item: any) => {
return {
value: item.Id,
label: `${item.DomainName}<${item.Id}>`,
domain: item.DomainName,
};
});
return optionsUtils.buildGroupOptions(options, this.certDomains);
}
}
new AwsDeployToCloudFront();

View File

@@ -0,0 +1,71 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { AwsAccess, AwsRegions } from '../access.js';
import { AwsAcmClient } from '../libs/aws-acm-client.js';
@IsTaskPlugin({
name: 'AwsUploadToACM',
title: '上传证书 AWS ACM',
desc: '上传证书 AWS ACM',
icon: 'clarity:plugin-line',
group: pluginGroups.aws.key,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class AwsUploadToACM extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: 'Access授权',
helper: 'aws的授权',
component: {
name: 'access-selector',
type: 'aws',
},
required: true,
})
accessId!: string;
@TaskInput({
title: '区域',
helper: '证书上传区域',
component: {
name: 'a-auto-complete',
vModel: 'value',
options: AwsRegions,
},
required: true,
})
region!: string;
@TaskOutput({
title: '证书ARN',
})
awsCertARN = '';
async onInstance() {}
async execute(): Promise<void> {
const { cert, accessId, region } = this;
const access = await this.accessService.getById<AwsAccess>(accessId);
const acmClient = new AwsAcmClient({
access,
region,
});
this.awsCertARN = await acmClient.importCertificate(cert);
this.logger.info('证书上传成功,id=', this.awsCertARN);
}
}
new AwsUploadToACM();

View File

@@ -26,7 +26,10 @@ export class DemoTest extends AbstractTaskPlugin {
component: {
//前端组件配置,具体配置见组件文档 https://www.antdv.com/components/input-cn
name: 'a-input',
vModel: 'value', //双向绑定组件的props名称
},
helper: '帮助说明,[链接](https://certd.docmirror.cn)',
required: false, //是否必填
})
text!: string;
@@ -38,13 +41,29 @@ export class DemoTest extends AbstractTaskPlugin {
name: 'a-auto-complete',
vModel: 'value',
options: [
{ value: '1', label: '选项1' },
{ value: '2', label: '选项2' },
//选项列表
{ value: 'show', label: '动态显' },
{ value: 'hide', label: '动态隐' },
],
},
})
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: '多选框',

View File

@@ -0,0 +1,272 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { utils } from '@certd/basic';
import dayjs from 'dayjs';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
@IsTaskPlugin({
name: 'DeployCertToTencentTKEIngress',
title: '部署到腾讯云TKE-ingress',
needPlus: true,
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: 'Qcloud类型需要【上传到腾讯云】作为前置任务ApiServer未开启外网访问则需要做域名的内网IP映射',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class DeployCertToTencentTKEIngressPlugin extends AbstractPlusTaskPlugin {
@TaskInput({ title: '大区', value: 'ap-guangzhou', required: true })
region!: string;
@TaskInput({
title: '集群ID',
required: true,
desc: '例如cls-6lbj1vee',
request: true,
})
clusterId!: string;
@TaskInput({ title: '集群namespace', value: 'default', required: true })
namespace!: string;
@TaskInput({ title: '证书的secret名称', required: true })
secretName!: string | string[];
@TaskInput({ title: 'ingress名称', required: true })
ingressName!: string | string[];
@TaskInput({
title: 'ingress类型',
component: {
name: 'a-auto-complete',
vModel: 'value',
options: [{ value: 'qcloud' }, { value: 'nginx' }],
},
helper: '可选 qcloud / nginx',
})
ingressClass!: string;
// @TaskInput({ title: "集群内网ip", helper: "如果开启了外网的话,无需设置" })
// clusterIp!: string;
@TaskInput({
title: '集群域名',
helper: '可不填,默认为:[clusterId].ccs.tencent-cloud.com',
})
clusterDomain!: string;
/**
* AccessProvider的key,或者一个包含access的具体的对象
*/
@TaskInput({
title: 'Access授权',
helper: 'access授权',
component: {
name: 'access-selector',
type: 'tencent',
},
required: true,
})
accessId!: string;
@TaskInput({
title: '腾讯云证书id',
helper: '请选择“上传证书到腾讯云”前置任务的输出',
component: {
name: 'output-selector',
from: 'UploadCertToTencent',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.ingressClass === "qcloud"
})
}
`,
required: true,
})
tencentCertId!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.ingressClass === "nginx"
})
}
`,
required: true,
})
cert!: any;
K8sClient: any;
async onInstance() {
// const TkeClient = this.tencentcloud.tke.v20180525.Client;
const k8sSdk = await import('@certd/lib-k8s');
this.K8sClient = k8sSdk.K8sClient;
}
async execute(): Promise<void> {
const accessProvider = await this.accessService.getById(this.accessId);
const tkeClient = await this.getTkeClient(accessProvider, this.region);
const kubeConfigStr = await this.getTkeKubeConfig(tkeClient, this.clusterId);
this.logger.info('kubeconfig已成功获取');
const k8sClient = new this.K8sClient({
kubeConfigStr,
logger: this.logger,
});
// if (this.clusterIp != null) {
// if (!this.clusterDomain) {
// this.clusterDomain = `${this.clusterId}.ccs.tencent-cloud.com`;
// }
// // 修改内网解析ip地址
// k8sClient.setLookup({ [this.clusterDomain]: { ip: this.clusterIp } });
// }
const ingressType = this.ingressClass || 'qcloud';
if (ingressType === 'qcloud') {
await this.patchQcloudCertSecret({ k8sClient });
} else {
await this.patchNginxCertSecret({ k8sClient });
}
await utils.sleep(5000); // 停留2秒等待secret部署完成
await this.restartIngress({ k8sClient });
}
async getTkeClient(accessProvider: any, region = 'ap-guangzhou') {
const sdk = await import('tencentcloud-sdk-nodejs/tencentcloud/services/tke/v20180525/index.js');
const TkeClient = sdk.v20180525.Client;
const clientConfig = {
credential: {
secretId: accessProvider.secretId,
secretKey: accessProvider.secretKey,
},
region,
profile: {
httpProfile: {
endpoint: 'tke.tencentcloudapi.com',
},
},
};
return new TkeClient(clientConfig);
}
async getTkeKubeConfig(client: any, clusterId: string) {
// Depends on tencentcloud-sdk-nodejs version 4.0.3 or higher
const params = {
ClusterId: clusterId,
};
const ret = await client.DescribeClusterKubeconfig(params);
this.checkRet(ret);
this.logger.info('注意:后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster');
return ret.Kubeconfig;
}
appendTimeSuffix(name: string) {
if (name == null) {
name = 'certd';
}
return name + '-' + dayjs().format('YYYYMMDD-HHmmss');
}
async patchQcloudCertSecret(options: { k8sClient: any }) {
if (this.tencentCertId == null) {
throw new Error('请先将【上传证书到腾讯云】作为前置任务');
}
this.logger.info('腾讯云证书ID:', this.tencentCertId);
const certIdBase64 = Buffer.from(this.tencentCertId).toString('base64');
const { namespace, secretName } = this;
const body = {
data: {
qcloud_cert_id: certIdBase64,
},
metadata: {
labels: {
certd: this.appendTimeSuffix('certd'),
},
},
};
let secretNames: any = secretName;
if (typeof secretName === 'string') {
secretNames = [secretName];
}
for (const secret of secretNames) {
await options.k8sClient.patchSecret({
namespace,
secretName: secret,
body,
});
this.logger.info(`CertSecret已更新:${secret}`);
}
}
async patchNginxCertSecret(options: { k8sClient: any }) {
const { k8sClient } = options;
const { cert } = this;
const crt = cert.crt;
const key = cert.key;
const crtBase64 = Buffer.from(crt).toString('base64');
const keyBase64 = Buffer.from(key).toString('base64');
const { namespace, secretName } = this;
const body = {
data: {
'tls.crt': crtBase64,
'tls.key': keyBase64,
},
metadata: {
labels: {
certd: this.appendTimeSuffix('certd'),
},
},
};
let secretNames = secretName;
if (typeof secretName === 'string') {
secretNames = [secretName];
}
for (const secret of secretNames) {
await k8sClient.patchSecret({ namespace, secretName: secret, body });
this.logger.info(`CertSecret已更新:${secret}`);
}
}
async restartIngress(options: { k8sClient: any }) {
const { k8sClient } = options;
const { namespace, ingressName } = this;
const body = {
metadata: {
labels: {
certd: this.appendTimeSuffix('certd'),
},
},
};
let ingressNames = this.ingressName;
if (typeof ingressName === 'string') {
ingressNames = [ingressName];
}
for (const ingress of ingressNames) {
await k8sClient.patchIngress({ namespace, ingressName: ingress, body });
this.logger.info(`ingress已重启:${ingress}`);
}
}
checkRet(ret: any) {
if (!ret || ret.Error) {
throw new Error('执行失败:' + ret.Error.Code + ',' + ret.Error.Message);
}
}
}

View File

@@ -6,3 +6,4 @@ export * from './upload-to-tencent/index.js';
export * from './deploy-to-cos/index.js';
export * from './deploy-to-eo/index.js';
export * from './delete-expiring-cert/index.js';
export * from './deploy-to-tke-ingress/index.js';

View File

@@ -0,0 +1,181 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AbstractTaskPlugin } from '@certd/pipeline';
import { TencentAccess } from '@certd/plugin-plus';
import { createRemoteSelectInputDefine } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'TencentActionInstancesPlugin',
title: '腾讯云实例开关机',
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: '腾讯云实例开关机',
default: {
strategy: {
runStrategy: RunStrategy.AlwaysRun,
},
},
needPlus: false,
})
export class TencentActionInstancesPlugin extends AbstractTaskPlugin {
@TaskInput({
title: 'Access提供者',
helper: 'access 授权',
component: {
name: 'access-selector',
type: 'tencent',
},
required: true,
})
accessId!: string;
@TaskInput({
title: '所在地域',
helper: '实例所在地域',
component: {
name: 'a-auto-complete',
vModel: 'value',
options: [
{ value: '', label: '--------中国大陆地区-------', disabled: true },
{ value: 'ap-beijing-1', label: '北京1区' },
{ value: 'ap-beijing', label: '北京' },
{ value: 'ap-nanjing', label: '南京' },
{ value: 'ap-shanghai', label: '上海' },
{ value: 'ap-guangzhou', label: '广州' },
{ value: 'ap-chengdu', label: '成都' },
{ value: 'ap-chongqing', label: '重庆' },
{ value: 'ap-shenzhen-fsi', label: '深圳金融' },
{ value: 'ap-shanghai-fsi', label: '上海金融' },
{ value: 'ap-beijing-fsi', label: '北京金融' },
{ value: '', label: '--------中国香港及境外-------', disabled: true },
{ value: 'ap-hongkong', label: '中国香港' },
{ value: 'ap-singapore', label: '新加坡' },
{ value: 'ap-mumbai', label: '孟买' },
{ value: 'ap-jakarta', label: '雅加达' },
{ value: 'ap-seoul', label: '首尔' },
{ value: 'ap-bangkok', label: '曼谷' },
{ value: 'ap-tokyo', label: '东京' },
{ value: 'na-siliconvalley', label: '硅谷' },
{ value: 'na-ashburn', label: '弗吉尼亚' },
{ value: 'sa-saopaulo', label: '圣保罗' },
{ value: 'eu-frankfurt', label: '法兰克福' },
],
},
required: true,
})
region!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: '实列ID',
helper: '请选择实列',
typeName: 'TencentStartInstancesPlugin',
action: TencentActionInstancesPlugin.prototype.onGetInstanceList.name,
watches: ['region'],
})
)
instanceId!: string[];
@TaskInput({
title: '操作',
component: {
name: 'a-radio-group',
vModel: 'value',
options: [
{ value: 'start', label: '开机' },
{ value: 'stop', label: '关机' },
],
},
required: true,
})
action!: string;
@TaskInput({
title: '实例关机不收费',
value: true,
component: {
name: 'a-switch',
vModel: 'checked',
placeholder: `按量计费实例关机不收费`,
},
required: false,
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.action === 'stop';
})
}
`,
})
charging = true;
async onInstance() {}
async execute(): Promise<void> {
const cvmClient = await this.getCvmClient();
const params = {
InstanceIds: this.instanceId,
};
let res: any;
if (this.action === 'start') {
res = await cvmClient.StartInstances(params);
} else {
res = await cvmClient.StopInstances(
Object.assign(params, {
StopType: 'SOFT_FIRST',
StoppedMode: this.charging ? 'STOP_CHARGING' : 'KEEP_CHARGING',
})
);
}
this.checkRet(res);
}
checkRet(ret: any) {
if (!ret || ret.Error) {
throw new Error('执行失败:' + ret.Error.Code + ',' + ret.Error.Message);
}
}
async getCvmClient() {
const accessProvider = await this.accessService.getById<TencentAccess>(this.accessId);
const sdk = await import('tencentcloud-sdk-nodejs/tencentcloud/services/cvm/v20170312/index.js');
const CvmClient = sdk.v20170312.Client;
if (!this.region) {
throw new Error('所在地域不能为空');
}
const clientConfig = {
credential: {
secretId: accessProvider.secretId,
secretKey: accessProvider.secretKey,
},
region: this.region,
profile: {
httpProfile: {
endpoint: 'cvm.tencentcloudapi.com',
},
},
};
return new CvmClient(clientConfig);
}
async onGetInstanceList(data: any) {
const cvmClient = await this.getCvmClient();
const res = await cvmClient.DescribeInstances({
Limit: 100,
});
this.checkRet(res);
this.ctx.logger.info('获取实列列表:', res);
return res.InstanceSet.map((item: any) => {
return {
label: item.InstanceName,
value: item.InstanceId,
};
});
}
}
new TencentActionInstancesPlugin();