paper-burner/js/config/proxy-config.js

358 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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()}`);
})();