From 827e8571aa280710280d1c1a3eb576dc60ebb350 Mon Sep 17 00:00:00 2001 From: hanhh <18330117883@163.com> Date: Sat, 18 Sep 2021 01:44:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=80=9A=E7=9F=A5=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/services/auth.ts | 36 ++++++++-- back/services/cron.ts | 2 +- back/services/notify.ts | 21 +++++- src/layouts/index.less | 11 +++ src/layouts/index.tsx | 1 + src/pages/setting/index.tsx | 18 +++++ src/pages/setting/notification.tsx | 83 +++++++++++++++++++++ src/utils/config.ts | 112 +++++++++++++++++++++++++++++ 8 files changed, 278 insertions(+), 6 deletions(-) create mode 100644 src/pages/setting/notification.tsx diff --git a/back/services/auth.ts b/back/services/auth.ts index bb874942..5fc04897 100644 --- a/back/services/auth.ts +++ b/back/services/auth.ts @@ -268,10 +268,38 @@ export default class AuthService { }); } - public async updateNotificationMode(notificationInfo: NotificationInfo) { - return await this.insertDb({ - type: AuthDataType.notification, - info: { notificationInfo }, + private async updateNotificationDb(payload: AuthInfo): Promise { + return new Promise((resolve) => { + this.authDb.update( + { type: AuthDataType.notification }, + payload, + { upsert: true, returnUpdatedDocs: true }, + (err, num, doc: any) => { + if (err) { + resolve({} as NotificationInfo); + } else { + resolve(doc.info); + } + }, + ); }); } + + public async updateNotificationMode(notificationInfo: NotificationInfo) { + const code = Math.random().toString().slice(-6); + const isSuccess = await this.notificationService.testNotify( + notificationInfo, + '青龙', + `【蛟龙】您本次的验证码:${code}`, + ); + if (isSuccess) { + const result = await this.updateNotificationDb({ + type: AuthDataType.notification, + info: { ...notificationInfo }, + }); + return { code: 200, data: { ...result, code } }; + } else { + return { code: 400, data: '通知发送失败,请检查参数' }; + } + } } diff --git a/back/services/cron.ts b/back/services/cron.ts index 0a2a966f..01b483d2 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -16,7 +16,7 @@ export default class CronService { private cronDb = new DataStore({ filename: config.cronDbFile }); private queue = new PQueue({ - concurrency: parseInt(process.env.MaxConcurrentNum) || 5, + concurrency: parseInt(process.env.MaxConcurrentNum as string) || 5, }); constructor(@Inject('logger') private logger: winston.Logger) { diff --git a/back/services/notify.ts b/back/services/notify.ts index ab18b1af..9acd4d4a 100644 --- a/back/services/notify.ts +++ b/back/services/notify.ts @@ -39,7 +39,26 @@ export default class NotificationService { this.content = content; this.params = rest; const notificationModeAction = this.modeMap.get(type); - notificationModeAction?.call(this); + try { + return await notificationModeAction?.call(this); + } catch (error: any) { + return error.message; + } + } + } + + public async testNotify( + info: NotificationInfo, + title: string, + content: string, + ) { + const { type, ...rest } = info; + if (type) { + this.title = title; + this.content = content; + this.params = rest; + const notificationModeAction = this.modeMap.get(type); + return await notificationModeAction?.call(this); } } diff --git a/src/layouts/index.less b/src/layouts/index.less index 1cb23273..fa5e63e8 100644 --- a/src/layouts/index.less +++ b/src/layouts/index.less @@ -230,3 +230,14 @@ input:-webkit-autofill:active { padding: 0 3px; } } + +.ant-form-item-extra { + word-break: break-all; + font-size: 13px; +} + +.dark { + ::placeholder { + opacity: 0.5 !important; + } +} diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 94a8a3b8..8eb1e9db 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -93,6 +93,7 @@ export default function (props: any) { 控制面板 diff --git a/src/pages/setting/index.tsx b/src/pages/setting/index.tsx index 32dd5f53..a5198cba 100644 --- a/src/pages/setting/index.tsx +++ b/src/pages/setting/index.tsx @@ -30,6 +30,7 @@ import { } from '@ant-design/icons'; import SecuritySettings from './security'; import LoginLog from './loginLog'; +import NotificationSetting from './notification'; const { Text } = Typography; const optionsWithDisabled = [ @@ -112,6 +113,7 @@ const Setting = ({ headerStyle, isPhone, user, reloadUser }: any) => { const [editedApp, setEditedApp] = useState(); const [tabActiveKey, setTabActiveKey] = useState('security'); const [loginLogData, setLoginLogData] = useState([]); + const [notificationInfo, setNotificationInfo] = useState(); const themeChange = (e: any) => { setTheme(e.target.value); @@ -238,9 +240,22 @@ const Setting = ({ headerStyle, isPhone, user, reloadUser }: any) => { getApps(); } else if (activeKey === 'login') { getLoginLog(); + } else if (activeKey === 'notification') { + getNotification(); } }; + const getNotification = () => { + request + .get(`${config.apiPrefix}user/notification`) + .then((data: any) => { + setNotificationInfo(data.data); + }) + .catch((error: any) => { + console.log(error); + }); + }; + useEffect(() => { setFetchMethod(window.fetch); if (theme === 'dark') { @@ -289,6 +304,9 @@ const Setting = ({ headerStyle, isPhone, user, reloadUser }: any) => { loading={loading} /> + + + diff --git a/src/pages/setting/notification.tsx b/src/pages/setting/notification.tsx new file mode 100644 index 00000000..d62dfbb1 --- /dev/null +++ b/src/pages/setting/notification.tsx @@ -0,0 +1,83 @@ +import React, { useEffect, useState } from 'react'; +import { Typography, Input, Form, Button, Select, message } from 'antd'; +import { request } from '@/utils/http'; +import config from '@/utils/config'; + +const { Option } = Select; + +const NotificationSetting = ({ data }: any) => { + const [loading, setLoading] = useState(false); + const [notificationMode, setNotificationMode] = useState(''); + const [fields, setFields] = useState([]); + const [form] = Form.useForm(); + + const handleOk = (values: any) => { + request + .put(`${config.apiPrefix}user/notification`, { + data: { + ...values, + }, + }) + .then((data: any) => { + if (data && data.code === 200) { + message.success('通知发送成功'); + } else { + message.error(data.data); + } + }) + .catch((error: any) => { + console.log(error); + }); + }; + + const notificationModeChange = (value: string) => { + setNotificationMode(value); + const _fields = (config.notificationModeMap as any)[value]; + setFields(_fields); + }; + + useEffect(() => { + if (data && data.type) { + notificationModeChange(data.type); + form.setFieldsValue({ ...data }); + } + }, [data]); + + return ( +
+
+ + + + {fields.map((x) => ( + + + + ))} + {notificationMode !== '' && ( + + )} +
+
+ ); +}; + +export default NotificationSetting; diff --git a/src/utils/config.ts b/src/utils/config.ts index 5bcfece4..085866d0 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -63,4 +63,116 @@ export default { scripts: '脚本管理', logs: '任务日志', }, + notificationModes: [ + { value: 'goCqHttpBot', label: 'GoCqHttpBot' }, + { value: 'serverChan', label: 'Server酱' }, + { value: 'bark', label: 'Bark' }, + { value: 'telegramBot', label: 'Telegram机器人' }, + { value: 'dingtalkBot', label: '钉钉机器人' }, + { value: 'weWorkBot', label: '企业微信机器人' }, + { value: 'weWorkApp', label: '企业微信应用' }, + { value: 'iGot', label: 'IGot' }, + { value: 'pushPlus', label: 'PushPlus' }, + { value: 'email', label: '邮箱' }, + { value: '', label: '已关闭' }, + ], + notificationModeMap: { + goCqHttpBot: [ + { + label: 'goCqHttpBotUrl', + tip: '推送到个人QQ: http://127.0.0.1/send_private_msg,群:http://127.0.0.1/send_group_msg', + required: true, + }, + { label: 'goCqHttpBotToken', tip: '访问密钥', required: true }, + { + label: 'goCqHttpBotQq', + tip: '如果GOBOT_URL设置 /send_private_msg 则需要填入 user_id=个人QQ 相反如果是 /send_group_msg 则需要填入 group_id=QQ群', + required: true, + }, + ], + serverChan: [ + { label: 'serverChanKey', tip: 'Server酱SENDKEY', required: true }, + ], + bark: [ + { + label: 'barkPush', + tip: 'Bark的信息IP/设备码,例如:https://api.day.app/XXXXXXXX', + required: true, + }, + { label: 'barkSound', tip: 'BARK推送铃声,铃声列表去APP查看复制填写' }, + { label: 'barkGroup', tip: 'BARK推送消息的分组, 默认为qinglong' }, + ], + telegramBot: [ + { + label: 'telegramBotToken', + tip: 'telegram机器人的token,例如:1077xxx4424:AAFjv0FcqxxxxxxgEMGfi22B4yh15R5uw', + required: true, + }, + { + label: 'telegramBotUserId', + tip: 'telegram用户的id,例如:129xxx206', + required: true, + }, + { label: 'telegramBotProxyHost', tip: '代理IP' }, + { label: 'telegramBotProxyPort', tip: '代理端口' }, + { label: 'telegramBotProxyAuth', tip: 'telegram代理配置认证参数' }, + { + label: 'telegramBotApiHost', + tip: 'telegram api自建的反向代理地址,默认tg官方api', + }, + ], + dingtalkBot: [ + { + label: 'dingtalkBotToken', + tip: '钉钉机器人webhook token,例如:5a544165465465645d0f31dca676e7bd07415asdasd', + required: true, + }, + { + label: 'dingtalkBotSecret', + tip: '密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的字符串', + }, + ], + weWorkBot: [ + { + label: 'weWorkBotKey', + tip: '企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770),例如:693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa', + required: true, + }, + ], + weWorkApp: [ + { + label: 'weWorkAppKey', + tip: 'corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型) 注意用,号隔开(英文输入法的逗号),例如:wwcfrs,B-76WERQ,qinglong,1000001,2COat', + required: true, + }, + ], + iGot: [ + { + label: 'iGotPushKey', + tip: 'iGot的信息推送key,例如:https://push.hellyw.com/XXXXXXXX', + required: true, + }, + ], + pushPlus: [ + { + label: 'pushPlusToken', + tip: '微信扫码登录后一对一推送或一对多推送下面的token(您的Token),不提供PUSH_PLUS_USER则默认为一对一推送', + required: true, + }, + { + label: 'pushPlusUser', + tip: '一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)', + required: true, + }, + ], + email: [ + { + label: 'emailService', + tip: '邮箱服务名称,比如126、163、Gmail、QQ等,支持列表https://nodemailer.com/smtp/well-known/', + required: true, + }, + { label: 'emailUser', tip: '邮箱地址', required: true }, + { label: 'emailPass', tip: '邮箱SMTP授权码', required: true }, + ], + }, };