Compare commits

...

13 Commits

Author SHA1 Message Date
xiaojunnuo
5b3fb7387d v1.33.4 2025-04-15 23:48:00 +08:00
xiaojunnuo
feac310caf build: prepare to build 2025-04-15 23:45:18 +08:00
xiaojunnuo
d67ec3feb3 chore: 2025-04-15 23:45:11 +08:00
xiaojunnuo
cf8abb4528 perf: 插件支持导入导出 2025-04-15 23:43:01 +08:00
xiaojunnuo
d66de26de4 Merge branch 'v2' into v2-dev 2025-04-15 21:34:13 +08:00
greper
7edf3f6147 fix: 修复腾讯云部署到任意资源插件,无法使用之前已上传的腾讯云证书问题 from xinghejd/dev @xinghejd
fix: 修复腾讯云部署到任意资源插件,无法使用之前已上传的腾讯云证书问题
2025-04-15 21:16:49 +08:00
xinghejd
2143dff2ae fix: 补充类型断言 2025-04-15 08:54:28 +00:00
xinghejd
32c714d1b6 fix: 修复腾讯云部署到任意资源插件,无法使用之前已上传的腾讯云证书问题 2025-04-15 08:47:57 +00:00
xiaojunnuo
84e699ee24 chore: 思维导图 2025-04-15 11:22:42 +08:00
xiaojunnuo
7fdb572b8b chore: 思维导图 2025-04-15 11:07:27 +08:00
xiaojunnuo
cfd3b66be9 perf: 支持上传证书到华为云CCM 2025-04-14 23:31:59 +08:00
xiaojunnuo
75c4f9dea8 build: publish 2025-04-14 22:27:24 +08:00
xiaojunnuo
a76a32230d build: trigger build image 2025-04-14 22:26:59 +08:00
47 changed files with 710 additions and 105 deletions

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Bug Fixes
* 补充类型断言 ([2143dff](https://github.com/certd/certd/commit/2143dff2ae96e6a78bef9f0498e36f8cd9e6941f))
* 修复腾讯云部署到任意资源插件,无法使用之前已上传的腾讯云证书问题 ([32c714d](https://github.com/certd/certd/commit/32c714d1b6e68c71a74a7452115040c87ac4bfdc))
### Performance Improvements
* 插件支持导入导出 ([cf8abb4](https://github.com/certd/certd/commit/cf8abb45282070c8ba91469f93fd379fabf1f74a))
* 支持上传证书到华为云CCM ([cfd3b66](https://github.com/certd/certd/commit/cfd3b66be9ebf53a26693057e70ed60c3f116be9))
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
### Bug Fixes

View File

@@ -16,6 +16,9 @@ Certd 是一个免费全自动申请和自动部署更新SSL证书的管理系
* 私有化部署数据保存本地授权信息加密存储镜像由Github Actions构建过程公开透明
* 支持SQLitePostgreSQL、MySQL数据库
![](./docs/images/intro/intro.svg)
>
> 流水线数量现已调整为无限制,欢迎大家使用
>

View File

@@ -1 +1 @@
01:33
22:26

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
### Bug Fixes
* 修复登录错误次数过多阻止再次登录逻辑 ([bf4d191](https://github.com/certd/certd/commit/bf4d191c8bd2f9209eb6768f662b9c77de99e998))
## [1.33.2](https://github.com/certd/certd/compare/v1.33.1...v1.33.2) (2025-04-12)
### Bug Fixes

View File

@@ -17,6 +17,8 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
* 支持SQLite、Postgresql、MySQL数据库
![](../images/intro/intro.svg)
## 二、一些说明
* 本项目申请证书过程遵循acme协议
* 需要验证域名所有权,一般有两种方式

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.33.3"
"version": "1.33.4"
}

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.33.4](https://github.com/publishlab/node-acme-client/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/acme-client
## [1.33.3](https://github.com/publishlab/node-acme-client/compare/v1.33.2...v1.33.3) (2025-04-14)
**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.33.3",
"version": "1.33.4",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.33.3",
"@certd/basic": "^1.33.4",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.7.2",
@@ -67,5 +67,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/basic
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/basic

View File

@@ -1 +1 @@
22:22
23:45

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.33.3",
"version": "1.33.4",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -44,5 +44,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/pipeline
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/pipeline

View File

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

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/lib-huawei
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/lib-huawei

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.33.3",
"version": "1.33.4",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -23,5 +23,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/lib-iframe
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/lib-iframe

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.33.3",
"version": "1.33.4",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -30,5 +30,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/jdcloud
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/jdcloud

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.33.3",
"version": "1.33.4",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -60,5 +60,5 @@
"fetch"
]
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/lib-k8s
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/lib-k8s

View File

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

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/lib-server
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/lib-server

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.33.3",
"version": "1.33.4",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -27,10 +27,10 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.33.3",
"@certd/basic": "^1.33.3",
"@certd/pipeline": "^1.33.3",
"@certd/plus-core": "^1.33.3",
"@certd/acme-client": "^1.33.4",
"@certd/basic": "^1.33.4",
"@certd/pipeline": "^1.33.4",
"@certd/plus-core": "^1.33.4",
"@midwayjs/cache": "~3.14.0",
"@midwayjs/core": "~3.20.3",
"@midwayjs/i18n": "~3.20.3",
@@ -61,5 +61,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/midway-flyway-js

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.33.3",
"version": "1.33.4",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/plugin-cert
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/plugin-cert

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.33.3",
"version": "1.33.4",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -15,10 +15,10 @@
"preview": "vite preview"
},
"dependencies": {
"@certd/acme-client": "^1.33.3",
"@certd/basic": "^1.33.3",
"@certd/pipeline": "^1.33.3",
"@certd/plugin-lib": "^1.33.3",
"@certd/acme-client": "^1.33.4",
"@certd/basic": "^1.33.4",
"@certd/pipeline": "^1.33.4",
"@certd/plugin-lib": "^1.33.4",
"@google-cloud/publicca": "^1.3.0",
"dayjs": "^1.11.7",
"jszip": "^3.10.1",
@@ -41,5 +41,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Performance Improvements
* 插件支持导入导出 ([cf8abb4](https://github.com/certd/certd/commit/cf8abb45282070c8ba91469f93fd379fabf1f74a))
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/plugin-lib

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.33.3",
"version": "1.33.4",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -16,8 +16,8 @@
},
"dependencies": {
"@alicloud/pop-core": "^1.7.10",
"@certd/basic": "^1.33.3",
"@certd/pipeline": "^1.33.3",
"@certd/basic": "^1.33.4",
"@certd/pipeline": "^1.33.4",
"@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.21.0",
"basic-ftp": "^5.0.5",
@@ -48,5 +48,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "be69244e8ddb757de45c7e2b6033195c1f5a6910"
"gitHead": "0730f5ff4f176452f82489caa837b717e176fdca"
}

View File

@@ -1,7 +1,4 @@
import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
import { ConnectConfig } from "ssh2";
import { SshClient } from "./ssh.js";
@IsAccess({
name: "ssh",
title: "主机登录授权",
@@ -9,7 +6,7 @@ import { SshClient } from "./ssh.js";
icon: "clarity:host-line",
input: {},
})
export class SshAccess extends BaseAccess implements ConnectConfig {
export class SshAccess extends BaseAccess {
@AccessInput({
title: "主机地址",
component: {
@@ -125,6 +122,7 @@ export class SshAccess extends BaseAccess implements ConnectConfig {
testRequest = true;
async onTestRequest() {
const { SshClient } = await import("./ssh.js");
const client = new SshClient(this.ctx.logger);
await client.exec({

View File

@@ -1,22 +1,33 @@
// @ts-ignore
import ssh2, { ConnectConfig, ExecOptions } from "ssh2";
import ssh2Constants from "ssh2/lib/protocol/constants.js";
import path from "path";
import * as _ from "lodash-es";
import { isArray } from "lodash-es";
import { ILogger } from "@certd/basic";
import { SshAccess } from "./ssh-access.js";
import stripAnsi from "strip-ansi";
import { SocksClient } from "socks";
import { SocksProxy, SocksProxyType } from "socks/typings/common/constants.js";
import fs from "fs";
import { SocksProxyType } from "socks/typings/common/constants";
export type TransportItem = { localPath: string; remotePath: string };
export interface SocksProxy {
ipaddress?: string;
host?: string;
port: number;
type: any;
userId?: string;
password?: string;
custom_auth_method?: number;
custom_auth_request_handler?: () => Promise<Buffer>;
custom_auth_response_size?: number;
custom_auth_response_handler?: (data: Buffer) => Promise<boolean>;
}
export type SshConnectConfig = {
sock?: any;
};
export class AsyncSsh2Client {
conn: ssh2.Client;
conn: any;
logger: ILogger;
connConf: SshAccess & ssh2.ConnectConfig;
connConf: SshAccess & SshConnectConfig;
windows = false;
encoding: string;
constructor(connConf: SshAccess, logger: ILogger) {
@@ -40,7 +51,10 @@ export class AsyncSsh2Client {
if (typeof this.connConf.port === "string") {
this.connConf.port = parseInt(this.connConf.port);
}
const proxyOption: SocksProxy = this.parseSocksProxyFromUri(this.connConf.socksProxy);
const { SocksClient } = await import("socks");
const proxyOption = this.parseSocksProxyFromUri(this.connConf.socksProxy);
const info = await SocksClient.createConnection({
proxy: proxyOption,
command: "connect",
@@ -53,10 +67,12 @@ export class AsyncSsh2Client {
this.connConf.sock = info.socket;
}
const { SUPPORTED_KEX, SUPPORTED_SERVER_HOST_KEY, SUPPORTED_CIPHER, SUPPORTED_MAC } = ssh2Constants;
const ssh2 = await import("ssh2");
const ssh2Constants = await import("ssh2/lib/protocol/constants.js");
const { SUPPORTED_KEX, SUPPORTED_SERVER_HOST_KEY, SUPPORTED_CIPHER, SUPPORTED_MAC } = ssh2Constants.default;
return new Promise((resolve, reject) => {
try {
const conn = new ssh2.Client();
const conn = new ssh2.default.Client();
conn
.on("error", (err: any) => {
this.logger.error("连接失败", err);
@@ -197,6 +213,8 @@ export class AsyncSsh2Client {
}
async shell(script: string | string[]): Promise<string> {
const stripAnsiModule = await import("strip-ansi");
const stripAnsi = stripAnsiModule.default;
return new Promise<any>((resolve, reject) => {
this.logger.info(`执行shell脚本[${this.connConf.host}][shell]: ` + script);
this.conn.shell((err: Error, stream: any) => {
@@ -449,7 +467,7 @@ export class SshClient {
script = script.join(" && ");
} else {
const newLine = isLinux ? "\n" : "\r\n";
if (_.isArray(script)) {
if (isArray(script)) {
script = script as Array<string>;
script = script.join(newLine);
}
@@ -465,7 +483,7 @@ export class SshClient {
async shell(options: { connectConf: SshAccess; script: string | Array<string> }): Promise<string> {
let { script } = options;
const { connectConf } = options;
if (_.isArray(script)) {
if (isArray(script)) {
script = script as Array<string>;
if (connectConf.windows) {
script = script.join("\r\n");

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Performance Improvements
* 插件支持导入导出 ([cf8abb4](https://github.com/certd/certd/commit/cf8abb45282070c8ba91469f93fd379fabf1f74a))
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
**Note:** Version bump only for package @certd/ui-client

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.33.3",
"version": "1.33.4",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -101,8 +101,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.33.3",
"@certd/pipeline": "^1.33.3",
"@certd/lib-iframe": "^1.33.4",
"@certd/pipeline": "^1.33.4",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",

View File

@@ -66,6 +66,22 @@ export async function SetDisabled(data: { id?: number; name?: string; type?: str
});
}
export async function ExportPlugin(id: number) {
return await request({
url: apiPrefix + "/export",
method: "post",
data: { id },
});
}
export async function ImportPlugin(body: any) {
return await request({
url: apiPrefix + "/import",
method: "post",
data: body,
});
}
export type PluginConfigBean = {
name: string;
disabled: boolean;

View File

@@ -2,8 +2,8 @@ import * as api from "./api";
import { useI18n } from "vue-i18n";
import { Ref, ref } from "vue";
import { useRouter } from "vue-router";
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { Modal } from "ant-design-vue";
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, useFormWrapper, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { Modal, notification } from "ant-design-vue";
//@ts-ignore
import yaml from "js-yaml";
@@ -36,7 +36,74 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const selectedRowKeys: Ref<any[]> = ref([]);
context.selectedRowKeys = selectedRowKeys;
const { openCrudFormDialog } = useFormWrapper();
async function openImportDialog() {
function createCrudOptions() {
return {
crudOptions: {
columns: {
content: {
title: "插件文件",
type: "text",
form: {
component: {
name: "pem-input",
vModel: "modelValue",
textarea: {
rows: 8,
},
},
col: {
span: 24,
},
helper: "选择插件文件",
},
},
override: {
title: "同名覆盖",
type: "dict-switch",
dict: dict({
data: [
{
value: true,
label: "覆盖",
},
{
value: false,
label: "不覆盖",
},
],
}),
form: {
value: false,
col: {
span: 24,
},
helper: "如果已有相同名称插件,直接覆盖",
},
},
},
form: {
wrapper: {
title: "导入插件",
saveRemind: false,
},
afterSubmit() {
notification.success({ message: "操作成功" });
},
async doSubmit({ form }: any) {
return await api.ImportPlugin({
...form,
});
},
},
},
};
}
const { crudOptions } = createCrudOptions();
await openCrudFormDialog({ crudOptions });
}
return {
crudOptions: {
settings: {
@@ -65,8 +132,18 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
buttons: {
add: {
show: true,
icon: "ion:ios-add-circle-outline",
text: "自定义插件",
},
import: {
show: true,
icon: "ion:cloud-upload-outline",
text: "导入",
type: "primary",
async click() {
await openImportDialog();
},
},
},
},
rowHandle: {
@@ -85,10 +162,33 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}),
},
remove: {
order: 999,
show: compute(({ row }) => {
return row.type === "custom";
}),
},
export: {
text: null,
icon: "ion:cloud-download-outline",
title: "导出",
type: "link",
show: compute(({ row }) => {
return row.type === "custom";
}),
async click({ row }) {
//将文本内容,作为文件下载
const content = await api.ExportPlugin(row.id);
if (content) {
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = `${row.name}.yaml`;
link.click();
URL.revokeObjectURL(url);
}
},
},
},
},
table: {
@@ -182,8 +282,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
},
form: {
show: true,
helper: "必须为英文,驼峰命名,类型作为前缀\n例如AliyunDeployToCDN\n插件一旦被使用不要修改名称",
rules: [{ required: true }],
helper: "必须为英文或数字,驼峰命名,类型作为前缀\n例如AliyunDeployToCDN\n插件一旦被使用不要修改名称",
rules: [
{ required: true },
{
type: "regexp",
pattern: /^[a-zA-Z][a-zA-Z0-9]+$/,
message: "必须为英文或数字,驼峰命名,类型作为前缀",
},
],
},
column: {
width: 250,
@@ -205,7 +312,14 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
form: {
show: true,
helper: "上传到插件商店时,将作为插件名称前缀,例如greper/pluginName",
rules: [{ required: true }],
rules: [
{ required: true },
{
type: "regexp",
pattern: /^[a-zA-Z][a-zA-Z0-9]+$/,
message: "必须为英文字母或数字",
},
],
},
column: {
width: 200,

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.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Bug Fixes
* 补充类型断言 ([2143dff](https://github.com/certd/certd/commit/2143dff2ae96e6a78bef9f0498e36f8cd9e6941f))
* 修复腾讯云部署到任意资源插件,无法使用之前已上传的腾讯云证书问题 ([32c714d](https://github.com/certd/certd/commit/32c714d1b6e68c71a74a7452115040c87ac4bfdc))
### Performance Improvements
* 插件支持导入导出 ([cf8abb4](https://github.com/certd/certd/commit/cf8abb45282070c8ba91469f93fd379fabf1f74a))
* 支持上传证书到华为云CCM ([cfd3b66](https://github.com/certd/certd/commit/cfd3b66be9ebf53a26693057e70ed60c3f116be9))
## [1.33.3](https://github.com/certd/certd/compare/v1.33.2...v1.33.3) (2025-04-14)
### Bug Fixes

View File

@@ -0,0 +1,53 @@
// 扫描目录,列出文件,然后加载为模块
import { join } from 'path';
import fs from 'fs'
import { pathToFileURL } from "node:url";
import path from 'path'
function scanDir(dir) {
const files = fs.readdirSync(dir);
const result = [];
// 扫描目录及子目录
for (const file of files) {
if (file.includes("index.js")) {
continue;
}
const filePath = join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
result.push(...scanDir(filePath));
} else {
if (!file.endsWith(".js")) {
continue;
}
result.push(filePath);
}
}
return result
}
export default async function loadModules(dir) {
const files = scanDir(dir);
const modules = {}
for (const file of files) {
try {
// 转换为 file:// URLWindows 必需)
const moduleUrl = pathToFileURL(file).href
const module = await import(moduleUrl)
// 如果模块有默认导出,优先使用
modules[file] = module.default || module
} catch (err) {
console.error(`加载模块 ${file} 失败:`, err)
}
}
return modules;
}
const modules = await loadModules('./dist/plugins');
for (const key in modules) {
console.log(key)
}

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.33.3",
"version": "1.33.4",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -38,19 +38,19 @@
"@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.33.3",
"@certd/basic": "^1.33.3",
"@certd/commercial-core": "^1.33.3",
"@certd/jdcloud": "^1.33.3",
"@certd/lib-huawei": "^1.33.3",
"@certd/lib-k8s": "^1.33.3",
"@certd/lib-server": "^1.33.3",
"@certd/midway-flyway-js": "^1.33.3",
"@certd/pipeline": "^1.33.3",
"@certd/plugin-cert": "^1.33.3",
"@certd/plugin-lib": "^1.33.3",
"@certd/plugin-plus": "^1.33.3",
"@certd/plus-core": "^1.33.3",
"@certd/acme-client": "^1.33.4",
"@certd/basic": "^1.33.4",
"@certd/commercial-core": "^1.33.4",
"@certd/jdcloud": "^1.33.4",
"@certd/lib-huawei": "^1.33.4",
"@certd/lib-k8s": "^1.33.4",
"@certd/lib-server": "^1.33.4",
"@certd/midway-flyway-js": "^1.33.4",
"@certd/pipeline": "^1.33.4",
"@certd/plugin-cert": "^1.33.4",
"@certd/plugin-lib": "^1.33.4",
"@certd/plugin-plus": "^1.33.4",
"@certd/plus-core": "^1.33.4",
"@corsinvest/cv4pve-api-javascript": "^8.3.0",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",

View File

@@ -26,6 +26,11 @@ process.on('uncaughtException', error => {
});
@Configuration({
// detectorOptions: {
// ignore: [
// '**/plugins/**'
// ]
// },
imports: [
koa,
orm,

View File

@@ -1,7 +1,7 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { merge } from 'lodash-es';
import { CrudController } from '@certd/lib-server';
import { PluginService } from '../../../modules/plugin/service/plugin-service.js';
import { PluginImportReq, PluginService } from "../../../modules/plugin/service/plugin-service.js";
import { CommPluginConfig, PluginConfigService } from '../../../modules/plugin/service/plugin-config-service.js';
/**
* 插件
@@ -82,4 +82,17 @@ export class PluginController extends CrudController<PluginService> {
const res = await this.pluginConfigService.saveCommPluginConfig(body);
return this.ok(res);
}
@Post('/import', { summary: 'sys:settings:edit' })
async import(@Body(ALL) body: PluginImportReq) {
const res = await this.service.importPlugin(body);
return this.ok(res);
}
@Post('/export', { summary: 'sys:settings:edit' })
async export(@Body('id') id: number) {
const res = await this.service.exportPlugin(id);
return this.ok(res);
}
}

View File

@@ -18,6 +18,8 @@ export type PluginFindReq = {
name?: string;
type: string;
};
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class PluginConfigService {

View File

@@ -12,6 +12,10 @@ import { logger } from "@certd/basic";
import yaml from "js-yaml";
import { getDefaultAccessPlugin, getDefaultDeployPlugin, getDefaultDnsPlugin } from "./default-plugin.js";
export type PluginImportReq = {
content: string,
override?: boolean;
};
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
@@ -41,7 +45,7 @@ export class PluginService extends BaseService<PluginEntity> {
const builtInList = await this.getBuiltInEntityList();
//获取分页数据
const data = builtInList.slice(offset, offset + limit);
const data = builtInList.slice(offset, offset + limit);
return {
records: data,
@@ -53,7 +57,7 @@ export class PluginService extends BaseService<PluginEntity> {
async getEnabledBuildInGroup(isSimple = false) {
const groups = this.builtInPluginService.getGroups();
if(isSimple){
if (isSimple) {
for (const key in groups) {
const group = groups[key];
group.plugins.forEach(item => {
@@ -97,8 +101,8 @@ export class PluginService extends BaseService<PluginEntity> {
});
const disabledNames = list.map(it => it.name);
return builtInList.filter(it =>{
return !disabledNames.includes(it.name)
return builtInList.filter(it => {
return !disabledNames.includes(it.name);
});
}
@@ -168,33 +172,48 @@ export class PluginService extends BaseService<PluginEntity> {
name: param.name,
author: param.author
}
})
});
if (old) {
throw new Error(`插件${param.author}/${param.name}已存在`);
}
let plugin:any = {}
let plugin: any = {};
if (param.pluginType === "access") {
plugin = getDefaultAccessPlugin()
delete param.group
}else if (param.pluginType === "deploy") {
plugin = getDefaultDeployPlugin()
}else if (param.pluginType === "dnsProvider") {
plugin = getDefaultDnsPlugin()
delete param.group
}else{
plugin = getDefaultAccessPlugin();
delete param.group;
} else if (param.pluginType === "deploy") {
plugin = getDefaultDeployPlugin();
} else if (param.pluginType === "dnsProvider") {
plugin = getDefaultDnsPlugin();
delete param.group;
} else {
throw new Error(`插件类型${param.pluginType}不支持`);
}
return await super.add({
return await super.add({
...param,
...plugin
});
}
async update(param: any) {
const old = await this.repository.findOne({
where: {
name: param.name,
author: param.author
}
});
if (old && old.id !== param.id) {
throw new Error(`插件${param.author}/${param.name}已存在`);
}
return await super.update(param);
}
async compile(code: string) {
const ts = await import("typescript")
const ts = await import("typescript");
return ts.transpileModule(code, {
compilerOptions: { module: ts.ModuleKind.ESNext }
}).outputText;
@@ -220,16 +239,16 @@ export class PluginService extends BaseService<PluginEntity> {
if (info && info.length > 0) {
const plugin = info[0];
try{
try {
const AsyncFunction = Object.getPrototypeOf(async () => {
}).constructor;
// const script = await this.compile(plugin.content);
const script = plugin.content
const script = plugin.content;
const getPluginClass = new AsyncFunction(script);
return await getPluginClass({ logger: logger });
}catch (e) {
logger.error("编译插件失败:",e)
throw e
} catch (e) {
logger.error("编译插件失败:", e);
throw e;
}
}
@@ -284,4 +303,80 @@ export class PluginService extends BaseService<PluginEntity> {
});
}
async exportPlugin(id: number) {
const info = await this.info(id);
if (!info) {
throw new Error("插件不存在");
}
const metadata = yaml.load(info.metadata || "");
const extra = yaml.load(info.extra || "");
const content = info.content;
delete info.metadata;
delete info.extra;
delete info.content;
delete info.id;
delete info.createTime;
delete info.updateTime;
const plugin = {
...info,
...metadata,
...extra,
content
};
return yaml.dump(plugin) as string;
}
async importPlugin(req: PluginImportReq) {
const loaded = yaml.load(req.content);
if (!loaded) {
throw new Error("插件内容不能为空");
}
delete loaded.id
const old = await this.repository.findOne({
where: {
name: loaded.name,
author: loaded.author
}
});
const metadata = {
input: loaded.input,
output: loaded.output
};
const extra = {
dependPlugins: loaded.dependPlugins,
default: loaded.default,
showRunStrategy: loaded.showRunStrategy
};
const pluginEntity = {
...loaded,
metadata: yaml.dump(metadata),
extra: yaml.dump(extra),
content: req.content,
disabled: false
};
if (!pluginEntity.pluginType) {
throw new Error(`插件类型不能为空`);
}
if (old) {
if (!req.override) {
throw new Error(`插件${loaded.author}/${loaded.name}已存在`);
}
//update
pluginEntity.id = old.id;
await this.update(pluginEntity);
} else {
//add
const { id } = await this.add(pluginEntity);
pluginEntity.id = id;
}
return {
id: pluginEntity.id
};
}
}

View File

@@ -0,0 +1,59 @@
//@ts-ignore
import { HcClient } from "@huaweicloud/huaweicloud-sdk-core/HcClient";
export class HuaweiCcmClient {
//@ts-ignore
hcClient: HcClient;
//@ts-ignore
constructor(client: HcClient) {
this.hcClient = client;
}
/**
*/
importCertificate(req: {
name: string,
certificate: string,
private_key: string,
duplicate_check: boolean,
}) {
const options: any = {
method: "POST",
url: "/v3/scm/certificates/import",
pathParams: {},
queryParams: {},
contentType: "application/json",
headers: {
"Content-Type": "application/json"
},
/**
* name
* string 是
* 证书名称。字符长度为3~63位, 请输入英文字符,数字,下划线,中划线,英文句点。
*
* certificate
* string 是
* 证书内容,可包含中间证书及根证书。若certificate_chain字段传入证书链,则该字段只取证书本身。回车换行需要使用转义字符\n或者\r\n替换。
*
* certificate_chain
* string 否
* 证书链,非必填,可通过certificate字段传入。回车换行需要使用转义字符\n或者\r\n替换。
*
* private_key
* string 是
* 证书私钥。
* 不能上传带有口令保护的私钥,回车换行需要使用转义字符\n或者\r\n替换。
*
* duplicate_check
*/
data: {
...req
}
};
// @ts-ignore
options["responseHeaders"] = ["X-Request-Id"];
return this.hcClient.sendRequest(options);
}
}

View File

@@ -0,0 +1,61 @@
name: HuaweiUploadToCCM
title: 华为-上传证书至CCM
desc: 上传证书至华为云CCM
type: plugin
pluginType: deploy
author: certd
version: 1.0.0
icon: 'svg:icon-huawei'
group: huawei
default:
strategy:
runStrategy: 1,
input: # 插件的输入参数
cert:
title: 前置任务证书
helper: 请选择前置任务产生的证书 # 帮助说明
component:
name: output-selector # 输入组件名称
vModel: modelValue # 组件参数
from:
- ApplyCert
- ApplyCertLego
- ApplyCertUpload
required: true
certDomains:
title: 当前证书域名
component:
name: cert-domains-getter
mergeScript: |
return {
component:{
inputKey: ctx.compute(({form})=>{
return form.cert
}),
}
}
required: true
accessId:
title: Access授权
helper: xxxx的授权
component:
name: access-selector # 授权选择组件名称
type: aliyun # 授权类型
required: true
key1:
title: 输入示例1
required: false
key2:
title: 可选项
component:
name: a-select
vMode: value
options:
- value: "1"
label: 选项1
- value: "2"
label: 选项2
required: false
#output: # 输出参数,一般插件都不需要配置此项
# outputName:
#

View File

@@ -0,0 +1,82 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
import { HuaweiAccess } from "../../access/index.js";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { createCertDomainGetterInputDefine } from "@certd/plugin-lib";
import { resetLogConfigure } from "@certd/basic";
@IsTaskPlugin({
name: 'HauweiUploadToCCM',
title: '华为云-上传证书至CCM',
icon: 'svg:icon-huawei',
group: pluginGroups.huawei.key,
desc: '上传证书到华为云CCM',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class HauweiUploadToCCM extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames]
},
required: true
})
cert!: CertInfo;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: "Access授权",
helper: "华为云授权AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "huawei"
},
required: true
})
accessId!: string;
@TaskOutput({
title: '华为云CertId',
})
huaweiCertId!: string;
async execute(): Promise<void> {
this.logger.info("开始部署证书到华为云CCM");
const { client } = await this.getCcmClient();
const res = await client.importCertificate({
name: this.appendTimeSuffix("certd"),
certificate: this.cert.crt,
private_key: this.cert.key,
duplicate_check: false
});
this.huaweiCertId = res.certificate_id;
this.logger.info(`上传证书到华为云ccm完成,certificate_id:${this.huaweiCertId}`);
}
async getCcmClient() {
const access = await this.getAccess<HuaweiAccess>(this.accessId);
const { BasicCredentials } = await import("@huaweicloud/huaweicloud-sdk-core");
const { ClientBuilder } = await import("@huaweicloud/huaweicloud-sdk-core/ClientBuilder.js");
//@ts-ignore
const {HuaweiCcmClient} = await import("./ccm-client.js")
//恢复华为云把log4j的config改了的问题
resetLogConfigure();
const credentials = new BasicCredentials().withAk(access.accessKeyId).withSk(access.accessKeySecret);
const client = new ClientBuilder((hcClient) => {
return new HuaweiCcmClient(hcClient);
}).withCredential(credentials).withEndpoint("https://scm.cn-north-4.myhuaweicloud.com").build();
return {
client
};
}
}

View File

@@ -128,11 +128,12 @@ export class DeployCertToTencentAll extends AbstractTaskPlugin {
});
let certId:string = null
if (typeof certId === 'object') {
//上传
certId = await this.uploadToTencent(access,this.tencentCertId as CertInfo);
if (typeof this.tencentCertId === 'string') {
certId = this.tencentCertId as string;
} else if (this.tencentCertId && typeof this.tencentCertId === 'object') {
certId = await this.uploadToTencent(access, this.tencentCertId as CertInfo);
} else {
certId = this.tencentCertId as string;
throw new Error('无效的证书输入类型');
}
const params = {