358 lines
10 KiB
JavaScript
358 lines
10 KiB
JavaScript
/**
|
||
* @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()}`);
|
||
|
||
})(); |