diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json
index 356d967c4..798821007 100644
--- a/packages/core/pipeline/package.json
+++ b/packages/core/pipeline/package.json
@@ -2,9 +2,9 @@
"name": "@certd/pipeline",
"private": false,
"version": "1.0.6",
- "main": "./dist/bundle.js",
- "module": "./dist/pipeline.mjs",
- "types": "./dist/d/index.d.ts",
+ "main": "./src",
+ "module": "./src",
+ "types": "./src",
"publishConfig": {
"main": "./dist/bundle.js",
"module": "./dist/bundle.mjs",
@@ -19,6 +19,7 @@
"dependencies": {
"axios": "^1.4.0",
"node-forge": "^1.3.1",
+ "nodemailer": "^6.9.3",
"qs": "^6.11.2"
},
"devDependencies": {
diff --git a/packages/core/pipeline/src/core/executor.ts b/packages/core/pipeline/src/core/executor.ts
index 8768014cc..1a629deee 100644
--- a/packages/core/pipeline/src/core/executor.ts
+++ b/packages/core/pipeline/src/core/executor.ts
@@ -1,4 +1,4 @@
-import { ConcurrencyStrategy, NotificationType, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
+import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
import _ from "lodash";
import { RunHistory, RunnableCollection } from "./run-history";
import { AbstractTaskPlugin, PluginDefine, pluginRegistry } from "../plugin";
@@ -216,13 +216,13 @@ export class Executor {
let subject = "";
let content = "";
if (when === "start") {
- subject = `【CertD】${this.pipeline.title} 开始执行,buildId:${this.runtime.id}`;
- content = `【CertD】${this.pipeline.title} 开始执行,buildId:${this.runtime.id}`;
+ 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 = `【CertD】${this.pipeline.title} 执行成功,buildId:${this.runtime.id}`;
+ subject = `【CertD】执行成功,${this.pipeline.title}, buildId:${this.runtime.id}`;
+ content = subject;
} else if (when === "error") {
- subject = `【CertD】${this.pipeline.title} 执行失败,buildId:${this.runtime.id}`;
+ subject = `【CertD】执行失败,${this.pipeline.title}, buildId:${this.runtime.id}`;
content = `
${error.message}`;
} else {
return;
@@ -234,6 +234,7 @@ export class Executor {
}
if (notification.type === "email") {
this.options.emailService?.send({
+ userId: this.pipeline.userId,
subject,
content,
receivers: notification.options.receivers,
diff --git a/packages/core/pipeline/src/service/email.ts b/packages/core/pipeline/src/service/email.ts
new file mode 100644
index 000000000..e918e2ab8
--- /dev/null
+++ b/packages/core/pipeline/src/service/email.ts
@@ -0,0 +1,10 @@
+export type EmailSend = {
+ userId: number;
+ subject: string;
+ content: string;
+ receivers: string[];
+};
+
+export interface IEmailService {
+ send(email: EmailSend): Promise;
+}
diff --git a/packages/core/pipeline/src/service/index.ts b/packages/core/pipeline/src/service/index.ts
index 871a185bc..e1722e10c 100644
--- a/packages/core/pipeline/src/service/index.ts
+++ b/packages/core/pipeline/src/service/index.ts
@@ -1,9 +1 @@
-export type EmailSend = {
- subject: string;
- content: string;
- receivers: string[];
-};
-
-export interface IEmailService {
- send(email: EmailSend): Promise;
-}
+export * from "./email";
diff --git a/packages/ui/certd-client/.env b/packages/ui/certd-client/.env
index 4933b20fc..c0541f2a7 100644
--- a/packages/ui/certd-client/.env
+++ b/packages/ui/certd-client/.env
@@ -1,3 +1,4 @@
VITE_APP_API=/api
#登录与权限关闭
VITE_APP_PM_ENABLED=true
+VITE_APP_TITLE=Certd
diff --git a/packages/ui/certd-client/index.html b/packages/ui/certd-client/index.html
index aac3b8bfe..38affcf48 100644
--- a/packages/ui/certd-client/index.html
+++ b/packages/ui/certd-client/index.html
@@ -4,7 +4,7 @@
- antdv-fast-crud
+ Certd-让你的证书永不过期
diff --git a/packages/ui/certd-client/src/api/service.ts b/packages/ui/certd-client/src/api/service.ts
index 41b089038..c5601d8b1 100644
--- a/packages/ui/certd-client/src/api/service.ts
+++ b/packages/ui/certd-client/src/api/service.ts
@@ -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")
diff --git a/packages/ui/certd-client/src/api/tools.ts b/packages/ui/certd-client/src/api/tools.ts
index 80fa784bf..905c1bb18 100644
--- a/packages/ui/certd-client/src/api/tools.ts
+++ b/packages/ui/certd-client/src/api/tools.ts
@@ -48,7 +48,10 @@ 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);
// 显示提示
@@ -59,8 +62,6 @@ 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) {
+ throw new Error(msg);
}
diff --git a/packages/ui/certd-client/src/router/source/modules/certd.ts b/packages/ui/certd-client/src/router/source/modules/certd.ts
index 2ece20295..8dcf62106 100644
--- a/packages/ui/certd-client/src/router/source/modules/certd.ts
+++ b/packages/ui/certd-client/src/router/source/modules/certd.ts
@@ -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",
+ auth: true
+ },
+ children: [
+ {
+ title: "邮箱设置",
+ name: "email",
+ path: "/certd/settings/email",
+ component: "/certd/settings/email-setting.vue",
+ meta: {
+ icon: "ion:mail-outline",
+ auth: true
+ }
+ }
+ ]
}
]
}
diff --git a/packages/ui/certd-client/src/utils/util.env.ts b/packages/ui/certd-client/src/utils/util.env.ts
index 002ffd526..0a33f55a8 100644
--- a/packages/ui/certd-client/src/utils/util.env.ts
+++ b/packages/ui/certd-client/src/utils/util.env.ts
@@ -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() {
diff --git a/packages/ui/certd-client/src/utils/util.site.ts b/packages/ui/certd-client/src/utils/util.site.ts
index 6305d396c..593e8f7b0 100644
--- a/packages/ui/certd-client/src/utils/util.site.ts
+++ b/packages/ui/certd-client/src/utils/util.site.ts
@@ -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}` : ""}`;
}
diff --git a/packages/ui/certd-client/src/views/certd/pipeline/api.history.ts b/packages/ui/certd-client/src/views/certd/pipeline/api.history.ts
index 9cfc54d63..7e7c2086e 100644
--- a/packages/ui/certd-client/src/views/certd/pipeline/api.history.ts
+++ b/packages/ui/certd-client/src/views/certd/pipeline/api.history.ts
@@ -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 {
+export async function GetDetail(query: any): Promise {
const detail = await request({
url: apiPrefix + "/detail",
method: "post",
diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/notification-form/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/notification-form/index.vue
new file mode 100644
index 000000000..5a0146e65
--- /dev/null
+++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/notification-form/index.vue
@@ -0,0 +1,200 @@
+
+
+
+ 编辑触发器
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 确定
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/notification-form/pi-notification-form-email.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/notification-form/pi-notification-form-email.vue
new file mode 100644
index 000000000..6fd0584b7
--- /dev/null
+++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/notification-form/pi-notification-form-email.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue
index 832ff8a74..849bce6f6 100644
--- a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue
+++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/index.vue
@@ -2,7 +2,6 @@
@@ -63,7 +62,7 @@
-
+
+
+
+
+
+ 【通知】 {{ item.type }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 【通知】 {{ item.type }}
+
+
+
@@ -140,6 +181,7 @@
+
@@ -148,9 +190,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 +202,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 +312,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 +412,13 @@ export default defineComponent({
pipeline.value.stages.splice(stageIndex, 0, stage);
});
};
+
+ function isLastStage(index: number) {
+ return !props.editMode && index === pipeline.value.stages.length - 1 && pipeline.value.notifications?.length < 1;
+ }
return {
- stageAdd
+ stageAdd,
+ isLastStage
};
}
@@ -406,6 +454,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 +567,7 @@ export default defineComponent({
historyCancel
};
}
+
const useTaskRet = useTask();
const useStageRet = useStage(useTaskRet);
@@ -495,7 +579,8 @@ export default defineComponent({
...useStageRet,
...useTrigger(),
...useActions(),
- ...useHistory()
+ ...useHistory(),
+ ...useNotification()
};
}
});
diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/plugin/index.ts b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/plugin/index.ts
index 07efe368c..94c4ea561 100644
--- a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/plugin/index.ts
+++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/plugin/index.ts
@@ -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++) {
diff --git a/packages/ui/certd-client/src/views/certd/settings/api.email.ts b/packages/ui/certd-client/src/views/certd/settings/api.email.ts
new file mode 100644
index 000000000..ffda61c88
--- /dev/null
+++ b/packages/ui/certd-client/src/views/certd/settings/api.email.ts
@@ -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
+ }
+ });
+}
diff --git a/packages/ui/certd-client/src/views/certd/settings/api.ts b/packages/ui/certd-client/src/views/certd/settings/api.ts
new file mode 100644
index 000000000..f56f52535
--- /dev/null
+++ b/packages/ui/certd-client/src/views/certd/settings/api.ts
@@ -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)
+ }
+ });
+}
diff --git a/packages/ui/certd-client/src/views/certd/settings/email-setting.vue b/packages/ui/certd-client/src/views/certd/settings/email-setting.vue
new file mode 100644
index 000000000..081463b0b
--- /dev/null
+++ b/packages/ui/certd-client/src/views/certd/settings/email-setting.vue
@@ -0,0 +1,128 @@
+
+
+
+ 邮件设置
+
+
+
+
+
+
+
+
diff --git a/packages/ui/certd-server/db/migration/v10002__settings.sql b/packages/ui/certd-server/db/migration/v10002__settings.sql
new file mode 100644
index 000000000..65790a949
--- /dev/null
+++ b/packages/ui/certd-server/db/migration/v10002__settings.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "sys_settings" (
+ "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
+ "user_id" integer NOT NULL,
+ "key" varchar(100) NOT NULL,
+ "title" varchar(100) NOT NULL,
+ "setting" varchar(1024),
+ "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
+ "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
+);
diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json
index 3081bfac6..d77565518 100644
--- a/packages/ui/certd-server/package.json
+++ b/packages/ui/certd-server/package.json
@@ -9,7 +9,7 @@
"online:preview": "NODE_ENV=preview node ./bootstrap.js",
"dev": "cross-env NODE_ENV=local midway-bin dev --ts --watchFile='../../core/pipeline/src,../../plugins/'",
"dev:preview": "cross-env NODE_ENV=preview midway-bin dev --ts",
- "dev:syncdb": "cross-env NODE_ENV=syncdb midway-bin dev --ts --watchFile='../../core/pipeline/src'",
+ "db": "cross-env NODE_ENV=syncdb midway-bin dev --ts",
"test": "midway-bin test --ts",
"cov": "midway-bin cov --ts",
"lint": "mwts check",
@@ -54,6 +54,7 @@
"md5": "^2.3.0",
"midway-flyway-js": "^3.0.0",
"node-cron": "^3.0.2",
+ "nodemailer": "^6.9.3",
"reflect-metadata": "^0.1.13",
"sqlite3": "^5.1.4",
"svg-captcha": "^1.4.0",
@@ -67,6 +68,7 @@
"@types/jest": "^26.0.24",
"@types/koa": "2.13.4",
"@types/node": "^14.18.35",
+ "@types/nodemailer": "^6.4.8",
"cross-env": "^6.0.3",
"jest": "^26.6.3",
"mwts": "^1.3.0",
diff --git a/packages/ui/certd-server/src/basic/base-controller.ts b/packages/ui/certd-server/src/basic/base-controller.ts
index 38a5d76b2..31c205931 100644
--- a/packages/ui/certd-server/src/basic/base-controller.ts
+++ b/packages/ui/certd-server/src/basic/base-controller.ts
@@ -10,7 +10,7 @@ export abstract class BaseController {
* 成功返回
* @param data 返回数据
*/
- ok(data) {
+ ok(data: any) {
const res = {
...Constants.res.success,
data: undefined,
@@ -22,12 +22,21 @@ export abstract class BaseController {
}
/**
* 失败返回
- * @param message
+ * @param msg
+ * @param code
*/
- fail(msg, code) {
+ fail(msg: string, code: any) {
return {
code: code ? code : Constants.res.error.code,
msg: msg ? msg : Constants.res.error.code,
};
}
+
+ getUserId() {
+ const userId = this.ctx.user?.id;
+ if (userId == null) {
+ throw new Error('Token已过期');
+ }
+ return userId;
+ }
}
diff --git a/packages/ui/certd-server/src/basic/base-service.ts b/packages/ui/certd-server/src/basic/base-service.ts
index 0976b502c..889aca681 100644
--- a/packages/ui/certd-server/src/basic/base-service.ts
+++ b/packages/ui/certd-server/src/basic/base-service.ts
@@ -187,14 +187,19 @@ export abstract class BaseService {
return await qb.getMany();
}
- async checkUserId(id = 0, userId, userKey = 'userId') {
+ async checkUserId(
+ id: any = 0,
+ userId,
+ userKey = 'userId',
+ queryIdKey = 'id'
+ ) {
// @ts-ignore
const res = await this.getRepository().findOne({
// @ts-ignore
select: { [userKey]: true },
+ // @ts-ignore
where: {
- // @ts-ignore
- id,
+ [queryIdKey]: id,
},
});
// @ts-ignore
diff --git a/packages/ui/certd-server/src/middleware/global-exception.ts b/packages/ui/certd-server/src/middleware/global-exception.ts
index 5c842febc..ffb7ff7a5 100644
--- a/packages/ui/certd-server/src/middleware/global-exception.ts
+++ b/packages/ui/certd-server/src/middleware/global-exception.ts
@@ -1,9 +1,5 @@
import { Provide } from '@midwayjs/decorator';
-import {
- IWebMiddleware,
- IMidwayKoaContext,
- NextFunction,
-} from '@midwayjs/koa';
+import { IWebMiddleware, IMidwayKoaContext, NextFunction } from '@midwayjs/koa';
import { logger } from '../utils/logger';
import { Result } from '../basic/result';
@@ -20,7 +16,10 @@ export class GlobalExceptionMiddleware implements IWebMiddleware {
} catch (err) {
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
ctx.status = 200;
- ctx.body = Result.error(err.code != null ? err.code : 1, err.message);
+ if (err.code == null || typeof err.code !== 'number') {
+ err.code = 1;
+ }
+ ctx.body = Result.error(err.code, err.message);
}
};
}
diff --git a/packages/ui/certd-server/src/modules/basic/controller/basic-controller.ts b/packages/ui/certd-server/src/modules/basic/controller/code-controller.ts
similarity index 75%
rename from packages/ui/certd-server/src/modules/basic/controller/basic-controller.ts
rename to packages/ui/certd-server/src/modules/basic/controller/code-controller.ts
index bd2eff18f..5c526ae34 100644
--- a/packages/ui/certd-server/src/modules/basic/controller/basic-controller.ts
+++ b/packages/ui/certd-server/src/modules/basic/controller/code-controller.ts
@@ -1,9 +1,10 @@
-import { Rule,RuleType } from '@midwayjs/validate';
+import { Rule, RuleType } from '@midwayjs/validate';
import { ALL, Inject } from '@midwayjs/decorator';
import { Body } from '@midwayjs/decorator';
import { Controller, Post, Provide } from '@midwayjs/decorator';
import { BaseController } from '../../../basic/base-controller';
import { CodeService } from '../service/code-service';
+import { EmailService } from '../service/email-service';
export class SmsCodeReq {
@Rule(RuleType.number().required())
phoneCode: number;
@@ -18,22 +19,17 @@ export class SmsCodeReq {
imgCode: string;
}
-// const enumsMap = {};
-// glob('src/modules/**/enums/*.ts', {}, (err, matches) => {
-// console.log('matched', matches);
-// for (const filePath of matches) {
-// const module = require('/' + filePath);
-// console.log('modules', module);
-// }
-// });
-
/**
*/
@Provide()
-@Controller('/api/basic')
+@Controller('/api/basic/code')
export class BasicController extends BaseController {
@Inject()
codeService: CodeService;
+
+ @Inject()
+ emailService: EmailService;
+
@Post('/sendSmsCode')
public sendSmsCode(
@Body(ALL)
@@ -53,4 +49,3 @@ export class BasicController extends BaseController {
return this.ok(captcha.data);
}
}
-
diff --git a/packages/ui/certd-server/src/modules/basic/controller/email-controller.ts b/packages/ui/certd-server/src/modules/basic/controller/email-controller.ts
new file mode 100644
index 000000000..49171d7e8
--- /dev/null
+++ b/packages/ui/certd-server/src/modules/basic/controller/email-controller.ts
@@ -0,0 +1,22 @@
+import { Body, Controller, Inject, Post, Provide } from '@midwayjs/decorator';
+import { BaseController } from '../../../basic/base-controller';
+import { EmailService } from '../service/email-service';
+
+/**
+ */
+@Provide()
+@Controller('/api/basic/email')
+export class EmailController extends BaseController {
+ @Inject()
+ emailService: EmailService;
+
+ @Post('/test')
+ public async test(
+ @Body('receiver')
+ receiver
+ ) {
+ const userId = super.getUserId();
+ await this.emailService.test(userId, receiver);
+ return this.ok({});
+ }
+}
diff --git a/packages/ui/certd-server/src/modules/basic/service/email-service.ts b/packages/ui/certd-server/src/modules/basic/service/email-service.ts
new file mode 100644
index 000000000..bcef8e29a
--- /dev/null
+++ b/packages/ui/certd-server/src/modules/basic/service/email-service.ts
@@ -0,0 +1,60 @@
+import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';
+import type { EmailSend } from '@certd/pipeline';
+import { IEmailService } from '@certd/pipeline';
+import nodemailer from 'nodemailer';
+import { SettingsService } from '../../system/service/settings-service';
+import type SMTPConnection from 'nodemailer/lib/smtp-connection';
+
+export type EmailConfig = {
+ host: string;
+ port: number;
+ auth: {
+ user: string;
+ pass: string;
+ };
+ secure: boolean; // use TLS
+ tls: {
+ // do not fail on invalid certs
+ rejectUnauthorized: boolean;
+ };
+ sender: string;
+} & SMTPConnection.Options;
+@Provide()
+@Scope(ScopeEnum.Singleton)
+export class EmailService implements IEmailService {
+ @Inject()
+ settingsService: SettingsService;
+
+ /**
+ */
+ async send(email: EmailSend) {
+ console.log('sendEmail', email);
+
+ const emailConfigEntity = await this.settingsService.getByKey(
+ 'email',
+ email.userId
+ );
+ if (emailConfigEntity == null || !emailConfigEntity.setting) {
+ throw new Error('email settings 未设置');
+ }
+ const emailConfig = JSON.parse(emailConfigEntity.setting) as EmailConfig;
+ const transporter = nodemailer.createTransport(emailConfig);
+ const mailOptions = {
+ from: emailConfig.sender,
+ to: email.receivers.join(', '), // list of receivers
+ subject: email.subject,
+ text: email.content,
+ };
+ await transporter.sendMail(mailOptions);
+ console.log('sendEmail success', email);
+ }
+
+ async test(userId: number, receiver: string) {
+ await this.send({
+ userId,
+ receivers: [receiver],
+ subject: '测试邮件,from certd',
+ content: '测试邮件,from certd',
+ });
+ }
+}
diff --git a/packages/ui/certd-server/src/modules/pipeline/auto/auto-register-cron.ts b/packages/ui/certd-server/src/modules/pipeline/auto/auto-register-cron.ts
index 263d67090..b9fdf5223 100644
--- a/packages/ui/certd-server/src/modules/pipeline/auto/auto-register-cron.ts
+++ b/packages/ui/certd-server/src/modules/pipeline/auto/auto-register-cron.ts
@@ -1,4 +1,4 @@
-import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/decorator";
+import { Autoload, Init, Inject, Scope, ScopeEnum } from '@midwayjs/decorator';
import { PipelineService } from '../service/pipeline-service';
import { logger } from '../../../utils/logger';
diff --git a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts
index eb55002b0..8538cbb1f 100644
--- a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts
+++ b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts
@@ -14,6 +14,7 @@ import { HistoryEntity } from '../entity/history';
import { HistoryLogEntity } from '../entity/history-log';
import { HistoryLogService } from './history-log-service';
import { logger } from '../../../utils/logger';
+import { EmailService } from '../../basic/service/email-service';
/**
* 证书申请
@@ -23,7 +24,8 @@ import { logger } from '../../../utils/logger';
export class PipelineService extends BaseService {
@InjectEntityModel(PipelineEntity)
repository: Repository;
-
+ @Inject()
+ emailService: EmailService;
@Inject()
accessService: AccessService;
@Inject()
@@ -191,6 +193,7 @@ export class PipelineService extends BaseService {
onChanged,
accessService: this.accessService,
storage: new DbStorage(userId, this.storageService),
+ emailService: this.emailService,
});
try {
await executor.init();
diff --git a/packages/ui/certd-server/src/modules/system/controller/settings-controller.ts b/packages/ui/certd-server/src/modules/system/controller/settings-controller.ts
index d15901bd1..a70d7511b 100644
--- a/packages/ui/certd-server/src/modules/system/controller/settings-controller.ts
+++ b/packages/ui/certd-server/src/modules/system/controller/settings-controller.ts
@@ -1,6 +1,15 @@
-import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/decorator";
-import { CrudController } from "../../../basic/crud-controller";
-import { SettingsService } from "../service/settings-service";
+import {
+ ALL,
+ Body,
+ Controller,
+ Inject,
+ Post,
+ Provide,
+ Query,
+} from '@midwayjs/decorator';
+import { CrudController } from '../../../basic/crud-controller';
+import { SettingsService } from '../service/settings-service';
+import { SettingsEntity } from '../entity/settings';
/**
*/
@@ -50,4 +59,18 @@ export class SettingsController extends CrudController {
return super.delete(id);
}
+ @Post('/save')
+ async save(@Body(ALL) bean: SettingsEntity) {
+ await this.service.checkUserId(bean.key, this.ctx.user.id, 'userId', 'key');
+ bean.userId = this.ctx.user.id;
+ await this.service.save(bean);
+ return this.ok({});
+ }
+
+ @Post('/get')
+ async get(@Query('key') key: string) {
+ await this.service.checkUserId(key, this.ctx.user.id, 'userId', 'key');
+ const entity = await this.service.getByKey(key, this.ctx.user.id);
+ return this.ok(entity);
+ }
}
diff --git a/packages/ui/certd-server/src/modules/system/entity/settings.ts b/packages/ui/certd-server/src/modules/system/entity/settings.ts
index ff68a002d..70829c94e 100644
--- a/packages/ui/certd-server/src/modules/system/entity/settings.ts
+++ b/packages/ui/certd-server/src/modules/system/entity/settings.ts
@@ -8,8 +8,10 @@ export class SettingsEntity {
id: number;
@Column({ name: 'user_id', comment: '用户id' })
userId: number;
+ @Column({ comment: 'key', length: 100 })
+ key: string;
@Column({ comment: '名称', length: 100 })
- name: string;
+ title: string;
@Column({ name: 'setting', comment: '设置', length: 1024, nullable: true })
setting: string;
diff --git a/packages/ui/certd-server/src/modules/system/service/settings-service.ts b/packages/ui/certd-server/src/modules/system/service/settings-service.ts
index 0aedcaed6..b90c3a422 100644
--- a/packages/ui/certd-server/src/modules/system/service/settings-service.ts
+++ b/packages/ui/certd-server/src/modules/system/service/settings-service.ts
@@ -1,17 +1,15 @@
-import { Provide, Scope, ScopeEnum } from "@midwayjs/decorator";
-import { InjectEntityModel } from "@midwayjs/typeorm";
-import { Repository } from "typeorm";
-import { BaseService } from "../../../basic/base-service";
-import { SettingsEntity } from "../entity/settings";
+import { Provide, Scope, ScopeEnum } from '@midwayjs/decorator';
+import { InjectEntityModel } from '@midwayjs/typeorm';
+import { Repository } from 'typeorm';
+import { BaseService } from '../../../basic/base-service';
+import { SettingsEntity } from '../entity/settings';
/**
* 授权
*/
@Provide()
@Scope(ScopeEnum.Singleton)
-export class SettingsService
- extends BaseService
-{
+export class SettingsService extends BaseService {
@InjectEntityModel(SettingsEntity)
repository: Repository;
@@ -19,8 +17,11 @@ export class SettingsService
return this.repository;
}
- async getById(id: any): Promise {
+ async getById(id: any): Promise {
const entity = await this.info(id);
+ if (!entity) {
+ return null;
+ }
// const access = accessRegistry.get(entity.type);
const setting = JSON.parse(entity.setting);
return {
@@ -29,5 +30,38 @@ export class SettingsService
};
}
+ async getByKey(key: string, userId: number): Promise {
+ if (!key || !userId) {
+ return null;
+ }
+ return await this.repository.findOne({
+ where: {
+ key,
+ userId,
+ },
+ });
+ }
+ async getSettingByKey(key: string, userId: number): Promise {
+ const entity = await this.getByKey(key, userId);
+ if (!entity) {
+ return null;
+ }
+ return JSON.parse(entity.setting);
+ }
+
+ async save(bean: SettingsEntity) {
+ const entity = await this.repository.findOne({
+ where: {
+ key: bean.key,
+ },
+ });
+ if (entity) {
+ entity.setting = bean.setting;
+ await this.repository.save(entity);
+ } else {
+ bean.title = bean.key;
+ await this.repository.save(bean);
+ }
+ }
}