diff --git a/app/config/api-config.ts b/app/config/api-config.ts index 66419aa..40fc57b 100644 --- a/app/config/api-config.ts +++ b/app/config/api-config.ts @@ -7,6 +7,8 @@ interface ApiConfig { // 主API基础URL baseUrl: string; + // Dify对话服务URL(独立配置,使用外网地址) + difyBaseUrl: string; // 文档服务URL documentUrl: string; // 文档上传API URL @@ -123,6 +125,7 @@ const configs: Record = { // 开发环境 development: { baseUrl: 'http://172.16.0.55:8000', + difyBaseUrl: 'http://nas.7bm.co:8000', // Dify对话服务使用外网地址 documentUrl: 'http://172.16.0.55:8000/docauditai/', uploadUrl: 'http://172.16.0.55:8000/admin/documents', oauth: { @@ -137,6 +140,7 @@ const configs: Record = { // 测试环境 testing: { baseUrl: 'http://nas.7bm.co:8873', + difyBaseUrl: 'http://nas.7bm.co:8000', // Dify对话服务使用外网地址 documentUrl: 'http://nas.7bm.co:8873/docauditai/', uploadUrl: 'http://nas.7bm.co:8873/admin/documents', oauth: { @@ -152,6 +156,8 @@ const configs: Record = { production: { // postgrest baseUrl: 'http://10.79.97.17:8000', + // Dify对话服务使用外网地址 + difyBaseUrl: 'http://nas.7bm.co:8000', // minio documentUrl: 'http://10.76.244.156:9000/docauditai/', // 文件上传 @@ -171,6 +177,7 @@ const configs: Record = { // 备用配置 (可以根据需要添加更多环境) staging: { baseUrl: 'http://172.16.0.119:9000/admin', + difyBaseUrl: 'http://nas.7bm.co:8000', // Dify对话服务使用外网地址 documentUrl: 'http://nas.7bm.co:9000/docauditai/', uploadUrl: 'http://172.16.0.119:8000/admin/documents/upload', oauth: { @@ -344,7 +351,8 @@ export const apiConfig = getCurrentConfig(); // 导出具体的配置项,方便使用 export const { baseUrl: API_BASE_URL, - documentUrl: DOCUMENT_URL, + difyBaseUrl: DIFY_BASE_URL, + documentUrl: DOCUMENT_URL, uploadUrl: UPLOAD_URL, oauth: OAUTH_CONFIG } = apiConfig; diff --git a/app/config/chat.ts b/app/config/chat.ts index c80273a..6930338 100644 --- a/app/config/chat.ts +++ b/app/config/chat.ts @@ -28,47 +28,19 @@ const extractAppId = (appUrl: string): string => { return appUrl; }; -// 获取Dify API配置 -const getDifyApiUrl = () => { - return getEnvVar('NEXT_PUBLIC_API_URL', 'https://api.dify.ai/v1'); -}; - -const getAppId = () => { - const rawAppId = getEnvVar('NEXT_PUBLIC_APP_ID', ''); - const extractedAppId = extractAppId(rawAppId); - // console.log('🔧 Chat Config Debug:', { - // rawAppId, - // extractedAppId, - // difyApiUrl: getDifyApiUrl(), - // hasApiKey: !!getEnvVar('NEXT_PUBLIC_APP_KEY', ''), - // }); - return extractedAppId; -}; - -// 生成用户ID (模拟服务端逻辑) -const generateUserId = () => { - const appId = getAppId(); - // 生成或获取会话ID (可以使用localStorage或其他方式) - let sessionId = ''; - if (typeof window !== 'undefined') { - sessionId = localStorage.getItem('dify_session_id') || ''; - if (!sessionId) { - sessionId = 'sess_' + Math.random().toString(36).substring(2, 15); - localStorage.setItem('dify_session_id', sessionId); - } - } - return `user_${appId}:${sessionId}`; +// 获取Remix API路由的基础URL(客户端调用服务端API routes) +// 注意:客户端不再直接调用Dify API,而是调用Remix的API routes +// 服务端的API routes会通过dify-client.server.ts代理到FastAPI,再到Dify +const getApiBaseUrl = () => { + // 客户端使用相对路径调用Remix API routes + return '/api'; }; // 聊天应用配置 export const CHAT_CONFIG = { - // API相关配置 - 直接使用Dify API - API_URL: getDifyApiUrl(), - APP_ID: getAppId(), - API_KEY: getEnvVar('NEXT_PUBLIC_APP_KEY', ''), - - // 用户生成函数 - generateUserId, + // API相关配置 - 调用Remix API routes(不再直接调用Dify) + // 客户端 → /api/* routes → dify-client.server.ts → FastAPI /dify → Dify + API_URL: getApiBaseUrl(), // 应用信息 APP_INFO: { diff --git a/app/services/api.client.ts b/app/services/api.client.ts index edbad92..0642548 100644 --- a/app/services/api.client.ts +++ b/app/services/api.client.ts @@ -3,13 +3,15 @@ import type { Feedbacktype, ThoughtItem, VisionFile, MessageEnd, MessageReplace import { unicodeToChar } from '../utils/chat-utils'; // 基础请求选项 +// 注意:客户端调用Remix API routes,不需要手动添加Authorization +// Remix会通过session自动处理JWT认证 const baseOptions = { method: 'GET', mode: 'cors' as RequestMode, - credentials: 'omit' as RequestCredentials, + credentials: 'include' as RequestCredentials, // 改为include以携带cookie headers: new Headers({ 'Content-Type': ContentType.json, - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, + // 移除Authorization头,由服务端自动处理 }), redirect: 'follow' as RequestRedirect, }; @@ -322,22 +324,14 @@ const handleStream = ( const baseFetch = (url: string, fetchOptions: any, needAllResponseContent: boolean = false) => { const options = Object.assign({}, baseOptions, fetchOptions); - // 直接构建Dify API URL + // 调用Remix API routes(如 /api/conversations) + // 服务端会通过session获取JWT并调用FastAPI代理 const urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`; - // 确保Authorization头存在 - if (CHAT_CONFIG.API_KEY && options.headers) { - options.headers['Authorization'] = `Bearer ${CHAT_CONFIG.API_KEY}`; - } - const { body } = options; if (body && typeof body === 'object') { - // 为所有请求添加user参数 - const bodyWithUser = { - ...body, - user: CHAT_CONFIG.generateUserId(), - }; - options.body = JSON.stringify(bodyWithUser); + // 不再添加user参数,服务端会从JWT自动提取 + options.body = JSON.stringify(body); } return fetch(urlWithPrefix, options) @@ -453,7 +447,7 @@ export const ssePost = ( method: 'POST', }, fetchOptions); - // 直接构建Dify API URL + // 调用Remix API routes(如 /api/chat-messages) const urlWithPrefix = `${CHAT_CONFIG.API_URL}/${url.replace(/^\//, '')}`; const controller = new AbortController(); @@ -464,19 +458,15 @@ export const ssePost = ( ...options.headers, 'Content-Type': 'application/json', 'Accept': ContentType.stream, - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, + // 移除Authorization头,由服务端自动处理 }; options.signal = controller.signal; const { body } = options; if (body && typeof body === 'object') { - // 为SSE请求添加user参数 - const bodyWithUser = { - ...body, - user: CHAT_CONFIG.generateUserId(), - }; - options.body = JSON.stringify(bodyWithUser); + // 不再添加user参数,服务端会从JWT自动提取 + options.body = JSON.stringify(body); } return fetch(urlWithPrefix, options) @@ -534,18 +524,14 @@ export const ssePost = ( * ``` */ export const fetchConversations = async () => { - const user = CHAT_CONFIG.generateUserId(); const params = new URLSearchParams({ - user, limit: '100', - first_id: '', + // 不再传递user参数,服务端会从JWT自动提取 }); return fetch(`${CHAT_CONFIG.API_URL}/conversations?${params}`, { method: 'GET', - headers: { - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, - }, + credentials: 'include', // 携带cookie }).then(res => { if (!res.ok) { throw new Error(`Failed to fetch conversations: ${res.status}`); @@ -578,19 +564,14 @@ export const fetchConversations = async () => { * ``` */ export const fetchChatList = async (conversationId: string) => { - const user = CHAT_CONFIG.generateUserId(); const params = new URLSearchParams({ - user, conversation_id: conversationId, - limit: '20', - last_id: '', + // 不再传递user参数,服务端会从JWT自动提取 }); return fetch(`${CHAT_CONFIG.API_URL}/messages?${params}`, { method: 'GET', - headers: { - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, - }, + credentials: 'include', // 携带cookie }).then(res => { if (!res.ok) { throw new Error(`Failed to fetch chat list: ${res.status}`); @@ -625,9 +606,7 @@ export const fetchChatList = async (conversationId: string) => { export const fetchAppParams = async () => { return fetch(`${CHAT_CONFIG.API_URL}/parameters`, { method: 'GET', - headers: { - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, - }, + credentials: 'include', // 携带cookie }).then(res => { if (!res.ok) { throw new Error(`Failed to fetch app params: ${res.status}`); @@ -668,12 +647,9 @@ export const updateFeedback = async ({ url, body }: { url: string; body: Feedbac method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, }, - body: JSON.stringify({ - ...body, - user: CHAT_CONFIG.generateUserId(), - }), + credentials: 'include', // 携带cookie + body: JSON.stringify(body), // 不再添加user参数 }).then(res => { if (!res.ok) { throw new Error(`Failed to update feedback: ${res.status}`); @@ -707,11 +683,11 @@ export const generateConversationName = async (id: string) => { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, }, + credentials: 'include', // 携带cookie body: JSON.stringify({ auto_generate: true, - user: CHAT_CONFIG.generateUserId(), + // 不再添加user参数 }), }).then(res => { if (!res.ok) { @@ -751,12 +727,12 @@ export const renameConversation = async (id: string, name: string, autoGenerate: method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, }, + credentials: 'include', // 携带cookie body: JSON.stringify({ name: autoGenerate ? undefined : name, auto_generate: autoGenerate, - user: CHAT_CONFIG.generateUserId(), + // 不再添加user参数 }), }).then(res => { if (!res.ok) { @@ -790,11 +766,9 @@ export const deleteConversation = async (id: string) => { method: 'DELETE', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, }, - body: JSON.stringify({ - user: CHAT_CONFIG.generateUserId(), - }), + credentials: 'include', // 携带cookie + // 不再发送body和user参数 }).then(res => { if (!res.ok) { throw new Error(`Failed to delete conversation: ${res.status}`); @@ -861,16 +835,12 @@ export const upload = (fetchOptions: any): Promise => { for (const key in options.headers) xhr.setRequestHeader(key, options.headers[key]); - if (CHAT_CONFIG.API_KEY) { - xhr.setRequestHeader('Authorization', `Bearer ${CHAT_CONFIG.API_KEY}`); - } + // 不再手动添加Authorization头,由服务端处理 - // 添加user参数到formData - if (options.data instanceof FormData) { - options.data.append('user', CHAT_CONFIG.generateUserId()); - } + // 不再添加user参数到formData + // 服务端会从JWT自动提取 - xhr.withCredentials = false; // 改为false,因为直接调用Dify API + xhr.withCredentials = true; // 改为true以携带cookie xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) diff --git a/app/services/dify-client.server.ts b/app/services/dify-client.server.ts index 4ee7975..19f1c2d 100644 --- a/app/services/dify-client.server.ts +++ b/app/services/dify-client.server.ts @@ -1,4 +1,4 @@ -import { API_BASE_URL } from '~/config/api-config'; +import { DIFY_BASE_URL } from '~/config/api-config'; // 获取环境变量的服务端函数 const getServerEnvVar = (name: string, defaultValue: string = '') => { @@ -14,8 +14,8 @@ const getServerEnvVar = (name: string, defaultValue: string = '') => { // Dify API 客户端配置 // 注意:现在通过 FastAPI 后端的 /dify 路由代理访问 Dify,使用 JWT 认证 const DIFY_CONFIG = { - // API_URL 指向 FastAPI 后端的 /dify 路由 - API_URL: `${API_BASE_URL}/dify`, + // API_URL 指向 FastAPI 后端的 /dify 路由(使用外网地址 nas.7bm.co:8000) + API_URL: `${DIFY_BASE_URL}/dify`, // API_KEY 保留用于配置验证(实际不再使用,改用JWT) API_KEY: getServerEnvVar('NEXT_PUBLIC_APP_KEY', ''), APP_ID: (() => { diff --git a/app/utils/dify-test.client.ts b/app/utils/dify-test.client.ts index 6bccb67..336aff2 100644 --- a/app/utils/dify-test.client.ts +++ b/app/utils/dify-test.client.ts @@ -1,33 +1,26 @@ import { CHAT_CONFIG } from '../config/chat'; /** - * 测试Dify API连接 - * 这个文件可以用来调试和测试前端直接调用Dify API的功能 + * 测试Dify API连接(通过Remix API routes代理) + * 这个文件用于测试前端调用Remix API routes,再由服务端转发到Dify */ export const testDifyConnection = async () => { - console.log('🔧 Dify Configuration:', { + console.log('🔧 API Configuration:', { apiUrl: CHAT_CONFIG.API_URL, - appId: CHAT_CONFIG.APP_ID, - hasApiKey: !!CHAT_CONFIG.API_KEY, - apiKeyPreview: CHAT_CONFIG.API_KEY ? `${CHAT_CONFIG.API_KEY.substring(0, 10)}...` : 'No API Key', + note: '客户端现在调用Remix API routes,不再直接调用Dify', }); - if (!CHAT_CONFIG.API_URL || !CHAT_CONFIG.APP_ID || !CHAT_CONFIG.API_KEY) { - console.error('❌ Dify配置不完整,请检查环境变量'); + if (!CHAT_CONFIG.API_URL) { + console.error('❌ API配置不完整'); return false; } - const user = CHAT_CONFIG.generateUserId(); - console.log('👤 Generated User ID:', user); - try { - // 测试获取应用参数 + // 测试获取应用参数(通过Remix API route) console.log('📋 测试获取应用参数...'); const response = await fetch(`${CHAT_CONFIG.API_URL}/parameters`, { method: 'GET', - headers: { - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, - }, + credentials: 'include', // 携带cookie以通过JWT认证 }); if (!response.ok) { @@ -40,19 +33,16 @@ export const testDifyConnection = async () => { const data = await response.json(); console.log('✅ 成功获取应用参数:', data); - // 测试获取会话列表 + // 测试获取会话列表(通过Remix API route) console.log('💬 测试获取会话列表...'); const params = new URLSearchParams({ - user, limit: '10', - first_id: '', + // 不再传递user参数,服务端会从JWT自动提取 }); const conversationsResponse = await fetch(`${CHAT_CONFIG.API_URL}/conversations?${params}`, { method: 'GET', - headers: { - 'Authorization': `Bearer ${CHAT_CONFIG.API_KEY}`, - }, + credentials: 'include', // 携带cookie }); if (!conversationsResponse.ok) { @@ -68,7 +58,7 @@ export const testDifyConnection = async () => { return true; } catch (error) { - console.error('❌ 测试Dify连接时发生错误:', error); + console.error('❌ 测试API连接时发生错误:', error); return false; } };