# 推送服务设置支持解绑

dependabot/pip/spug_api/paramiko-3.4.0
vapao 2023-11-10 10:30:53 +08:00
parent 3d83dbb117
commit 3d3697e47b
4 changed files with 118 additions and 65 deletions

View File

@ -13,5 +13,6 @@ urlpatterns = [
url(r'^email_test/$', email_test), url(r'^email_test/$', email_test),
url(r'^mfa/$', MFAView.as_view()), url(r'^mfa/$', MFAView.as_view()),
url(r'^about/$', get_about), url(r'^about/$', get_about),
url(r'^balance/$', get_push_balance), url(r'^push/bind/$', handle_push_bind),
url(r'^push/balance/$', handle_push_balance),
] ]

View File

@ -31,6 +31,10 @@ class AppSetting:
else: else:
raise KeyError('invalid key') raise KeyError('invalid key')
@classmethod
def delete(cls, key):
Setting.objects.filter(key=key).delete()
@classmethod @classmethod
def get_ssh_key(cls): def get_ssh_key(cls):
public_key = cls.get_default('public_key') public_key = cls.get_default('public_key')

View File

@ -21,7 +21,10 @@ class SettingView(AdminView):
def get(self, request): def get(self, request):
response = deepcopy(KEYS_DEFAULT) response = deepcopy(KEYS_DEFAULT)
for item in Setting.objects.all(): for item in Setting.objects.all():
response[item.key] = item.real_val if item.key == 'spug_push_key':
response[item.key] = f'{item.real_val[:8]}********{item.real_val[-8:]}'
else:
response[item.key] = item.real_val
return json_response(response) return json_response(response)
def post(self, request): def post(self, request):
@ -126,7 +129,27 @@ def get_about(request):
@auth('admin') @auth('admin')
def get_push_balance(request): def handle_push_bind(request):
form, error = JsonParser(
Argument('spug_push_key', required=False),
).parse(request.body)
if error is None:
if not form.spug_push_key:
AppSetting.delete('spug_push_key')
return json_response()
try:
res = get_balance(form.spug_push_key)
except Exception as e:
return json_response(error=f'绑定失败:{e}')
AppSetting.set('spug_push_key', form.spug_push_key)
return json_response(res)
return json_response(error=error)
@auth('admin')
def handle_push_balance(request):
token = AppSetting.get_default('spug_push_key') token = AppSetting.get_default('spug_push_key')
if not token: if not token:
return json_response(error='请先配置推送服务绑定账户') return json_response(error='请先配置推送服务绑定账户')

View File

