fix: 提交短链服务、sentry监控

This commit is contained in:
2025-09-04 06:18:10 -07:00
parent 3c615237e7
commit 57173a0221
13 changed files with 142 additions and 67 deletions
@@ -5,12 +5,9 @@ import useGlobalStore from '@/config/use-global';
import { Card, CardContent } from '@workspace/airo-ui/components/card';
import { useTranslations } from 'next-intl';
import CopyShortenedLink from '@/components/CopyShortenedLink/CopyShortenedLink';
import Recharge from '@/components/subscribe/recharge';
import SvgIcon from '@/components/SvgIcon';
import { Button } from '@workspace/airo-ui/components/button';
import Link from 'next/link';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { toast } from 'sonner';
import Table from './components/Table/Table';
import WalletDialog from './components/WalletDialog/WalletDialog';
@@ -67,18 +64,7 @@ export default function Page() {
</p>
<p className='flex justify-between text-base font-medium text-[#225BA9]'>
<span> {user?.refer_code}</span>
<CopyToClipboard
text={`${location?.origin}/?invite=${user?.refer_code}`}
onCopy={(text, result) => {
if (result) {
toast.success(dashboardT('copySuccess'));
}
}}
>
<Button variant='link' size='sm' className='h-auto p-0 px-0 [&_svg]:size-5'>
<SvgIcon name={'copy'}></SvgIcon>
</Button>
</CopyToClipboard>
<CopyShortenedLink className={'sm:block'} />
</p>
</div>
</div>
+1 -10
View File
@@ -1,12 +1,3 @@
import dynamic from 'next/dynamic';
export default async function MainLayout({ children }: { children: React.ReactNode }) {
const CrispWithNoSSR = dynamic(() => import('@/components/Crisp/Crisp'));
return (
<>
<CrispWithNoSSR />
{children}
</>
);
return <>{children}</>;
}
+47
View File
@@ -0,0 +1,47 @@
// 你的后端 API 密钥,请确保将其存储在 .env.local 文件中
const BACKEND_API_KEY = 'Q4PuYh7J2H_DlW2X4XUrwYV-yaKty8dw0dwP4LXM';
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
try {
// 从客户端请求中获取 JSON 数据
const requestBody = await request.json();
// 检查请求的 hostname 是否为本地地址 (localhost 或 127.0.0.1)
if (requestBody?.target?.includes('localhost') || requestBody?.target?.includes('127.0.0.1')) {
console.log('Request is from localhost, returning self.');
// 如果是本地请求,直接返回一个响应,不做转发
return NextResponse.json({
link: requestBody.target,
});
}
// 将客户端请求的 JSON 数据转发到真正的后端 API
const backendResponse = await fetch('https://url.airoport.co/api/v2/links', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 🚨 在这里使用你的后端 API 密钥,它只在服务器端运行,非常安全
'x-api-key': BACKEND_API_KEY,
},
body: JSON.stringify({
reuse: true,
...requestBody,
}),
});
// 检查后端响应状态
if (!backendResponse.ok) {
// 转发后端 API 的错误响应
const errorData = await backendResponse.json();
return Response.json(errorData, { status: backendResponse.status });
}
const backendData = await backendResponse.json();
// 将后端 API 的响应转发给前端
return Response.json(backendData);
} catch (error) {
console.error('Proxy API Error:', error);
return Response.json({ error: 'Internal server error' }, { status: 500 });
}
}
+2
View File
@@ -7,6 +7,7 @@ import { useCountDown } from 'ahooks';
import { useTranslations } from 'next-intl';
import { useEffect, useState } from 'react';
import { UseFormReturn } from 'react-hook-form';
import { toast } from 'sonner';
interface SendCodeProps {
type: 'email' | 'phone';
@@ -47,6 +48,7 @@ export default function SendCode({ type, params, form }: SendCodeProps) {
const setCodeTimer = () => {
const endTime = Date.now() + verify_code_interval * 1000;
setTargetDate(endTime);
toast.success(t('register.sendCode'));
localStorage.setItem(`verify_code_${type}`, endTime.toString());
};
+3
View File
@@ -11,6 +11,7 @@ import { unstable_noStore as noStore } from 'next/cache';
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
// import { Geist, Geist_Mono } from 'next/font/google';
import dynamic from 'next/dynamic';
import { cookies } from 'next/headers';
import { Metadata, Viewport } from 'next/types';
import NextTopLoader from 'nextjs-toploader';
@@ -99,6 +100,7 @@ export default async function RootLayout({
console.log('Error fetching user info:', error);
}
}
const CrispWithNoSSR = dynamic(() => import('@/components/Crisp/Crisp'));
return (
<html suppressHydrationWarning lang={locale} dir={getLangDir(locale)}>
@@ -120,6 +122,7 @@ export default async function RootLayout({
<NextTopLoader showSpinner={false} />
<Providers common={{ ...config }} user={user}>
<Toaster richColors closeButton />
<CrispWithNoSSR />
{children}
</Providers>
</NextIntlClientProvider>