mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-26 00:46:07 +08:00
添加通知设置页面
This commit is contained in:
parent
aed08b0f59
commit
827e8571aa
|
@ -268,10 +268,38 @@ export default class AuthService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateNotificationMode(notificationInfo: NotificationInfo) {
|
private async updateNotificationDb(payload: AuthInfo): Promise<any> {
|
||||||
return await this.insertDb({
|
return new Promise((resolve) => {
|
||||||
type: AuthDataType.notification,
|
this.authDb.update(
|
||||||
info: { notificationInfo },
|
{ 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: '通知发送失败,请检查参数' };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default class CronService {
|
||||||
private cronDb = new DataStore({ filename: config.cronDbFile });
|
private cronDb = new DataStore({ filename: config.cronDbFile });
|
||||||
|
|
||||||
private queue = new PQueue({
|
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) {
|
constructor(@Inject('logger') private logger: winston.Logger) {
|
||||||
|
|
|
@ -39,7 +39,26 @@ export default class NotificationService {
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.params = rest;
|
this.params = rest;
|
||||||
const notificationModeAction = this.modeMap.get(type);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -230,3 +230,14 @@ input:-webkit-autofill:active {
|
||||||
padding: 0 3px;
|
padding: 0 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-form-item-extra {
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
::placeholder {
|
||||||
|
opacity: 0.5 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -93,6 +93,7 @@ export default function (props: any) {
|
||||||
<ProLayout
|
<ProLayout
|
||||||
selectedKeys={[props.location.pathname]}
|
selectedKeys={[props.location.pathname]}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
className={theme.theme === 'vs-dark' ? 'dark' : 'white'}
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
控制面板
|
控制面板
|
||||||
|
|
|
@ -30,6 +30,7 @@ import {
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import SecuritySettings from './security';
|
import SecuritySettings from './security';
|
||||||
import LoginLog from './loginLog';
|
import LoginLog from './loginLog';
|
||||||
|
import NotificationSetting from './notification';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
const optionsWithDisabled = [
|
const optionsWithDisabled = [
|
||||||
|
@ -112,6 +113,7 @@ const Setting = ({ headerStyle, isPhone, user, reloadUser }: any) => {
|
||||||
const [editedApp, setEditedApp] = useState();
|
const [editedApp, setEditedApp] = useState();
|
||||||
const [tabActiveKey, setTabActiveKey] = useState('security');
|
const [tabActiveKey, setTabActiveKey] = useState('security');
|
||||||
const [loginLogData, setLoginLogData] = useState<any[]>([]);
|
const [loginLogData, setLoginLogData] = useState<any[]>([]);
|
||||||
|
const [notificationInfo, setNotificationInfo] = useState<any>();
|
||||||
|
|
||||||
const themeChange = (e: any) => {
|
const themeChange = (e: any) => {
|
||||||
setTheme(e.target.value);
|
setTheme(e.target.value);
|
||||||
|
@ -238,9 +240,22 @@ const Setting = ({ headerStyle, isPhone, user, reloadUser }: any) => {
|
||||||
getApps();
|
getApps();
|
||||||
} else if (activeKey === 'login') {
|
} else if (activeKey === 'login') {
|
||||||
getLoginLog();
|
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(() => {
|
useEffect(() => {
|
||||||
setFetchMethod(window.fetch);
|
setFetchMethod(window.fetch);
|
||||||
if (theme === 'dark') {
|
if (theme === 'dark') {
|
||||||
|
@ -289,6 +304,9 @@ const Setting = ({ headerStyle, isPhone, user, reloadUser }: any) => {
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab="通知设置" key="notification">
|
||||||
|
<NotificationSetting data={notificationInfo} />
|
||||||
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab="登陆日志" key="login">
|
<Tabs.TabPane tab="登陆日志" key="login">
|
||||||
<LoginLog data={loginLogData} />
|
<LoginLog data={loginLogData} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
|
83
src/pages/setting/notification.tsx
Normal file
83
src/pages/setting/notification.tsx
Normal file
|
@ -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<string>('');
|
||||||
|
const [fields, setFields] = useState<any[]>([]);
|
||||||
|
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 (
|
||||||
|
<div>
|
||||||
|
<Form onFinish={handleOk} form={form} layout="vertical">
|
||||||
|
<Form.Item
|
||||||
|
label="通知方式"
|
||||||
|
name="type"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
style={{ maxWidth: 400 }}
|
||||||
|
initialValue={notificationMode}
|
||||||
|
>
|
||||||
|
<Select onChange={notificationModeChange}>
|
||||||
|
{config.notificationModes.map((x) => (
|
||||||
|
<Option value={x.value}>{x.label}</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
{fields.map((x) => (
|
||||||
|
<Form.Item
|
||||||
|
label={x.label}
|
||||||
|
name={x.label}
|
||||||
|
extra={x.tip}
|
||||||
|
rules={[{ required: x.required }]}
|
||||||
|
style={{ maxWidth: 400 }}
|
||||||
|
>
|
||||||
|
<Input.TextArea autoSize={true} placeholder={`请输入${x.label}`} />
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
{notificationMode !== '' && (
|
||||||
|
<Button type="primary" htmlType="submit">
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NotificationSetting;
|
|
@ -63,4 +63,116 @@ export default {
|
||||||
scripts: '脚本管理',
|
scripts: '脚本管理',
|
||||||
logs: '任务日志',
|
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 },
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user