From 866e4cd2fbac451f0181c4618f32d2ddcdd85f83 Mon Sep 17 00:00:00 2001
From: felinae98 <731499577@qq.com>
Date: Wed, 15 Dec 2021 22:29:54 +0800
Subject: [PATCH] fresh available
---
admin-frontend/package.json | 2 +
admin-frontend/src/App.tsx | 45 ++++-----
admin-frontend/src/api/utils.ts | 2 +-
admin-frontend/src/component/addSubsModal.tsx | 7 +-
.../src/component/subscribeCard.tsx | 10 +-
admin-frontend/src/index.tsx | 8 +-
admin-frontend/src/pages/admin/index.tsx | 7 +-
admin-frontend/src/pages/auth.tsx | 35 +++----
admin-frontend/src/store/globalConfSlice.ts | 35 +++++++
admin-frontend/src/store/hooks.ts | 5 +
admin-frontend/src/store/index.ts | 15 +++
admin-frontend/src/store/loginSlice.ts | 99 +++++++++++++++++++
admin-frontend/src/utils/context.ts | 17 ----
admin-frontend/src/utils/type.ts | 3 +-
admin-frontend/yarn.lock | 5 +
15 files changed, 219 insertions(+), 76 deletions(-)
create mode 100644 admin-frontend/src/store/globalConfSlice.ts
create mode 100644 admin-frontend/src/store/hooks.ts
create mode 100644 admin-frontend/src/store/index.ts
create mode 100644 admin-frontend/src/store/loginSlice.ts
delete mode 100644 admin-frontend/src/utils/context.ts
diff --git a/admin-frontend/package.json b/admin-frontend/package.json
index 133f9b2..42adc92 100644
--- a/admin-frontend/package.json
+++ b/admin-frontend/package.json
@@ -16,6 +16,7 @@
"@types/react-dom": "^17.0.0",
"antd": "^4.16.13",
"axios": "^0.21.4",
+ "jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -51,6 +52,7 @@
},
"devDependencies": {
"@types/lodash": "^4.14.175",
+ "@types/react-redux": "^7.1.20",
"@types/react-router-dom": "^5.3.0",
"react-app-rewired": "^2.1.8",
"redux-devtools": "^3.7.0"
diff --git a/admin-frontend/src/App.tsx b/admin-frontend/src/App.tsx
index 0446598..aacb1e7 100644
--- a/admin-frontend/src/App.tsx
+++ b/admin-frontend/src/App.tsx
@@ -1,45 +1,36 @@
-import React, { useContext, useEffect, useState } from 'react';
-import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
-import './App.css';
-import { LoginContext, loginContextDefault, GlobalConfContext } from './utils/context';
-import { LoginStatus, GlobalConf, AllPlatformConf } from './utils/type';
-import { Admin } from './pages/admin';
-import { getGlobalConf } from './api/config';
-import { Auth } from './pages/auth';
import 'antd/dist/antd.css';
+import React, {useEffect} from 'react';
+import {useDispatch, useSelector} from 'react-redux';
+import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
+import './App.css';
+import {Admin} from './pages/admin';
+import {Auth} from './pages/auth';
+import {getGlobalConf} from './store/globalConfSlice';
+import {useAppSelector} from './store/hooks';
+import {loadLoginState, loginSelector} from './store/loginSlice';
function LoginSwitch() {
- const {login, save} = useContext(LoginContext);
+ const login = useSelector(loginSelector)
if (login.login) {
return ;
} else {
return (
not login
-
)
}
}
function App() {
- const [loginStatus, setLogin] = useState(loginContextDefault.login);
- const [globalConf, setGlobalConf] = useState({platformConf: {} as AllPlatformConf, loaded: false});
- // const globalConfContext = useContext(GlobalConfContext);
- const save = (login: LoginStatus) => setLogin(_ => login);
+ const dispatch = useDispatch()
+ const globalConf = useAppSelector(state => state.globalConf)
useEffect(() => {
- const fetchGlobalConf = async () => {
- const res = await getGlobalConf();
- setGlobalConf(_ => {return {...res, loaded: true}});
- };
- fetchGlobalConf();
- }, []);
- return (
-
-
+ dispatch(getGlobalConf());
+ dispatch(loadLoginState())
+ }, [dispatch]);
+ return <>
{ globalConf.loaded &&
@@ -52,9 +43,7 @@ function App() {
}
-
-
- );
+ >;
}
export default App;
diff --git a/admin-frontend/src/api/utils.ts b/admin-frontend/src/api/utils.ts
index 5c00cde..751cdd2 100644
--- a/admin-frontend/src/api/utils.ts
+++ b/admin-frontend/src/api/utils.ts
@@ -8,7 +8,7 @@ export const baseUrl = '/bison/api/'
axios.interceptors.request.use(function (config) {
if (config.url && config.url.startsWith(baseUrl) && config.url !== `${baseUrl}auth`
&& config.url !== `${baseUrl}global_conf`) {
- const token = sessionStorage.getItem('token');
+ const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
} else {
diff --git a/admin-frontend/src/component/addSubsModal.tsx b/admin-frontend/src/component/addSubsModal.tsx
index ef53a76..b51113e 100644
--- a/admin-frontend/src/component/addSubsModal.tsx
+++ b/admin-frontend/src/component/addSubsModal.tsx
@@ -1,8 +1,9 @@
import {Form, Input, Modal, Select, Tag} from 'antd';
-import React, {useContext, useEffect, useState} from "react";
+import React, {useEffect, useState} from "react";
+import {useSelector} from 'react-redux';
import {addSubscribe, getTargetName, updateSubscribe} from 'src/api/config';
import {InputTag} from 'src/component/inputTag';
-import {GlobalConfContext} from "src/utils/context";
+import {platformConfSelector} from 'src/store/globalConfSlice';
import {CategoryConfig, SubscribeConfig} from 'src/utils/type';
interface InputTagCustomProp {
@@ -49,7 +50,7 @@ export function AddModal({
showModal, groupNumber, setShowModal, refresh, initVal
}: AddModalProp) {
const [ confirmLoading, setConfirmLoading ] = useState(false);
- const { platformConf } = useContext(GlobalConfContext);
+ const platformConf = useSelector(platformConfSelector)
const [ hasTarget, setHasTarget ] = useState(false);
const [ categories, setCategories ] = useState({} as CategoryConfig);
const [ enabledTag, setEnableTag ] = useState(false);
diff --git a/admin-frontend/src/component/subscribeCard.tsx b/admin-frontend/src/component/subscribeCard.tsx
index 71c5ac8..395c58d 100644
--- a/admin-frontend/src/component/subscribeCard.tsx
+++ b/admin-frontend/src/component/subscribeCard.tsx
@@ -1,9 +1,10 @@
import {CopyOutlined, DeleteOutlined, EditOutlined} from '@ant-design/icons';
import {Card, Col, Form, message, Popconfirm, Select, Tag, Tooltip} from 'antd';
import Modal from 'antd/lib/modal/Modal';
-import React, {useContext, useState} from "react";
+import React, {useState} from "react";
+import {useSelector} from 'react-redux';
import {addSubscribe, delSubscribe} from 'src/api/config';
-import {GlobalConfContext} from "src/utils/context";
+import {platformConfSelector} from 'src/store/globalConfSlice';
import {PlatformConfig, SubscribeConfig, SubscribeResp} from 'src/utils/type';
import {AddModal} from './addSubsModal';
@@ -57,10 +58,11 @@ interface SubscribeCardProp {
reload: () => void
}
export function SubscribeCard({groupNumber, config, reload, groupSubscribes}: SubscribeCardProp) {
- const globalConf = useContext(GlobalConfContext);
+ // const globalConf = useSelector()
+ const platformConfs = useSelector(platformConfSelector)
const [showModal, setShowModal] = useState(false)
const [showEditModal, setShowEditModal] = useState(false)
- const platformConf = globalConf.platformConf[config.platformName] as PlatformConfig;
+ const platformConf = platformConfs[config.platformName] as PlatformConfig;
const handleDelete = (groupNumber: string, platformName: string, target: string) => () => {
delSubscribe(groupNumber, platformName, target).then(() => {
reload()
diff --git a/admin-frontend/src/index.tsx b/admin-frontend/src/index.tsx
index ef2edf8..f56de60 100644
--- a/admin-frontend/src/index.tsx
+++ b/admin-frontend/src/index.tsx
@@ -1,12 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import './index.css';
+import {Provider} from 'react-redux';
import App from './App';
+import './index.css';
import reportWebVitals from './reportWebVitals';
+import store from './store';
ReactDOM.render(
-
+
+
+
,
document.getElementById('root')
);
diff --git a/admin-frontend/src/pages/admin/index.tsx b/admin-frontend/src/pages/admin/index.tsx
index 38e998e..4d860be 100644
--- a/admin-frontend/src/pages/admin/index.tsx
+++ b/admin-frontend/src/pages/admin/index.tsx
@@ -1,12 +1,13 @@
import {BugOutlined, SettingOutlined} from '@ant-design/icons';
import {Layout, Menu} from 'antd';
-import React, {useContext, useState} from "react";
-import {LoginContext} from "src/utils/context";
+import React, {useState} from "react";
+import {useSelector} from 'react-redux';
+import {loginSelector} from 'src/store/loginSlice';
import './admin.css';
import {ConfigPage} from './configPage';
export function Admin() {
- const { login } = useContext(LoginContext);
+ const login = useSelector(loginSelector)
const [ tab, changeTab ] = useState("manage");
return (
diff --git a/admin-frontend/src/pages/auth.tsx b/admin-frontend/src/pages/auth.tsx
index c8894be..299c58d 100644
--- a/admin-frontend/src/pages/auth.tsx
+++ b/admin-frontend/src/pages/auth.tsx
@@ -1,27 +1,28 @@
-import React, {useContext, useEffect, useState} from "react";
-import { useParams } from "react-router";
-import { auth } from '../api/config';
-import { LoginContext } from '../utils/context';
-import { Redirect } from 'react-router-dom'
+import React, {useEffect} from "react";
+import {useDispatch, useSelector} from "react-redux";
+import {useParams} from "react-router";
+import {Redirect} from 'react-router-dom';
+import {login, loginSelector} from 'src/store/loginSlice';
+
interface AuthParam {
code: string
}
export function Auth() {
const { code } = useParams();
- const [ content, contentUpdate ] = useState(Logining...
);
- const { save } = useContext(LoginContext);
+ const dispatch = useDispatch();
+ const loginState = useSelector(loginSelector)
useEffect(() => {
const loginFun = async () => {
- const resp = await auth(code);
- if (resp.status === 200) {
- save({login: true, type: resp.type, name: resp.name, id: resp.id, token: resp.token});
- contentUpdate(_ => );
- sessionStorage.setItem('token', resp.token);
- } else {
- contentUpdate(_ => 登录失败,请重新获取连接
);
- }
+ dispatch(login(code));
}
loginFun();
- }, [code, save])
- return content;
+ }, [code, dispatch])
+ return <>
+ { loginState.login ?
+ :
+ loginState.failed ?
+ 登录失败,请重新获取连接
:
+ Logining...
+ }
+>;
}
diff --git a/admin-frontend/src/store/globalConfSlice.ts b/admin-frontend/src/store/globalConfSlice.ts
new file mode 100644
index 0000000..914066a
--- /dev/null
+++ b/admin-frontend/src/store/globalConfSlice.ts
@@ -0,0 +1,35 @@
+import {CaseReducer, createAsyncThunk, createSlice, PayloadAction} from "@reduxjs/toolkit";
+import {getGlobalConf as getGlobalConfApi} from "src/api/config";
+import {GlobalConf} from "src/utils/type";
+import {RootState} from ".";
+
+
+const initialState: GlobalConf = {
+ platformConf: {},
+ loaded: false
+}
+
+const setGlobalConf: CaseReducer> = (_, action) => {
+ return {...action.payload, loaded: true}
+}
+
+export const getGlobalConf = createAsyncThunk(
+ "globalConf/set",
+ getGlobalConfApi,
+ {
+ condition: (_, { getState }) => !(getState() as RootState).globalConf.loaded
+ }
+);
+
+export const globalConfSlice = createSlice({
+ name: "globalConf",
+ initialState,
+ reducers: {},
+ extraReducers: (builder) => {
+ builder.addCase(getGlobalConf.fulfilled, setGlobalConf)
+ }
+})
+
+export const platformConfSelector = (state: RootState) => state.globalConf.platformConf
+
+export default globalConfSlice.reducer
diff --git a/admin-frontend/src/store/hooks.ts b/admin-frontend/src/store/hooks.ts
new file mode 100644
index 0000000..99221df
--- /dev/null
+++ b/admin-frontend/src/store/hooks.ts
@@ -0,0 +1,5 @@
+import {TypedUseSelectorHook, useDispatch, useSelector} from "react-redux";
+import {AppDispatch, RootState} from ".";
+
+export const useAppDispacher = () => useDispatch()
+export const useAppSelector: TypedUseSelectorHook = useSelector
diff --git a/admin-frontend/src/store/index.ts b/admin-frontend/src/store/index.ts
new file mode 100644
index 0000000..654f73f
--- /dev/null
+++ b/admin-frontend/src/store/index.ts
@@ -0,0 +1,15 @@
+import {configureStore} from "@reduxjs/toolkit";
+import loginSlice from "./loginSlice";
+import globalConfSlice from "./globalConfSlice";
+
+const store = configureStore({
+ reducer: {
+ login: loginSlice,
+ globalConf: globalConfSlice,
+ }
+})
+
+export default store;
+
+export type RootState = ReturnType;
+export type AppDispatch = typeof store.dispatch;
diff --git a/admin-frontend/src/store/loginSlice.ts b/admin-frontend/src/store/loginSlice.ts
new file mode 100644
index 0000000..9898d7a
--- /dev/null
+++ b/admin-frontend/src/store/loginSlice.ts
@@ -0,0 +1,99 @@
+import { AnyAction, CaseReducer, createAsyncThunk, createSlice, PayloadAction, ThunkAction } from "@reduxjs/toolkit";
+import jwt_decode from 'jwt-decode';
+import { LoginStatus, TokenResp } from "src/utils/type";
+import { auth } from "src/api/config";
+import {RootState} from ".";
+
+const initialState: LoginStatus = {
+ login: false,
+ type: '',
+ name: '',
+ id: '123',
+ // groups: [],
+ token: '',
+ failed: false
+}
+
+interface storedInfo {
+ type: string
+ name: string
+ id: string
+}
+
+const loginAction: CaseReducer> = (_, action) => {
+ return {
+ login: true,
+ failed: false,
+ type: action.payload.type,
+ name: action.payload.name,
+ id: action.payload.id,
+ token: action.payload.token
+ }
+}
+
+export const login = createAsyncThunk(
+ "auth/login",
+ async (code: string) => {
+ let res = await auth(code);
+ if (res.status !== 200) {
+ throw Error("Login Error")
+ } else {
+ localStorage.setItem('loginInfo', JSON.stringify({
+ 'type': res.type,
+ 'name': res.name,
+ id: res.id,
+ }))
+ localStorage.setItem('token', res.token)
+ }
+ return res
+ },
+ {
+ condition: (_: string, { getState }) => {
+ const { login } = getState() as { login: LoginStatus }
+ return !login.login;
+ }
+ }
+)
+
+
+export const loginSlice = createSlice({
+ name: 'auth',
+ initialState,
+ reducers: {
+ doLogin: loginAction
+ },
+ extraReducers: (builder) => {
+ builder.addCase(login.fulfilled, loginAction);
+ builder.addCase(login.rejected, (stat) => {
+ stat.failed = true
+ })
+ }
+})
+
+export const { doLogin } = loginSlice.actions
+
+export const loadLoginState = (): ThunkAction =>
+ (dispatch, getState) => {
+ if (getState().login.login) {
+ return
+ }
+ const infoJson = localStorage.getItem('loginInfo')
+ const jwtToken = localStorage.getItem('token');
+ if (infoJson && jwtToken) {
+ const decodedJwt = jwt_decode(jwtToken) as { exp: number };
+ if (decodedJwt.exp < Date.now() / 1000) {
+ return
+ }
+ const info = JSON.parse(infoJson) as storedInfo
+ const payload: TokenResp = {
+ ...info,
+ status: 200,
+ token: jwtToken,
+ }
+ dispatch(doLogin(payload))
+ }
+ }
+
+export const loginSelector = (state: RootState) => state.login
+
+export default loginSlice.reducer
diff --git a/admin-frontend/src/utils/context.ts b/admin-frontend/src/utils/context.ts
deleted file mode 100644
index ca96c34..0000000
--- a/admin-frontend/src/utils/context.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { createContext } from "react";
-import { LoginContextType, GlobalConf } from "./type";
-
-export const loginContextDefault: LoginContextType = {
- login: {
- login: false,
- type: '',
- name: '',
- id: '123',
- // groups: [],
- token: ''
- },
- save: () => {}
-};
-
-export const LoginContext = createContext(loginContextDefault);
-export const GlobalConfContext = createContext({platformConf: {}, loaded: false});
diff --git a/admin-frontend/src/utils/type.ts b/admin-frontend/src/utils/type.ts
index fc4a838..e514ed0 100644
--- a/admin-frontend/src/utils/type.ts
+++ b/admin-frontend/src/utils/type.ts
@@ -9,7 +9,8 @@ export interface LoginStatus {
name: string
id: string
// groups: Array
- token: string
+ token: string,
+ failed: boolean,
}
export type LoginContextType = {
diff --git a/admin-frontend/yarn.lock b/admin-frontend/yarn.lock
index a5c8ae2..5912b3a 100644
--- a/admin-frontend/yarn.lock
+++ b/admin-frontend/yarn.lock
@@ -7138,6 +7138,11 @@ jsonfile@^6.0.1:
array-includes "^3.1.3"
object.assign "^4.1.2"
+jwt-decode@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.npm.taobao.org/jwt-decode/download/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
+ integrity sha1-P7MZ82daLfDCiVyPXp+ktnsE7Vk=
+
killable@^1.0.1:
version "1.0.1"
resolved "https://registry.nlark.com/killable/download/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"