Merge branch 'PingChuan' into shiy-login

This commit is contained in:
2025-11-30 19:33:46 +08:00
43 changed files with 4762 additions and 2634 deletions
+64 -13
View File
@@ -1,7 +1,66 @@
import { type ActionFunctionArgs } from '@remix-run/node';
import { difyClient } from '../services/dify-client.server';
import { getSessionInfo } from '../utils/session.server';
import { type ActionFunctionArgs, type LoaderFunctionArgs } from '@remix-run/node';
import { difyClient } from '~/api/dify-chat/client.server';
/**
* GET /api/chat-messages - 获取会话消息列表
*/
export async function loader({ request }: LoaderFunctionArgs) {
try {
// 获取用户会话信息和 JWT
const { getUserSession } = await import("~/api/login/auth.server");
const { frontendJWT } = await getUserSession(request);
// 检查 JWT 是否存在
if (!frontendJWT) {
console.error('❌ [API] Chat Messages GET - JWT不存在');
return new Response(
JSON.stringify({ error: 'JWT认证失败,请重新登录' }),
{
status: 401,
headers: { 'Content-Type': 'application/json' },
}
);
}
// 从 URL 参数获取 conversation_id
const url = new URL(request.url);
const conversationId = url.searchParams.get('conversation_id');
if (!conversationId) {
return new Response(
JSON.stringify({ error: '缺少 conversation_id 参数' }),
{
status: 400,
headers: { 'Content-Type': 'application/json' },
}
);
}
console.log('客戶端調用remix路由_Chat Messages GET - 获取消息列表:', { conversationId });
const result = await difyClient.getConversationMessages(conversationId, frontendJWT);
return new Response(JSON.stringify(result), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
} catch (error: any) {
console.error('❌ [API] Chat Messages GET - Error:', error.message);
const status = error.message?.includes('JWT认证失败') ? 401 : 500;
return new Response(
JSON.stringify({ error: error.message || 'Failed to get messages' }),
{
status,
headers: { 'Content-Type': 'application/json' },
}
);
}
}
/**
* POST /api/chat-messages - 发送聊天消息
*/
export async function action({ request }: ActionFunctionArgs) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
@@ -34,7 +93,7 @@ export async function action({ request }: ActionFunctionArgs) {
response_mode: responseMode,
} = body;
console.log('🚀 [API] Chat Messages API - 收到请求:', {
console.log('客戶端調用remix路由_Chat Messages API - 收到请求:', {
queryLength: query?.length || 0,
queryPreview: query?.substring(0, 100) + (query?.length > 100 ? '...' : ''),
conversationId,
@@ -54,16 +113,9 @@ export async function action({ request }: ActionFunctionArgs) {
frontendJWT // 传递 JWT
);
console.log('📡 [API] Dify响应状态:', {
status: response.status,
statusText: response.statusText,
hasBody: !!response.body,
headers: Object.fromEntries(response.headers.entries())
});
// 对于流式响应,直接返回流
if (responseMode === 'streaming') {
console.log('🌊 [API] 返回流式响应');
console.log('Dify转发fastapi,返回流式响应');
return new Response(response.body, {
status: response.status,
headers: {
@@ -78,7 +130,6 @@ export async function action({ request }: ActionFunctionArgs) {
}
// 对于非流式响应,返回JSON
console.log('📄 [API] 返回JSON响应');
return new Response(JSON.stringify(response), {
status: 200,
headers: {
+3 -5
View File
@@ -1,6 +1,6 @@
import { json, type ActionFunctionArgs } from '@remix-run/node';
import { difyClient } from '../services/dify-client.server';
import { getSessionInfo, commitSession } from '../utils/session.server';
import { difyClient } from '~/api/dify-chat/client.server';
import { commitSession, getSessionInfo } from '../utils/session.server';
export async function action({ request, params }: ActionFunctionArgs) {
try {
@@ -31,7 +31,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
const body = await request.json();
const { auto_generate, name } = body;
console.log('💬 [API] Rename Conversation API - 重命名会话:', {
console.log('客戶端調用remix路由Rename Conversation API - 重命名会话:', {
id,
autoGenerate: auto_generate,
name,
@@ -41,8 +41,6 @@ export async function action({ request, params }: ActionFunctionArgs) {
// 调用服务端API重命名会话
const data = await difyClient.renameConversation(id, name, auto_generate, frontendJWT);
console.log('✅ [API] Rename Conversation API - Success');
return json(data, {
headers: {
'Set-Cookie': await commitSession(session),
+2 -8
View File
@@ -1,6 +1,6 @@
import { json, type ActionFunctionArgs } from '@remix-run/node';
import { difyClient } from '../services/dify-client.server';
import { getSessionInfo, commitSession } from '../utils/session.server';
import { difyClient } from '~/api/dify-chat/client.server';
import { commitSession, getSessionInfo } from '../utils/session.server';
export async function action({ request, params }: ActionFunctionArgs) {
try {
@@ -31,16 +31,10 @@ export async function action({ request, params }: ActionFunctionArgs) {
const method = request.method;
if (method === 'DELETE') {
console.log('🗑️ [API] Delete Conversation API - 删除会话:', {
id,
hasJWT: !!frontendJWT
});
// 调用服务端API删除会话
const data = await difyClient.deleteConversation(id, frontendJWT);
console.log('✅ [API] Delete Conversation API - Success');
return json(data, {
headers: {
'Set-Cookie': await commitSession(session),
+2 -8
View File
@@ -1,6 +1,6 @@
import { json, type LoaderFunctionArgs } from '@remix-run/node';
import { difyClient } from '../services/dify-client.server';
import { getSessionInfo, commitSession } from '../utils/session.server';
import { difyClient } from '~/api/dify-chat/client.server';
import { commitSession, getSessionInfo } from '../utils/session.server';
export async function loader({ request }: LoaderFunctionArgs) {
try {
@@ -23,14 +23,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
);
}
console.log('💬 [API] Conversations API - 获取会话列表:', {
hasJWT: !!frontendJWT
});
const data = await difyClient.getConversations(frontendJWT);
console.log('✅ [API] Conversations API - Success');
return json(data, {
headers: {
'Set-Cookie': await commitSession(session),
@@ -0,0 +1,56 @@
import { type ActionFunctionArgs } from '@remix-run/node';
import { API_BASE_URL } from '~/config/api-config';
/**
* PATCH /api/dataset/datasets/:datasetId/documents/:documentId/status - 切换文档状态
*/
export async function action({ request, params }: ActionFunctionArgs) {
try {
const { getUserSession } = await import("~/api/login/auth.server");
const { frontendJWT } = await getUserSession(request);
if (!frontendJWT) {
return new Response(
JSON.stringify({ error: 'JWT认证失败,请重新登录' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
);
}
const { datasetId, documentId } = params;
if (!datasetId || !documentId) {
return new Response(
JSON.stringify({ error: '缺少必要参数' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
const body = await request.json();
const { enabled } = body;
console.log('[API] Toggle Document Status:', { datasetId, documentId, enabled });
const apiUrl = `${API_BASE_URL}/dify-dataset/datasets/${datasetId}/documents/${documentId}/status`;
const response = await fetch(apiUrl, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${frontendJWT}`,
},
body: JSON.stringify({ enabled }),
});
const data = await response.json();
return new Response(JSON.stringify(data), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
} catch (error: any) {
console.error('[API] Toggle Document Status - Error:', error.message);
return new Response(
JSON.stringify({ error: error.message || 'Failed to toggle status' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
@@ -0,0 +1,118 @@
import { type LoaderFunctionArgs, type ActionFunctionArgs } from '@remix-run/node';
import { API_BASE_URL } from '~/config/api-config';
/**
* GET /api/dataset/datasets/:datasetId/documents/:documentId - 获取文档详情
*/
export async function loader({ request, params }: LoaderFunctionArgs) {
try {
const { getUserSession } = await import("~/api/login/auth.server");
const { frontendJWT } = await getUserSession(request);
if (!frontendJWT) {
return new Response(
JSON.stringify({ error: 'JWT认证失败,请重新登录' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
);
}
const { datasetId, documentId } = params;
if (!datasetId || !documentId) {
return new Response(
JSON.stringify({ error: '缺少必要参数' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
console.log('[API] Document Detail:', { datasetId, documentId });
const apiUrl = `${API_BASE_URL}/dify-dataset/datasets/${datasetId}/documents/${documentId}`;
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${frontendJWT}`,
},
});
const data = await response.json();
return new Response(JSON.stringify(data), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
} catch (error: any) {
console.error('[API] Document Detail - Error:', error.message);
return new Response(
JSON.stringify({ error: error.message || 'Failed to get document' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
/**
* DELETE /api/dataset/datasets/:datasetId/documents/:documentId - 删除文档
*/
export async function action({ request, params }: ActionFunctionArgs) {
try {
const { getUserSession } = await import("~/api/login/auth.server");
const { frontendJWT } = await getUserSession(request);
if (!frontendJWT) {
return new Response(
JSON.stringify({ error: 'JWT认证失败,请重新登录' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
);
}
const { datasetId, documentId } = params;
if (!datasetId || !documentId) {
return new Response(
JSON.stringify({ error: '缺少必要参数' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
const method = request.method;
if (method === 'DELETE') {
console.log('[API] Delete Document:', { datasetId, documentId });
const apiUrl = `${API_BASE_URL}/dify-dataset/datasets/${datasetId}/documents/${documentId}`;
const response = await fetch(apiUrl, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${frontendJWT}`,
},
});
// 处理 204 No Content
if (response.status === 204) {
return new Response(
JSON.stringify({ result: 'success' }),
{ status: 200, headers: { 'Content-Type': 'application/json' } }
);
}
const data = await response.json();
return new Response(JSON.stringify(data), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
}
return new Response(
JSON.stringify({ error: 'Method not allowed' }),
{ status: 405, headers: { 'Content-Type': 'application/json' } }
);
} catch (error: any) {
console.error('[API] Document Action - Error:', error.message);
return new Response(
JSON.stringify({ error: error.message || 'Failed to process request' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
@@ -0,0 +1,119 @@
import { type LoaderFunctionArgs, type ActionFunctionArgs } from '@remix-run/node';
import { API_BASE_URL } from '~/config/api-config';
/**
* GET /api/dataset/datasets/:datasetId/documents - 获取文档列表
*/
export async function loader({ request, params }: LoaderFunctionArgs) {
try {
// 获取用户会话信息和 JWT
const { getUserSession } = await import("~/api/login/auth.server");
const { frontendJWT } = await getUserSession(request);
if (!frontendJWT) {
return new Response(
JSON.stringify({ error: 'JWT认证失败,请重新登录' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
);
}
const { datasetId } = params;
if (!datasetId) {
return new Response(
JSON.stringify({ error: '缺少 datasetId 参数' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
// 获取查询参数
const url = new URL(request.url);
const page = url.searchParams.get('page') || '1';
const limit = url.searchParams.get('limit') || '20';
const keyword = url.searchParams.get('keyword') || '';
console.log('[API] Documents List:', { datasetId, page, limit, keyword });
// 构建查询参数
const queryParams = new URLSearchParams({ page, limit });
if (keyword) queryParams.append('keyword', keyword);
// 转发请求到 FastAPI
const apiUrl = `${API_BASE_URL}/dify-dataset/datasets/${datasetId}/documents?${queryParams}`;
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${frontendJWT}`,
},
});
const data = await response.json();
return new Response(JSON.stringify(data), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
} catch (error: any) {
console.error('[API] Documents List - Error:', error.message);
return new Response(
JSON.stringify({ error: error.message || 'Failed to get documents' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
/**
* POST /api/dataset/datasets/:datasetId/documents - 创建文档(上传文件)
*/
export async function action({ request, params }: ActionFunctionArgs) {
try {
// 获取用户会话信息和 JWT
const { getUserSession } = await import("~/api/login/auth.server");
const { frontendJWT } = await getUserSession(request);
if (!frontendJWT) {
return new Response(
JSON.stringify({ error: 'JWT认证失败,请重新登录' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
);
}
const { datasetId } = params;
if (!datasetId) {
return new Response(
JSON.stringify({ error: '缺少 datasetId 参数' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
// 获取表单数据
const formData = await request.formData();
console.log('[API] Upload Document:', { datasetId });
// 转发请求到 FastAPI
const apiUrl = `${API_BASE_URL}/dify-dataset/datasets/${datasetId}/documents/create-by-file`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${frontendJWT}`,
},
body: formData,
});
const data = await response.json();
return new Response(JSON.stringify(data), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
} catch (error: any) {
console.error('[API] Upload Document - Error:', error.message);
return new Response(
JSON.stringify({ error: error.message || 'Failed to upload document' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
+60
View File
@@ -0,0 +1,60 @@
import { type LoaderFunctionArgs } from '@remix-run/node';
import { API_BASE_URL } from '~/config/api-config';
/**
* GET /api/dataset/datasets - 获取知识库列表
*/
export async function loader({ request }: LoaderFunctionArgs) {
try {
// 获取用户会话信息和 JWT
const { getUserSession } = await import("~/api/login/auth.server");
const { frontendJWT } = await getUserSession(request);
// 检查 JWT 是否存在
if (!frontendJWT) {
console.error('[API] Dataset List - JWT不存在');
return new Response(
JSON.stringify({ error: 'JWT认证失败,请重新登录' }),
{
status: 401,
headers: { 'Content-Type': 'application/json' },
}
);
}
// 获取查询参数
const url = new URL(request.url);
const page = url.searchParams.get('page') || '1';
const limit = url.searchParams.get('limit') || '20';
console.log('[API] Dataset List - 获取知识库列表:', { page, limit });
// 转发请求到 FastAPI
const apiUrl = `${API_BASE_URL}/dify-dataset/datasets?page=${page}&limit=${limit}`;
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${frontendJWT}`,
},
});
const data = await response.json();
console.log('[API] Dataset List - Success');
return new Response(JSON.stringify(data), {
status: response.status,
headers: { 'Content-Type': 'application/json' },
});
} catch (error: any) {
console.error('[API] Dataset List - Error:', error.message);
return new Response(
JSON.stringify({ error: error.message || 'Failed to get datasets' }),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
}
);
}
}
@@ -0,0 +1,67 @@
import { type ActionFunctionArgs } from '@remix-run/node';
import { difyClient } from '~/api/dify-chat/client.server';
/**
* POST /api/messages/:messageId/feedbacks - 提交消息反馈
*/
export async function action({ request, params }: ActionFunctionArgs) {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
try {
// 获取用户会话信息和 JWT
const { getUserSession } = await import("~/api/login/auth.server");
const { frontendJWT } = await getUserSession(request);
// 检查 JWT 是否存在
if (!frontendJWT) {
console.error('❌ [API] Message Feedback - JWT不存在');
return new Response(
JSON.stringify({ error: 'JWT认证失败,请重新登录' }),
{
status: 401,
headers: { 'Content-Type': 'application/json' },
}
);
}
const { messageId } = params;
if (!messageId) {
return new Response(
JSON.stringify({ error: '缺少 messageId 参数' }),
{
status: 400,
headers: { 'Content-Type': 'application/json' },
}
);
}
const body = await request.json();
const { rating } = body;
console.log('👍 [API] Message Feedback - 提交反馈:', {
messageId,
rating,
});
const result = await difyClient.updateMessageFeedback(messageId, rating, frontendJWT);
console.log('✅ [API] Message Feedback - Success');
return new Response(JSON.stringify(result), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
} catch (error: any) {
console.error('❌ [API] Message Feedback - Error:', error.message);
const status = error.message?.includes('JWT认证失败') ? 401 : 500;
return new Response(
JSON.stringify({ error: error.message || 'Failed to submit feedback' }),
{
status,
headers: { 'Content-Type': 'application/json' },
}
);
}
}
+2 -8
View File
@@ -1,6 +1,6 @@
import { json, type LoaderFunctionArgs } from '@remix-run/node';
import { difyClient } from '../services/dify-client.server';
import { getSessionInfo, commitSession } from '../utils/session.server';
import { difyClient } from '~/api/dify-chat/client.server';
import { commitSession, getSessionInfo } from '../utils/session.server';
export async function loader({ request }: LoaderFunctionArgs) {
try {
@@ -23,14 +23,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
);
}
console.log('📋 [API] Parameters API - 获取应用参数:', {
hasJWT: !!frontendJWT
});
const data = await difyClient.getApplicationParameters(frontendJWT);
console.log('✅ [API] Parameters API - Success');
return json(data, {
headers: {
'Set-Cookie': await commitSession(session),
+1 -1
View File
@@ -1,6 +1,6 @@
import { json } from "@remix-run/node";
import { type MetaFunction } from "@remix-run/node";
import Chat from "~/components/chat";
import Chat from "~/components/dify-chat";
import chatIndexStyles from "~/styles/components/chat-with-llm/index.css?url";
import chatMessageStyles from "~/styles/components/chat-with-llm/chat-message.css?url";
import chatInputStyles from "~/styles/components/chat-with-llm/chat-input.css?url";
+26
View File
@@ -0,0 +1,26 @@
import DatasetManager from "~/components/dify-dataset-manager";
import datasetManagerStyles from "~/styles/components/dify-dataset-manager/index.css?url";
import sidebarStyles from "~/styles/components/dify-dataset-manager/sidebar.css?url";
import documentListStyles from "~/styles/components/dify-dataset-manager/document-list.css?url";
/**
* 注册样式
*/
export function links() {
return [
{ rel: "stylesheet", href: datasetManagerStyles },
{ rel: "stylesheet", href: sidebarStyles },
{ rel: "stylesheet", href: documentListStyles },
];
}
/**
* 知识库管理首页
*/
export default function DatasetManagerIndex() {
return (
<div className="dataset-manager-page" style={{ height: '93vh', padding: '16px' }}>
<DatasetManager />
</div>
);
}
+20
View File
@@ -0,0 +1,20 @@
import { Outlet } from "@remix-run/react";
import type { MetaFunction } from "@remix-run/node";
export const meta: MetaFunction = () => {
return [
{ title: "知识库管理 - 智能审核系统" },
{ name: "description", content: "Dify 知识库文档管理" },
];
};
export const handle = {
breadcrumb: "知识库管理",
};
/**
* 知识库管理布局路由
*/
export default function DatasetManagerLayout() {
return <Outlet />;
}