2024-07-15 00:30:33 +08:00
|
|
|
import { Registrable } from "../registry/index.js";
|
|
|
|
|
import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../dt/index.js";
|
|
|
|
|
import { FileStore } from "../core/file-store.js";
|
2023-06-25 23:25:56 +08:00
|
|
|
import { Logger } from "log4js";
|
2024-07-15 00:30:33 +08:00
|
|
|
import { IAccessService } from "../access/index.js";
|
2024-10-07 03:21:16 +08:00
|
|
|
import { ICnameProxyService, IEmailService } from "../service/index.js";
|
2024-09-30 18:00:51 +08:00
|
|
|
import { IContext, PluginRequestHandleReq, RunnableCollection } from "../core/index.js";
|
2024-09-27 02:15:41 +08:00
|
|
|
import { ILogger, logger, utils } from "../utils/index.js";
|
2024-10-08 19:02:51 +08:00
|
|
|
import { HttpClient } from "../utils/index.js";
|
2024-09-19 14:23:15 +08:00
|
|
|
import dayjs from "dayjs";
|
2024-10-12 23:51:05 +08:00
|
|
|
import { IPluginConfigService } from "../service/config";
|
2024-10-13 01:27:08 +08:00
|
|
|
import { upperFirst } from "lodash-es";
|
2024-09-29 01:14:21 +08:00
|
|
|
export type UserInfo = {
|
|
|
|
|
role: "admin" | "user";
|
|
|
|
|
id: any;
|
|
|
|
|
};
|
2022-10-26 09:02:47 +08:00
|
|
|
export enum ContextScope {
|
|
|
|
|
global,
|
|
|
|
|
pipeline,
|
|
|
|
|
runtime,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type TaskOutputDefine = {
|
|
|
|
|
title: string;
|
|
|
|
|
value?: any;
|
2024-09-05 15:36:35 +08:00
|
|
|
type?: string;
|
2022-10-26 09:02:47 +08:00
|
|
|
};
|
2023-06-25 23:25:56 +08:00
|
|
|
|
2024-10-14 03:17:10 +08:00
|
|
|
export type TaskInputDefine = {
|
|
|
|
|
required?: boolean;
|
|
|
|
|
isSys?: boolean;
|
|
|
|
|
} & FormItemProps;
|
2022-10-26 09:02:47 +08:00
|
|
|
|
|
|
|
|
export type PluginDefine = Registrable & {
|
2022-12-27 12:32:09 +08:00
|
|
|
default?: any;
|
2024-07-21 02:26:03 +08:00
|
|
|
group?: string;
|
2024-09-19 17:38:51 +08:00
|
|
|
icon?: string;
|
2023-01-07 23:22:02 +08:00
|
|
|
input?: {
|
2022-10-26 09:02:47 +08:00
|
|
|
[key: string]: TaskInputDefine;
|
|
|
|
|
};
|
2023-01-07 23:22:02 +08:00
|
|
|
output?: {
|
2022-10-26 09:02:47 +08:00
|
|
|
[key: string]: TaskOutputDefine;
|
|
|
|
|
};
|
2022-12-27 12:32:09 +08:00
|
|
|
|
2023-01-07 23:22:02 +08:00
|
|
|
autowire?: {
|
|
|
|
|
[key: string]: any;
|
|
|
|
|
};
|
2023-06-25 16:25:23 +08:00
|
|
|
|
2024-08-30 18:50:53 +08:00
|
|
|
needPlus?: boolean;
|
2022-10-26 09:02:47 +08:00
|
|
|
};
|
|
|
|
|
|
2023-05-24 15:41:35 +08:00
|
|
|
export type ITaskPlugin = {
|
2023-05-09 10:16:49 +08:00
|
|
|
onInstance(): Promise<void>;
|
2022-12-27 12:32:09 +08:00
|
|
|
execute(): Promise<void>;
|
2024-09-27 02:15:41 +08:00
|
|
|
onRequest(req: PluginRequestHandleReq<any>): Promise<any>;
|
2023-05-24 15:41:35 +08:00
|
|
|
[key: string]: any;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type TaskResult = {
|
|
|
|
|
clearLastStatus?: boolean;
|
2023-06-25 23:25:56 +08:00
|
|
|
files?: FileItem[];
|
2024-08-04 02:35:45 +08:00
|
|
|
pipelineVars: Record<string, any>;
|
2024-10-15 12:59:40 +08:00
|
|
|
pipelinePrivateVars?: Record<string, any>;
|
2023-05-24 15:41:35 +08:00
|
|
|
};
|
2023-06-25 23:25:56 +08:00
|
|
|
export type TaskInstanceContext = {
|
2024-09-11 18:01:46 +08:00
|
|
|
//流水线定义
|
2023-06-25 23:25:56 +08:00
|
|
|
pipeline: Pipeline;
|
2024-09-11 18:01:46 +08:00
|
|
|
//步骤定义
|
2023-06-25 23:25:56 +08:00
|
|
|
step: Step;
|
2024-09-11 18:01:46 +08:00
|
|
|
//日志
|
2023-06-25 23:25:56 +08:00
|
|
|
logger: Logger;
|
2024-09-11 18:01:46 +08:00
|
|
|
//当前步骤输入参数跟上一次执行比较是否有变化
|
2024-09-09 16:01:42 +08:00
|
|
|
inputChanged: boolean;
|
2024-09-11 18:01:46 +08:00
|
|
|
//授权获取服务
|
2023-06-25 23:25:56 +08:00
|
|
|
accessService: IAccessService;
|
2024-09-11 18:01:46 +08:00
|
|
|
//邮件服务
|
2023-06-25 23:25:56 +08:00
|
|
|
emailService: IEmailService;
|
2024-10-07 03:21:16 +08:00
|
|
|
//cname记录服务
|
|
|
|
|
cnameProxyService: ICnameProxyService;
|
2024-10-12 23:51:05 +08:00
|
|
|
//插件配置服务
|
2024-10-13 01:27:08 +08:00
|
|
|
pluginConfigService: IPluginConfigService;
|
2024-09-11 18:01:46 +08:00
|
|
|
//流水线上下文
|
2023-06-25 23:25:56 +08:00
|
|
|
pipelineContext: IContext;
|
2024-09-11 18:01:46 +08:00
|
|
|
//用户上下文
|
2023-06-25 23:25:56 +08:00
|
|
|
userContext: IContext;
|
2024-09-11 18:01:46 +08:00
|
|
|
//http请求客户端
|
2024-09-09 16:01:42 +08:00
|
|
|
http: HttpClient;
|
2024-09-11 18:01:46 +08:00
|
|
|
//文件存储
|
2023-06-25 23:25:56 +08:00
|
|
|
fileStore: FileStore;
|
2024-09-11 18:01:46 +08:00
|
|
|
//上一次执行结果状态
|
2023-06-25 23:25:56 +08:00
|
|
|
lastStatus?: Runnable;
|
2024-09-11 18:01:46 +08:00
|
|
|
//用户取消信号
|
2024-08-23 23:26:31 +08:00
|
|
|
signal: AbortSignal;
|
2024-09-11 18:01:46 +08:00
|
|
|
//工具类
|
|
|
|
|
utils: typeof utils;
|
2024-10-07 03:21:16 +08:00
|
|
|
//用户信息
|
2024-09-29 01:14:21 +08:00
|
|
|
user: UserInfo;
|
2023-06-25 23:25:56 +08:00
|
|
|
};
|
|
|
|
|
|
2023-05-24 15:41:35 +08:00
|
|
|
export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
2024-10-15 12:59:40 +08:00
|
|
|
_result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {}, pipelinePrivateVars: {} };
|
2023-06-25 23:25:56 +08:00
|
|
|
ctx!: TaskInstanceContext;
|
2024-08-13 20:30:42 +08:00
|
|
|
logger!: ILogger;
|
|
|
|
|
accessService!: IAccessService;
|
|
|
|
|
|
2023-05-24 15:41:35 +08:00
|
|
|
clearLastStatus() {
|
2023-06-25 23:25:56 +08:00
|
|
|
this._result.clearLastStatus = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getFiles() {
|
|
|
|
|
return this._result.files;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 15:31:45 +08:00
|
|
|
checkSignal() {
|
|
|
|
|
if (this.ctx.signal && this.ctx.signal.aborted) {
|
|
|
|
|
throw new Error("用户取消");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-25 23:25:56 +08:00
|
|
|
setCtx(ctx: TaskInstanceContext) {
|
|
|
|
|
this.ctx = ctx;
|
2024-08-13 20:30:42 +08:00
|
|
|
this.logger = ctx.logger;
|
|
|
|
|
this.accessService = ctx.accessService;
|
2023-05-24 15:41:35 +08:00
|
|
|
}
|
2023-06-25 23:25:56 +08:00
|
|
|
|
2024-09-27 02:15:41 +08:00
|
|
|
async getAccess(accessId: string) {
|
|
|
|
|
if (accessId == null) {
|
|
|
|
|
throw new Error("您还没有配置授权");
|
|
|
|
|
}
|
|
|
|
|
const res = await this.ctx.accessService.getById(accessId);
|
|
|
|
|
if (res == null) {
|
|
|
|
|
throw new Error("授权不存在,可能已被删除,请前往任务配置里面重新选择授权");
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-28 23:15:37 +08:00
|
|
|
randomFileId() {
|
|
|
|
|
return Math.random().toString(36).substring(2, 9);
|
|
|
|
|
}
|
2023-06-25 23:25:56 +08:00
|
|
|
saveFile(filename: string, file: Buffer) {
|
|
|
|
|
const filePath = this.ctx.fileStore.writeFile(filename, file);
|
2024-05-30 10:12:48 +08:00
|
|
|
logger.info(`saveFile:${filePath}`);
|
2024-07-15 00:30:33 +08:00
|
|
|
this._result.files?.push({
|
2023-06-28 23:15:37 +08:00
|
|
|
id: this.randomFileId(),
|
2023-06-25 23:25:56 +08:00
|
|
|
filename,
|
|
|
|
|
path: filePath,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-15 00:30:33 +08:00
|
|
|
extendsFiles() {
|
|
|
|
|
if (this._result.files == null) {
|
|
|
|
|
this._result.files = [];
|
|
|
|
|
}
|
|
|
|
|
this._result.files.push(...(this.ctx.lastStatus?.status?.files || []));
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-25 23:25:56 +08:00
|
|
|
get pipeline() {
|
|
|
|
|
return this.ctx.pipeline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get step() {
|
|
|
|
|
return this.ctx.step;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 15:41:35 +08:00
|
|
|
async onInstance(): Promise<void> {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-06-25 23:25:56 +08:00
|
|
|
|
2023-05-24 15:41:35 +08:00
|
|
|
abstract execute(): Promise<void>;
|
2024-09-19 14:23:15 +08:00
|
|
|
|
|
|
|
|
appendTimeSuffix(name?: string) {
|
|
|
|
|
if (name == null) {
|
|
|
|
|
name = "certd";
|
|
|
|
|
}
|
|
|
|
|
return name + "_" + dayjs().format("YYYYMMDDHHmmss");
|
|
|
|
|
}
|
2024-09-27 02:15:41 +08:00
|
|
|
|
|
|
|
|
async onRequest(req: PluginRequestHandleReq<any>) {
|
|
|
|
|
if (!req.action) {
|
|
|
|
|
throw new Error("action is required");
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-30 18:00:51 +08:00
|
|
|
let methodName = req.action;
|
|
|
|
|
if (!req.action.startsWith("on")) {
|
2024-10-13 01:27:08 +08:00
|
|
|
methodName = `on${upperFirst(req.action)}`;
|
2024-09-30 18:00:51 +08:00
|
|
|
}
|
2024-09-27 02:15:41 +08:00
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
const method = this[methodName];
|
|
|
|
|
if (method) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
return await this[methodName](req.data);
|
|
|
|
|
}
|
|
|
|
|
throw new Error(`action ${req.action} not found`);
|
|
|
|
|
}
|
2024-09-29 01:14:21 +08:00
|
|
|
|
|
|
|
|
isAdmin() {
|
|
|
|
|
return this.ctx.user.role === "admin";
|
|
|
|
|
}
|
2024-09-30 18:00:51 +08:00
|
|
|
|
|
|
|
|
getStepFromPipeline(stepId: string) {
|
|
|
|
|
let found: any = null;
|
|
|
|
|
RunnableCollection.each(this.ctx.pipeline.stages, (step) => {
|
|
|
|
|
if (step.id === stepId) {
|
|
|
|
|
found = step;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getStepIdFromRefInput(ref = ".") {
|
|
|
|
|
return ref.split(".")[1];
|
|
|
|
|
}
|
2022-10-26 09:02:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type OutputVO = {
|
|
|
|
|
key: string;
|
|
|
|
|
title: string;
|
|
|
|
|
value: any;
|
|
|
|
|
};
|