perf: 支持群晖

pull/189/head
xiaojunnuo 2024-09-09 16:01:42 +08:00
parent 18718f6a25
commit 5c270b6b9d
11 changed files with 60 additions and 46 deletions

View File

@ -1,6 +1,4 @@
import { AxiosInstance } from "axios";
import { IContext } from "../core/index.js"; import { IContext } from "../core/index.js";
export type HttpClient = AxiosInstance;
export type UserContext = IContext; export type UserContext = IContext;
export type PipelineContext = IContext; export type PipelineContext = IContext;

View File

@ -93,7 +93,7 @@ export class Executor {
await this.notification("success"); await this.notification("success");
} catch (e: any) { } catch (e: any) {
await this.notification("error", e); await this.notification("error", e);
this.logger.error("pipeline 执行失败", e.stack); this.logger.error("pipeline 执行失败", e);
} finally { } finally {
clearInterval(intervalFlushLogId); clearInterval(intervalFlushLogId);
await this.onChanged(this.runtime); await this.onChanged(this.runtime);
@ -237,14 +237,13 @@ export class Executor {
//判断是否需要跳过 //判断是否需要跳过
const lastNode = this.lastStatusMap.get(step.id); const lastNode = this.lastStatusMap.get(step.id);
const lastResult = lastNode?.status?.status; const lastResult = lastNode?.status?.status;
let inputChanged = true;
const lastInputHash = lastNode?.status?.inputHash;
if (lastInputHash && newInputHash && lastInputHash === newInputHash) {
//参数有变化
inputChanged = false;
}
if (step.strategy?.runStrategy === RunStrategy.SkipWhenSucceed) { if (step.strategy?.runStrategy === RunStrategy.SkipWhenSucceed) {
//如果是成功后跳过策略
let inputChanged = true;
const lastInputHash = lastNode?.status?.inputHash;
if (lastInputHash && newInputHash && lastInputHash === newInputHash) {
//参数有变化
inputChanged = false;
}
if (lastResult != null && lastResult === ResultType.success && !inputChanged) { if (lastResult != null && lastResult === ResultType.success && !inputChanged) {
step.status!.output = lastNode?.status?.output; step.status!.output = lastNode?.status?.output;
step.status!.files = lastNode?.status?.files; step.status!.files = lastNode?.status?.files;
@ -259,6 +258,7 @@ export class Executor {
lastStatus, lastStatus,
http, http,
logger: currentLogger, logger: currentLogger,
inputChanged,
accessService: this.options.accessService, accessService: this.options.accessService,
emailService: this.options.emailService, emailService: this.options.emailService,
pipelineContext: this.pipelineContext, pipelineContext: this.pipelineContext,

View File

@ -107,8 +107,8 @@ export class RunHistory {
logError(runnable: Runnable, e: Error) { logError(runnable: Runnable, e: Error) {
// @ts-ignore // @ts-ignore
const errorInfo = runnable.runnableType == "step" ? e.stack : e.message; const errorInfo = runnable.runnableType === "step" ? e : e.message;
this._loggers[runnable.id].error(`[${runnable.runnableType}] [${runnable.title}]<id:${runnable.id}> ${errorInfo}`); this._loggers[runnable.id].error(`[${runnable.runnableType}] [${runnable.title}]<id:${runnable.id}> `, errorInfo);
} }
finally(runnable: Runnable) { finally(runnable: Runnable) {

View File

@ -5,8 +5,8 @@ import { Logger } from "log4js";
import { IAccessService } from "../access/index.js"; import { IAccessService } from "../access/index.js";
import { IEmailService } from "../service/index.js"; import { IEmailService } from "../service/index.js";
import { IContext } from "../core/index.js"; import { IContext } from "../core/index.js";
import { AxiosInstance } from "axios";
import { ILogger, logger } from "../utils/index.js"; import { ILogger, logger } from "../utils/index.js";
import { HttpClient } from "../utils/util.request";
export enum ContextScope { export enum ContextScope {
global, global,
@ -60,11 +60,12 @@ export type TaskInstanceContext = {
pipeline: Pipeline; pipeline: Pipeline;
step: Step; step: Step;
logger: Logger; logger: Logger;
inputChanged: boolean;
accessService: IAccessService; accessService: IAccessService;
emailService: IEmailService; emailService: IEmailService;
pipelineContext: IContext; pipelineContext: IContext;
userContext: IContext; userContext: IContext;
http: AxiosInstance; http: HttpClient;
fileStore: FileStore; fileStore: FileStore;
lastStatus?: Runnable; lastStatus?: Runnable;
signal: AbortSignal; signal: AbortSignal;

View File

@ -1,5 +1,6 @@
import sleep from "./util.sleep.js"; import sleep from "./util.sleep.js";
import { request } from "./util.request.js"; import { http } from "./util.request.js";
export * from "./util.request.js";
export * from "./util.log.js"; export * from "./util.log.js";
export * from "./util.file.js"; export * from "./util.file.js";
export * from "./util.sp.js"; export * from "./util.sp.js";
@ -7,5 +8,5 @@ export * as promises from "./util.promise.js";
export * from "./util.hash.js"; export * from "./util.hash.js";
export const utils = { export const utils = {
sleep, sleep,
http: request, http,
}; };

View File

@ -1,29 +1,39 @@
import axios from "axios"; import axios, { AxiosRequestConfig } from "axios";
import { logger } from "./util.log.js"; import { logger } from "./util.log.js";
import { Logger } from "log4js"; import { Logger } from "log4js";
export class HttpError extends Error { export class HttpError extends Error {
request?: { url: string; method: string; data?: any };
response?: { data: any };
status?: number; status?: number;
statusText?: string; statusText?: string;
code?: string;
request?: { url: string; method: string; params?: any; data?: any };
response?: { data: any };
cause?: any;
constructor(error: any) { constructor(error: any) {
if (!error) { if (!error) {
return; return;
} }
super(error.message); super(error.message);
this.name = error.name; this.name = error.name;
this.stack = error.stack; this.code = error.code;
this.status = error?.response?.status; this.cause = error.cause;
this.statusText = error?.response?.statusText;
this.status = error.response?.status;
this.statusText = error.response?.statusText;
this.request = { this.request = {
url: error?.response?.config?.url, url: error.config?.url,
method: error?.response?.config?.method, method: error.config?.method,
data: error?.response?.config?.data, params: error.config?.params,
data: error.config?.data,
}; };
this.response = { this.response = {
data: error?.response?.data, data: error.response?.data,
}; };
delete error.response;
delete error.config;
delete error.request;
logger.error(error);
} }
} }
/** /**
@ -35,7 +45,7 @@ export function createAxiosService({ logger }: { logger: Logger }) {
// 请求拦截 // 请求拦截
service.interceptors.request.use( service.interceptors.request.use(
(config: any) => { (config: any) => {
logger.info(`http request:${config.url}method:${config.method}`); logger.info(`http request:${config.url}method:${config.method}params:${JSON.stringify(config.params)}`);
return config; return config;
}, },
(error: Error) => { (error: Error) => {
@ -67,9 +77,10 @@ export function createAxiosService({ logger }: { logger: Logger }) {
// default: break // default: break
// } // }
logger.error( logger.error(
`请求出错status:${error?.response?.status},statusText:${error?.response?.statusText},url:${error?.config?.url},method:${error?.config?.method}` `请求出错status:${error.response?.status},statusText:${error.response?.statusText},url:${error.config?.url},method:${error.config?.method}`
); );
logger.error("返回数据:", JSON.stringify(error?.response?.data)); logger.error("返回数据:", JSON.stringify(error.response?.data));
if (error instanceof AggregateError) { if (error instanceof AggregateError) {
logger.error(error); logger.error(error);
} }
@ -80,4 +91,8 @@ export function createAxiosService({ logger }: { logger: Logger }) {
return service; return service;
} }
export const request = createAxiosService({ logger }); export const http = createAxiosService({ logger }) as HttpClient;
export type HttpClientResponse<R> = any;
export type HttpClient = {
request<D = any, R = any>(config: AxiosRequestConfig<D>): Promise<HttpClientResponse<R>>;
};

View File

@ -45,7 +45,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
email!: string; email!: string;
@TaskInput({ @TaskInput({
title: "PFX密码", title: "PFX证书密码",
component: { component: {
name: "a-input-password", name: "a-input-password",
vModel: "value", vModel: "value",
@ -191,14 +191,14 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
*/ */
async condition() { async condition() {
if (this.forceUpdate) { if (this.forceUpdate) {
this.logger.info("强制更新证书选项已勾选,准备申请新证书");
return null; return null;
} }
let inputChanged = false; const inputChanged = this.ctx.inputChanged;
const oldInput = JSON.stringify(this.lastStatus?.input?.domains); if (inputChanged) {
const thisInput = JSON.stringify(this.domains); this.logger.info("输入参数变更,准备申请新证书");
if (oldInput !== thisInput) { return null;
inputChanged = true;
} }
let oldCert: CertReader | undefined = undefined; let oldCert: CertReader | undefined = undefined;
@ -212,11 +212,6 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
return null; return null;
} }
if (inputChanged) {
this.logger.info("输入参数变更,申请新证书");
return null;
}
const ret = this.isWillExpire(oldCert.expires, this.renewDays); const ret = this.isWillExpire(oldCert.expires, this.renewDays);
if (!ret.isWillExpire) { if (!ret.isWillExpire) {
this.logger.info(`证书还未过期:过期时间${dayjs(oldCert.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${ret.leftDays}`); this.logger.info(`证书还未过期:过期时间${dayjs(oldCert.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${ret.leftDays}`);

View File

@ -69,13 +69,15 @@ export class CertReader {
const tmpDerPath = this.saveToFile("der"); const tmpDerPath = this.saveToFile("der");
logger.info("本地文件写入成功"); logger.info("本地文件写入成功");
try { try {
await opts.handle({ return await opts.handle({
reader: this, reader: this,
tmpCrtPath: tmpCrtPath, tmpCrtPath: tmpCrtPath,
tmpKeyPath: tmpKeyPath, tmpKeyPath: tmpKeyPath,
tmpPfxPath: tmpPfxPath, tmpPfxPath: tmpPfxPath,
tmpDerPath: tmpDerPath, tmpDerPath: tmpDerPath,
}); });
} catch (err) {
throw err;
} finally { } finally {
//删除临时文件 //删除临时文件
logger.info("删除临时文件"); logger.info("删除临时文件");

View File

@ -47,6 +47,7 @@
"cache-manager": "^3.6.3", "cache-manager": "^3.6.3",
"cron-parser": "^4.9.0", "cron-parser": "^4.9.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"form-data": "^4.0.0",
"glob": "^10.4.5", "glob": "^10.4.5",
"https-proxy-agent": "^7.0.5", "https-proxy-agent": "^7.0.5",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
@ -67,6 +68,7 @@
"ssh2": "^1.15.0", "ssh2": "^1.15.0",
"strip-ansi": "^7.1.0", "strip-ansi": "^7.1.0",
"svg-captcha": "^1.4.0", "svg-captcha": "^1.4.0",
"syno": "^2.2.0",
"tencentcloud-sdk-nodejs": "^4.0.44", "tencentcloud-sdk-nodejs": "^4.0.44",
"typeorm": "^0.3.20" "typeorm": "^0.3.20"
}, },

View File

@ -1,13 +1,13 @@
import crypto from 'crypto'; import crypto from 'crypto';
import querystring from 'querystring'; import querystring from 'querystring';
import { DogeCloudAccess } from '../access.js'; import { DogeCloudAccess } from '../access.js';
import { AxiosInstance } from 'axios'; import { HttpClient } from '@certd/pipeline';
export class DogeClient { export class DogeClient {
accessKey: string; accessKey: string;
secretKey: string; secretKey: string;
http: AxiosInstance; http: HttpClient;
constructor(access: DogeCloudAccess, http: AxiosInstance) { constructor(access: DogeCloudAccess, http: HttpClient) {
this.accessKey = access.accessKey; this.accessKey = access.accessKey;
this.secretKey = access.secretKey; this.secretKey = access.secretKey;
this.http = http; this.http = http;

View File

@ -2,7 +2,7 @@ import { utils } from '@certd/pipeline';
export async function request(config: any): Promise<any> { export async function request(config: any): Promise<any> {
try { try {
return await utils.http(config); return await utils.http.request(config);
} catch (e) { } catch (e) {
const data = e.data || e.response?.data; const data = e.data || e.response?.data;
if (data) { if (data) {