feat: save files

pull/21/merge
xiaojunnuo 2023-06-25 23:25:56 +08:00
parent 2851a33eb2
commit 671d273e2f
30 changed files with 253 additions and 222 deletions

View File

@ -1,7 +1,7 @@
import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts"; import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
import _ from "lodash"; import _ from "lodash";
import { RunHistory, RunnableCollection } from "./run-history"; import { RunHistory, RunnableCollection } from "./run-history";
import { AbstractTaskPlugin, PluginDefine, pluginRegistry } from "../plugin"; import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext } from "../plugin";
import { ContextFactory, IContext } from "./context"; import { ContextFactory, IContext } from "./context";
import { IStorage } from "./storage"; import { IStorage } from "./storage";
import { logger } from "../utils/util.log"; import { logger } from "../utils/util.log";
@ -11,6 +11,7 @@ import { IAccessService } from "../access";
import { RegistryItem } from "../registry"; import { RegistryItem } from "../registry";
import { Decorator } from "../decorator"; import { Decorator } from "../decorator";
import { IEmailService } from "../service"; import { IEmailService } from "../service";
import { FileStore } from "./file-store";
export type ExecutorOptions = { export type ExecutorOptions = {
userId: any; userId: any;
@ -19,6 +20,7 @@ export type ExecutorOptions = {
onChanged: (history: RunHistory) => Promise<void>; onChanged: (history: RunHistory) => Promise<void>;
accessService: IAccessService; accessService: IAccessService;
emailService: IEmailService; emailService: IEmailService;
fileRootDir?: string;
}; };
export class Executor { export class Executor {
pipeline: Pipeline; pipeline: Pipeline;
@ -59,7 +61,7 @@ export class Executor {
}); });
await this.notification("success"); await this.notification("success");
} catch (e) { } catch (e) {
await this.notification("error"); await this.notification("error", e);
this.logger.error("pipeline 执行失败", e); this.logger.error("pipeline 执行失败", e);
} finally { } finally {
await this.pipelineContext.setObj("lastRuntime", this.runtime); await this.pipelineContext.setObj("lastRuntime", this.runtime);
@ -185,28 +187,38 @@ export class Executor {
} }
}); });
const context: any = { const taskCtx: TaskInstanceContext = {
pipeline: this.pipeline,
step,
lastStatus,
http: request,
logger: this.runtime._loggers[step.id], logger: this.runtime._loggers[step.id],
accessService: this.options.accessService, accessService: this.options.accessService,
emailService: this.options.emailService,
pipelineContext: this.pipelineContext, pipelineContext: this.pipelineContext,
lastStatus,
userContext: this.contextFactory.getContext("user", this.options.userId), userContext: this.contextFactory.getContext("user", this.options.userId),
http: request, fileStore: new FileStore({
scope: this.pipeline.id,
parent: this.runtime.id,
rootDir: this.options.fileRootDir,
}),
}; };
Decorator.inject(define.autowire, instance, context); instance.setCtx(taskCtx);
await instance.onInstance(); await instance.onInstance();
await instance.execute(); await instance.execute();
if (instance.result.clearLastStatus) { if (instance._result.clearLastStatus) {
this.lastStatusMap.clear(); this.lastStatusMap.clear();
} }
//输出到output context //输出到output context
_.forEach(define.output, (item, key) => { _.forEach(define.output, (item, key) => {
step!.status!.output[key] = instance[key]; step.status!.output[key] = instance[key];
const stepOutputKey = `step.${step.id}.${key}`; const stepOutputKey = `step.${step.id}.${key}`;
this.runtime.context[stepOutputKey] = instance[key]; this.runtime.context[stepOutputKey] = instance[key];
}); });
step.status!.files = instance.getFiles();
} }
async notification(when: NotificationWhen, error?: any) { async notification(when: NotificationWhen, error?: any) {

View File

@ -0,0 +1,38 @@
import { fileUtils } from "../utils/util.file";
import dayjs from "dayjs";
import path from "path";
import fs from "fs";
export type FileStoreOptions = {
rootDir?: string;
scope: string;
parent: string;
};
export class FileStore {
rootDir: string;
scope: string;
parent: string;
constructor(options?: FileStoreOptions) {
this.rootDir = fileUtils.getFileRootDir(options?.rootDir);
this.scope = options?.scope || "0";
this.parent = options?.parent || "0";
}
readFile(filePath: string) {
if (!fs.existsSync(filePath)) {
return null;
}
return fs.readFileSync(filePath);
}
writeFile(filename: string, file: Buffer) {
const localPath = this.buildFilePath(filename);
fs.writeFileSync(localPath, file);
return localPath;
}
private buildFilePath(filename: string) {
return path.join(this.rootDir, this.scope, dayjs().format("YYYY-MM-DD"), this.parent, filename);
}
}

View File

@ -1,7 +1,6 @@
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import { fileUtils } from "../utils/util.file";
export interface IStorage { export interface IStorage {
get(scope: string, namespace: string, version: string, key: string): Promise<string | null>; get(scope: string, namespace: string, version: string, key: string): Promise<string | null>;
@ -12,15 +11,7 @@ export interface IStorage {
export class FileStorage implements IStorage { export class FileStorage implements IStorage {
root: string; root: string;
constructor(rootDir?: string) { constructor(rootDir?: string) {
if (rootDir == null) { this.root = fileUtils.getFileRootDir(rootDir);
const userHome = process.env.HOME || process.env.USERPROFILE;
rootDir = userHome + "/.certd/storage/";
}
this.root = rootDir;
if (!fs.existsSync(this.root)) {
fs.mkdirSync(this.root, { recursive: true });
}
} }
async remove(scope: string, namespace: string, version: string, key: string): Promise<void> { async remove(scope: string, namespace: string, version: string, key: string): Promise<void> {

View File

@ -55,6 +55,10 @@ export type Trigger = {
type: string; type: string;
}; };
export type FileItem = {
filename: string;
path: string;
};
export type Runnable = { export type Runnable = {
id: string; id: string;
title: string; title: string;
@ -64,6 +68,7 @@ export type Runnable = {
default?: { default?: {
[key: string]: any; [key: string]: any;
}; };
files?: FileItem[];
}; };
export type EmailOptions = { export type EmailOptions = {
@ -113,6 +118,7 @@ export type HistoryResultGroup = {
export type HistoryResult = { export type HistoryResult = {
input: any; input: any;
output: any; output: any;
files?: FileItem[];
/** /**
* *
*/ */

View File

@ -1,5 +1,11 @@
import { Registrable } from "../registry"; import { Registrable } from "../registry";
import { FormItemProps } from "../d.ts"; import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../d.ts";
import { FileStore } from "../core/file-store";
import { Logger } from "log4js";
import { IAccessService } from "../access";
import { IEmailService } from "../service";
import { IContext } from "../core";
import { AxiosInstance } from "axios";
export enum ContextScope { export enum ContextScope {
global, global,
@ -7,16 +13,11 @@ export enum ContextScope {
runtime, runtime,
} }
export type Storage = {
scope: ContextScope;
path: string;
};
export type TaskOutputDefine = { export type TaskOutputDefine = {
title: string; title: string;
value?: any; value?: any;
storage?: Storage;
}; };
export type TaskInputDefine = FormItemProps; export type TaskInputDefine = FormItemProps;
export type PluginDefine = Registrable & { export type PluginDefine = Registrable & {
@ -47,15 +48,56 @@ export type ITaskPlugin = {
export type TaskResult = { export type TaskResult = {
clearLastStatus?: boolean; clearLastStatus?: boolean;
files?: FileItem[];
}; };
export type TaskInstanceContext = {
pipeline: Pipeline;
step: Step;
logger: Logger;
accessService: IAccessService;
emailService: IEmailService;
pipelineContext: IContext;
userContext: IContext;
http: AxiosInstance;
fileStore: FileStore;
lastStatus?: Runnable;
};
export abstract class AbstractTaskPlugin implements ITaskPlugin { export abstract class AbstractTaskPlugin implements ITaskPlugin {
result: TaskResult = {}; _result: TaskResult = { clearLastStatus: false, files: [] };
ctx!: TaskInstanceContext;
clearLastStatus() { clearLastStatus() {
this.result.clearLastStatus = true; this._result.clearLastStatus = true;
} }
getFiles() {
return this._result.files;
}
setCtx(ctx: TaskInstanceContext) {
this.ctx = ctx;
}
saveFile(filename: string, file: Buffer) {
const filePath = this.ctx.fileStore.writeFile(filename, file);
this._result.files!.push({
filename,
path: filePath,
});
}
get pipeline() {
return this.ctx.pipeline;
}
get step() {
return this.ctx.step;
}
async onInstance(): Promise<void> { async onInstance(): Promise<void> {
return; return;
} }
abstract execute(): Promise<void>; abstract execute(): Promise<void>;
} }

View File

@ -61,3 +61,11 @@ export function TaskOutput(output?: TaskOutputDefine): PropertyDecorator {
Reflect.defineMetadata(PLUGIN_OUTPUT_KEY, output, target, propertyKey); Reflect.defineMetadata(PLUGIN_OUTPUT_KEY, output, target, propertyKey);
}; };
} }
export const PLUGIN_DOWNLOAD_KEY = "pipeline:plugin:download";
export function TaskDownload(output?: TaskOutputDefine): PropertyDecorator {
return (target, propertyKey) => {
target = Decorator.target(target, propertyKey);
Reflect.defineMetadata(PLUGIN_DOWNLOAD_KEY, output, target, propertyKey);
};
}

View File

@ -0,0 +1,16 @@
import fs from "fs";
function getFileRootDir(rootDir?: string) {
if (rootDir == null) {
const userHome = process.env.HOME || process.env.USERPROFILE;
rootDir = userHome + "/.certd/storage/";
}
if (!fs.existsSync(rootDir)) {
fs.mkdirSync(rootDir, { recursive: true });
}
return rootDir;
}
export const fileUtils = {
getFileRootDir,
};

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline";
// @ts-ignore // @ts-ignore
import { ROAClient } from "@alicloud/pop-core"; import { ROAClient } from "@alicloud/pop-core";
import { AliyunAccess } from "../../access"; import { AliyunAccess } from "../../access";
@ -103,11 +103,13 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractTaskPlugin {
}) })
accessId!: string; accessId!: string;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire()
logger!: ILogger; logger!: ILogger;
async onInstance(): Promise<void> {
this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
console.log("开始部署证书到阿里云cdn"); console.log("开始部署证书到阿里云cdn");
const { regionId, ingressClass, clusterId, isPrivateIpAddress, cert } = this; const { regionId, ingressClass, clusterId, isPrivateIpAddress, cert } = this;

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline";
import dayjs from "dayjs"; import dayjs from "dayjs";
import Core from "@alicloud/pop-core"; import Core from "@alicloud/pop-core";
import RPCClient from "@alicloud/pop-core"; import RPCClient from "@alicloud/pop-core";
@ -17,7 +17,7 @@ import { AliyunAccess } from "../../access";
export class DeployCertToAliyunCDN extends AbstractTaskPlugin { export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
@TaskInput({ @TaskInput({
title: "CDN加速域名", title: "CDN加速域名",
helper: "你在阿里云上配置的CDN加速域名比如certd.docmirror.cn", helper: "你在阿里云上配置的CDN加速域名比如:certd.docmirror.cn",
required: true, required: true,
}) })
domainName!: string; domainName!: string;
@ -49,13 +49,13 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
}) })
accessId!: string; accessId!: string;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire()
logger!: ILogger; logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function
async onInstance() {} async onInstance() {
this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
console.log("开始部署证书到阿里云cdn"); console.log("开始部署证书到阿里云cdn");
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess; const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, IsTaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, IsTaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
import Core from "@alicloud/pop-core"; import Core from "@alicloud/pop-core";
import { AliyunAccess } from "../../access"; import { AliyunAccess } from "../../access";
import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils"; import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils";
@ -59,14 +59,13 @@ export class UploadCertToAliyun extends AbstractTaskPlugin {
}) })
aliyunCertId!: string; aliyunCertId!: string;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire()
logger!: Logger; logger!: Logger;
// eslint-disable-next-line @typescript-eslint/no-empty-function async onInstance() {
async onInstance() {} this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
console.log("开始部署证书到阿里云cdn"); console.log("开始部署证书到阿里云cdn");

View File

@ -1,9 +1,8 @@
import { AbstractTaskPlugin, Autowire, HttpClient, IAccessService, IContext, IsTaskPlugin, RunStrategy, Step, TaskInput, TaskOutput } from "@certd/pipeline"; import { AbstractTaskPlugin, Decorator, HttpClient, IAccessService, IContext, IsTaskPlugin, RunStrategy, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { AcmeService, CertInfo } from "./acme"; import { AcmeService, CertInfo } from "./acme";
import _ from "lodash"; import _ from "lodash";
import { Logger } from "log4js"; import { Logger } from "log4js";
import { Decorator } from "@certd/pipeline";
import { DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider"; import { DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
import { CertReader } from "./cert-reader"; import { CertReader } from "./cert-reader";
@ -109,22 +108,11 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
}) })
csrInfo: any; csrInfo: any;
// @ts-ignore acme!: AcmeService;
acme: AcmeService;
@Autowire()
logger!: Logger; logger!: Logger;
@Autowire()
userContext!: IContext; userContext!: IContext;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire()
http!: HttpClient; http!: HttpClient;
@Autowire()
lastStatus!: Step; lastStatus!: Step;
@TaskOutput({ @TaskOutput({
@ -133,13 +121,19 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
cert?: CertInfo; cert?: CertInfo;
async onInstance() { async onInstance() {
this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
this.userContext = this.ctx.userContext;
this.http = this.ctx.http;
this.lastStatus = this.ctx.lastStatus as Step;
this.acme = new AcmeService({ userContext: this.userContext, logger: this.logger }); this.acme = new AcmeService({ userContext: this.userContext, logger: this.logger });
} }
async execute(): Promise<void> { async execute(): Promise<void> {
const oldCert = await this.condition(); const oldCert = await this.condition();
if (oldCert != null) { if (oldCert != null) {
return this.output(oldCert); return this.output(oldCert.toCertInfo());
} }
const cert = await this.doCertApply(); const cert = await this.doCertApply();
if (cert != null) { if (cert != null) {

View File

@ -61,6 +61,9 @@ export class SshClient {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.connect({ this.connect({
connectConf, connectConf,
onError(err: any) {
reject(err);
},
onReady: (conn: any) => { onReady: (conn: any) => {
conn.exec(script, (err: Error, stream: any) => { conn.exec(script, (err: Error, stream: any) => {
if (err) { if (err) {
@ -98,6 +101,10 @@ export class SshClient {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.connect({ this.connect({
connectConf, connectConf,
onError: (err: any) => {
this.logger.error(err);
reject(err);
},
onReady: (conn: any) => { onReady: (conn: any) => {
conn.shell((err: Error, stream: any) => { conn.shell((err: Error, stream: any) => {
if (err) { if (err) {
@ -122,10 +129,13 @@ export class SshClient {
}); });
} }
connect(options: { connectConf: any; onReady: any }) { connect(options: { connectConf: any; onReady: any; onError: any }) {
const { connectConf, onReady } = options; const { connectConf, onReady, onError } = options;
const conn = new ssh2.Client(); const conn = new ssh2.Client();
conn conn
.on("error", (err: any) => {
onError(err);
})
.on("ready", () => { .on("ready", () => {
this.logger.info("Client :: ready"); this.logger.info("Client :: ready");
onReady(conn); onReady(conn);

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline";
import { SshClient } from "../../lib/ssh"; import { SshClient } from "../../lib/ssh";
@IsTaskPlugin({ @IsTaskPlugin({
@ -32,13 +32,12 @@ export class HostShellExecutePlugin extends AbstractTaskPlugin {
}) })
script!: string; script!: string;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire()
logger!: ILogger; logger!: ILogger;
async onInstance() {
// eslint-disable-next-line @typescript-eslint/no-empty-function this.accessService = this.ctx.accessService;
async onInstance() {} this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
const { script, accessId } = this; const { script, accessId } = this;
const connectConf = await this.accessService.getById(accessId); const connectConf = await this.accessService.getById(accessId);

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
import { SshClient } from "../../lib/ssh"; import { SshClient } from "../../lib/ssh";
import { CertInfo, CertReader } from "@certd/plugin-cert"; import { CertInfo, CertReader } from "@certd/plugin-cert";
import * as fs from "fs"; import * as fs from "fs";
@ -49,11 +49,6 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
}) })
sudo!: boolean; sudo!: boolean;
@Autowire()
accessService!: IAccessService;
@Autowire()
logger!: ILogger;
@TaskOutput({ @TaskOutput({
title: "证书保存路径", title: "证书保存路径",
}) })
@ -64,7 +59,13 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
}) })
hostKeyPath!: string; hostKeyPath!: string;
async onInstance() {} accessService!: IAccessService;
logger!: ILogger;
async onInstance() {
this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
const { crtPath, keyPath, cert, accessId, sudo } = this; const { crtPath, keyPath, cert, accessId, sudo } = this;
const certReader = new CertReader(cert); const certReader = new CertReader(cert);

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput } from "@certd/pipeline";
import tencentcloud from "tencentcloud-sdk-nodejs/index"; import tencentcloud from "tencentcloud-sdk-nodejs/index";
import { TencentAccess } from "../../access"; import { TencentAccess } from "../../access";
import { CertInfo } from "@certd/plugin-cert"; import { CertInfo } from "@certd/plugin-cert";
@ -47,14 +47,14 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin {
}) })
domainName!: string; domainName!: string;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire()
logger!: ILogger; logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function async onInstance() {
async onInstance() {} this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
const accessProvider: TencentAccess = (await this.accessService.getById(this.accessId)) as TencentAccess; const accessProvider: TencentAccess = (await this.accessService.getById(this.accessId)) as TencentAccess;

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline";
import tencentcloud from "tencentcloud-sdk-nodejs/index"; import tencentcloud from "tencentcloud-sdk-nodejs/index";
import { TencentAccess } from "../../access"; import { TencentAccess } from "../../access";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -71,14 +71,13 @@ export class DeployToClbPlugin extends AbstractTaskPlugin {
}) })
accessId!: string; accessId!: string;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire()
logger!: ILogger; logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function async onInstance() {
async onInstance() {} this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
const accessProvider = (await this.accessService.getById(this.accessId)) as TencentAccess; const accessProvider = (await this.accessService.getById(this.accessId)) as TencentAccess;
const client = this.getClient(accessProvider, this.region); const client = this.getClient(accessProvider, this.region);

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, IsTaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, IsTaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline";
import tencentcloud from "tencentcloud-sdk-nodejs/index"; import tencentcloud from "tencentcloud-sdk-nodejs/index";
import { K8sClient } from "@certd/plugin-util"; import { K8sClient } from "@certd/plugin-util";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -80,15 +80,12 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractTaskPlugin {
}) })
cert!: any; cert!: any;
@Autowire()
logger!: Logger; logger!: Logger;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
async onInstance() {
// eslint-disable-next-line @typescript-eslint/no-empty-function this.accessService = this.ctx.accessService;
async onInstance() {} this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
const accessProvider = this.accessService.getById(this.accessId); const accessProvider = this.accessService.getById(this.accessId);
const tkeClient = this.getTkeClient(accessProvider, this.region); const tkeClient = this.getTkeClient(accessProvider, this.region);

View File

@ -1,4 +1,4 @@
import { AbstractTaskPlugin, Autowire, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; import { AbstractTaskPlugin, IAccessService, ILogger, IsTaskPlugin, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
import tencentcloud from "tencentcloud-sdk-nodejs/index"; import tencentcloud from "tencentcloud-sdk-nodejs/index";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -42,14 +42,13 @@ export class UploadToTencentPlugin extends AbstractTaskPlugin {
}) })
tencentCertId?: string; tencentCertId?: string;
@Autowire()
accessService!: IAccessService; accessService!: IAccessService;
@Autowire()
logger!: ILogger; logger!: ILogger;
// eslint-disable-next-line @typescript-eslint/no-empty-function async onInstance() {
async onInstance() {} this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
}
async execute(): Promise<void> { async execute(): Promise<void> {
const { accessId, name, cert } = this; const { accessId, name, cert } = this;

View File

@ -63,6 +63,7 @@
"@rollup/plugin-commonjs": "^23.0.4", "@rollup/plugin-commonjs": "^23.0.4",
"@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-node-resolve": "^15.0.1",
"@types/chai": "^4.3.4", "@types/chai": "^4.3.4",
"@types/lodash": "^4.14.195",
"@types/mocha": "^10.0.1", "@types/mocha": "^10.0.1",
"@types/node": "^18.11.15", "@types/node": "^18.11.15",
"@typescript-eslint/eslint-plugin": "^5.46.1", "@typescript-eslint/eslint-plugin": "^5.46.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,106 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
<svg width="500" height="500" viewBox="0 0 500.000000 500.000000"
xmlns="http://www.w3.org/2000/svg" >
width="210mm" <path d="M28.34 56.68h28.34V36.12H28.34a7.79 7.79 0 1 1 0-15.58h19.84v9.05h8.5V12H28.34a16.29 16.29 0 0 0 0 32.58h19.84v3.56H28.34a19.84 19.84 0 0 1 0-39.68h28.34V0H28.34a28.34 28.34 0 0 0 0 56.68z"
height="210mm" transform="translate(70, 76) scale(6,6)"
viewBox="0 0 210 210" ></path>
version="1.1"
id="svg8"
>
<g id="layer1" style="display:inline">
<path
style="fill:#002255;stroke:none;stroke-width:0.625348"
d="M 35.587501,69.766667 V 59.766664 h 70.000109 69.99991 v 10.000003 9.999997 H 105.58761 35.587501 Z"
id="path12" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2"
width="32.244232"
height="20"
x="71.506088"
y="106.64581" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-8"
width="32.244232"
height="20"
x="107.42467"
y="106.64581" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-5-8"
width="32.244232"
height="20"
x="143.34325"
y="106.64581" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-4"
width="32.244232"
height="20"
x="71.506088"
y="129.82079" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-8-3"
width="32.244232"
height="20"
x="107.42467"
y="129.82079" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-5-8-2"
width="32.244232"
height="20"
x="143.34325"
y="129.82079" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-7"
width="32.244232"
height="20"
x="35.587502"
y="106.64581" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-4-0"
width="32.244232"
height="20"
x="35.587502"
y="129.82079" />
<rect
style="display:inline;fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-9"
width="32.244232"
height="20"
x="71.506088"
y="82.941666" />
<rect
style="display:inline;fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-8-37"
width="32.244232"
height="20"
x="107.42467"
y="82.941666" />
<rect
style="display:inline;fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-5-8-4"
width="32.244232"
height="20"
x="143.34325"
y="82.941666" />
<rect
style="display:inline;fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-7-1"
width="32.244232"
height="20"
x="35.587502"
y="82.941666" />
</g>
<polygon
points="75.3,174.4 103.1,103.6 79.8,103.6 112.6,41.3 156.4,41.3 129.9,90.5 148.1,90.5 "
fill="#f6cc00"
id="polygon276"
transform="matrix(1.0930933,0,0,0.99853202,-17.517362,-0.52287941)" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 402 B

View File

@ -30,13 +30,19 @@ export const useResourceStore = defineStore({
currentAsidePath: "" currentAsidePath: ""
}), }),
getters: { getters: {
// @ts-ignore
getAsideMenus() { getAsideMenus() {
// @ts-ignore
return this.asideMenus; return this.asideMenus;
}, },
// @ts-ignore
getHeaderMenus() { getHeaderMenus() {
// @ts-ignore
return this.headerMenus; return this.headerMenus;
}, },
// @ts-ignore
getFrameworkMenus() { getFrameworkMenus() {
// @ts-ignore
return this.frameworkMenus; return this.frameworkMenus;
} }
}, },
@ -54,17 +60,17 @@ export const useResourceStore = defineStore({
this.inited = true; this.inited = true;
const showMenus = _.cloneDeep(frameworkMenus[0].children); const showMenus = _.cloneDeep(frameworkMenus[0].children);
this.frameworkMenus = filterMenus(showMenus, (item) => { this.frameworkMenus = filterMenus(showMenus, (item: any) => {
return item?.meta?.showOnHeader !== false; return item?.meta?.showOnHeader !== false;
}); });
this.fixedAsideMenus = findMenus(showMenus, (item) => { this.fixedAsideMenus = findMenus(showMenus, (item: any) => {
return item?.meta?.fixedAside === true; return item?.meta?.fixedAside === true;
}); });
this.headerMenus = headerMenus; this.headerMenus = headerMenus;
this.setAsideMenu(); this.setAsideMenu();
}, },
setAsideMenu(topMenu?) { setAsideMenu(topMenu?: any) {
if (this.frameworkMenus.length === 0) { if (this.frameworkMenus.length === 0) {
return; return;
} }
@ -74,13 +80,13 @@ export const useResourceStore = defineStore({
const asideMenus = topMenu?.children || []; const asideMenus = topMenu?.children || [];
this.asideMenus = [...this.fixedAsideMenus, ...asideMenus]; this.asideMenus = [...this.fixedAsideMenus, ...asideMenus];
}, },
setAsideMenuByCurrentRoute(matched) { setAsideMenuByCurrentRoute(matched: any) {
const menuHeader = this.frameworkMenus; const menuHeader = this.frameworkMenus;
if (matched?.length <= 1) { if (matched?.length <= 1) {
return; return;
} }
function findFromTree(tree, find) { function findFromTree(tree: any, find: any) {
const results: Array<any> = []; const results: Array<any> = [];
for (const item of tree) { for (const item of tree) {
if (find(item)) { if (find(item)) {
@ -88,7 +94,7 @@ export const useResourceStore = defineStore({
return results; return results;
} }
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
const found = findFromTree(item.children, find); const found: any = findFromTree(item.children, find);
if (found) { if (found) {
results.push(item); results.push(item);
return results.concat(found); return results.concat(found);
@ -97,7 +103,7 @@ export const useResourceStore = defineStore({
} }
} }
const matchedPath = matched[1].path; const matchedPath = matched[1].path;
const _side = findFromTree(menuHeader, (menu) => menu.path === matchedPath); const _side = findFromTree(menuHeader, (menu: any) => menu.path === matchedPath);
if (_side?.length > 0) { if (_side?.length > 0) {
if (this.currentAsidePath === _side[0]) { if (this.currentAsidePath === _side[0]) {
return; return;
@ -106,11 +112,11 @@ export const useResourceStore = defineStore({
this.setAsideMenu(_side[0]); this.setAsideMenu(_side[0]);
} }
}, },
filterByPermission(permissions) { filterByPermission(permissions: any) {
this.frameworkMenus = this.filterChildrenByPermission(this.frameworkMenus, permissions); this.frameworkMenus = this.filterChildrenByPermission(this.frameworkMenus, permissions);
}, },
filterChildrenByPermission(list, permissions) { filterChildrenByPermission(list: any, permissions: any) {
const menus = list.filter((item) => { const menus = list.filter((item: any) => {
if (item?.meta?.permission) { if (item?.meta?.permission) {
return permissions.includes(item.meta.permission); return permissions.includes(item.meta.permission);
} }

View File

@ -51,7 +51,7 @@ export const useSettingStore = defineStore({
this.persistTheme(); this.persistTheme();
// await changeTheme(this.theme.primaryColor); // await changeTheme(this.theme.primaryColor);
}, },
async setPrimaryColor(color) { async setPrimaryColor(color: any) {
const theme = this.theme; const theme = this.theme;
theme.primaryColor = color; theme.primaryColor = color;
await this.setTheme(); await this.setTheme();

View File

@ -1,13 +1,13 @@
import _ from "lodash"; import _ from "lodash";
export default { export default {
arrayToMap(array) { arrayToMap(array: any) {
if (!array) { if (!array) {
return {}; return {};
} }
if (!_.isArray(array)) { if (!_.isArray(array)) {
return array; return array;
} }
const map = {}; const map: any = {};
for (const item of array) { for (const item of array) {
if (item.key) { if (item.key) {
map[item.key] = item; map[item.key] = item;
@ -15,7 +15,7 @@ export default {
} }
return map; return map;
}, },
mapToArray(map) { mapToArray(map: any) {
if (!map) { if (!map) {
return []; return [];
} }

View File

@ -57,6 +57,9 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
name: { name: {
title: "名称", title: "名称",
type: "text", type: "text",
search: {
show: true
},
form: { form: {
rules: [{ required: true, message: "必填项" }] rules: [{ required: true, message: "必填项" }]
} }

View File

@ -2,6 +2,7 @@
<fs-page v-if="pipeline" class="page-pipeline-edit"> <fs-page v-if="pipeline" class="page-pipeline-edit">
<template #header> <template #header>
<div class="title"> <div class="title">
<fs-button class="back" icon="ion:chevron-back-outline" @click="goBack"></fs-button>
<pi-editable v-model="pipeline.title" :hover-show="false" :disabled="!editMode"></pi-editable> <pi-editable v-model="pipeline.title" :hover-show="false" :disabled="!editMode"></pi-editable>
</div> </div>
<div class="more"> <div class="more">
@ -588,6 +589,7 @@ export default defineComponent({
pipeline, pipeline,
currentHistory, currentHistory,
histories, histories,
goBack,
...useTaskRet, ...useTaskRet,
...useStageRet, ...useStageRet,
...useTrigger(), ...useTrigger(),
@ -602,6 +604,10 @@ export default defineComponent({
.page-pipeline-edit { .page-pipeline-edit {
.fs-page-header { .fs-page-header {
.title { .title {
display: flex;
.back {
margin-right: 10px;
}
.pi-editable { .pi-editable {
width: 300px; width: 300px;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

View File

@ -5,7 +5,7 @@
</div> </div>
<p class="d2-page-cover__sub-title">让你的证书永不过期</p> <p class="d2-page-cover__sub-title">让你的证书永不过期</p>
<div class="content"> <div class="content">
<img src="./image/preview.png" class="preview_img" /> <img src="/images/preview.png" class="preview_img" />
</div> </div>
<div class="footer_box"> <div class="footer_box">
<div class="left"></div> <div class="left"></div>
@ -76,6 +76,7 @@ export default defineComponent({
width: 80%; width: 80%;
.preview_img { .preview_img {
width: 100%; width: 100%;
border: 1px solid #eee;
} }
} }

View File

@ -40,7 +40,7 @@ export class UserController extends CrudController<UserService> {
const users = ret.data.records; const users = ret.data.records;
//获取roles //获取roles
const userIds = users.map((item) => item.id); const userIds = users.map(item => item.id);
const userRoles = await this.roleService.getByUserIds(userIds); const userRoles = await this.roleService.getByUserIds(userIds);
const userRolesMap = new Map(); const userRolesMap = new Map();
for (const ur of userRoles) { for (const ur of userRoles) {
@ -116,4 +116,3 @@ export class UserController extends CrudController<UserService> {
return this.ok(tree); return this.ok(tree);
} }
} }

View File

@ -4,6 +4,7 @@ import { IEmailService } from '@certd/pipeline';
import nodemailer from 'nodemailer'; import nodemailer from 'nodemailer';
import { SettingsService } from '../../system/service/settings-service'; import { SettingsService } from '../../system/service/settings-service';
import type SMTPConnection from 'nodemailer/lib/smtp-connection'; import type SMTPConnection from 'nodemailer/lib/smtp-connection';
import { logger } from '../../../utils/logger';
export type EmailConfig = { export type EmailConfig = {
host: string; host: string;
@ -46,7 +47,7 @@ export class EmailService implements IEmailService {
text: email.content, text: email.content,
}; };
await transporter.sendMail(mailOptions); await transporter.sendMail(mailOptions);
console.log('sendEmail success', email); logger.info('sendEmail complete: ', email);
} }
async test(userId: number, receiver: string) { async test(userId: number, receiver: string) {