diff --git a/src/actions/base.ts b/src/actions/base.ts index f246038..5552e14 100644 --- a/src/actions/base.ts +++ b/src/actions/base.ts @@ -3,7 +3,6 @@ import {API_BASE_URL, ApiResponse, CLIENT_ID, CLIENT_SECRET} from '@/lib/api' import {cookies, headers} from 'next/headers' import {cache} from 'react' import {redirect} from 'next/navigation' -import {userAgent} from 'next/server' // ====================== // public @@ -20,14 +19,7 @@ const _callPublic = cache(async ( endpoint: string, data?: string, ): Promise> => { - return call(`${API_BASE_URL}${endpoint}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: data, - }, - ) + return call(`${API_BASE_URL}${endpoint}`, data) }) // ====================== @@ -56,14 +48,7 @@ const _callByDevice = cache(async ( const token = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64url') // 发起请求 - return call(`${API_BASE_URL}${endpoint}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Basic ${token}`, - }, - body: data, - }) + return call(`${API_BASE_URL}${endpoint}`, data, `Basic ${token}`) }) // ====================== @@ -81,8 +66,6 @@ const _callByUser = cache(async ( endpoint: string, data?: string, ): Promise> => { - const header = await headers() - // 获取用户令牌 const cookie = await cookies() const token = cookie.get('auth_token')?.value @@ -95,35 +78,30 @@ const _callByUser = cache(async ( } // 发起请求 - return await call(`${API_BASE_URL}${endpoint}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}`, - 'X-Forwarded-For': header.get('x-forwarded-for') || '[web]unknown', - 'User-Agent': header.get('user-agent') || '[web]unknown', - } as Record, - body: data, - }) + return await call(`${API_BASE_URL}${endpoint}`, data, `Bearer ${token}`) }) // ====================== // call // ====================== -async function call(url: string, request: RequestInit): Promise> { +async function call(url: string, body: RequestInit['body'], auth?: string): Promise> { let response: Response try { - const userHeaders = await headers() - // request.headers['x-data-ip'] = header.get('x-forwarded-for') - // request.headers['x-data-ua'] = header.get('user-agent') + const reqHeaders = await headers() + const reqIP = reqHeaders.get('x-forwarded-for') + const reqUA = reqHeaders.get('user-agent') + const callHeaders: RequestInit['headers'] = { + 'Content-Type': 'application/json', + } + if (auth) callHeaders['Authorization'] = auth + if (reqIP) callHeaders['X-Forwarded-For'] = reqIP + if (reqUA) callHeaders['User-Agent'] = reqUA + response = await fetch(url, { - ...request, - headers: { - ...request.headers, - 'x-data-ip': userHeaders.get('x-forwarded-for') || '', - 'x-data-ua': userHeaders.get('user-agent') || '', - }, + method: 'POST', + headers: callHeaders, + body, }) } catch (e) { diff --git a/src/actions/ip.ts b/src/actions/ip.ts new file mode 100644 index 0000000..62a60a5 --- /dev/null +++ b/src/actions/ip.ts @@ -0,0 +1,35 @@ +'use server' + +import {headers} from 'next/headers' + +export async function getClientIp(): Promise<{ip?: string, error?: string}> { + try { + // 1. 从headers获取 + const headersList = await headers() + + // 尝试常见header + const forwardedFor = headersList.get('x-forwarded-for') + if (forwardedFor) { + const ip = forwardedFor.split(',')[0].trim() + if (ip) return {ip} + } + + const realIp = headersList.get('x-real-ip') + if (realIp) return {ip: realIp} + + // 回退到ipify + const response = await fetch('https://api.ipify.org?format=json', { + cache: 'no-store', + }) + + if (response.ok) { + const data = await response.json() + return {ip: data.ip} + } + + return {error: '无法获取IP'} + } + catch (error) { + return {error: error instanceof Error ? error.message : '获取失败'} + } +} diff --git a/src/app/admin/(dashboard)/_client/charts.tsx b/src/app/admin/(dashboard)/_client/charts.tsx index bcc8380..fb83ee5 100644 --- a/src/app/admin/(dashboard)/_client/charts.tsx +++ b/src/app/admin/(dashboard)/_client/charts.tsx @@ -50,6 +50,8 @@ export default function Charts({initialData}: ChartsProps) { resource_no: value.resource_no ?? '', create_after: value.create_after ?? sevenDaysAgo, create_before: value.create_before ?? today, + // create_after: value.create_after ? format(value.create_after, 'yyyy-MM-dd') : format(sevenDaysAgo, 'yyyy-MM-dd'), + // create_before: value.create_before ? format(value.create_before, 'yyyy-MM-dd') : format(today, 'yyyy-MM-dd'), } const resp = await statisticsResourceUsage(res) @@ -67,6 +69,7 @@ export default function Charts({initialData}: ChartsProps) { date: item.date, count: item.count, })) + formattedData.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) setSubmittedData(formattedData) }, @@ -147,7 +150,11 @@ type DashboardChartProps = { } function DashboardChart(props: DashboardChartProps) { - const chartData = props.data.map((item) => { + const sortedData = [...props.data].sort((a, b) => { + return new Date(a.date).getTime() - new Date(b.date).getTime() + }) + + const chartData = sortedData.map((item) => { const date = new Date(item.date.split('T')[0]) return { ...item, diff --git a/src/app/admin/identify/page.tsx b/src/app/admin/identify/page.tsx index f309770..3151f7f 100644 --- a/src/app/admin/identify/page.tsx +++ b/src/app/admin/identify/page.tsx @@ -169,12 +169,12 @@ export default function IdentifyPage(props: IdentifyPageProps) {

