forked from 77media/video-flow
188 lines
5.2 KiB
TypeScript
188 lines
5.2 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
||
import type { OAuthCallbackParams } from '@/app/types/google-oauth';
|
||
|
||
/**
|
||
* Google OAuth回调处理API
|
||
* 处理从Google OAuth返回的授权码,完成用户认证
|
||
*/
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
const body = await request.json();
|
||
const { code, state, inviteCode } = body;
|
||
|
||
// 验证必需参数
|
||
if (!code || !state) {
|
||
return NextResponse.json(
|
||
{
|
||
success: false,
|
||
message: 'Missing required parameters: code and state'
|
||
},
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
// 解析state参数
|
||
let stateData: any = {};
|
||
try {
|
||
stateData = JSON.parse(state);
|
||
} catch (e) {
|
||
console.warn('无法解析state参数:', state);
|
||
return NextResponse.json(
|
||
{
|
||
success: false,
|
||
message: 'Invalid state parameter'
|
||
},
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
console.log('Google OAuth回调处理开始:', {
|
||
codeLength: code.length,
|
||
stateData,
|
||
inviteCode
|
||
});
|
||
|
||
// 第一步:使用authorization code向Google换取access token和id_token
|
||
const googleClientId = '847079918888-o1nne8d3ij80dn20qurivo987pv07225.apps.googleusercontent.com';
|
||
const googleClientSecret = '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'; // 需要设置环境变量
|
||
|
||
if (!googleClientSecret) {
|
||
console.error('Google Client Secret未配置');
|
||
return NextResponse.json(
|
||
{
|
||
success: false,
|
||
message: 'Google Client Secret not configured'
|
||
},
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
|
||
const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/x-www-form-urlencoded',
|
||
},
|
||
body: new URLSearchParams({
|
||
code,
|
||
client_id: googleClientId,
|
||
client_secret: googleClientSecret,
|
||
redirect_uri: getRedirectUri(request),
|
||
grant_type: 'authorization_code',
|
||
}),
|
||
});
|
||
|
||
if (!tokenResponse.ok) {
|
||
const errorText = await tokenResponse.text();
|
||
console.error('Google token exchange failed:', errorText);
|
||
return NextResponse.json(
|
||
{
|
||
success: false,
|
||
message: 'Failed to exchange authorization code for tokens'
|
||
},
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
const tokenData = await tokenResponse.json();
|
||
const { id_token } = tokenData;
|
||
|
||
if (!id_token) {
|
||
console.error('No id_token received from Google');
|
||
return NextResponse.json(
|
||
{
|
||
success: false,
|
||
message: 'No id_token received from Google'
|
||
},
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
// 第二步:使用id_token调用Java后端
|
||
const javaBaseUrl = process.env.NEXT_PUBLIC_JAVA_URL || 'https://77.app.java.auth.qikongjian.com';
|
||
|
||
const backendResponse = await fetch(`${javaBaseUrl}/api/auth/google/login`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Accept': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
idToken: id_token, // 使用从Google获取的id_token
|
||
action: 'auto', // 自动判断登录或注册
|
||
inviteCode: inviteCode || stateData.inviteCode || undefined
|
||
})
|
||
});
|
||
|
||
const backendResult = await backendResponse.json();
|
||
|
||
if (!backendResponse.ok || !backendResult.success) {
|
||
console.error('Java后端处理Google OAuth失败:', backendResult);
|
||
return NextResponse.json(
|
||
{
|
||
success: false,
|
||
message: backendResult.message || 'Google authentication failed'
|
||
},
|
||
{ status: backendResponse.status || 500 }
|
||
);
|
||
}
|
||
|
||
console.log('Google OAuth认证成功:', {
|
||
userId: backendResult.data?.user?.userId,
|
||
email: backendResult.data?.user?.email
|
||
});
|
||
|
||
// 返回成功结果
|
||
return NextResponse.json({
|
||
success: true,
|
||
data: {
|
||
token: backendResult.data.token,
|
||
user: backendResult.data.userInfo || backendResult.data.user,
|
||
message: 'Google authentication successful'
|
||
}
|
||
});
|
||
|
||
} catch (error: any) {
|
||
console.error('Google OAuth回调处理错误:', error);
|
||
|
||
return NextResponse.json(
|
||
{
|
||
success: false,
|
||
message: error.message || 'Internal server error during Google OAuth callback'
|
||
},
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据请求获取正确的redirect_uri
|
||
*/
|
||
function getRedirectUri(request: NextRequest): string {
|
||
const host = request.headers.get('host') || '';
|
||
const protocol = request.headers.get('x-forwarded-proto') || 'https';
|
||
|
||
if (host.includes('localhost') || host.includes('127.0.0.1')) {
|
||
return `${protocol}://${host}/users/oauth/callback`;
|
||
} else if (host.includes('movieflow.net')) {
|
||
return 'https://www.movieflow.net/users/oauth/callback';
|
||
} else if (host.includes('movieflow.ai')) {
|
||
return 'https://www.movieflow.ai/users/oauth/callback';
|
||
} else {
|
||
// 默认使用生产环境
|
||
return 'https://www.movieflow.ai/users/oauth/callback';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理GET请求(如果需要)
|
||
*/
|
||
export async function GET(request: NextRequest) {
|
||
return NextResponse.json(
|
||
{
|
||
success: false,
|
||
message: 'This endpoint only accepts POST requests'
|
||
},
|
||
{ status: 405 }
|
||
);
|
||
}
|