diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index 09ecd12..90f49f0 100644 --- a/app/api/auth/[...nextauth]/route.ts +++ b/app/api/auth/[...nextauth]/route.ts @@ -1,7 +1,7 @@ import { handlers } from "@/app/auth" import { NextResponse } from 'next/server' import { NextRequest } from 'next/server' -import { getProxyConfig } from "@/proxy-config" +import { getProxyConfig, disableProxy } from "@/proxy-config" import { HttpsProxyAgent } from "https-proxy-agent" const TIMEOUT_DURATION = 60000; @@ -12,24 +12,63 @@ interface ExtendedRequestInit extends RequestInit { timeout?: number; } -// 定义超时 Promise 的类型 -type TimeoutPromise = Promise - export async function GET(request: NextRequest): Promise { try { const { isEnabled, httpsAgent } = getProxyConfig(); + const nextauthUrl = process.env.NEXTAUTH_URL || ''; + + // 检查是否是回调请求 + if (request.url.includes('/api/auth/callback')) { + console.log('Callback request detected:', request.url); + + // 处理请求 + const response = await handlers.GET(request); + + // 如果是成功的回调(通常是 302 重定向) + if (response instanceof Response && response.status === 302) { + const location = response.headers.get('location'); + + // 检查重定向 URL 是否匹配 NEXTAUTH_URL + if (location && ( + location === nextauthUrl || + location.startsWith(nextauthUrl + '/') || + location === '/' || + !location.includes('/api/auth') + )) { + console.log('Redirecting to app URL, disabling proxy:', location); + // 在响应发送后禁用代理 + setTimeout(() => { + disableProxy(); + }, 100); + } + } + + return response instanceof Response ? response : NextResponse.json(response); + } - // 只在认证请求时使用代理 + // 处理其他认证请求 if (isEnabled && request.url.includes('/api/auth')) { - console.log('originalFetch!!!!!!!!!!:', global.fetch); + console.log('Auth request with proxy:', request.url); const originalFetch = global.fetch; + global.fetch = async (input: RequestInfo | URL, init?: RequestInit) => { - const extendedInit: ExtendedRequestInit = { - ...init, - agent: httpsAgent, - timeout: TIMEOUT_DURATION, - }; - return originalFetch(input, extendedInit); + const inputStr = typeof input === 'string' ? input : input.toString(); + + // 只对 GitHub OAuth 相关 URL 使用代理 + if (inputStr.includes('github.com/login/oauth') || + inputStr.includes('api.github.com/user')) { + + console.log('Using proxy for fetch request:', inputStr); + const extendedInit: ExtendedRequestInit = { + ...init, + agent: httpsAgent, + timeout: TIMEOUT_DURATION, + }; + return originalFetch(input, extendedInit); + } else { + // 其他请求不使用代理 + return originalFetch(input, init); + } }; try { @@ -40,7 +79,7 @@ export async function GET(request: NextRequest): Promise { global.fetch = originalFetch; } } else { - // 非认证请求使用普通 fetch + // 非认证请求或代理未启用 const response = await handlers.GET(request); return response instanceof Response ? response : NextResponse.json(response); } @@ -59,17 +98,72 @@ export async function GET(request: NextRequest): Promise { export async function POST(request: NextRequest): Promise { try { - const { isEnabled } = getProxyConfig(); - - if (isEnabled) { - // 使用超时机制 - const timeoutPromise: TimeoutPromise = new Promise((_, reject) => - setTimeout(() => reject(new Error('Request timeout')), TIMEOUT_DURATION) - ); - const response = await Promise.race([handlers.POST(request), timeoutPromise]); + const { isEnabled, httpsAgent } = getProxyConfig(); + const nextauthUrl = process.env.NEXTAUTH_URL || ''; + + // 检查是否是回调请求 + if (request.url.includes('/api/auth/callback')) { + console.log('Callback POST request detected:', request.url); + + // 处理请求 + const response = await handlers.POST(request); + + // 如果是成功的回调(通常是 302 重定向) + if (response instanceof Response && response.status === 302) { + const location = response.headers.get('location'); + + // 检查重定向 URL 是否匹配 NEXTAUTH_URL + if (location && ( + location === nextauthUrl || + location.startsWith(nextauthUrl + '/') || + location === '/' || + !location.includes('/api/auth') + )) { + console.log('POST: Redirecting to app URL, disabling proxy:', location); + // 在响应发送后禁用代理 + setTimeout(() => { + disableProxy(); + }, 100); + } + } + return response instanceof Response ? response : NextResponse.json(response); + } + + // 处理其他认证请求 + if (isEnabled && request.url.includes('/api/auth')) { + console.log('Auth POST request with proxy:', request.url); + const originalFetch = global.fetch; + + global.fetch = async (input: RequestInfo | URL, init?: RequestInit) => { + const inputStr = typeof input === 'string' ? input : input.toString(); + + // 只对 GitHub OAuth 相关 URL 使用代理 + if (inputStr.includes('github.com/login/oauth') || + inputStr.includes('api.github.com/user')) { + + console.log('Using proxy for POST fetch request:', inputStr); + const extendedInit: ExtendedRequestInit = { + ...init, + agent: httpsAgent, + timeout: TIMEOUT_DURATION, + }; + return originalFetch(input, extendedInit); + } else { + // 其他请求不使用代理 + return originalFetch(input, init); + } + }; + + try { + const response = await handlers.POST(request); + return response instanceof Response ? response : NextResponse.json(response); + } finally { + // 请求完成后恢复原始的 fetch + global.fetch = originalFetch; + } } else { - // 不使用超时机制 + // 非认证请求或代理未启用 const response = await handlers.POST(request); return response instanceof Response ? response : NextResponse.json(response); } diff --git a/app/api/auth/enable-proxy/route.tsx b/app/api/auth/enable-proxy/route.tsx new file mode 100644 index 0000000..aabdacf --- /dev/null +++ b/app/api/auth/enable-proxy/route.tsx @@ -0,0 +1,17 @@ +import { NextResponse } from 'next/server'; +import { enableProxy } from "@/proxy-config"; + +export async function POST() { + try { + const config = enableProxy(); + return NextResponse.json({ + success: true, + proxyEnabled: config.isEnabled + }); + } catch (error) { + return NextResponse.json({ + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }, { status: 500 }); + } +} \ No newline at end of file diff --git a/app/api/proxy-image/route.tsx b/app/api/proxy-image/route.tsx new file mode 100644 index 0000000..d4fd913 --- /dev/null +++ b/app/api/proxy-image/route.tsx @@ -0,0 +1,40 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getProxyConfig } from '@/proxy-config'; + +export async function GET(request: NextRequest) { + try { + const url = request.nextUrl.searchParams.get('url'); + + if (!url) { + return new NextResponse('Missing URL parameter', { status: 400 }); + } + + const { isEnabled, httpsAgent } = getProxyConfig(); + + const fetchOptions: RequestInit = {}; + if (isEnabled && url.includes('githubusercontent.com')) { + // 使用 @ts-expect-error 代替 @ts-ignore + // @ts-expect-error - agent 属性在浏览器端 RequestInit 类型中不存在 + fetchOptions.agent = httpsAgent; + } + + const response = await fetch(url, fetchOptions); + + if (!response.ok) { + return new NextResponse('Failed to fetch image', { status: response.status }); + } + + const buffer = await response.arrayBuffer(); + const headers = new Headers(); + headers.set('Content-Type', response.headers.get('Content-Type') || 'image/jpeg'); + headers.set('Cache-Control', 'public, max-age=86400'); + + return new NextResponse(buffer, { + status: 200, + headers + }); + } catch (error) { + console.error('Error proxying image:', error); + return new NextResponse('Error fetching image', { status: 500 }); + } +} \ No newline at end of file diff --git a/app/auth.ts b/app/auth.ts index b54e3c4..f72c2b3 100644 --- a/app/auth.ts +++ b/app/auth.ts @@ -1,6 +1,9 @@ import NextAuth from "next-auth" import GitHub from "next-auth/providers/github" -// import { getProxyConfig } from "@/proxy-config" +import { setupGlobalFetch } from "@/proxy-config" + +// 设置全局 fetch 拦截器(但不启用代理) +setupGlobalFetch(); // 打印环境变量(开发环境调试用) console.log('Environment Config:', { @@ -31,32 +34,34 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ debug: true, callbacks: { async signIn({ user, account, profile, email, credentials }) { - console.log('Sign in attempt:', { user, account, profile, email, credentials }); - + console.log('signIn!!!!!!!!!!:', user, account, profile, email, credentials); + console.log('Sign in callback executed'); return true; }, async redirect({ url, baseUrl }) { - console.log('Redirect:', { url, baseUrl }); + + console.log('redirect!!!!!!!!!!:', url, baseUrl); return baseUrl; }, async session({ session, user, token }) { - console.log('Session:', { session, user, token }); + console.log('session!!!!!!!!!!:', session, user, token); return session; }, async jwt({ token, user, account, profile }) { - console.log('JWT:', { token, user, account, profile }); + console.log('jwt!!!!!!!!!!:', token, user, account, profile); return token; } }, events: { - async signIn(message) { console.log('signIn:', message) }, - async signOut(message) { console.log('signOut:', message) }, + async signIn(message) { + console.log('signIn event:', message); + }, + async signOut(message) { + console.log('signOut:', message); + }, }, pages: { signIn: '/auth/signin', error: '/auth/error', } -}) - - - +}) \ No newline at end of file diff --git a/components/sign-in.tsx b/components/sign-in.tsx index b04e578..6160670 100644 --- a/components/sign-in.tsx +++ b/components/sign-in.tsx @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { signIn, signOut, useSession } from 'next-auth/react'; -import Image from 'next/image'; +// import Image from 'next/image'; import Link from 'next/link'; import { Dropdown } from 'antd'; import type { MenuProps } from 'antd'; @@ -32,6 +32,36 @@ export default function SignInButton() { } }, [session]); // 依赖于 session + // 处理 GitHub 登录 + const handleGitHubSignIn = async () => { + try { + // 先启用代理 + await fetch('/api/auth/enable-proxy', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }); + + console.log('已启用代理用于 GitHub 登录'); + + // 然后进行 GitHub 登录 + await signIn("github"); + } catch (error) { + console.error('登录过程中出错:', error); + } + }; + + // 在组件内部添加一个函数来处理图片 URL + // const getProxiedImageUrl = (imageUrl: string) => { + // if (!imageUrl) return ''; + // // 检查是否是 GitHub 头像 + // if (imageUrl.includes('githubusercontent.com')) { + // return `/api/proxy-image?url=${encodeURIComponent(imageUrl)}`; + // } + // return imageUrl; + // }; + if (status === 'loading') { return (