Skip to content

Commit

Permalink
# 优化推送集成
Browse files Browse the repository at this point in the history
  • Loading branch information
vapao committed Nov 13, 2023
1 parent 06d6bb9 commit 2efb1b8
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 104 deletions.
9 changes: 6 additions & 3 deletions spug_api/apps/account/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from libs.mixins import AdminView, View
from libs import JsonParser, Argument, human_datetime, json_response
from libs.utils import get_request_real_ip, generate_random_str
from libs.spug import send_login_wx_code
from libs.push import send_login_code
from apps.account.models import User, Role, History
from apps.setting.utils import AppSetting
from apps.account.utils import verify_password
Expand Down Expand Up @@ -255,9 +255,12 @@ def handle_user_info(handle_response, request, user, captcha):
mfa = AppSetting.get_default('MFA', {'enable': False})
if mfa['enable']:
if not user.wx_token:
return handle_response(error='已启用登录双重认证,但您的账户未配置微信Token,请联系管理员')
return handle_response(error='已启用登录双重认证,但您的账户未配置推送标识,请联系管理员')
spug_push_key = AppSetting.get_default('spug_push_key')
if not spug_push_key:
return handle_response(error='已启用登录双重认证,但系统未配置推送服务,请联系管理员')
code = generate_random_str(6)
send_login_wx_code(user.wx_token, code)
send_login_code(spug_push_key, user.wx_token, code)
cache.set(key, code, 300)
return json_response({'required_mfa': True})

Expand Down
21 changes: 6 additions & 15 deletions spug_api/apps/setting/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
from libs import JsonParser, Argument, json_response, auth
from libs.utils import generate_random_str
from libs.mail import Mail
from libs.spug import send_login_wx_code
from libs.push import get_balance
from libs.push import get_balance, send_login_code
from libs.mixins import AdminView
from apps.setting.utils import AppSetting
from apps.setting.models import Setting, KEYS_DEFAULT
Expand Down Expand Up @@ -41,9 +40,12 @@ class MFAView(AdminView):
def get(self, request):
if not request.user.wx_token:
return json_response(
error='检测到当前账户未配置微信Token,请配置后再尝试启用MFA认证,否则可能造成系统无法正常登录。')
error='检测到当前账户未配置推送标识(账户管理/编辑),请配置后再尝试启用MFA认证,否则可能造成系统无法正常登录。')
spug_push_key = AppSetting.get_default('spug_push_key')
if not spug_push_key:
return json_response(error='检测到当前账户未绑定推送服务,请在系统设置/推送服务设置中绑定推送助手账户。')
code = generate_random_str(6)
send_login_wx_code(request.user.wx_token, code)
send_login_code(spug_push_key, request.user.wx_token, code)
cache.set(f'{request.user.username}:code', code, 300)
return json_response()

Expand Down Expand Up @@ -107,17 +109,6 @@ def email_test(request):
return json_response(error=error)


@auth('admin')
def mfa_test(request):
if not request.user.wx_token:
return json_response(
error='检测到当前账户未配置微信Token,请配置后再尝试启用MFA认证,否则可能造成系统无法正常登录。')
code = generate_random_str(6)
send_login_wx_code(request.user.wx_token, code)
cache.set(f'{request.user.username}:code', code, 300)
return json_response()


