AIClient-2-API/static/app/file-upload.js
hex2077 ee050c77f2 feat: 新增Web UI管理控制台和认证系统
新增Web UI管理控制台,支持实时配置管理和健康状态监控
添加登录认证系统,包含token生成和验证机制
实现供应商池的启用/禁用功能
更新README文档,添加安装脚本和Web UI使用说明
优化配置文件管理界面,增加API客户端封装
新增登录页面和认证中间件
2025-11-12 17:37:39 +08:00

209 lines
No EOL
6.6 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.

// 文件上传功能模块
import { showToast } from './utils.js';
/**
* 文件上传处理器类
*/
class FileUploadHandler {
constructor() {
this.currentProvider = 'gemini'; // 默认提供商
this.initEventListeners();
}
/**
* 初始化事件监听器
*/
initEventListeners() {
// 监听所有上传按钮的点击事件
document.addEventListener('click', (event) => {
if (event.target.closest('.upload-btn')) {
const button = event.target.closest('.upload-btn');
const targetInputId = button.getAttribute('data-target');
if (targetInputId) {
this.handleFileUpload(button, targetInputId);
}
}
});
// 监听提供商切换事件
const modelProvider = document.getElementById('modelProvider');
if (modelProvider) {
modelProvider.addEventListener('change', (event) => {
this.updateCurrentProvider(event.target.value);
});
}
}
/**
* 更新当前提供商
* @param {string} provider - 选择的提供商
*/
updateCurrentProvider(provider) {
this.currentProvider = this.getProviderKey(provider);
}
/**
* 获取提供商对应的键名
* @param {string} provider - 提供商名称
* @returns {string} - 提供商标识
*/
getProviderKey(provider) {
const providerMap = {
'gemini-cli-oauth': 'gemini',
'claude-kiro-oauth': 'kiro',
'openai-qwen-oauth': 'qwen'
};
return providerMap[provider] || 'gemini';
}
/**
* 处理文件上传
* @param {HTMLElement} button - 上传按钮元素
* @param {string} targetInputId - 目标输入框ID
*/
async handleFileUpload(button, targetInputId) {
// 创建隐藏的文件输入元素
const fileInput = this.createFileInput();
// 设置文件选择回调
fileInput.onchange = async (event) => {
const file = event.target.files[0];
if (file) {
// 只有文件被实际选择后才显示加载状态并上传
this.setButtonLoading(button, true);
await this.uploadFile(file, targetInputId, button);
}
// 清理临时文件输入元素
fileInput.remove();
};
// 触发文件选择
fileInput.click();
}
/**
* 创建文件输入元素
* @returns {HTMLInputElement} - 文件输入元素
*/
createFileInput() {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json,.txt,.key,.pem,.p12,.pfx';
fileInput.style.display = 'none';
document.body.appendChild(fileInput);
return fileInput;
}
/**
* 上传文件到服务器
* @param {File} file - 要上传的文件
* @param {string} targetInputId - 目标输入框ID
* @param {HTMLElement} button - 上传按钮
*/
async uploadFile(file, targetInputId, button) {
try {
// 验证文件类型
if (!this.validateFileType(file)) {
showToast('不支持的文件类型,请选择 JSON、TXT、KEY、PEM、P12 或 PFX 文件', 'error');
this.setButtonLoading(button, false);
return;
}
// 验证文件大小 (5MB 限制)
if (file.size > 5 * 1024 * 1024) {
showToast('文件大小不能超过 5MB', 'error');
this.setButtonLoading(button, false);
return;
}
// 创建 FormData
const formData = new FormData();
formData.append('file', file);
formData.append('provider', this.currentProvider);
formData.append('targetInputId', targetInputId);
// 使用封装接口发送上传请求
const result = await window.apiClient.upload('/upload-oauth-credentials', formData);
// 成功上传,设置文件路径到输入框
this.setFilePathToInput(targetInputId, result.filePath);
showToast('文件上传成功', 'success');
} catch (error) {
console.error('文件上传错误:', error);
showToast('文件上传失败: ' + error.message, 'error');
} finally {
this.setButtonLoading(button, false);
}
}
/**
* 验证文件类型
* @param {File} file - 要验证的文件
* @returns {boolean} - 是否为有效文件类型
*/
validateFileType(file) {
const allowedExtensions = ['.json', '.txt', '.key', '.pem', '.p12', '.pfx'];
const fileName = file.name.toLowerCase();
return allowedExtensions.some(ext => fileName.endsWith(ext));
}
/**
* 设置按钮加载状态
* @param {HTMLElement} button - 按钮元素
* @param {boolean} isLoading - 是否加载中
*/
setButtonLoading(button, isLoading) {
const icon = button.querySelector('i');
if (isLoading) {
button.disabled = true;
icon.className = 'fas fa-spinner fa-spin';
} else {
button.disabled = false;
icon.className = 'fas fa-upload';
}
}
/**
* 设置文件路径到输入框
* @param {string} inputId - 输入框ID
* @param {string} filePath - 文件路径
*/
setFilePathToInput(inputId, filePath) {
// console.log('设置文件路径到输入框:', inputId, filePath);
let input = document.getElementById(inputId);
if (input) {
// console.log('输入框元素存在,设置文件路径:', filePath);
input.value = filePath;
// 同时更新data-config-value属性用于编辑模式
if (input.hasAttribute('data-config-value')) {
input.setAttribute('data-config-value', filePath);
console.log('更新data-config-value属性:', filePath);
}
// 触发输入事件,通知其他监听器
input.dispatchEvent(new Event('input', { bubbles: true }));
} else {
console.error('无法找到输入框:', inputId);
}
}
}
/**
* 初始化文件上传功能
*/
function initFileUpload() {
// 文件上传功能是自初始化的单例
console.log('文件上传功能已初始化');
}
// 导出单例实例
const fileUploadHandler = new FileUploadHandler();
export {
fileUploadHandler,
FileUploadHandler,
initFileUpload
};