mirror of
https://github.com/certd/certd.git
synced 2026-04-04 15:00:54 +08:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd4b9527c3 | ||
|
|
ea8dc446ae | ||
|
|
ba6270990b | ||
|
|
df55f1066c | ||
|
|
b562d661db | ||
|
|
aede78a0ec | ||
|
|
7e8c3fbab7 | ||
|
|
6621601155 | ||
|
|
1fbd585a46 | ||
|
|
5a51c14de5 | ||
|
|
27a4c81c6d | ||
|
|
fdc25dc0d7 | ||
|
|
99522fb49a | ||
|
|
671d273e2f | ||
|
|
2851a33eb2 | ||
|
|
937e3fac19 | ||
|
|
64afebecd4 | ||
|
|
4c324960e6 | ||
|
|
f516b0931f | ||
|
|
2297121eff | ||
|
|
0c2684d1cf | ||
|
|
4b4c5dba73 | ||
|
|
7b9d70e093 | ||
|
|
71a289b009 | ||
|
|
ddf98ff593 | ||
|
|
db8043ecb6 | ||
|
|
ea756cf0a4 | ||
|
|
8446a6d813 | ||
|
|
a18aaeacf7 | ||
|
|
58a43b3785 |
15
CHANGELOG.md
15
CHANGELOG.md
@@ -3,6 +3,21 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复access选择类型trigger ([2851a33](https://github.com/certd/certd/commit/2851a33eb2510f038fadb55da29512597a4ba512))
|
||||
|
||||
### Features
|
||||
|
||||
* 权限控制 ([27a4c81](https://github.com/certd/certd/commit/27a4c81c6d70e70abb3892c3ea58d4719988808a))
|
||||
* 邮件通知 ([937e3fa](https://github.com/certd/certd/commit/937e3fac19cd03b8aa91db8ba03fda7fcfbacea2))
|
||||
* cert download ([5a51c14](https://github.com/certd/certd/commit/5a51c14de521cb8075a80d2ae41a16e6d5281259))
|
||||
* config merge ([fdc25dc](https://github.com/certd/certd/commit/fdc25dc0d795555cffacc4572648ec158988fbbb))
|
||||
* save files ([99522fb](https://github.com/certd/certd/commit/99522fb49adb42c1dfdf7bec3dd52d641158285b))
|
||||
* save files ([671d273](https://github.com/certd/certd/commit/671d273e2f9136d16896536b0ca127cf372f1619))
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package root
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
FROM registry.cn-shenzhen.aliyuncs.com/handsfree/node:16-alpine
|
||||
RUN npm install -g pnpm && npm install -g cross-env
|
||||
ADD ./workspace/certd-server/ /app/
|
||||
EXPOSE 7001
|
||||
ENV NODE_ENV production
|
||||
WORKDIR /app/
|
||||
RUN pnpm install -P
|
||||
CMD ["npm","run","start"]
|
||||
#RUN npm install cross-env -g --registry=https://registry.npmmirror.com
|
||||
#RUN npm install pm2 -g --registry=https://registry.npmmirror.com
|
||||
#RUN pm2 install pm2-logrotate
|
||||
ADD ./workspace/certd-server/ /app/
|
||||
RUN yarn install --production --registry=https://registry.npmmirror.com
|
||||
RUN npm run build
|
||||
#CMD ["pm2-runtime", "start", "./bootstrap.js","--name", "certd","-i","1"]
|
||||
CMD ["npm", "run","start"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
4
docker/image/build.sh
Normal file → Executable file
4
docker/image/build.sh
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
echo "请先输入一个版本号:"
|
||||
read version
|
||||
|
||||
@@ -11,7 +11,7 @@ build=$(pwd)
|
||||
cd ../../
|
||||
root=$(pwd)
|
||||
echo "安装依赖"
|
||||
pnpm install
|
||||
pnpm install --registry=https://registry.npmmirror.com
|
||||
|
||||
echo "client build"
|
||||
cd $root/packages/ui/certd-client
|
||||
|
||||
9
docker/run/Dockerfile
Normal file
9
docker/run/Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
EXPOSE 7001
|
||||
RUN npm run build
|
||||
#RUN npm install pm2 -g --registry=https://registry.npmmirror.com
|
||||
#CMD ["pm2-runtime", "start", "./bootstrap.js","--name", "certd","-i","1","--", "-p", "7001"]
|
||||
CMD ["npm","run", "start"]
|
||||
|
||||
|
||||
|
||||
7
docker/run/build.sh
Executable file
7
docker/run/build.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
echo "请先输入一个版本号(如 1.0.6):"
|
||||
read version
|
||||
echo "您输入的版本号是: $version"
|
||||
export TAG="$version"
|
||||
sudo -E docker compose up -d
|
||||
@@ -1,7 +1,10 @@
|
||||
version: '3.3' # 指定docker-compose 版本
|
||||
services: # 要拉起的服务们
|
||||
certd:
|
||||
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
# build:
|
||||
# context: ./
|
||||
# dockerfile: Dockerfile
|
||||
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:v${TAG}
|
||||
container_name: certd # 容器名
|
||||
restart: unless-stopped # 重启
|
||||
volumes: # 挂载目录
|
||||
@@ -10,6 +13,6 @@ services: # 要拉起的服务们
|
||||
- "7001:7001"
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- CERTD_AUTH_JWT_KEY=changeme
|
||||
- certd_auth_jwt_secret=changeme
|
||||
#注意修改成你的自定义密钥 ↑↑↑↑↑
|
||||
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.0.6"
|
||||
"version": "1.1.0"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/publishlab/node-acme-client/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.0.6](https://github.com/publishlab/node-acme-client/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
@@ -1 +1 @@
|
||||
00:19
|
||||
10:22
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"main": "src/index.js",
|
||||
"types": "types",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复access选择类型trigger ([2851a33](https://github.com/certd/certd/commit/2851a33eb2510f038fadb55da29512597a4ba512))
|
||||
|
||||
### Features
|
||||
|
||||
* 邮件通知 ([937e3fa](https://github.com/certd/certd/commit/937e3fac19cd03b8aa91db8ba03fda7fcfbacea2))
|
||||
* cert download ([5a51c14](https://github.com/certd/certd/commit/5a51c14de521cb8075a80d2ae41a16e6d5281259))
|
||||
* save files ([99522fb](https://github.com/certd/certd/commit/99522fb49adb42c1dfdf7bec3dd52d641158285b))
|
||||
* save files ([671d273](https://github.com/certd/certd/commit/671d273e2f9136d16896536b0ca127cf372f1619))
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.0.6",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/pipeline.mjs",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
"version": "1.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"publishConfig": {
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.mjs",
|
||||
@@ -19,10 +19,12 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.4.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"qs": "^6.11.2"
|
||||
"nodemailer": "^6.9.3",
|
||||
"qs": "^6.11.2",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/acme-client": "^1.0.6",
|
||||
"@certd/acme-client": "^1.1.0",
|
||||
"@rollup/plugin-commonjs": "^23.0.4",
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
@@ -32,6 +34,7 @@
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node-forge": "^1.3.2",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||
"@typescript-eslint/parser": "^5.59.7",
|
||||
"chai": "^4.3.7",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ConcurrencyStrategy, 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 { RunHistory, RunnableCollection } from "./run-history";
|
||||
import { AbstractTaskPlugin, PluginDefine, pluginRegistry } from "../plugin";
|
||||
import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext } from "../plugin";
|
||||
import { ContextFactory, IContext } from "./context";
|
||||
import { IStorage } from "./storage";
|
||||
import { logger } from "../utils/util.log";
|
||||
@@ -10,31 +10,35 @@ import { request } from "../utils/util.request";
|
||||
import { IAccessService } from "../access";
|
||||
import { RegistryItem } from "../registry";
|
||||
import { Decorator } from "../decorator";
|
||||
import { IEmailService } from "../service";
|
||||
import { FileStore } from "./file-store";
|
||||
|
||||
export class Executor {
|
||||
export type ExecutorOptions = {
|
||||
userId: any;
|
||||
pipeline: Pipeline;
|
||||
runtime!: RunHistory;
|
||||
storage: IStorage;
|
||||
onChanged: (history: RunHistory) => Promise<void>;
|
||||
accessService: IAccessService;
|
||||
emailService: IEmailService;
|
||||
fileRootDir?: string;
|
||||
};
|
||||
export class Executor {
|
||||
pipeline: Pipeline;
|
||||
runtime!: RunHistory;
|
||||
contextFactory: ContextFactory;
|
||||
logger: Logger;
|
||||
pipelineContext!: IContext;
|
||||
lastStatusMap!: RunnableCollection;
|
||||
lastRuntime!: RunHistory;
|
||||
options: ExecutorOptions;
|
||||
onChanged: (history: RunHistory) => void;
|
||||
constructor(options: {
|
||||
userId: any;
|
||||
pipeline: Pipeline;
|
||||
storage: IStorage;
|
||||
onChanged: (history: RunHistory) => Promise<void>;
|
||||
accessService: IAccessService;
|
||||
}) {
|
||||
constructor(options: ExecutorOptions) {
|
||||
this.options = options;
|
||||
this.pipeline = _.cloneDeep(options.pipeline);
|
||||
this.onChanged = async (history: RunHistory) => {
|
||||
await options.onChanged(history);
|
||||
};
|
||||
this.accessService = options.accessService;
|
||||
this.userId = options.userId;
|
||||
this.pipeline.userId = this.userId;
|
||||
this.pipeline.userId = options.userId;
|
||||
this.contextFactory = new ContextFactory(options.storage);
|
||||
this.logger = logger;
|
||||
this.pipelineContext = this.contextFactory.getContext("pipeline", this.pipeline.id);
|
||||
@@ -42,6 +46,7 @@ export class Executor {
|
||||
|
||||
async init() {
|
||||
const lastRuntime = await this.pipelineContext.getObj(`lastRuntime`);
|
||||
this.lastRuntime = lastRuntime;
|
||||
this.lastStatusMap = new RunnableCollection(lastRuntime?.pipeline);
|
||||
}
|
||||
|
||||
@@ -52,10 +57,16 @@ export class Executor {
|
||||
// 读取last
|
||||
this.runtime = new RunHistory(runtimeId, trigger, this.pipeline);
|
||||
this.logger.info(`pipeline.${this.pipeline.id} start`);
|
||||
await this.notification("start");
|
||||
await this.runWithHistory(this.pipeline, "pipeline", async () => {
|
||||
await this.runStages(this.pipeline);
|
||||
});
|
||||
if (this.lastRuntime && this.lastRuntime.pipeline.status?.status === ResultType.error) {
|
||||
await this.notification("turnToSuccess");
|
||||
}
|
||||
await this.notification("success");
|
||||
} catch (e) {
|
||||
await this.notification("error", e);
|
||||
this.logger.error("pipeline 执行失败", e);
|
||||
} finally {
|
||||
await this.pipelineContext.setObj("lastRuntime", this.runtime);
|
||||
@@ -181,27 +192,74 @@ export class Executor {
|
||||
}
|
||||
});
|
||||
|
||||
const context: any = {
|
||||
logger: this.runtime._loggers[step.id],
|
||||
accessService: this.accessService,
|
||||
pipelineContext: this.pipelineContext,
|
||||
const taskCtx: TaskInstanceContext = {
|
||||
pipeline: this.pipeline,
|
||||
step,
|
||||
lastStatus,
|
||||
userContext: this.contextFactory.getContext("user", this.userId),
|
||||
http: request,
|
||||
logger: this.runtime._loggers[step.id],
|
||||
accessService: this.options.accessService,
|
||||
emailService: this.options.emailService,
|
||||
pipelineContext: this.pipelineContext,
|
||||
userContext: this.contextFactory.getContext("user", this.options.userId),
|
||||
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.execute();
|
||||
|
||||
if (instance.result.clearLastStatus) {
|
||||
if (instance._result.clearLastStatus) {
|
||||
this.lastStatusMap.clear();
|
||||
}
|
||||
//输出到output context
|
||||
_.forEach(define.output, (item, key) => {
|
||||
step!.status!.output[key] = instance[key];
|
||||
step.status!.output[key] = instance[key];
|
||||
const stepOutputKey = `step.${step.id}.${key}`;
|
||||
this.runtime.context[stepOutputKey] = instance[key];
|
||||
});
|
||||
|
||||
step.status!.files = instance.getFiles();
|
||||
}
|
||||
|
||||
async notification(when: NotificationWhen, error?: any) {
|
||||
if (!this.pipeline.notifications) {
|
||||
return;
|
||||
}
|
||||
let subject = "";
|
||||
let content = "";
|
||||
if (when === "start") {
|
||||
subject = `【CertD】开始执行,${this.pipeline.title}, buildId:${this.runtime.id}`;
|
||||
content = subject;
|
||||
} else if (when === "success") {
|
||||
subject = `【CertD】执行成功,${this.pipeline.title}, buildId:${this.runtime.id}`;
|
||||
content = subject;
|
||||
} else if (when === "turnToSuccess") {
|
||||
subject = `【CertD】执行成功(错误转成功),${this.pipeline.title}, buildId:${this.runtime.id}`;
|
||||
content = subject;
|
||||
} else if (when === "error") {
|
||||
subject = `【CertD】执行失败,${this.pipeline.title}, buildId:${this.runtime.id}`;
|
||||
content = `<pre>${error.message}</pre>`;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const notification of this.pipeline.notifications) {
|
||||
if (!notification.when.includes(when)) {
|
||||
continue;
|
||||
}
|
||||
if (notification.type === "email") {
|
||||
this.options.emailService?.send({
|
||||
userId: this.pipeline.userId,
|
||||
subject,
|
||||
content,
|
||||
receivers: notification.options.receivers,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
packages/core/pipeline/src/core/file-store.ts
Normal file
43
packages/core/pipeline/src/core/file-store.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
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) {
|
||||
const parentDir = path.join(this.rootDir, this.scope + "", dayjs().format("YYYY-MM-DD"), this.parent + "");
|
||||
if (!fs.existsSync(parentDir)) {
|
||||
fs.mkdirSync(parentDir, { recursive: true });
|
||||
}
|
||||
return path.join(parentDir, filename);
|
||||
}
|
||||
}
|
||||
@@ -119,25 +119,25 @@ export class RunnableCollection {
|
||||
this.collection = map;
|
||||
}
|
||||
|
||||
private each<T extends Runnable>(list: T[], exec: (item: Runnable) => void) {
|
||||
static each<T extends Runnable>(list: T[], exec: (item: Runnable) => void) {
|
||||
list.forEach((item) => {
|
||||
exec(item);
|
||||
if (item.runnableType === "pipeline") {
|
||||
// @ts-ignore
|
||||
this.each<Stage>(item.stages, exec);
|
||||
RunnableCollection.each<Stage>(item.stages, exec);
|
||||
} else if (item.runnableType === "stage") {
|
||||
// @ts-ignore
|
||||
this.each<Task>(item.tasks, exec);
|
||||
RunnableCollection.each<Task>(item.tasks, exec);
|
||||
} else if (item.runnableType === "task") {
|
||||
// @ts-ignore
|
||||
this.each<Step>(item.steps, exec);
|
||||
RunnableCollection.each<Step>(item.steps, exec);
|
||||
}
|
||||
});
|
||||
}
|
||||
private toMap(pipeline: Pipeline) {
|
||||
public toMap(pipeline: Pipeline) {
|
||||
const map: RunnableMap = {};
|
||||
|
||||
this.each(pipeline.stages, (item) => {
|
||||
RunnableCollection.each(pipeline.stages, (item) => {
|
||||
map[item.id] = item;
|
||||
});
|
||||
return map;
|
||||
@@ -151,7 +151,7 @@ export class RunnableCollection {
|
||||
if (!this.pipeline) {
|
||||
return;
|
||||
}
|
||||
this.each(this.pipeline.stages, (item) => {
|
||||
RunnableCollection.each(this.pipeline.stages, (item) => {
|
||||
item.status = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
|
||||
import { fileUtils } from "../utils/util.file";
|
||||
|
||||
export interface IStorage {
|
||||
get(scope: string, namespace: string, version: string, key: string): Promise<string | null>;
|
||||
@@ -12,15 +11,7 @@ export interface IStorage {
|
||||
export class FileStorage implements IStorage {
|
||||
root: string;
|
||||
constructor(rootDir?: string) {
|
||||
if (rootDir == null) {
|
||||
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 });
|
||||
}
|
||||
this.root = fileUtils.getFileRootDir(rootDir);
|
||||
}
|
||||
|
||||
async remove(scope: string, namespace: string, version: string, key: string): Promise<void> {
|
||||
|
||||
@@ -55,6 +55,11 @@ export type Trigger = {
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type FileItem = {
|
||||
id: string;
|
||||
filename: string;
|
||||
path: string;
|
||||
};
|
||||
export type Runnable = {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -66,11 +71,23 @@ export type Runnable = {
|
||||
};
|
||||
};
|
||||
|
||||
export type EmailOptions = {
|
||||
receivers: string[];
|
||||
};
|
||||
export type NotificationWhen = "error" | "success" | "turnToSuccess" | "start";
|
||||
export type NotificationType = "email" | "url";
|
||||
export type Notification = {
|
||||
type: NotificationType;
|
||||
when: NotificationWhen[];
|
||||
options: EmailOptions;
|
||||
};
|
||||
|
||||
export type Pipeline = Runnable & {
|
||||
version?: number;
|
||||
userId: any;
|
||||
stages: Stage[];
|
||||
triggers: Trigger[];
|
||||
notifications: Notification[];
|
||||
};
|
||||
|
||||
export type Context = {
|
||||
@@ -101,6 +118,7 @@ export type HistoryResultGroup = {
|
||||
export type HistoryResult = {
|
||||
input: any;
|
||||
output: any;
|
||||
files?: FileItem[];
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
|
||||
@@ -7,3 +7,4 @@ export * from "./plugin";
|
||||
export * from "./utils";
|
||||
export * from "./context";
|
||||
export * from "./decorator";
|
||||
export * from "./service";
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
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";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
export enum ContextScope {
|
||||
global,
|
||||
pipeline,
|
||||
runtime,
|
||||
}
|
||||
|
||||
export type Storage = {
|
||||
scope: ContextScope;
|
||||
path: string;
|
||||
};
|
||||
|
||||
export type TaskOutputDefine = {
|
||||
title: string;
|
||||
value?: any;
|
||||
storage?: Storage;
|
||||
};
|
||||
|
||||
export type TaskInputDefine = FormItemProps;
|
||||
|
||||
export type PluginDefine = Registrable & {
|
||||
@@ -31,6 +32,12 @@ export type PluginDefine = Registrable & {
|
||||
autowire?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
reference?: {
|
||||
src: string;
|
||||
dest: string;
|
||||
type: "computed";
|
||||
}[];
|
||||
};
|
||||
|
||||
export type ITaskPlugin = {
|
||||
@@ -41,15 +48,57 @@ export type ITaskPlugin = {
|
||||
|
||||
export type TaskResult = {
|
||||
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 {
|
||||
result: TaskResult = {};
|
||||
_result: TaskResult = { clearLastStatus: false, files: [] };
|
||||
ctx!: TaskInstanceContext;
|
||||
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({
|
||||
id: uuidv4(),
|
||||
filename,
|
||||
path: filePath,
|
||||
});
|
||||
}
|
||||
|
||||
get pipeline() {
|
||||
return this.ctx.pipeline;
|
||||
}
|
||||
|
||||
get step() {
|
||||
return this.ctx.step;
|
||||
}
|
||||
|
||||
async onInstance(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
abstract execute(): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,3 +61,11 @@ export function TaskOutput(output?: TaskOutputDefine): PropertyDecorator {
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
10
packages/core/pipeline/src/service/email.ts
Normal file
10
packages/core/pipeline/src/service/email.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export type EmailSend = {
|
||||
userId: number;
|
||||
subject: string;
|
||||
content: string;
|
||||
receivers: string[];
|
||||
};
|
||||
|
||||
export interface IEmailService {
|
||||
send(email: EmailSend): Promise<void>;
|
||||
}
|
||||
1
packages/core/pipeline/src/service/index.ts
Normal file
1
packages/core/pipeline/src/service/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./email";
|
||||
16
packages/core/pipeline/src/utils/util.file.ts
Normal file
16
packages/core/pipeline/src/utils/util.file.ts
Normal 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,
|
||||
};
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
### Features
|
||||
|
||||
* save files ([671d273](https://github.com/certd/certd/commit/671d273e2f9136d16896536b0ca127cf372f1619))
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-aliyun
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-aliyun",
|
||||
"private": false,
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
@@ -23,10 +23,10 @@
|
||||
"node-forge": "^0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/acme-client": "^1.0.6",
|
||||
"@certd/pipeline": "^1.0.6",
|
||||
"@certd/plugin-cert": "^1.0.6",
|
||||
"@certd/plugin-util": "^1.0.6",
|
||||
"@certd/acme-client": "^1.1.0",
|
||||
"@certd/pipeline": "^1.1.0",
|
||||
"@certd/plugin-cert": "^1.1.0",
|
||||
"@certd/plugin-util": "^1.1.0",
|
||||
"@midwayjs/core": "^3.0.0",
|
||||
"@midwayjs/decorator": "^3.0.0",
|
||||
"@rollup/plugin-commonjs": "^23.0.4",
|
||||
|
||||
@@ -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
|
||||
import { ROAClient } from "@alicloud/pop-core";
|
||||
import { AliyunAccess } from "../../access";
|
||||
@@ -103,11 +103,13 @@ export class DeployCertToAliyunAckIngressPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
accessId!: string;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
@Autowire()
|
||||
logger!: ILogger;
|
||||
|
||||
async onInstance(): Promise<void> {
|
||||
this.accessService = this.ctx.accessService;
|
||||
this.logger = this.ctx.logger;
|
||||
}
|
||||
async execute(): Promise<void> {
|
||||
console.log("开始部署证书到阿里云cdn");
|
||||
const { regionId, ingressClass, clusterId, isPrivateIpAddress, cert } = this;
|
||||
|
||||
@@ -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 Core from "@alicloud/pop-core";
|
||||
import RPCClient from "@alicloud/pop-core";
|
||||
@@ -17,7 +17,7 @@ import { AliyunAccess } from "../../access";
|
||||
export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
|
||||
@TaskInput({
|
||||
title: "CDN加速域名",
|
||||
helper: "你在阿里云上配置的CDN加速域名,比如certd.docmirror.cn",
|
||||
helper: "你在阿里云上配置的CDN加速域名,比如:certd.docmirror.cn",
|
||||
required: true,
|
||||
})
|
||||
domainName!: string;
|
||||
@@ -49,13 +49,13 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
|
||||
})
|
||||
accessId!: string;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
|
||||
@Autowire()
|
||||
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> {
|
||||
console.log("开始部署证书到阿里云cdn");
|
||||
const access = (await this.accessService.getById(this.accessId)) as AliyunAccess;
|
||||
|
||||
@@ -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 { AliyunAccess } from "../../access";
|
||||
import { appendTimeSuffix, checkRet, ZoneOptions } from "../../utils";
|
||||
@@ -59,14 +59,13 @@ export class UploadCertToAliyun extends AbstractTaskPlugin {
|
||||
})
|
||||
aliyunCertId!: string;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
|
||||
@Autowire()
|
||||
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> {
|
||||
console.log("开始部署证书到阿里云cdn");
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-all
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-all
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-all",
|
||||
"private": false,
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
@@ -17,12 +17,12 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/pipeline": "^1.0.6",
|
||||
"@certd/plugin-aliyun": "^1.0.6",
|
||||
"@certd/plugin-cert": "^1.0.6",
|
||||
"@certd/plugin-host": "^1.0.6",
|
||||
"@certd/plugin-huawei": "^1.0.6",
|
||||
"@certd/plugin-tencent": "^1.0.6",
|
||||
"@certd/pipeline": "^1.1.0",
|
||||
"@certd/plugin-aliyun": "^1.1.0",
|
||||
"@certd/plugin-cert": "^1.1.0",
|
||||
"@certd/plugin-host": "^1.1.0",
|
||||
"@certd/plugin-huawei": "^1.1.0",
|
||||
"@certd/plugin-tencent": "^1.1.0",
|
||||
"@rollup/plugin-commonjs": "^23.0.4",
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复access选择类型trigger ([2851a33](https://github.com/certd/certd/commit/2851a33eb2510f038fadb55da29512597a4ba512))
|
||||
|
||||
### Features
|
||||
|
||||
* save files ([99522fb](https://github.com/certd/certd/commit/99522fb49adb42c1dfdf7bec3dd52d641158285b))
|
||||
* save files ([671d273](https://github.com/certd/certd/commit/671d273e2f9136d16896536b0ca127cf372f1619))
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
@@ -17,8 +17,9 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.0.6",
|
||||
"@certd/pipeline": "^1.0.6",
|
||||
"@certd/acme-client": "^1.1.0",
|
||||
"@certd/pipeline": "^1.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"node-forge": "^0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
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 { AcmeService, CertInfo } from "./acme";
|
||||
import _ from "lodash";
|
||||
import { Logger } from "log4js";
|
||||
import { Decorator } from "@certd/pipeline";
|
||||
import { DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
|
||||
import { CertReader } from "./cert-reader";
|
||||
|
||||
import JSZip from "jszip";
|
||||
export { CertReader };
|
||||
export type { CertInfo };
|
||||
|
||||
@@ -73,6 +72,13 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
|
||||
},
|
||||
required: true,
|
||||
helper: "请选择dns解析提供商授权",
|
||||
reference: [
|
||||
{
|
||||
src: "form.dnsProviderType",
|
||||
dest: "component.type",
|
||||
type: "computed",
|
||||
},
|
||||
],
|
||||
})
|
||||
dnsProviderAccess!: string;
|
||||
|
||||
@@ -102,22 +108,11 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
csrInfo: any;
|
||||
|
||||
// @ts-ignore
|
||||
acme: AcmeService;
|
||||
|
||||
@Autowire()
|
||||
acme!: AcmeService;
|
||||
logger!: Logger;
|
||||
|
||||
@Autowire()
|
||||
userContext!: IContext;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
|
||||
@Autowire()
|
||||
http!: HttpClient;
|
||||
|
||||
@Autowire()
|
||||
lastStatus!: Step;
|
||||
|
||||
@TaskOutput({
|
||||
@@ -126,17 +121,23 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
|
||||
cert?: CertInfo;
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
async execute(): Promise<void> {
|
||||
const oldCert = await this.condition();
|
||||
if (oldCert != null) {
|
||||
return this.output(oldCert);
|
||||
return await this.output(oldCert.toCertInfo());
|
||||
}
|
||||
const cert = await this.doCertApply();
|
||||
if (cert != null) {
|
||||
this.output(cert.toCertInfo());
|
||||
await this.output(cert.toCertInfo());
|
||||
//清空后续任务的状态,让后续任务能够重新执行
|
||||
this.clearLastStatus();
|
||||
} else {
|
||||
@@ -144,8 +145,17 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
output(cert: CertInfo) {
|
||||
async output(cert: CertInfo) {
|
||||
this.cert = cert;
|
||||
await this.zipCert(cert);
|
||||
}
|
||||
|
||||
async zipCert(cert: CertInfo) {
|
||||
const zip = new JSZip();
|
||||
zip.file("cert.crt", cert.crt);
|
||||
zip.file("cert.key", cert.key);
|
||||
const content = await zip.generateAsync({ type: "nodebuffer" });
|
||||
this.saveFile("cert.zip", content);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
### Features
|
||||
|
||||
* save files ([671d273](https://github.com/certd/certd/commit/671d273e2f9136d16896536b0ca127cf372f1619))
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-host
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-host",
|
||||
"private": false,
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
@@ -17,8 +17,8 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/pipeline": "^1.0.6",
|
||||
"@certd/plugin-cert": "^1.0.6",
|
||||
"@certd/pipeline": "^1.1.0",
|
||||
"@certd/plugin-cert": "^1.1.0",
|
||||
"ssh2": "^0.8.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -61,6 +61,9 @@ export class SshClient {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connect({
|
||||
connectConf,
|
||||
onError(err: any) {
|
||||
reject(err);
|
||||
},
|
||||
onReady: (conn: any) => {
|
||||
conn.exec(script, (err: Error, stream: any) => {
|
||||
if (err) {
|
||||
@@ -98,6 +101,10 @@ export class SshClient {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connect({
|
||||
connectConf,
|
||||
onError: (err: any) => {
|
||||
this.logger.error(err);
|
||||
reject(err);
|
||||
},
|
||||
onReady: (conn: any) => {
|
||||
conn.shell((err: Error, stream: any) => {
|
||||
if (err) {
|
||||
@@ -122,10 +129,13 @@ export class SshClient {
|
||||
});
|
||||
}
|
||||
|
||||
connect(options: { connectConf: any; onReady: any }) {
|
||||
const { connectConf, onReady } = options;
|
||||
connect(options: { connectConf: any; onReady: any; onError: any }) {
|
||||
const { connectConf, onReady, onError } = options;
|
||||
const conn = new ssh2.Client();
|
||||
conn
|
||||
.on("error", (err: any) => {
|
||||
onError(err);
|
||||
})
|
||||
.on("ready", () => {
|
||||
this.logger.info("Client :: ready");
|
||||
onReady(conn);
|
||||
|
||||
@@ -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";
|
||||
|
||||
@IsTaskPlugin({
|
||||
@@ -32,13 +32,12 @@ export class HostShellExecutePlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
script!: string;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
@Autowire()
|
||||
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> {
|
||||
const { script, accessId } = this;
|
||||
const connectConf = await this.accessService.getById(accessId);
|
||||
|
||||
@@ -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 { CertInfo, CertReader } from "@certd/plugin-cert";
|
||||
import * as fs from "fs";
|
||||
@@ -49,11 +49,6 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
sudo!: boolean;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
@Autowire()
|
||||
logger!: ILogger;
|
||||
|
||||
@TaskOutput({
|
||||
title: "证书保存路径",
|
||||
})
|
||||
@@ -64,7 +59,13 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
hostKeyPath!: string;
|
||||
|
||||
async onInstance() {}
|
||||
accessService!: IAccessService;
|
||||
logger!: ILogger;
|
||||
|
||||
async onInstance() {
|
||||
this.accessService = this.ctx.accessService;
|
||||
this.logger = this.ctx.logger;
|
||||
}
|
||||
async execute(): Promise<void> {
|
||||
const { crtPath, keyPath, cert, accessId, sudo } = this;
|
||||
const certReader = new CertReader(cert);
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-huawei
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-huawei",
|
||||
"private": false,
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
@@ -17,10 +17,10 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.0.6",
|
||||
"@certd/pipeline": "^1.0.6",
|
||||
"@certd/plugin-cert": "^1.0.6",
|
||||
"@certd/plugin-util": "^1.0.6",
|
||||
"@certd/acme-client": "^1.1.0",
|
||||
"@certd/pipeline": "^1.1.0",
|
||||
"@certd/plugin-cert": "^1.1.0",
|
||||
"@certd/plugin-util": "^1.1.0",
|
||||
"axios": "^0.27.2",
|
||||
"dayjs": "^1.11.6",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
### Features
|
||||
|
||||
* save files ([671d273](https://github.com/certd/certd/commit/671d273e2f9136d16896536b0ca127cf372f1619))
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-tencent
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-tencent",
|
||||
"private": false,
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
@@ -17,9 +17,9 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/pipeline": "^1.0.6",
|
||||
"@certd/plugin-cert": "^1.0.6",
|
||||
"@certd/plugin-util": "^1.0.6",
|
||||
"@certd/pipeline": "^1.1.0",
|
||||
"@certd/plugin-cert": "^1.1.0",
|
||||
"@certd/plugin-util": "^1.1.0",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -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 { TencentAccess } from "../../access";
|
||||
import { CertInfo } from "@certd/plugin-cert";
|
||||
@@ -47,14 +47,14 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
domainName!: string;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
|
||||
@Autowire()
|
||||
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> {
|
||||
const accessProvider: TencentAccess = (await this.accessService.getById(this.accessId)) as TencentAccess;
|
||||
|
||||
@@ -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 { TencentAccess } from "../../access";
|
||||
import dayjs from "dayjs";
|
||||
@@ -71,14 +71,13 @@ export class DeployToClbPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
accessId!: string;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
|
||||
@Autowire()
|
||||
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> {
|
||||
const accessProvider = (await this.accessService.getById(this.accessId)) as TencentAccess;
|
||||
const client = this.getClient(accessProvider, this.region);
|
||||
|
||||
@@ -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 { K8sClient } from "@certd/plugin-util";
|
||||
import dayjs from "dayjs";
|
||||
@@ -80,15 +80,12 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
cert!: any;
|
||||
|
||||
@Autowire()
|
||||
logger!: Logger;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
|
||||
// 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> {
|
||||
const accessProvider = this.accessService.getById(this.accessId);
|
||||
const tkeClient = this.getTkeClient(accessProvider, this.region);
|
||||
|
||||
@@ -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 dayjs from "dayjs";
|
||||
|
||||
@@ -42,14 +42,13 @@ export class UploadToTencentPlugin extends AbstractTaskPlugin {
|
||||
})
|
||||
tencentCertId?: string;
|
||||
|
||||
@Autowire()
|
||||
accessService!: IAccessService;
|
||||
|
||||
@Autowire()
|
||||
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> {
|
||||
const { accessId, name, cert } = this;
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-util
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-util
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-util",
|
||||
"private": false,
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
@@ -21,7 +21,7 @@
|
||||
"shelljs": "^0.8.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/pipeline": "^1.0.6",
|
||||
"@certd/pipeline": "^1.1.0",
|
||||
"@rollup/plugin-commonjs": "^23.0.4",
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
VITE_APP_API=/api
|
||||
#登录与权限关闭
|
||||
VITE_APP_PM_ENABLED=true
|
||||
VITE_APP_TITLE=Certd
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
VITE_APP_API=http://www.docmirror.cn:7001/api
|
||||
VITE_APP_API=/api
|
||||
#登录与权限开启
|
||||
VITE_APP_PM_ENABLED=true
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.1.0](https://github.com/certd/certd/compare/v1.0.6...v1.1.0) (2023-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复access选择类型trigger ([2851a33](https://github.com/certd/certd/commit/2851a33eb2510f038fadb55da29512597a4ba512))
|
||||
|
||||
### Features
|
||||
|
||||
* 权限控制 ([27a4c81](https://github.com/certd/certd/commit/27a4c81c6d70e70abb3892c3ea58d4719988808a))
|
||||
* 邮件通知 ([937e3fa](https://github.com/certd/certd/commit/937e3fac19cd03b8aa91db8ba03fda7fcfbacea2))
|
||||
* cert download ([5a51c14](https://github.com/certd/certd/commit/5a51c14de521cb8075a80d2ae41a16e6d5281259))
|
||||
* save files ([671d273](https://github.com/certd/certd/commit/671d273e2f9136d16896536b0ca127cf372f1619))
|
||||
|
||||
## [1.0.6](https://github.com/certd/certd/compare/v1.0.5...v1.0.6) (2023-05-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>antdv-fast-crud</title>
|
||||
<title>Certd-让你的证书永不过期</title>
|
||||
<link rel="stylesheet" type="text/css" href="/index.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.0.6",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -22,8 +22,8 @@
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^6.0.0",
|
||||
"@ant-design/icons-vue": "^6.0.1",
|
||||
"@certd/acme-client": "^1.0.6",
|
||||
"@certd/pipeline": "^1.0.6",
|
||||
"@certd/acme-client": "^1.1.0",
|
||||
"@certd/pipeline": "^1.1.0",
|
||||
"@fast-crud/fast-crud": "^1.13.8",
|
||||
"@fast-crud/fast-extends": "^1.13.8",
|
||||
"@fast-crud/ui-antdv": "^1.13.8",
|
||||
@@ -63,6 +63,7 @@
|
||||
"@rollup/plugin-commonjs": "^23.0.4",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@types/chai": "^4.3.4",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^18.11.15",
|
||||
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
||||
@@ -73,7 +74,7 @@
|
||||
"@vue/compiler-sfc": "^3.2.45",
|
||||
"@vue/eslint-config-typescript": "^11.0.2",
|
||||
"@vue/test-utils": "^2.2.6",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"caller-path": "^4.0.0",
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "8.29.0",
|
||||
@@ -88,7 +89,7 @@
|
||||
"less": "^4.1.3",
|
||||
"less-loader": "^11.0.0",
|
||||
"lint-staged": "^13.1.0",
|
||||
"postcss": "^8.4.20",
|
||||
"postcss": "^8.4.23",
|
||||
"prettier": "2.8.1",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"rimraf": "^3.0.2",
|
||||
@@ -97,7 +98,7 @@
|
||||
"stylelint": "^14.16.0",
|
||||
"stylelint-config-prettier": "^9.0.4",
|
||||
"stylelint-order": "^5.0.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "4.9.4",
|
||||
"vite": "^4.0.1",
|
||||
|
||||
6
packages/ui/certd-client/postcss.config.js
Normal file
6
packages/ui/certd-client/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
||||
BIN
packages/ui/certd-client/public/images/preview.png
Normal file
BIN
packages/ui/certd-client/public/images/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -1,106 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="210mm"
|
||||
height="210mm"
|
||||
viewBox="0 0 210 210"
|
||||
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 version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="500" height="500" viewBox="0 0 500.000000 500.000000"
|
||||
>
|
||||
<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"
|
||||
transform="translate(70, 76) scale(6,6)"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 402 B |
@@ -11,9 +11,9 @@ import { provide, ref, nextTick } from "vue";
|
||||
import { usePageStore } from "/src/store/modules/page";
|
||||
import { useResourceStore } from "/src/store/modules/resource";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import 'dayjs/locale/zh-cn';
|
||||
import 'dayjs/locale/en';
|
||||
import dayjs from 'dayjs'
|
||||
import "dayjs/locale/zh-cn";
|
||||
import "dayjs/locale/en";
|
||||
import dayjs from "dayjs";
|
||||
export default {
|
||||
name: "App",
|
||||
setup() {
|
||||
@@ -29,13 +29,13 @@ export default {
|
||||
console.log("locale changed:", value);
|
||||
if (value === "zh-cn") {
|
||||
locale.value = zhCN;
|
||||
dayjs.locale('zh-cn');
|
||||
dayjs.locale("zh-cn");
|
||||
} else if (value === "en") {
|
||||
locale.value = enUS;
|
||||
dayjs.locale('en');
|
||||
dayjs.locale("en");
|
||||
}
|
||||
}
|
||||
localeChanged('zh-cn')
|
||||
localeChanged("zh-cn");
|
||||
provide("fn:router.reload", reload);
|
||||
provide("fn:locale.changed", localeChanged);
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { request, requestForMock } from "../service";
|
||||
import { env } from "/@/utils/util.env";
|
||||
|
||||
export interface RegisterReq {
|
||||
username: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
/**
|
||||
* @description: Login interface parameters
|
||||
*/
|
||||
@@ -19,6 +25,14 @@ export interface LoginRes {
|
||||
expire: number;
|
||||
}
|
||||
|
||||
export async function register(user: RegisterReq): Promise<UserInfoRes> {
|
||||
return await request({
|
||||
url: "/register",
|
||||
method: "post",
|
||||
data: user
|
||||
});
|
||||
}
|
||||
|
||||
export async function login(data: LoginReq): Promise<LoginRes> {
|
||||
if (env.PM_ENABLED === "false") {
|
||||
//没有开启权限模块,模拟登录
|
||||
|
||||
@@ -31,7 +31,7 @@ function createService() {
|
||||
const { code } = dataAxios;
|
||||
// 根据 code 进行判断
|
||||
if (code === undefined) {
|
||||
// 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本
|
||||
// 如果没有 code 代表这不是项目后端开发的接口
|
||||
errorCreate(`非标准返回:${dataAxios}, ${response.config.url}`);
|
||||
return dataAxios;
|
||||
} else {
|
||||
@@ -106,8 +106,8 @@ function createService() {
|
||||
* @description 创建请求方法
|
||||
* @param {Object} service axios 实例
|
||||
*/
|
||||
function createRequestFunction(service) {
|
||||
return function (config) {
|
||||
function createRequestFunction(service: any) {
|
||||
return function (config: any) {
|
||||
const configDefault = {
|
||||
headers: {
|
||||
"Content-Type": get(config, "headers.Content-Type", "application/json")
|
||||
|
||||
@@ -48,9 +48,12 @@ export function responseError(data = {}, msg = "请求失败", code = 500) {
|
||||
* @description 记录和显示错误
|
||||
* @param {Error} error 错误对象
|
||||
*/
|
||||
export function errorLog(error) {
|
||||
export function errorLog(error: any) {
|
||||
if (error?.response?.data?.message) {
|
||||
error.message = error?.response?.data?.message;
|
||||
}
|
||||
// 打印到控制台
|
||||
console.error(error);
|
||||
console.error("errorLog", error);
|
||||
// 显示提示
|
||||
uiContext.get().notification.error({ message: error.message });
|
||||
}
|
||||
@@ -59,8 +62,9 @@ export function errorLog(error) {
|
||||
* @description 创建一个错误
|
||||
* @param {String} msg 错误信息
|
||||
*/
|
||||
export function errorCreate(msg) {
|
||||
const error = new Error(msg);
|
||||
errorLog(error);
|
||||
throw error;
|
||||
export function errorCreate(msg: string) {
|
||||
const err = new Error(msg);
|
||||
console.error("errorCreate", err);
|
||||
uiContext.get().notification.error({ message: err.message });
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<a-dropdown class="fs-locale-picker">
|
||||
<fs-iconify icon="ion-globe-outline" @click.prevent></fs-iconify>
|
||||
<div>
|
||||
<fs-iconify icon="ion-globe-outline" @click.prevent></fs-iconify>
|
||||
</div>
|
||||
|
||||
<template #overlay>
|
||||
<a-menu @click="changeLocale">
|
||||
<a-menu-item v-for="item in languages" :key="item.key" :command="item.key">
|
||||
@@ -46,7 +49,7 @@ export default {
|
||||
const changeLocale = (change) => {
|
||||
i18n.global.locale.value = change.key;
|
||||
routerReload();
|
||||
localeChanged(change.key)
|
||||
localeChanged(change.key);
|
||||
};
|
||||
return {
|
||||
languages,
|
||||
@@ -58,7 +61,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.locale-picker {
|
||||
.fs-locale-picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template xmlns:w="http://www.w3.org/1999/xhtml">
|
||||
<template>
|
||||
<a-layout class="fs-framework">
|
||||
<a-layout-sider v-model:collapsed="asideCollapsed" :trigger="null" collapsible>
|
||||
<div class="header-logo">
|
||||
@@ -49,10 +49,10 @@
|
||||
</router-view>
|
||||
</a-layout-content>
|
||||
<a-layout-footer class="fs-framework-footer">
|
||||
<div>Powered by Greper</div>
|
||||
<div>Powered by handsfree.work</div>
|
||||
<div>v{{ version }}</div>
|
||||
|
||||
<fs-source-link />
|
||||
<!-- <fs-source-link />-->
|
||||
</a-layout-footer>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
@@ -95,7 +95,7 @@ export default {
|
||||
}
|
||||
onErrorCaptured((e) => {
|
||||
console.error("ErrorCaptured:", e);
|
||||
notification.error({ message: e.message });
|
||||
// notification.error({ message: e.message });
|
||||
//阻止错误向上传递
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -2,8 +2,6 @@ import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import Antd from "ant-design-vue";
|
||||
import "ant-design-vue/dist/antd.less";
|
||||
// import "virtual:windi.css";
|
||||
import "./style/common.less";
|
||||
import "./mock";
|
||||
import i18n from "./i18n";
|
||||
|
||||
@@ -12,7 +12,7 @@ export const frameworkResource = [
|
||||
component: LayoutFramework,
|
||||
meta: {
|
||||
icon: "ion:accessibility",
|
||||
auth: true
|
||||
authOnly: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -13,14 +13,14 @@ export const headerResource = [
|
||||
children: [
|
||||
{
|
||||
title: "github",
|
||||
path: "http://github.com/certd/certd",
|
||||
path: "https://github.com/certd/certd",
|
||||
meta: {
|
||||
icon: "ion:logo-github"
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "gitee",
|
||||
path: "http://gitee.com/certd/certd",
|
||||
path: "https://gitee.com/certd/certd",
|
||||
meta: {
|
||||
icon: "ion:logo-octocat"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export const certdResources = [
|
||||
redirect: "/certd/pipeline",
|
||||
meta: {
|
||||
icon: "ion:key-outline",
|
||||
auth: true
|
||||
authOnly: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -35,6 +35,28 @@ export const certdResources = [
|
||||
meta: {
|
||||
icon: "ion:disc-outline"
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "设置",
|
||||
name: "certdSettings",
|
||||
path: "/certd/settings",
|
||||
redirect: "/certd/settings/email",
|
||||
meta: {
|
||||
icon: "ion:settings-outline",
|
||||
authOnly: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "邮箱设置",
|
||||
name: "email",
|
||||
path: "/certd/settings/email",
|
||||
component: "/certd/settings/email-setting.vue",
|
||||
meta: {
|
||||
icon: "ion:mail-outline",
|
||||
authOnly: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,6 +15,14 @@ export const outsideResource = [
|
||||
name: "login",
|
||||
path: "/login",
|
||||
component: "/framework/login/index.vue"
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: "注册"
|
||||
},
|
||||
name: "register",
|
||||
path: "/register",
|
||||
component: "/framework/register/index.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@ export const usePageStore = defineStore({
|
||||
fullPath: "/index",
|
||||
meta: {
|
||||
title: "首页",
|
||||
auth: false
|
||||
authOnly: false
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -30,13 +30,19 @@ export const useResourceStore = defineStore({
|
||||
currentAsidePath: ""
|
||||
}),
|
||||
getters: {
|
||||
// @ts-ignore
|
||||
getAsideMenus() {
|
||||
// @ts-ignore
|
||||
return this.asideMenus;
|
||||
},
|
||||
// @ts-ignore
|
||||
getHeaderMenus() {
|
||||
// @ts-ignore
|
||||
return this.headerMenus;
|
||||
},
|
||||
// @ts-ignore
|
||||
getFrameworkMenus() {
|
||||
// @ts-ignore
|
||||
return this.frameworkMenus;
|
||||
}
|
||||
},
|
||||
@@ -54,17 +60,17 @@ export const useResourceStore = defineStore({
|
||||
this.inited = true;
|
||||
|
||||
const showMenus = _.cloneDeep(frameworkMenus[0].children);
|
||||
this.frameworkMenus = filterMenus(showMenus, (item) => {
|
||||
this.frameworkMenus = filterMenus(showMenus, (item: any) => {
|
||||
return item?.meta?.showOnHeader !== false;
|
||||
});
|
||||
|
||||
this.fixedAsideMenus = findMenus(showMenus, (item) => {
|
||||
this.fixedAsideMenus = findMenus(showMenus, (item: any) => {
|
||||
return item?.meta?.fixedAside === true;
|
||||
});
|
||||
this.headerMenus = headerMenus;
|
||||
this.setAsideMenu();
|
||||
},
|
||||
setAsideMenu(topMenu?) {
|
||||
setAsideMenu(topMenu?: any) {
|
||||
if (this.frameworkMenus.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -74,13 +80,13 @@ export const useResourceStore = defineStore({
|
||||
const asideMenus = topMenu?.children || [];
|
||||
this.asideMenus = [...this.fixedAsideMenus, ...asideMenus];
|
||||
},
|
||||
setAsideMenuByCurrentRoute(matched) {
|
||||
setAsideMenuByCurrentRoute(matched: any) {
|
||||
const menuHeader = this.frameworkMenus;
|
||||
if (matched?.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
function findFromTree(tree, find) {
|
||||
function findFromTree(tree: any, find: any) {
|
||||
const results: Array<any> = [];
|
||||
for (const item of tree) {
|
||||
if (find(item)) {
|
||||
@@ -88,7 +94,7 @@ export const useResourceStore = defineStore({
|
||||
return results;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
const found = findFromTree(item.children, find);
|
||||
const found: any = findFromTree(item.children, find);
|
||||
if (found) {
|
||||
results.push(item);
|
||||
return results.concat(found);
|
||||
@@ -97,7 +103,7 @@ export const useResourceStore = defineStore({
|
||||
}
|
||||
}
|
||||
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 (this.currentAsidePath === _side[0]) {
|
||||
return;
|
||||
@@ -106,11 +112,11 @@ export const useResourceStore = defineStore({
|
||||
this.setAsideMenu(_side[0]);
|
||||
}
|
||||
},
|
||||
filterByPermission(permissions) {
|
||||
filterByPermission(permissions: any) {
|
||||
this.frameworkMenus = this.filterChildrenByPermission(this.frameworkMenus, permissions);
|
||||
},
|
||||
filterChildrenByPermission(list, permissions) {
|
||||
const menus = list.filter((item) => {
|
||||
filterChildrenByPermission(list: any, permissions: any) {
|
||||
const menus = list.filter((item: any) => {
|
||||
if (item?.meta?.permission) {
|
||||
return permissions.includes(item.meta.permission);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export const useSettingStore = defineStore({
|
||||
this.persistTheme();
|
||||
// await changeTheme(this.theme.primaryColor);
|
||||
},
|
||||
async setPrimaryColor(color) {
|
||||
async setPrimaryColor(color: any) {
|
||||
const theme = this.theme;
|
||||
theme.primaryColor = color;
|
||||
await this.setTheme();
|
||||
|
||||
@@ -7,10 +7,11 @@ import { LocalStorage } from "/src/utils/util.storage";
|
||||
import * as UserApi from "/src/api/modules/api.user";
|
||||
// @ts-ignore
|
||||
import { LoginReq, UserInfoRes } from "/@/api/modules/api.user";
|
||||
import { Modal } from "ant-design-vue";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
import { mitter } from "/src/utils/util.mitt";
|
||||
import { RegisterReq } from "/src/api/modules/api.user";
|
||||
|
||||
interface UserState {
|
||||
userInfo: Nullable<UserInfoRes>;
|
||||
@@ -50,6 +51,14 @@ export const useUserStore = defineStore({
|
||||
LocalStorage.remove(TOKEN_KEY);
|
||||
LocalStorage.remove(USER_INFO_KEY);
|
||||
},
|
||||
|
||||
async register(user: RegisterReq) {
|
||||
await UserApi.register(user);
|
||||
notification.success({
|
||||
message: "注册成功,请登录"
|
||||
});
|
||||
await router.replace("/login");
|
||||
},
|
||||
/**
|
||||
* @description: login
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import './tailwind.less';
|
||||
@import "ant-design-vue/dist/antd.less";
|
||||
@import './theme/index.less';
|
||||
@import './theme/default.less';
|
||||
@import './scroll.less';
|
||||
@@ -49,9 +51,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.red{
|
||||
color:red
|
||||
}
|
||||
|
||||
|
||||
.font12{
|
||||
font-size: 12px;
|
||||
@@ -88,27 +88,6 @@ h1, h2, h3, h4, h5, h6 {
|
||||
}
|
||||
}
|
||||
|
||||
.mt-10{
|
||||
margin-top:10px;
|
||||
}
|
||||
.ml-5{
|
||||
margin-left:5px;
|
||||
}
|
||||
.ml-10{
|
||||
margin-left: 10px;
|
||||
}
|
||||
.mtb-5{
|
||||
margin: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
.mb-10{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.mlr-5{
|
||||
margin: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
.gray{
|
||||
color:gray;
|
||||
}
|
||||
@@ -125,10 +104,6 @@ h1, h2, h3, h4, h5, h6 {
|
||||
color:yellow;
|
||||
}
|
||||
|
||||
.ml-2{
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.font-20{
|
||||
font-size:20px
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
img.ant-image-preview-img{
|
||||
display: initial;
|
||||
}
|
||||
|
||||
|
||||
3
packages/ui/certd-client/src/style/tailwind.less
Normal file
3
packages/ui/certd-client/src/style/tailwind.less
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,13 +1,13 @@
|
||||
import _ from "lodash";
|
||||
export default {
|
||||
arrayToMap(array) {
|
||||
arrayToMap(array: any) {
|
||||
if (!array) {
|
||||
return {};
|
||||
}
|
||||
if (!_.isArray(array)) {
|
||||
return array;
|
||||
}
|
||||
const map = {};
|
||||
const map: any = {};
|
||||
for (const item of array) {
|
||||
if (item.key) {
|
||||
map[item.key] = item;
|
||||
@@ -15,7 +15,7 @@ export default {
|
||||
}
|
||||
return map;
|
||||
},
|
||||
mapToArray(map) {
|
||||
mapToArray(map: any) {
|
||||
if (!map) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
// @ts-ignore
|
||||
import _ from "lodash";
|
||||
export function getEnvValue(key) {
|
||||
export function getEnvValue(key: string) {
|
||||
// @ts-ignore
|
||||
return import.meta.env["VITE_APP_" + key];
|
||||
}
|
||||
|
||||
export class EnvConfig {
|
||||
API;
|
||||
MODE;
|
||||
STORAGE;
|
||||
TITLE;
|
||||
PM_ENABLED;
|
||||
API: string;
|
||||
MODE: string;
|
||||
STORAGE: string;
|
||||
TITLE: string;
|
||||
PM_ENABLED: string;
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
@@ -19,6 +20,7 @@ export class EnvConfig {
|
||||
_.forEach(import.meta.env, (value, key) => {
|
||||
if (key.startsWith("VITE_APP")) {
|
||||
key = key.replace("VITE_APP_", "");
|
||||
//@ts-ignore
|
||||
this[key] = value;
|
||||
}
|
||||
});
|
||||
@@ -26,7 +28,8 @@ export class EnvConfig {
|
||||
this.MODE = import.meta.env.MODE;
|
||||
}
|
||||
|
||||
get(key, defaultValue) {
|
||||
get(key: string, defaultValue: string) {
|
||||
//@ts-ignore
|
||||
return this[key] ?? defaultValue;
|
||||
}
|
||||
isDev() {
|
||||
|
||||
@@ -2,9 +2,9 @@ import { env } from "./util.env";
|
||||
export const site = {
|
||||
/**
|
||||
* @description 更新标题
|
||||
* @param {String} title 标题
|
||||
* @param titleText
|
||||
*/
|
||||
title: function (titleText) {
|
||||
title: function (titleText: string) {
|
||||
const processTitle = env.TITLE || "FsAdmin";
|
||||
window.document.title = `${processTitle}${titleText ? ` | ${titleText}` : ""}`;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,9 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
||||
name: {
|
||||
title: "名称",
|
||||
type: "text",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
form: {
|
||||
rules: [{ required: true, message: "必填项" }]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">授权管理</div>
|
||||
<div class="title">
|
||||
授权管理
|
||||
<span class="sub">管理第三方系统授权信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { RunHistory } from "/@/views/certd/pipeline/pipeline/type";
|
||||
|
||||
const apiPrefix = "/pi/history";
|
||||
|
||||
export async function GetList(query) {
|
||||
export async function GetList(query: any) {
|
||||
const list = await request({
|
||||
url: apiPrefix + "/list",
|
||||
method: "post",
|
||||
@@ -18,7 +18,7 @@ export async function GetList(query) {
|
||||
return list;
|
||||
}
|
||||
|
||||
export async function GetDetail(query): Promise<RunHistory> {
|
||||
export async function GetDetail(query: any): Promise<RunHistory> {
|
||||
const detail = await request({
|
||||
url: apiPrefix + "/detail",
|
||||
method: "post",
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { request } from "/src/api/service";
|
||||
const apiPrefix = "/pi/pipeline";
|
||||
|
||||
export function GetList(query) {
|
||||
const apiPrefix = "/pi/pipeline";
|
||||
const historyApiPrefix = "/pi/history";
|
||||
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
@@ -9,7 +11,7 @@ export function GetList(query) {
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj) {
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
@@ -17,7 +19,7 @@ export function AddObj(obj) {
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj) {
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
@@ -25,7 +27,7 @@ export function UpdateObj(obj) {
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id) {
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
@@ -33,7 +35,7 @@ export function DelObj(id) {
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id) {
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
@@ -41,7 +43,7 @@ export function GetObj(id) {
|
||||
});
|
||||
}
|
||||
|
||||
export function GetDetail(id) {
|
||||
export function GetDetail(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/detail",
|
||||
method: "post",
|
||||
@@ -49,7 +51,7 @@ export function GetDetail(id) {
|
||||
});
|
||||
}
|
||||
|
||||
export function Save(pipelineEntity) {
|
||||
export function Save(pipelineEntity: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/save",
|
||||
method: "post",
|
||||
@@ -57,10 +59,18 @@ export function Save(pipelineEntity) {
|
||||
});
|
||||
}
|
||||
|
||||
export function Trigger(id) {
|
||||
export function Trigger(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/trigger",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetFiles(pipelineId: number) {
|
||||
return request({
|
||||
url: historyApiPrefix + "/files",
|
||||
method: "post",
|
||||
params: { pipelineId }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import { useRouter } from "vue-router";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { statusUtil } from "/@/views/certd/pipeline/pipeline/utils/util.status";
|
||||
import { nanoid } from "nanoid";
|
||||
import { message } from "ant-design-vue";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { env } from "/@/utils/util.env";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
|
||||
export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
@@ -73,6 +75,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
|
||||
});
|
||||
}
|
||||
const userStore = useUserStore();
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
@@ -124,6 +127,33 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
order: 2,
|
||||
icon: "ant-design:setting-outlined"
|
||||
},
|
||||
download: {
|
||||
order: 3,
|
||||
title: null,
|
||||
type: "link",
|
||||
icon: "ant-design:download-outlined",
|
||||
async click({ row }) {
|
||||
const files = await api.GetFiles(row.id);
|
||||
Modal.success({
|
||||
title: "文件下载",
|
||||
content: () => {
|
||||
const children = [];
|
||||
for (const file of files) {
|
||||
const downloadUrl = `${env.API}/pi/history/download?pipelineId=${row.id}&fileId=${file.id}`;
|
||||
children.push(
|
||||
<p>
|
||||
<a href={downloadUrl} target={"_blank"}>
|
||||
{file.filename}
|
||||
</a>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return <div class={"mt-3"}>{children}</div>;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
remove: {
|
||||
order: 5
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
<p>
|
||||
<fs-date-format :model-value="runnable.status?.startTime"></fs-date-format>
|
||||
<a-tag class="ml-10" :color="status.color">{{ status.label }}</a-tag>
|
||||
<a-tag class="ml-1" :color="status.color">{{ status.label }}</a-tag>
|
||||
|
||||
<a-tag v-if="isCurrent" class="pointer" color="green" :closable="true" @close="cancel">当前</a-tag>
|
||||
<a-tag v-else-if="!editMode" class="pointer" color="blue" @click="view">查看</a-tag>
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<a-drawer v-model:visible="notificationDrawerVisible" placement="right" :closable="true" width="600px" class="pi-notification-form" :after-visible-change="notificationDrawerOnAfterVisibleChange">
|
||||
<template #title>
|
||||
编辑触发器
|
||||
<a-button v-if="mode === 'edit'" @click="notificationDelete()">
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
</a-button>
|
||||
</template>
|
||||
<template v-if="currentNotification">
|
||||
<pi-container>
|
||||
<a-form ref="notificationFormRef" class="notification-form" :model="currentNotification" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<fs-form-item
|
||||
v-model="currentNotification.type"
|
||||
:item="{
|
||||
title: '类型',
|
||||
key: 'type',
|
||||
value: 'email',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
disabled: !editMode,
|
||||
options: [{ value: 'email', label: '邮件' }]
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
/>
|
||||
<fs-form-item
|
||||
v-model="currentNotification.when"
|
||||
:item="{
|
||||
title: '触发时机',
|
||||
key: 'type',
|
||||
value: ['error'],
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
disabled: !editMode,
|
||||
mode: 'multiple',
|
||||
options: [
|
||||
{ value: 'start', label: '开始时' },
|
||||
{ value: 'success', label: '成功时' },
|
||||
{ value: 'turnToSuccess', label: '错误转成功时' },
|
||||
{ value: 'error', label: '错误时' }
|
||||
]
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
/>
|
||||
<pi-notification-form-email ref="optionsRef" v-model:options="currentNotification.options"></pi-notification-form-email>
|
||||
</a-form>
|
||||
|
||||
<template #footer>
|
||||
<a-form-item v-if="editMode" :wrapper-col="{ span: 14, offset: 4 }">
|
||||
<a-button type="primary" @click="notificationSave"> 确定 </a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</pi-container>
|
||||
</template>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Modal } from "ant-design-vue";
|
||||
import { ref } from "vue";
|
||||
import _ from "lodash";
|
||||
import { nanoid } from "nanoid";
|
||||
import PiNotificationFormEmail from "./pi-notification-form-email.vue";
|
||||
|
||||
export default {
|
||||
name: "PiNotificationForm",
|
||||
components: { PiNotificationFormEmail },
|
||||
props: {
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
emits: ["update"],
|
||||
setup(props: any, context: any) {
|
||||
/**
|
||||
* notification drawer
|
||||
* @returns
|
||||
*/
|
||||
function useNotificationForm() {
|
||||
const mode = ref("add");
|
||||
const callback = ref();
|
||||
const currentNotification = ref({ type: undefined, when: [], options: {} });
|
||||
const currentPlugin = ref({});
|
||||
const notificationFormRef = ref(null);
|
||||
const notificationDrawerVisible = ref(false);
|
||||
const optionsRef = ref();
|
||||
const rules = ref({
|
||||
type: [
|
||||
{
|
||||
type: "string",
|
||||
required: true,
|
||||
message: "请选择类型"
|
||||
}
|
||||
],
|
||||
when: [
|
||||
{
|
||||
type: "string",
|
||||
required: true,
|
||||
message: "请选择通知时机"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const notificationDrawerShow = () => {
|
||||
notificationDrawerVisible.value = true;
|
||||
};
|
||||
const notificationDrawerClose = () => {
|
||||
notificationDrawerVisible.value = false;
|
||||
};
|
||||
|
||||
const notificationDrawerOnAfterVisibleChange = (val: any) => {
|
||||
console.log("notificationDrawerOnAfterVisibleChange", val);
|
||||
};
|
||||
|
||||
const notificationOpen = (notification: any, emit: any) => {
|
||||
callback.value = emit;
|
||||
currentNotification.value = _.cloneDeep(notification);
|
||||
console.log("currentNotificationOpen", currentNotification.value);
|
||||
notificationDrawerShow();
|
||||
};
|
||||
|
||||
const notificationAdd = (emit: any) => {
|
||||
mode.value = "add";
|
||||
const notification = { id: nanoid(), type: "email", when: ["error"] };
|
||||
notificationOpen(notification, emit);
|
||||
};
|
||||
|
||||
const notificationEdit = (notification: any, emit: any) => {
|
||||
mode.value = "edit";
|
||||
notificationOpen(notification, emit);
|
||||
};
|
||||
|
||||
const notificationView = (notification: any, emit: any) => {
|
||||
mode.value = "view";
|
||||
notificationOpen(notification, emit);
|
||||
};
|
||||
|
||||
const notificationSave = async (e: any) => {
|
||||
currentNotification.value.options = await optionsRef.value.getValue();
|
||||
console.log("currentNotificationSave", currentNotification.value);
|
||||
try {
|
||||
await notificationFormRef.value.validate();
|
||||
} catch (e) {
|
||||
console.error("表单验证失败:", e);
|
||||
return;
|
||||
}
|
||||
|
||||
callback.value("save", currentNotification.value);
|
||||
notificationDrawerClose();
|
||||
};
|
||||
|
||||
const notificationDelete = () => {
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要删除此触发器吗?`,
|
||||
async onOk() {
|
||||
callback.value("delete");
|
||||
notificationDrawerClose();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const blankFn = () => {
|
||||
return {};
|
||||
};
|
||||
return {
|
||||
notificationFormRef,
|
||||
mode,
|
||||
notificationAdd,
|
||||
notificationEdit,
|
||||
notificationView,
|
||||
notificationDrawerShow,
|
||||
notificationDrawerVisible,
|
||||
notificationDrawerOnAfterVisibleChange,
|
||||
currentNotification,
|
||||
currentPlugin,
|
||||
notificationSave,
|
||||
notificationDelete,
|
||||
rules,
|
||||
blankFn,
|
||||
optionsRef
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...useNotificationForm(),
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 16 }
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.pi-notification-form {
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div>
|
||||
<fs-form-item
|
||||
v-model="optionsFormState.receivers"
|
||||
:item="{
|
||||
title: '收件邮箱',
|
||||
key: 'type',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
mode: 'tags'
|
||||
},
|
||||
rules: [{ required: true, message: '此项必填' }]
|
||||
}"
|
||||
/>
|
||||
|
||||
<a-alert class="m-1" type="info">
|
||||
<template #message> 还没有配置邮件服务器?<router-link :to="{ path: '/certd/settings/email' }">现在就去</router-link> </template>
|
||||
</a-alert>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { Ref, ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Object as PropType<any>,
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
const optionsFormState: Ref<any> = ref({});
|
||||
|
||||
watch(
|
||||
() => {
|
||||
return props.options;
|
||||
},
|
||||
() => {
|
||||
optionsFormState.value = {
|
||||
...props.options
|
||||
};
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits(["change"]);
|
||||
function doEmit() {
|
||||
emit("change", { ...optionsFormState.value });
|
||||
}
|
||||
|
||||
function getValue() {
|
||||
return { ...optionsFormState.value };
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
doEmit,
|
||||
getValue
|
||||
});
|
||||
</script>
|
||||
@@ -84,11 +84,12 @@
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="jsx">
|
||||
<script lang="tsx">
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { inject, ref } from "vue";
|
||||
import { computed, inject, Ref, ref } from "vue";
|
||||
import _ from "lodash";
|
||||
import { nanoid } from "nanoid";
|
||||
import { compute } from "@fast-crud/fast-crud";
|
||||
export default {
|
||||
name: "PiStepForm",
|
||||
props: {
|
||||
@@ -98,21 +99,21 @@ export default {
|
||||
}
|
||||
},
|
||||
emits: ["update"],
|
||||
setup(props, context) {
|
||||
setup(props: any, context: any) {
|
||||
/**
|
||||
* step drawer
|
||||
* @returns
|
||||
*/
|
||||
function useStepForm() {
|
||||
const stepPluginDefineList = inject("plugins");
|
||||
const stepPluginDefineList: any = inject("plugins");
|
||||
|
||||
const mode = ref("add");
|
||||
const callback = ref();
|
||||
const currentStep = ref({ title: undefined, input: {} });
|
||||
const currentPlugin = ref({});
|
||||
const stepFormRef = ref(null);
|
||||
const stepDrawerVisible = ref(false);
|
||||
const rules = ref({
|
||||
const mode: Ref = ref("add");
|
||||
const callback: Ref = ref();
|
||||
const currentStep: Ref = ref({ title: undefined, input: {} });
|
||||
const currentPlugin: Ref = ref({});
|
||||
const stepFormRef: Ref = ref(null);
|
||||
const stepDrawerVisible: Ref = ref(false);
|
||||
const rules: Ref = ref({
|
||||
name: [
|
||||
{
|
||||
type: "string",
|
||||
@@ -122,7 +123,7 @@ export default {
|
||||
]
|
||||
});
|
||||
|
||||
const stepTypeSelected = (item) => {
|
||||
const stepTypeSelected = (item: any) => {
|
||||
currentStep.value.type = item.name;
|
||||
currentStep.value.title = item.title;
|
||||
console.log("currentStepTypeChanged:", currentStep.value);
|
||||
@@ -155,13 +156,14 @@ export default {
|
||||
stepDrawerVisible.value = false;
|
||||
};
|
||||
|
||||
const stepDrawerOnAfterVisibleChange = (val) => {
|
||||
const stepDrawerOnAfterVisibleChange = (val: any) => {
|
||||
console.log("stepDrawerOnAfterVisibleChange", val);
|
||||
};
|
||||
|
||||
const stepOpen = (step, emit) => {
|
||||
const stepOpen = (step: any, emit: any) => {
|
||||
callback.value = emit;
|
||||
currentStep.value = _.merge({ input: {}, strategy: {} }, step);
|
||||
|
||||
console.log("currentStepOpen", currentStep.value);
|
||||
if (step.type) {
|
||||
changeCurrentPlugin(currentStep.value);
|
||||
@@ -169,9 +171,9 @@ export default {
|
||||
stepDrawerShow();
|
||||
};
|
||||
|
||||
const stepAdd = (emit) => {
|
||||
const stepAdd = (emit: any) => {
|
||||
mode.value = "add";
|
||||
const step = {
|
||||
const step: any = {
|
||||
id: nanoid(),
|
||||
title: "新任务",
|
||||
type: undefined,
|
||||
@@ -182,31 +184,49 @@ export default {
|
||||
stepOpen(step, emit);
|
||||
};
|
||||
|
||||
const stepEdit = (step, emit) => {
|
||||
const stepEdit = (step: any, emit: any) => {
|
||||
mode.value = "edit";
|
||||
stepOpen(step, emit);
|
||||
};
|
||||
|
||||
const stepView = (step, emit) => {
|
||||
const stepView = (step: any, emit: any) => {
|
||||
mode.value = "view";
|
||||
stepOpen(step, emit);
|
||||
};
|
||||
|
||||
const changeCurrentPlugin = (step) => {
|
||||
const changeCurrentPlugin = (step: any) => {
|
||||
const stepType = step.type;
|
||||
const pluginDefine = stepPluginDefineList.value.find((p) => {
|
||||
const pluginDefine = stepPluginDefineList.value.find((p: any) => {
|
||||
return p.name === stepType;
|
||||
});
|
||||
if (pluginDefine) {
|
||||
step.type = stepType;
|
||||
step._isAdd = false;
|
||||
currentPlugin.value = pluginDefine;
|
||||
currentPlugin.value = _.cloneDeep(pluginDefine);
|
||||
for (let key in currentPlugin.value.input) {
|
||||
const input = currentPlugin.value.input[key];
|
||||
if (input?.reference) {
|
||||
for (const reference of input.reference) {
|
||||
_.set(
|
||||
input,
|
||||
reference.dest,
|
||||
computed<any>(() => {
|
||||
const scope = {
|
||||
form: currentStep.value.input
|
||||
};
|
||||
return _.get(scope, reference.src);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("currentStepTypeChanged:", currentStep.value);
|
||||
console.log("currentStepPlugin:", currentPlugin.value);
|
||||
};
|
||||
|
||||
const stepSave = async (e) => {
|
||||
const stepSave = async (e: any) => {
|
||||
console.log("currentStepSave", currentStep.value);
|
||||
try {
|
||||
await stepFormRef.value.validate();
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<a-drawer
|
||||
v-model:visible="taskDrawerVisible"
|
||||
placement="right"
|
||||
:closable="true"
|
||||
width="600px"
|
||||
class="pi-task-form"
|
||||
:after-visible-change="taskDrawerOnAfterVisibleChange"
|
||||
>
|
||||
<a-drawer v-model:visible="taskDrawerVisible" placement="right" :closable="true" width="600px" class="pi-task-form" :after-visible-change="taskDrawerOnAfterVisibleChange">
|
||||
<template #title>
|
||||
编辑任务
|
||||
<a-button v-if="editMode" @click="taskDelete()">
|
||||
@@ -15,13 +8,7 @@
|
||||
</template>
|
||||
<template v-if="currentTask">
|
||||
<pi-container>
|
||||
<a-form
|
||||
ref="taskFormRef"
|
||||
class="task-form"
|
||||
:model="currentTask"
|
||||
:label-col="labelCol"
|
||||
:wrapper-col="wrapperCol"
|
||||
>
|
||||
<a-form ref="taskFormRef" class="task-form" :model="currentTask" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<fs-form-item
|
||||
v-model="currentTask.title"
|
||||
:item="{
|
||||
@@ -37,13 +24,7 @@
|
||||
/>
|
||||
|
||||
<div class="steps">
|
||||
<a-form-item
|
||||
:value="currentTask.steps"
|
||||
name="steps"
|
||||
label=""
|
||||
:wrapper-col="{ span: 24 }"
|
||||
:rules="[{ required: true, message: '至少需要一个步骤,或者你可以点击标题右边删除按钮删除此任务' }]"
|
||||
>
|
||||
<a-form-item :value="currentTask.steps" name="steps" label="" :wrapper-col="{ span: 24 }" :rules="[{ required: true, message: '至少需要一个步骤,或者你可以点击标题右边删除按钮删除此任务' }]">
|
||||
<a-descriptions title="任务步骤" size="small">
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="stepAdd(currentTask)">添加步骤</a-button>
|
||||
@@ -100,14 +81,14 @@ export default {
|
||||
}
|
||||
},
|
||||
emits: ["update"],
|
||||
setup(props, ctx) {
|
||||
setup(props: any, ctx: any) {
|
||||
function useStep() {
|
||||
const stepFormRef: Ref<any> = ref(null);
|
||||
const currentStepIndex = ref(0);
|
||||
provide("currentStepIndex", currentStepIndex);
|
||||
const stepAdd = (task) => {
|
||||
const stepAdd = (task: any) => {
|
||||
currentStepIndex.value = task.steps.length;
|
||||
stepFormRef.value.stepAdd((type, value) => {
|
||||
stepFormRef.value.stepAdd((type: any, value: any) => {
|
||||
if (type === "save") {
|
||||
task.steps.push(value);
|
||||
if (!task.title || task.title === "新任务") {
|
||||
@@ -116,12 +97,12 @@ export default {
|
||||
}
|
||||
});
|
||||
};
|
||||
const stepEdit = (task, step, stepIndex) => {
|
||||
const stepEdit = (task: any, step: any, stepIndex: any) => {
|
||||
currentStepIndex.value = stepIndex;
|
||||
console.log("step.edit start", task, step, props.editMode);
|
||||
if (props.editMode) {
|
||||
console.log("step.edit", task, step);
|
||||
stepFormRef.value.stepEdit(step, (type, value) => {
|
||||
stepFormRef.value.stepEdit(step, (type: any, value: any) => {
|
||||
console.log("step.save", step, type, value);
|
||||
if (type === "delete") {
|
||||
task.steps.splice(stepIndex, 1);
|
||||
@@ -131,11 +112,11 @@ export default {
|
||||
console.log("task.steps", task.steps);
|
||||
});
|
||||
} else {
|
||||
stepFormRef.value.stepView(step, (type, value) => {});
|
||||
stepFormRef.value.stepView(step, (type: any, value: any) => {});
|
||||
}
|
||||
};
|
||||
|
||||
const stepDelete = (task, stepIndex) => {
|
||||
const stepDelete = (task: any, stepIndex: any) => {
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要删除此步骤吗?`,
|
||||
@@ -176,34 +157,34 @@ export default {
|
||||
taskDrawerVisible.value = false;
|
||||
};
|
||||
|
||||
const taskDrawerOnAfterVisibleChange = (val) => {
|
||||
const taskDrawerOnAfterVisibleChange = (val: any) => {
|
||||
console.log("taskDrawerOnAfterVisibleChange", val);
|
||||
};
|
||||
|
||||
const taskOpen = (task, emit) => {
|
||||
const taskOpen = (task: any, emit: any) => {
|
||||
callback.value = emit;
|
||||
currentTask.value = _.merge({ steps: {} }, task);
|
||||
console.log("currentTaskOpen", currentTask.value);
|
||||
taskDrawerShow();
|
||||
};
|
||||
|
||||
const taskAdd = (emit) => {
|
||||
const taskAdd = (emit: any) => {
|
||||
mode.value = "add";
|
||||
const task = { id: nanoid(), title: "新任务", steps: [], status: null };
|
||||
const task: any = { id: nanoid(), title: "新任务", steps: [], status: null };
|
||||
taskOpen(task, emit);
|
||||
};
|
||||
|
||||
const taskEdit = (task, emit) => {
|
||||
const taskEdit = (task: any, emit: any) => {
|
||||
mode.value = "edit";
|
||||
taskOpen(task, emit);
|
||||
};
|
||||
|
||||
const taskView = (task, emit) => {
|
||||
const taskView = (task: any, emit: any) => {
|
||||
mode.value = "view";
|
||||
taskOpen(task, emit);
|
||||
};
|
||||
|
||||
const taskSave = async (e) => {
|
||||
const taskSave = async (e: any) => {
|
||||
console.log("currentTaskSave", currentTask.value);
|
||||
try {
|
||||
await taskFormRef.value.validate();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<fs-page v-if="pipeline" class="page-pipeline-edit">
|
||||
<template #header>
|
||||
<div class="title">
|
||||
<fs-button icon="ion:left" @click="goBack" />
|
||||
<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>
|
||||
</div>
|
||||
<div class="more">
|
||||
@@ -63,7 +63,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="(stage, index) of pipeline.stages" :key="stage.id" class="stage" :class="{ 'last-stage': !editMode && index === pipeline.stages.length - 1 }">
|
||||
<div v-for="(stage, index) of pipeline.stages" :key="stage.id" class="stage" :class="{ 'last-stage': isLastStage(index) }">
|
||||
<div class="title">
|
||||
<pi-editable v-model="stage.title" :disabled="!editMode"></pi-editable>
|
||||
</div>
|
||||
@@ -114,7 +114,62 @@
|
||||
<div class="task">
|
||||
<a-button shape="round" type="dashed" @click="stageAdd()">
|
||||
<fs-icon icon="ion:add-circle-outline"></fs-icon>
|
||||
新任务
|
||||
添加任务
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-container">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" type="dashed" @click="notificationAdd()">
|
||||
<fs-icon icon="ion:add-circle-outline"></fs-icon>
|
||||
|
||||
添加通知
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(item, ii) of pipeline.notifications" :key="ii" class="task-container">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" @click="notificationEdit(item, ii as number)">
|
||||
<fs-icon icon="ion:notifications"></fs-icon>
|
||||
【通知】 {{ item.type }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="stage last-stage">
|
||||
<div class="title">
|
||||
<pi-editable model-value="结束" :disabled="true" />
|
||||
</div>
|
||||
<div v-if="pipeline.notifications?.length > 0" class="tasks">
|
||||
<div v-for="(item, index) of pipeline.notifications" :key="index" class="task-container" :class="{ 'first-task': index == 0 }">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" @click="notificationEdit(item, index)">
|
||||
<fs-icon icon="ion:notifications"></fs-icon>
|
||||
|
||||
【通知】 {{ item.type }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="tasks">
|
||||
<div class="task-container first-task">
|
||||
<div class="line">
|
||||
<div class="flow-line"></div>
|
||||
</div>
|
||||
<div class="task">
|
||||
<a-button shape="round" type="dashed">
|
||||
<fs-icon icon="ion:notifications"></fs-icon>
|
||||
通知未设置
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,6 +195,7 @@
|
||||
<pi-task-form ref="taskFormRef" :edit-mode="editMode"></pi-task-form>
|
||||
<pi-trigger-form ref="triggerFormRef" :edit-mode="editMode"></pi-trigger-form>
|
||||
<pi-task-view ref="taskViewRef"></pi-task-view>
|
||||
<PiNotificationForm ref="notificationFormRef" :edit-mode="editMode"></PiNotificationForm>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
@@ -148,9 +204,10 @@ import { defineComponent, ref, provide, Ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import PiTaskForm from "./component/task-form/index.vue";
|
||||
import PiTriggerForm from "./component/trigger-form/index.vue";
|
||||
import PiNotificationForm from "./component/notification-form/index.vue";
|
||||
import PiTaskView from "./component/task-view/index.vue";
|
||||
import PiStatusShow from "./component/status-show.vue";
|
||||
import _ from "lodash-es";
|
||||
import _ from "lodash";
|
||||
import { message, Modal, notification } from "ant-design-vue";
|
||||
import { pluginManager } from "/@/views/certd/pipeline/pipeline/plugin";
|
||||
import { nanoid } from "nanoid";
|
||||
@@ -159,7 +216,7 @@ import PiHistoryTimelineItem from "/@/views/certd/pipeline/pipeline/component/hi
|
||||
export default defineComponent({
|
||||
name: "PipelineEdit",
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
components: { PiHistoryTimelineItem, PiTaskForm, PiTriggerForm, PiTaskView, PiStatusShow },
|
||||
components: { PiHistoryTimelineItem, PiTaskForm, PiTriggerForm, PiTaskView, PiStatusShow, PiNotificationForm },
|
||||
props: {
|
||||
pipelineId: {
|
||||
type: [Number, String],
|
||||
@@ -269,7 +326,7 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
const detail: PipelineDetail = await props.options.getPipelineDetail({ pipelineId: value });
|
||||
currentPipeline.value = _.merge({ title: "新管道流程", stages: [], triggers: [] }, detail.pipeline);
|
||||
currentPipeline.value = _.merge({ title: "新管道流程", stages: [], triggers: [], notifications: [] }, detail.pipeline);
|
||||
pipeline.value = currentPipeline.value;
|
||||
await loadHistoryList(true);
|
||||
},
|
||||
@@ -369,8 +426,13 @@ export default defineComponent({
|
||||
pipeline.value.stages.splice(stageIndex, 0, stage);
|
||||
});
|
||||
};
|
||||
|
||||
function isLastStage(index: number) {
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
stageAdd
|
||||
stageAdd,
|
||||
isLastStage
|
||||
};
|
||||
}
|
||||
|
||||
@@ -406,6 +468,41 @@ export default defineComponent({
|
||||
};
|
||||
}
|
||||
|
||||
function useNotification() {
|
||||
const notificationFormRef = ref();
|
||||
const notificationAdd = () => {
|
||||
notificationFormRef.value.notificationAdd((type: string, value: any) => {
|
||||
if (type === "save") {
|
||||
if (pipeline.value.notifications == null) {
|
||||
pipeline.value.notifications = [];
|
||||
}
|
||||
pipeline.value.notifications.push(value);
|
||||
}
|
||||
});
|
||||
};
|
||||
const notificationEdit = (notification: any, index: any) => {
|
||||
if (notificationFormRef.value == null) {
|
||||
return;
|
||||
}
|
||||
if (props.editMode) {
|
||||
notificationFormRef.value.notificationEdit(notification, (type: string, value: any) => {
|
||||
if (type === "delete") {
|
||||
pipeline.value.notifications.splice(index, 1);
|
||||
} else if (type === "save") {
|
||||
pipeline.value.notifications[index] = value;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
notificationFormRef.value.notificationView(notification, (type: string, value: any) => {});
|
||||
}
|
||||
};
|
||||
return {
|
||||
notificationAdd,
|
||||
notificationEdit,
|
||||
notificationFormRef
|
||||
};
|
||||
}
|
||||
|
||||
function useActions() {
|
||||
const saveLoading = ref();
|
||||
const run = async () => {
|
||||
@@ -484,6 +581,7 @@ export default defineComponent({
|
||||
historyCancel
|
||||
};
|
||||
}
|
||||
|
||||
const useTaskRet = useTask();
|
||||
const useStageRet = useStage(useTaskRet);
|
||||
|
||||
@@ -491,11 +589,13 @@ export default defineComponent({
|
||||
pipeline,
|
||||
currentHistory,
|
||||
histories,
|
||||
goBack,
|
||||
...useTaskRet,
|
||||
...useStageRet,
|
||||
...useTrigger(),
|
||||
...useActions(),
|
||||
...useHistory()
|
||||
...useHistory(),
|
||||
...useNotification()
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -504,6 +604,10 @@ export default defineComponent({
|
||||
.page-pipeline-edit {
|
||||
.fs-page-header {
|
||||
.title {
|
||||
display: flex;
|
||||
.back {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.pi-editable {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ export class PluginManager {
|
||||
* 初始化plugins
|
||||
* @param plugins
|
||||
*/
|
||||
init(plugins) {
|
||||
init(plugins: any) {
|
||||
const list = plugins;
|
||||
const map = {};
|
||||
const map: any = {};
|
||||
for (const plugin of list) {
|
||||
map[plugin.key] = plugin;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ export class PluginManager {
|
||||
return this.map[name];
|
||||
}
|
||||
|
||||
getPreStepOutputOptions({ pipeline, currentStageIndex, currentStepIndex, currentTask }) {
|
||||
getPreStepOutputOptions({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
|
||||
const steps = this.collectionPreStepOutputs({
|
||||
pipeline,
|
||||
currentStageIndex,
|
||||
@@ -42,7 +42,7 @@ export class PluginManager {
|
||||
return options;
|
||||
}
|
||||
|
||||
collectionPreStepOutputs({ pipeline, currentStageIndex, currentStepIndex, currentTask }) {
|
||||
collectionPreStepOutputs({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
|
||||
const steps: any[] = [];
|
||||
// 开始放step
|
||||
for (let i = 0; i < currentStageIndex; i++) {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { request } from "/@/api/service";
|
||||
const apiPrefix = "/basic/email";
|
||||
|
||||
export async function TestSend(receiver: string) {
|
||||
await request({
|
||||
url: apiPrefix + "/test",
|
||||
method: "post",
|
||||
data: {
|
||||
receiver
|
||||
}
|
||||
});
|
||||
}
|
||||
26
packages/ui/certd-client/src/views/certd/settings/api.ts
Normal file
26
packages/ui/certd-client/src/views/certd/settings/api.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { request } from "/@/api/service";
|
||||
const apiPrefix = "/sys/settings";
|
||||
|
||||
export const SettingKeys = {
|
||||
Email: "email"
|
||||
};
|
||||
export async function SettingsGet(key: string) {
|
||||
return await request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "post",
|
||||
params: {
|
||||
key
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function SettingsSave(key: string, setting: any) {
|
||||
await request({
|
||||
url: apiPrefix + "/save",
|
||||
method: "post",
|
||||
data: {
|
||||
key,
|
||||
setting: JSON.stringify(setting)
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<fs-page class="page-setting-email">
|
||||
<template #header>
|
||||
<div class="title">邮件设置</div>
|
||||
</template>
|
||||
<div class="email-form">
|
||||
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onFinish" @finish-failed="onFinishFailed">
|
||||
<a-form-item label="STMP域名" name="host" :rules="[{ required: true, message: '请输入smtp域名或ip' }]">
|
||||
<a-input v-model:value="formState.host" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="STMP端口" name="port" :rules="[{ required: true, message: '请输入smtp端口号' }]">
|
||||
<a-input v-model:value="formState.port" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="用户名" :name="['auth', 'user']" :rules="[{ required: true, message: '请输入用户名' }]">
|
||||
<a-input v-model:value="formState.auth.user" />
|
||||
</a-form-item>
|
||||
<a-form-item label="密码" :name="['auth', 'pass']" :rules="[{ required: true, message: '请输入密码' }]">
|
||||
<a-input-password v-model:value="formState.auth.pass" />
|
||||
</a-form-item>
|
||||
<a-form-item label="发件邮箱" name="sender" :rules="[{ required: true, message: '请输入发件邮箱' }]">
|
||||
<a-input v-model:value="formState.sender" />
|
||||
</a-form-item>
|
||||
<a-form-item label="是否ssl" name="secure">
|
||||
<a-switch v-model:checked="formState.secure" />
|
||||
</a-form-item>
|
||||
<a-form-item label="忽略证书校验" name="tls.rejectUnauthorized">
|
||||
<a-switch v-model:checked="formState.tls.rejectUnauthorized" />
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<a-button type="primary" html-type="submit">保存</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div>
|
||||
<a-form :model="testFormState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off" @finish="onTestSend">
|
||||
<a-form-item label="测试收件邮箱" name="receiver" :rules="[{ required: true, message: '请输入测试收件邮箱' }]">
|
||||
<a-input v-model:value="testFormState.receiver" />
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<a-button type="primary" html-type="submit">测试</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from "vue";
|
||||
import * as api from "./api";
|
||||
import * as emailApi from "./api.email";
|
||||
|
||||
import { SettingKeys } from "./api";
|
||||
import { notification } from "ant-design-vue";
|
||||
|
||||
interface FormState {
|
||||
host: string;
|
||||
port: number;
|
||||
auth: {
|
||||
user: string;
|
||||
pass: string;
|
||||
};
|
||||
secure: boolean; // use TLS
|
||||
tls: {
|
||||
// do not fail on invalid certs
|
||||
rejectUnauthorized?: boolean;
|
||||
};
|
||||
sender: string;
|
||||
}
|
||||
|
||||
const formState = reactive<Partial<FormState>>({
|
||||
auth: {
|
||||
user: "",
|
||||
pass: ""
|
||||
},
|
||||
tls: {}
|
||||
});
|
||||
|
||||
async function load() {
|
||||
const data: any = await api.SettingsGet(SettingKeys.Email);
|
||||
const setting = JSON.parse(data.setting);
|
||||
Object.assign(formState, setting);
|
||||
}
|
||||
|
||||
load();
|
||||
|
||||
const onFinish = async (form: any) => {
|
||||
console.log("Success:", form);
|
||||
await api.SettingsSave(SettingKeys.Email, form);
|
||||
notification.success({
|
||||
message: "保存成功"
|
||||
});
|
||||
};
|
||||
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
// console.log("Failed:", errorInfo);
|
||||
};
|
||||
|
||||
interface TestFormState {
|
||||
receiver: string;
|
||||
loading: boolean;
|
||||
}
|
||||
const testFormState = reactive<TestFormState>({
|
||||
receiver: "",
|
||||
loading: false
|
||||
});
|
||||
async function onTestSend() {
|
||||
testFormState.loading = true;
|
||||
try {
|
||||
await emailApi.TestSend(testFormState.receiver);
|
||||
notification.success({
|
||||
message: "发送成功"
|
||||
});
|
||||
} finally {
|
||||
testFormState.loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.page-setting-email {
|
||||
.email-form {
|
||||
width: 500px;
|
||||
margin: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,7 +7,7 @@
|
||||
<div style="margin-top: 10px">
|
||||
<a-button @click="formSubmit">提交表单</a-button>
|
||||
<a-button @click="formReset">重置表单</a-button>
|
||||
<a-button class="ml-10" @click="setFormDataTest">setFormData</a-button>
|
||||
<a-button class="ml-1" @click="setFormDataTest">setFormData</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB |
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<p class="d2-page-cover__sub-title">让你的证书永不过期</p>
|
||||
<div class="content">
|
||||
<img src="./image/preview.png" class="preview_img" />
|
||||
<img src="/images/preview.png" class="preview_img" />
|
||||
</div>
|
||||
<div class="footer_box">
|
||||
<div class="left"></div>
|
||||
@@ -76,6 +76,7 @@ export default defineComponent({
|
||||
width: 80%;
|
||||
.preview_img {
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item class="user-login-other">
|
||||
<!-- <router-link class="register" :to="{ name: 'index' }"> 注册 </router-link>-->
|
||||
<router-link class="register" :to="{ name: 'register' }"> 注册 </router-link>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
<template>
|
||||
<div class="main">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
class="user-layout-login"
|
||||
name="custom-validation"
|
||||
:model="formState"
|
||||
:rules="rules"
|
||||
v-bind="layout"
|
||||
@finish="handleFinish"
|
||||
@finishFailed="handleFinishFailed"
|
||||
>
|
||||
<a-form ref="formRef" class="user-layout-register" name="custom-validation" :model="formState" :rules="rules" v-bind="layout" @finish="handleFinish" @finishFailed="handleFinishFailed">
|
||||
<div class="login-title">用户注册</div>
|
||||
|
||||
<a-form-item required has-feedback name="username">
|
||||
<a-input v-model:value="formState.username" size="large" autocomplete="off">
|
||||
<template #prefix>
|
||||
@@ -46,13 +39,14 @@
|
||||
import { defineComponent, reactive, ref, toRaw } from "vue";
|
||||
import { useUserStore } from "/src/store/modules/user";
|
||||
export default defineComponent({
|
||||
name: "Register",
|
||||
name: "RegisterPage",
|
||||
setup() {
|
||||
const userStore = useUserStore();
|
||||
const formRef = ref();
|
||||
const formState = reactive({
|
||||
username: "",
|
||||
password: ""
|
||||
password: "",
|
||||
confirmPassword: ""
|
||||
});
|
||||
|
||||
const rules = {
|
||||
@@ -88,8 +82,7 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
console.log(values, formState);
|
||||
const userInfo = await userStore.login(
|
||||
await userStore.register(
|
||||
toRaw({
|
||||
password: formState.password,
|
||||
username: formState.username
|
||||
@@ -120,16 +113,17 @@ export default defineComponent({
|
||||
|
||||
<style lang="less">
|
||||
@import "../../../style/theme/index.less";
|
||||
.user-layout-login {
|
||||
.user-layout-register {
|
||||
label {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
color: @primary-color;
|
||||
// color: @primary-color;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
margin: 20px;
|
||||
margin: 30px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
.getCaptcha {
|
||||
display: block;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
purge: [],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
darkMode: "class",
|
||||
important: true,
|
||||
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
variants: {
|
||||
extend: {}
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import visualizer from "rollup-plugin-visualizer";
|
||||
import viteCompression from "vite-plugin-compression";
|
||||
import PurgeIcons from "vite-plugin-purge-icons";
|
||||
import * as path from "path";
|
||||
// import WindiCSS from "vite-plugin-windicss";
|
||||
// import { generateModifyVars } from "./build/modify-vars";
|
||||
// import { configThemePlugin } from "./build/theme-plugin";
|
||||
// import OptimizationPersist from "vite-plugin-optimize-persist";
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
CERTD_AUTH_JWT_KEYS=certd666
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user