This commit is contained in:
felinae98 2021-10-01 16:17:22 +08:00
parent cdeea09522
commit 5d9872af29
No known key found for this signature in database
GPG Key ID: 00C8B010587FF610
11 changed files with 339 additions and 19 deletions

View File

@ -15,6 +15,7 @@
"@types/react-dom": "^17.0.0", "@types/react-dom": "^17.0.0",
"antd": "^4.16.13", "antd": "^4.16.13",
"axios": "^0.21.4", "axios": "^0.21.4",
"lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-router-dom": "^5.3.0", "react-router-dom": "^5.3.0",
@ -24,7 +25,7 @@
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build && mv build ../src/plugins/nonebot_hk_reporter/admin_page/dist", "build": "react-scripts build && mv -f build ../src/plugins/nonebot_hk_reporter/admin_page/dist",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
@ -47,6 +48,7 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.14.175",
"@types/react-router-dom": "^5.3.0" "@types/react-router-dom": "^5.3.0"
} }
} }

View File

@ -1,5 +1,5 @@
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { HashRouter as Router, Route, Switch } from 'react-router-dom'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.css'; import './App.css';
import { LoginContext, loginContextDefault, GlobalConfContext } from './utils/context'; import { LoginContext, loginContextDefault, GlobalConfContext } from './utils/context';
import { LoginStatus, GlobalConf, AllPlatformConf } from './utils/type'; import { LoginStatus, GlobalConf, AllPlatformConf } from './utils/type';
@ -41,7 +41,7 @@ function App() {
<LoginContext.Provider value={{login: loginStatus, save}}> <LoginContext.Provider value={{login: loginStatus, save}}>
<GlobalConfContext.Provider value={globalConf}> <GlobalConfContext.Provider value={globalConf}>
{ globalConf.loaded && { globalConf.loaded &&
<Router> <Router basename="/hk_reporter">
<Switch> <Switch>
<Route path="/auth/:code"> <Route path="/auth/:code">
<Auth /> <Auth />

View File

@ -1,5 +1,5 @@
import axios from "axios"; import axios from "axios";
import { GlobalConf, TokenResp, SubscribeResp, TargetNameResp } from "../utils/type"; import { GlobalConf, TokenResp, SubscribeResp, TargetNameResp, CreateSubscribeReq } from "../utils/type";
import { baseUrl } from './utils'; import { baseUrl } from './utils';
export async function getGlobalConf(): Promise<GlobalConf> { export async function getGlobalConf(): Promise<GlobalConf> {
@ -21,3 +21,8 @@ export async function getTargetName(platformName: string, target: string): Promi
const res = await axios.get(`${baseUrl}target_name`, {params: {platformName, target}}); const res = await axios.get(`${baseUrl}target_name`, {params: {platformName, target}});
return res.data; return res.data;
} }
export async function addSubscribe(groupNumber: string, req: CreateSubscribeReq) {
const res = await axios.post(`${baseUrl}subs`, req, {params: {groupNumber}})
return res.data;
}

View File

@ -0,0 +1,117 @@
import {Input, Tag, Tooltip} from "antd";
import {PresetColorType, PresetStatusColorType} from 'antd/lib/_util/colors'
import {LiteralUnion} from 'antd/lib/_util/type'
import React, {useRef, useState, useEffect} from "react";
import { PlusOutlined } from '@ant-design/icons';
interface InputTagProp {
value?: Array<string>,
onChange?: (value: Array<string>) => void
color?: LiteralUnion<PresetColorType | PresetStatusColorType, string>;
addText?: string
}
export function InputTag(prop: InputTagProp) {
const [ value, setValue ] = useState<Array<string>>(prop.value || []);
const [ inputVisible, setInputVisible ] = useState(false);
const [ inputValue, setInputValue ] = useState('');
const [ editInputIndex, setEditInputIndex ] = useState(-1);
const [ editInputValue, setEditInputValue ] = useState('');
const inputRef = useRef(null as any);
const editInputRef = useRef(null as any);
useEffect(() => {
if (inputVisible) {
inputRef.current.focus()
}
}, [inputVisible]);
useEffect(() => {
if (editInputIndex !== -1) {
editInputRef.current.focus();
}
}, [editInputIndex]);
const handleClose = (removedTag: string) => {
const tags = value.filter(tag => tag !== removedTag);
setValue(_ => tags);
if (prop.onChange) {
prop.onChange(tags);
}
}
const showInput = () => {
setInputVisible(_ => true);
}
const handleInputConfirm = () => {
if (inputValue && value.indexOf(inputValue) === -1) {
const newVal = [...value, inputValue];
setValue(_ => newVal);
if (prop.onChange) {
prop.onChange(newVal);
}
}
setInputVisible(_ => false);
setInputValue(_ => '');
}
const handleEditInputChange = (e: any) => {
setEditInputValue(_ => e.target.value);
}
const handleEditInputConfirm = () => {
const newTags = value.slice();
newTags[editInputIndex] = editInputValue;
setValue(_ => newTags);
if (prop.onChange) {
prop.onChange(newTags);
}
setEditInputIndex(_ => -1);
setEditInputValue(_ => '');
}
const handleInputChange = (e: any) => {
setInputValue(e.target.value);
}
return (
<>
{ value.map((tag, index) => {
if (editInputIndex === index) {
return (
<Input ref={editInputRef} key={tag} size="small"
value={editInputValue} onChange={handleEditInputChange}
onBlur={handleEditInputConfirm} onPressEnter={handleInputConfirm} />
);
}
const isLongTag = tag.length > 20;
const tagElem = (
<Tag color={prop.color || "default"} style={{userSelect: 'none'}} key={tag} closable onClose={() => handleClose(tag)}>
<span onDoubleClick={e => {
setEditInputIndex(_ => index);
setEditInputValue(_ => tag);
e.preventDefault();
}}>
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
</span>
</Tag>
);
return isLongTag ? (
<Tooltip title={tag} key={tag}>
{tagElem}
</Tooltip>
) : ( tagElem );
})}
{inputVisible && (
<Input ref={inputRef} type="text" size="small"
style={{width: '78px', marginRight: '8px', verticalAlign: 'top'}} value={inputValue}
onChange={handleInputChange} onBlur={handleInputConfirm}
onPressEnter={handleInputConfirm} />
)}
{!inputVisible && (
<Tag className="site-tag-plus" onClick={showInput} style={{background: '#fff', border: 'dashed'}}>
<PlusOutlined/> {prop.addText || "Add Tag"}
</Tag>
)}
</>
);
}

View File

@ -2,9 +2,11 @@ import React, { ReactElement, useContext, useEffect, useState } from "react";
import { LoginContext, GlobalConfContext } from "../utils/context"; import { LoginContext, GlobalConfContext } from "../utils/context";
import { Layout, Menu, Empty, Collapse, Card, Tag, Row, Col, Form, Tooltip, Button, Modal, Select, import { Layout, Menu, Empty, Collapse, Card, Tag, Row, Col, Form, Tooltip, Button, Modal, Select,
Input} from 'antd'; Input} from 'antd';
import { SubscribeConfig, SubscribeResp, PlatformConfig } from '../utils/type'; import { SubscribeConfig, SubscribeResp, PlatformConfig, CategoryConfig } from '../utils/type';
import { SettingOutlined, BugOutlined, DeleteOutlined, CopyOutlined, PlusOutlined } from '@ant-design/icons'; import { SettingOutlined, BugOutlined, DeleteOutlined, CopyOutlined, PlusOutlined } from '@ant-design/icons';
import { getSubscribe, getTargetName } from '../api/config'; import { getSubscribe, getTargetName } from '../api/config';
import { InputTag } from '../component/inputTag';
import _ from "lodash";
import './admin.css'; import './admin.css';
export function Admin() { export function Admin() {
@ -63,7 +65,7 @@ function ConfigPage(prop: ConfigPageProp) {
<Tooltip title="删除"><DeleteOutlined /></Tooltip>, <Tooltip title="删除"><DeleteOutlined /></Tooltip>,
<Tooltip title="添加到其他群"><CopyOutlined /></Tooltip> <Tooltip title="添加到其他群"><CopyOutlined /></Tooltip>
]}> ]}>
<Form labelCol={{ span: 4 }}> <Form labelCol={{ span: 6 }}>
<Form.Item label="订阅类型"> <Form.Item label="订阅类型">
{Object.keys(platformConf.categories).length > 0 ? {Object.keys(platformConf.categories).length > 0 ?
config.cats.map((catKey: number) => (<Tag color="green" key={catKey}>{platformConf.categories[catKey]}</Tag>)) : config.cats.map((catKey: number) => (<Tag color="green" key={catKey}>{platformConf.categories[catKey]}</Tag>)) :
@ -103,6 +105,28 @@ function ConfigPage(prop: ConfigPageProp) {
} }
} }
interface InputTagCustomProp {
value?: Array<string>,
onChange?: (value: Array<string>) => void
}
function InputTagCustom(prop: InputTagCustomProp) {
const [value, setValue] = useState(prop.value || []);
const handleSetValue = (newVal: Array<string>) => {
setValue(() => newVal);
if (prop.onChange) {
prop.onChange(newVal);
}
}
return (
<>
{value.length === 0 &&
<Tag color="green"></Tag>
}
<InputTag color="blue" addText="添加标签" value={value} onChange={handleSetValue} />
</>
)
}
interface AddModalProp { interface AddModalProp {
showModal: boolean, showModal: boolean,
setShowModal: (s: boolean) => void setShowModal: (s: boolean) => void
@ -111,26 +135,39 @@ function AddModal(prop: AddModalProp) {
const [ confirmLoading, setConfirmLoading ] = useState<boolean>(false); const [ confirmLoading, setConfirmLoading ] = useState<boolean>(false);
const { platformConf } = useContext(GlobalConfContext); const { platformConf } = useContext(GlobalConfContext);
const [ hasTarget, setHasTarget ] = useState(false); const [ hasTarget, setHasTarget ] = useState(false);
const [ categories, setCategories ] = useState({} as CategoryConfig);
const [ form ] = Form.useForm(); const [ form ] = Form.useForm();
const changePlatformSelect = (platform: string) => { const changePlatformSelect = (platform: string) => {
setHasTarget(_ => platformConf[platform].hasTarget); setHasTarget(_ => platformConf[platform].hasTarget);
setCategories(_ => platformConf[platform].categories);
if (! platformConf[platform].hasTarget) { if (! platformConf[platform].hasTarget) {
getTargetName(platform, 'default') getTargetName(platform, 'default')
.then(res => { .then(res => {
console.log(res) console.log(res)
form.setFieldsValue(() => { return { form.setFieldsValue({
targetName: res.targetName targetName: res.targetName,
}}) target: ''
}) })
})
} else {
form.setFieldsValue({
targetName: '',
target: ''
})
} }
} }
const handleSubmit = (value: any) => { const handleSubmit = (value: any) => {
console.log(value); console.log(value);
} }
const handleModleFinish = () => {
form.submit();
setConfirmLoading(() => true);
}
return <Modal title="添加订阅" visible={prop.showModal} return <Modal title="添加订阅" visible={prop.showModal}
confirmLoading={confirmLoading} onCancel={() => prop.setShowModal(false)}> confirmLoading={confirmLoading} onCancel={() => prop.setShowModal(false)}
<Form form={form} labelCol={{ span: 6 }} name="b"> onOk={handleModleFinish}>
<Form form={form} labelCol={{ span: 6 }} name="b" onFinish={handleSubmit}>
<Form.Item label="平台" name="platformType" rules={[]}> <Form.Item label="平台" name="platformType" rules={[]}>
<Select style={{ width: '80%' }} onChange={changePlatformSelect}> <Select style={{ width: '80%' }} onChange={changePlatformSelect}>
{Object.keys(platformConf).map(platformName => {Object.keys(platformConf).map(platformName =>
@ -138,11 +175,50 @@ function AddModal(prop: AddModalProp) {
)} )}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item label="账号" name="target" rules={[]}> <Form.Item label="账号" name="target" rules={[
<Input placeholder={hasTarget ? "获取方式见文档" : "此平台不需要账号"} disabled={! hasTarget} style={{ width: "80%" }} /> {required: hasTarget, message: "请输入账号"},
{validator: async (_, value) => {
try {
const res = await getTargetName(form.getFieldValue('platformType'), value);
if (res.targetName) {
form.setFieldsValue({
targetName: res.targetName
})
return Promise.resolve()
} else {
form.setFieldsValue({
targetName: ''
})
return Promise.reject("账号不正确,请重新检查账号")
}
} catch {
return Promise.reject('服务器错误,请稍后再试')
}
}
}
]}>
<Input placeholder={hasTarget ? "获取方式见文档" : "此平台不需要账号"}
disabled={! hasTarget} style={{ width: "80%" }}/>
</Form.Item> </Form.Item>
<Form.Item label="账号名称" name="targetName"> <Form.Item label="账号名称" name="targetName">
<Input style={{ width: "80%" }} /> <Input style={{ width: "80%" }} disabled />
</Form.Item>
<Form.Item label="订阅分类" name="categories">
<Select style={{ width: '80%' }} mode="multiple"
disabled={Object.keys(categories).length === 0}
placeholder={Object.keys(categories).length > 0 ?
"请选择要订阅的分类" : "本平台不支持分类"}>
{Object.keys(categories).length > 0 &&
Object.keys(categories).map((indexStr) =>
<Select.Option key={indexStr} value={parseInt(indexStr)}>
{categories[parseInt(indexStr)]}
</Select.Option>
)
}
</Select>
</Form.Item>
<Form.Item label="订阅Tag" name="tags">
<InputTagCustom/>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>

View File

@ -34,7 +34,7 @@ export interface AllPlatformConf {
[idx: string]: PlatformConfig; [idx: string]: PlatformConfig;
} }
interface CategoryConfig { export interface CategoryConfig {
[idx: number]: string [idx: number]: string
} }
@ -66,3 +66,11 @@ export interface SubscribeResp {
export interface TargetNameResp { export interface TargetNameResp {
targetName: string targetName: string
} }
export interface CreateSubscribeReq {
platformName: string,
targetName: string,
target: string,
categories: Array<string>,
tags: Array<string>
}

View File

@ -1899,6 +1899,11 @@
resolved "https://registry.npm.taobao.org/@types/json5/download/@types/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.npm.taobao.org/@types/json5/download/@types/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/lodash@^4.14.175":
version "4.14.175"
resolved "https://registry.npmmirror.com/@types/lodash/download/@types/lodash-4.14.175.tgz#b78dfa959192b01fae0ad90e166478769b215f45"
integrity sha1-t436lZGSsB+uCtkOFmR4dpshX0U=
"@types/minimatch@*": "@types/minimatch@*":
version "3.0.5" version "3.0.5"
resolved "https://registry.nlark.com/@types/minimatch/download/@types/minimatch-3.0.5.tgz?cache=0&sync_timestamp=1629708375365&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fminimatch%2Fdownload%2F%40types%2Fminimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" resolved "https://registry.nlark.com/@types/minimatch/download/@types/minimatch-3.0.5.tgz?cache=0&sync_timestamp=1629708375365&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fminimatch%2Fdownload%2F%40types%2Fminimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"

78
poetry.lock generated
View File

@ -565,6 +565,25 @@ type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple" url = "https://mirrors.aliyun.com/pypi/simple"
reference = "aliyun" reference = "aliyun"
[[package]]
name = "jinja2"
version = "3.0.1"
description = "A very fast and expressive template engine."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[package.source]
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
reference = "aliyun"
[[package]] [[package]]
name = "loguru" name = "loguru"
version = "0.5.3" version = "0.5.3"
@ -585,6 +604,19 @@ type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple" url = "https://mirrors.aliyun.com/pypi/simple"
reference = "aliyun" reference = "aliyun"
[[package]]
name = "markupsafe"
version = "2.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.6"
[package.source]
type = "legacy"
url = "https://mirrors.aliyun.com/pypi/simple"
reference = "aliyun"
[[package]] [[package]]
name = "matplotlib-inline" name = "matplotlib-inline"
version = "0.1.2" version = "0.1.2"
@ -1365,7 +1397,7 @@ reference = "aliyun"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "2094fead0b3c31a3d27536045ff4bdf6e26ab874ec9f79a6460db14f90bf8140" content-hash = "f953dbdde807df6c88dcd1cb962dcaff43dda3d45f5f4e27ef76e3bec3786d8b"
[metadata.files] [metadata.files]
aiofiles = [ aiofiles = [
@ -1414,7 +1446,7 @@ bidict = [
{file = "bidict-0.21.3.tar.gz", hash = "sha256:d50bd81fae75e34198ffc94979a0eb0939ff9adb3ef32bcc93a913d8b3e3ed1d"}, {file = "bidict-0.21.3.tar.gz", hash = "sha256:d50bd81fae75e34198ffc94979a0eb0939ff9adb3ef32bcc93a913d8b3e3ed1d"},
] ]
bs4 = [ bs4 = [
{file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"}, {file = "bs4-0.0.1.tar.gz", hash = "md5:fe7e51587ac3b174608f3c4f8bd893ac"},
] ]
certifi = [ certifi = [
{file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
@ -1561,10 +1593,50 @@ jedi = [
{file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"},
{file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"},
] ]
jinja2 = [
{file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
{file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
]
loguru = [ loguru = [
{file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"}, {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
{file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"}, {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
] ]
markupsafe = [
{file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
{file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
{file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
{file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
{file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
{file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
{file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
{file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
{file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
{file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
]
matplotlib-inline = [ matplotlib-inline = [
{file = "matplotlib-inline-0.1.2.tar.gz", hash = "sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"}, {file = "matplotlib-inline-0.1.2.tar.gz", hash = "sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"},
{file = "matplotlib_inline-0.1.2-py3-none-any.whl", hash = "sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811"}, {file = "matplotlib_inline-0.1.2-py3-none-any.whl", hash = "sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811"},
@ -1775,7 +1847,7 @@ rfc3986 = [
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
] ]
sgmllib3k = [ sgmllib3k = [
{file = "sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"}, {file = "sgmllib3k-1.0.0.tar.gz", hash = "md5:d70efde06e40797f37e867123aa080ec"},
] ]
six = [ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},

View File

@ -33,6 +33,7 @@ expiringdict = "^1.2.1"
pyjwt = "^2.1.0" pyjwt = "^2.1.0"
aiofiles = "^0.7.0" aiofiles = "^0.7.0"
python-socketio = "^5.4.0" python-socketio = "^5.4.0"
jinja2 = "^3.0.1"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
ipdb = "^0.13.4" ipdb = "^0.13.4"

View File

@ -1,8 +1,10 @@
from dataclasses import dataclass
import importlib import importlib
from pathlib import Path from pathlib import Path
from typing import Callable from typing import Callable
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from nonebot import get_driver, on_command from nonebot import get_driver, on_command
import nonebot import nonebot
from nonebot.adapters.cqhttp.bot import Bot from nonebot.adapters.cqhttp.bot import Bot
@ -14,7 +16,9 @@ from nonebot.typing import T_State
import socketio import socketio
import functools import functools
from .api import test, get_global_conf, auth, get_subs_info, get_target_name from starlette.requests import Request
from .api import test, get_global_conf, auth, get_subs_info, get_target_name, add_group_sub
from .token_manager import token_manager as tm from .token_manager import token_manager as tm
from .jwt import load_jwt from .jwt import load_jwt
from ..plugin_config import plugin_config from ..plugin_config import plugin_config
@ -43,6 +47,19 @@ def register_router_fastapi(driver: Driver, socketio):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED) raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
return obj return obj
async def check_group_permission(groupNumber: str, token_obj: dict = Depends(get_jwt_obj)):
groups = token_obj['groups']
if groupNumber not in groups:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
@dataclass
class AddSubscribeReq:
platformName: str
target: str
targetName: str
categories: list[str]
tags: list[str]
app = driver.server_app app = driver.server_app
static_path = str((Path(__file__).parent / "dist").resolve()) static_path = str((Path(__file__).parent / "dist").resolve())
app.get(TEST_URL)(test) app.get(TEST_URL)(test)
@ -55,7 +72,17 @@ def register_router_fastapi(driver: Driver, socketio):
@app.get(GET_TARGET_NAME_URL) @app.get(GET_TARGET_NAME_URL)
async def _get_target_name(platformName: str, target: str, jwt_obj: dict = Depends(get_jwt_obj)): async def _get_target_name(platformName: str, target: str, jwt_obj: dict = Depends(get_jwt_obj)):
return await get_target_name(platformName, target, jwt_obj) return await get_target_name(platformName, target, jwt_obj)
@app.post(SUBSCRIBE_URL, dependencies=[Depends(check_group_permission)])
async def _add_group_subs(groupNumber: str, req: AddSubscribeReq):
return await add_group_sub(group_number=groupNumber, platform_name=req.platformName,
target=req.target, target_name=req.targetName, cats=req.categories, tags=req.tags)
app.mount(URL_BASE, StaticFiles(directory=static_path, html=True), name="hk_reporter") app.mount(URL_BASE, StaticFiles(directory=static_path, html=True), name="hk_reporter")
templates = Jinja2Templates(directory=static_path)
@app.get(f'{URL_BASE}{{rest_path:path}}')
async def serve_sap(request: Request, rest_path: str):
return templates.TemplateResponse("index.html", {"request": request})
def init(): def init():
driver = get_driver() driver = get_driver()

View File

@ -63,3 +63,10 @@ async def get_subs_info(jwt_obj: dict):
async def get_target_name(platform_name: str, target: str, jwt_obj: dict): async def get_target_name(platform_name: str, target: str, jwt_obj: dict):
return {'targetName': await check_sub_target(platform_name, target)} return {'targetName': await check_sub_target(platform_name, target)}
async def add_group_sub(group_number: str, platform_name: str, target: str,
target_name: str, cats: list[str], tags: list[str]):
config = Config()
config.add_subscribe(group_number, 'group', target, target_name, platform_name, cats, tags)
return { 'status': 200, 'msg': '' }