74 lines
2.7 KiB
TypeScript
74 lines
2.7 KiB
TypeScript
import { NextResponse, type NextRequest } from 'next/server'
|
|
import { parseDingtalkOAuthState } from '@/lib/dingtalk-oauth'
|
|
import { getPublicAppOrigin } from '@/lib/public-app-url'
|
|
import { buildDingtalkAutoLoginPath } from '@/lib/safe-redirect'
|
|
|
|
export const runtime = 'nodejs'
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
function appendLoginSetCookie(target: NextResponse, source: Response) {
|
|
const getSetCookie = (source.headers as any).getSetCookie
|
|
const cookies = typeof getSetCookie === 'function'
|
|
? getSetCookie.call(source.headers)
|
|
: [source.headers.get('set-cookie')].filter(Boolean)
|
|
|
|
for (const cookie of cookies) {
|
|
target.headers.append('set-cookie', cookie)
|
|
}
|
|
}
|
|
|
|
function getForwardedHeaders(request: NextRequest) {
|
|
const publicUrl = new URL(getPublicAppOrigin())
|
|
return {
|
|
'x-forwarded-proto': publicUrl.protocol.replace(':', ''),
|
|
'x-forwarded-host': publicUrl.host,
|
|
'x-real-ip': request.headers.get('x-real-ip') || '',
|
|
'x-forwarded-for': request.headers.get('x-forwarded-for') || '',
|
|
}
|
|
}
|
|
|
|
function getInternalLoginUrl() {
|
|
const port = process.env.PORT || '3000'
|
|
return `http://127.0.0.1:${port}/api/dingtalk/login`
|
|
}
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const code = request.nextUrl.searchParams.get('code') || request.nextUrl.searchParams.get('authCode')
|
|
const { redirectPath } = parseDingtalkOAuthState(request.nextUrl.searchParams.get('state'))
|
|
const publicOrigin = getPublicAppOrigin()
|
|
|
|
if (!code) {
|
|
return NextResponse.redirect(new URL(buildDingtalkAutoLoginPath(redirectPath), publicOrigin))
|
|
}
|
|
|
|
try {
|
|
const loginResponse = await fetch(getInternalLoginUrl(), {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...getForwardedHeaders(request),
|
|
},
|
|
body: JSON.stringify({ code, state: request.nextUrl.searchParams.get('state') || '' }),
|
|
cache: 'no-store',
|
|
})
|
|
const data = await loginResponse.json().catch(() => null)
|
|
|
|
if (!loginResponse.ok || !data?.success || data.needCompleteProfile) {
|
|
const loginPath = buildDingtalkAutoLoginPath(redirectPath)
|
|
const url = new URL(loginPath, publicOrigin)
|
|
if (data?.message) {
|
|
url.searchParams.set('error', data.message)
|
|
}
|
|
return NextResponse.redirect(url)
|
|
}
|
|
|
|
const redirectResponse = NextResponse.redirect(new URL(redirectPath, publicOrigin))
|
|
appendLoginSetCookie(redirectResponse, loginResponse)
|
|
redirectResponse.headers.set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
|
return redirectResponse
|
|
} catch (error) {
|
|
console.error('[Dingtalk] 服务端授权回调失败:', error)
|
|
return NextResponse.redirect(new URL(buildDingtalkAutoLoginPath(redirectPath), publicOrigin))
|
|
}
|
|
}
|