chore: 支持手动上传证书并部署

pull/361/head
xiaojunnuo 2025-03-18 00:52:50 +08:00
parent 29a6a992f0
commit de40be430b
74 changed files with 1040 additions and 597 deletions

View File

@ -0,0 +1,7 @@
{
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -16,7 +16,7 @@
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
// "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }]
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": "off"
}
}

View File

@ -0,0 +1,7 @@
{
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -7,8 +7,8 @@ function sha256(data: string, digest: BinaryToTextEncoding = 'hex') {
return crypto.createHash('sha256').update(data).digest(digest);
}
function HmacSha256(data: string, key: string, digest: BinaryToTextEncoding = 'base64') {
return crypto.createHmac('sha256', Buffer.from(key, 'base64')).update(data).digest(digest);
function hmacSha256(data: string, digest: BinaryToTextEncoding = 'base64') {
return crypto.createHmac('sha256', data).update(Buffer.alloc(0)).digest(digest);
}
function base64(data: string) {
@ -18,5 +18,5 @@ export const hashUtils = {
md5,
sha256,
base64,
HmacSha256,
hmacSha256,
};

View File

@ -16,7 +16,7 @@
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
// "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }]
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": "off"
}
}

View File

@ -1,3 +1,7 @@
{
"printWidth": 160
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -7,7 +7,7 @@ import { createAxiosService, hashUtils, HttpRequestConfig, ILogger, logger, util
import { IAccessService } from "../access/index.js";
import { RegistryItem } from "../registry/index.js";
import { Decorator } from "../decorator/index.js";
import { ICnameProxyService, IEmailService, IPluginConfigService, IUrlService } from "../service/index.js";
import { ICnameProxyService, IEmailService, IPluginConfigService, IServiceGetter, IUrlService } from "../service/index.js";
import { FileStore } from "./file-store.js";
import { cloneDeep, forEach, merge } from "lodash-es";
import { INotificationService } from "../notification/index.js";
@ -33,7 +33,7 @@ export type ExecutorOptions = {
user: UserInfo;
baseURL?: string;
sysInfo?: SysInfo;
serviceGetter: (name: string) => any;
serviceGetter: IServiceGetter;
};
export class Executor {
@ -366,6 +366,7 @@ export class Executor {
step,
pipeline: this.pipeline,
}),
serviceGetter: this.options.serviceGetter,
};
instance.setCtx(taskCtx);

View File

@ -2,7 +2,7 @@ import { Registrable } from "../registry/index.js";
import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../dt/index.js";
import { FileStore } from "../core/file-store.js";
import { IAccessService } from "../access/index.js";
import { ICnameProxyService, IEmailService, IUrlService } from "../service/index.js";
import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js";
import { CancelError, IContext, RunHistory, RunnableCollection } from "../core/index.js";
import { HttpRequestConfig, ILogger, logger, utils } from "@certd/basic";
import { HttpClient } from "@certd/basic";
@ -116,7 +116,7 @@ export type TaskInstanceContext = {
emitter: TaskEmitter;
//service 容器
serviceContainer?: Record<string, any>;
serviceGetter?: IServiceGetter;
};
export abstract class AbstractTaskPlugin implements ITaskPlugin {
@ -224,7 +224,7 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
getStepFromPipeline(stepId: string) {
let found: any = null;
RunnableCollection.each(this.ctx.pipeline.stages, (step) => {
RunnableCollection.each(this.ctx.pipeline.stages, step => {
if (step.id === stepId) {
found = step;
return;

View File

@ -3,3 +3,6 @@ export * from "./cname.js";
export * from "./config.js";
export * from "./url.js";
export * from "./emit.js";
export type IServiceGetter = {
get: (name: string) => Promise<any>;
};

View File

@ -17,7 +17,6 @@
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }]
"@typescript-eslint/no-unused-vars": "off"
}
}

View File

@ -1,3 +1,7 @@
{
"printWidth": 160
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -17,7 +17,6 @@
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }]
"@typescript-eslint/no-unused-vars": "off"
}
}

View File

@ -1,7 +1,7 @@
{
"printWidth": 160,
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -1,7 +1,7 @@
{
"printWidth": 160,
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -1,7 +1,7 @@
{
"printWidth": 160,
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -1,7 +1,7 @@
{
"printWidth": 160,
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -17,7 +17,6 @@
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }]
"@typescript-eslint/no-unused-vars": "off"
}
}

View File

@ -1,3 +1,7 @@
{
"printWidth": 160
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -0,0 +1,177 @@
import { AbstractTaskPlugin, IContext, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import dayjs from "dayjs";
import type { CertInfo } from "./acme.js";
import { CertReader } from "./cert-reader.js";
import JSZip from "jszip";
import { CertConverter } from "./convert.js";
export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
@TaskInput({
title: "域名",
component: {
name: "a-select",
vModel: "value",
mode: "tags",
open: false,
placeholder: "foo.com / *.foo.com / *.bar.com",
tokenSeparators: [",", " ", "", "、", "|"],
},
rules: [{ type: "domains" }],
required: true,
col: {
span: 24,
},
order: -999,
helper:
"1、支持多个域名打到一个证书上例如 foo.com*.foo.com*.bar.com\n" +
"2、子域名被通配符包含的不要填写例如www.foo.com已经被*.foo.com包含不要填写www.foo.com\n" +
"3、泛域名只能通配*号那一级(*.foo.com的证书不能用于xxx.yyy.foo.com、不能用于foo.com\n" +
"4、输入一个空格之后再输入下一个",
})
domains!: string[];
@TaskInput({
title: "证书密码",
component: {
name: "input-password",
vModel: "value",
},
required: false,
order: 100,
helper: "PFX、jks格式证书是否加密\njks必须设置密码不传则默认123456\npfx不传则为空密码",
})
pfxPassword!: string;
@TaskInput({
title: "PFX证书转换参数",
value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES",
component: {
name: "a-auto-complete",
vModel: "value",
options: [
{ value: "", label: "兼容 Windows Server 最新" },
{ value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2016" },
{ value: "-nomac -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2008" },
],
},
required: false,
order: 100,
helper: "兼容Windows Server各个版本",
})
pfxArgs = "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES";
userContext!: IContext;
lastStatus!: Step;
@TaskOutput({
title: "域名证书",
})
cert?: CertInfo;
async onInstance() {
this.userContext = this.ctx.userContext;
this.lastStatus = this.ctx.lastStatus as Step;
await this.onInit();
}
abstract onInit(): Promise<void>;
async output(certReader: CertReader, isNew: boolean) {
const cert: CertInfo = certReader.toCertInfo();
this.cert = cert;
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf();
if (!this._result.pipelinePrivateVars) {
this._result.pipelinePrivateVars = {};
}
this._result.pipelinePrivateVars.cert = cert;
if (isNew) {
try {
const converter = new CertConverter({ logger: this.logger });
const res = await converter.convert({
cert,
pfxPassword: this.pfxPassword,
pfxArgs: this.pfxArgs,
});
if (cert.pfx == null && res.pfx) {
cert.pfx = res.pfx;
}
if (cert.der == null && res.der) {
cert.der = res.der;
}
if (cert.jks == null && res.jks) {
cert.jks = res.jks;
}
this.logger.info("转换证书格式成功");
} catch (e) {
this.logger.error("转换证书格式失败", e);
}
}
if (isNew) {
const zipFileName = certReader.buildCertFileName("zip", certReader.detail.notBefore);
await this.zipCert(cert, zipFileName);
} else {
this.extendsFiles();
}
}
async zipCert(cert: CertInfo, filename: string) {
const zip = new JSZip();
zip.file("证书.pem", cert.crt);
zip.file("私钥.pem", cert.key);
zip.file("中间证书.pem", cert.ic);
zip.file("cert.crt", cert.crt);
zip.file("cert.key", cert.key);
zip.file("intermediate.crt", cert.ic);
zip.file("origin.crt", cert.oc);
zip.file("one.pem", cert.one);
if (cert.pfx) {
zip.file("cert.pfx", Buffer.from(cert.pfx, "base64"));
}
if (cert.der) {
zip.file("cert.der", Buffer.from(cert.der, "base64"));
}
if (cert.jks) {
zip.file("cert.jks", Buffer.from(cert.jks, "base64"));
}
zip.file(
"说明.txt",
`证书文件说明
cert.crtpem
cert.keypem
intermediate.crtpem
origin.crtpem
one.pem pemcrt+key
cert.pfxpfxiis使
cert.derder
cert.jksjksjava使
`
);
const content = await zip.generateAsync({ type: "nodebuffer" });
this.saveFile(filename, content);
this.logger.info(`已保存文件:${filename}`);
}
formatCert(pem: string) {
pem = pem.replace(/\r/g, "");
pem = pem.replace(/\n\n/g, "\n");
pem = pem.replace(/\n$/g, "");
return pem;
}
formatCerts(cert: { crt: string; key: string; csr: string }) {
const newCert: CertInfo = {
crt: this.formatCert(cert.crt),
key: this.formatCert(cert.key),
csr: this.formatCert(cert.csr),
};
return newCert;
}
}

View File

@ -1,10 +1,8 @@
import { AbstractTaskPlugin, IContext, NotificationBody, Step, TaskEmitter, TaskInput, TaskOutput } from "@certd/pipeline";
import { NotificationBody, Step, TaskEmitter, TaskInput } from "@certd/pipeline";
import dayjs from "dayjs";
import type { CertInfo } from "./acme.js";
import { CertReader } from "./cert-reader.js";
import JSZip from "jszip";
import { CertConverter } from "./convert.js";
import { pick } from "lodash-es";
import { CertApplyBaseConvertPlugin } from "./base-convert.js";
export const EVENT_CERT_APPLY_SUCCESS = "CertApply.success";
@ -12,30 +10,7 @@ export async function emitCertApplySuccess(emitter: TaskEmitter, cert: CertReade
await emitter.emit(EVENT_CERT_APPLY_SUCCESS, cert);
}
export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
@TaskInput({
title: "域名",
component: {
name: "a-select",
vModel: "value",
mode: "tags",
open: false,
placeholder: "foo.com / *.foo.com / *.bar.com",
tokenSeparators: [",", " ", "", "、", "|"],
},
rules: [{ type: "domains" }],
required: true,
col: {
span: 24,
},
order: -999,
helper:
"1、支持多个域名打到一个证书上例如 foo.com*.foo.com*.bar.com\n" +
"2、子域名被通配符包含的不要填写例如www.foo.com已经被*.foo.com包含不要填写www.foo.com\n" +
"3、泛域名只能通配*号那一级(*.foo.com的证书不能用于xxx.yyy.foo.com、不能用于foo.com\n" +
"4、输入一个空格之后再输入下一个",
})
domains!: string[];
export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
@TaskInput({
title: "邮箱",
@ -50,36 +25,6 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
})
email!: string;
@TaskInput({
title: "证书密码",
component: {
name: "input-password",
vModel: "value",
},
required: false,
order: 100,
helper: "PFX、jks格式证书是否加密\njks必须设置密码不传则默认123456\npfx不传则为空密码",
})
pfxPassword!: string;
@TaskInput({
title: "PFX证书转换参数",
value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES",
component: {
name: "a-auto-complete",
vModel: "value",
options: [
{ value: "", label: "兼容 Windows Server 最新" },
{ value: "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2016" },
{ value: "-nomac -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES", label: "兼容 Windows Server 2008" },
],
},
required: false,
order: 100,
helper: "兼容Windows Server各个版本",
})
pfxArgs = "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES";
@TaskInput({
title: "更新天数",
value: 35,
@ -111,14 +56,6 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
// })
csrInfo!: string;
userContext!: IContext;
lastStatus!: Step;
@TaskOutput({
title: "域名证书",
})
cert?: CertInfo;
async onInstance() {
this.userContext = this.ctx.userContext;
this.lastStatus = this.ctx.lastStatus as Step;
@ -151,89 +88,6 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
}
}
async output(certReader: CertReader, isNew: boolean) {
const cert: CertInfo = certReader.toCertInfo();
this.cert = cert;
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf();
if (!this._result.pipelinePrivateVars) {
this._result.pipelinePrivateVars = {};
}
this._result.pipelinePrivateVars.cert = cert;
if (isNew) {
try {
const converter = new CertConverter({ logger: this.logger });
const res = await converter.convert({
cert,
pfxPassword: this.pfxPassword,
pfxArgs: this.pfxArgs,
});
if (cert.pfx == null && res.pfx) {
cert.pfx = res.pfx;
}
if (cert.der == null && res.der) {
cert.der = res.der;
}
if (cert.jks == null && res.jks) {
cert.jks = res.jks;
}
this.logger.info("转换证书格式成功");
} catch (e) {
this.logger.error("转换证书格式失败", e);
}
}
if (isNew) {
const zipFileName = certReader.buildCertFileName("zip", certReader.detail.notBefore);
await this.zipCert(cert, zipFileName);
} else {
this.extendsFiles();
}
}
async zipCert(cert: CertInfo, filename: string) {
const zip = new JSZip();
zip.file("证书.pem", cert.crt);
zip.file("私钥.pem", cert.key);
zip.file("中间证书.pem", cert.ic);
zip.file("cert.crt", cert.crt);
zip.file("cert.key", cert.key);
zip.file("intermediate.crt", cert.ic);
zip.file("origin.crt", cert.oc);
zip.file("one.pem", cert.one);
if (cert.pfx) {
zip.file("cert.pfx", Buffer.from(cert.pfx, "base64"));
}
if (cert.der) {
zip.file("cert.der", Buffer.from(cert.der, "base64"));
}
if (cert.jks) {
zip.file("cert.jks", Buffer.from(cert.jks, "base64"));
}
zip.file(
"说明.txt",
`证书文件说明
cert.crtpem
cert.keypem
intermediate.crtpem
origin.crtpem
one.pem pemcrt+key
cert.pfxpfxiis使
cert.derder
cert.jksjksjava使
`
);
const content = await zip.generateAsync({ type: "nodebuffer" });
this.saveFile(filename, content);
this.logger.info(`已保存文件:${filename}`);
}
/**
*
*/
@ -279,22 +133,6 @@ cert.jksjks格式证书文件java服务器使用
return null;
}
formatCert(pem: string) {
pem = pem.replace(/\r/g, "");
pem = pem.replace(/\n\n/g, "\n");
pem = pem.replace(/\n$/g, "");
return pem;
}
formatCerts(cert: { crt: string; key: string; csr: string }) {
const newCert: CertInfo = {
crt: this.formatCert(cert.crt),
key: this.formatCert(cert.key),
csr: this.formatCert(cert.csr),
};
return newCert;
}
async readLastCert(): Promise<CertReader | undefined> {
const cert = this.lastStatus?.status?.output?.cert;
if (cert == null) {

View File

@ -1,38 +1,53 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, Step, TaskInput } from "@certd/pipeline";
import { IsTaskPlugin, pluginGroups, RunStrategy, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import type { CertInfo } from "../acme.js";
import { CertReader } from "../cert-reader.js";
import { CertApplyBasePlugin } from "../base.js";
import { CertApplyBaseConvertPlugin } from "../base-convert.js";
import dayjs from "dayjs";
export { CertReader };
export type { CertInfo };
@IsTaskPlugin({
name: "CertUpload",
name: "CertApplyUpload",
icon: "ph:certificate",
title: "证书手动上传",
group: pluginGroups.cert.key,
desc: "在证书仓库手动上传后触发部署证书",
default: {
input: {
renewDays: 35,
forceUpdate: false,
},
strategy: {
runStrategy: RunStrategy.AlwaysRun,
},
},
})
export class CertUploadPlugin extends CertApplyBasePlugin {
export class CertApplyUploadPlugin extends CertApplyBaseConvertPlugin {
@TaskInput({
title: "证书仓库ID",
component: {
name: "a-cert-select",
vModel: "value",
name: "cert-info-selector",
vModel: "modelValue",
},
order: -9999,
required: true,
mergeScript: `
return {
component:{
on:{
selectedChange(scope){
console.log(scope)
scope.form.input.domains = scope.$event?.domains
}
}
}
}
`,
})
certInfoId!: string;
@TaskOutput({
title: "证书MD5",
})
certMd5?: string;
async onInstance() {
this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
@ -41,21 +56,45 @@ export class CertUploadPlugin extends CertApplyBasePlugin {
}
async onInit(): Promise<void> {}
async doCertApply() {
const siteInfoService = this.ctx.serviceContainer["CertInfoService"];
async getCertFromStore() {
const siteInfoService = await this.ctx.serviceGetter.get("CertInfoService");
const certInfo = await siteInfoService.getCertInfo({
certId: this.certInfoId,
userid: this.pipeline.userId,
userId: this.pipeline.userId,
});
const certReader = new CertReader(certInfo);
if (!certReader.expires && certReader.expires < new Date().getTime()) {
throw new Error("证书已过期,停止部署");
throw new Error("证书已过期,停止部署,请重新上传证书");
}
return certReader;
}
async execute(): Promise<string | void> {
const certReader = await this.getCertFromStore();
const crtMd5 = this.ctx.utils.hash.md5(certReader.cert.crt);
const leftDays = dayjs(certReader.expires).diff(dayjs(), "day");
this.logger.info(`证书过期时间${dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${leftDays}`);
const lastCrtMd5 = this.lastStatus.status.output?.certMd5;
this.logger.info("证书MD5", crtMd5);
this.logger.info("上次证书MD5", lastCrtMd5);
if (lastCrtMd5 === crtMd5) {
this.logger.info("证书无变化,跳过");
//输出证书MD5
this.certMd5 = crtMd5;
await this.output(certReader, false);
return "skip";
}
this.logger.info("证书有变化,重新部署");
this.clearLastStatus();
//输出证书MD5
this.certMd5 = crtMd5;
await this.output(certReader, true);
return;
}
}
new CertUploadPlugin();
new CertApplyUploadPlugin();

View File

@ -1,2 +1,4 @@
export * from "./cert-plugin/index.js";
export * from "./cert-plugin/lego/index.js";
export * from "./cert-plugin/custom/index.js";
export const CertApplyPluginNames = ["CertApply", "CertApplyLego", "CertApplyUpload"];

View File

@ -17,7 +17,6 @@
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }]
"@typescript-eslint/no-unused-vars": "off"
}
}

View File

@ -1,3 +1,7 @@
{
"printWidth": 160
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -3,34 +3,34 @@ module.exports = {
env: {
browser: true,
node: true,
es6: true
es6: true,
},
parser: "vue-eslint-parser",
parser: 'vue-eslint-parser',
parserOptions: {
parser: "@typescript-eslint/parser",
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: "module",
jsxPragma: "React",
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true,
tsx: true
}
tsx: true,
},
},
extends: ["plugin:vue/vue3-recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended", "prettier"],
extends: ['plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'prettier'],
rules: {
//"max-len": [0, 200, 2, { ignoreUrls: true }],
"@typescript-eslint/no-unused-vars": "off",
"no-unused-vars": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
'@typescript-eslint/no-unused-vars': 'off',
'no-unused-vars': 'off',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
// "@typescript-eslint/no-unused-vars": [
// "error",
// {
@ -69,5 +69,5 @@ module.exports = {
// math: "always",
// },
// ],
}
},
};

View File

@ -1,5 +1,7 @@
{
"trailingComma": "none",
"printWidth": 220
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -5,6 +5,7 @@ import OutputSelector from "/@/components/plugins/common/output-selector/index.v
import DnsProviderSelector from "/@/components/plugins/cert/dns-provider-selector/index.vue";
import DomainsVerifyPlanEditor from "/@/components/plugins/cert/domains-verify-plan-editor/index.vue";
import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
import CertInfoSelector from "/@/views/certd/monitor/cert/selector/index.vue";
import InputPassword from "./common/input-password.vue";
import ApiTest from "./common/api-test.vue";
export * from "./cert/index.js";
@ -14,6 +15,8 @@ export default {
app.component("DnsProviderSelector", DnsProviderSelector);
app.component("DomainsVerifyPlanEditor", DomainsVerifyPlanEditor);
app.component("AccessSelector", AccessSelector);
app.component("CertInfoSelector", CertInfoSelector);
app.component("ApiTest", ApiTest);
app.component("SynologyDeviceIdGetter", SynologyIdDeviceGetter);

View File

@ -7,7 +7,7 @@ export const certInfoApi = {
return await request({
url: apiPrefix + "/page",
method: "post",
data: query
data: query,
});
},
@ -15,7 +15,7 @@ export const certInfoApi = {
return await request({
url: apiPrefix + "/add",
method: "post",
data: obj
data: obj,
});
},
@ -23,7 +23,7 @@ export const certInfoApi = {
return await request({
url: apiPrefix + "/update",
method: "post",
data: obj
data: obj,
});
},
@ -31,7 +31,7 @@ export const certInfoApi = {
return await request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
params: { id },
});
},
@ -39,27 +39,35 @@ export const certInfoApi = {
return await request({
url: apiPrefix + "/info",
method: "post",
params: { id }
params: { id },
});
},
async ListAll() {
return await request({
url: apiPrefix + "/all",
method: "post"
method: "post",
});
},
async Upload(body: { id?: number; cert: { crt: string; key: string } }) {
return await request({
url: apiPrefix + "/upload",
method: "post",
data: body
data: body,
});
},
async GetCert(id: number): Promise<CertInfo> {
return await request({
url: apiPrefix + "/getCert",
method: "post",
params: { id: id }
params: { id: id },
});
}
},
async GetOptionsByIds(ids: number[]): Promise<any[]> {
return await request({
url: apiPrefix + "/getOptionsByIds",
method: "post",
data: { ids },
});
},
};

View File

@ -1,6 +1,17 @@
// @ts-ignore
import { useI18n } from "vue-i18n";
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, useFormWrapper, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import {
AddReq,
compute,
CreateCrudOptionsProps,
CreateCrudOptionsRet,
DelReq,
dict,
EditReq,
useFormWrapper,
UserPageQuery,
UserPageRes
} from "@fast-crud/fast-crud";
import { certInfoApi } from "./api";
import dayjs from "dayjs";
import { useRouter } from "vue-router";
@ -9,7 +20,6 @@ import { notification } from "ant-design-vue";
import CertView from "/@/views/certd/pipeline/cert-view.vue";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n();
const api = certInfoApi;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
@ -48,7 +58,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
width: 800,
content: () => {
return <CertView cert={cert}></CertView>;
}
},
});
};
@ -69,35 +79,35 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
title: "ID",
type: "number",
form: {
show: false
}
show: false,
},
},
"cert.crt": {
title: "证书",
type: "textarea",
form: {
component: {
rows: 4
rows: 4,
},
rules: [{ required: true, message: "此项必填" }],
col: { span: 24 }
}
col: { span: 24 },
},
},
"cert.key": {
title: "私钥",
type: "textarea",
form: {
component: {
rows: 4
rows: 4,
},
rules: [{ required: true, message: "此项必填" }],
col: { span: 24 }
}
}
col: { span: 24 },
},
},
},
form: {
wrapper: {
title: "上传自定义证书"
title: "上传自定义证书",
},
async doSubmit({ form }: any) {
if (!id) {
@ -106,9 +116,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
form.id = id;
}
return await api.Upload(form);
}
}
}
},
},
},
};
}
const { crudOptions } = createCrudOptions();
@ -121,22 +131,22 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
pageRequest,
addRequest,
editRequest,
delRequest
delRequest,
},
form: {
labelCol: {
//固定label宽度
span: null,
style: {
width: "100px"
}
width: "100px",
},
},
col: {
span: 22
span: 22,
},
wrapper: {
width: 600
}
width: 600,
},
},
actionbar: {
show: true,
@ -147,13 +157,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: true,
async click() {
await openUpload();
}
}
}
},
},
},
},
tabs: {
name: "fromType",
show: true
show: true,
},
rowHandle: {
width: 140,
@ -167,7 +177,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
icon: "ph:certificate",
async click({ row }) {
await viewCert(row);
}
},
},
copy: { show: false },
edit: { show: false },
@ -181,15 +191,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
icon: "ph:upload",
async click({ row }) {
await openUpload(row.id);
}
},
},
remove: {
order: 10,
show: compute(({ row }) => {
return row.fromType === "upload";
})
}
}
}),
},
},
},
columns: {
id: {
@ -197,85 +207,85 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
key: "id",
type: "number",
search: {
show: false
show: false,
},
column: {
width: 100,
editable: {
disabled: true
}
disabled: true,
},
},
form: {
show: false
}
show: false,
},
},
fromType: {
title: "来源",
search: {
show: true
show: true,
},
type: "dict-select",
dict: dict({
data: [
{ label: "流水线", value: "pipeline" },
{ label: "手动上传", value: "upload" }
]
{ label: "手动上传", value: "upload" },
],
}),
form: {
show: false
show: false,
},
column: {
width: 100,
sorter: true,
component: {
color: "auto"
color: "auto",
},
conditionalRender: false
conditionalRender: false,
},
valueBuilder({ value, row, key }) {
if (!value) {
row[key] = "pipeline";
}
}
},
},
domains: {
title: "域名",
search: {
show: true
show: true,
},
type: "text",
form: {
rules: [{ required: true, message: "请输入域名" }]
rules: [{ required: true, message: "请输入域名" }],
},
column: {
width: 450,
sorter: true,
component: {
name: "fs-values-format",
color: "auto"
}
}
color: "auto",
},
},
},
domainCount: {
title: "域名数量",
type: "number",
form: {
show: false
show: false,
},
column: {
width: 120,
sorter: true,
show: false
}
show: false,
},
},
expiresLeft: {
title: "有效天数",
search: {
show: false
show: false,
},
type: "date",
form: {
show: false
show: false,
},
column: {
sorter: true,
@ -290,54 +300,54 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const color = leftDays < 20 ? "red" : "#389e0d";
const percent = (leftDays / 90) * 100;
return <a-progress title={expireDate + "过期"} percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}`} />;
}
}
},
},
},
expiresTime: {
title: "过期时间",
search: {
show: false
show: false,
},
type: "datetime",
form: {
show: false
show: false,
},
column: {
sorter: true
}
sorter: true,
},
},
certProvider: {
title: "证书颁发机构",
search: {
show: false
show: false,
},
type: "text",
form: {
show: false
show: false,
},
column: {
width: 200
}
width: 200,
},
},
applyTime: {
title: "申请时间",
search: {
show: false
show: false,
},
type: "datetime",
form: {
show: false
show: false,
},
column: {
sorter: true
}
sorter: true,
},
},
"pipeline.title": {
title: "关联流水线",
search: { show: false },
type: "link",
form: {
show: false
show: false,
},
column: {
width: 350,
@ -346,12 +356,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
on: {
onClick({ row }) {
router.push({ path: "/certd/pipeline/detail", query: { id: row.pipelineId, editMode: "false" } });
}
}
}
}
}
}
}
},
},
},
},
},
},
},
};
}

View File

@ -0,0 +1,123 @@
<template>
<div class="cert-info-selector w-full">
<div class="flex-o w-full">
<fs-table-select
ref="tableSelectRef"
class="flex-0"
:model-value="modelValue"
:dict="optionsDictRef"
:create-crud-options="createCrudOptions"
:crud-options-override="{
search: { show: false },
table: {
scroll: {
x: 540,
},
},
}"
:show-current="false"
:show-select="false"
:dialog="{ width: 960 }"
:destroy-on-close="false"
height="400px"
@update:model-value="onChange"
@dialog-closed="doRefresh"
@selected-change="onSelectedChange"
/>
</div>
</div>
</template>
<script lang="tsx" setup>
import { inject, ref, Ref, watch } from "vue";
import { message } from "ant-design-vue";
import createCrudOptions from "../crud";
import { dict } from "@fast-crud/fast-crud";
import { certInfoApi } from "../api";
defineOptions({
name: "CertInfoSelector",
});
const props = defineProps<{
modelValue?: number | string;
type?: string;
placeholder?: string;
size?: string;
disabled?: boolean;
}>();
const onChange = async (value: number) => {
await emitValue(value);
};
const emit = defineEmits(["update:modelValue", "selectedChange", "change"]);
const tableSelectRef = ref();
const optionsDictRef = dict({
value: "id",
label: "domain",
getNodesByValues: async (values: any[]) => {
return await certInfoApi.GetOptionsByIds(values);
},
});
// async function openTableSelectDialog() {
// await tableSelectRef.value.open({});
// await tableSelectRef.value.crudExpose.openAdd({});
// }
const target: Ref<any> = ref({});
function clear() {
if (props.disabled) {
return;
}
emitValue(null);
}
async function emitValue(value: any) {
target.value = optionsDictRef.dataMap[value];
if (value !== 0 && pipeline?.value && target && pipeline.value.userId !== target.value.userId) {
message.error("对不起您不能修改他人流水线的证书仓库ID");
return;
}
emit("change", value);
emit("update:modelValue", value);
}
function onSelectedChange(value: any) {
if (value && value.length > 0) {
emit("selectedChange", value[0]);
} else {
emit("selectedChange", null);
}
}
// watch(
// () => {
// return props.modelValue;
// },
// async value => {
// await optionsDictRef.loadDict();
// target.value = optionsDictRef.dataMap[value];
// emit("selectedChange", target.value);
// },
// {
// immediate: true,
// }
// );
//pipeline
const pipeline = inject("pipeline", null);
async function doRefresh() {
await optionsDictRef.reloadDict();
}
</script>
<style lang="less">
.cert-info-selector {
width: 100%;
}
</style>

View File

@ -1,16 +1,7 @@
<template>
<div class="notification-selector">
<div class="flex-o w-100">
<fs-dict-select
class="flex-1"
:value="modelValue"
:dict="optionsDictRef"
:disabled="disabled"
:render-label="renderLabel"
:slots="selectSlots"
:allow-clear="true"
@update:value="onChange"
/>
<fs-dict-select class="flex-1" :value="modelValue" :dict="optionsDictRef" :disabled="disabled" :render-label="renderLabel" :slots="selectSlots" :allow-clear="true" @update:value="onChange" />
<fs-table-select
ref="tableSelectRef"
class="flex-0"
@ -18,12 +9,12 @@
:dict="optionsDictRef"
:create-crud-options="createCrudOptions"
:crud-options-override="{
search: { show: false },
search: { show: false, initialForm: { fromType: 'upload' } },
table: {
scroll: {
x: 540
}
}
x: 540,
},
},
}"
:show-current="false"
:show-select="false"
@ -50,7 +41,7 @@ import createCrudOptions from "../crud";
import { notificationProvide } from "/@/views/certd/notification/common";
defineOptions({
name: "NotificationSelector"
name: "NotificationSelector",
});
const props = defineProps<{
@ -89,12 +80,12 @@ const optionsDictRef = dict({
{
id: 0,
name: "使用默认通知",
icon: "ion:notifications"
icon: "ion:notifications",
},
...dict.data
...dict.data,
];
dict.setData(data);
}
},
});
const renderLabel = (option: any) => {
return <span>{option.name}</span>;
@ -115,7 +106,7 @@ const selectSlots = ref({
// res.push(<a-space style="padding: 4px 8px" />);
// res.push(<fs-button class="w-100" type="text" icon="plus-outlined" text="" onClick={openTableSelectDialog}></fs-button>);
return res;
}
},
});
const target: Ref<any> = ref({});
@ -141,13 +132,13 @@ watch(
() => {
return props.modelValue;
},
async (value) => {
async value => {
await optionsDictRef.loadDict();
target.value = optionsDictRef.dataMap[value];
emit("selectedChange", target.value);
},
{
immediate: true
immediate: true,
}
);

View File

@ -27,7 +27,7 @@ export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOpt
title: inputDefine.title,
form: {
...inputDefine,
show: compute((ctx) => {
show: compute(ctx => {
const form = formWrapperRef.value.getFormData();
if (!form) {
return false;
@ -43,8 +43,8 @@ export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOpt
}
}
return form?.certApplyPlugin === plugin.name && inputDefineShow;
})
}
}),
},
};
}
}
@ -57,17 +57,17 @@ export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOpt
wrapper: {
width: 1350,
saveRemind: false,
title: "创建证书流水线"
title: "创建证书流水线",
},
group: {
groups: {
more: {
header: "更多参数",
columns: moreParams,
collapsed: true
}
}
}
collapsed: true,
},
},
},
},
columns: {
certApplyPlugin: {
@ -76,8 +76,8 @@ export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOpt
dict: dict({
data: [
{ value: "CertApply", label: "JS-ACME" },
{ value: "CertApplyLego", label: "Lego-ACME" }
]
{ value: "CertApplyLego", label: "Lego-ACME" },
],
}),
form: {
order: 0,
@ -90,21 +90,21 @@ export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOpt
<li>Lego-ACMELegoDNSLEGO使</li>
</ul>
);
}
},
},
valueChange: {
handle: async ({ form, value }) => {
const config = await api.GetPluginConfig({
name: value,
type: "builtIn"
type: "builtIn",
});
if (config.sysSetting?.input) {
merge(form, config.sysSetting.input);
}
},
immediate: true
}
}
immediate: true,
},
},
},
...inputs,
triggerCron: {
@ -115,11 +115,11 @@ export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOpt
component: {
name: "cron-editor",
vModel: "modelValue",
placeholder: "0 0 4 * * *"
placeholder: "0 0 4 * * *",
},
helper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次证书未到期之前任务会跳过不会重复执行",
order: 100
}
order: 100,
},
},
notification: {
title: "失败通知",
@ -132,14 +132,14 @@ export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOpt
on: {
selectedChange({ $event, form }) {
form.notificationTarget = $event;
}
}
},
},
},
order: 101,
helper: "任务执行失败实时提醒"
}
}
}
}
helper: "任务执行失败实时提醒",
},
},
},
},
};
}

View File

@ -76,7 +76,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
const addRequest = async ({ form }: AddReq) => {
if (form.content == null) {
form.content = JSON.stringify({
title: form.title
title: form.title,
});
} else {
//复制的流水线
@ -106,7 +106,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
const max = suiteDetail.pipelineCount.max;
if (max != -1 && max <= suiteDetail.pipelineCount.used) {
notification.error({
message: `对不起,您最多只能创建${max}条流水线,请购买或升级套餐`
message: `对不起,您最多只能创建${max}条流水线,请购买或升级套餐`,
});
return;
}
@ -124,7 +124,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
type: "custom",
when: ["error", "turnToSuccess", "success"],
notificationId: form.notification,
title: form.notificationTarget?.name || "自定义通知"
title: form.notificationTarget?.name || "自定义通知",
});
}
let pipeline = {
@ -145,20 +145,20 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
runnableType: "step",
input: {
renewDays: 35,
...form
...form,
},
strategy: {
runStrategy: 0 // 正常执行
runStrategy: 0, // 正常执行
},
type: form.certApplyPlugin
}
]
}
]
}
type: form.certApplyPlugin,
},
],
},
],
},
],
triggers,
notifications
notifications,
};
pipeline = setRunnableIds(pipeline);
@ -173,7 +173,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
content: JSON.stringify(pipeline),
keepHistoryCount: 30,
type: "cert",
from: "custom"
from: "custom",
});
message.success("创建成功,请添加证书部署任务");
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
@ -195,7 +195,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
width: 800,
content: () => {
return <CertView cert={cert}></CertView>;
}
},
});
};
@ -230,7 +230,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
<div> {children}</div>
</div>
);
}
},
});
};
const userStore = useUserStore();
@ -242,7 +242,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
pageRequest,
addRequest,
editRequest,
delRequest
delRequest,
},
settings: {
plugins: {
@ -259,16 +259,16 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
selectedRowKeys,
onSelectedChanged(selected) {
console.log("已选择变化:", selected);
}
}
}
}
},
},
},
},
},
actionbar: {
buttons: {
add: {
order: 5,
text: "自定义流水线"
text: "自定义流水线",
},
addCertd: {
order: 1,
@ -276,29 +276,29 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
type: "primary",
click() {
addCertdPipeline();
}
}
}
},
},
},
},
form: {
afterSubmit({ form, res, mode }) {
if (mode === "add") {
router.push({ path: "/certd/pipeline/detail", query: { id: res.id, editMode: "true" } });
}
}
},
},
table: {
scroll: { x: 1500 }
scroll: { x: 1500 },
},
tabs: {
name: "groupId",
show: true
show: true,
},
rowHandle: {
width: 200,
fixed: "right",
dropdown: {
show: true
show: true,
},
buttons: {
play: {
@ -313,18 +313,18 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
async onOk() {
await api.Trigger(row.id);
notification.success({ message: "管道已经开始运行" });
}
},
});
}
},
},
view: {
show: false,
click({ row }) {
router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "false" } });
}
},
},
copy: {
click: async (context) => {
click: async context => {
settingStore.checkPlus();
const { ui } = useUi();
// @ts-ignore
@ -333,10 +333,10 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
row.title = row.title + "_copy";
await crudExpose.openCopy({
row: row,
index: context.index
index: context.index,
});
},
class: "need-plus"
class: "need-plus",
},
config: {
order: 1,
@ -346,13 +346,13 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
icon: "ant-design:edit-outlined",
click({ row }) {
router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "true" } });
}
},
},
edit: {
order: 2,
title: "修改配置/分组",
icon: "ant-design:setting-outlined",
dropdown: true
dropdown: true,
},
viewCert: {
order: 3,
@ -361,7 +361,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
icon: "ph:certificate",
async click({ row }) {
await viewCert(row);
}
},
},
download: {
order: 4,
@ -370,13 +370,13 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
icon: "ant-design:download-outlined",
async click({ row }) {
await downloadCert(row);
}
},
},
remove: {
order: 5,
dropdown: true
}
}
dropdown: true,
},
},
},
columns: {
id: {
@ -384,14 +384,14 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
key: "id",
type: "number",
search: {
show: true
show: true,
},
column: {
width: 100
width: 100,
},
form: {
show: false
}
show: false,
},
},
userId: {
title: "用户Id",
@ -399,17 +399,17 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
search: {
show: computed(() => {
return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline;
})
}),
},
form: {
show: false
show: false,
},
column: {
show: computed(() => {
return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline;
}),
width: 100
}
width: 100,
},
},
title: {
title: "流水线名称",
@ -418,11 +418,11 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
show: true,
title: "关键字",
component: {
name: "a-input"
}
name: "a-input",
},
},
form: {
rules: [{ required: true, message: "此项必填" }]
rules: [{ required: true, message: "此项必填" }],
},
column: {
width: 350,
@ -432,16 +432,16 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
// 注意必须要on前缀
onClick({ row }) {
router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "false" } });
}
}
}
}
},
},
},
},
},
content: {
title: "流水线内容",
form: { show: false },
column: {
show: false
show: false,
},
valueBuilder({ row }) {
if (row.content) {
@ -463,18 +463,18 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
if (row.content) {
row.content = JSON.stringify(row.content);
}
}
},
},
_triggerCount: {
title: "定时任务数",
type: "number",
column: {
align: "center",
width: 100
width: 100,
},
form: {
show: false
}
show: false,
},
},
_stepCount: {
title: "部署任务数",
@ -482,14 +482,14 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
form: { show: false },
column: {
align: "center",
width: 100
}
width: 100,
},
},
lastVars: {
title: "到期剩余",
type: "number",
form: {
show: false
show: false,
},
column: {
cellRender({ row }) {
@ -501,52 +501,52 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
const percent = (leftDays / 90) * 100;
return <a-progress percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}`} />;
},
width: 150
}
width: 150,
},
},
"lastVars.certExpiresTime": {
title: "过期时间",
search: {
show: false
show: false,
},
type: "datetime",
form: {
show: false
show: false,
},
column: {
sorter: true,
align: "center"
}
align: "center",
},
},
status: {
title: "状态",
type: "dict-select",
search: {
show: true
show: true,
},
dict: dict({
data: statusUtil.getOptions()
data: statusUtil.getOptions(),
}),
form: {
show: false
show: false,
},
column: {
sorter: true,
width: 120,
align: "center"
}
align: "center",
},
},
lastHistoryTime: {
title: "最后运行",
type: "datetime",
form: {
show: false
show: false,
},
column: {
sorter: true,
width: 150,
align: "center"
}
align: "center",
},
},
disabled: {
title: "启用",
@ -554,12 +554,12 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
dict: dict({
data: [
{ value: false, label: "启用" },
{ value: true, label: "禁用" }
]
{ value: true, label: "禁用" },
],
}),
form: {
value: false,
show: false
show: false,
},
column: {
sorter: true,
@ -567,30 +567,30 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
align: "center",
component: {
name: "fs-dict-switch",
vModel: "checked"
vModel: "checked",
},
async valueChange({ row, key, value }) {
return await api.UpdateObj({
id: row.id,
disabled: row[key]
disabled: row[key],
});
}
}
},
},
},
groupId: {
title: "分组",
type: "dict-select",
search: {
show: true
show: true,
},
dict: groupDictRef,
column: {
width: 130,
align: "center",
component: {
color: "auto"
}
}
color: "auto",
},
},
},
order: {
title: "排序号",
@ -598,48 +598,48 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
column: {
sorter: true,
align: "center",
width: 80
width: 80,
},
form: {
value: 0
}
value: 0,
},
},
keepHistoryCount: {
title: "历史记录保持数",
type: "number",
form: {
value: 20,
helper: "历史记录保持条数,多余的会被删除"
helper: "历史记录保持条数,多余的会被删除",
},
column: {
width: 130,
show: false
}
show: false,
},
},
createTime: {
title: "创建时间",
type: "datetime",
form: {
show: false
show: false,
},
column: {
sorter: true,
width: 155,
align: "center"
}
align: "center",
},
},
updateTime: {
title: "更新时间",
type: "datetime",
form: {
show: false
show: false,
},
column: {
width: 125,
show: false
}
}
}
}
show: false,
},
},
},
},
};
}

View File

@ -16,8 +16,6 @@
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-function": "off",
// "no-unused-expressions": "off",
"max-len": [0, 160, 2, { "ignoreUrls": true }]
"@typescript-eslint/no-empty-function": "off"
}
}

View File

@ -0,0 +1,7 @@
{
"printWidth": 220,
"bracketSpacing": true,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -3,6 +3,9 @@ import { Constants, CrudController } from '@certd/lib-server';
import { AuthService } from '../../../modules/sys/authority/service/auth-service.js';
import { CertInfoService } from '../../../modules/monitor/index.js';
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js';
import { SelectQueryBuilder } from "typeorm";
import { CertUploadService } from "../../../modules/monitor/service/cert-upload-service.js";
import { CertInfo } from "@certd/plugin-cert";
/**
*/
@ -14,6 +17,8 @@ export class CertInfoController extends CrudController<CertInfoService> {
@Inject()
authService: AuthService;
@Inject()
certUploadService: CertUploadService;
@Inject()
pipelineService: PipelineService;
getService(): CertInfoService {
@ -57,6 +62,31 @@ export class CertInfoController extends CrudController<CertInfoService> {
return await super.list(body);
}
@Post('/getOptionsByIds', { summary: Constants.per.authOnly })
async getOptionsByIds(@Body(ALL) body: {ids:any[]}) {
const list = await this.service.list({
query:{
userId: this.getUserId(),
},
buildQuery: (bq: SelectQueryBuilder<any>) => {
bq.andWhere('id in (:...ids)', { ids: body.ids });
}
})
const safeList =list.map((item:any) => {
const domainsArr = item.domains? item.domains.split(',') : [];
return {
id: item.id,
domain: item.domain,
domains:domainsArr,
userId: item.userId,
}
})
return this.ok(safeList);
}
@Post('/add', { summary: Constants.per.authOnly })
async add(@Body(ALL) bean: any) {
bean.userId = this.getUserId();
@ -92,18 +122,25 @@ export class CertInfoController extends CrudController<CertInfoService> {
}
@Post('/upload', { summary: Constants.per.authOnly })
async upload(@Body(ALL) body: any) {
async upload(@Body(ALL) body: {cert: CertInfo, pipeline: any, id?: number}) {
if (body.id) {
//修改
await this.service.checkUserId(body.id, this.getUserId());
await this.certUploadService.updateCert({
id: body.id,
userId: this.getUserId(),
cert: body.cert,
});
}else{
//添加
body.userId = this.getUserId();
await this.certUploadService.createUploadCertPipeline({
userId: this.getUserId(),
cert: body.cert,
});
}
const res = await this.service.upload(body);
return this.ok(res);
return this.ok();
}
@Post('/getCert', { summary: Constants.per.authOnly })

View File

@ -1,10 +1,10 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { BaseService, CodeException, CommonException, Constants, PageReq } from '@certd/lib-server';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { CertInfoEntity } from '../entity/cert-info.js';
import { utils } from '@certd/basic';
import { CertInfo, CertReader } from '@certd/plugin-cert';
import { Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { BaseService, CodeException, Constants, PageReq } from "@certd/lib-server";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { Repository } from "typeorm";
import { CertInfoEntity } from "../entity/cert-info.js";
import { utils } from "@certd/basic";
import { CertInfo, CertReader } from "@certd/plugin-cert";
export type UploadCertReq = {
id?: number;
@ -13,6 +13,7 @@ export type UploadCertReq = {
userId?: number;
};
@Provide("CertInfoService")
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class CertInfoService extends BaseService<CertInfoEntity> {
@ -168,17 +169,4 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
return bean;
}
async upload(body: { id?: number; userId?:number ;cert: CertInfo }) {
const { id, userId, cert } = body;
if (!cert) {
throw new CommonException("cert can't be empty");
}
const res = await this.updateCert({
id,
certReader: new CertReader(cert),
fromType: 'upload',
userId
});
return res.id;
}
}

View File

@ -0,0 +1,178 @@
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { BaseService, CommonException } from "@certd/lib-server";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { EntityManager, Repository } from "typeorm";
import { CertInfoEntity } from "../entity/cert-info.js";
import { logger } from "@certd/basic";
import { CertInfo, CertReader } from "@certd/plugin-cert";
import { PipelineService } from "../../pipeline/service/pipeline-service.js";
import { CertInfoService } from "./cert-info-service.js";
import { PipelineEntity } from "../../pipeline/entity/pipeline.js";
import { nanoid } from "nanoid";
export type UploadCertReq = {
id?: number;
certReader: CertReader;
fromType?: string;
userId?: number;
};
export type UpdateCertReq = {
id: number;
cert: CertInfo;
userId?: number;
};
export type CreateUploadPipelineReq = {
cert: CertInfo;
userId: number;
};
@Provide("CertUploadService")
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class CertUploadService extends BaseService<CertInfoEntity> {
@InjectEntityModel(CertInfoEntity)
repository: Repository<CertInfoEntity>;
@Inject()
pipelineService: PipelineService;
@Inject()
certInfoService: CertInfoService;
//@ts-ignore
getRepository() {
return this.repository;
}
/**
* 线
* @param req
*/
async updateCert(req: UpdateCertReq) {
const certInfoEntity = await this.certInfoService.info(req.id);
if (!certInfoEntity) {
throw new CommonException("cert not found");
}
if(certInfoEntity.fromType !== 'upload') {
throw new CommonException("cert can't be custom upload");
}
await this.uploadCert(this.repository.manager,{
id: req.id,
fromType: 'upload',
userId: req.userId,
certReader: new CertReader(req.cert)
})
if (certInfoEntity.pipelineId) {
logger.info( `触发流水线部署:${certInfoEntity.pipelineId}`)
await this.pipelineService.trigger(certInfoEntity.pipelineId)
}
}
async createUploadCertPipeline(body:CreateUploadPipelineReq) {
const { userId, cert } = body;
if (!cert) {
throw new CommonException("cert can't be empty");
}
const certReader = new CertReader(cert)
return await this.transaction(async (tx:EntityManager)=>{
const newCertInfo = await this.uploadCert(tx,{
certReader: certReader,
fromType: 'upload',
userId
});
const pipelineTitle = certReader.getAllDomains()[0] +"上传证书自动部署";
const notifications = [];
notifications.push({
type: "custom",
when: ["error", "turnToSuccess", "success"],
notificationId: 0,
title: "默认通知",
});
let pipeline = {
id: nanoid(10),
title: pipelineTitle,
runnableType: "pipeline",
stages: [
{
id: nanoid(10),
title: "上传证书解析阶段",
maxTaskCount: 1,
runnableType: "stage",
tasks: [
{
id: nanoid(10),
title: "上传证书解析任务",
runnableType: "task",
steps: [
{
id: nanoid(10),
title: "上传证书解析",
runnableType: "step",
input: {
certInfoId: newCertInfo.id,
domains: newCertInfo.domains.split(','),
},
strategy: {
runStrategy: 0, // 正常执行
},
type: "CertApplyUpload",
},
],
},
],
},
],
triggers:[],
notifications,
}
const newPipeline = await tx.getRepository(PipelineEntity).save({
userId,
title: pipelineTitle,
type:"cert",
from:"cert_upload",
content: JSON.stringify(pipeline),
keepHistory:20,
})
newCertInfo.pipelineId = newPipeline.id;
await tx.getRepository(CertInfoEntity).save({
id: newCertInfo.id,
pipelineId: newPipeline.id
});
return newCertInfo.id
})
}
private async uploadCert(tx:EntityManager,req: UploadCertReq) {
const bean = new CertInfoEntity();
const { id, fromType,userId, certReader } = req;
if (id) {
bean.id = id;
} else {
bean.fromType = fromType;
bean.userId = userId
}
const certInfo = certReader.toCertInfo();
bean.certInfo = JSON.stringify(certInfo);
bean.applyTime = new Date().getTime();
const domains = certReader.detail.domains.altNames;
bean.domains = domains.join(',');
bean.domain = domains[0];
bean.domainCount = domains.length;
bean.expiresTime = certReader.expires;
bean.certProvider = certReader.detail.issuer.commonName;
await tx.getRepository(CertInfoEntity).save(bean);
return bean;
}
}

View File

@ -1,6 +1,6 @@
import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { In, MoreThan, Repository } from 'typeorm';
import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from "@midwayjs/core";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { In, MoreThan, Repository } from "typeorm";
import {
AccessGetter,
AccessService,
@ -10,35 +10,37 @@ import {
PageReq,
SysPublicSettings,
SysSettingsService,
SysSiteInfo,
} from '@certd/lib-server';
import { PipelineEntity } from '../entity/pipeline.js';
import { PipelineDetail } from '../entity/vo/pipeline-detail.js';
import { Executor, Pipeline, ResultType, RunHistory, RunnableCollection, SysInfo, UserInfo } from '@certd/pipeline';
import { DbStorage } from './db-storage.js';
import { StorageService } from './storage-service.js';
import { Cron } from '../../cron/cron.js';
import { HistoryService } from './history-service.js';
import { HistoryEntity } from '../entity/history.js';
import { HistoryLogEntity } from '../entity/history-log.js';
import { HistoryLogService } from './history-log-service.js';
import { EmailService } from '../../basic/service/email-service.js';
import { UserService } from '../../sys/authority/service/user-service.js';
import { CnameRecordService } from '../../cname/service/cname-record-service.js';
import { CnameProxyService } from './cname-proxy-service.js';
import { PluginConfigGetter } from '../../plugin/service/plugin-config-getter.js';
import dayjs from 'dayjs';
import { DbAdapter } from '../../db/index.js';
import { isComm } from '@certd/plus-core';
import { logger } from '@certd/basic';
import { UrlService } from './url-service.js';
import { NotificationService } from './notification-service.js';
import { NotificationGetter } from './notification-getter.js';
import { UserSuiteEntity, UserSuiteService } from '@certd/commercial-core';
import { CertInfoService } from '../../monitor/service/cert-info-service.js';
SysSiteInfo
} from "@certd/lib-server";
import { PipelineEntity } from "../entity/pipeline.js";
import { PipelineDetail } from "../entity/vo/pipeline-detail.js";
import { Executor, Pipeline, ResultType, RunHistory, RunnableCollection, SysInfo, UserInfo } from "@certd/pipeline";
import { DbStorage } from "./db-storage.js";
import { StorageService } from "./storage-service.js";
import { Cron } from "../../cron/cron.js";
import { HistoryService } from "./history-service.js";
import { HistoryEntity } from "../entity/history.js";
import { HistoryLogEntity } from "../entity/history-log.js";
import { HistoryLogService } from "./history-log-service.js";
import { EmailService } from "../../basic/service/email-service.js";
import { UserService } from "../../sys/authority/service/user-service.js";
import { CnameRecordService } from "../../cname/service/cname-record-service.js";
import { CnameProxyService } from "./cname-proxy-service.js";
import { PluginConfigGetter } from "../../plugin/service/plugin-config-getter.js";
import dayjs from "dayjs";
import { DbAdapter } from "../../db/index.js";
import { isComm } from "@certd/plus-core";
import { logger } from "@certd/basic";
import { UrlService } from "./url-service.js";
import { NotificationService } from "./notification-service.js";
import { NotificationGetter } from "./notification-getter.js";
import { UserSuiteEntity, UserSuiteService } from "@certd/commercial-core";
import { CertInfoService } from "../../monitor/service/cert-info-service.js";
const runningTasks: Map<string | number, Executor> = new Map();
/**
*
*/
@ -191,7 +193,10 @@ export class PipelineService extends BaseService<PipelineEntity> {
await this.registerTriggerById(bean.id);
//保存域名信息到certInfo表
await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains);
if(bean.from !== 'cert_upload'){
await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains);
}
return bean;
}
@ -478,8 +483,10 @@ export class PipelineService extends BaseService<PipelineEntity> {
const serviceContainer = {
CertInfoService: this.certInfoService
}
const serviceGetter = (name: string) => {
return serviceContainer[name]
const serviceGetter = {
get:(name: string) => {
return serviceContainer[name]
}
}
const executor = new Executor({
user,
@ -684,4 +691,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
},
});
}
}

View File

@ -1,5 +1,5 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { CertInfo ,CertApplyPluginNames} from '@certd/plugin-cert';
import { AliyunAccess, AliyunClient, AliyunSslClient, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
@IsTaskPlugin({
@ -21,7 +21,7 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin {
helper: '请选择证书申请任务输出的域名证书\n或者选择前置任务“上传证书到阿里云”任务的证书ID可以减少上传到阿里云的证书数量',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'uploadCertToAliyun'],
from: [...CertApplyPluginNames, 'uploadCertToAliyun'],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AliyunAccess, AliyunClient, AliyunSslClient, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToAliyunCDN',
title: '阿里云-部署证书至CDN',
@ -36,7 +36,7 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'uploadCertToAliyun'],
from: [...CertApplyPluginNames, 'uploadCertToAliyun'],
},
required: true,
})

View File

@ -2,7 +2,7 @@ import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput
import dayjs from 'dayjs';
import { AliyunAccess, AliyunClient, createCertDomainGetterInputDefine } from '@certd/plugin-lib';
import { CertInfo } from '@certd/plugin-cert';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToAliyunDCDN',
title: '阿里云-部署证书至DCDN',
@ -21,7 +21,7 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'uploadCertToAliyun'],
from: [...CertApplyPluginNames, 'uploadCertToAliyun'],
},
required: true,
})

View File

@ -2,7 +2,7 @@ import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipel
import { CertInfo } from '@certd/plugin-cert';
import { AliyunAccess, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'AliyunDeployCertToFC',
title: '阿里云-部署至阿里云FC(3.0)',
@ -22,7 +22,7 @@ export class AliyunDeployCertToFC extends AbstractPlusTaskPlugin {
helper: '请选择证书申请任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { AliyunAccess, AliyunClient, AliyunSslClient, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'AliyunDeployCertToNLB',
title: '阿里云-部署至NLB网络负载均衡',
@ -21,7 +21,7 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin {
helper: '请选择证书申请任务输出的域名证书\n或者选择前置任务“上传证书到阿里云”任务的证书ID可以减少上传到阿里云的证书数量',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'uploadCertToAliyun'],
from: [...CertApplyPluginNames, 'uploadCertToAliyun'],
},
required: true,
})

View File

@ -1,6 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AliyunAccess } from '@certd/plugin-lib';
import { CertInfo } from '@certd/plugin-cert';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToAliyunOSS',
title: '阿里云-部署证书至OSS',
@ -81,7 +82,7 @@ export class DeployCertToAliyunOSS extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { AliyunAccess, AliyunClient, AliyunSslClient, CasCertInfo, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'AliyunDeployCertToSLB',
title: '阿里云-部署至SLB(传统负载均衡)',
@ -21,7 +21,7 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin {
helper: '请选择证书申请任务输出的域名证书\n或者选择前置任务“上传证书到阿里云”任务的证书ID可以减少上传到阿里云的证书数量',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'uploadCertToAliyun'],
from: [...CertApplyPluginNames, 'uploadCertToAliyun'],
},
required: true,
})

View File

@ -2,7 +2,7 @@ import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipel
import { CertInfo } from '@certd/plugin-cert';
import { AliyunAccess, AliyunClient, AliyunSslClient, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'AliyunDeployCertToWaf',
title: '阿里云-部署至阿里云WAF',
@ -22,7 +22,7 @@ export class AliyunDeployCertToWaf extends AbstractPlusTaskPlugin {
helper: '请选择证书申请任务输出的域名证书\n或者选择前置任务“上传证书到阿里云”任务的证书ID可以减少上传到阿里云的证书数量',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'uploadCertToAliyun'],
from: [...CertApplyPluginNames, 'uploadCertToAliyun'],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
import { AliyunAccess } from '@certd/plugin-lib';
import { AliyunSslClient } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
/**
* 1 cn-hangzhou cas.aliyuncs.com cas-vpc.cn-hangzhou.aliyuncs.com
* 西 ap-southeast-3 cas.ap-southeast-3.aliyuncs.com cas-vpc.ap-southeast-3.aliyuncs.com
@ -57,7 +57,7 @@ export class UploadCertToAliyun extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -5,7 +5,7 @@ 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';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'AwsDeployToCloudFront',
title: 'AWS-部署证书到CloudFront',
@ -25,7 +25,7 @@ export class AwsDeployToCloudFront extends AbstractPlusTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'AwsUploadToACM'],
from: [...CertApplyPluginNames, 'AwsUploadToACM'],
},
required: true,
})

View File

@ -2,7 +2,7 @@ import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput,
import { CertInfo } from '@certd/plugin-cert';
import { AwsAccess, AwsRegions } from '../access.js';
import { AwsAcmClient } from '../libs/aws-acm-client.js';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'AwsUploadToACM',
title: 'AWS-上传证书到ACM',
@ -21,7 +21,7 @@ export class AwsUploadToACM extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { CacheflyAccess } from '../access.js';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'CacheFly',
title: 'CacheFly-部署证书到CacheFly',
@ -20,7 +20,7 @@ export class CacheFlyPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -2,7 +2,7 @@ import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput
import { CertInfo, CertReader } from '@certd/plugin-cert';
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
//命名规范,插件名称+功能就是目录plugin-demo中的demo大写字母开头驼峰命名
name: 'DemoTest',
@ -97,7 +97,7 @@ export class DemoTest extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
// required: true, // 必填
})

View File

@ -2,7 +2,7 @@ import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput
import { CertInfo } from '@certd/plugin-cert';
import { DogeClient } from '../../lib/index.js';
import dayjs from 'dayjs';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DogeCloudDeployToCDN',
title: '多吉云-部署到多吉云CDN',
@ -27,7 +27,7 @@ export class DogeCloudDeployToCDNPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { GcoreAccess } from '../access.js';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'Gcoreflush',
title: 'Gcore-刷新Gcore证书',
@ -30,7 +30,7 @@ export class GcoreflushPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { GcoreAccess } from '../access.js';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'Gcoreupload',
title: 'Gcore-部署证书到Gcore',
@ -26,7 +26,7 @@ export class GcoreuploadPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -3,7 +3,7 @@ import { CertInfo, CertReader } from '@certd/plugin-cert';
import * as fs from 'fs';
import { Constants } from '@certd/lib-server';
import path from 'path';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'CopyToLocal',
title: '主机-复制到本机',
@ -22,7 +22,7 @@ export class CopyCertToLocalPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,6 +1,5 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { SshClient } from '@certd/plugin-lib';
@IsTaskPlugin({
name: 'hostShellExecute',
title: '主机-执行远程主机脚本命令',

View File

@ -2,7 +2,7 @@ import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput,
import { CertInfo, CertReader, CertReaderHandleContext } from '@certd/plugin-cert';
import dayjs from 'dayjs';
import { SshAccess, SshClient } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'uploadCertToHost',
title: '主机-部署证书到SSH主机',
@ -21,7 +21,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -3,7 +3,7 @@ import { HuaweiAccess } from '../../access/index.js';
import { CertInfo } from '@certd/plugin-cert';
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { resetLogConfigure } from '@certd/basic';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'HauweiDeployCertToCDN',
title: '华为云-部署证书至CDN',
@ -22,7 +22,7 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,6 +1,6 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskInstanceContext } from '@certd/pipeline';
import { CertInfo, CertReader } from '@certd/plugin-cert';
import { CertApplyPluginNames} from '@certd/plugin-cert';
export type CustomScriptContext = {
CertReader: typeof CertReader;
self: CustomScriptPlugin;
@ -37,7 +37,7 @@ export class CustomScriptPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: false,
})

View File

@ -3,7 +3,7 @@ import { CertInfo } from '@certd/plugin-cert';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { ProxmoxAccess } from '../access.js';
import { createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
//命名规范,插件名称+功能就是目录plugin-demo中的demo大写字母开头驼峰命名
name: 'ProxmoxUploadCert',
@ -27,7 +27,7 @@ export class ProxmoxUploadCert extends AbstractPlusTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
// required: true, // 必填
})

View File

@ -2,7 +2,7 @@ import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine, QiniuAccess, QiniuClient } from '@certd/plugin-lib';
import { CertInfo } from '@certd/plugin-cert';
import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'QiniuDeployCertToCDN',
title: '七牛云-部署证书至CDN',
@ -21,7 +21,7 @@ export class QiniuDeployCertToCDN extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书或者上传到七牛云的证书id',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'QiniuCertUpload'],
from: [...CertApplyPluginNames, 'QiniuCertUpload'],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { QiniuAccess, QiniuClient } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'QiniuCertUpload',
title: '七牛云-上传证书到七牛云',
@ -26,7 +26,7 @@ export class QiniuCertUpload extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -4,7 +4,7 @@ import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { tmpdir } from 'node:os';
import fs from 'fs';
import { SshAccess, SshClient } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'QnapDeploy',
title: '威联通-部署证书到威联通',
@ -25,7 +25,7 @@ export class QnapDeploy extends AbstractPlusTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -2,6 +2,7 @@ import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput
import { CertInfo } from '@certd/plugin-cert';
import { createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { TencentAccess, TencentSslClient } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'TencentDeployCertToCDNv2',
title: '腾讯云-部署到CDN-v2',
@ -41,7 +42,7 @@ export class TencentDeployCertToCDNv2 extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书或者选择前置任务“上传证书到腾讯云”任务的证书ID',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'UploadCertToTencent'],
from: [...CertApplyPluginNames, 'UploadCertToTencent'],
},
required: true,
})

View File

@ -1,6 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { TencentAccess } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToTencentCDN',
title: '腾讯云-部署到CDN废弃',
@ -19,7 +20,7 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,6 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import dayjs from 'dayjs';
import { TencentAccess } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToTencentCLB',
title: '腾讯云-部署到CLB',
@ -81,7 +82,7 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { createRemoteSelectInputDefine, TencentSslClient } from '@certd/plugin-lib';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToTencentCosPlugin',
title: '腾讯云-部署证书到COS',
@ -90,7 +90,7 @@ export class DeployCertToTencentCosPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书或者选择前置任务“上传证书到腾讯云”任务的证书ID',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'UploadCertToTencent'],
from: [...CertApplyPluginNames, 'UploadCertToTencent'],
},
required: true,
})

View File

@ -1,5 +1,6 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { TencentAccess } from '@certd/plugin-lib';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { TencentAccess } from "@certd/plugin-lib";
@IsTaskPlugin({
name: 'DeployCertToTencentEO',
title: '腾讯云-部署到腾讯云EO',

View File

@ -2,7 +2,7 @@ import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipel
import { CertInfo } from '@certd/plugin-cert';
import { createRemoteSelectInputDefine, TencentAccess, TencentSslClient } from '@certd/plugin-lib';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'TencentDeployCertToLive',
title: '腾讯云-部署到腾讯云直播',
@ -43,7 +43,7 @@ export class TencentDeployCertToLive extends AbstractPlusTaskPlugin {
helper: '请选择前置任务输出的域名证书或者选择前置任务“上传证书到腾讯云”任务的证书ID',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego', 'UploadCertToTencent'],
from: [...CertApplyPluginNames, 'UploadCertToTencent'],
},
required: true,
})

View File

@ -3,7 +3,7 @@ import { utils } from '@certd/basic';
import dayjs from 'dayjs';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToTencentTKEIngress',
title: '腾讯云-部署到TKE-ingress',
@ -95,7 +95,7 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractPlusTaskPlugin
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
mergeScript: `
return {

View File

@ -1,7 +1,5 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { AbstractTaskPlugin } from '@certd/pipeline';
import { TencentAccess } from '@certd/plugin-lib';
import { createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { createRemoteSelectInputDefine, TencentAccess } from "@certd/plugin-lib";
@IsTaskPlugin({
name: 'TencentActionInstancesPlugin',

View File

@ -1,6 +1,6 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
import dayjs from 'dayjs';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'UploadCertToTencent',
title: '腾讯云-上传证书到腾讯云',
@ -33,7 +33,7 @@ export class UploadCertToTencent extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})

View File

@ -1,7 +1,7 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { WoaiAccess } from '../access.js';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'WoaiCDN',
title: '我爱云-部署证书到我爱云CDN',
@ -34,7 +34,7 @@ export class WoaiCdnPlugin extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
from: [...CertApplyPluginNames],
},
required: true,
})