diff --git a/packages/libs/lib-server/src/basic/constants.ts b/packages/libs/lib-server/src/basic/constants.ts
index 8fdc4424..f7afaa75 100644
--- a/packages/libs/lib-server/src/basic/constants.ts
+++ b/packages/libs/lib-server/src/basic/constants.ts
@@ -107,5 +107,13 @@ export const Constants = {
code: 20012,
message: '证书还未生成',
},
+ openCertApplying: {
+ code: 20013,
+ message: '证书正在申请中,请稍后重新获取(需要事先在“域名管理”页面配置好校验方式)',
+ },
+ openEmailNotFound: {
+ code: 20021,
+ message: '用户邮箱还未配置',
+ },
},
};
diff --git a/packages/libs/lib-server/src/basic/exception/common-exception.ts b/packages/libs/lib-server/src/basic/exception/common-exception.ts
index 5e97743a..97f6e15f 100644
--- a/packages/libs/lib-server/src/basic/exception/common-exception.ts
+++ b/packages/libs/lib-server/src/basic/exception/common-exception.ts
@@ -11,13 +11,13 @@ export class CommonException extends BaseException {
}
export class CodeException extends BaseException {
- constructor(res: { code: number; message: string }) {
- super("CodeException", res.code, res.message);
+ constructor(res: { code: number; message: string; data?: any }) {
+ super("CodeException", res.code, res.message, res.data);
}
}
export class TextException extends BaseException {
- constructor(name, code,message, data?) {
+ constructor(name, code, message, data?) {
super(name, code, message, data);
}
}
diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts
index f41afe97..6c654cf1 100644
--- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts
+++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts
@@ -470,6 +470,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
const domain = fullDomain.replaceAll("*.", "");
const mainDomain = await domainParser.parse(domain);
const planSetting: DomainVerifyPlanInput = verifyPlanSetting[mainDomain];
+ if (planSetting == null) {
+ throw new Error(`没有找到域名(${domain})的校验计划`);
+ }
if (planSetting.type === "dns") {
plan[domain] = await this.createDnsDomainVerifyPlan(planSetting, domain, mainDomain);
} else if (planSetting.type === "cname") {
@@ -498,6 +501,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
for (const domain in verifiers) {
const verifier = verifiers[domain];
+ if (verifier == null) {
+ throw new Error(`没有找到与该域名(${domain})匹配的校验方式,请先到‘域名管理’页面添加校验方式`);
+ }
if (verifier.type === "dns") {
plan[domain] = await this.createDnsDomainVerifyPlan(verifier.dns, domain, verifier.mainDomain);
} else if (verifier.type === "cname") {
diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx
index b5599e8f..c53a3214 100644
--- a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx
+++ b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx
@@ -46,6 +46,20 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const { openSiteIpMonitorDialog } = useSiteIpMonitor();
const { openSiteImportDialog } = useSiteImport();
+
+ function checkAll() {
+ Modal.confirm({
+ title: t("certd.monitor.confirmTitle"), // "确认"
+ content: t("certd.monitor.confirmContent"), // "确认触发检查全部站点证书吗?"
+ onOk: async () => {
+ await siteInfoApi.CheckAll();
+ notification.success({
+ message: t("certd.monitor.checkSubmitted"), // "检查任务已提交"
+ description: t("certd.monitor.pleaseRefresh"), // "请稍后刷新页面查看结果"
+ });
+ },
+ });
+ }
return {
id: "siteMonitorCrud",
crudOptions: {
@@ -114,6 +128,14 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
});
},
},
+ checkAll: {
+ show: true,
+ text: t("certd.monitor.checkAll"),
+ type: "primary",
+ click() {
+ checkAll();
+ },
+ },
},
},
rowHandle: {
diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/index.vue b/packages/ui/certd-client/src/views/certd/monitor/site/index.vue
index 78227e94..436620c4 100644
--- a/packages/ui/certd-client/src/views/certd/monitor/site/index.vue
+++ b/packages/ui/certd-client/src/views/certd/monitor/site/index.vue
@@ -14,9 +14,6 @@
-
-
{{ t("certd.monitor.checkAll") }}
-
@@ -35,19 +32,6 @@ defineOptions({
name: "SiteCertMonitor",
});
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
-function checkAll() {
- Modal.confirm({
- title: t("certd.monitor.confirmTitle"), // "确认"
- content: t("certd.monitor.confirmContent"), // "确认触发检查全部站点证书吗?"
- onOk: async () => {
- await siteInfoApi.CheckAll();
- notification.success({
- message: t("certd.monitor.checkSubmitted"), // "检查任务已提交"
- description: t("certd.monitor.pleaseRefresh"), // "请稍后刷新页面查看结果"
- });
- },
- });
-}
// 页面打开后获取列表数据
onMounted(() => {
diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx
index f1edd6c4..74c89195 100644
--- a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx
+++ b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/use.tsx
@@ -125,6 +125,7 @@ export function useCertPipelineCreator() {
const pluginStore = usePluginStore();
const randomHour = Math.floor(Math.random() * 6);
const randomMin = Math.floor(Math.random() * 60);
+ const randomCron = `0 ${randomMin} ${randomHour} * * *`;
const groupDictRef = dict({
url: "/pi/pipeline/group/all",
@@ -193,7 +194,7 @@ export function useCertPipelineCreator() {
title: t("certd.pipelineForm.triggerCronTitle"),
type: "text",
form: {
- value: `0 ${randomMin} ${randomHour} * * *`,
+ value: randomCron,
component: {
name: "cron-editor",
vModel: "modelValue",
diff --git a/packages/ui/certd-server/src/controller/openapi/v1/cert-controller.ts b/packages/ui/certd-server/src/controller/openapi/v1/cert-controller.ts
index 0b02fabd..da78766d 100644
--- a/packages/ui/certd-server/src/controller/openapi/v1/cert-controller.ts
+++ b/packages/ui/certd-server/src/controller/openapi/v1/cert-controller.ts
@@ -1,13 +1,15 @@
-import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from '@midwayjs/core';
-import { CodeException, Constants, EncryptService } from '@certd/lib-server';
-import { CertInfoService } from '../../../modules/monitor/service/cert-info-service.js';
-import { CertInfo } from '@certd/plugin-cert';
-import { OpenKey } from '../../../modules/open/service/open-key-service.js';
-import { BaseOpenController } from '../base-open-controller.js';
+import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
+import { CodeException, Constants, EncryptService } from "@certd/lib-server";
+import { CertInfo } from "@certd/plugin-cert";
+import { OpenKey } from "../../../modules/open/service/open-key-service.js";
+import { BaseOpenController } from "../base-open-controller.js";
+import { CertInfoFacade } from "../../../modules/monitor/facade/cert-info-facade.js";
+import { merge } from "lodash-es";
export type CertGetReq = {
domains?: string;
certId: number;
+ autoApply?:boolean;
};
/**
@@ -16,7 +18,7 @@ export type CertGetReq = {
@Controller('/api/v1/cert')
export class OpenCertController extends BaseOpenController {
@Inject()
- certInfoService: CertInfoService;
+ certInfoFacade: CertInfoFacade;
@Inject()
encryptService: EncryptService;
@@ -29,10 +31,13 @@ export class OpenCertController extends BaseOpenController {
throw new CodeException(Constants.res.openKeyError);
}
- const res: CertInfo = await this.certInfoService.getCertInfo({
+ const req = merge({}, bean, query)
+
+ const res: CertInfo = await this.certInfoFacade.getCertInfo({
userId,
- domains: bean.domains || query.domains,
- certId: bean.certId || query.certId,
+ domains: req.domains,
+ certId: req.certId,
+ autoApply: req.autoApply??false,
});
return this.ok(res);
}
diff --git a/packages/ui/certd-server/src/modules/monitor/facade/cert-info-facade.ts b/packages/ui/certd-server/src/modules/monitor/facade/cert-info-facade.ts
new file mode 100644
index 00000000..88249deb
--- /dev/null
+++ b/packages/ui/certd-server/src/modules/monitor/facade/cert-info-facade.ts
@@ -0,0 +1,96 @@
+import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
+import { CodeException, Constants } from "@certd/lib-server";
+import { CertInfoEntity } from "../entity/cert-info.js";
+import { utils } from "@certd/basic";
+import { PipelineService } from "../../pipeline/service/pipeline-service.js";
+import { UserSettingsService } from "../../mine/service/user-settings-service.js";
+import { UserEmailSetting } from "../../mine/service/models.js";
+import { PipelineEntity } from "../../pipeline/entity/pipeline.js";
+import { CertInfoService } from "../service/cert-info-service.js";
+
+
+@Provide("CertInfoFacade")
+@Scope(ScopeEnum.Request, { allowDowngrade: true })
+export class CertInfoFacade {
+
+ @Inject()
+ pipelineService: PipelineService;
+
+ @Inject()
+ certInfoService: CertInfoService ;
+
+ @Inject()
+ userSettingsService : UserSettingsService
+
+ async getCertInfo(req: { domains?: string; certId?: number; userId: number,autoApply?:boolean }) {
+ const { domains, certId, userId } = req;
+ if (certId) {
+ return await this.certInfoService.getCertInfoById({ id: certId, userId });
+ }
+ const domainArr = domains.split(',');
+
+ const matchedList = await this.certInfoService.getMatchCertList({domains:domainArr,userId})
+ let matched: CertInfoEntity = null
+ if (matchedList.length === 0 ) {
+ if(req.autoApply === true){
+ //自动申请,先创建自动申请流水线
+ const pipeline:PipelineEntity = await this.createAutoPipeline({domains:domainArr,userId})
+ await this.triggerApplyPipeline({pipelineId:pipeline.id})
+ }else{
+ throw new CodeException(Constants.res.openCertNotFound);
+ }
+ }
+ matched = null;
+ for (const item of matchedList) {
+ if (item.expiresTime>0 && item.expiresTime < new Date().getTime()) {
+ matched = item;
+ break
+ }
+ }
+ if (!matched) {
+ if(req.autoApply === true){
+ //如果没有找到有效期内的证书,则自动触发一次申请
+ const first = matchedList[0]
+ await this.triggerApplyPipeline({pipelineId:first.pipelineId})
+ return
+ }else{
+ throw new CodeException(Constants.res.openCertNotFound);
+ }
+ }
+
+ return await this.certInfoService.getCertInfoById({ id: matched.id, userId: userId });
+ }
+
+ async createAutoPipeline(req:{domains:string[],userId:number}){
+ const userEmailSetting = await this.userSettingsService.getSetting(req.userId,UserEmailSetting)
+ if(!userEmailSetting.list){
+ throw new CodeException(Constants.res.openEmailNotFound)
+ }
+ const email = userEmailSetting.list[0]
+
+ return await this.pipelineService.createAutoPipeline({
+ domains: req.domains,
+ email,
+ userId: req.userId,
+ from:"OpenAPI"
+ })
+
+ }
+
+ async triggerApplyPipeline(req:{pipelineId:number}){
+ //查询流水线状态
+ const status = await this.pipelineService.getStatus(req.pipelineId)
+ if (status != 'running') {
+ await this.pipelineService.trigger(req.pipelineId)
+ await utils.sleep(1000)
+ }
+ throw new CodeException({
+ ...Constants.res.openCertApplying,
+ data:{
+ pipelineId:req.pipelineId
+ }
+ });
+ }
+
+
+}
diff --git a/packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts b/packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts
index 7a4cce25..a72bef65 100644
--- a/packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts
+++ b/packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts
@@ -1,10 +1,10 @@
-import {Provide, Scope, ScopeEnum} from "@midwayjs/core";
-import {BaseService, CodeException, Constants, PageReq} from "@certd/lib-server";
-import {InjectEntityModel} from "@midwayjs/typeorm";
-import {MoreThan, 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;
@@ -71,6 +71,7 @@ export class CertInfoService extends BaseService {
}
await this.addOrUpdate(bean);
+ return bean.id
}
async deleteByPipelineId(id: number) {
@@ -82,44 +83,28 @@ export class CertInfoService extends BaseService {
});
}
- async getCertInfo(params: { domains?: string; certId?: number; userId: number }) {
- const { domains, certId, userId } = params;
- if (certId) {
- return await this.getCertInfoById({ id: certId, userId });
- }
- return await this.getCertInfoByDomains({
- domains,
- userId,
- });
- }
-
- private async getCertInfoByDomains(params: { domains: string; userId: number }) {
+ async getMatchCertList(params: { domains: string[]; userId: number }) {
const { domains, userId } = params;
if (!domains) {
throw new CodeException(Constants.res.openCertNotFound);
}
- const domainArr = domains.split(',');
const list = await this.find({
select: {
id: true,
domains: true,
+ expiresTime:true,
+ pipelineId:true,
},
where: {
userId,
- expiresTime: MoreThan(new Date().getTime())
},
});
//遍历查找
- const matched = list.find(item => {
+ return list.filter(item => {
const itemDomains = item.domains.split(',');
- return utils.domain.match(domainArr, itemDomains);
+ return utils.domain.match(domains, itemDomains);
});
- if (!matched) {
- throw new CodeException(Constants.res.openCertNotFound);
- }
-
- return await this.getCertInfoById({ id: matched.id, userId: userId });
}
async getCertInfoById(req: { id: number; userId: number }) {
diff --git a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts
index b52c6151..31cd0479 100644
--- a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts
+++ b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts
@@ -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 {
AccessService,
BaseService,
@@ -11,8 +11,8 @@ import {
SysSettingsService,
SysSiteInfo
} from "@certd/lib-server";
-import {PipelineEntity} from "../entity/pipeline.js";
-import {PipelineDetail} from "../entity/vo/pipeline-detail.js";
+import { PipelineEntity } from "../entity/pipeline.js";
+import { PipelineDetail } from "../entity/vo/pipeline-detail.js";
import {
Executor,
IAccessService,
@@ -25,33 +25,32 @@ import {
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 {PluginConfigGetter} from "../../plugin/service/plugin-config-getter.js";
+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 { PluginConfigGetter } from "../../plugin/service/plugin-config-getter.js";
import dayjs from "dayjs";
-import {DbAdapter} from "../../db/index.js";
-import {isComm, isPlus} from "@certd/plus-core";
-import {logger} from "@certd/basic";
-import {UrlService} from "./url-service.js";
-import {NotificationService} from "./notification-service.js";
-import {UserSuiteEntity, UserSuiteService} from "@certd/commercial-core";
-import {CertInfoService} from "../../monitor/service/cert-info-service.js";
-import {TaskServiceBuilder} from "./getter/task-service-getter.js";
-import {nanoid} from "nanoid";
-import {set} from "lodash-es";
+import { DbAdapter } from "../../db/index.js";
+import { isComm, isPlus } from "@certd/plus-core";
+import { logger } from "@certd/basic";
+import { UrlService } from "./url-service.js";
+import { NotificationService } from "./notification-service.js";
+import { UserSuiteEntity, UserSuiteService } from "@certd/commercial-core";
+import { CertInfoService } from "../../monitor/service/cert-info-service.js";
+import { TaskServiceBuilder } from "./getter/task-service-getter.js";
+import { nanoid } from "nanoid";
+import { set } from "lodash-es";
const runningTasks: Map = new Map();
-
/**
* 证书申请
*/
@@ -91,7 +90,7 @@ export class PipelineService extends BaseService {
@Inject()
cron: Cron;
- @Config('certd')
+ @Config("certd")
private certdConfig: any;
@Inject()
@@ -112,31 +111,31 @@ export class PipelineService extends BaseService {
}
async add(bean: PipelineEntity) {
- bean.status = ResultType.none
+ bean.status = ResultType.none;
await this.save(bean);
return bean;
}
async page(pageReq: PageReq) {
//模版流水线不要被查询出来
- set(pageReq,"query.isTemplate",false)
+ set(pageReq, "query.isTemplate", false);
const result = await super.page(pageReq);
await this.fillLastVars(result.records);
for (const item of result.records) {
- if (!item.content){
- continue
+ if (!item.content) {
+ continue;
}
const pipeline = JSON.parse(item.content);
- let stepCount = 0
+ let stepCount = 0;
RunnableCollection.each(pipeline.stages, (runnable: any) => {
- stepCount++
- })
+ stepCount++;
+ });
// @ts-ignore
- item.stepCount = stepCount
+ item.stepCount = stepCount;
// @ts-ignore
- item.triggerCount = pipeline.triggers.length
- delete item.content
+ item.triggerCount = pipeline.triggers.length;
+ delete item.content;
}
return result;
@@ -148,7 +147,7 @@ export class PipelineService extends BaseService {
for (const record of records) {
pipelineIds.push(record.id);
recordMap[record.id] = record;
- record.title = record.title + '';
+ record.title = record.title + "";
}
if (pipelineIds?.length > 0) {
const vars = await this.storageService.findPipelineVars(pipelineIds);
@@ -169,17 +168,17 @@ export class PipelineService extends BaseService {
const info = await this.info(pipelineId);
if (info && !info.disabled) {
const pipeline = JSON.parse(info.content);
- this.registerTriggers(pipeline,false);
+ this.registerTriggers(pipeline, false);
}
}
- public async registerTrigger(info:PipelineEntity) {
+ public async registerTrigger(info: PipelineEntity) {
if (info == null) {
return;
}
if (info && !info.disabled) {
const pipeline = JSON.parse(info.content);
- this.registerTriggers(pipeline,false);
+ this.registerTriggers(pipeline, false);
}
}
@@ -206,12 +205,12 @@ export class PipelineService extends BaseService {
const isUpdate = bean.id > 0 && old != null;
- const pipeline = JSON.parse(bean.content || '{}');
+ const pipeline = JSON.parse(bean.content || "{}");
RunnableCollection.initPipelineRunnableType(pipeline);
let domains = [];
if (pipeline.stages) {
RunnableCollection.each(pipeline.stages, (runnable: any) => {
- if (runnable.runnableType === 'step' && runnable.type.indexOf('CertApply')>=0) {
+ if (runnable.runnableType === "step" && runnable.type.indexOf("CertApply") >= 0) {
domains = runnable.input.domains || [];
}
});
@@ -222,7 +221,7 @@ export class PipelineService extends BaseService {
await this.checkMaxPipelineCount(bean, pipeline, domains);
}
- if (!bean.status ){
+ if (!bean.status) {
bean.status = ResultType.none;
}
if (!isUpdate) {
@@ -233,9 +232,11 @@ export class PipelineService extends BaseService {
await this.doUpdatePipelineJson(bean, pipeline);
//保存域名信息到certInfo表
- let fromType = 'pipeline';
- if (bean.type === 'cert_upload') {
- fromType = 'upload';
+ let fromType = "pipeline";
+ if (bean.type === "cert_upload") {
+ fromType = "upload";
+ }else if (bean.type === "cert_auto") {
+ fromType = "auto";
}
await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains, fromType);
return bean;
@@ -246,7 +247,7 @@ export class PipelineService extends BaseService {
* @param bean
* @param pipeline
*/
- async doUpdatePipelineJson(bean: PipelineEntity, pipeline:Pipeline) {
+ async doUpdatePipelineJson(bean: PipelineEntity, pipeline: Pipeline) {
await this.clearTriggers(bean);
if (pipeline.title) {
bean.title = pipeline.title;
@@ -277,7 +278,7 @@ export class PipelineService extends BaseService {
throw new NeedSuiteException(`对不起,您最多只能添加${userSuite.domainCount.max}个域名,请购买或升级套餐`);
}
}
- }else{
+ } else {
//非商业版校验用户最大流水线数量
const userId = bean.userId;
const userIsAdmin = await this.userService.isAdmin(userId);
@@ -296,12 +297,12 @@ export class PipelineService extends BaseService {
async foreachPipeline(callback: (pipeline: PipelineEntity) => void) {
const idEntityList = await this.repository.find({
select: {
- id: true,
+ id: true
},
where: {
disabled: false,
- templateId: 0,
- },
+ templateId: 0
+ }
});
const ids = idEntityList.map(item => {
return item.id;
@@ -321,7 +322,7 @@ export class PipelineService extends BaseService {
//分段加载记录
for (const idArr of idsSpan) {
const list = await this.repository.findBy({
- id: In(idArr),
+ id: In(idArr)
});
for (const entity of list) {
@@ -346,14 +347,14 @@ export class PipelineService extends BaseService {
if (onlyAdminUser && entity.userId !== 1) {
return;
}
- const pipeline = JSON.parse(entity.content ?? '{}');
+ const pipeline = JSON.parse(entity.content ?? "{}");
try {
await this.registerTriggers(pipeline, immediateTriggerOnce);
} catch (e) {
- logger.error('加载定时trigger失败:', e);
+ logger.error("加载定时trigger失败:", e);
}
});
- logger.info('定时器数量:', this.cron.getTaskSize());
+ logger.info("定时器数量:", this.cron.getTaskSize());
}
async registerTriggers(pipeline?: Pipeline, immediateTriggerOnce = false) {
@@ -375,18 +376,18 @@ export class PipelineService extends BaseService {
if (isComm()) {
await this.checkHasDeployCount(id, entity.userId);
}
- await this.checkUserStatus(entity.userId)
+ await this.checkUserStatus(entity.userId);
this.cron.register({
name: `pipeline.${id}.trigger.once`,
cron: null,
job: async () => {
- logger.info('用户手动启动job');
+ logger.info("用户手动启动job");
try {
await this.doRun(entity, null, stepId);
} catch (e) {
- logger.error('手动job执行失败:', e);
+ logger.error("手动job执行失败:", e);
}
- },
+ }
});
}
@@ -398,7 +399,7 @@ export class PipelineService extends BaseService {
logger.error(e.message);
await this.update({
id: pipelineId,
- status: 'no_deploy_count',
+ status: "no_deploy_count"
});
}
throw e;
@@ -406,7 +407,7 @@ export class PipelineService extends BaseService {
}
//@ts-ignore
- async delete(id:any) {
+ async delete(id: any) {
await this.clearTriggers(id);
//TODO 删除storage
// const storage = new DbStorage(pipeline.userId, this.storageService);
@@ -421,11 +422,11 @@ export class PipelineService extends BaseService {
if (id == null) {
return;
}
- let pipeline:PipelineEntity = null
- if (typeof id === 'number') {
+ let pipeline: PipelineEntity = null;
+ if (typeof id === "number") {
pipeline = await this.info(id);
- }else{
- pipeline = id
+ } else {
+ pipeline = id;
}
if (!pipeline) {
return;
@@ -445,7 +446,7 @@ export class PipelineService extends BaseService {
registerCron(pipelineId, trigger) {
if (pipelineId == null) {
- logger.warn('pipelineId为空,无法注册定时任务');
+ logger.warn("pipelineId为空,无法注册定时任务");
return;
}
@@ -454,11 +455,11 @@ export class PipelineService extends BaseService {
return;
}
cron = cron.trim();
- if (cron.startsWith('* *')) {
- cron = cron.replace('* *', '0 0');
+ if (cron.startsWith("* *")) {
+ cron = cron.replace("* *", "0 0");
}
- if (cron.startsWith('*')) {
- cron = cron.replace('*', '0');
+ if (cron.startsWith("*")) {
+ cron = cron.replace("*", "0");
}
const triggerId = trigger.id;
const name = this.buildCronKey(pipelineId, triggerId);
@@ -467,19 +468,19 @@ export class PipelineService extends BaseService {
name,
cron,
job: async () => {
- logger.info('定时任务触发:', pipelineId, triggerId);
+ logger.info("定时任务触发:", pipelineId, triggerId);
if (pipelineId == null) {
- logger.warn('pipelineId为空,无法执行');
+ logger.warn("pipelineId为空,无法执行");
return;
}
try {
await this.run(pipelineId, triggerId);
} catch (e) {
- logger.error('定时job执行失败:', e);
+ logger.error("定时job执行失败:", e);
}
- },
+ }
});
- logger.info('当前定时器数量:', this.cron.getTaskSize());
+ logger.info("当前定时器数量:", this.cron.getTaskSize());
}
/**
@@ -499,11 +500,11 @@ export class PipelineService extends BaseService {
if (isComm()) {
suite = await this.checkHasDeployCount(id, entity.userId);
}
- try{
- await this.checkUserStatus(entity.userId)
- }catch (e) {
- logger.info(e.message)
- return
+ try {
+ await this.checkUserStatus(entity.userId);
+ } catch (e) {
+ logger.info(e.message);
+ return;
}
@@ -521,7 +522,7 @@ export class PipelineService extends BaseService {
return;
}
- if (triggerType === 'timer') {
+ if (triggerType === "timer") {
if (entity.disabled) {
return;
}
@@ -530,25 +531,25 @@ export class PipelineService extends BaseService {
const onChanged = async (history: RunHistory) => {
//保存执行历史
try {
- logger.info('保存执行历史:', history.id);
+ logger.info("保存执行历史:", history.id);
await this.saveHistory(history);
} catch (e) {
const pipelineEntity = new PipelineEntity();
pipelineEntity.id = id;
- pipelineEntity.status = 'error';
+ pipelineEntity.status = "error";
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
await this.update(pipelineEntity);
- logger.error('保存执行历史失败:', e);
+ logger.error("保存执行历史失败:", e);
throw e;
}
};
const userId = entity.userId;
- const historyId = await this.historyService.start(entity,triggerType);
+ const historyId = await this.historyService.start(entity, triggerType);
const userIsAdmin = await this.userService.isAdmin(userId);
const user: UserInfo = {
id: userId,
- role: userIsAdmin ? 'admin' : 'user',
+ role: userIsAdmin ? "admin" : "user"
};
@@ -559,11 +560,11 @@ export class PipelineService extends BaseService {
}
const taskServiceGetter = this.taskServiceBuilder.create({
- userId,
- })
- const accessGetter = await taskServiceGetter.get("accessService")
- const notificationGetter =await taskServiceGetter.get("notificationService")
- const cnameProxyService =await taskServiceGetter.get("cnameProxyService")
+ userId
+ });
+ const accessGetter = await taskServiceGetter.get("accessService");
+ const notificationGetter = await taskServiceGetter.get("notificationService");
+ const cnameProxyService = await taskServiceGetter.get("cnameProxyService");
const executor = new Executor({
user,
pipeline,
@@ -577,7 +578,7 @@ export class PipelineService extends BaseService {
notificationService: notificationGetter,
fileRootDir: this.certdConfig.fileRootDir,
sysInfo,
- serviceGetter:taskServiceGetter
+ serviceGetter: taskServiceGetter
});
try {
runningTasks.set(historyId, executor);
@@ -595,7 +596,7 @@ export class PipelineService extends BaseService {
}
}
} catch (e) {
- logger.error('执行失败:', e);
+ logger.error("执行失败:", e);
// throw e;
} finally {
runningTasks.delete(historyId);
@@ -619,7 +620,7 @@ export class PipelineService extends BaseService {
}
private getTriggerType(triggerId, pipeline) {
- let triggerType = 'user';
+ let triggerType = "user";
if (triggerId != null) {
//如果不是手动触发
//查找trigger
@@ -629,8 +630,8 @@ export class PipelineService extends BaseService {
this.cron.remove(this.buildCronKey(pipeline.id, triggerId));
triggerType = null;
} else {
- logger.info('timer trigger:' + found.id, found.title, found.cron);
- triggerType = 'timer';
+ logger.info("timer trigger:" + found.id, found.title, found.cron);
+ triggerType = "timer";
}
}
return triggerType;
@@ -653,7 +654,7 @@ export class PipelineService extends BaseService {
//修改pipeline状态
const pipelineEntity = new PipelineEntity();
pipelineEntity.id = parseInt(history.pipeline.id);
- pipelineEntity.status = history.pipeline.status.result + '';
+ pipelineEntity.status = history.pipeline.status.result + "";
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
await this.update(pipelineEntity);
@@ -677,8 +678,8 @@ export class PipelineService extends BaseService {
async count(param: { userId?: any }) {
const count = await this.repository.count({
where: {
- userId: param.userId,
- },
+ userId: param.userId
+ }
});
return count;
}
@@ -686,12 +687,12 @@ export class PipelineService extends BaseService {
async statusCount(param: { userId?: any } = {}) {
const statusCount = await this.repository
.createQueryBuilder()
- .select('status')
- .addSelect('count(1)', 'count')
+ .select("status")
+ .addSelect("count(1)", "count")
.where({
- userId: param.userId,
+ userId: param.userId
})
- .groupBy('status')
+ .groupBy("status")
.getRawMany();
return statusCount;
}
@@ -701,11 +702,11 @@ export class PipelineService extends BaseService {
select: {
id: true,
title: true,
- status: true,
+ status: true
},
where: {
- userId,
- },
+ userId
+ }
});
await this.fillLastVars(list);
list = list.filter(item => {
@@ -719,16 +720,16 @@ export class PipelineService extends BaseService {
}
async createCountPerDay(param: { days: number } = { days: 7 }) {
- const todayEnd = dayjs().endOf('day');
+ const todayEnd = dayjs().endOf("day");
const result = await this.getRepository()
- .createQueryBuilder('main')
- .select(`${this.dbAdapter.date('main.createTime')} AS date`) // 将UNIX时间戳转换为日期
- .addSelect('COUNT(1) AS count')
+ .createQueryBuilder("main")
+ .select(`${this.dbAdapter.date("main.createTime")} AS date`) // 将UNIX时间戳转换为日期
+ .addSelect("COUNT(1) AS count")
.where({
// 0点
- createTime: MoreThan(todayEnd.add(-param.days, 'day').toDate()),
+ createTime: MoreThan(todayEnd.add(-param.days, "day").toDate())
})
- .groupBy('date')
+ .groupBy("date")
.getRawMany();
return result;
@@ -745,48 +746,48 @@ export class PipelineService extends BaseService {
await this.repository.update(
{
id: In(ids),
- userId,
+ userId
},
{ groupId }
);
}
- async batchUpdateTrigger(ids: number[], trigger: any, userId: any){
+ async batchUpdateTrigger(ids: number[], trigger: any, userId: any) {
const list = await this.find({
- where:{
+ where: {
id: In(ids),
userId
}
- })
+ });
for (const item of list) {
const pipeline = JSON.parse(item.content);
pipeline.triggers = [{
id: nanoid(),
- title: '定时触发',
+ title: "定时触发",
...trigger
- }]
- await this.doUpdatePipelineJson(item,pipeline)
+ }];
+ await this.doUpdatePipelineJson(item, pipeline);
}
}
- async batchUpdateNotifications(ids: number[], notification: Notification, userId: any){
+ async batchUpdateNotifications(ids: number[], notification: Notification, userId: any) {
const list = await this.find({
- where:{
+ where: {
id: In(ids),
userId
}
- })
+ });
for (const item of list) {
const pipeline = JSON.parse(item.content);
pipeline.notifications = [{
id: nanoid(),
- title: '通知',
+ title: "通知",
/**
* type: NotificationType;
* when: NotificationWhen[];
@@ -797,44 +798,44 @@ export class PipelineService extends BaseService {
*/
type: "other",
...notification
- }]
- await this.doUpdatePipelineJson(item,pipeline)
+ }];
+ await this.doUpdatePipelineJson(item, pipeline);
}
}
async batchRerun(ids: number[], userId: any) {
- if (!isPlus()){
- throw new NeedVIPException("此功能需要升级专业版")
+ if (!isPlus()) {
+ throw new NeedVIPException("此功能需要升级专业版");
}
if (!userId || ids.length === 0) {
return;
}
const list = await this.repository.find({
- select:{
- id:true
+ select: {
+ id: true
},
- where:{
+ where: {
id: In(ids),
userId
}
- })
+ });
- ids = list.map(item=>item.id)
+ ids = list.map(item => item.id);
//异步执行
- this.startBatchRerun(ids)
+ this.startBatchRerun(ids);
}
- async startBatchRerun(ids: number[]){
+ async startBatchRerun(ids: number[]) {
//20条一批
const batchSize = 20;
for (let i = 0; i < ids.length; i += batchSize) {
const batchIds = ids.slice(i, i + batchSize);
- const batchPromises = batchIds.map(async (id)=>{
- await this.run(id,null,"ALL")
+ const batchPromises = batchIds.map(async (id) => {
+ await this.run(id, null, "ALL");
});
- await Promise.all(batchPromises)
+ await Promise.all(batchPromises);
}
}
@@ -847,35 +848,130 @@ export class PipelineService extends BaseService {
return await this.repository.find({
select: {
id: true,
- title: true,
+ title: true
},
where: {
id: In(pipelineIds),
- userId,
- },
+ userId
+ }
});
}
private async checkUserStatus(userId: number) {
const userEntity = await this.userService.info(userId);
- if(userEntity == null){
- throw new Error('用户不存在');
+ if (userEntity == null) {
+ throw new Error("用户不存在");
}
- if(userEntity.status === 0){
- const message = `账户${userId}已被禁用,禁止运行流水线`
- throw new Error(message)
+ if (userEntity.status === 0) {
+ const message = `账户${userId}已被禁用,禁止运行流水线`;
+ throw new Error(message);
}
- const sysPublic = await this.sysSettingsService.getPublicSettings()
- if(isPlus() && sysPublic.userValidTimeEnabled === true){
+ const sysPublic = await this.sysSettingsService.getPublicSettings();
+ if (isPlus() && sysPublic.userValidTimeEnabled === true) {
//校验用户有效期是否设置
- if(userEntity.validTime!= null && userEntity.validTime > 0){
- if(userEntity.validTime < new Date().getTime()){
+ if (userEntity.validTime != null && userEntity.validTime > 0) {
+ if (userEntity.validTime < new Date().getTime()) {
//用户已过期
- const message = `账户${userId}已过有效期,禁止运行流水线`
- throw new Error(message)
+ const message = `账户${userId}已过有效期,禁止运行流水线`;
+ throw new Error(message);
}
}
}
}
+
+ async createAutoPipeline(req: { domains: string[]; email: string; userId: number ,from:string}) {
+
+ const randomHour = Math.floor(Math.random() * 6);
+ const randomMin = Math.floor(Math.random() * 60);
+ const randomCron = `0 ${randomMin} ${randomHour} * * *`;
+
+ let pipeline: any = {
+ title: req.domains[0] + `证书自动申请【${req.from??"OpenAPI"}】`,
+ runnableType: "pipeline",
+ triggers: [
+ {
+ id: nanoid(),
+ title: "定时触发",
+ cron: randomCron,
+ type: "cron"
+ }
+ ],
+ notifications: [
+ {
+ id: nanoid(),
+ type: "custom",
+ when: ["error", "turnToSuccess", "success"],
+ notificationId: 0,
+ title: "默认通知",
+ }
+ ],
+ stages: [
+ {
+ id: nanoid(),
+ title: "证书申请阶段",
+ maxTaskCount: 1,
+ runnableType: "stage",
+ tasks: [
+ {
+ id: nanoid(),
+ title: "证书申请任务",
+ runnableType: "task",
+ steps: [
+ {
+ id: nanoid(),
+ title: "申请证书",
+ runnableType: "step",
+ input: {
+ renewDays: 35,
+ domains: req.domains,
+ email: req.email,
+ "challengeType": "auto",
+ "sslProvider": "letsencrypt",
+ "privateKeyType": "rsa_2048",
+ "certProfile": "classic",
+ "useProxy": false,
+ "skipLocalVerify": false,
+ "maxCheckRetryCount": 20,
+ "waitDnsDiffuseTime": 30,
+ "pfxArgs": "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES",
+ "successNotify": true
+ },
+ strategy: {
+ runStrategy: 0 // 正常执行
+ },
+ type: "CertApply"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ };
+
+ const bean = new PipelineEntity();
+ bean.title = pipeline.title;
+ bean.content = JSON.stringify(pipeline);
+ bean.userId = req.userId;
+ bean.status = "none";
+ bean.type = "cert_auto";
+ bean.disabled = false
+ bean.keepHistoryCount = 30
+ await this.save(bean)
+
+
+ return bean;
+ }
+
+ async getStatus(pipelineId: number) {
+ const res = await this.repository.findOne({
+ select: {
+ status: true
+ },
+ where: {
+ id: pipelineId
+ }
+ });
+ return res?.status;
+ }
}