feat: 统一管理所有的baseURL
This commit is contained in:
parent
05b130b5a8
commit
c832edfab1
|
|
@ -1484,6 +1484,8 @@
|
||||||
<script src="js/utils/github-stars.js"></script>
|
<script src="js/utils/github-stars.js"></script>
|
||||||
<!-- 自动探测后端:若 /api/健康检查通过将切换为 backend;也可用 ?mode=backend 强制 -->
|
<!-- 自动探测后端:若 /api/健康检查通过将切换为 backend;也可用 ?mode=backend 强制 -->
|
||||||
<script>window.ENV_DEPLOYMENT_MODE = 'auto';</script>
|
<script>window.ENV_DEPLOYMENT_MODE = 'auto';</script>
|
||||||
|
<!-- 代理服务器地址统一配置(必须在其他 API 相关脚本之前加载) -->
|
||||||
|
<script src="js/config/proxy-config.js"></script>
|
||||||
<script src="js/api/api.js?v=2"></script>
|
<script src="js/api/api.js?v=2"></script>
|
||||||
<script src="js/storage/storage.js"></script>
|
<script src="js/storage/storage.js"></script>
|
||||||
<!-- 先初始化存储适配器(提供 isFrontendMode 标记) -->
|
<!-- 先初始化存储适配器(提供 isFrontendMode 标记) -->
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,12 @@ async function uploadToMistral(fileToProcess, mistralKey) {
|
||||||
formData.append('file', fileToProcess);
|
formData.append('file', fileToProcess);
|
||||||
formData.append('purpose', 'ocr');
|
formData.append('purpose', 'ocr');
|
||||||
|
|
||||||
const response = await fetch('http://localhost:3456/api/mistral/v1/files', {
|
// 使用统一配置获取代理地址
|
||||||
|
const proxyUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getMistralUrl('/v1/files')
|
||||||
|
: 'http://localhost:3456/api/mistral/v1/files';
|
||||||
|
|
||||||
|
const response = await fetch(proxyUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Authorization': `Bearer ${mistralKey || 'sk-local-proxy'}` },
|
headers: { 'Authorization': `Bearer ${mistralKey || 'sk-local-proxy'}` },
|
||||||
body: formData
|
body: formData
|
||||||
|
|
@ -91,11 +96,12 @@ async function uploadFileToOssViaProxy(fileToProcess, fileName) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', fileToProcess, fileName);
|
formData.append('file', fileToProcess, fileName);
|
||||||
|
|
||||||
// 假设 local-proxy 运行在 3456 端口,如果部署到线上可能需要改相对路径或读取配置
|
// 使用统一配置获取代理地址
|
||||||
// 这里采用跟 Mistral API 相同的基础写法
|
const proxyUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
const proxyUrl = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
|
? window.ProxyConfig.getOssUploadUrl()
|
||||||
? 'http://localhost:3456/api/upload/oss'
|
: (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
|
||||||
: '/api/upload/oss';
|
? 'http://localhost:3456/api/upload/oss'
|
||||||
|
: '/api/upload/oss');
|
||||||
|
|
||||||
const response = await fetch(proxyUrl, {
|
const response = await fetch(proxyUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -124,8 +130,12 @@ async function uploadFileToOssViaProxy(fileToProcess, fileName) {
|
||||||
* @throws {Error} 如果获取签名 URL 失败(例如文件 ID 无效、认证失败),则抛出错误。
|
* @throws {Error} 如果获取签名 URL 失败(例如文件 ID 无效、认证失败),则抛出错误。
|
||||||
*/
|
*/
|
||||||
async function getMistralSignedUrl(fileId, mistralKey) {
|
async function getMistralSignedUrl(fileId, mistralKey) {
|
||||||
const urlEndpoint = `http://localhost:3456/api/mistral/v1/files/${fileId}/url?expiry=24`;
|
// 使用统一配置获取代理地址
|
||||||
const response = await fetch(urlEndpoint, {
|
const proxyUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getMistralUrl(`/v1/files/${fileId}/url?expiry=24`)
|
||||||
|
: `http://localhost:3456/api/mistral/v1/files/${fileId}/url?expiry=24`;
|
||||||
|
|
||||||
|
const response = await fetch(proxyUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: { 'Authorization': `Bearer ${mistralKey || 'sk-local-proxy'}`, 'Accept': 'application/json' }
|
headers: { 'Authorization': `Bearer ${mistralKey || 'sk-local-proxy'}`, 'Accept': 'application/json' }
|
||||||
});
|
});
|
||||||
|
|
@ -149,7 +159,12 @@ async function getMistralSignedUrl(fileId, mistralKey) {
|
||||||
* @throws {Error} 如果 OCR 处理失败(例如 URL 无效、API Key 错误、处理超时),则抛出错误。
|
* @throws {Error} 如果 OCR 处理失败(例如 URL 无效、API Key 错误、处理超时),则抛出错误。
|
||||||
*/
|
*/
|
||||||
async function callMistralOcr(signedUrl, mistralKey) {
|
async function callMistralOcr(signedUrl, mistralKey) {
|
||||||
const response = await fetch('http://localhost:3456/api/mistral/v1/ocr', {
|
// 使用统一配置获取代理地址
|
||||||
|
const proxyUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getMistralUrl('/v1/ocr')
|
||||||
|
: 'http://localhost:3456/api/mistral/v1/ocr';
|
||||||
|
|
||||||
|
const response = await fetch(proxyUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${mistralKey || 'sk-local-proxy'}`,
|
'Authorization': `Bearer ${mistralKey || 'sk-local-proxy'}`,
|
||||||
|
|
@ -183,7 +198,12 @@ async function callMistralOcr(signedUrl, mistralKey) {
|
||||||
*/
|
*/
|
||||||
async function deleteMistralFile(fileId, apiKey) {
|
async function deleteMistralFile(fileId, apiKey) {
|
||||||
if (!fileId) return; // 参数校验
|
if (!fileId) return; // 参数校验
|
||||||
const deleteUrl = `http://localhost:3456/api/mistral/v1/files/${fileId}`;
|
|
||||||
|
// 使用统一配置获取代理地址
|
||||||
|
const deleteUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getMistralUrl(`/v1/files/${fileId}`)
|
||||||
|
: `http://localhost:3456/api/mistral/v1/files/${fileId}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(deleteUrl, {
|
const response = await fetch(deleteUrl, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,8 @@
|
||||||
options.proxyMode === 'proxy' ||
|
options.proxyMode === 'proxy' ||
|
||||||
(options.proxyMode !== 'direct' && globalProxyMode === 'proxy');
|
(options.proxyMode !== 'direct' && globalProxyMode === 'proxy');
|
||||||
const proxyBaseUrl = options.proxyBaseUrl ||
|
const proxyBaseUrl = options.proxyBaseUrl ||
|
||||||
(typeof window !== 'undefined' && window.PBX_PROXY_BASE_URL) ||
|
(typeof window !== 'undefined' && window.ProxyConfig ? window.ProxyConfig.getProxyUrl() : null) ||
|
||||||
|
(typeof window !== 'undefined' && window.PBX_PROXY_BASE_URL ? window.PBX_PROXY_BASE_URL : null) ||
|
||||||
'http://localhost:3456';
|
'http://localhost:3456';
|
||||||
const provider = options.provider || 'openai';
|
const provider = options.provider || 'openai';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,9 @@
|
||||||
// 检测是否使用代理服务器
|
// 检测是否使用代理服务器
|
||||||
const globalProxyMode = (typeof window !== 'undefined' && window.PBX_PROXY_MODE) || 'auto';
|
const globalProxyMode = (typeof window !== 'undefined' && window.PBX_PROXY_MODE) || 'auto';
|
||||||
const useProxy = globalProxyMode === 'proxy';
|
const useProxy = globalProxyMode === 'proxy';
|
||||||
const proxyBaseUrl = (typeof window !== 'undefined' && window.PBX_PROXY_BASE_URL) || 'http://localhost:3456';
|
const proxyBaseUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getProxyUrl()
|
||||||
|
: (window.PBX_PROXY_BASE_URL || 'http://localhost:3456');
|
||||||
|
|
||||||
// 确定提供商(从模型 ID 或配置推断)
|
// 确定提供商(从模型 ID 或配置推断)
|
||||||
let provider = 'openai';
|
let provider = 'openai';
|
||||||
|
|
|
||||||
|
|
@ -445,7 +445,9 @@ async function sendChatbotMessage(userInput, updateChatbotUI, externalConfig = n
|
||||||
// 检测是否使用代理服务器模式
|
// 检测是否使用代理服务器模式
|
||||||
const globalProxyMode = (typeof window !== 'undefined' && window.PBX_PROXY_MODE) || 'auto';
|
const globalProxyMode = (typeof window !== 'undefined' && window.PBX_PROXY_MODE) || 'auto';
|
||||||
const useProxy = globalProxyMode === 'proxy';
|
const useProxy = globalProxyMode === 'proxy';
|
||||||
const proxyBaseUrl = (typeof window !== 'undefined' && window.PBX_PROXY_BASE_URL) || 'http://localhost:3456';
|
const proxyBaseUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getProxyUrl()
|
||||||
|
: (window.PBX_PROXY_BASE_URL || 'http://localhost:3456');
|
||||||
|
|
||||||
// 在使用代理服务器模式时,API Key 在后端配置,前端可以没有 API Key
|
// 在使用代理服务器模式时,API Key 在后端配置,前端可以没有 API Key
|
||||||
// 否则,检查前端是否配置了有效的 API Key
|
// 否则,检查前端是否配置了有效的 API Key
|
||||||
|
|
@ -1736,7 +1738,9 @@ async function singleChunkSummary(sysPrompt, userInput, config, apiKey) {
|
||||||
// 代理服务器配置(与 buildCustomApiConfig 保持一致)
|
// 代理服务器配置(与 buildCustomApiConfig 保持一致)
|
||||||
const globalProxyMode = (typeof window !== 'undefined' && window.PBX_PROXY_MODE) || 'auto';
|
const globalProxyMode = (typeof window !== 'undefined' && window.PBX_PROXY_MODE) || 'auto';
|
||||||
const useProxy = globalProxyMode === 'proxy';
|
const useProxy = globalProxyMode === 'proxy';
|
||||||
const proxyBaseUrl = (typeof window !== 'undefined' && window.PBX_PROXY_BASE_URL) || 'http://localhost:3456';
|
const proxyBaseUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getProxyUrl()
|
||||||
|
: (window.PBX_PROXY_BASE_URL || 'http://localhost:3456');
|
||||||
|
|
||||||
// 只做单轮整理,不带历史
|
// 只做单轮整理,不带历史
|
||||||
let apiConfig;
|
let apiConfig;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,358 @@
|
||||||
|
/**
|
||||||
|
* @file js/config/proxy-config.js
|
||||||
|
* @description
|
||||||
|
* 代理服务器地址统一配置文件
|
||||||
|
*
|
||||||
|
* 集中管理所有代理服务器地址、API 端点和端口配置。
|
||||||
|
*
|
||||||
|
* 使用方式:
|
||||||
|
* 1. 前端代码:window.ProxyConfig.getProxyUrl()
|
||||||
|
* 2. 支持环境变量覆盖:window.ENV_PROXY_URL
|
||||||
|
* 3. 支持 localStorage 用户自定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ==================== 默认配置 ====================
|
||||||
|
|
||||||
|
const DEFAULT_CONFIG = {
|
||||||
|
// 本地代理服务器端口
|
||||||
|
LOCAL_PROXY_PORT: 3456,
|
||||||
|
|
||||||
|
// 前端静态服务端口
|
||||||
|
FRONTEND_PORT: 8080,
|
||||||
|
|
||||||
|
// 后端 API 服务端口(Docker 部署时)
|
||||||
|
BACKEND_API_PORT: 3000,
|
||||||
|
|
||||||
|
// 本地代理服务器地址
|
||||||
|
LOCAL_PROXY_URL: 'http://localhost:3456',
|
||||||
|
|
||||||
|
// 生产环境代理地址(相对路径,由 Nginx 等代理)
|
||||||
|
PROD_PROXY_URL: '/api',
|
||||||
|
|
||||||
|
// 各服务端点路径
|
||||||
|
ENDPOINTS: {
|
||||||
|
// OCR 相关
|
||||||
|
MISTRAL_BASE: '/api/mistral',
|
||||||
|
MINERU_BASE: '/mineru',
|
||||||
|
DOC2X_BASE: '/doc2x',
|
||||||
|
|
||||||
|
// LLM 代理
|
||||||
|
LLM_PROXY: '/api/llm',
|
||||||
|
|
||||||
|
// OSS 上传
|
||||||
|
OSS_UPLOAD: '/api/upload/oss',
|
||||||
|
|
||||||
|
// 学术搜索
|
||||||
|
SEMANTIC_SCHOLAR: '/api/semanticscholar',
|
||||||
|
PUBMED: '/api/pubmed',
|
||||||
|
CROSSREF: '/api/crossref',
|
||||||
|
OPENALEX: '/api/openalex',
|
||||||
|
ARXIV: '/api/arxiv',
|
||||||
|
|
||||||
|
// PDF 下载代理
|
||||||
|
PDF_DOWNLOAD: '/api/pdf/download',
|
||||||
|
|
||||||
|
// 健康检查
|
||||||
|
HEALTH: '/health',
|
||||||
|
|
||||||
|
// 后端 API
|
||||||
|
BACKEND_API: '/api'
|
||||||
|
},
|
||||||
|
|
||||||
|
// 外部服务直连地址(供参考,实际通过代理访问)
|
||||||
|
EXTERNAL_SERVICES: {
|
||||||
|
MINERU_API: 'https://mineru.net/api/v4',
|
||||||
|
DOC2X_API: 'https://v2.doc2x.noedgeai.com',
|
||||||
|
MISTRAL_API: 'https://api.mistral.ai',
|
||||||
|
OPENAI_API: 'https://api.openai.com',
|
||||||
|
DEEPSEEK_API: 'https://api.deepseek.com',
|
||||||
|
ANTHROPIC_API: 'https://api.anthropic.com',
|
||||||
|
GEMINI_API: 'https://generativelanguage.googleapis.com',
|
||||||
|
ALIYUN_DASHSCOPE: 'https://dashscope.aliyuncs.com/compatible-mode',
|
||||||
|
ZHIPU_API: 'https://open.bigmodel.cn/api/paas/v4'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 配置管理类 ====================
|
||||||
|
|
||||||
|
class ProxyConfig {
|
||||||
|
constructor() {
|
||||||
|
this.config = { ...DEFAULT_CONFIG };
|
||||||
|
this._loadUserConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载用户自定义配置
|
||||||
|
* 优先级:环境变量 > localStorage > 默认值
|
||||||
|
*/
|
||||||
|
_loadUserConfig() {
|
||||||
|
// 1. 检查环境变量覆盖
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
if (window.ENV_PROXY_URL) {
|
||||||
|
this.config.LOCAL_PROXY_URL = window.ENV_PROXY_URL;
|
||||||
|
}
|
||||||
|
if (window.ENV_API_BASE_URL) {
|
||||||
|
this.config.PROD_PROXY_URL = window.ENV_API_BASE_URL;
|
||||||
|
}
|
||||||
|
if (window.ENV_LOCAL_PROXY_PORT) {
|
||||||
|
this.config.LOCAL_PROXY_PORT = parseInt(window.ENV_LOCAL_PROXY_PORT, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查 localStorage 用户配置
|
||||||
|
try {
|
||||||
|
const savedProxyUrl = localStorage.getItem('proxyUrl');
|
||||||
|
if (savedProxyUrl) {
|
||||||
|
this.config.LOCAL_PROXY_URL = savedProxyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedPort = localStorage.getItem('proxyPort');
|
||||||
|
if (savedPort) {
|
||||||
|
this.config.LOCAL_PROXY_PORT = parseInt(savedPort, 10);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// localStorage 不可用时忽略
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为本地开发环境
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isLocalDevelopment() {
|
||||||
|
if (typeof window === 'undefined') return false;
|
||||||
|
|
||||||
|
const hostname = window.location.hostname;
|
||||||
|
return hostname === 'localhost' ||
|
||||||
|
hostname === '127.0.0.1' ||
|
||||||
|
hostname.startsWith('192.168.') ||
|
||||||
|
hostname.startsWith('10.') ||
|
||||||
|
hostname.endsWith('.local');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为 file:// 协议(纯本地文件访问)
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isFileProtocol() {
|
||||||
|
if (typeof window === 'undefined') return false;
|
||||||
|
return window.location.protocol === 'file:';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理服务器基础 URL
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getProxyUrl() {
|
||||||
|
// file:// 协议无法访问本地服务器
|
||||||
|
if (this.isFileProtocol()) {
|
||||||
|
console.warn('[ProxyConfig] file:// 协议无法访问代理服务器');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本地开发环境使用完整 URL
|
||||||
|
if (this.isLocalDevelopment()) {
|
||||||
|
return this.config.LOCAL_PROXY_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生产环境使用相对路径
|
||||||
|
return this.config.PROD_PROXY_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地代理服务器端口
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
getLocalProxyPort() {
|
||||||
|
return this.config.LOCAL_PROXY_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取前端服务端口
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
getFrontendPort() {
|
||||||
|
return this.config.FRONTEND_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定端点的完整 URL
|
||||||
|
* @param {string} endpointName - 端点名称(ENDPOINTS 中的 key)
|
||||||
|
* @param {string} [suffix=''] - 路径后缀
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getEndpointUrl(endpointName, suffix = '') {
|
||||||
|
const basePath = this.config.ENDPOINTS[endpointName];
|
||||||
|
if (!basePath) {
|
||||||
|
console.warn(`[ProxyConfig] Unknown endpoint: ${endpointName}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return `${this.getProxyUrl()}${basePath}${suffix}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 便捷方法:各服务 URL ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mistral OCR 服务 URL
|
||||||
|
* @param {string} [path=''] - 路径后缀
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getMistralUrl(path = '') {
|
||||||
|
const base = this.config.ENDPOINTS.MISTRAL_BASE;
|
||||||
|
return `${this.getProxyUrl()}${base}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MinerU OCR 服务 URL
|
||||||
|
* @param {string} [path=''] - 路径后缀
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getMinerUUrl(path = '') {
|
||||||
|
const base = this.config.ENDPOINTS.MINERU_BASE;
|
||||||
|
return `${this.getProxyUrl()}${base}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doc2X OCR 服务 URL
|
||||||
|
* @param {string} [path=''] - 路径后缀
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getDoc2XUrl(path = '') {
|
||||||
|
const base = this.config.ENDPOINTS.DOC2X_BASE;
|
||||||
|
return `${this.getProxyUrl()}${base}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LLM 代理服务 URL
|
||||||
|
* @param {string} provider - 提供商名称(openai, deepseek, anthropic 等)
|
||||||
|
* @param {string} [path=''] - 路径后缀
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getLLMProxyUrl(provider, path = '') {
|
||||||
|
const base = this.config.ENDPOINTS.LLM_PROXY;
|
||||||
|
return `${this.getProxyUrl()}${base}/${provider}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OSS 上传服务 URL
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getOssUploadUrl() {
|
||||||
|
return `${this.getProxyUrl()}${this.config.ENDPOINTS.OSS_UPLOAD}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 健康检查 URL
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getHealthCheckUrl() {
|
||||||
|
return `${this.getProxyUrl()}${this.config.ENDPOINTS.HEALTH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端 API URL(用于后端模式)
|
||||||
|
* @param {string} [path=''] - 路径后缀
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getBackendApiUrl(path = '') {
|
||||||
|
return `${this.config.PROD_PROXY_URL}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 配置更新方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新代理服务器 URL
|
||||||
|
* @param {string} url - 新的代理 URL
|
||||||
|
*/
|
||||||
|
setProxyUrl(url) {
|
||||||
|
this.config.LOCAL_PROXY_URL = url.replace(/\/+$/, ''); // 移除末尾斜杠
|
||||||
|
try {
|
||||||
|
localStorage.setItem('proxyUrl', this.config.LOCAL_PROXY_URL);
|
||||||
|
console.log(`[ProxyConfig] 代理地址已更新为: ${this.config.LOCAL_PROXY_URL}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[ProxyConfig] 无法保存代理地址到 localStorage');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新代理服务器端口
|
||||||
|
* @param {number} port - 新的端口
|
||||||
|
*/
|
||||||
|
setProxyPort(port) {
|
||||||
|
this.config.LOCAL_PROXY_PORT = port;
|
||||||
|
this.config.LOCAL_PROXY_URL = `http://localhost:${port}`;
|
||||||
|
try {
|
||||||
|
localStorage.setItem('proxyPort', String(port));
|
||||||
|
localStorage.setItem('proxyUrl', this.config.LOCAL_PROXY_URL);
|
||||||
|
console.log(`[ProxyConfig] 代理端口已更新为: ${port}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[ProxyConfig] 无法保存代理端口到 localStorage');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置为默认配置
|
||||||
|
*/
|
||||||
|
resetToDefault() {
|
||||||
|
this.config = { ...DEFAULT_CONFIG };
|
||||||
|
try {
|
||||||
|
localStorage.removeItem('proxyUrl');
|
||||||
|
localStorage.removeItem('proxyPort');
|
||||||
|
console.log('[ProxyConfig] 已重置为默认配置');
|
||||||
|
} catch (e) {
|
||||||
|
// 忽略
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取完整配置对象(只读)
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
getConfig() {
|
||||||
|
return Object.freeze({ ...this.config });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认配置(只读)
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
getDefaultConfig() {
|
||||||
|
return Object.freeze({ ...DEFAULT_CONFIG });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 初始化并导出 ====================
|
||||||
|
|
||||||
|
const proxyConfig = new ProxyConfig();
|
||||||
|
|
||||||
|
// 导出到全局
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.ProxyConfig = proxyConfig;
|
||||||
|
|
||||||
|
// 同时暴露类定义,方便扩展
|
||||||
|
window.ProxyConfigClass = ProxyConfig;
|
||||||
|
|
||||||
|
// 兼容旧代码:设置全局代理地址变量
|
||||||
|
// 这些变量会被其他模块引用
|
||||||
|
if (!window.PBX_PROXY_BASE_URL) {
|
||||||
|
window.PBX_PROXY_BASE_URL = proxyConfig.getProxyUrl();
|
||||||
|
}
|
||||||
|
if (!window.PBX_PROXY_MODE) {
|
||||||
|
window.PBX_PROXY_MODE = 'auto';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容 CommonJS
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = { ProxyConfig, proxyConfig, DEFAULT_CONFIG };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化日志
|
||||||
|
console.log(`[ProxyConfig] 已初始化`);
|
||||||
|
console.log(`[ProxyConfig] 本地开发环境: ${proxyConfig.isLocalDevelopment()}`);
|
||||||
|
console.log(`[ProxyConfig] 代理地址: ${proxyConfig.getProxyUrl()}`);
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
@ -807,7 +807,10 @@ async function triggerReprocessWithMinerU() {
|
||||||
*/
|
*/
|
||||||
async function executeMinerUStructuredTranslation() {
|
async function executeMinerUStructuredTranslation() {
|
||||||
const logPrefix = '[MinerU结构化翻译]';
|
const logPrefix = '[MinerU结构化翻译]';
|
||||||
const PROXY_BASE = 'http://localhost:3456';
|
// 使用统一配置获取代理地址
|
||||||
|
const PROXY_BASE = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getProxyUrl()
|
||||||
|
: (window.PBX_PROXY_BASE_URL || 'http://localhost:3456');
|
||||||
|
|
||||||
// 获取翻译配置
|
// 获取翻译配置
|
||||||
const settings = typeof loadSettings === 'function' ? loadSettings() : {};
|
const settings = typeof loadSettings === 'function' ? loadSettings() : {};
|
||||||
|
|
|
||||||
|
|
@ -798,7 +798,9 @@ const fileType = fileToProcess.name.split('.').pop().toLowerCase();
|
||||||
...translationOptions,
|
...translationOptions,
|
||||||
useBackendProxy,
|
useBackendProxy,
|
||||||
provider: selectedTranslationModelName,
|
provider: selectedTranslationModelName,
|
||||||
proxyBase: 'http://localhost:3456',
|
proxyBase: (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getProxyUrl()
|
||||||
|
: (window.PBX_PROXY_BASE_URL || 'http://localhost:3456'),
|
||||||
// 允许从设置自定义重试,若无则用默认
|
// 允许从设置自定义重试,若无则用默认
|
||||||
maxRetries: (typeof loadSettings === 'function' ? (loadSettings().structuredMaxRetries || undefined) : undefined),
|
maxRetries: (typeof loadSettings === 'function' ? (loadSettings().structuredMaxRetries || undefined) : undefined),
|
||||||
retryDelay: (typeof loadSettings === 'function' ? (loadSettings().structuredRetryDelayMs || undefined) : undefined)
|
retryDelay: (typeof loadSettings === 'function' ? (loadSettings().structuredRetryDelayMs || undefined) : undefined)
|
||||||
|
|
|
||||||
|
|
@ -714,7 +714,10 @@ ${jsonContent}
|
||||||
|
|
||||||
// 后端代理模式 - API Key 由后端管理
|
// 后端代理模式 - API Key 由后端管理
|
||||||
if (options.useBackendProxy) {
|
if (options.useBackendProxy) {
|
||||||
const proxyBase = options.proxyBase || 'http://localhost:3456';
|
const proxyBase = options.proxyBase ||
|
||||||
|
(typeof window !== 'undefined' && window.ProxyConfig ? window.ProxyConfig.getProxyUrl() : null) ||
|
||||||
|
(typeof window !== 'undefined' && window.PBX_PROXY_BASE_URL ? window.PBX_PROXY_BASE_URL : null) ||
|
||||||
|
'http://localhost:3456';
|
||||||
const provider = options.provider || 'aliyun';
|
const provider = options.provider || 'aliyun';
|
||||||
|
|
||||||
// 后端代理端点映射
|
// 后端代理端点映射
|
||||||
|
|
@ -831,9 +834,12 @@ ${jsonContent}
|
||||||
// 预设模型 - 从 translation.js 获取配置
|
// 预设模型 - 从 translation.js 获取配置
|
||||||
// 简化:仅支持常用模型
|
// 简化:仅支持常用模型
|
||||||
// 前端发出的请求源头
|
// 前端发出的请求源头
|
||||||
|
const proxyUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getProxyUrl()
|
||||||
|
: (window.PBX_PROXY_BASE_URL || 'http://localhost:3456');
|
||||||
const predefinedConfigs = {
|
const predefinedConfigs = {
|
||||||
'proxy': {
|
'proxy': {
|
||||||
endpoint: 'http://localhost:3456/api/llm/aliyun/v1/chat/completions',
|
endpoint: `${proxyUrl}/api/llm/aliyun/v1/chat/completions`,
|
||||||
modelName: '通义百炼 (via local proxy)',
|
modelName: '通义百炼 (via local proxy)',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
bodyBuilder: (sys, user) => ({
|
bodyBuilder: (sys, user) => ({
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,16 @@ class MistralOcrAdapter extends OcrAdapter {
|
||||||
super(config);
|
super(config);
|
||||||
this.keys = config.keys && config.keys.length > 0 ? config.keys : ['sk-local-proxy'];
|
this.keys = config.keys && config.keys.length > 0 ? config.keys : ['sk-local-proxy'];
|
||||||
this.currentKeyIndex = 0;
|
this.currentKeyIndex = 0;
|
||||||
this.baseUrl = (config.baseUrl && config.baseUrl !== 'https://api.mistral.ai' ? config.baseUrl : 'http://localhost:3456/api/mistral').replace(/\/+$/, ''); // 移除尾部斜杠
|
|
||||||
|
// 使用统一配置获取代理地址
|
||||||
|
// 优先级:config.baseUrl > window.ProxyConfig > 默认值
|
||||||
|
if (config.baseUrl && config.baseUrl !== 'https://api.mistral.ai') {
|
||||||
|
this.baseUrl = config.baseUrl.replace(/\/+$/, '');
|
||||||
|
} else if (typeof window !== 'undefined' && window.ProxyConfig) {
|
||||||
|
this.baseUrl = window.ProxyConfig.getMistralUrl();
|
||||||
|
} else {
|
||||||
|
this.baseUrl = 'http://localhost:3456/api/mistral';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -94,10 +94,14 @@ class OcrManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'mineru':
|
case 'mineru':
|
||||||
|
// 使用统一配置获取默认 workerUrl
|
||||||
|
const mineruDefaultUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getProxyUrl()
|
||||||
|
: 'http://localhost:3456';
|
||||||
return {
|
return {
|
||||||
engine: 'mineru',
|
engine: 'mineru',
|
||||||
token: localStorage.getItem('ocrMinerUToken') || '',
|
token: localStorage.getItem('ocrMinerUToken') || '',
|
||||||
workerUrl: (localStorage.getItem('ocrMinerUWorkerUrl') || '').replace(/\/+$/, ''), // 去掉末尾斜杠
|
workerUrl: (localStorage.getItem('ocrMinerUWorkerUrl') || mineruDefaultUrl).replace(/\/+$/, ''), // 去掉末尾斜杠
|
||||||
authKey: localStorage.getItem('ocrWorkerAuthKey') || '',
|
authKey: localStorage.getItem('ocrWorkerAuthKey') || '',
|
||||||
tokenMode: localStorage.getItem('ocrMinerUTokenMode') || 'frontend',
|
tokenMode: localStorage.getItem('ocrMinerUTokenMode') || 'frontend',
|
||||||
enableOcr: localStorage.getItem('ocrMinerUEnableOcr') !== 'false',
|
enableOcr: localStorage.getItem('ocrMinerUEnableOcr') !== 'false',
|
||||||
|
|
@ -106,10 +110,14 @@ class OcrManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'doc2x':
|
case 'doc2x':
|
||||||
|
// 使用统一配置获取默认 workerUrl
|
||||||
|
const doc2xDefaultUrl = (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getProxyUrl()
|
||||||
|
: 'http://localhost:3456';
|
||||||
return {
|
return {
|
||||||
engine: 'doc2x',
|
engine: 'doc2x',
|
||||||
token: localStorage.getItem('ocrDoc2XToken') || '',
|
token: localStorage.getItem('ocrDoc2XToken') || '',
|
||||||
workerUrl: (localStorage.getItem('ocrDoc2XWorkerUrl') || '').replace(/\/+$/, ''), // 去掉末尾斜杠
|
workerUrl: (localStorage.getItem('ocrDoc2XWorkerUrl') || doc2xDefaultUrl).replace(/\/+$/, ''), // 去掉末尾斜杠
|
||||||
authKey: localStorage.getItem('ocrWorkerAuthKey') || '',
|
authKey: localStorage.getItem('ocrWorkerAuthKey') || '',
|
||||||
tokenMode: localStorage.getItem('ocrDoc2XTokenMode') || 'frontend'
|
tokenMode: localStorage.getItem('ocrDoc2XTokenMode') || 'frontend'
|
||||||
// 注意:不再需要 exportFormat,因为我们总是导出 Markdown + 图片
|
// 注意:不再需要 exportFormat,因为我们总是导出 Markdown + 图片
|
||||||
|
|
|
||||||
|
|
@ -703,7 +703,9 @@ async function translateMarkdown(
|
||||||
const predefinedConfigs = {
|
const predefinedConfigs = {
|
||||||
'aliyun': {
|
'aliyun': {
|
||||||
// 所有翻译请求都指向后端代理,由后端决定使用哪个模型
|
// 所有翻译请求都指向后端代理,由后端决定使用哪个模型
|
||||||
endpoint: 'http://localhost:3456/api/llm/tongyi/v1/chat/completions',
|
endpoint: (typeof window !== 'undefined' && window.ProxyConfig)
|
||||||
|
? window.ProxyConfig.getLLMProxyUrl('tongyi', '/v1/chat/completions')
|
||||||
|
: ((window.PBX_PROXY_BASE_URL || 'http://localhost:3456') + '/api/llm/tongyi/v1/chat/completions'),
|
||||||
modelName: '通义百炼',
|
modelName: '通义百炼',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
bodyBuilder: (sys, user) => {
|
bodyBuilder: (sys, user) => {
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,8 @@
|
||||||
|
|
||||||
<!-- 引入必要的库 -->
|
<!-- 引入必要的库 -->
|
||||||
<script src="js/lib/jszip.min.js"></script>
|
<script src="js/lib/jszip.min.js"></script>
|
||||||
|
<!-- 代理服务器地址统一配置(必须在其他 API 相关脚本之前加载) -->
|
||||||
|
<script src="js/config/proxy-config.js"></script>
|
||||||
<script src="js/storage/storage.js"></script>
|
<script src="js/storage/storage.js"></script>
|
||||||
<script src="js/api/api.js"></script>
|
<script src="js/api/api.js"></script>
|
||||||
<script src="js/ui/ocr-settings.js"></script>
|
<script src="js/ui/ocr-settings.js"></script>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue