From 12ec2ad7bd819e27710b76fdc5be1ba70354eaf1 Mon Sep 17 00:00:00 2001 From: yorn <1057707203@qq.com> Date: Tue, 11 Nov 2025 14:25:44 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AE=8C=E5=96=84=E5=8D=95=E7=82=B9?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E4=BC=A0=E9=80=92=E5=9B=9E=E8=B0=83=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E5=92=8CserverUrl=E7=9A=84=E5=8A=9F=E8=83=BD=E3=80=82?= =?UTF-8?q?=E4=BC=98=E5=8C=96token=E5=88=B7=E6=96=B0=E6=9C=BA=E5=88=B6?= =?UTF-8?q?=EF=BC=8C=E5=88=A4=E6=96=AD=E5=8D=95=E7=82=B9=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=92=8C=E7=AE=A1=E7=90=86=E5=91=98=E7=99=BB=E5=BD=95=E7=AD=89?= =?UTF-8?q?=E7=AD=89=E4=B8=8D=E5=90=8C=E8=B7=AF=E5=BE=84=E7=9A=84=E5=A4=84?= =?UTF-8?q?=E7=90=86=E6=9C=BA=E5=88=B6=E3=80=82=E6=8F=90=E7=A4=BA=E8=AF=8D?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=9A=84=E6=A8=A1=E6=9D=BF=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=9F=A5=E6=89=BE=E7=9A=84=E6=97=B6=E5=80=99=E5=8F=AA=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E8=BF=94=E5=9B=9E=E5=9B=BA=E5=AE=9A=E7=9A=845?= =?UTF-8?q?=E4=B8=AA=E7=B1=BB=E5=9E=8B=E3=80=82=E9=9A=90=E8=97=8F=E8=AF=84?= =?UTF-8?q?=E6=9F=A5=E7=82=B9=E8=AE=BE=E7=BD=AE=E4=B8=AD=E5=85=B3=E4=BA=8E?= =?UTF-8?q?=E6=8A=BD=E5=8F=96=E7=9A=84=E8=87=AA=E5=AE=9A=E4=B9=89=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E7=9A=84=E9=80=89=E6=8B=A9=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/login/auth.server.ts | 59 ++++++++++------ app/api/login/oauth-client.ts | 108 ++++++++++++++++++++++++------ app/api/prompts/prompts.ts | 3 + app/config/oauth-secret.server.ts | 39 ++++++----- app/models/evaluation_points.ts | 4 +- app/routes/callback.tsx | 62 +++++++++++++++-- app/routes/login.tsx | 12 ++-- app/routes/logout.tsx | 30 +++++---- app/routes/prompts._index.tsx | 2 +- app/routes/rules-new.tsx | 2 +- vite.config.ts | 2 +- 11 files changed, 238 insertions(+), 85 deletions(-) diff --git a/app/api/login/auth.server.ts b/app/api/login/auth.server.ts index de06ca1..45105c5 100644 --- a/app/api/login/auth.server.ts +++ b/app/api/login/auth.server.ts @@ -196,13 +196,16 @@ export async function getUserSession(request: Request) { let tokenExpiresIn = session.get("tokenExpiresIn"); const userInfo = session.get("userInfo"); let frontendJWT = session.get("frontendJWT"); - + let isTokenExpired = false; let refreshedSession = null; let shouldRegenerateJWT = false; - - // 如果有token信息,检查是否需要刷新 - if (accessToken && refreshToken && tokenIssuedAt && tokenExpiresIn) { + + // 🔑 admin 用户不需要刷新 OAuth token,只需要维护 JWT + const isAdmin = userRole === 'admin'; + + // 如果有token信息,检查是否需要刷新(admin用户跳过OAuth token刷新) + if (!isAdmin && accessToken && refreshToken && tokenIssuedAt && tokenExpiresIn) { try { const tokenInfo = { accessToken, @@ -213,30 +216,30 @@ export async function getUserSession(request: Request) { // 检查并自动刷新token const refreshResult = await tokenManager.checkAndRefreshToken(tokenInfo); - + if (refreshResult.success && refreshResult.newTokenInfo) { const newToken = refreshResult.newTokenInfo; - + // 如果token被刷新了,更新session if (newToken.accessToken !== accessToken) { console.log("Token已刷新,更新session"); - + session.set("accessToken", newToken.accessToken); session.set("refreshToken", newToken.refreshToken); session.set("tokenIssuedAt", newToken.tokenIssuedAt); session.set("tokenExpiresIn", newToken.tokenExpiresIn); - + // 更新本地变量 accessToken = newToken.accessToken; tokenIssuedAt = newToken.tokenIssuedAt; tokenExpiresIn = newToken.tokenExpiresIn; - + // 标记需要重新生成JWT shouldRegenerateJWT = true; - + refreshedSession = session; } - + isTokenExpired = false; } else { console.error("Token刷新失败:", refreshResult.error); @@ -246,6 +249,18 @@ export async function getUserSession(request: Request) { console.error("Token验证过程中出错:", error); isTokenExpired = true; } + } else if (isAdmin) { + // admin 用户:不检查 OAuth token 过期,始终保持登录状态 + // console.log("admin 用户登录,跳过 OAuth token 刷新"); + isTokenExpired = false; + + // admin 用户需要有一个合理的 tokenExpiresIn 用于 JWT 生成 + // 如果没有设置,使用一个默认值(如2小时) + if (!tokenExpiresIn) { + tokenExpiresIn = 7200; // 2小时 + session.set("tokenExpiresIn", tokenExpiresIn); + refreshedSession = session; + } } // 检查前端JWT状态 @@ -458,11 +473,11 @@ export async function createSimpleUserSession(isAuthenticated: boolean, userRole */ export async function logout(request: Request) { const session = await getSession(request); - + // 获取访问令牌和应用ID,用于调用IDaaS单点登出 const accessToken = session.get("accessToken"); - const appId = OAUTH_CONFIG.appId; - + const appId = OAUTH_CONFIG.appId || 'idaasoauth2'; + // 如果存在访问令牌,调用IDaaS单点登出 if (accessToken && appId) { try { @@ -485,18 +500,20 @@ export async function logout(request: Request) { /** * 调用IDaaS单点登出接口 - * + * * @param accessToken - 用户的访问令牌 * @param appId - 应用ID * @returns Promise */ async function callIDaaSLogout(accessToken: string, appId: string): Promise { - const logoutUrl = `${OAUTH_CONFIG.serverUrl}/public/sp/slo/${appId}`; - + const serverUrl = OAUTH_CONFIG.serverUrl || 'http://10.79.112.85'; + const redirectUri = OAUTH_CONFIG.redirectUri || 'http://10.79.97.17/'; + const logoutUrl = `${serverUrl}/public/sp/slo/${appId}`; + const formData = new URLSearchParams(); formData.append('access_token', accessToken); - formData.append('redirect_url', encodeURIComponent(OAUTH_CONFIG.redirectUri)); - + formData.append('redirect_url', encodeURIComponent(redirectUri)); + try { const response = await fetch(logoutUrl, { method: 'POST', @@ -505,11 +522,11 @@ async function callIDaaSLogout(accessToken: string, appId: string): Promise { - console.log('🔧 OAuth配置信息:', { - serverUrl: this.config.serverUrl, - clientId: this.config.clientId, - redirectUri: this.config.redirectUri - }); - const url = `${this.config.serverUrl}/oauth/token`; const data = new URLSearchParams({ grant_type: 'authorization_code', code: code, client_id: this.config.clientId, - client_secret: this.config.clientSecret, + client_secret: this.config.clientSecret || '', // 提供默认值避免类型错误 redirect_uri: this.config.redirectUri }); @@ -92,18 +107,27 @@ export class OAuthClient { grant_type: 'authorization_code', code: code, client_id: this.config.clientId, - redirect_uri: this.config.redirectUri + redirect_uri: this.config.redirectUri, + // 仅在服务器端打印 client_secret 状态 + hasClientSecret: !!this.config.clientSecret, + clientSecretLength: this.config.clientSecret?.length || 0 }); try { + // 创建 AbortController 用于超时控制 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时 + const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: data + body: data, + signal: controller.signal }); + clearTimeout(timeoutId); console.log('🔧 Token响应状态:', response.status, response.statusText); if (!response.ok) { @@ -125,7 +149,12 @@ export class OAuthClient { return tokenResponse; } catch (error) { - console.error('❌ 获取访问令牌网络错误:', error); + // 判断是否为超时错误 + if (error instanceof Error && error.name === 'AbortError') { + console.error('❌ 获取访问令牌超时(15秒):', error.message); + } else { + console.error('❌ 获取访问令牌网络错误:', error); + } return null; } } @@ -139,12 +168,19 @@ export class OAuthClient { const url = `${this.config.serverUrl}/api/bff/v1.2/oauth2/userinfo`; try { + // 创建 AbortController 用于超时控制 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时 + const response = await fetch(url, { headers: { 'Authorization': `Bearer ${accessToken}` - } + }, + signal: controller.signal }); + clearTimeout(timeoutId); + if (!response.ok) { console.error('获取用户信息失败:', response.status, response.statusText); return null; @@ -152,7 +188,12 @@ export class OAuthClient { return await response.json() as UserInfoResponse; } catch (error) { - console.error('获取用户信息网络错误:', error); + // 判断是否为超时错误 + if (error instanceof Error && error.name === 'AbortError') { + console.error('❌ 获取用户信息超时(15秒):', error.message); + } else { + console.error('❌ 获取用户信息网络错误:', error); + } return null; } } @@ -168,18 +209,31 @@ export class OAuthClient { grant_type: 'refresh_token', refresh_token: refreshToken, client_id: this.config.clientId, - client_secret: this.config.clientSecret + client_secret: this.config.clientSecret || '' // 提供默认值避免类型错误 + }); + + console.log('🔧 [刷新Token] 请求参数状态:', { + hasClientSecret: !!this.config.clientSecret, + clientSecretLength: this.config.clientSecret?.length || 0, + refreshTokenLength: refreshToken?.length || 0 }); try { + // 创建 AbortController 用于超时控制 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时 + const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: data + body: data, + signal: controller.signal }); + clearTimeout(timeoutId); + if (!response.ok) { const errorData = await response.json(); console.error('刷新访问令牌失败:', errorData); @@ -188,7 +242,12 @@ export class OAuthClient { return await response.json() as TokenResponse; } catch (error) { - console.error('刷新访问令牌网络错误:', error); + // 判断是否为超时错误 + if (error instanceof Error && error.name === 'AbortError') { + console.error('❌ 刷新访问令牌超时(15秒):', error.message); + } else { + console.error('❌ 刷新访问令牌网络错误:', error); + } return null; } } @@ -207,17 +266,28 @@ export class OAuthClient { }); try { + // 创建 AbortController 用于超时控制 + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时 + const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: data + body: data, + signal: controller.signal }); + clearTimeout(timeoutId); return response.ok; } catch (error) { - console.error('登出失败:', error); + // 判断是否为超时错误 + if (error instanceof Error && error.name === 'AbortError') { + console.error('❌ 登出超时(15秒):', error.message); + } else { + console.error('❌ 登出失败:', error); + } return false; } } diff --git a/app/api/prompts/prompts.ts b/app/api/prompts/prompts.ts index 4a49e16..57b72e6 100644 --- a/app/api/prompts/prompts.ts +++ b/app/api/prompts/prompts.ts @@ -131,6 +131,7 @@ export async function getPromptTemplates(searchParams: PromptSearchParams = {}, }> { try { // console.log('获取提示词模板列表,参数:', searchParams); + const TYPE = 'Common,LLM_Extraction,VLM_Extraction,Evaluation,Summary'; const page = searchParams.page || 1; const pageSize = searchParams.pageSize || 10; @@ -157,6 +158,8 @@ export async function getPromptTemplates(searchParams: PromptSearchParams = {}, if (searchParams.type) { filter['template_type'] = `eq.${searchParams.type}`; + }else{ + filter['template_type'] = `in.(${TYPE})`; } if (searchParams.status) { diff --git a/app/config/oauth-secret.server.ts b/app/config/oauth-secret.server.ts index ea7a78f..03a9aa3 100644 --- a/app/config/oauth-secret.server.ts +++ b/app/config/oauth-secret.server.ts @@ -1,10 +1,12 @@ /** * 🔒 服务器端专用:OAuth 敏感配置 - * + * * 此文件只在服务器端运行,确保环境变量在运行时读取 * Remix 会自动排除 .server.ts 文件不打包到客户端 */ +import { OAUTH_CONFIG } from './api-config'; + // 用于控制日志输出(避免重复日志) let hasLoggedSecret = false; @@ -14,17 +16,17 @@ let hasLoggedSecret = false; */ export function getOAuthClientSecret(): string { const secret = process.env.OAUTH_CLIENT_SECRET; - + // 只在第一次调用时输出详细日志(避免启动时就输出) if (!hasLoggedSecret) { hasLoggedSecret = true; - + console.log('🔍 [oauth-secret.server] 读取 OAUTH_CLIENT_SECRET:'); console.log(' - 值存在:', !!secret); console.log(' - 值长度:', secret?.length || 0); console.log(' - 值预览:', secret ? `${secret.substring(0, 10)}...` : 'undefined'); console.log(' - 是否为占位符:', secret === 'placeholder' || secret === 'none'); - + if (!secret || secret === 'placeholder' || secret === 'none') { console.warn('⚠️ 警告:未设置有效的 OAUTH_CLIENT_SECRET 环境变量'); console.warn('⚠️ 当前值:', secret); @@ -34,26 +36,33 @@ export function getOAuthClientSecret(): string { console.log('✅ [oauth-secret.server] OAUTH_CLIENT_SECRET 已成功读取'); } } - + return secret || ''; } /** * 获取服务器端 OAuth 配置 + * 使用 api-config.ts 中根据端口号配置的 OAuth 配置 + * 只有 clientSecret 从环境变量获取 */ export function getServerOAuthConfigRuntime() { const secret = getOAuthClientSecret(); - - // 从基础配置中获取其他 OAuth 参数 - const baseConfig = { - serverUrl: process.env.NEXT_PUBLIC_OAUTH_SERVER_URL || 'http://10.79.112.85', - clientId: process.env.NEXT_PUBLIC_OAUTH_CLIENT_ID || '54d2a619fe5c81ae1250434c441fccccqMtKwh7H4fO', - redirectUri: process.env.NEXT_PUBLIC_OAUTH_REDIRECT_URI || 'http://10.79.97.17/', - appId: process.env.NEXT_PUBLIC_OAUTH_APP_ID || 'idaasoauth2', - }; - + + // 使用 api-config.ts 中根据端口号配置的 OAuth 配置 + // 只覆盖 clientSecret 为从环境变量读取的值 + console.log('🔧 [oauth-secret.server] 使用端口配置的 OAuth 配置:', { + serverUrl: OAUTH_CONFIG.serverUrl, + clientId: OAUTH_CONFIG.clientId, + redirectUri: OAUTH_CONFIG.redirectUri, + appId: OAUTH_CONFIG.appId, + hasClientSecret: !!secret + }); + return { - ...baseConfig, + serverUrl: OAUTH_CONFIG.serverUrl!, + clientId: OAUTH_CONFIG.clientId!, + redirectUri: OAUTH_CONFIG.redirectUri!, + appId: OAUTH_CONFIG.appId!, clientSecret: secret }; } diff --git a/app/models/evaluation_points.ts b/app/models/evaluation_points.ts index 1e5ad1b..460ead1 100644 --- a/app/models/evaluation_points.ts +++ b/app/models/evaluation_points.ts @@ -233,8 +233,8 @@ export const EVALUATION_OPTIONS = { // llm提示词类型选项 llmPromptTypeOptions: [ - { value: 'llm_default_prompt', label: '使用系统默认提示词' }, - { value: 'custom', label: '使用自定义提示词' } + { value: 'llm_default_prompt', label: '使用系统默认提示词' } + // { value: 'custom', label: '使用自定义提示词' } ], // 逻辑类型选项 diff --git a/app/routes/callback.tsx b/app/routes/callback.tsx index cc03857..3c8a887 100644 --- a/app/routes/callback.tsx +++ b/app/routes/callback.tsx @@ -1,7 +1,7 @@ import { type LoaderFunctionArgs, redirect } from "@remix-run/node"; import { createUserSession, saveUserInfo, sessionStorage } from "~/api/login/auth.server"; import { OAuthClient } from "~/api/login/oauth-client"; -import { OAUTH_CONFIG } from "~/config/api-config"; +import { getServerOAuthConfigRuntime } from "~/config/oauth-secret.server"; import { JWTUtils, type UserInfoForJWT } from "~/utils/jwt"; /** @@ -79,16 +79,20 @@ export async function loader({ request }: LoaderFunctionArgs) { console.log("✅ OAuth2.0回调参数验证通过"); + // 声明在 try 外部,以便在 catch 中访问 + let tokenResponse = null; + const oauthClient = new OAuthClient(getServerOAuthConfigRuntime()); + try { console.log("🔧 开始处理OAuth2.0回调"); - - const oauthClient = new OAuthClient(OAUTH_CONFIG) // 开始获取访问令牌 - const tokenResponse = await oauthClient.getAccessToken(code); + tokenResponse = await oauthClient.getAccessToken(code); if (!tokenResponse) { console.error("获取访问令牌失败"); - return redirect("/login?error=token_error") + // 注意:此时还没有 access_token,无法调用 IDaaS 登出 + // 只能重定向到登录页,让用户重新开始登录流程 + return redirectToLoginWithError(request, "获取访问令牌失败,请重试"); } console.log("✅ [Callback] 访问令牌获取成功"); @@ -96,7 +100,22 @@ export async function loader({ request }: LoaderFunctionArgs) { const userInfo = await oauthClient.getUserInfo(tokenResponse.access_token); if(!userInfo || !userInfo.success){ console.error('获取用户信息失败:',userInfo); - return redirect('/login?error=userinfo_error') + + // 🔑 关键:此时 IDaaS 那边已经登录成功,但我们获取用户信息失败 + // 需要调用 IDaaS 登出,清除 IDaaS 的登录状态,避免用户下次登录时出现问题 + try { + const logoutUrl = `${url.protocol}//${url.host}/login`; + const logoutSuccess = await oauthClient.logout(tokenResponse.access_token, logoutUrl); + if (logoutSuccess) { + console.log("✅ [Callback] 已清除 IDaaS 登录状态"); + } else { + console.warn("⚠️ [Callback] 清除 IDaaS 登录状态失败"); + } + } catch (logoutError) { + console.error("❌ [Callback] 调用 IDaaS 登出时出错:", logoutError); + } + + return redirectToLoginWithError(request, "获取用户信息失败,请重试"); } console.log("✅ [Callback] 用户信息获取成功"); @@ -128,7 +147,20 @@ export async function loader({ request }: LoaderFunctionArgs) { const saveResult = await saveUserInfo(userInfo.data, tempToken, area); if (!saveResult.success) { console.error("保存用户信息到数据库失败:", saveResult.error); - // 注意:即使保存到数据库失败,我们仍然继续登录流程,因为用户已经通过了身份验证 + + // 🔑 保存用户信息失败,需要清除 IDaaS 登录状态 + try { + const logoutUrl = `${url.protocol}//${url.host}/login`; + const logoutSuccess = await oauthClient.logout(tokenResponse.access_token, logoutUrl); + if (logoutSuccess) { + console.log("✅ [Callback] 已清除 IDaaS 登录状态(数据库保存失败)"); + } else { + console.warn("⚠️ [Callback] 清除 IDaaS 登录状态失败(数据库保存失败)"); + } + } catch (logoutError) { + console.error("❌ [Callback] 调用 IDaaS 登出时出错(数据库保存失败):", logoutError); + } + return redirectToLoginWithError(request, "保存用户信息失败,请重新登录"); } @@ -182,6 +214,22 @@ export async function loader({ request }: LoaderFunctionArgs) { } catch (error) { console.error("OAuth2.0回调处理失败:", error); + + // 🔑 如果已经获取到了 access_token,需要清除 IDaaS 登录状态 + if (tokenResponse?.access_token) { + try { + const logoutUrl = `${url.protocol}//${url.host}/login`; + const logoutSuccess = await oauthClient.logout(tokenResponse.access_token, logoutUrl); + if (logoutSuccess) { + console.log("✅ [Callback] 已清除 IDaaS 登录状态(回调处理异常)"); + } else { + console.warn("⚠️ [Callback] 清除 IDaaS 登录状态失败(回调处理异常)"); + } + } catch (logoutError) { + console.error("❌ [Callback] 调用 IDaaS 登出时出错(回调处理异常):", logoutError); + } + } + return redirectToLoginWithError(request, "登录回调处理失败,请重新登录"); } } diff --git a/app/routes/login.tsx b/app/routes/login.tsx index a20a68f..20833a3 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { useActionData, useLoaderData, Form } from "@remix-run/react"; import { type MetaFunction, type LoaderFunctionArgs, type ActionFunctionArgs, redirect } from "@remix-run/node"; import { OAuthClient } from "~/api/login/oauth-client"; -import { OAUTH_CONFIG } from "~/config/api-config"; +import { CLIENT_OAUTH_CONFIG } from "~/config/api-config"; import { getUserSession, getSession, simpleRootLogin } from "~/api/login/auth.server"; import styles from "~/styles/pages/login.css?url"; import { toastService } from "~/components/ui"; @@ -114,8 +114,8 @@ export default function Login() { // 处理OAuth2.0登录 const handleOAuthLogin = () => { try { - // 创建OAuth客户端 - const oauthClient = new OAuthClient(OAUTH_CONFIG); + // 创建OAuth客户端(使用客户端安全配置,不包含 clientSecret) + const oauthClient = new OAuthClient(CLIENT_OAUTH_CONFIG); // 生成状态值 const state = oauthClient.generateState(); @@ -172,9 +172,9 @@ export default function Login() { }; useEffect(() => { - // 检查OAuth配置是否完整 - if (!OAUTH_CONFIG.serverUrl || !OAUTH_CONFIG.clientId || !OAUTH_CONFIG.clientSecret) { - console.error("OAuth2.0配置不完整:", OAUTH_CONFIG); + // 检查OAuth配置是否完整(客户端不需要检查 clientSecret) + if (!CLIENT_OAUTH_CONFIG.serverUrl || !CLIENT_OAUTH_CONFIG.clientId) { + console.error("OAuth2.0配置不完整:", CLIENT_OAUTH_CONFIG); } }, []); diff --git a/app/routes/logout.tsx b/app/routes/logout.tsx index 8f508e8..fb77238 100644 --- a/app/routes/logout.tsx +++ b/app/routes/logout.tsx @@ -1,37 +1,43 @@ import { type LoaderFunctionArgs, redirect } from "@remix-run/node"; import { OAuthClient } from "~/api/login/oauth-client"; -import { OAUTH_CONFIG } from "~/config/api-config"; +import { getServerOAuthConfigRuntime } from "~/config/oauth-secret.server"; import { sessionStorage } from "~/api/login/auth.server"; export async function loader({ request }: LoaderFunctionArgs) { const session = await sessionStorage.getSession(request.headers.get("Cookie")); - - // 获取访问令牌 + + // 获取访问令牌和用户角色 const accessToken = session.get("accessToken"); - - if (accessToken) { + const userRole = session.get("userRole"); + + // 🔑 只有非 admin 用户才需要调用 IDaaS 单点登出 + const isAdmin = userRole === 'admin'; + + if (accessToken && !isAdmin) { try { - // 创建OAuth客户端 - const oauthClient = new OAuthClient(OAUTH_CONFIG); - + // 🔒 安全:使用服务器端专用函数获取完整配置 + const oauthClient = new OAuthClient(getServerOAuthConfigRuntime()); + // 构建登出后重定向URL const url = new URL(request.url); const redirectUrl = url.searchParams.get("redirect") || `${url.protocol}//${url.host}/login`; - + // 调用IDaaS单点登出 const logoutSuccess = await oauthClient.logout(accessToken, redirectUrl); - + if (!logoutSuccess) { console.warn("IDaaS单点登出失败,但仍清除本地会话"); } } catch (error) { console.error("单点登出过程中出错:", error); } + } else if (isAdmin) { + console.log("admin 用户登出,跳过 IDaaS 单点登出"); } - + // 无论IDaaS登出是否成功,都清除本地会话 const cookie = await sessionStorage.destroySession(session); - + return redirect("/login", { headers: { "Set-Cookie": cookie diff --git a/app/routes/prompts._index.tsx b/app/routes/prompts._index.tsx index 8588643..0ea234c 100644 --- a/app/routes/prompts._index.tsx +++ b/app/routes/prompts._index.tsx @@ -52,7 +52,7 @@ export async function loader({ request }: LoaderFunctionArgs) { const page = parseInt(url.searchParams.get('page') || '1', 10); const pageSize = parseInt(url.searchParams.get('pageSize') || '10', 10); - // console.log('加载提示词模板参数:', { name, type, status, page, pageSize }); + // console.log('加载提示词模板参数:', { name, type: typeParam, status, page, pageSize }); // 从 API 获取数据 const result = await getPromptTemplates({ diff --git a/app/routes/rules-new.tsx b/app/routes/rules-new.tsx index cbf19ae..aa1bdb1 100644 --- a/app/routes/rules-new.tsx +++ b/app/routes/rules-new.tsx @@ -372,7 +372,7 @@ export default function RuleNew() { // 添加自定义选项 const optionsWithCustom = [ ...response.data, - { value: 'custom', label: '自定义' } + // { value: 'custom', label: '自定义' } ]; setVlmFieldTypeOptions(optionsWithCustom); } else if (response.error) { diff --git a/vite.config.ts b/vite.config.ts index 75d6bfe..73cce77 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -53,7 +53,7 @@ export default defineConfig({ server: { host: '0.0.0.0', // port: 5173, - port: Number(process.env.PORT) || 5173, + port: Number(process.env.PORT) || 51703, open: true, // open: false, allowedHosts: ['nas.7bm.co', 'localhost', '127.0.0.1'], // 允许的主机名列表1