P.help / src /utils.ts
Momoxiao111's picture
fix: pdf-parse compatibility and custom fetch integration
87436cf
Raw History Blame Contribute Delete
5.81 kB
export function getApiHeaders(): Record<string, string> {
const saved = localStorage.getItem("patent_api_settings");
if (!saved) return {};
try {
const settings = JSON.parse(saved);
if (settings) {
return {
"x-user-api-provider": settings.defaultProvider || settings.provider || "gemini",
"x-user-gemini-api-key": settings.geminiApiKey || "",
"x-user-gemini-model": settings.geminiModel || "",
"x-user-deepseek-api-key": settings.deepseekApiKey || "",
"x-user-deepseek-base-url": settings.deepseekBaseUrl || "",
"x-user-deepseek-model": settings.deepseekModel || "",
// Backward compatibility
"x-user-api-key": settings.apiKey || "",
"x-user-api-base-url": settings.baseUrl || "",
"x-user-api-model": settings.model || "",
};
}
} catch (e) {
console.error("Failed to load custom API settings", e);
}
return {};
}
export async function customFetch(input: RequestInfo | URL, init?: RequestInit & { timeout?: number }): Promise<Response> {
const url = typeof input === "string" ? input : (input instanceof URL ? input.href : (input && "url" in input ? input.url : ""));
const timeoutMs = init?.timeout ?? (url.includes("/api/") ? 40000 : undefined);
let controller: AbortController | null = null;
let timeoutId: any = null;
if (timeoutMs) {
controller = new AbortController();
timeoutId = setTimeout(() => {
if (controller) controller.abort();
}, timeoutMs);
init = init || {};
init.signal = controller.signal;
}
if (url.includes("/api/")) {
const customHeaders = getApiHeaders();
if (Object.keys(customHeaders).length > 0) {
init = init || {};
const newHeaders = new Headers(init.headers || {});
Object.entries(customHeaders).forEach(([key, val]) => {
if (val) {
newHeaders.set(key, val);
}
});
init.headers = newHeaders;
}
}
try {
const response = await fetch(input, init);
if (timeoutId) clearTimeout(timeoutId);
return response;
} catch (error: any) {
if (timeoutId) clearTimeout(timeoutId);
if (error.name === "AbortError") {
throw new Error("请求超时:AI 接口响应时间过长。请稍后重试,或检查页面右上角「API设置」以配置更稳定的专属 API Key。");
}
throw error;
}
}
/**
* Parses PDF files entirely in the client browser using pdfjs-dist via CDN.
* This completely avoids sending large binary files through the reverse proxy,
* bypassing any "413 Request Entity Too Large" errors.
*/
export async function parsePdfClient(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const executeParsing = async () => {
try {
const pdfjsLib = (window as any).pdfjsLib;
if (!pdfjsLib) {
throw new Error("浏览器中未加载 pdfjsLib 组件");
}
// Use matching worker CDN link
pdfjsLib.GlobalWorkerOptions.workerSrc = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js";
const arrayBuffer = await file.arrayBuffer();
const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer });
const pdfDoc = await loadingTask.promise;
let textContent = "";
for (let i = 1; i <= pdfDoc.numPages; i++) {
const page = await pdfDoc.getPage(i);
const textObj = await page.getTextContent();
const pageText = textObj.items
.map((item: any) => item.str)
.join(" ");
textContent += pageText + "\n";
}
if (textContent.trim().length < 5) {
throw new Error("未在 PDF 中提取到有效文本。该文件可能为扫描件、图片、或已加密。建议在页面右上角「API设置」中配置您个人的专属 Google Gemini Key 启用多模态 OCR 识别,或直接复制文本发送。");
}
resolve(textContent);
} catch (err: any) {
reject(err);
}
};
if ((window as any).pdfjsLib) {
executeParsing();
} else {
const script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js";
script.onload = () => {
executeParsing();
};
script.onerror = () => {
reject(new Error("加载在线 PDF 解析组件失败,请检查您的网络。"));
};
document.head.appendChild(script);
}
});
}
/**
* Parses DOCX files entirely in the client browser using mammoth.js via CDN.
*/
export async function parseDocxClient(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const executeParsing = async () => {
try {
const mammoth = (window as any).mammoth;
if (!mammoth) {
throw new Error("浏览器中未加载 mammoth 组件");
}
const arrayBuffer = await file.arrayBuffer();
const result = await mammoth.extractRawText({ arrayBuffer });
if (result.value && result.value.trim().length > 0) {
resolve(result.value);
} else {
throw new Error("Word 文档中未提取到有效文本");
}
} catch (err: any) {
reject(err);
}
};
if ((window as any).mammoth) {
executeParsing();
} else {
const script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js";
script.onload = () => {
executeParsing();
};
script.onerror = () => {
reject(new Error("加载在线 Word 解析组件失败,请检查您的网络。"));
};
document.head.appendChild(script);
}
});
}