fix(env): fix env

This commit is contained in:
zhangheng
2026-07-14 15:21:28 +08:00
parent d0234effd6
commit c6ab525140
8 changed files with 269 additions and 109 deletions

View File

@@ -1,65 +1,138 @@
import { NextResponse } from 'next/server'
import { NextResponse } from "next/server";
/**
* SSO 回调接口
* 接收 sso_code调用 Mock SSO 验证接口换取用户信息,
* 然后将用户信息写入 Cookie。
*
* OAuth2 授权码流程:用一次性授权 code 向真实后端换取 access_token
* 然后将 token / 用户信息写入 Cookie 建立本地会话。
*
* ⚠️ client_secret 仅在服务端 envSSO_CLIENT_SECRET绝不下发浏览器。
*/
const BACKEND = (
process.env.SSO_BACKEND_API || "http://172.16.115.31:6610"
).replace(/\/+$/, "");
export async function POST(req: Request) {
try {
const { code } = await req.json()
const { code } = await req.json();
if (!code) {
return NextResponse.json({ error: '缺少 code 参数' }, { status: 400 })
return NextResponse.json({ error: "缺少 code 参数" }, { status: 400 });
}
// 调用 Mock SSO 的验证接口
const ssoUrl = process.env.SSO_URL || 'http://localhost:5501'
const verifyRes = await fetch(`${ssoUrl}/api/auth/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code }),
})
const clientId = process.env.NEXT_PUBLIC_SSO_APP_ID;
const clientSecret = process.env.SSO_CLIENT_SECRET;
const tenant = process.env.SSO_TENANT || "cowarobot";
if (!verifyRes.ok) {
const errData = await verifyRes.json().catch(() => ({}))
if (!clientId || !clientSecret) {
console.error(
"[external-app] 缺少 NEXT_PUBLIC_SSO_APP_ID 或 SSO_CLIENT_SECRET",
);
return NextResponse.json(
{ error: errData.error || 'SSO 验证失败' },
{ status: 401 }
)
{ error: "服务端 SSO 配置缺失" },
{ status: 500 },
);
}
const ssoData = await verifyRes.json()
const { token, refresh_token, user, permissions } = ssoData
// 用授权 code 换 access_token标准 OAuth2
const tokenRes = await fetch(`${BACKEND}/api/v1/basis/sso/access_token`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
client_id: clientId,
client_secret: clientSecret,
code,
grant_type: "authorization_code",
tenant,
}),
});
// 将用户信息和 token 写入 Cookie
const response = NextResponse.json({ success: true })
const data = await tokenRes.json().catch(() => ({}));
console.log(
"[external-app] access_token 响应 status:",
tokenRes.status,
"| body:",
JSON.stringify(data).slice(0, 500),
);
response.cookies.set('user', JSON.stringify(user), {
httpOnly: false, // 客户端需要读取用于展示
path: '/',
maxAge: 86400,
})
if (!tokenRes.ok) {
const message = data?.message || data?.msg || "换 token 失败";
console.error(
"[external-app] access_token error:",
tokenRes.status,
message,
);
return NextResponse.json({ error: message }, { status: 401 });
}
response.cookies.set('token', token, {
// 兼容平铺 / 外壳两种响应;字段名兼容 user / user_infobasis 习惯用 user_info
const token = data?.access_token ?? data?.data?.access_token;
const refreshToken = data?.refresh_token ?? data?.data?.refresh_token;
if (!token) {
console.error(
"[external-app] access_token 响应中无 token:",
JSON.stringify(data).slice(0, 300),
);
return NextResponse.json(
{ error: "后端未返回 access_token" },
{ status: 502 },
);
}
const infoRes = await fetch(
`${BACKEND}/api/v1/basis/sso/user_info?access_token=${token}`,
{
method: "GET",
},
);
const infoData = await infoRes.json().catch(() => ({}));
const user = infoData?.data;
console.log(
"[external-app] 提取 → token:",
!!token,
"| refresh_token:",
!!refreshToken,
"| user:",
!!user,
user
? `(${Object.keys(user).join(",")})`
: "(无 — 登录态无法建立,首页会跳回 sso-portal)",
);
// 写入 Cookie 建立会话
const response = NextResponse.json({ success: true });
response.cookies.set("token", token, {
httpOnly: true,
path: '/',
path: "/",
maxAge: 86400,
})
});
if (refresh_token) {
response.cookies.set('refresh_token', refresh_token, {
if (refreshToken) {
response.cookies.set("refresh_token", refreshToken, {
httpOnly: true,
path: '/',
path: "/",
maxAge: 86400 * 7,
})
});
}
return response
if (user) {
response.cookies.set("user", JSON.stringify(user), {
httpOnly: false, // 客户端需要读取用于展示
path: "/",
maxAge: 86400,
});
}
return response;
} catch (err: any) {
console.error("[external-app] callback error:", err?.message);
return NextResponse.json(
{ error: err?.message || '回调处理失败' },
{ status: 500 }
)
{ error: err?.message || "回调处理失败" },
{ status: 500 },
);
}
}