Skip to content

Commit

Permalink
feat: 增加 OAuth 登录页面;美化页面
Browse files Browse the repository at this point in the history
  • Loading branch information
CaoMeiYouRen committed Oct 2, 2024
1 parent 80c8fff commit 35c5773
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 9 deletions.
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ BASE_URL=''
# REDIRECT_URL='http://localhost:4000'
REDIRECT_URL=''

# 通过验证码登录后,回调地址
# OAUTH_REDIRECT_URL='http://localhost:5000'
OAUTH_REDIRECT_URL=''

# 微信公众号相关配置
WX_TOKEN=''
WX_APP_ID=''
Expand Down
2 changes: 2 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { winstonLogger } from './utils/logger'
import routes from './routes'
import { errorhandler, notFoundHandler } from './middlewares/error'
import indexPage from './pages/index'
import oauthPage from './pages/oauth'

const app = new Hono()

Expand All @@ -23,6 +24,7 @@ app.onError(errorhandler)
app.notFound(notFoundHandler)

app.route('/', indexPage)
app.route('/oauth', oauthPage)
app.route('/', routes)

__DEV__ && showRoutes(app, {
Expand Down
3 changes: 3 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@ export const BASE_URL = process.env.BASE_URL || `http://localhost:${PORT}`
// 重定向地址
export const REDIRECT_URL = process.env.REDIRECT_URL || ''

// 回调地址
export const OAUTH_REDIRECT_URL = process.env.OAUTH_REDIRECT_URL || ''

// jwt 密钥
export const JWT_SECRET = process.env.JWT_SECRET || ''
4 changes: 4 additions & 0 deletions src/layout/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ export const Layout: FC<Props> = (props) => {
return (
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com"></script>
<title>{props.title}</title>
</head>
<body>{props.children}</body>
</html>
)
}

39 changes: 30 additions & 9 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,61 @@
import { Hono } from 'hono'
import { FC } from 'hono/jsx'
import { html } from 'hono/html'
import { Layout } from '@/layout/layout'
import { json2xml } from '@/utils/helper'
import { json2xml, verifyJwtToken } from '@/utils/helper'
import winstonLogger from '@/utils/logger'
import { getDataSource } from '@/db'
import { User } from '@/db/models/user'

const app = new Hono()

const Welcome: FC = () => {
type Props = {
name: string
}
const Welcome: FC<Props> = (props) => {
return (
<Layout title="Home">
<h1 align="center">Hello Hono!</h1>
<Layout title="主页">
<div className="flex items-start justify-center min-h-screen bg-gray-100">
<h1 className="text-4xl font-bold text-center text-gray-800 mt-16">Hello {props.name}!</h1>
</div>
</Layout>
)
}

app.all('/', (c) => {
app.all('/', async (c) => {
let name = 'Hono'
const token = c.req.query('token') // 如果有 jwt token,则验证是否有效,如果有效,则查询对应的用户信息
try {
if (token) {
winstonLogger.isDebugEnabled() && winstonLogger.debug('token: ', token)
const payload = await verifyJwtToken(token)
const id = payload.id as number
const userRepository = (await getDataSource()).getRepository(User)
const user = await userRepository.findOneBy({ id })
name = user?.username || 'Hono'
}
} catch (error) {
winstonLogger.error(error)
}
// 根据 header 判断请求类型
const accept = c.req.header('Accept')
// 如果是页面,则返回html
if (accept.includes('text/html')) {
c.header('Content-Type', 'text/html')
return c.html(
<Welcome />,
<Welcome name={name} />,
)
}
// 如果是 xml,则返回xml
if (accept.includes('application/xml') || accept.includes('text/xml')) {
c.header('Content-Type', 'text/xml')
return c.text(json2xml({
message: 'Hello Hono!',
message: `Hello ${name}!`,
}))
}
// 其他情况则返回json
c.header('Content-Type', 'application/json')
return c.json({
message: 'Hello Hono!',
message: `Hello ${name}!`,
})
})

Expand Down
29 changes: 29 additions & 0 deletions src/pages/oauth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Hono } from 'hono'
import { FC } from 'hono/jsx'
import { Layout } from '@/layout/layout'

const app = new Hono()

// 通过验证码登录的前端表单
const OAuthLogin: FC = () => {
return (
<Layout title="验证码登录">
<form action="/auth/loginByOAuth" method="post" className="max-w-md mx-auto p-6 bg-white shadow-md rounded-lg">
<div className="mb-4">
<label htmlFor="code" className="block text-sm font-medium text-gray-700">验证码</label>
<input type="text" name="code" id="code" className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" />
</div>
<button type="submit" className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">
登录
</button>
</form>
</Layout>
)
}
app.get('/', (c) => {
return c.html(
<OAuthLogin />,
)
})

export default app
20 changes: 20 additions & 0 deletions src/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { User } from '@/db/models/user'
import { getJwtToken, verifyPassword } from '@/utils/helper'
import { VerifyCode } from '@/db/models/verify-code'
import { jwtAuth } from '@/middlewares/auth'
import { OAUTH_REDIRECT_URL } from '@/env'

const app = new Hono()

Expand Down Expand Up @@ -55,6 +56,25 @@ app.post('/loginByCode', async (c) => {
})
})

// 登录通过后,回调 OAUTH_REDIRECT_URL
app.post('/loginByOAuth', async (c) => {
const { code, scene } = await c.req.parseBody() as Record<string, any>
const verifyCodeRepository = (await getDataSource()).getRepository(VerifyCode)
const verifyCode = await verifyCodeRepository.findOneBy({ code, scene, used: false, expiredAt: MoreThanOrEqual(dayjs().add(-5, 'minutes').toDate()) })
if (code !== verifyCode?.code) {
throw new HTTPException(400, { message: '验证码错误' })
}
verifyCode.used = true
await verifyCodeRepository.save(verifyCode)
// 查找用户信息
const wechatOpenid = verifyCode.wechatOpenid
const userRepository = (await getDataSource()).getRepository(User)
const user = await userRepository.findOneBy({ wechatOpenid })
const token = await getJwtToken({ id: user.id })
const redirectUrl = `${OAUTH_REDIRECT_URL}?token=${token}`
return c.redirect(redirectUrl, 302)
})

// 使用 jwt token 获取用户信息
app.get('/me', jwtAuth, async (c) => {
const payload = c.get('jwtPayload')
Expand Down

0 comments on commit 35c5773

Please sign in to comment.