From 57173a02215840cf467be234983c051e7de06958 Mon Sep 17 00:00:00 2001 From: speakeloudest Date: Thu, 4 Sep 2025 06:18:10 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8F=90=E4=BA=A4=E7=9F=AD=E9=93=BE?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E3=80=81sentry=E7=9B=91=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(main)/(content)/(user)/wallet/page.tsx | 18 +---- apps/user/app/(main)/(content)/layout.tsx | 11 +--- apps/user/app/api/kutt/route.ts | 47 +++++++++++++ apps/user/app/auth/send-code.tsx | 2 + apps/user/app/layout.tsx | 3 + .../CopyShortenedLink/CopyShortenedLink.tsx | 66 +++++++++++++++++++ apps/user/components/affiliate/index.tsx | 42 ++---------- apps/user/components/main/HomeContent.tsx | 6 +- apps/user/instrumentation-client.ts | 3 + apps/user/locales/en-US/auth.json | 1 + apps/user/locales/zh-CN/auth.json | 3 +- apps/user/sentry.edge.config.ts | 3 + apps/user/sentry.server.config.ts | 4 ++ 13 files changed, 142 insertions(+), 67 deletions(-) create mode 100644 apps/user/app/api/kutt/route.ts create mode 100644 apps/user/components/CopyShortenedLink/CopyShortenedLink.tsx diff --git a/apps/user/app/(main)/(content)/(user)/wallet/page.tsx b/apps/user/app/(main)/(content)/(user)/wallet/page.tsx index 36688af..a00c602 100644 --- a/apps/user/app/(main)/(content)/(user)/wallet/page.tsx +++ b/apps/user/app/(main)/(content)/(user)/wallet/page.tsx @@ -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() {

{user?.refer_code} - { - if (result) { - toast.success(dashboardT('copySuccess')); - } - }} - > - - +

