feat:合同文档列表预览完成Collabora集成

This commit is contained in:
PingChuan
2025-11-21 11:04:14 +08:00
parent c3e4b3837f
commit 7e7648383e
10 changed files with 527 additions and 409 deletions
+12 -3
View File
@@ -11,7 +11,7 @@
import { type LoaderFunctionArgs, json } from '@remix-run/node';
import { getUserSession } from '~/api/login/auth.server';
import { generateCollaboraConfig } from '~/lib/collabora/config.server';
import { generateCollaboraConfig } from '~/services/collabora.config.server';
/**
* GET /api/collabora/config
@@ -24,8 +24,8 @@ import { generateCollaboraConfig } from '~/lib/collabora/config.server';
*/
export async function loader({ request }: LoaderFunctionArgs) {
try {
// 获取用户会话信息
const { userInfo } = await getUserSession(request);
// 获取用户会话信息和 frontendJWT
const { userInfo, frontendJWT } = await getUserSession(request);
// 解析查询参数
const url = new URL(request.url);
@@ -42,12 +42,21 @@ export async function loader({ request }: LoaderFunctionArgs) {
);
}
// 验证 frontendJWT
if (!frontendJWT) {
return json(
{ error: '用户未认证' },
{ status: 401 }
);
}
// 生成 Collabora 配置
const config = await generateCollaboraConfig({
fileId,
mode,
userId,
userName,
frontendJWT,
});
return json(config);
+111
View File
@@ -0,0 +1,111 @@
/**
* WOPI 协议 API 路由(Splat路由,支持多级路径)
*
* 功能:
* - CheckFileInfo: GET /api/collabora/wopi/files/{...fileId}
* - GetFile: GET /api/collabora/wopi/files/{...fileId}/contents
* - PutFile: POST /api/collabora/wopi/files/{...fileId}/contents
*
* 注意:使用splat路由($)匹配多级文件路径,如 documents/mz/合同文档/2025/test.docx
*
* @encoding UTF-8
*/
import { type LoaderFunctionArgs, type ActionFunctionArgs } from '@remix-run/node';
import { WopiService } from '../services/collabora.wopi.server';
const wopiService = new WopiService();
/**
* GET 请求处理
* - 无 /contents 后缀 → CheckFileInfo
* - 有 /contents 后缀 → GetFile
*/
export async function loader({ request, params }: LoaderFunctionArgs) {
try {
const url = new URL(request.url);
const accessToken = url.searchParams.get('access_token');
if (!accessToken) {
return new Response('访问令牌缺失', { status: 401 });
}
// 获取文件 ID(使用 splat 参数 '*'
let fileId = params['*'] || '';
// 判断是否是 GetFile 请求(路径以 /contents 结尾)
const isContentsRequest = url.pathname.endsWith('/contents');
// 如果是 GetFile 请求,需要移除路径末尾的 /contents
if (isContentsRequest && fileId.endsWith('/contents')) {
fileId = fileId.slice(0, -9); // 移除 '/contents'
}
if (isContentsRequest) {
// GetFile: 返回文件内容
const { buffer, metadata } = await wopiService.getFile(fileId, accessToken);
return new Response(buffer, {
headers: {
'Content-Type': metadata.contentType,
'Content-Length': metadata.size.toString(),
'Content-Disposition': 'inline',
},
});
}
// CheckFileInfo: 返回文件元数据
const checkFileInfo = await wopiService.checkFileInfo(fileId, accessToken);
// 注意:CheckFileInfo 必须返回纯 JSON,不能使用 Result.success() 包装
return Response.json(checkFileInfo);
} catch (error) {
console.error('WOPI GET 失败:', error);
return new Response(
error instanceof Error ? error.message : 'Internal server error',
{ status: 500 }
);
}
}
/**
* POST 请求处理
* - PutFile: 保存文件内容
*/
export async function action({ request, params }: ActionFunctionArgs) {
try {
const url = new URL(request.url);
const accessToken = url.searchParams.get('access_token');
if (!accessToken) {
return new Response('访问令牌缺失', { status: 401 });
}
// 获取文件 ID(使用 splat 参数 '*'
let fileId = params['*'] || '';
// 判断是否是 PutFile 请求(路径以 /contents 结尾)
const isContentsRequest = url.pathname.endsWith('/contents');
if (!isContentsRequest) {
return new Response('PutFile 必须使用 /contents 路径', { status: 400 });
}
// 移除路径末尾的 /contents
if (fileId.endsWith('/contents')) {
fileId = fileId.slice(0, -9);
}
// PutFile: 保存文件
const fileBuffer = await request.arrayBuffer();
await wopiService.putFile(fileId, accessToken, fileBuffer);
return new Response(null, { status: 200 });
} catch (error) {
console.error('WOPI POST 失败:', error);
return new Response(
error instanceof Error ? error.message : 'Internal server error',
{ status: 500 }
);
}
}
@@ -10,7 +10,7 @@
*/
import { type LoaderFunctionArgs, type ActionFunctionArgs } from '@remix-run/node';
import { WopiService } from '~/lib/collabora/wopi.server';
import { WopiService } from '../services/collabora.wopi.server';
const wopiService = new WopiService();