完善错误提示
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- 总览页
|
- 总览页
|
||||||
- 个人中心
|
|
||||||
- 页面图片替换
|
- 页面图片替换
|
||||||
- 网页标题根据实际页面变化
|
- 网页标题根据实际页面变化
|
||||||
- 表格页筛选日期,范围筛选需要联动;检查时间范围选择,限定到一定范围内
|
- 表格页筛选日期,范围筛选需要联动;检查时间范围选择,限定到一定范围内
|
||||||
|
- 中间件 Limiter
|
||||||
- 购买套餐页的冗余组件
|
- 购买套餐页的冗余组件
|
||||||
- 确认各个页面操作列的内容
|
- 确认各个页面操作列的内容
|
||||||
- 提取后刷新提取页套餐可用余量
|
- 提取后刷新提取页套餐可用余量
|
||||||
@@ -13,16 +13,18 @@
|
|||||||
- 后台页面:
|
- 后台页面:
|
||||||
- 提取记录
|
- 提取记录
|
||||||
- 使用记录
|
- 使用记录
|
||||||
|
- 登录流程有问题,在人机验证前不允许提交登录请求
|
||||||
|
|
||||||
### 下阶段
|
### 下阶段
|
||||||
|
|
||||||
|
- markdown 文档渲染
|
||||||
- 后台首页改为 grid 布局,需要额外实现用于布局的客户端组件
|
- 后台首页改为 grid 布局,需要额外实现用于布局的客户端组件
|
||||||
- 检查页面,为后端请求标记 wait 实现防抖机制
|
- 检查页面,为后端请求标记 wait 实现防抖机制
|
||||||
- 页面切换动效
|
- 页面切换动效
|
||||||
- 使用 pure js 的包代替 canvas,加快编译速度
|
- 使用 pure js 的包代替 canvas,加快编译速度
|
||||||
- 验证码读秒用 store 保存到本地,(全局共享读秒时间)?
|
- 验证码读秒用 store 保存到本地,(全局共享读秒时间)?
|
||||||
- 将翻页操作反映在路由历史中,可以通过后退返回到上一个翻页状态?
|
- 将翻页操作反映在路由历史中,可以通过后退返回到上一个翻页状态?
|
||||||
-
|
|
||||||
### 长期
|
### 长期
|
||||||
|
|
||||||
- 检查扩大服务端组件边界
|
- 检查扩大服务端组件边界
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ const _callPublic = cache(async <R = undefined>(
|
|||||||
data?: string,
|
data?: string,
|
||||||
): Promise<ApiResponse<R>> => {
|
): Promise<ApiResponse<R>> => {
|
||||||
return call(`${API_BASE_URL}${endpoint}`, {
|
return call(`${API_BASE_URL}${endpoint}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
|
||||||
body: data,
|
|
||||||
},
|
},
|
||||||
|
body: data,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -113,24 +113,17 @@ const _callByUser = cache(async <R = undefined>(
|
|||||||
// ======================
|
// ======================
|
||||||
|
|
||||||
async function call<R = undefined>(url: string, request: RequestInit): Promise<ApiResponse<R>> {
|
async function call<R = undefined>(url: string, request: RequestInit): Promise<ApiResponse<R>> {
|
||||||
const response = await fetch(url, request)
|
let response: Response
|
||||||
const type = response.headers.get('Content-Type') ?? 'text/plain'
|
try {
|
||||||
if (type.indexOf('application/json') !== -1) {
|
response = await fetch(url, request)
|
||||||
const json = await response.json()
|
|
||||||
if (!response.ok) {
|
|
||||||
console.log('后端请求失败', url, `status=${response.status}`, json)
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
status: response.status,
|
|
||||||
message: json.message || '请求失败',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data: json,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (type.indexOf('text/plain') !== -1) {
|
catch (e) {
|
||||||
|
console.error('后端请求失败', url, (e as Error).message)
|
||||||
|
throw new Error(`请求失败,网络错误`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = response.headers.get('Content-Type') ?? 'text/plain'
|
||||||
|
if (type.indexOf('text/plain') !== -1) {
|
||||||
const text = await response.text()
|
const text = await response.text()
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.log('后端请求失败', url, `status=${response.status}`, text)
|
console.log('后端请求失败', url, `status=${response.status}`, text)
|
||||||
@@ -141,12 +134,30 @@ async function call<R = undefined>(url: string, request: RequestInit): Promise<A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('未处理的响应成功', `type=text`, `text=${text}`)
|
if (!!text?.trim()?.length) {
|
||||||
|
console.log('未处理的响应成功', `type=text`, `text=${text}`)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: undefined as R, // 强转类型,考虑优化
|
data: undefined as R, // 强转类型,考虑优化
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (type.indexOf('application/json') !== -1) {
|
||||||
|
const json = await response.json()
|
||||||
|
if (!response.ok) {
|
||||||
|
console.log('后端请求失败', url, `status=${response.status}`, json)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
status: response.status,
|
||||||
|
message: json.message || json.error_description || '请求失败' // 业务错误(message)或者 oauth 错误(error_description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: json,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(`无法解析响应数据,未处理的 Content-Type: ${type}`)
|
throw new Error(`无法解析响应数据,未处理的 Content-Type: ${type}`)
|
||||||
}
|
}
|
||||||
@@ -163,8 +174,7 @@ async function postCall<R = undefined>(rawResp: Promise<ApiResponse<R>>) {
|
|||||||
].some(item => item.test(pathname))
|
].some(item => item.test(pathname))
|
||||||
|
|
||||||
if (match && !resp.success && resp.status === 401) {
|
if (match && !resp.success && resp.status === 401) {
|
||||||
console.log('!!!!!!!!!redirect', '"', pathname, '"', resp.message)
|
redirect('/login?force=true')
|
||||||
redirect(pathname === '/' ? '/login' : `/login?redirect=${pathname}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|||||||
@@ -46,19 +46,19 @@ export async function GET(req: NextRequest) {
|
|||||||
const separator = rSeparator.split(',').map(code => String.fromCharCode(parseInt(code))).join('')
|
const separator = rSeparator.split(',').map(code => String.fromCharCode(parseInt(code))).join('')
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 'json':
|
case 'json':
|
||||||
const body = JSON.stringify(params)
|
const body = JSON.stringify(params)
|
||||||
return NextResponse.json(body)
|
return NextResponse.json(body)
|
||||||
case 'text':
|
case 'text':
|
||||||
const text = result.data.map(item => {
|
const text = result.data.map(item => {
|
||||||
const list = [item.host, item.port]
|
const list = [item.host, item.port]
|
||||||
if (item.username && item.password) {
|
if (item.username && item.password) {
|
||||||
list.push(item.username)
|
list.push(item.username)
|
||||||
list.push(item.password)
|
list.push(item.password)
|
||||||
}
|
}
|
||||||
return list.join(separator)
|
return list.join(separator)
|
||||||
}).join(breaker)
|
}).join(breaker)
|
||||||
return new NextResponse(text)
|
return new NextResponse(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ export default function LoginPage(props: LoginPageProps) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
toast.error(`请求失败:${e}`)
|
toast.error('短信发送失败', {
|
||||||
|
description: (e as Error).message,
|
||||||
|
})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,9 +166,7 @@ export default function LoginPage(props: LoginPageProps) {
|
|||||||
|
|
||||||
// 登录失败
|
// 登录失败
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return toast.error(result.message, {
|
throw new Error(result.message || '请检查手机号码和验证码是否正确')
|
||||||
description: '请检查您的手机号码和验证码',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 登录成功
|
// 登录成功
|
||||||
@@ -177,7 +177,7 @@ export default function LoginPage(props: LoginPageProps) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
toast.error('登录错误', {
|
toast.error('登录失败', {
|
||||||
description: (e as Error).message,
|
description: (e as Error).message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,6 @@ export default function BillsPage(props: BillsPageProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('init bill list')
|
|
||||||
refresh(1, 10).then()
|
|
||||||
refresh(1, 10).then()
|
refresh(1, 10).then()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -240,11 +238,11 @@ export default function BillsPage(props: BillsPageProps) {
|
|||||||
{
|
{
|
||||||
accessorKey: 'amount', header: `支付信息`, cell: ({row}) => (
|
accessorKey: 'amount', header: `支付信息`, cell: ({row}) => (
|
||||||
<div className={`flex gap-1`}>
|
<div className={`flex gap-1`}>
|
||||||
<span>
|
<span>
|
||||||
{!row.original.trade && '余额'}
|
{!row.original.trade && '余额'}
|
||||||
{row.original.trade && row.original.trade.method === 1 && '支付宝'}
|
{row.original.trade && row.original.trade.method === 1 && '支付宝'}
|
||||||
{row.original.trade && row.original.trade.method === 2 && '微信'}
|
{row.original.trade && row.original.trade.method === 2 && '微信'}
|
||||||
</span>
|
</span>
|
||||||
<span className={
|
<span className={
|
||||||
row.original.amount > 0 ? `text-green-400` : `text-orange-400`
|
row.original.amount > 0 ? `text-green-400` : `text-orange-400`
|
||||||
}>¥{row.original.amount}</span>
|
}>¥{row.original.amount}</span>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {NextRequest, NextResponse} from 'next/server'
|
import {NextRequest, NextResponse} from 'next/server'
|
||||||
import {refreshAuth} from '@/actions/auth'
|
import {refreshAuth} from '@/actions/auth'
|
||||||
|
import { UnauthorizedError } from './lib/api'
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: [
|
matcher: [
|
||||||
@@ -28,15 +29,23 @@ export async function middleware(request: NextRequest) {
|
|||||||
console.log('❌ 刷新访问令牌失败', e)
|
console.log('❌ 刷新访问令牌失败', e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果刷新访问令牌成功,则继续访问之前的页面
|
||||||
|
const isLogin = request.nextUrl.pathname === '/login'
|
||||||
|
const redirect = request.nextUrl.searchParams.get('redirect')
|
||||||
|
if (isLogin && redirect) {
|
||||||
|
console.log('redirect to', redirect)
|
||||||
|
return NextResponse.redirect(`${request.nextUrl.origin}${redirect}`)
|
||||||
|
}
|
||||||
|
|
||||||
// 没有访问令牌不允许访问 admin 页面
|
// 没有访问令牌不允许访问 admin 页面
|
||||||
const match = [
|
const isAdmin = [
|
||||||
RegExp(`^/admin.*`),
|
RegExp(`^/admin.*`),
|
||||||
].some(item => item.test(request.nextUrl.pathname))
|
].some(item => item.test(request.nextUrl.pathname))
|
||||||
|
|
||||||
const accessToken = request.cookies.get('auth_token')
|
const accessToken = request.cookies.get('auth_token')
|
||||||
|
|
||||||
if (match && !accessToken) {
|
if (isAdmin && !accessToken) {
|
||||||
console.log('redirect!!!!!!!!!')
|
console.log('🚗🚗🚗🚗🚗 非正常重定向 🚗🚗🚗🚗🚗')
|
||||||
return NextResponse.redirect(`${request.nextUrl.origin}/login?redirect=${request.nextUrl.pathname}`)
|
return NextResponse.redirect(`${request.nextUrl.origin}/login?redirect=${request.nextUrl.pathname}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user