diff --git a/apps/user/app/(main)/(content)/layout.tsx b/apps/user/app/(main)/(content)/layout.tsx index ca6eccb..2b8874f 100644 --- a/apps/user/app/(main)/(content)/layout.tsx +++ b/apps/user/app/(main)/(content)/layout.tsx @@ -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 ( - <> - - {children} - - ); + return <>{children}; } diff --git a/apps/user/app/api/kutt/route.ts b/apps/user/app/api/kutt/route.ts new file mode 100644 index 0000000..1b15d67 --- /dev/null +++ b/apps/user/app/api/kutt/route.ts @@ -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 }); + } +} diff --git a/apps/user/app/auth/send-code.tsx b/apps/user/app/auth/send-code.tsx index 6a6f39d..bd2e04a 100644 --- a/apps/user/app/auth/send-code.tsx +++ b/apps/user/app/auth/send-code.tsx @@ -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()); }; diff --git a/apps/user/app/layout.tsx b/apps/user/app/layout.tsx index 51459fa..f24c726 100644 --- a/apps/user/app/layout.tsx +++ b/apps/user/app/layout.tsx @@ -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 ( @@ -120,6 +122,7 @@ export default async function RootLayout({ + {children} diff --git a/apps/user/components/CopyShortenedLink/CopyShortenedLink.tsx b/apps/user/components/CopyShortenedLink/CopyShortenedLink.tsx new file mode 100644 index 0000000..a70a207 --- /dev/null +++ b/apps/user/components/CopyShortenedLink/CopyShortenedLink.tsx @@ -0,0 +1,66 @@ +'use client'; + +// components/CopyShortenedLink.jsx +import SvgIcon from '@/components/SvgIcon'; +import useGlobalStore from '@/config/use-global'; +import { useQuery } from '@tanstack/react-query'; +import { Button } from '@workspace/airo-ui/components/button'; +import { cn } from '@workspace/airo-ui/lib/utils'; +import { useTranslations } from 'next-intl'; +import { CopyToClipboard } from 'react-copy-to-clipboard'; +import { toast } from 'sonner'; + +const CopyShortenedLink = ({ className }: { className?: string }) => { + const t = useTranslations('affiliate'); + const { user } = useGlobalStore(); + + // 构建长链接,使用用户的唯一标识符作为查询键 + const target = `${location?.origin}/?invite=${user?.refer_code}`; + const queryKey = ['short-url', user?.refer_code]; + + const { data: shortUrl } = useQuery({ + queryKey: queryKey, + queryFn: async () => { + const response = await fetch('/api/kutt', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ target }), + }); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + // 关键步骤:解析 JSON 数据并返回 + const json = await response.json(); + console.log('CopyShortened link', json); + return json.link ?? null; + }, + enabled: !!user?.refer_code, // 默认不自动执行 + staleTime: Infinity, // 数据永不过期,除非手动失效 + }); + // 渲染组件 + return ( + { + if (text) { + toast.success('text is undefined'); + return ''; + } + if (result) { + toast.success(t('copySuccess')); + } + }} + > + {/* 按钮的逻辑 */} + + + ); +}; + +export default CopyShortenedLink; diff --git a/apps/user/components/affiliate/index.tsx b/apps/user/components/affiliate/index.tsx index d3ab0d8..aafc598 100644 --- a/apps/user/components/affiliate/index.tsx +++ b/apps/user/components/affiliate/index.tsx @@ -4,8 +4,8 @@ import { AffiliateDialog, AffiliateDialogRef, } from '@/components/affiliate/components/AffiliateDialog'; +import CopyShortenedLink from '@/components/CopyShortenedLink/CopyShortenedLink'; import { Display } from '@/components/display'; -import SvgIcon from '@/components/SvgIcon'; import useGlobalStore from '@/config/use-global'; import { querySubscribeList } from '@/services/user/subscribe'; import { queryUserAffiliate, queryUserAffiliateList } from '@/services/user/user'; @@ -17,8 +17,6 @@ import { default as Airo_Empty } from '@workspace/airo-ui/custom-components/empt import { formatDate } from '@workspace/airo-ui/utils'; import { useTranslations } from 'next-intl'; import { useRef, useState } from 'react'; -import { CopyToClipboard } from 'react-copy-to-clipboard'; -import { toast } from 'sonner'; export default function Affiliate() { const t = useTranslations('affiliate'); @@ -44,11 +42,11 @@ export default function Affiliate() { }); const { data: proPlan } = useQuery({ - queryKey: ['querySubscribeList1'], + queryKey: ['querySubscribeList'], queryFn: async () => { const { data } = await querySubscribeList(); const list = data.data?.list?.filter((v) => v.unit_time === 'Month') || []; - return list.find((v) => v.name.includes('Pro')); + return list.find((v) => v.name.includes('Pro')) ?? []; }, }); @@ -83,41 +81,11 @@ export default function Affiliate() {

{t('commissionInviteCode')} - { - if (result) { - toast.success(t('copySuccess')); - } - }} - > - - +

{user?.refer_code} - { - if (result) { - toast.success(t('copySuccess')); - } - }} - > - - +

diff --git a/apps/user/components/main/HomeContent.tsx b/apps/user/components/main/HomeContent.tsx index 7be5e39..b4fd029 100644 --- a/apps/user/components/main/HomeContent.tsx +++ b/apps/user/components/main/HomeContent.tsx @@ -1,7 +1,7 @@ 'use client'; import { useTranslations } from 'next-intl'; -import OfferDialog from './OfferDialog/index'; +// import OfferDialog from './OfferDialog/index'; export default function HomeContent() { const t = useTranslations('components.home'); @@ -17,7 +17,7 @@ export default function HomeContent() { {t('anywhere')} {/* 副标题 */} -
+

AiroPort @@ -27,7 +27,7 @@ export default function HomeContent() {

{t('getSubscription')}

{/* 按钮 */} - + {/**/}
); } diff --git a/apps/user/instrumentation-client.ts b/apps/user/instrumentation-client.ts index 918daa0..6460073 100644 --- a/apps/user/instrumentation-client.ts +++ b/apps/user/instrumentation-client.ts @@ -3,6 +3,7 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/ import * as Sentry from '@sentry/nextjs'; +const isProduction = process.env.NODE_ENV === 'production'; Sentry.init({ dsn: 'https://e519096f8b71cba99d86ddc46d8e424f@o4509950153719808.ingest.us.sentry.io/4509950194679808', @@ -20,6 +21,8 @@ Sentry.init({ // in development and sample at a lower rate in production replaysSessionSampleRate: 0.1, + enabled: isProduction, + // Define how likely Replay events are sampled when an error occurs. replaysOnErrorSampleRate: 1.0, diff --git a/apps/user/locales/en-US/auth.json b/apps/user/locales/en-US/auth.json index ffb5775..9a61957 100644 --- a/apps/user/locales/en-US/auth.json +++ b/apps/user/locales/en-US/auth.json @@ -28,6 +28,7 @@ "description": "Create a new account, fill in your information to register.", "email": "Please enter a valid email address.", "existingAccount": "Already have an account?", + "sendCode": "The verification code was sent successfully. If you did not receive it, please check your spam folder.", "get": "Get", "invite": "Invitation code", "message": "#### Dear User, Hello!\n\nThank you for your attention and support. Due to adjustments in site operation strategy, we have closed the new user registration function. During this period, existing users will not be affected.\n\nWe are committed to providing you with better service and experience, so we will conduct comprehensive system optimization and feature upgrades during the registration closure. In the future, we will welcome you with better content and services.\n\nPlease follow our website and social media platforms to get the latest updates and notifications. Thank you for your understanding and support.\n\nIf you have any questions or need assistance, please feel free to contact our customer service team.\n\n**Thank you again for your support and understanding.**", diff --git a/apps/user/locales/zh-CN/auth.json b/apps/user/locales/zh-CN/auth.json index 24e2697..776917f 100644 --- a/apps/user/locales/zh-CN/auth.json +++ b/apps/user/locales/zh-CN/auth.json @@ -35,7 +35,8 @@ "success": "注册成功,已自动登录!", "switchToLogin": "登录", "title": "注册", - "whitelist": "电子邮件域名不在允许的白名单中。" + "whitelist": "电子邮件域名不在允许的白名单中。", + "sendCode": "验证码发送成功,如未收到请检查垃圾邮件" }, "reset": { "description": "请输入您的电子邮件地址以重置密码。", diff --git a/apps/user/sentry.edge.config.ts b/apps/user/sentry.edge.config.ts index b73d5bd..c749bf5 100644 --- a/apps/user/sentry.edge.config.ts +++ b/apps/user/sentry.edge.config.ts @@ -4,6 +4,7 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/ import * as Sentry from '@sentry/nextjs'; +const isProduction = process.env.NODE_ENV === 'production'; Sentry.init({ dsn: 'https://e519096f8b71cba99d86ddc46d8e424f@o4509950153719808.ingest.us.sentry.io/4509950194679808', @@ -14,6 +15,8 @@ Sentry.init({ // Enable logs to be sent to Sentry enableLogs: true, + enabled: isProduction, + // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, }); diff --git a/apps/user/sentry.server.config.ts b/apps/user/sentry.server.config.ts index 163cd54..a3a5405 100644 --- a/apps/user/sentry.server.config.ts +++ b/apps/user/sentry.server.config.ts @@ -4,6 +4,8 @@ import * as Sentry from '@sentry/nextjs'; +const isProduction = process.env.NODE_ENV === 'production'; + Sentry.init({ dsn: 'https://e519096f8b71cba99d86ddc46d8e424f@o4509950153719808.ingest.us.sentry.io/4509950194679808', @@ -13,6 +15,8 @@ Sentry.init({ // Enable logs to be sent to Sentry enableLogs: true, + enabled: isProduction, + // Setting this option to true will print useful information to the console while you're setting up Sentry. debug: false, });