@ -14,17 +14,18 @@ import store from './store';
export default observer(function () { export default observer(function () {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [fetching, setFetching] = useState(false); const [fetching, setFetching] = useState(false);
const [balance, setBalance] = useState({}) const [balance, setBalance] = useState({});
const [pushKey, setPushKey] = useState(store.settings.spug_push_key);
useEffect(() => { useEffect(() => {
if (store.settings.spug_push_key) { if (pushKey) {
fetchBalance() fetchBalance()
} }
}, []); }, []);
function fetchBalance() { function fetchBalance() {
setFetching(true) setFetching(true)
http.get('/api/setting/balance/') http.get('/api/setting/push/balance/')
.then(res => setBalance(res)) .then(res => setBalance(res))
.finally(() => { .finally(() => {
setLoading(false) setLoading(false)
@ -33,19 +34,31 @@ export default observer(function () {
} }
function handleBind() { function handleBind() {
const spug_push_key = store.settings.spug_push_key if (!pushKey) return message.error('请输入要绑定的推送助手用户ID')
if (!spug_push_key) return message.error('请输入要绑定的推送助手用户ID')
setLoading(true); setLoading(true);
http.post('/api/setting/', {data: [{key: 'spug_push_key', value: spug_push_key}]}) http.post('/api/setting/push/bind/', {spug_push_key: pushKey})
.then(() => { .then(res => {
message.success('保存成功'); message.success('绑定成功');
store.fetchSettings(); store.fetchSettings();
fetchBalance() setBalance(res)
}) })
.finally(() => store.loading = false) .finally(() => setLoading(false))
} }
const isVip = true function handleUnbind() {
setLoading(true);
http.post('/api/setting/push/bind/', {spug_push_key: ''})
.then(() => {
message.success('解绑成功');
store.fetchSettings();
setBalance({})
setPushKey('')
})
.finally(() => setLoading(false))
}
const isVip = balance.is_vip
const spugPushKey = store.settings.spug_push_key
return ( return (
<React.Fragment> <React.Fragment>
<div className={css.title}>推送服务设置</div> <div className={css.title}>推送服务设置</div>
@ -54,61 +67,73 @@ export default observer(function () {
extra={<div>请登录 <Link href="https://push.spug.cc/login" title="推送助手"/>至个人中心 / extra={<div>请登录 <Link href="https://push.spug.cc/login" title="推送助手"/>至个人中心 /
个人设置查看用户ID注意保密该ID请勿泄漏给第三方</div>}> 个人设置查看用户ID注意保密该ID请勿泄漏给第三方</div>}>
<Input.Group compact> {spugPushKey ? (
<Input <Input.Group compact>
value={store.settings.spug_push_key} <div className={css.keyText}
onChange={e => store.settings.spug_push_key = e.target.value} style={{width: 'calc(100% - 100px)', lineHeight: '32px', fontWeight: 'bold'}}>{spugPushKey}</div>
style={{width: 'calc(100% - 100px)'}} <Button
placeholder="请输入要绑定的推送助手用户ID"/> ghost
<Button type="danger"
type="primary" style={{width: 80, marginLeft: 20}}
style={{width: 80, marginLeft: 20}} onClick={handleUnbind}
onClick={handleBind} loading={loading}>解绑</Button>
loading={loading}>确定</Button> </Input.Group>
</Input.Group> ) : (
{/*<Input.Group compact>*/} <Input.Group compact>
{/* <Input bordered={false} style={{width: 'calc(100% - 100px)', paddingLeft: 0}} value="32uu73******64823d"/>*/} <Input
{/* <Button style={{width: 80, marginLeft: 20}}>解绑</Button>*/} value={pushKey}
{/*</Input.Group>*/} onChange={e => setPushKey(e.target.value)}
style={{width: 'calc(100% - 100px)'}}
placeholder="请输入要绑定的推送助手用户ID"/>
<Button
type="primary"
style={{width: 80, marginLeft: 20}}
onClick={handleBind}
loading={loading}>确定</Button>
</Input.Group>
)}
</Form.Item> </Form.Item>
</div> </div>
<Form.Item style={{marginTop: 24}} {spugPushKey ? (
extra={<div> 如需充值请至 <Link href="https://push.spug.cc/buy/sms" title="推送助手"/>具体计费规则及说明请查看推送助手官网 <Form.Item style={{marginTop: 24}}
</div>}> extra={<div> 如需充值请至 <Link href="https://push.spug.cc/buy/sms" title="推送助手"/>具体计费规则及说明请查看推送助手官网
<div className={css.statistic}> </div>}>
<Spin spinning={fetching}> <div className={css.statistic}>
<div className={css.body}> <Spin spinning={fetching}>
<div className={css.item}> <div className={css.body}>
<div className={css.title}>短信余额</div> <div className={css.item}>
<div className={css.value}>{balance.sms_balance}</div> <div className={css.title}>短信余额</div>
<div className={css.value}>{balance.sms_balance}</div>
</div>
<div className={css.item}>
<div className={css.title}>语音余额</div>
<div className={css.value}>{balance.voice_balance}</div>
</div>
<div className={css.item}>
<div className={css.title}>邮件余额</div>
<div className={css.value}>{balance.mail_balance}</div>
{isVip ? (
<div className={clsNames(css.tips, css.active)}>+ 会员免费20封 / </div>
) : (
<div className={css.tips}>会员免费20封 / </div>
)}
</div>
<div className={css.item}>
<div className={css.title}>微信公众号余额</div>
<div className={css.value}>{balance.wx_mp_balance}</div>
{isVip ? (
<div className={clsNames(css.tips, css.active)}>+ 会员免费100条 / </div>
) : (
<div className={css.tips}>会员免费20封 / </div>
)}
</div>
</div> </div>
<div className={css.item}> </Spin>
<div className={css.title}>语音余额</div> </div>
<div className={css.value}>{balance.voice_balance}</div> </Form.Item>
</div> ) : null}
<div className={css.item}>
<div className={css.title}>邮件余额</div>
<div className={css.value}>{balance.mail_balance}</div>
{isVip ? (
<div className={clsNames(css.tips, css.active)}>+ 会员免费20封 / </div>
) : (
<div className={css.tips}>会员免费20封 / </div>
)}
</div>
<div className={css.item}>
<div className={css.title}>微信公众号余额</div>
<div className={css.value}>{balance.wx_mp_balance}</div>
{isVip ? (
<div className={clsNames(css.tips, css.active)}>+ 会员免费100条 / </div>
) : (
<div className={css.tips}>会员免费20封 / </div>
)}
</div>
</div>
</Spin>
</div>
</Form.Item>
</React.Fragment> </React.Fragment>
) )
}) })