2025-07-14 23:02:47 +08:00
|
|
|
|
import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from "@midwayjs/core";
|
|
|
|
|
|
import { InjectEntityModel } from "@midwayjs/typeorm";
|
|
|
|
|
|
import { In, MoreThan, Repository } from "typeorm";
|
2024-12-22 14:00:46 +08:00
|
|
|
|
import {
|
|
|
|
|
|
AccessService,
|
|
|
|
|
|
BaseService,
|
|
|
|
|
|
NeedSuiteException,
|
|
|
|
|
|
NeedVIPException,
|
|
|
|
|
|
PageReq,
|
|
|
|
|
|
SysPublicSettings,
|
|
|
|
|
|
SysSettingsService,
|
2025-03-18 00:52:50 +08:00
|
|
|
|
SysSiteInfo
|
|
|
|
|
|
} from "@certd/lib-server";
|
2025-07-14 23:02:47 +08:00
|
|
|
|
import { PipelineEntity } from "../entity/pipeline.js";
|
|
|
|
|
|
import { PipelineDetail } from "../entity/vo/pipeline-detail.js";
|
2025-04-11 12:13:57 +08:00
|
|
|
|
import {
|
|
|
|
|
|
Executor,
|
|
|
|
|
|
IAccessService,
|
|
|
|
|
|
ICnameProxyService,
|
2025-06-18 12:29:43 +08:00
|
|
|
|
INotificationService, Notification,
|
2025-04-11 12:13:57 +08:00
|
|
|
|
Pipeline,
|
|
|
|
|
|
ResultType,
|
|
|
|
|
|
RunHistory,
|
|
|
|
|
|
RunnableCollection,
|
|
|
|
|
|
SysInfo,
|
|
|
|
|
|
UserInfo
|
|
|
|
|
|
} from "@certd/pipeline";
|
2025-07-14 23:02:47 +08:00
|
|
|
|
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";
|
2025-03-18 00:52:50 +08:00
|
|
|
|
import dayjs from "dayjs";
|
2025-07-14 23:02:47 +08:00
|
|
|
|
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";
|
2023-01-29 13:44:19 +08:00
|
|
|
|
|
2023-07-03 10:54:03 +08:00
|
|
|
|
const runningTasks: Map<string | number, Executor> = new Map();
|
2024-09-29 01:14:21 +08:00
|
|
|
|
|
2025-03-18 00:52:50 +08:00
|
|
|
|
|
2023-01-29 13:44:19 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 证书申请
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Provide()
|
2024-12-22 14:00:46 +08:00
|
|
|
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
2023-01-29 13:44:19 +08:00
|
|
|
|
export class PipelineService extends BaseService<PipelineEntity> {
|
|
|
|
|
|
@InjectEntityModel(PipelineEntity)
|
|
|
|
|
|
repository: Repository<PipelineEntity>;
|
2023-06-25 15:30:18 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
emailService: EmailService;
|
2023-01-29 13:44:19 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
accessService: AccessService;
|
|
|
|
|
|
@Inject()
|
2024-10-07 03:21:16 +08:00
|
|
|
|
cnameRecordService: CnameRecordService;
|
|
|
|
|
|
@Inject()
|
2023-01-29 13:44:19 +08:00
|
|
|
|
storageService: StorageService;
|
|
|
|
|
|
@Inject()
|
|
|
|
|
|
historyService: HistoryService;
|
|
|
|
|
|
@Inject()
|
|
|
|
|
|
historyLogService: HistoryLogService;
|
|
|
|
|
|
|
2024-10-13 01:27:08 +08:00
|
|
|
|
@Inject()
|
2024-10-14 03:17:10 +08:00
|
|
|
|
pluginConfigGetter: PluginConfigGetter;
|
2024-10-13 01:27:08 +08:00
|
|
|
|
|
2025-04-11 12:13:57 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
taskServiceBuilder: TaskServiceBuilder;
|
|
|
|
|
|
|
2024-10-26 23:54:49 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
sysSettingsService: SysSettingsService;
|
|
|
|
|
|
|
2024-09-29 01:14:21 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
userService: UserService;
|
|
|
|
|
|
|
2024-12-22 14:00:46 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
userSuiteService: UserSuiteService;
|
|
|
|
|
|
|
2023-01-29 13:44:19 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
cron: Cron;
|
|
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
@Config("certd")
|
2023-06-28 15:16:19 +08:00
|
|
|
|
private certdConfig: any;
|
|
|
|
|
|
|
2024-11-23 23:58:31 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
urlService: UrlService;
|
|
|
|
|
|
|
|
|
|
|
|
@Inject()
|
|
|
|
|
|
notificationService: NotificationService;
|
|
|
|
|
|
|
2024-10-31 22:35:05 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
dbAdapter: DbAdapter;
|
|
|
|
|
|
|
2024-12-22 14:00:46 +08:00
|
|
|
|
@Inject()
|
|
|
|
|
|
certInfoService: CertInfoService;
|
|
|
|
|
|
|
2024-10-09 02:34:28 +08:00
|
|
|
|
//@ts-ignore
|
2023-01-29 13:44:19 +08:00
|
|
|
|
getRepository() {
|
|
|
|
|
|
return this.repository;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-14 21:24:12 +08:00
|
|
|
|
async add(bean: PipelineEntity) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
bean.status = ResultType.none;
|
2024-09-03 11:42:05 +08:00
|
|
|
|
await this.save(bean);
|
2024-08-14 21:24:12 +08:00
|
|
|
|
return bean;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-14 00:19:55 +08:00
|
|
|
|
async page(pageReq: PageReq<PipelineEntity>) {
|
2025-06-25 14:41:27 +08:00
|
|
|
|
//模版流水线不要被查询出来
|
2025-07-14 23:02:47 +08:00
|
|
|
|
set(pageReq, "query.isTemplate", false);
|
2024-10-14 00:19:55 +08:00
|
|
|
|
const result = await super.page(pageReq);
|
2024-10-31 15:14:56 +08:00
|
|
|
|
await this.fillLastVars(result.records);
|
|
|
|
|
|
|
2025-07-14 01:36:40 +08:00
|
|
|
|
for (const item of result.records) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (!item.content) {
|
|
|
|
|
|
continue;
|
2025-07-14 01:36:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
const pipeline = JSON.parse(item.content);
|
2025-07-14 23:02:47 +08:00
|
|
|
|
let stepCount = 0;
|
2025-07-15 10:53:11 +08:00
|
|
|
|
if(pipeline.stages){
|
|
|
|
|
|
RunnableCollection.each(pipeline.stages, (runnable: any) => {
|
|
|
|
|
|
stepCount++;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-07-14 01:36:40 +08:00
|
|
|
|
// @ts-ignore
|
2025-07-14 23:02:47 +08:00
|
|
|
|
item.stepCount = stepCount;
|
2025-07-14 01:36:40 +08:00
|
|
|
|
// @ts-ignore
|
2025-07-15 10:53:11 +08:00
|
|
|
|
item.triggerCount = pipeline.triggers?.length;
|
2025-07-14 23:02:47 +08:00
|
|
|
|
delete item.content;
|
2025-07-14 01:36:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-31 15:14:56 +08:00
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async fillLastVars(records: PipelineEntity[]) {
|
2024-08-04 02:35:45 +08:00
|
|
|
|
const pipelineIds: number[] = [];
|
|
|
|
|
|
const recordMap = {};
|
2024-10-31 15:14:56 +08:00
|
|
|
|
for (const record of records) {
|
2024-08-04 02:35:45 +08:00
|
|
|
|
pipelineIds.push(record.id);
|
|
|
|
|
|
recordMap[record.id] = record;
|
2025-07-14 23:02:47 +08:00
|
|
|
|
record.title = record.title + "";
|
2024-08-04 02:35:45 +08:00
|
|
|
|
}
|
2024-10-20 01:55:15 +08:00
|
|
|
|
if (pipelineIds?.length > 0) {
|
|
|
|
|
|
const vars = await this.storageService.findPipelineVars(pipelineIds);
|
|
|
|
|
|
for (const varEntity of vars) {
|
|
|
|
|
|
const record = recordMap[varEntity.namespace];
|
|
|
|
|
|
if (record) {
|
|
|
|
|
|
const value = JSON.parse(varEntity.value);
|
|
|
|
|
|
record.lastVars = value.value;
|
|
|
|
|
|
}
|
2024-08-04 02:35:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-05-24 15:41:35 +08:00
|
|
|
|
public async registerTriggerById(pipelineId) {
|
2023-01-29 13:44:19 +08:00
|
|
|
|
if (pipelineId == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const info = await this.info(pipelineId);
|
|
|
|
|
|
if (info && !info.disabled) {
|
|
|
|
|
|
const pipeline = JSON.parse(info.content);
|
2025-07-14 23:02:47 +08:00
|
|
|
|
this.registerTriggers(pipeline, false);
|
2025-06-18 12:29:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
public async registerTrigger(info: PipelineEntity) {
|
2025-06-18 12:29:43 +08:00
|
|
|
|
if (info == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (info && !info.disabled) {
|
|
|
|
|
|
const pipeline = JSON.parse(info.content);
|
2025-07-14 23:02:47 +08:00
|
|
|
|
this.registerTriggers(pipeline, false);
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取详情
|
|
|
|
|
|
* @param id
|
|
|
|
|
|
*/
|
|
|
|
|
|
async detail(id) {
|
|
|
|
|
|
const pipeline = await this.info(id);
|
|
|
|
|
|
return new PipelineDetail(pipeline);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-24 17:09:06 +08:00
|
|
|
|
async update(bean: Partial<PipelineEntity>) {
|
2024-08-05 16:00:04 +08:00
|
|
|
|
//更新非trigger部分
|
2024-08-04 02:35:45 +08:00
|
|
|
|
await super.update(bean);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-29 13:44:19 +08:00
|
|
|
|
async save(bean: PipelineEntity) {
|
2024-09-03 11:42:05 +08:00
|
|
|
|
let old = null;
|
|
|
|
|
|
if (bean.id > 0) {
|
|
|
|
|
|
//修改
|
|
|
|
|
|
old = await this.info(bean.id);
|
|
|
|
|
|
}
|
|
|
|
|
|
const isUpdate = bean.id > 0 && old != null;
|
2024-12-22 14:00:46 +08:00
|
|
|
|
|
2025-06-18 12:29:43 +08:00
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
const pipeline = JSON.parse(bean.content || "{}");
|
2025-06-18 12:29:43 +08:00
|
|
|
|
RunnableCollection.initPipelineRunnableType(pipeline);
|
2024-12-22 14:00:46 +08:00
|
|
|
|
let domains = [];
|
2024-12-23 00:24:31 +08:00
|
|
|
|
if (pipeline.stages) {
|
|
|
|
|
|
RunnableCollection.each(pipeline.stages, (runnable: any) => {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (runnable.runnableType === "step" && runnable.type.indexOf("CertApply") >= 0) {
|
2024-12-23 00:24:31 +08:00
|
|
|
|
domains = runnable.input.domains || [];
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2024-12-22 14:00:46 +08:00
|
|
|
|
|
2024-10-26 23:54:49 +08:00
|
|
|
|
if (!isUpdate) {
|
|
|
|
|
|
//如果是添加,校验数量
|
2024-12-22 14:00:46 +08:00
|
|
|
|
await this.checkMaxPipelineCount(bean, pipeline, domains);
|
2024-08-14 21:24:12 +08:00
|
|
|
|
}
|
2024-10-26 23:54:49 +08:00
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (!bean.status) {
|
2025-06-07 01:19:37 +08:00
|
|
|
|
bean.status = ResultType.none;
|
|
|
|
|
|
}
|
2024-09-03 11:42:05 +08:00
|
|
|
|
if (!isUpdate) {
|
|
|
|
|
|
//如果是添加,先保存一下,获取到id,更新pipeline.id
|
|
|
|
|
|
await this.addOrUpdate(bean);
|
|
|
|
|
|
}
|
2025-06-18 12:29:43 +08:00
|
|
|
|
|
|
|
|
|
|
await this.doUpdatePipelineJson(bean, pipeline);
|
2024-12-22 14:00:46 +08:00
|
|
|
|
|
|
|
|
|
|
//保存域名信息到certInfo表
|
2025-07-14 23:02:47 +08:00
|
|
|
|
let fromType = "pipeline";
|
|
|
|
|
|
if (bean.type === "cert_upload") {
|
|
|
|
|
|
fromType = "upload";
|
|
|
|
|
|
}else if (bean.type === "cert_auto") {
|
|
|
|
|
|
fromType = "auto";
|
2025-03-18 00:52:50 +08:00
|
|
|
|
}
|
2025-06-18 12:29:43 +08:00
|
|
|
|
await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains, fromType);
|
2024-09-03 11:42:05 +08:00
|
|
|
|
return bean;
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-18 12:29:43 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 更新Pipeline, 包括trigger
|
|
|
|
|
|
* @param bean
|
|
|
|
|
|
* @param pipeline
|
|
|
|
|
|
*/
|
2025-07-14 23:02:47 +08:00
|
|
|
|
async doUpdatePipelineJson(bean: PipelineEntity, pipeline: Pipeline) {
|
2025-06-18 12:29:43 +08:00
|
|
|
|
await this.clearTriggers(bean);
|
|
|
|
|
|
if (pipeline.title) {
|
|
|
|
|
|
bean.title = pipeline.title;
|
|
|
|
|
|
}
|
|
|
|
|
|
pipeline.id = bean.id;
|
|
|
|
|
|
bean.content = JSON.stringify(pipeline);
|
|
|
|
|
|
await this.addOrUpdate(bean);
|
|
|
|
|
|
await this.registerTrigger(bean);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-22 14:00:46 +08:00
|
|
|
|
private async checkMaxPipelineCount(bean: PipelineEntity, pipeline: Pipeline, domains: string[]) {
|
|
|
|
|
|
// if (!isPlus()) {
|
|
|
|
|
|
// const count = await this.repository.count();
|
|
|
|
|
|
// if (count >= freeCount) {
|
|
|
|
|
|
// throw new NeedVIPException(`基础版最多只能创建${freeCount}条流水线`);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
if (isComm()) {
|
|
|
|
|
|
//校验pipelineCount
|
2024-12-25 11:42:42 +08:00
|
|
|
|
const suiteSetting = await this.userSuiteService.getSuiteSetting();
|
|
|
|
|
|
if (suiteSetting.enabled) {
|
|
|
|
|
|
const userSuite = await this.userSuiteService.getMySuiteDetail(bean.userId);
|
|
|
|
|
|
if (userSuite?.pipelineCount.max != -1 && userSuite?.pipelineCount.used + 1 > userSuite?.pipelineCount.max) {
|
|
|
|
|
|
throw new NeedSuiteException(`对不起,您最多只能创建${userSuite?.pipelineCount.max}条流水线,请购买或升级套餐`);
|
|
|
|
|
|
}
|
2024-12-22 14:00:46 +08:00
|
|
|
|
|
2024-12-25 11:42:42 +08:00
|
|
|
|
if (userSuite.domainCount.max != -1 && userSuite.domainCount.used + domains.length > userSuite.domainCount.max) {
|
|
|
|
|
|
throw new NeedSuiteException(`对不起,您最多只能添加${userSuite.domainCount.max}个域名,请购买或升级套餐`);
|
|
|
|
|
|
}
|
2024-12-22 14:00:46 +08:00
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
} else {
|
2025-06-05 16:29:13 +08:00
|
|
|
|
//非商业版校验用户最大流水线数量
|
|
|
|
|
|
const userId = bean.userId;
|
|
|
|
|
|
const userIsAdmin = await this.userService.isAdmin(userId);
|
|
|
|
|
|
if (!userIsAdmin) {
|
|
|
|
|
|
//非管理员用户,限制pipeline数量
|
|
|
|
|
|
const count = await this.repository.count({ where: { userId } });
|
|
|
|
|
|
const sysPublic = await this.sysSettingsService.getSetting<SysPublicSettings>(SysPublicSettings);
|
|
|
|
|
|
const limitUserPipelineCount = sysPublic.limitUserPipelineCount;
|
|
|
|
|
|
if (limitUserPipelineCount && limitUserPipelineCount > 0 && count >= limitUserPipelineCount) {
|
|
|
|
|
|
throw new NeedVIPException(`普通用户最多只能创建${limitUserPipelineCount}条流水线`);
|
|
|
|
|
|
}
|
2024-12-22 14:00:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-05 12:49:44 +08:00
|
|
|
|
async foreachPipeline(callback: (pipeline: PipelineEntity) => void) {
|
2023-01-29 13:44:19 +08:00
|
|
|
|
const idEntityList = await this.repository.find({
|
|
|
|
|
|
select: {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
id: true
|
2023-01-29 13:44:19 +08:00
|
|
|
|
},
|
|
|
|
|
|
where: {
|
|
|
|
|
|
disabled: false,
|
2025-07-14 23:02:47 +08:00
|
|
|
|
templateId: 0
|
|
|
|
|
|
}
|
2023-01-29 13:44:19 +08:00
|
|
|
|
});
|
|
|
|
|
|
const ids = idEntityList.map(item => {
|
|
|
|
|
|
return item.id;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
//id 分段
|
|
|
|
|
|
const idsSpan = [];
|
|
|
|
|
|
let arr = [];
|
|
|
|
|
|
for (let i = 0; i < ids.length; i++) {
|
|
|
|
|
|
if (i % 20 === 0) {
|
|
|
|
|
|
arr = [];
|
|
|
|
|
|
idsSpan.push(arr);
|
|
|
|
|
|
}
|
|
|
|
|
|
arr.push(ids[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//分段加载记录
|
|
|
|
|
|
for (const idArr of idsSpan) {
|
|
|
|
|
|
const list = await this.repository.findBy({
|
2025-07-14 23:02:47 +08:00
|
|
|
|
id: In(idArr)
|
2023-01-29 13:44:19 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
for (const entity of list) {
|
2024-08-05 12:49:44 +08:00
|
|
|
|
await callback(entity);
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-08-05 12:49:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async stopOtherUserPipeline(userId: number) {
|
|
|
|
|
|
await this.foreachPipeline(async entity => {
|
|
|
|
|
|
if (entity.userId !== userId) {
|
|
|
|
|
|
await this.clearTriggers(entity.id);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 应用启动后初始加载记录
|
|
|
|
|
|
*/
|
2024-08-13 20:30:42 +08:00
|
|
|
|
async onStartup(immediateTriggerOnce: boolean, onlyAdminUser: boolean) {
|
2024-08-05 12:49:44 +08:00
|
|
|
|
await this.foreachPipeline(async entity => {
|
2024-08-13 20:30:42 +08:00
|
|
|
|
if (onlyAdminUser && entity.userId !== 1) {
|
2024-08-05 12:49:44 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
const pipeline = JSON.parse(entity.content ?? "{}");
|
2024-08-05 12:49:44 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await this.registerTriggers(pipeline, immediateTriggerOnce);
|
|
|
|
|
|
} catch (e) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.error("加载定时trigger失败:", e);
|
2024-08-05 12:49:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.info("定时器数量:", this.cron.getTaskSize());
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-21 02:26:03 +08:00
|
|
|
|
async registerTriggers(pipeline?: Pipeline, immediateTriggerOnce = false) {
|
2023-01-29 13:44:19 +08:00
|
|
|
|
if (pipeline?.triggers == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (const trigger of pipeline.triggers) {
|
|
|
|
|
|
this.registerCron(pipeline.id, trigger);
|
|
|
|
|
|
}
|
2024-07-21 02:26:03 +08:00
|
|
|
|
|
|
|
|
|
|
if (immediateTriggerOnce) {
|
|
|
|
|
|
await this.trigger(pipeline.id);
|
2024-08-05 15:08:24 +08:00
|
|
|
|
await sleep(200);
|
2024-07-21 02:26:03 +08:00
|
|
|
|
}
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-02 18:36:12 +08:00
|
|
|
|
async trigger(id: any, stepId?: string) {
|
2024-12-24 17:09:06 +08:00
|
|
|
|
const entity: PipelineEntity = await this.info(id);
|
|
|
|
|
|
if (isComm()) {
|
|
|
|
|
|
await this.checkHasDeployCount(id, entity.userId);
|
|
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
await this.checkUserStatus(entity.userId);
|
2023-01-29 13:44:19 +08:00
|
|
|
|
this.cron.register({
|
|
|
|
|
|
name: `pipeline.${id}.trigger.once`,
|
|
|
|
|
|
cron: null,
|
|
|
|
|
|
job: async () => {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.info("用户手动启动job");
|
2024-07-15 00:30:33 +08:00
|
|
|
|
try {
|
2024-12-24 17:09:06 +08:00
|
|
|
|
await this.doRun(entity, null, stepId);
|
2024-07-15 00:30:33 +08:00
|
|
|
|
} catch (e) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.error("手动job执行失败:", e);
|
2024-07-15 00:30:33 +08:00
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
}
|
2023-01-29 13:44:19 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-24 17:09:06 +08:00
|
|
|
|
async checkHasDeployCount(pipelineId: number, userId: number) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return await this.userSuiteService.checkHasDeployCount(userId);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
if (e instanceof NeedSuiteException) {
|
|
|
|
|
|
logger.error(e.message);
|
|
|
|
|
|
await this.update({
|
|
|
|
|
|
id: pipelineId,
|
2025-07-14 23:02:47 +08:00
|
|
|
|
status: "no_deploy_count"
|
2024-12-24 17:09:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
throw e;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-25 14:41:27 +08:00
|
|
|
|
//@ts-ignore
|
2025-07-14 23:02:47 +08:00
|
|
|
|
async delete(id: any) {
|
2024-08-04 02:35:45 +08:00
|
|
|
|
await this.clearTriggers(id);
|
|
|
|
|
|
//TODO 删除storage
|
|
|
|
|
|
// const storage = new DbStorage(pipeline.userId, this.storageService);
|
|
|
|
|
|
// await storage.remove(pipeline.id);
|
|
|
|
|
|
await super.delete([id]);
|
2024-08-05 12:57:13 +08:00
|
|
|
|
await this.historyService.deleteByPipelineId(id);
|
|
|
|
|
|
await this.historyLogService.deleteByPipelineId(id);
|
2024-12-27 22:40:07 +08:00
|
|
|
|
await this.certInfoService.deleteByPipelineId(id);
|
2024-08-04 02:35:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-18 12:29:43 +08:00
|
|
|
|
async clearTriggers(id: number | PipelineEntity) {
|
2024-08-04 22:25:51 +08:00
|
|
|
|
if (id == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
let pipeline: PipelineEntity = null;
|
|
|
|
|
|
if (typeof id === "number") {
|
2025-06-18 12:29:43 +08:00
|
|
|
|
pipeline = await this.info(id);
|
2025-07-14 23:02:47 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
pipeline = id;
|
2025-06-18 12:29:43 +08:00
|
|
|
|
}
|
2023-06-28 12:46:29 +08:00
|
|
|
|
if (!pipeline) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const pipelineObj = JSON.parse(pipeline.content);
|
|
|
|
|
|
if (pipelineObj.triggers) {
|
|
|
|
|
|
for (const trigger of pipelineObj.triggers) {
|
|
|
|
|
|
this.removeCron(id, trigger);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
removeCron(pipelineId, trigger) {
|
|
|
|
|
|
const name = this.buildCronKey(pipelineId, trigger.id);
|
|
|
|
|
|
this.cron.remove(name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-29 13:44:19 +08:00
|
|
|
|
registerCron(pipelineId, trigger) {
|
2024-08-05 15:08:24 +08:00
|
|
|
|
if (pipelineId == null) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.warn("pipelineId为空,无法注册定时任务");
|
2024-08-05 15:08:24 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-29 13:44:19 +08:00
|
|
|
|
let cron = trigger.props?.cron;
|
|
|
|
|
|
if (cron == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2024-08-04 02:35:45 +08:00
|
|
|
|
cron = cron.trim();
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (cron.startsWith("* *")) {
|
2025-08-07 10:39:48 +08:00
|
|
|
|
cron = cron.replace("\* \*", "0 0");
|
2024-11-01 10:23:27 +08:00
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (cron.startsWith("*")) {
|
2025-08-07 10:39:48 +08:00
|
|
|
|
cron = cron.replace("\*", "0");
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
2024-08-05 15:08:24 +08:00
|
|
|
|
const triggerId = trigger.id;
|
|
|
|
|
|
const name = this.buildCronKey(pipelineId, triggerId);
|
2023-05-24 15:41:35 +08:00
|
|
|
|
this.cron.remove(name);
|
2023-01-29 13:44:19 +08:00
|
|
|
|
this.cron.register({
|
2023-05-24 15:41:35 +08:00
|
|
|
|
name,
|
2024-08-04 02:35:45 +08:00
|
|
|
|
cron,
|
2023-01-29 13:44:19 +08:00
|
|
|
|
job: async () => {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.info("定时任务触发:", pipelineId, triggerId);
|
2024-08-05 15:08:24 +08:00
|
|
|
|
if (pipelineId == null) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.warn("pipelineId为空,无法执行");
|
2024-08-05 15:08:24 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2024-07-18 11:17:13 +08:00
|
|
|
|
try {
|
2024-08-05 15:08:24 +08:00
|
|
|
|
await this.run(pipelineId, triggerId);
|
2024-07-18 11:17:13 +08:00
|
|
|
|
} catch (e) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.error("定时job执行失败:", e);
|
2024-07-18 11:17:13 +08:00
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
}
|
2023-01-29 13:44:19 +08:00
|
|
|
|
});
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.info("当前定时器数量:", this.cron.getTaskSize());
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-27 11:08:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param id
|
|
|
|
|
|
* @param triggerId =null手动启动
|
|
|
|
|
|
* @param stepId 如果传入ALL,清空所有状态
|
|
|
|
|
|
*/
|
2024-09-02 18:36:12 +08:00
|
|
|
|
async run(id: number, triggerId: string, stepId?: string) {
|
2023-01-29 13:44:19 +08:00
|
|
|
|
const entity: PipelineEntity = await this.info(id);
|
2024-12-24 17:09:06 +08:00
|
|
|
|
await this.doRun(entity, triggerId, stepId);
|
|
|
|
|
|
}
|
2024-08-04 02:49:40 +08:00
|
|
|
|
|
2024-12-24 17:09:06 +08:00
|
|
|
|
async doRun(entity: PipelineEntity, triggerId: string, stepId?: string) {
|
|
|
|
|
|
const id = entity.id;
|
2024-12-24 10:39:54 +08:00
|
|
|
|
let suite: UserSuiteEntity = null;
|
|
|
|
|
|
if (isComm()) {
|
2024-12-24 17:09:06 +08:00
|
|
|
|
suite = await this.checkHasDeployCount(id, entity.userId);
|
2024-12-24 10:39:54 +08:00
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await this.checkUserStatus(entity.userId);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
logger.info(e.message);
|
|
|
|
|
|
return;
|
2025-06-04 23:00:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-24 10:39:54 +08:00
|
|
|
|
|
2023-01-29 13:44:19 +08:00
|
|
|
|
const pipeline = JSON.parse(entity.content);
|
2024-08-23 17:41:02 +08:00
|
|
|
|
if (!pipeline.id) {
|
|
|
|
|
|
pipeline.id = id;
|
|
|
|
|
|
}
|
2023-01-29 13:44:19 +08:00
|
|
|
|
|
|
|
|
|
|
if (!pipeline.stages || pipeline.stages.length === 0) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const triggerType = this.getTriggerType(triggerId, pipeline);
|
|
|
|
|
|
if (triggerType == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (triggerType === "timer") {
|
2024-08-04 02:49:40 +08:00
|
|
|
|
if (entity.disabled) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-29 13:44:19 +08:00
|
|
|
|
const onChanged = async (history: RunHistory) => {
|
|
|
|
|
|
//保存执行历史
|
2023-05-23 18:01:20 +08:00
|
|
|
|
try {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.info("保存执行历史:", history.id);
|
2023-05-23 18:01:20 +08:00
|
|
|
|
await this.saveHistory(history);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
const pipelineEntity = new PipelineEntity();
|
2024-08-23 17:41:02 +08:00
|
|
|
|
pipelineEntity.id = id;
|
2025-07-14 23:02:47 +08:00
|
|
|
|
pipelineEntity.status = "error";
|
2023-05-23 18:01:20 +08:00
|
|
|
|
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
|
|
|
|
|
|
await this.update(pipelineEntity);
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.error("保存执行历史失败:", e);
|
2023-05-23 18:01:20 +08:00
|
|
|
|
throw e;
|
|
|
|
|
|
}
|
2023-01-29 13:44:19 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const userId = entity.userId;
|
2025-07-14 23:02:47 +08:00
|
|
|
|
const historyId = await this.historyService.start(entity, triggerType);
|
2024-09-29 01:14:21 +08:00
|
|
|
|
const userIsAdmin = await this.userService.isAdmin(userId);
|
|
|
|
|
|
const user: UserInfo = {
|
|
|
|
|
|
id: userId,
|
2025-07-14 23:02:47 +08:00
|
|
|
|
role: userIsAdmin ? "admin" : "user"
|
2024-09-29 01:14:21 +08:00
|
|
|
|
};
|
2024-12-04 12:36:17 +08:00
|
|
|
|
|
2025-04-11 12:13:57 +08:00
|
|
|
|
|
2024-12-04 12:36:17 +08:00
|
|
|
|
const sysInfo: SysInfo = {};
|
|
|
|
|
|
if (isComm()) {
|
|
|
|
|
|
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
|
|
|
|
|
|
sysInfo.title = siteInfo.title;
|
|
|
|
|
|
}
|
2025-04-11 12:13:57 +08:00
|
|
|
|
|
|
|
|
|
|
const taskServiceGetter = this.taskServiceBuilder.create({
|
2025-07-14 23:02:47 +08:00
|
|
|
|
userId
|
|
|
|
|
|
});
|
|
|
|
|
|
const accessGetter = await taskServiceGetter.get<IAccessService>("accessService");
|
|
|
|
|
|
const notificationGetter = await taskServiceGetter.get<INotificationService>("notificationService");
|
|
|
|
|
|
const cnameProxyService = await taskServiceGetter.get<ICnameProxyService>("cnameProxyService");
|
2023-01-29 13:44:19 +08:00
|
|
|
|
const executor = new Executor({
|
2024-09-29 01:14:21 +08:00
|
|
|
|
user,
|
2023-01-29 13:44:19 +08:00
|
|
|
|
pipeline,
|
|
|
|
|
|
onChanged,
|
2024-10-07 03:21:16 +08:00
|
|
|
|
accessService: accessGetter,
|
|
|
|
|
|
cnameProxyService,
|
2024-10-14 03:17:10 +08:00
|
|
|
|
pluginConfigService: this.pluginConfigGetter,
|
2023-01-29 13:44:19 +08:00
|
|
|
|
storage: new DbStorage(userId, this.storageService),
|
2023-06-25 15:30:18 +08:00
|
|
|
|
emailService: this.emailService,
|
2024-11-23 23:58:31 +08:00
|
|
|
|
urlService: this.urlService,
|
|
|
|
|
|
notificationService: notificationGetter,
|
2023-06-28 15:16:19 +08:00
|
|
|
|
fileRootDir: this.certdConfig.fileRootDir,
|
2024-12-04 12:36:17 +08:00
|
|
|
|
sysInfo,
|
2025-07-14 23:02:47 +08:00
|
|
|
|
serviceGetter: taskServiceGetter
|
2023-01-29 13:44:19 +08:00
|
|
|
|
});
|
2023-05-24 15:41:35 +08:00
|
|
|
|
try {
|
2023-07-03 11:16:46 +08:00
|
|
|
|
runningTasks.set(historyId, executor);
|
2023-05-24 15:41:35 +08:00
|
|
|
|
await executor.init();
|
2024-09-02 18:36:12 +08:00
|
|
|
|
if (stepId) {
|
|
|
|
|
|
// 清除该step的状态
|
|
|
|
|
|
executor.clearLastStatus(stepId);
|
|
|
|
|
|
}
|
2024-12-24 10:39:54 +08:00
|
|
|
|
const result = await executor.run(historyId, triggerType);
|
|
|
|
|
|
|
|
|
|
|
|
if (result === ResultType.success) {
|
|
|
|
|
|
if (isComm()) {
|
|
|
|
|
|
// 消耗成功次数
|
|
|
|
|
|
await this.userSuiteService.consumeDeployCount(suite, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-05-24 15:41:35 +08:00
|
|
|
|
} catch (e) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.error("执行失败:", e);
|
2024-07-15 00:30:33 +08:00
|
|
|
|
// throw e;
|
2023-07-03 10:54:03 +08:00
|
|
|
|
} finally {
|
2023-07-03 11:16:46 +08:00
|
|
|
|
runningTasks.delete(historyId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-23 18:02:14 +08:00
|
|
|
|
async cancel(historyId: number) {
|
2023-07-03 11:16:46 +08:00
|
|
|
|
const executor = runningTasks.get(historyId);
|
|
|
|
|
|
if (executor) {
|
2023-07-03 11:45:32 +08:00
|
|
|
|
await executor.cancel();
|
2023-05-24 15:41:35 +08:00
|
|
|
|
}
|
2024-07-18 11:17:13 +08:00
|
|
|
|
const entity = await this.historyService.info(historyId);
|
|
|
|
|
|
if (entity == null) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const pipeline: Pipeline = JSON.parse(entity.pipeline);
|
|
|
|
|
|
pipeline.status.status = ResultType.canceled;
|
|
|
|
|
|
pipeline.status.result = ResultType.canceled;
|
|
|
|
|
|
const runtime = new RunHistory(historyId, null, pipeline);
|
|
|
|
|
|
await this.saveHistory(runtime);
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private getTriggerType(triggerId, pipeline) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
let triggerType = "user";
|
2023-01-29 13:44:19 +08:00
|
|
|
|
if (triggerId != null) {
|
|
|
|
|
|
//如果不是手动触发
|
|
|
|
|
|
//查找trigger
|
|
|
|
|
|
const found = this.findTrigger(pipeline, triggerId);
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
|
//如果没有找到triggerId,说明被用户删掉了,这里再删除一次
|
|
|
|
|
|
this.cron.remove(this.buildCronKey(pipeline.id, triggerId));
|
|
|
|
|
|
triggerType = null;
|
|
|
|
|
|
} else {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
logger.info("timer trigger:" + found.id, found.title, found.cron);
|
|
|
|
|
|
triggerType = "timer";
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return triggerType;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private buildCronKey(pipelineId, triggerId) {
|
|
|
|
|
|
return `pipeline.${pipelineId}.trigger.${triggerId}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private findTrigger(pipeline, triggerId) {
|
|
|
|
|
|
for (const trigger of pipeline.triggers) {
|
|
|
|
|
|
if (trigger.id === triggerId) {
|
|
|
|
|
|
return trigger;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async saveHistory(history: RunHistory) {
|
|
|
|
|
|
//修改pipeline状态
|
|
|
|
|
|
const pipelineEntity = new PipelineEntity();
|
|
|
|
|
|
pipelineEntity.id = parseInt(history.pipeline.id);
|
2025-07-14 23:02:47 +08:00
|
|
|
|
pipelineEntity.status = history.pipeline.status.result + "";
|
2023-01-29 13:44:19 +08:00
|
|
|
|
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
|
|
|
|
|
|
await this.update(pipelineEntity);
|
|
|
|
|
|
|
|
|
|
|
|
const entity: HistoryEntity = new HistoryEntity();
|
|
|
|
|
|
entity.id = parseInt(history.id);
|
|
|
|
|
|
entity.userId = history.pipeline.userId;
|
2024-07-15 00:30:33 +08:00
|
|
|
|
entity.status = pipelineEntity.status;
|
2023-01-29 13:44:19 +08:00
|
|
|
|
entity.pipeline = JSON.stringify(history.pipeline);
|
2023-05-23 18:01:20 +08:00
|
|
|
|
entity.pipelineId = parseInt(history.pipeline.id);
|
2023-01-29 13:44:19 +08:00
|
|
|
|
await this.historyService.save(entity);
|
|
|
|
|
|
|
|
|
|
|
|
const logEntity: HistoryLogEntity = new HistoryLogEntity();
|
|
|
|
|
|
logEntity.id = entity.id;
|
|
|
|
|
|
logEntity.userId = entity.userId;
|
|
|
|
|
|
logEntity.pipelineId = entity.pipelineId;
|
|
|
|
|
|
logEntity.historyId = entity.id;
|
|
|
|
|
|
logEntity.logs = JSON.stringify(history.logs);
|
|
|
|
|
|
await this.historyLogService.addOrUpdate(logEntity);
|
|
|
|
|
|
}
|
2024-10-31 15:14:56 +08:00
|
|
|
|
|
2024-10-31 16:19:35 +08:00
|
|
|
|
async count(param: { userId?: any }) {
|
2024-10-31 15:14:56 +08:00
|
|
|
|
const count = await this.repository.count({
|
|
|
|
|
|
where: {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
userId: param.userId
|
|
|
|
|
|
}
|
2024-10-31 15:14:56 +08:00
|
|
|
|
});
|
|
|
|
|
|
return count;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-31 16:19:35 +08:00
|
|
|
|
async statusCount(param: { userId?: any } = {}) {
|
2024-10-31 15:14:56 +08:00
|
|
|
|
const statusCount = await this.repository
|
|
|
|
|
|
.createQueryBuilder()
|
2025-07-14 23:02:47 +08:00
|
|
|
|
.select("status")
|
|
|
|
|
|
.addSelect("count(1)", "count")
|
2024-10-31 15:14:56 +08:00
|
|
|
|
.where({
|
2025-07-14 23:02:47 +08:00
|
|
|
|
userId: param.userId
|
2024-10-31 15:14:56 +08:00
|
|
|
|
})
|
2025-07-14 23:02:47 +08:00
|
|
|
|
.groupBy("status")
|
2024-10-31 15:14:56 +08:00
|
|
|
|
.getRawMany();
|
|
|
|
|
|
return statusCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async latestExpiringList({ userId }: any) {
|
|
|
|
|
|
let list = await this.repository.find({
|
|
|
|
|
|
select: {
|
|
|
|
|
|
id: true,
|
|
|
|
|
|
title: true,
|
2025-07-14 23:02:47 +08:00
|
|
|
|
status: true
|
2024-10-31 15:14:56 +08:00
|
|
|
|
},
|
|
|
|
|
|
where: {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
userId
|
|
|
|
|
|
}
|
2024-10-31 15:14:56 +08:00
|
|
|
|
});
|
|
|
|
|
|
await this.fillLastVars(list);
|
|
|
|
|
|
list = list.filter(item => {
|
|
|
|
|
|
return item.lastVars?.certExpiresTime != null;
|
|
|
|
|
|
});
|
|
|
|
|
|
list = list.sort((a, b) => {
|
|
|
|
|
|
return a.lastVars.certExpiresTime - b.lastVars.certExpiresTime;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return list.slice(0, 5);
|
|
|
|
|
|
}
|
2024-10-31 16:19:35 +08:00
|
|
|
|
|
|
|
|
|
|
async createCountPerDay(param: { days: number } = { days: 7 }) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
const todayEnd = dayjs().endOf("day");
|
2024-10-31 16:19:35 +08:00
|
|
|
|
const result = await this.getRepository()
|
2025-07-14 23:02:47 +08:00
|
|
|
|
.createQueryBuilder("main")
|
|
|
|
|
|
.select(`${this.dbAdapter.date("main.createTime")} AS date`) // 将UNIX时间戳转换为日期
|
|
|
|
|
|
.addSelect("COUNT(1) AS count")
|
2024-10-31 16:19:35 +08:00
|
|
|
|
.where({
|
|
|
|
|
|
// 0点
|
2025-07-14 23:02:47 +08:00
|
|
|
|
createTime: MoreThan(todayEnd.add(-param.days, "day").toDate())
|
2024-10-31 16:19:35 +08:00
|
|
|
|
})
|
2025-07-14 23:02:47 +08:00
|
|
|
|
.groupBy("date")
|
2024-10-31 16:19:35 +08:00
|
|
|
|
.getRawMany();
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
2024-12-01 02:10:40 +08:00
|
|
|
|
|
|
|
|
|
|
async batchDelete(ids: number[], userId: number) {
|
|
|
|
|
|
for (const id of ids) {
|
|
|
|
|
|
await this.checkUserId(id, userId);
|
|
|
|
|
|
await this.delete(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async batchUpdateGroup(ids: number[], groupId: number, userId: any) {
|
|
|
|
|
|
await this.repository.update(
|
|
|
|
|
|
{
|
|
|
|
|
|
id: In(ids),
|
2025-07-14 23:02:47 +08:00
|
|
|
|
userId
|
2024-12-01 02:10:40 +08:00
|
|
|
|
},
|
|
|
|
|
|
{ groupId }
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-06-18 12:29:43 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
async batchUpdateTrigger(ids: number[], trigger: any, userId: any) {
|
2025-06-18 12:29:43 +08:00
|
|
|
|
|
|
|
|
|
|
const list = await this.find({
|
2025-07-14 23:02:47 +08:00
|
|
|
|
where: {
|
2025-06-18 12:29:43 +08:00
|
|
|
|
id: In(ids),
|
|
|
|
|
|
userId
|
|
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
});
|
2025-06-18 12:29:43 +08:00
|
|
|
|
|
|
|
|
|
|
for (const item of list) {
|
|
|
|
|
|
const pipeline = JSON.parse(item.content);
|
|
|
|
|
|
pipeline.triggers = [{
|
|
|
|
|
|
id: nanoid(),
|
2025-07-14 23:02:47 +08:00
|
|
|
|
title: "定时触发",
|
2025-06-18 12:29:43 +08:00
|
|
|
|
...trigger
|
2025-07-14 23:02:47 +08:00
|
|
|
|
}];
|
|
|
|
|
|
await this.doUpdatePipelineJson(item, pipeline);
|
2025-06-18 12:29:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any) {
|
2025-06-18 12:29:43 +08:00
|
|
|
|
|
|
|
|
|
|
const list = await this.find({
|
2025-07-14 23:02:47 +08:00
|
|
|
|
where: {
|
2025-06-18 12:29:43 +08:00
|
|
|
|
id: In(ids),
|
|
|
|
|
|
userId
|
|
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
});
|
2025-06-18 12:29:43 +08:00
|
|
|
|
|
|
|
|
|
|
for (const item of list) {
|
|
|
|
|
|
const pipeline = JSON.parse(item.content);
|
|
|
|
|
|
pipeline.notifications = [{
|
|
|
|
|
|
id: nanoid(),
|
2025-07-14 23:02:47 +08:00
|
|
|
|
title: "通知",
|
2025-06-18 12:29:43 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* type: NotificationType;
|
|
|
|
|
|
* when: NotificationWhen[];
|
|
|
|
|
|
* options: EmailOptions;
|
|
|
|
|
|
* notificationId: number;
|
|
|
|
|
|
* title: string;
|
|
|
|
|
|
* subType: string;
|
|
|
|
|
|
*/
|
|
|
|
|
|
type: "other",
|
|
|
|
|
|
...notification
|
2025-07-14 23:02:47 +08:00
|
|
|
|
}];
|
|
|
|
|
|
await this.doUpdatePipelineJson(item, pipeline);
|
2025-06-18 12:29:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-27 11:08:08 +08:00
|
|
|
|
async batchRerun(ids: number[], userId: any) {
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (!isPlus()) {
|
|
|
|
|
|
throw new NeedVIPException("此功能需要升级专业版");
|
2025-05-27 11:08:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!userId || ids.length === 0) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const list = await this.repository.find({
|
2025-07-14 23:02:47 +08:00
|
|
|
|
select: {
|
|
|
|
|
|
id: true
|
2025-05-27 11:08:08 +08:00
|
|
|
|
},
|
2025-07-14 23:02:47 +08:00
|
|
|
|
where: {
|
2025-05-27 11:08:08 +08:00
|
|
|
|
id: In(ids),
|
|
|
|
|
|
userId
|
|
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
});
|
2025-05-27 11:08:08 +08:00
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
ids = list.map(item => item.id);
|
2025-05-27 11:08:08 +08:00
|
|
|
|
|
|
|
|
|
|
//异步执行
|
2025-07-14 23:02:47 +08:00
|
|
|
|
this.startBatchRerun(ids);
|
2025-05-27 11:08:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-14 23:02:47 +08:00
|
|
|
|
async startBatchRerun(ids: number[]) {
|
2025-05-27 11:08:08 +08:00
|
|
|
|
//20条一批
|
|
|
|
|
|
const batchSize = 20;
|
|
|
|
|
|
for (let i = 0; i < ids.length; i += batchSize) {
|
|
|
|
|
|
const batchIds = ids.slice(i, i + batchSize);
|
2025-07-14 23:02:47 +08:00
|
|
|
|
const batchPromises = batchIds.map(async (id) => {
|
|
|
|
|
|
await this.run(id, null, "ALL");
|
2025-05-27 11:08:08 +08:00
|
|
|
|
});
|
2025-07-14 23:02:47 +08:00
|
|
|
|
await Promise.all(batchPromises);
|
2025-05-27 11:08:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-22 14:00:46 +08:00
|
|
|
|
|
|
|
|
|
|
async getUserPipelineCount(userId) {
|
|
|
|
|
|
return await this.repository.count({ where: { userId } });
|
|
|
|
|
|
}
|
2024-12-23 00:24:31 +08:00
|
|
|
|
|
|
|
|
|
|
async getSimplePipelines(pipelineIds: number[], userId?: number) {
|
|
|
|
|
|
return await this.repository.find({
|
|
|
|
|
|
select: {
|
|
|
|
|
|
id: true,
|
2025-07-14 23:02:47 +08:00
|
|
|
|
title: true
|
2024-12-23 00:24:31 +08:00
|
|
|
|
},
|
|
|
|
|
|
where: {
|
|
|
|
|
|
id: In(pipelineIds),
|
2025-07-14 23:02:47 +08:00
|
|
|
|
userId
|
|
|
|
|
|
}
|
2024-12-23 00:24:31 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-03-18 00:52:50 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-06-04 23:00:37 +08:00
|
|
|
|
private async checkUserStatus(userId: number) {
|
|
|
|
|
|
const userEntity = await this.userService.info(userId);
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (userEntity == null) {
|
|
|
|
|
|
throw new Error("用户不存在");
|
2025-06-04 23:00:37 +08:00
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (userEntity.status === 0) {
|
|
|
|
|
|
const message = `账户${userId}已被禁用,禁止运行流水线`;
|
|
|
|
|
|
throw new Error(message);
|
2025-06-04 23:00:37 +08:00
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
const sysPublic = await this.sysSettingsService.getPublicSettings();
|
|
|
|
|
|
if (isPlus() && sysPublic.userValidTimeEnabled === true) {
|
2025-06-04 23:00:37 +08:00
|
|
|
|
//校验用户有效期是否设置
|
2025-07-14 23:02:47 +08:00
|
|
|
|
if (userEntity.validTime != null && userEntity.validTime > 0) {
|
|
|
|
|
|
if (userEntity.validTime < new Date().getTime()) {
|
2025-06-04 23:00:37 +08:00
|
|
|
|
//用户已过期
|
2025-07-14 23:02:47 +08:00
|
|
|
|
const message = `账户${userId}已过有效期,禁止运行流水线`;
|
|
|
|
|
|
throw new Error(message);
|
2025-06-04 23:00:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-14 23:02:47 +08:00
|
|
|
|
|
|
|
|
|
|
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: "定时触发",
|
2025-07-14 23:26:54 +08:00
|
|
|
|
props:{
|
|
|
|
|
|
cron: randomCron,
|
|
|
|
|
|
},
|
2025-07-14 23:29:35 +08:00
|
|
|
|
type: "timer"
|
2025-07-14 23:02:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2023-01-29 13:44:19 +08:00
|
|
|
|
}
|