@auth('admin')
def get_about(request):
return json_response({
Expand Down
19 changes: 19 additions & 0 deletions spug_api/libs/push.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
# Copyright: (c) <[email protected]>
# Released under the AGPL-3.0 License.
from apps.setting.utils import AppSetting
import requests

push_server = 'https://push.spug.cc'
Expand All @@ -24,3 +25,21 @@ def get_contacts(token):
return res['data']
except Exception:
return []


def send_login_code(token, user, code):
url = f'{push_server}/spug/message/'
data = {
'token': token,
'targets': [user],
'source': 'mfa',
'dataset': {
'code': code
}
}
res = requests.post(url, json=data, timeout=15)
if res.status_code != 200:
raise Exception(f'status code: {res.status_code}')
res = res.json()
if res.get('error'):
raise Exception(res['error'])
68 changes: 8 additions & 60 deletions spug_api/libs/spug.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,9 @@
from libs.mail import Mail
from libs.utils import human_datetime
from libs.push import push_server
from functools import partial
import requests
import json

spug_server = 'https://api.spug.cc'
notify_source = 'monitor'
make_no_spug_key_notify = partial(
Notify.make_monitor_notify,
'发送报警信息失败',
'未配置报警服务调用凭据,请在系统管理/系统设置/基本设置/调用凭据中配置。'
)
make_no_push_key_notify = partial(
Notify.make_monitor_notify,
'发送报警信息失败',
'未绑定推送服务,请在系统管理/系统设置/推送服务设置中绑定推送助手账户。'
)


def send_login_wx_code(wx_token, code):
url = f'{spug_server}/apis/login/wx/'
spug_key = AppSetting.get_default('spug_key')
res = requests.post(url, json={'token': spug_key, 'user': wx_token, 'code': code}, timeout=30)
if res.status_code != 200:
raise Exception(f'status code: {res.status_code}')
res = res.json()
if res.get('error'):
raise Exception(res['error'])


class Notification:
def __init__(self, grp, event, target, title, message, duration):
Expand All @@ -44,7 +19,6 @@ def __init__(self, grp, event, target, title, message, duration):
self.target = target
self.message = message
self.duration = duration
self.spug_key = AppSetting.get_default('spug_key')
self.spug_push_key = AppSetting.get_default('spug_push_key')

@staticmethod
Expand Down Expand Up @@ -72,20 +46,6 @@ def handle_request(url, data, mode=None):
raise NotImplementedError
Notify.make_system_notify('通知发送失败', f'返回数据:{res}')

def monitor_by_wx(self, users):
if not self.spug_key:
make_no_spug_key_notify()
return
data = {
'token': self.spug_key,
'event': self.event,
'subject': f'{self.title} >> {self.target}',
'desc': self.message,
'remark': f'故障持续{self.duration}' if self.event == '2' else None,
'users': list(users)
}
self.handle_request(f'{spug_server}/apis/notify/wx/', data, 'spug')

def monitor_by_email(self, users):
mail_service = AppSetting.get_default('mail_service', {})
body = [
Expand All @@ -101,17 +61,11 @@ def monitor_by_email(self, users):
subject = f'{event_map[self.event]}-{self.title}'
mail = Mail(**mail_service)
mail.send_text_mail(users, subject, '\r\n'.join(body) + '\r\n\r\n自动发送,请勿回复。')
elif self.spug_key:
data = {
'token': self.spug_key,
'event': self.event,
'subject': self.title,
'body': '\r\n'.join(body),
'users': list(users)
}
self.handle_request(f'{spug_server}/apis/notify/mail/', data, 'spug')
else:
make_no_spug_key_notify()
Notify.make_monitor_notify(
'发送报警信息失败',
'未配置报警服务,请在系统管理/系统设置/报警服务设置中配置邮件服务。'
)

def monitor_by_dd(self, users):
texts = [
Expand Down Expand Up @@ -158,7 +112,10 @@ def monitor_by_qy_wx(self, users):

def monitor_by_spug_push(self, targets):
if not self.spug_push_key:
make_no_push_key_notify()
Notify.make_monitor_notify(
'发送报警信息失败',
'未绑定推送服务,请在系统管理/系统设置/推送服务设置中绑定推送助手账户。'
)
return
data = {
'source': 'monitor',
Expand Down Expand Up @@ -188,15 +145,6 @@ def dispatch_monitor(self, modes):
if mode == '1':
wx_mp_ids = set(x for x in push_ids if x.startswith('wx_mp_'))
targets.update(wx_mp_ids)
users = set(x.wx_token for x in Contact.objects.filter(id__in=u_ids, wx_token__isnull=False))
if not users:
if not wx_mp_ids:
Notify.make_monitor_notify(
'发送报警信息失败',
'未找到可用的通知对象,请确保设置了相关报警联系人的微信Token。'
)
continue
self.monitor_by_wx(users)
elif mode == '2':
sms_ids = set(x for x in push_ids if x.startswith('sms_'))
targets.update(sms_ids)
Expand Down
30 changes: 21 additions & 9 deletions spug_web/src/pages/system/account/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@
* Copyright (c) <[email protected]>
* Released under the AGPL-3.0 License.
*/
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { observer } from 'mobx-react';
import { Modal, Form, Select, Input, message } from 'antd';
import http from 'libs/http';
import React, {useState, useEffect} from 'react';
import {Link} from 'react-router-dom';
import {observer} from 'mobx-react';
import {Modal, Form, Select, Input, message} from 'antd';
import {http, includes} from 'libs';
import store from './store';
import rStore from '../role/store';


export default observer(function () {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [contacts, setContacts] = useState([])

useEffect(() => {
http.get('/api/alarm/contact/?only_push=1')
.then(res => setContacts(res))
}, []);

function handleSubmit() {
setLoading(true);
Expand Down Expand Up @@ -44,11 +50,13 @@ export default observer(function () {
<Form.Item required name="nickname" label="姓名">
<Input placeholder="请输入姓名"/>
</Form.Item>
<Form.Item required hidden={store.record.id} name="password" label="密码" extra="至少8位包含数字、小写和大写字母。">
<Form.Item required hidden={store.record.id} name="password" label="密码"
extra="至少8位包含数字、小写和大写字母。">
<Input.Password placeholder="请输入密码"/>
</Form.Item>
<Form.Item hidden={store.record.is_supper} label="角色" style={{marginBottom: 0}}>
<Form.Item name="role_ids" style={{display: 'inline-block', width: '80%'}} extra="权限最大化原则,组合多个角色权限。">
<Form.Item name="role_ids" style={{display: 'inline-block', width: '80%'}}
extra="权限最大化原则,组合多个角色权限。">
<Select mode="multiple" placeholder="请选择">
{rStore.records.map(item => (
<Select.Option value={item.id} key={item.id}>{item.name}</Select.Option>
Expand All @@ -61,13 +69,17 @@ export default observer(function () {
</Form.Item>
<Form.Item
name="wx_token"
label="微信Token"
label="推送标识"
extra={(
<span>
如果启用了MFA(两步验证)则该项为必填。
<a target="_blank" rel="noopener noreferrer" href="https://spug.cc/docs/wx-token/">什么是微信Token?</a>
</span>)}>
<Input placeholder="请输入微信Token"/>
<Select showSearch filterOption={(i, o) => includes(o.children, i)} placeholder="请选择绑定推送标识">
{contacts.map(item => (
<Select.Option value={item.id} key={item.id}>{item.name}</Select.Option>
))}
</Select>
</Form.Item>
</Form>
</Modal>
Expand Down
32 changes: 15 additions & 17 deletions spug_web/src/pages/system/setting/SecuritySetting.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@
* Copyright (c) <[email protected]>
* Released under the AGPL-3.0 License.
*/
import React, { useState, useEffect } from 'react';
import { observer } from 'mobx-react';
import { Form, Switch, Input, Space, message, Button } from 'antd';
import React, {useState, useEffect} from 'react';
import {observer} from 'mobx-react';
import {Form, Switch, Input, Space, Spin, message, Button} from 'antd';
import styles from './index.module.css';
import http from 'libs/http';
import store from './store';

export default observer(function () {
const [verify_ip, setVerifyIP] = useState(store.settings.verify_ip);
const [bind_ip, setBindIP] = useState(store.settings.bind_ip);
const [mfa, setMFA] = useState(store.settings.MFA || {});
const [code, setCode] = useState();
const [visible, setVisible] = useState(false);
const [counter, setCounter] = useState(0);
Expand All @@ -29,25 +26,25 @@ export default observer(function () {
}, [counter])

function handleChangeVerifyIP(v) {
setVerifyIP(v);
store.isFetching = true;
http.post('/api/setting/', {data: [{key: 'verify_ip', value: v}]})
.then(() => {
message.success('设置成功');
store.fetchSettings()
})
}, () => store.isFetching = false)
}

function handleChangeBindIP(v) {
setBindIP(v);
store.isFetching = true;
http.post('/api/setting/', {data: [{key: 'bind_ip', value: v}]})
.then(() => {
message.success('设置成功');
store.fetchSettings()
})
}, () => store.isFetching = false)
}

function handleChangeMFA(v) {
if (v && !store.settings.spug_key) return message.error('开启MFA认证需要先在基本设置中配置调用凭据');
if (v && !store.settings.spug_push_key) return message.error('开启MFA认证需要先在推送服务设置中绑定推送助手账户');
v ? setVisible(true) : handleMFAModify(false)
}

Expand All @@ -62,16 +59,16 @@ export default observer(function () {
setLoading2(true)
http.post('/api/setting/mfa/', {enable: v, code})
.then(() => {
setMFA({enable: v});
setVisible(false);
message.success('设置成功');
store.fetchSettings()
})
.finally(() => setLoading2(false))
}

const {verify_ip, bind_ip, MFA} = store.settings;
return (
<React.Fragment>
<Spin spinning={store.isFetching}>
<div className={styles.title}>安全设置</div>
<Form layout="vertical" style={{maxWidth: 500}}>
<Form.Item
Expand All @@ -98,8 +95,9 @@ export default observer(function () {
label="登录MFA(两步)认证"
style={{marginTop: 24}}
extra={visible ? '输入验证码,通过验证后开启。' :
<span>建议开启,登录时额外使用验证码进行身份验证。开启前至少要确保管理员账户配置了微信Token(账户管理/编辑),开启后未配置微信Token的账户将无法登录,<a
target="_blank" rel="noopener noreferrer" href="https://spug.cc/docs/wx-token/">什么是微信Token?</a></span>}>
<span>建议开启,登录时额外使用验证码进行身份验证。开启前至少要确保管理员账户配置了推送标识(账户管理/编辑),开启后未配置的账户将无法登录,<a
target="_blank" rel="noopener noreferrer"
href="https://spug.cc/docs/wx-token/">什么是微信Token?</a></span>}>
{visible ? (
<div style={{display: 'flex', width: 490}}>
<Form.Item noStyle extra="验证通过后开启MFA(两步验证)。">
Expand All @@ -120,10 +118,10 @@ export default observer(function () {
checkedChildren="开启"
unCheckedChildren="关闭"
onChange={handleChangeMFA}
checked={mfa.enable}/>
checked={MFA?.enable}/>
)}
</Form.Item>
</Form>
</React.Fragment>
</Spin>
)
})

0 comments on commit 2efb1b8

Please sign in to comment.