99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
/**
|
|
* WOPI 协议 API 路由
|
|
*
|
|
* 功能:
|
|
* - CheckFileInfo: GET /api/collabora/wopi/files/{fileId}
|
|
* - GetFile: GET /api/collabora/wopi/files/{fileId}/contents
|
|
* - PutFile: POST /api/collabora/wopi/files/{fileId}/contents
|
|
*
|
|
* @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
|
|
const fileId = params.fileId || '';
|
|
|
|
// 判断是否是 GetFile 请求(路径以 /contents 结尾)
|
|
const isContentsRequest = url.pathname.endsWith('/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 });
|
|
}
|
|
|
|
const fileId = params.fileId || '';
|
|
|
|
// 判断是否是 PutFile 请求(路径以 /contents 结尾)
|
|
const isContentsRequest = url.pathname.endsWith('/contents');
|
|
|
|
if (!isContentsRequest) {
|
|
return new Response('PutFile 必须使用 /contents 路径', { status: 400 });
|
|
}
|
|
|
|
// 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 }
|
|
);
|
|
}
|
|
}
|