Skip to content

Commit

Permalink
feat: auth service, password 404 pages
Browse files Browse the repository at this point in the history
  • Loading branch information
BIYUEHU committed Jul 20, 2024
1 parent 2abd7f0 commit 35cedff
Show file tree
Hide file tree
Showing 35 changed files with 792 additions and 191 deletions.
17 changes: 17 additions & 0 deletions packages/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import { HashRouter as Router, Route, Routes } from 'react-router-dom'
import Layout from '@/components/Layout'
import routes from './routes'
import { useDispatch } from 'react-redux'
import useSWR from 'swr'
import { getSettings } from './http'
import { useEffect } from 'react'
import ErrorResult from './components/result/error'
import Loading from './components/Loading'
import { loadSettings } from './store/settingsReducer'

function App() {
const dispatch = useDispatch()
const { data, error } = useSWR('/api/settings', getSettings)

useEffect(() => {
if (data) dispatch(loadSettings(data))
}, [data, dispatch])

if (error) return <ErrorResult />
if (!data) return <Loading />

return (
<Router>
<Routes>
Expand Down
34 changes: 12 additions & 22 deletions packages/client/src/components/Layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@ import { LoginOutlined } from '@ant-design/icons'
import { Link, useNavigate } from 'react-router-dom'
import styles from './styles.module.css'
import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { isLoggedIn } from '@/store/adminReducer'
import useSWR from 'swr'
import { getSettings } from '@/http'
import { loadSettings } from '@/store/settingsReducer'
import Loading from '../Loading'
import ErrorResult from '../result/error'
import { getToken } from '@/store/adminReducer'
import { getCurrentBackground, getSettings } from '@/store/settingsReducer'
import { useSelector } from 'react-redux'

interface LayoutProps {
title: string
Expand All @@ -19,36 +15,30 @@ interface LayoutProps {

const Layout: React.FC<LayoutProps> = ({ title, outlet, isPrivate }) => {
const navigate = useNavigate()
const isLogged = useSelector(isLoggedIn)
const dispatch = useDispatch()
const { data, error } = useSWR('/api/settings', getSettings)
const isLogged = useSelector(getToken)
const settings = useSelector(getSettings)
const currentBackground = useSelector(getCurrentBackground)

useEffect(() => {
if (data) {
dispatch(loadSettings(data))
document.title = title ? `${title} | ${data.site_title}` : data.site_title
}
if (isPrivate && !isLogged) navigate('./login')
}, [data, dispatch, navigate, isPrivate, isLogged, title])

if (error) return <ErrorResult />
if (!data) return <Loading />
document.title = title ? `${title} | ${settings.site_title}` : settings.site_title
if (isPrivate && !isLogged) navigate('/admin/login')
})

return (
<>
<div
className={styles.background}
style={{
backgroundImage: `url(${data.site_backgrounds[Math.floor(Math.random() * data.site_backgrounds.length)]})`
backgroundImage: `url(${currentBackground})`
}}
/>
<Flex gap="middle" wrap>
<AntLayout className={styles.layout}>
<AntLayout.Header className={styles.header}>
<h1>
<Link className={styles.headerTitle} to="/">
<Avatar onClick={() => {}} style={{ marginRight: 10 }} src={data.site_logo} />
{data.site_name}
<Avatar onClick={() => {}} style={{ marginRight: 10 }} src={settings.site_logo} />
{settings.site_name}
</Link>
</h1>
<Link to="/admin">
Expand Down
34 changes: 33 additions & 1 deletion packages/client/src/http/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ import type {
MoehubApiBase,
MoehubApiCharacter,
MoehubApiCharacters,
MoehubApiCollection,
MoehubApiCollections,
MoehubApiLogin,
MoehubApiSeries,
MoehubApiSettings,
MoehubApiTags,
MoehubDataCharacterSubmit,
MoehubDataCollectionSubmit,
MoehubDataSettingsSubmit
} from '@moehub/common'
import http from './http'
import http, { httpNoCatcher } from './http'

export async function getCharacter(id: number): Promise<MoehubApiCharacter['data']> {
return (await http.get(`/character/${id}`)).data
Expand Down Expand Up @@ -38,10 +42,38 @@ export function getSeries(): Promise<MoehubApiSeries> {
return http.get('/series')
}

export async function getCollection(id: number): Promise<MoehubApiCollection['data']> {
return (await http.get(`/collection/${id}`)).data
}

export async function getCollections(): Promise<MoehubApiCollections['data']> {
return (await http.get('/collection')).data
}

export function createCollection(collection: MoehubDataCollectionSubmit): Promise<MoehubApiBase<201>> {
return http.post('/collection', collection)
}

export function updateCollection(id: number, collection: MoehubDataCollectionSubmit): Promise<MoehubApiBase<204>> {
return http.put(`/collection/${id}`, collection)
}

export function deleteCollection(id: number): Promise<MoehubApiBase<204>> {
return http.delete(`/collection/${id}`)
}

export async function getSettings(): Promise<MoehubApiSettings['data']> {
return (await http.get('/settings')).data
}

export function updateSettings(settings: MoehubDataSettingsSubmit): Promise<MoehubApiBase<204>> {
return http.put('/settings', settings)
}

export async function postLogin(email: string, password: string): Promise<MoehubApiLogin['data']> {
return (await httpNoCatcher.post('/settings/login', { email, password })).data
}

export async function updateLogin(newPassword: string, oldPassword: string): Promise<MoehubApiBase<204>> {
return await httpNoCatcher.put('/settings/login', { newPassword, oldPassword })
}
73 changes: 43 additions & 30 deletions packages/client/src/http/http.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
import axios from 'axios';
import { notification } from 'antd';
import config from './config.js';

const http = axios.create({
baseURL: config.url,
timeout: 20000
});

http.interceptors.request.use(
(config) => config,
(err) => Promise.reject(err)
);

http.interceptors.response.use(
(response) => {
if (response.data) response.config.headers['Content-Type'] = 'application/json';
return response.data;
},
(err) => {
notification.error({
message: 'Http request error',
description: err instanceof Error ? err.message : err || 'Unknown error',
placement: 'topRight'
});
return Promise.reject(err);
}
);

export default http;
import axios from 'axios'
import { notification } from 'antd'
import config from './config.js'
import { getToken } from '@/store/adminReducer.js'
import store from '@/store/'

function axiosCreator() {
const http = axios.create({
baseURL: config.url,
timeout: 20000
})

http.interceptors.request.use(
(config) => {
const token = getToken(store.getState())
if (token) config.headers.Authorization = `Bearer ${token}`
return config
},
(err) => Promise.reject(err)
)

http.interceptors.response.use((response) => {
if (response.data) response.config.headers['Content-Type'] = 'application/json'
return response.data
})

return http
}

const http = axiosCreator()

http.interceptors.response.use(undefined, (err) => {
notification.error({
message: 'Http request error',
description: err instanceof Error ? err.message : err || 'Unknown error',
placement: 'topRight'
})
return Promise.reject(err)
})

export const httpNoCatcher = axiosCreator()

export default http
11 changes: 11 additions & 0 deletions packages/client/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ const routes: RouteConfig[] = [
title: '系统设置',
isPrivate: true
},
{
path: '/admin/password',
component: lazyLoader(() => import('@/views/Admin/Password')),
title: '密码修改',
isPrivate: true
},
{
path: '/admin/list',
component: lazyLoader(() => import('@/views/Admin/List')),
Expand All @@ -70,6 +76,11 @@ const routes: RouteConfig[] = [
component: lazyLoader(() => import('@/views/Admin/Edit')),
title: '角色编辑',
isPrivate: true
},
{
path: '*',
component: lazyLoader(() => import('@/views/404')),
title: '404'
}
]

Expand Down
12 changes: 6 additions & 6 deletions packages/client/src/store/adminReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ import { createSlice } from '@reduxjs/toolkit'
export const adminSlice = createSlice({
name: 'admin',
initialState: {
isLoggedIn: false
token: ''
},
reducers: {
login: (state) => {
state.isLoggedIn = true
login: (state, { payload }: { payload: string }) => {
state.token = payload
},
logout: (state) => {
state.isLoggedIn = false
state.token = ''
}
},
selectors: {
isLoggedIn: (state) => state.isLoggedIn
getToken: (state) => state.token
}
})

export const { login, logout } = adminSlice.actions

export const { isLoggedIn } = adminSlice.selectors
export const { getToken } = adminSlice.selectors

export default adminSlice.reducer
2 changes: 1 addition & 1 deletion packages/client/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const persistedReducer = persistReducer(
{
key: 'moehub',
storage,
whitelist: ['admin', 'settings']
whitelist: ['admin']
},
combineReducers({
admin: adminReducer,
Expand Down
13 changes: 7 additions & 6 deletions packages/client/src/store/settingsReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@ import { createSlice } from '@reduxjs/toolkit'

export const settingsSlice = createSlice({
name: 'settings',
initialState: {} as MoehubDataSettings,
initialState: {} as MoehubDataSettings & { currentBackground: string },
reducers: {
loadSettings: (state, { payload }: { payload: MoehubDataSettingsSubmit }) => {
for (const key in payload) {
;(state as Record<string, unknown>)[key] = payload[key as keyof MoehubDataSettings]
}
for (const key in payload) (state as Record<string, unknown>)[key] = payload[key as keyof MoehubDataSettings]
if (!state.site_backgrounds) return
state.currentBackground = state.site_backgrounds[Math.floor(Math.random() * state.site_backgrounds.length)]
}
},
selectors: {
getSettings: (state) => state
getSettings: (state) => state,
getCurrentBackground: (state) => state.currentBackground
}
})

export const { loadSettings } = settingsSlice.actions

export const { getSettings } = settingsSlice.selectors
export const { getSettings, getCurrentBackground } = settingsSlice.selectors

export default settingsSlice.reducer
8 changes: 7 additions & 1 deletion packages/client/src/styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ a::selection {

.cardButton {
height: 40px;
width: 110px;
/* width: 110px; */
color: #212121 !important;
font-size: 1.2rem;
margin: 10px;
Expand All @@ -50,6 +50,12 @@ a::selection {
margin-bottom: 14px;
}

.cardSquare {
width: 550px;
max-width: 95vw;

}

.clean {
background: none !important;
}
Expand Down
31 changes: 31 additions & 0 deletions packages/client/src/views/404/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Card, Typography, Flex, Button } from 'antd'
import { HomeOutlined } from '@ant-design/icons'
import { useNavigate } from 'react-router-dom'

const { Title } = Typography

const NotFoundView: React.FC = () => {
const navigate = useNavigate()

return (
<div>
<Flex justify="center" align="center" style={{ marginTop: '25vh' }}>
<Card className="card cardFixed" style={{ maxWidth: 500 }}>
<Flex vertical align="center" gap="middle">
<Title level={1} style={{ marginBottom: 0, color: 'pink', fontSize: '10rem' }}>
404
</Title>
<Title level={3} type="warning">
{'页面走丢了呜呜 ヽ(*。>Д<)o゜'}
</Title>
<Button className="cardButton clean" icon={<HomeOutlined />} onClick={() => navigate('/')}>
回到主页
</Button>
</Flex>
</Card>
</Flex>
</div>
)
}

export default NotFoundView
9 changes: 4 additions & 5 deletions packages/client/src/views/Admin/Create/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ import { useNavigate } from 'react-router-dom'
const CreateView: React.FC = () => {
const navigate = useNavigate()

function onSubmit(values: MoehubDataCharacterHandle) {
createCharacter(handleMoehubDataCharacter(values)).then(() => {
notification.success({ message: '角色创建成功' })
setTimeout(() => navigate(0), 500)
})
async function onSubmit(values: MoehubDataCharacterHandle) {
await createCharacter(handleMoehubDataCharacter(values))
notification.success({ message: '角色创建成功' })
setTimeout(() => navigate(0), 500)
}

return (
Expand Down
7 changes: 3 additions & 4 deletions packages/client/src/views/Admin/Edit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ const EditView: React.FC = () => {

const { data, error } = useSWR(`/api/character/${characterId}`, () => getCharacter(Number(characterId)))

function onSubmit(values: MoehubDataCharacterHandle) {
updateCharacter(Number(characterId), handleMoehubDataCharacter(values)).then(() =>
notification.success({ message: '角色编辑成功' })
)
async function onSubmit(values: MoehubDataCharacterHandle) {
await updateCharacter(Number(characterId), handleMoehubDataCharacter(values))
notification.success({ message: '角色编辑成功' })
}

return (
Expand Down
Loading

0 comments on commit 35cedff

Please sign in to comment.