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))
}
}