mirror of
https://github.com/whyour/qinglong.git
synced 2025-05-25 16:36: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) {
|
||||
return await this.insertDb({
|
||||
type: AuthDataType.notification,
|
||||
info: { notificationInfo },
|
||||
private async updateNotificationDb(payload: AuthInfo): Promise<any> {
|
||||
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: '通知发送失败,请检查参数' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ export default function (props: any) {
|
|||
<ProLayout
|
||||
selectedKeys={[props.location.pathname]}
|
||||
loading={loading}
|
||||
className={theme.theme === 'vs-dark' ? 'dark' : 'white'}
|
||||
title={
|
||||
<>
|
||||
控制面板
|
||||
|
|
|
@ -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<any[]>([]);
|
||||
const [notificationInfo, setNotificationInfo] = useState<any>();
|
||||
|
||||
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}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="通知设置" key="notification">
|
||||
<NotificationSetting data={notificationInfo} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="登陆日志" key="login">
|
||||
<LoginLog data={loginLogData} />
|
||||
</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: '脚本管理',
|
||||
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