请扫码完成认证

- {/* */} +
)} diff --git a/src/app/admin/whitelist/page.tsx b/src/app/admin/whitelist/page.tsx index f853f8a..a5ef28b 100644 --- a/src/app/admin/whitelist/page.tsx +++ b/src/app/admin/whitelist/page.tsx @@ -23,7 +23,7 @@ import { import Page from '@/components/page' import DataTable from '@/components/data-table' import {format, parseISO} from 'date-fns' - +import {getClientIp} from '@/actions/ip' const schema = z.object({ host: z.string().min(1, {message: 'IP地址不能为空'}), remark: z.string().optional(), @@ -226,6 +226,27 @@ export default function WhitelistPage(props: WhitelistPageProps) { return
加载失败,请重试
} + const getCurrentIP = async () => { + setWait(true) + + try { + const result = await getClientIp() + + if (result.ip) { + form.setValue('host', result.ip) + toast.success('已获取当前IP地址') + } + else { + toast.error('获取失败', { + description: result.error || '请手动输入IP地址', + }) + } + } + finally { + setWait(false) + } + } + return ( @@ -236,14 +257,6 @@ export default function WhitelistPage(props: WhitelistPageProps) { 添加白名单 {data.total >= MAX_WHITELIST_COUNT && '(已达上限)'} - {/* */} {/* 数据表 */} @@ -306,7 +319,23 @@ export default function WhitelistPage(props: WhitelistPageProps) { onSubmit={onSubmit}> {({id, field}) => ( - +
+ + +
)}
diff --git a/src/components/composites/extract/index.tsx b/src/components/composites/extract/index.tsx index 2966b0d..8e3a4d4 100644 --- a/src/components/composites/extract/index.tsx +++ b/src/components/composites/extract/index.tsx @@ -75,8 +75,11 @@ export default function Extract(props: ExtractProps) { 提取IP前需要将本机IP添加到白名单后才可使用 - - + + 去添加 → @@ -526,12 +529,16 @@ function ApplyLink() { const handler = form.handleSubmit( // eslint-disable-next-line react-hooks/refs async (values: z.infer) => { + console.log(values, 'values') + const params = link(values) + console.log(params, 'paramsparams') switch (type.current) { case 'copy': const url = new URL(window.location.href).origin const text = `${url}${params}` + console.log(text, 'text') // 使用 clipboard API 复制链接 let copied = false diff --git a/src/components/composites/payment/payment-modal.tsx b/src/components/composites/payment/payment-modal.tsx index 7563198..ec978cb 100644 --- a/src/components/composites/payment/payment-modal.tsx +++ b/src/components/composites/payment/payment-modal.tsx @@ -33,33 +33,28 @@ export function PaymentModal(props: PaymentModalProps) { } } - // 轮询检查支付状态 + // SSE处理方式检查支付状态 useEffect(() => { - const pollInterval = 2000 - const maxRetries = 30 - let retries = 0 - - const interval = setInterval(async () => { - try { - await props.onConfirm(false) - return + const eventSource = new EventSource( + `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/trade/check?trade_no=${props.inner_no}&method=${props.method}`, + ) + eventSource.onmessage = async (event) => { + console.log(event, 'eventeventevent') + switch (event.data) { + case '1': + props.onConfirm?.(true) + case '2': + props.onClose?.() } - catch (error) { - console.error('支付状态检查失败:', error) - } - finally { - console.log('进入轮询支付状态') - retries++ - if (retries >= maxRetries) { - clearInterval(interval) - } - } - }, pollInterval) + } + eventSource.onerror = (error) => { + console.error('SSE 连接错误:', error) + } return () => { - clearInterval(interval) + eventSource.close() } - }, [props]) + }, [props.inner_no, props.method, props.onConfirm]) return (