1690 lines
65 KiB
JavaScript
1690 lines
65 KiB
JavaScript
// 配置管理系统 JavaScript
|
||
|
||
// 全局变量
|
||
let currentConfig = null;
|
||
let configList = [];
|
||
|
||
// 页面加载完成后执行
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 添加删除确认模态框的HTML
|
||
const deleteConfirmModalHTML = `
|
||
<div class="modal" id="deleteConfirmModal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>删除配置</h3>
|
||
<button class="close-btn" id="deleteConfirmCloseBtn">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>确定要删除这个配置吗?此操作不可恢复。</p>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn secondary" id="cancelDeleteBtn">取消</button>
|
||
<button type="button" class="btn danger" id="confirmDeleteBtn">删除</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
document.body.insertAdjacentHTML('beforeend', deleteConfirmModalHTML);
|
||
|
||
// 绑定事件监听器
|
||
bindEventListeners();
|
||
|
||
// 加载配置列表
|
||
loadConfigs();
|
||
});
|
||
|
||
// 绑定事件监听器
|
||
function bindEventListeners() {
|
||
// 配置类型选择
|
||
const configTypeSelect = document.getElementById('configTypeSelect');
|
||
|
||
// 选中下拉框选项后直接显示配置详情界面
|
||
configTypeSelect.addEventListener('change', function() {
|
||
if (this.value) {
|
||
handleAddConfigDirectly();
|
||
}
|
||
});
|
||
|
||
// 关闭添加配置模态框
|
||
document.getElementById('addConfigCloseBtn').addEventListener('click', hideAddConfigModal);
|
||
|
||
// 取消添加配置
|
||
document.getElementById('cancelAddBtn').addEventListener('click', hideAddConfigModal);
|
||
|
||
// 提交添加配置表单
|
||
document.getElementById('addConfigForm').addEventListener('submit', handleAddConfig);
|
||
|
||
// 保存配置按钮
|
||
document.getElementById('saveBtn').addEventListener('click', saveConfig);
|
||
|
||
// 删除配置按钮
|
||
document.getElementById('deleteBtn').addEventListener('click', showDeleteConfirm);
|
||
|
||
// 关闭删除确认模态框
|
||
document.getElementById('deleteConfirmCloseBtn').addEventListener('click', hideDeleteConfirmModal);
|
||
|
||
// 取消删除
|
||
document.getElementById('cancelDeleteBtn').addEventListener('click', hideDeleteConfirmModal);
|
||
|
||
// 确认删除
|
||
document.getElementById('confirmDeleteBtn').addEventListener('click', confirmDeleteConfig);
|
||
|
||
// 点击模态框外部关闭模态框
|
||
window.addEventListener('click', function(event) {
|
||
if (event.target.classList.contains('modal')) {
|
||
event.target.classList.remove('show');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 加载配置列表
|
||
async function loadConfigs() {
|
||
try {
|
||
const response = await fetch('/folder-configs');
|
||
if (!response.ok) {
|
||
throw new Error('Failed to load configurations');
|
||
}
|
||
|
||
const data = await response.json();
|
||
configList = data.configurations;
|
||
|
||
// 渲染配置列表
|
||
renderConfigList();
|
||
|
||
// 如果没有选择任何配置,显示欢迎界面
|
||
if (!currentConfig) {
|
||
showWelcomeScreen();
|
||
} else {
|
||
// 重新加载当前配置
|
||
loadConfigDetails(currentConfig.id);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Error loading configurations:', error);
|
||
alert('加载配置失败: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// 渲染配置列表
|
||
function renderConfigList() {
|
||
const configListElement = document.getElementById('configList');
|
||
configListElement.innerHTML = '';
|
||
|
||
if (configList.length === 0) {
|
||
configListElement.innerHTML = '<div style="text-align: center; padding: 20px; color: #666;">暂无配置</div>';
|
||
return;
|
||
}
|
||
|
||
configList.forEach(config => {
|
||
const configItem = document.createElement('div');
|
||
configItem.className = 'config-item';
|
||
configItem.dataset.id = config.id;
|
||
configItem.dataset.type = config.type;
|
||
|
||
if (currentConfig && config.id === currentConfig.id) {
|
||
configItem.classList.add('active');
|
||
}
|
||
|
||
// 生成配置描述信息
|
||
let configInfo = '';
|
||
if (config.type === 'database') {
|
||
const dbConfig = config.config;
|
||
if (dbConfig.database && dbConfig.table_name) {
|
||
configInfo = `${dbConfig.database}.${dbConfig.table_name}`;
|
||
} else if (dbConfig.database) {
|
||
configInfo = dbConfig.database;
|
||
}
|
||
} else if (config.type === 'folder') {
|
||
const folderConfig = config.config;
|
||
if (folderConfig.host && folderConfig.folder_path) {
|
||
configInfo = `${folderConfig.host}:${folderConfig.folder_path}`;
|
||
} else if (folderConfig.folder_path) {
|
||
configInfo = folderConfig.folder_path;
|
||
} else if (folderConfig.host) {
|
||
configInfo = folderConfig.host;
|
||
}
|
||
}
|
||
|
||
// 使用配置ID作为标题
|
||
const configName = config.id;
|
||
|
||
// 生成HTML结构
|
||
configItem.innerHTML = `
|
||
<div class="config-item-content">
|
||
<div class="config-item-details">
|
||
<div class="config-item-title">${configName}</div>
|
||
<div class="config-item-info">${configInfo}</div>
|
||
<div class="config-item-meta">
|
||
<div class="config-item-time">${new Date(config.update_at).toLocaleString()}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
configItem.addEventListener('click', () => loadConfigDetails(config.id));
|
||
|
||
configListElement.appendChild(configItem);
|
||
});
|
||
}
|
||
|
||
// 加载配置详情
|
||
async function loadConfigDetails(configId) {
|
||
try {
|
||
// 查找配置
|
||
let config = configList.find(c => c.id === configId);
|
||
|
||
// 如果配置不存在,可能是刚创建的,尝试从服务器重新获取配置列表
|
||
if (!config) {
|
||
await loadConfigs();
|
||
config = configList.find(c => c.id === configId);
|
||
}
|
||
|
||
// 如果仍然找不到配置,说明真的有问题,直接报错
|
||
if (!config) {
|
||
throw new Error('配置不存在');
|
||
}
|
||
|
||
currentConfig = config;
|
||
|
||
// 更新配置列表选中状态
|
||
renderConfigList();
|
||
|
||
// 显示配置详情界面
|
||
showConfigScreen();
|
||
|
||
// 更新配置标题,使用配置ID(即文件名)
|
||
if (config.id === null) {
|
||
document.getElementById('configTitle').textContent = '新增数据源';
|
||
} else {
|
||
document.getElementById('configTitle').textContent = config.id;
|
||
}
|
||
|
||
// 生成配置表单
|
||
generateConfigForm(config.config);
|
||
|
||
} catch (error) {
|
||
console.error('Error loading config details:', error);
|
||
alert('加载配置详情失败: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// 生成配置表单
|
||
function generateConfigForm(config) {
|
||
const formElement = document.getElementById('configForm');
|
||
formElement.innerHTML = '';
|
||
|
||
// 基本信息部分
|
||
const basicSection = document.createElement('div');
|
||
basicSection.innerHTML = `
|
||
<h3 class="section-title">基本信息</h3>
|
||
<!-- 配置名称自动生成,隐藏输入框 -->
|
||
<input type="text" id="formName" value="${config.name || ''}" style="display: none;">
|
||
<div class="form-group">
|
||
<label for="formType">配置类型</label>
|
||
<select id="formType" disabled>
|
||
<option value="database" ${config.type === 'database' ? 'selected' : ''}>数据库 (database)</option>
|
||
<option value="folder" ${config.type === 'folder' ? 'selected' : ''}>文件夹 (folder)</option>
|
||
<option value="git" ${config.type === 'git' ? 'selected' : ''}>Git代码库 (git)</option>
|
||
</select>
|
||
</div>
|
||
`;
|
||
formElement.appendChild(basicSection);
|
||
|
||
// 根据配置类型生成相应的表单字段
|
||
if (config.type === 'folder') {
|
||
const folderSection = document.createElement('div');
|
||
folderSection.innerHTML = `
|
||
<h3 class="section-title">文件夹配置</h3>
|
||
<div class="form-group">
|
||
<label for="formFolderPath">文件夹路径 <span class="required">*</span></label>
|
||
<input type="text" id="formFolderPath" value="${config.folder_path || ''}" required>
|
||
</div>
|
||
<div class="form-group checkbox">
|
||
<input type="checkbox" id="formRecursive" ${config.recursive ? 'checked' : ''}>
|
||
<label for="formRecursive">递归扫描子文件夹</label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formIgnorePatterns">忽略的文件模式 (用逗号分隔,示例:*.log, *.tmp, node_modules/)</label>
|
||
<input type="text" id="formIgnorePatterns" value="${config.ignore_patterns ? config.ignore_patterns.join(', ') : ''}">
|
||
<div style="margin-top: 5px; font-size: 0.85em; color: #666;">
|
||
示例:*.log, *.tmp, node_modules/
|
||
</div>
|
||
</div>
|
||
`;
|
||
formElement.appendChild(folderSection);
|
||
|
||
}
|
||
|
||
// 为folder添加SSH连接配置
|
||
if (config.type === 'folder') {
|
||
const sshSection = document.createElement('div');
|
||
sshSection.innerHTML = `
|
||
<h3 class="section-title">SSH连接配置</h3>
|
||
<div class="form-group">
|
||
<label for="formHost">主机地址 <span class="required">*</span></label>
|
||
<input type="text" id="formHost" value="${config.host || ''}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formPort">端口 <span class="required">*</span></label>
|
||
<input type="number" id="formPort" value="${config.port || 22}" min="1" max="65535" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formUsername">用户名 <span class="required">*</span></label>
|
||
<input type="text" id="formUsername" value="${config.username || ''}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formPassword">密码 <span class="required">*</span></label>
|
||
<input type="password" id="formPassword" value="${config.password || ''}" required>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button type="button" id="testSshConnectionBtn" class="btn secondary" style="margin-top: 10px;">🔌 测试SSH连接</button>
|
||
</div>
|
||
`;
|
||
formElement.appendChild(sshSection);
|
||
|
||
// 绑定事件
|
||
bindFolderEvents();
|
||
}
|
||
|
||
// Git代码库配置
|
||
if (config.type === 'git') {
|
||
const gitSection = document.createElement('div');
|
||
gitSection.innerHTML = `
|
||
<h3 class="section-title">Git代码库配置</h3>
|
||
<div class="form-group">
|
||
<label for="formGitUrl">Git仓库地址 <span class="required">*</span></label>
|
||
<input type="text" id="formGitUrl" value="${config.git_url || ''}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formBranch">分支名称</label>
|
||
<input type="text" id="formBranch" value="${config.branch || 'main'}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formProtocol">协议类型</label>
|
||
<select id="formProtocol">
|
||
<option value="https" ${config.protocol === 'https' ? 'selected' : ''}>HTTPS</option>
|
||
<option value="ssh" ${config.protocol === 'ssh' ? 'selected' : ''}>SSH</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group" id="httpsTokenGroup">
|
||
<label for="formHttpsToken">HTTPS令牌</label>
|
||
<input type="password" id="formHttpsToken" value="${config.https_token || ''}">
|
||
</div>
|
||
<div class="form-group" id="sshKeyGroup" style="display: none;">
|
||
<label for="formSshKey">SSH私钥</label>
|
||
<textarea id="formSshKey" rows="10" value="${config.ssh_key || ''}">${config.ssh_key || ''}</textarea>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formPollInterval">轮询间隔(秒)</label>
|
||
<input type="number" id="formPollInterval" value="${config.poll_interval || 300}" min="60" max="3600">
|
||
</div>
|
||
<div class="form-actions">
|
||
<button type="button" id="testGitConnectionBtn" class="btn secondary" style="margin-top: 10px;">🔌 测试Git连接</button>
|
||
</div>
|
||
`;
|
||
formElement.appendChild(gitSection);
|
||
|
||
// 绑定事件
|
||
bindGitEvents();
|
||
}
|
||
|
||
if (config.type === 'database') {
|
||
// 数据库连接配置(放在前面,方便先测试连接)
|
||
const connectionSection = document.createElement('div');
|
||
connectionSection.innerHTML = `
|
||
<h3 class="section-title">数据库连接配置</h3>
|
||
<div class="form-group">
|
||
<label for="formDbType">数据库类型 <span class="required">*</span></label>
|
||
<select id="formDbType" required>
|
||
<option value="mysql" ${config.db_type === 'mysql' ? 'selected' : config.db_type === undefined ? 'selected' : ''}>MySQL</option>
|
||
<option value="dameng" ${config.db_type === 'dameng' ? 'selected' : ''}>达梦数据库</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formMysqlHost">主机地址 <span class="required">*</span></label>
|
||
<input type="text" id="formMysqlHost" value="${config.mysql_host || ''}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formMysqlPort">端口 <span class="required">*</span></label>
|
||
<input type="number" id="formMysqlPort" value="${config.mysql_port || 3306}" min="1" max="65535" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formMysqlUser">用户名 <span class="required">*</span></label>
|
||
<input type="text" id="formMysqlUser" value="${config.mysql_user || ''}" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formMysqlPassword">密码 <span class="required">*</span></label>
|
||
<input type="password" id="formMysqlPassword" value="${config.mysql_password || ''}" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<button type="button" id="testConnectionBtn" class="btn primary" style="margin-right: 10px;">🔌 测试连接</button>
|
||
<button type="button" id="getDatabasesBtn" class="btn primary">📋 获取数据库列表</button>
|
||
</div>
|
||
`;
|
||
formElement.appendChild(connectionSection);
|
||
|
||
const databaseSection = document.createElement('div');
|
||
databaseSection.innerHTML = `
|
||
<h3 class="section-title">数据库配置</h3>
|
||
<div class="form-group">
|
||
<label for="formDatabase">数据库名称 <span class="required">*</span></label>
|
||
<select id="formDatabase" required>
|
||
<option value="">-- 选择数据库 --</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<button type="button" id="getTablesBtn" class="btn primary">📋 获取表列表</button>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formTableName">表名 <span class="required">*</span></label>
|
||
<select id="formTableName" required>
|
||
<option value="">-- 选择表 --</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<button type="button" id="getTableStructureBtn" class="btn primary">📋 获取表结构</button>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formIdColumn">ID列 <span class="required">*</span></label>
|
||
<select id="formIdColumn" required>
|
||
<option value="">-- 选择ID列 --</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>内容列(可多选) <span class="required">*</span></label>
|
||
<div id="formContentColumn" class="checkbox-group"></div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="formTitleColumn">标题列(可选)</label>
|
||
<select id="formTitleColumn">
|
||
<option value="">-- 选择标题列(可选)--</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="formUpdatedAtColumn">更新时间列(可选)</label>
|
||
<select id="formUpdatedAtColumn">
|
||
<option value="">-- 选择更新时间列(可选)--</option>
|
||
</select>
|
||
</div>
|
||
`;
|
||
formElement.appendChild(databaseSection);
|
||
|
||
// 设置初始值
|
||
const formDatabase = document.getElementById('formDatabase');
|
||
const formTableName = document.getElementById('formTableName');
|
||
|
||
if (config.database) {
|
||
// 添加已保存的数据库名称作为选项
|
||
const dbOption = document.createElement('option');
|
||
dbOption.value = config.database;
|
||
dbOption.textContent = config.database;
|
||
dbOption.selected = true;
|
||
formDatabase.appendChild(dbOption);
|
||
}
|
||
|
||
if (config.table_name) {
|
||
// 添加已保存的表名作为选项
|
||
const tableOption = document.createElement('option');
|
||
tableOption.value = config.table_name;
|
||
tableOption.textContent = config.table_name;
|
||
tableOption.selected = true;
|
||
formTableName.appendChild(tableOption);
|
||
}
|
||
|
||
// 如果有完整的数据库配置,尝试获取实际的数据表结构
|
||
if (config.database && config.table_name) {
|
||
// 尝试从后端获取实际的数据表结构
|
||
fetchTableStructure(config).then(columns => {
|
||
if (columns && columns.length > 0) {
|
||
// 更新列选择器,显示所有列
|
||
updateColumnSelectors(columns);
|
||
|
||
// 确保所有选择器都有正确的值
|
||
if (config.id_column) {
|
||
document.getElementById('formIdColumn').value = config.id_column;
|
||
}
|
||
if (config.title_column) {
|
||
document.getElementById('formTitleColumn').value = config.title_column;
|
||
}
|
||
|
||
// 处理内容列的选中状态(复选框组)
|
||
const contentColumns = config.content_column ? config.content_column.split(',') : [];
|
||
if (contentColumns.length > 0) {
|
||
const contentGroup = document.getElementById('formContentColumn');
|
||
const checkboxes = contentGroup.querySelectorAll('input[name="contentColumn"]');
|
||
checkboxes.forEach(checkbox => {
|
||
if (contentColumns.includes(checkbox.value)) {
|
||
checkbox.checked = true;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取表结构失败:', error);
|
||
// 如果获取失败,继续使用原有的模拟列列表
|
||
const mockColumns = [];
|
||
|
||
// 添加ID列
|
||
mockColumns.push({ name: config.id_column || '', type: '', key: 'PRI' });
|
||
|
||
// 添加内容列
|
||
const contentColumns = config.content_column ? config.content_column.split(',') : [];
|
||
contentColumns.forEach(colName => {
|
||
if (colName !== config.id_column) {
|
||
mockColumns.push({ name: colName, type: '', key: '' });
|
||
}
|
||
});
|
||
|
||
// 添加标题列
|
||
if (config.title_column && config.title_column !== config.id_column && !contentColumns.includes(config.title_column)) {
|
||
mockColumns.push({ name: config.title_column, type: '', key: '' });
|
||
}
|
||
|
||
// 更新列选择器
|
||
updateColumnSelectors(mockColumns);
|
||
});
|
||
}
|
||
|
||
// 确保所有选择器都有正确的值
|
||
if (config.id_column) {
|
||
document.getElementById('formIdColumn').value = config.id_column;
|
||
}
|
||
if (config.title_column) {
|
||
document.getElementById('formTitleColumn').value = config.title_column;
|
||
}
|
||
|
||
// 处理内容列的选中状态(复选框组)
|
||
const contentColumns = config.content_column ? config.content_column.split(',') : [];
|
||
if (contentColumns.length > 0) {
|
||
const contentGroup = document.getElementById('formContentColumn');
|
||
const checkboxes = contentGroup.querySelectorAll('input[name="contentColumn"]');
|
||
checkboxes.forEach(checkbox => {
|
||
if (contentColumns.includes(checkbox.value)) {
|
||
checkbox.checked = true;
|
||
}
|
||
});
|
||
}
|
||
|
||
// 绑定事件
|
||
bindDatabaseEvents();
|
||
|
||
|
||
|
||
// 绑定认证方式选择的事件
|
||
|
||
}
|
||
}
|
||
|
||
// 数据库事件绑定函数
|
||
function bindDatabaseEvents() {
|
||
// 测试数据库连接
|
||
document.getElementById('testConnectionBtn')?.addEventListener('click', async () => {
|
||
try {
|
||
const host = document.getElementById('formMysqlHost').value;
|
||
const port = parseInt(document.getElementById('formMysqlPort').value);
|
||
const username = document.getElementById('formMysqlUser').value;
|
||
const password = document.getElementById('formMysqlPassword').value;
|
||
const dbType = document.getElementById('formDbType').value;
|
||
|
||
if (!host || !username) {
|
||
alert('请填写主机地址和用户名');
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
// SSH隧道配置
|
||
const useSshTunnel = document.getElementById('useSshTunnel')?.checked || false;
|
||
let sshConfig = {};
|
||
if (useSshTunnel) {
|
||
const sshHost = document.getElementById('formSshHost').value;
|
||
const sshPort = parseInt(document.getElementById('formSshPort').value || 22);
|
||
const sshUsername = document.getElementById('formSshUsername').value;
|
||
const sshPassword = document.getElementById('formSshPassword').value;
|
||
const sshKeyPath = document.getElementById('formSshKeyPathDisplay').value;
|
||
const sshKeyContent = document.getElementById('sshPrivateKeyContent')?.value || '';
|
||
|
||
if (!sshHost || !sshUsername) {
|
||
alert('请填写SSH主机地址和用户名');
|
||
return;
|
||
}
|
||
|
||
sshConfig = {
|
||
use_ssh_tunnel: true,
|
||
ssh_host: sshHost,
|
||
ssh_port: sshPort,
|
||
ssh_username: sshUsername,
|
||
ssh_password: sshPassword,
|
||
ssh_key_path: sshKeyPath,
|
||
ssh_key_content: sshKeyContent
|
||
};
|
||
}
|
||
|
||
// 禁用按钮
|
||
const btn = document.getElementById('testConnectionBtn');
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '🔌 测试中...';
|
||
btn.disabled = true;
|
||
|
||
// 发送请求
|
||
const response = await fetch('/database/databases', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
host,
|
||
port,
|
||
username,
|
||
password,
|
||
db_type: dbType,
|
||
...sshConfig
|
||
})
|
||
});
|
||
|
||
if (response.ok) {
|
||
alert('数据库连接成功!');
|
||
} else {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || '连接失败');
|
||
}
|
||
|
||
} catch (error) {
|
||
alert('数据库连接失败: ' + error.message);
|
||
} finally {
|
||
// 恢复按钮
|
||
const btn = document.getElementById('testConnectionBtn');
|
||
btn.textContent = '🔌 测试连接';
|
||
btn.disabled = false;
|
||
}
|
||
});
|
||
|
||
// 获取数据库列表
|
||
document.getElementById('getDatabasesBtn')?.addEventListener('click', async () => {
|
||
try {
|
||
const host = document.getElementById('formMysqlHost').value;
|
||
const port = parseInt(document.getElementById('formMysqlPort').value);
|
||
const username = document.getElementById('formMysqlUser').value;
|
||
const password = document.getElementById('formMysqlPassword').value;
|
||
const dbType = document.getElementById('formDbType').value;
|
||
|
||
if (!host || !username) {
|
||
alert('请填写主机地址和用户名');
|
||
return;
|
||
}
|
||
|
||
// SSH隧道配置
|
||
const useSshTunnel = document.getElementById('useSshTunnel')?.checked || false;
|
||
let sshConfig = {};
|
||
if (useSshTunnel) {
|
||
const sshHost = document.getElementById('formSshHost').value;
|
||
const sshPort = parseInt(document.getElementById('formSshPort').value || 22);
|
||
const sshUsername = document.getElementById('formSshUsername').value;
|
||
const sshPassword = document.getElementById('formSshPassword').value;
|
||
const sshKeyPath = document.getElementById('formSshKeyPathDisplay').value;
|
||
const sshKeyContent = document.getElementById('sshPrivateKeyContent')?.value || '';
|
||
|
||
if (!sshHost || !sshUsername) {
|
||
alert('请填写SSH主机地址和用户名');
|
||
return;
|
||
}
|
||
|
||
sshConfig = {
|
||
use_ssh_tunnel: true,
|
||
ssh_host: sshHost,
|
||
ssh_port: sshPort,
|
||
ssh_username: sshUsername,
|
||
ssh_password: sshPassword,
|
||
ssh_key_path: sshKeyPath,
|
||
ssh_key_content: sshKeyContent
|
||
};
|
||
}
|
||
|
||
// 禁用按钮
|
||
const btn = document.getElementById('getDatabasesBtn');
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '📋 获取中...';
|
||
btn.disabled = true;
|
||
|
||
// 发送请求
|
||
const response = await fetch('/database/databases', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
host,
|
||
port,
|
||
username,
|
||
password,
|
||
db_type: dbType,
|
||
...sshConfig
|
||
})
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || '获取失败');
|
||
}
|
||
|
||
const data = await response.json();
|
||
const databases = data.databases;
|
||
|
||
// 更新下拉列表
|
||
const select = document.getElementById('formDatabase');
|
||
select.innerHTML = '<option value="">-- 选择数据库 --</option>';
|
||
|
||
databases.forEach(db => {
|
||
const option = document.createElement('option');
|
||
option.value = db;
|
||
option.textContent = db;
|
||
select.appendChild(option);
|
||
});
|
||
|
||
alert(`成功获取 ${databases.length} 个数据库`);
|
||
|
||
} catch (error) {
|
||
alert('获取数据库列表失败: ' + error.message);
|
||
} finally {
|
||
// 恢复按钮
|
||
const btn = document.getElementById('getDatabasesBtn');
|
||
btn.textContent = '📋 获取数据库列表';
|
||
btn.disabled = false;
|
||
}
|
||
});
|
||
|
||
// 获取表列表
|
||
document.getElementById('getTablesBtn')?.addEventListener('click', async () => {
|
||
try {
|
||
const host = document.getElementById('formMysqlHost').value;
|
||
const port = parseInt(document.getElementById('formMysqlPort').value);
|
||
const username = document.getElementById('formMysqlUser').value;
|
||
const password = document.getElementById('formMysqlPassword').value;
|
||
const database = document.getElementById('formDatabase').value;
|
||
const dbType = document.getElementById('formDbType').value;
|
||
|
||
if (!host || !username || !database) {
|
||
alert('请填写完整的数据库连接信息并选择数据库');
|
||
return;
|
||
}
|
||
|
||
// SSH隧道配置
|
||
const useSshTunnel = document.getElementById('useSshTunnel')?.checked || false;
|
||
let sshConfig = {};
|
||
if (useSshTunnel) {
|
||
const sshHost = document.getElementById('formSshHost').value;
|
||
const sshPort = parseInt(document.getElementById('formSshPort').value || 22);
|
||
const sshUsername = document.getElementById('formSshUsername').value;
|
||
const sshPassword = document.getElementById('formSshPassword').value;
|
||
const sshKeyPath = document.getElementById('formSshKeyPathDisplay').value;
|
||
const sshKeyContent = document.getElementById('sshPrivateKeyContent')?.value || '';
|
||
|
||
if (!sshHost || !sshUsername) {
|
||
alert('请填写SSH主机地址和用户名');
|
||
return;
|
||
}
|
||
|
||
sshConfig = {
|
||
use_ssh_tunnel: true,
|
||
ssh_host: sshHost,
|
||
ssh_port: sshPort,
|
||
ssh_username: sshUsername,
|
||
ssh_password: sshPassword,
|
||
ssh_key_path: sshKeyPath,
|
||
ssh_key_content: sshKeyContent
|
||
};
|
||
}
|
||
|
||
// 禁用按钮
|
||
const btn = document.getElementById('getTablesBtn');
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '📋 获取中...';
|
||
btn.disabled = true;
|
||
|
||
// 发送请求
|
||
const response = await fetch('/database/tables', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
host,
|
||
port,
|
||
username,
|
||
password,
|
||
database,
|
||
db_type: dbType,
|
||
...sshConfig
|
||
})
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || '获取失败');
|
||
}
|
||
|
||
const data = await response.json();
|
||
const tables = data.tables;
|
||
|
||
// 更新下拉列表
|
||
const select = document.getElementById('formTableName');
|
||
select.innerHTML = '<option value="">-- 选择表 --</option>';
|
||
|
||
tables.forEach(table => {
|
||
const option = document.createElement('option');
|
||
option.value = table;
|
||
option.textContent = table;
|
||
select.appendChild(option);
|
||
});
|
||
|
||
alert(`成功获取 ${tables.length} 个表`);
|
||
|
||
} catch (error) {
|
||
alert('获取表列表失败: ' + error.message);
|
||
} finally {
|
||
// 恢复按钮
|
||
const btn = document.getElementById('getTablesBtn');
|
||
btn.textContent = '📋 获取表列表';
|
||
btn.disabled = false;
|
||
}
|
||
});
|
||
|
||
// 获取表结构
|
||
document.getElementById('getTableStructureBtn')?.addEventListener('click', async () => {
|
||
try {
|
||
const host = document.getElementById('formMysqlHost').value;
|
||
const port = parseInt(document.getElementById('formMysqlPort').value);
|
||
const username = document.getElementById('formMysqlUser').value;
|
||
const password = document.getElementById('formMysqlPassword').value;
|
||
const database = document.getElementById('formDatabase').value;
|
||
const table_name = document.getElementById('formTableName').value;
|
||
const dbType = document.getElementById('formDbType').value;
|
||
|
||
if (!host || !username || !database || !table_name) {
|
||
alert('请填写完整的数据库连接信息并选择数据库和表');
|
||
return;
|
||
}
|
||
|
||
// SSH隧道配置
|
||
const useSshTunnel = document.getElementById('useSshTunnel')?.checked || false;
|
||
let sshConfig = {};
|
||
if (useSshTunnel) {
|
||
const sshHost = document.getElementById('formSshHost').value;
|
||
const sshPort = parseInt(document.getElementById('formSshPort').value || 22);
|
||
const sshUsername = document.getElementById('formSshUsername').value;
|
||
const sshPassword = document.getElementById('formSshPassword').value;
|
||
const sshKeyPath = document.getElementById('formSshKeyPathDisplay').value;
|
||
const sshKeyContent = document.getElementById('sshPrivateKeyContent')?.value || '';
|
||
|
||
if (!sshHost || !sshUsername) {
|
||
alert('请填写SSH主机地址和用户名');
|
||
return;
|
||
}
|
||
|
||
sshConfig = {
|
||
use_ssh_tunnel: true,
|
||
ssh_host: sshHost,
|
||
ssh_port: sshPort,
|
||
ssh_username: sshUsername,
|
||
ssh_password: sshPassword,
|
||
ssh_key_path: sshKeyPath,
|
||
ssh_key_content: sshKeyContent
|
||
};
|
||
}
|
||
|
||
// 禁用按钮
|
||
const btn = document.getElementById('getTableStructureBtn');
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '📋 获取中...';
|
||
btn.disabled = true;
|
||
|
||
// 发送请求
|
||
const response = await fetch('/database/table-structure', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
host,
|
||
port,
|
||
username,
|
||
password,
|
||
database,
|
||
table_name,
|
||
db_type: dbType,
|
||
...sshConfig
|
||
})
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || '获取失败');
|
||
}
|
||
|
||
const data = await response.json();
|
||
const columns = data.columns;
|
||
|
||
// 更新所有列选择器
|
||
updateColumnSelectors(columns);
|
||
|
||
alert(`成功获取表结构,共 ${columns.length} 列`);
|
||
|
||
} catch (error) {
|
||
alert('获取表结构失败: ' + error.message);
|
||
} finally {
|
||
// 恢复按钮
|
||
const btn = document.getElementById('getTableStructureBtn');
|
||
btn.textContent = '📋 获取表结构';
|
||
btn.disabled = false;
|
||
}
|
||
});
|
||
|
||
// 自动填充已有的列信息
|
||
const fillExistingColumns = () => {
|
||
const database = document.getElementById('formDatabase').value;
|
||
const table_name = document.getElementById('formTableName').value;
|
||
|
||
if (database && table_name && currentConfig) {
|
||
// 如果有当前配置,尝试获取表结构
|
||
document.getElementById('getTableStructureBtn')?.click();
|
||
}
|
||
};
|
||
|
||
// 监听数据库和表选择变化
|
||
document.getElementById('formDatabase')?.addEventListener('change', async () => {
|
||
// 清除表选择
|
||
document.getElementById('formTableName').innerHTML = '<option value="">-- 选择表 --</option>';
|
||
// 清除列选择
|
||
clearColumnSelectors();
|
||
});
|
||
|
||
document.getElementById('formTableName')?.addEventListener('change', fillExistingColumns);
|
||
}
|
||
|
||
// 更新列选择器
|
||
function updateColumnSelectors(columns) {
|
||
// ID列选择器
|
||
const idSelect = document.getElementById('formIdColumn');
|
||
idSelect.innerHTML = '<option value="">-- 选择ID列 --</option>';
|
||
|
||
// 内容列选择器(改为复选框组)
|
||
const contentGroup = document.getElementById('formContentColumn');
|
||
contentGroup.innerHTML = '';
|
||
|
||
// 标题列选择器
|
||
const titleSelect = document.getElementById('formTitleColumn');
|
||
titleSelect.innerHTML = '<option value="">-- 选择标题列(可选)--</option>';
|
||
|
||
// 更新时间列选择器
|
||
const updatedAtSelect = document.getElementById('formUpdatedAtColumn');
|
||
updatedAtSelect.innerHTML = '<option value="">-- 选择更新时间列(可选)--</option>';
|
||
|
||
// 文件列选择器 - 只在元素存在时操作(database类型配置中已删除)
|
||
const fileSelect = document.getElementById('formFileColumn');
|
||
|
||
// 填充选项
|
||
columns.forEach(col => {
|
||
const colName = col.name;
|
||
const colType = col.type;
|
||
|
||
// 添加到ID列选择器(优先选择主键)
|
||
const idOption = document.createElement('option');
|
||
idOption.value = colName;
|
||
idOption.textContent = colName + (col.key === 'PRI' ? ' (主键)' : '');
|
||
idSelect.appendChild(idOption);
|
||
|
||
// 如果是主键,默认选中
|
||
if (col.key === 'PRI') {
|
||
idSelect.value = colName;
|
||
}
|
||
|
||
// 添加到内容列选择器(复选框组)
|
||
const checkboxItem = document.createElement('label');
|
||
// 类型为空时不显示类型信息
|
||
const displayText = colType ? `${colName} (${colType})` : colName;
|
||
checkboxItem.innerHTML = `
|
||
<input type="checkbox" name="contentColumn" value="${colName}">
|
||
<span>${displayText}</span>
|
||
`;
|
||
contentGroup.appendChild(checkboxItem);
|
||
|
||
// 添加到文件列选择器 - 只在元素存在时操作(database类型配置中已删除)
|
||
if (fileSelect) {
|
||
const fileOption = document.createElement('option');
|
||
fileOption.value = colName;
|
||
fileOption.textContent = colType ? `${colName} (${colType})` : colName;
|
||
fileSelect.appendChild(fileOption);
|
||
}
|
||
|
||
// 添加到标题列选择器
|
||
const titleOption = document.createElement('option');
|
||
titleOption.value = colName;
|
||
titleOption.textContent = colType ? `${colName} (${colType})` : colName;
|
||
titleSelect.appendChild(titleOption);
|
||
|
||
// 添加到更新时间列选择器
|
||
const updatedAtOption = document.createElement('option');
|
||
updatedAtOption.value = colName;
|
||
updatedAtOption.textContent = colType ? `${colName} (${colType})` : colName;
|
||
updatedAtSelect.appendChild(updatedAtOption);
|
||
});
|
||
|
||
// 如果有当前配置,保持选中状态
|
||
if (currentConfig) {
|
||
const config = currentConfig.config;
|
||
if (config.id_column) {
|
||
idSelect.value = config.id_column;
|
||
}
|
||
|
||
// 处理内容列的选中状态(复选框组)
|
||
const contentColumns = config.content_column ? config.content_column.split(',') : [];
|
||
if (contentColumns.length > 0) {
|
||
const checkboxes = contentGroup.querySelectorAll('input[name="contentColumn"]');
|
||
checkboxes.forEach(checkbox => {
|
||
if (contentColumns.includes(checkbox.value)) {
|
||
checkbox.checked = true;
|
||
}
|
||
});
|
||
}
|
||
|
||
if (config.title_column) {
|
||
titleSelect.value = config.title_column;
|
||
}
|
||
if (config.updated_at_column) {
|
||
updatedAtSelect.value = config.updated_at_column;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 清除列选择器
|
||
function clearColumnSelectors() {
|
||
document.getElementById('formIdColumn').innerHTML = '<option value="">-- 选择ID列 --</option>';
|
||
document.getElementById('formContentColumn').innerHTML = '';
|
||
|
||
// 文件列选择器 - 只在元素存在时操作(database类型配置中已删除)
|
||
const fileSelect = document.getElementById('formFileColumn');
|
||
if (fileSelect) {
|
||
fileSelect.innerHTML = '<option value="">-- 选择文件列(可选)--</option>';
|
||
}
|
||
|
||
document.getElementById('formTitleColumn').innerHTML = '<option value="">-- 选择标题列(可选)--</option>';
|
||
document.getElementById('formUpdatedAtColumn').innerHTML = '<option value="">-- 选择更新时间列(可选)--</option>';
|
||
}
|
||
|
||
// 获取数据表结构
|
||
async function fetchTableStructure(config) {
|
||
try {
|
||
// 确保有必要的连接信息
|
||
if (!config.mysql_host || !config.mysql_user) {
|
||
throw new Error('缺少数据库连接信息');
|
||
}
|
||
|
||
// 发送请求获取表结构
|
||
const response = await fetch('/database/table-structure', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
host: config.mysql_host,
|
||
port: config.mysql_port || 3306,
|
||
username: config.mysql_user,
|
||
password: config.mysql_password || '',
|
||
database: config.database,
|
||
table_name: config.table_name,
|
||
db_type: config.db_type || 'mysql'
|
||
})
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || '获取表结构失败');
|
||
}
|
||
|
||
const data = await response.json();
|
||
return data.columns;
|
||
} catch (error) {
|
||
console.error('获取表结构失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 显示欢迎界面
|
||
function showWelcomeScreen() {
|
||
document.getElementById('welcomeScreen').style.display = 'flex';
|
||
document.getElementById('configScreen').style.display = 'none';
|
||
currentConfig = null;
|
||
}
|
||
|
||
// 显示配置详情界面
|
||
function showConfigScreen() {
|
||
document.getElementById('welcomeScreen').style.display = 'none';
|
||
document.getElementById('configScreen').style.display = 'flex';
|
||
}
|
||
|
||
// 显示添加配置模态框
|
||
function showAddConfigModal() {
|
||
document.getElementById('addConfigModal').classList.add('show');
|
||
|
||
// 添加配置类型选择的事件监听器
|
||
const configTypeSelect = document.getElementById('configType');
|
||
const dbTypeGroup = document.getElementById('dbTypeGroup');
|
||
|
||
configTypeSelect.addEventListener('change', function() {
|
||
if (this.value === 'database') {
|
||
dbTypeGroup.style.display = 'block';
|
||
} else {
|
||
dbTypeGroup.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 触发一次change事件,确保初始状态正确
|
||
configTypeSelect.dispatchEvent(new Event('change'));
|
||
}
|
||
|
||
// 隐藏添加配置模态框
|
||
function hideAddConfigModal() {
|
||
document.getElementById('addConfigModal').classList.remove('show');
|
||
document.getElementById('addConfigForm').reset();
|
||
}
|
||
|
||
// 直接创建配置(根据下拉框选择的类型)
|
||
async function handleAddConfigDirectly() {
|
||
// 获取选择的配置类型
|
||
const configTypeSelect = document.getElementById('configTypeSelect');
|
||
const configType = configTypeSelect.value;
|
||
|
||
// 检查是否选择了类型
|
||
if (!configType) {
|
||
alert('请选择配置类型');
|
||
return;
|
||
}
|
||
|
||
// 根据配置类型创建一个临时的配置对象
|
||
let tempConfig;
|
||
|
||
if (configType === 'database') {
|
||
tempConfig = {
|
||
type: configType,
|
||
mysql_host: '',
|
||
mysql_port: 3306,
|
||
mysql_password: '',
|
||
database: '',
|
||
table_name: '',
|
||
id_column: '',
|
||
content_column: '',
|
||
db_type: 'mysql'
|
||
};
|
||
} else if (configType === 'folder') {
|
||
tempConfig = {
|
||
type: configType,
|
||
folder_path: '',
|
||
recursive: true,
|
||
ignore_patterns: [],
|
||
host: '',
|
||
port: 22,
|
||
username: '',
|
||
password: ''
|
||
};
|
||
} else if (configType === 'git') {
|
||
tempConfig = {
|
||
type: configType,
|
||
git_url: '',
|
||
branch: 'main',
|
||
protocol: 'https',
|
||
https_token: '',
|
||
ssh_key: '',
|
||
poll_interval: 300
|
||
};
|
||
} else {
|
||
alert('不支持的配置类型');
|
||
return;
|
||
}
|
||
|
||
// 直接设置当前配置,不立即发送到后端
|
||
currentConfig = {
|
||
id: null, // 临时ID,保存时由后端生成
|
||
config: tempConfig
|
||
};
|
||
|
||
// 渲染配置表单
|
||
generateConfigForm(tempConfig);
|
||
|
||
// 切换到配置详情界面
|
||
showConfigScreen();
|
||
|
||
// 更新配置标题为"新增数据源"
|
||
document.getElementById('configTitle').textContent = '新增数据源';
|
||
|
||
// 重置类型选择
|
||
configTypeSelect.value = '';
|
||
|
||
// 提示用户需要填写配置信息
|
||
alert('请填写配置信息,然后点击保存按钮创建配置');
|
||
}
|
||
|
||
// 处理添加配置(旧的模态框方式,保留以便兼容)
|
||
async function handleAddConfig(event) {
|
||
event.preventDefault();
|
||
|
||
// 获取表单数据
|
||
const formData = new FormData(event.target);
|
||
const configType = formData.get('type');
|
||
const configName = formData.get('name');
|
||
const dbType = formData.get('dbType') || 'mysql';
|
||
|
||
// 根据配置类型创建不同的配置对象
|
||
let configData;
|
||
|
||
if (configType === 'database') {
|
||
configData = {
|
||
name: configName,
|
||
type: configType,
|
||
mysql_host: '',
|
||
mysql_port: 3306,
|
||
mysql_password: '',
|
||
database: '',
|
||
table_name: '',
|
||
id_column: '',
|
||
content_column: '',
|
||
db_type: dbType
|
||
};
|
||
} else if (configType === 'folder') {
|
||
configData = {
|
||
name: configName,
|
||
type: configType,
|
||
folder_path: '',
|
||
recursive: true,
|
||
ignore_patterns: [],
|
||
host: '',
|
||
port: 22,
|
||
username: '',
|
||
password: ''
|
||
};
|
||
} else {
|
||
alert('不支持的配置类型');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 创建新配置
|
||
const response = await fetch('/folder-configs', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(configData)
|
||
});
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || 'Failed to create config');
|
||
}
|
||
|
||
const newConfig = await response.json();
|
||
|
||
// 隐藏模态框
|
||
hideAddConfigModal();
|
||
|
||
// 刷新配置列表并等待完成
|
||
await loadConfigs();
|
||
|
||
// 加载新配置详情
|
||
loadConfigDetails(newConfig.id);
|
||
|
||
} catch (error) {
|
||
console.error('Error creating config:', error);
|
||
alert('创建配置失败: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// 保存配置
|
||
async function saveConfig() {
|
||
if (!currentConfig) {
|
||
alert('没有选中的配置');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 收集表单数据
|
||
const formData = collectFormData();
|
||
|
||
console.log(formData);
|
||
|
||
// 检查必要的信息是否都填写了
|
||
let missingFields = [];
|
||
|
||
// 检查基本信息
|
||
if (!formData.type) missingFields.push('配置类型');
|
||
|
||
// 自动生成配置名称(如果为空)
|
||
if (!formData.name) {
|
||
let generatedName = '';
|
||
|
||
if (formData.type === 'database') {
|
||
// 数据库配置:database_数据库名_表名
|
||
generatedName = `database_${formData.database || 'unknown'}_${formData.table_name || 'unknown'}`;
|
||
} else if (formData.type === 'folder') {
|
||
// 文件夹:folder_主机_文件夹路径(替换特殊字符)
|
||
const folderName = formData.folder_path ? formData.folder_path.replace(/[\\/:*?"<>|]/g, '_') : 'unknown';
|
||
generatedName = `folder_${formData.host || 'unknown'}_${folderName}`;
|
||
} else if (formData.type === 'git') {
|
||
// Git配置:git_仓库地址(替换特殊字符)
|
||
const repoName = formData.git_url ? formData.git_url.replace(/[\\/:*?"<>|]/g, '_') : 'unknown';
|
||
generatedName = `git_${repoName}_${formData.branch || 'main'}`;
|
||
} else {
|
||
// 不支持的配置类型
|
||
alert('不支持的配置类型');
|
||
return;
|
||
}
|
||
|
||
formData.name = generatedName;
|
||
}
|
||
|
||
// 根据不同类型检查特定字段
|
||
if (formData.type === 'database') {
|
||
if (!formData.mysql_host) missingFields.push('数据库主机');
|
||
if (!formData.mysql_port) missingFields.push('数据库端口');
|
||
if (!formData.mysql_user) missingFields.push('数据库用户名');
|
||
if (!formData.mysql_password) missingFields.push('数据库密码');
|
||
if (!formData.database) missingFields.push('数据库名称');
|
||
if (!formData.table_name) missingFields.push('表名');
|
||
if (!formData.id_column) missingFields.push('ID列');
|
||
if (!formData.content_column || formData.content_column.trim() === '') {
|
||
missingFields.push('内容列');
|
||
}
|
||
} else if (formData.type === 'folder') {
|
||
if (!formData.folder_path) missingFields.push('文件夹路径');
|
||
if (!formData.host) missingFields.push('主机地址');
|
||
if (!formData.port) missingFields.push('端口');
|
||
if (!formData.username) missingFields.push('用户名');
|
||
if (!formData.password) missingFields.push('密码');
|
||
} else if (formData.type === 'git') {
|
||
if (!formData.git_url) missingFields.push('Git仓库地址');
|
||
if (!formData.branch) missingFields.push('分支名称');
|
||
if (!formData.protocol) missingFields.push('协议类型');
|
||
}
|
||
|
||
// 如果有缺失的字段,提示用户
|
||
if (missingFields.length > 0) {
|
||
alert('请填写以下必要信息:\n' + missingFields.join('\n'));
|
||
return;
|
||
}
|
||
|
||
// 更新当前配置 - 直接使用新的表单数据作为完整配置,这样可以删除不再需要的旧字段
|
||
currentConfig.config = formData;
|
||
|
||
// 根据当前配置ID决定使用POST还是PUT方法
|
||
let response;
|
||
if (currentConfig.id === null) {
|
||
// 新配置,使用POST方法创建
|
||
response = await fetch('/folder-configs', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(currentConfig.config)
|
||
});
|
||
} else {
|
||
// 现有配置,使用PUT方法更新
|
||
response = await fetch(`/folder-configs/${currentConfig.id}`, {
|
||
method: 'PUT',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(currentConfig.config)
|
||
});
|
||
}
|
||
|
||
if (!response.ok) {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || 'Failed to save config');
|
||
}
|
||
|
||
// 获取保存/更新后的配置
|
||
const updatedConfig = await response.json();
|
||
|
||
// 检查是否是新配置(POST)
|
||
const isNewConfig = currentConfig.id === null;
|
||
|
||
// 更新当前配置的ID
|
||
currentConfig.id = updatedConfig.id;
|
||
|
||
// 刷新配置列表
|
||
await loadConfigs();
|
||
|
||
// 使用新的ID重新加载配置详情
|
||
loadConfigDetails(updatedConfig.id);
|
||
|
||
// 根据配置类型执行不同的同步操作
|
||
try {
|
||
// 数据源名称是配置的ID,而不是config中的name字段
|
||
const sourceName = updatedConfig.id;
|
||
|
||
if (isNewConfig) {
|
||
// 新配置(POST):保存成功后,立刻启动数据源的同步操作
|
||
alert('配置保存成功,正在启动同步...');
|
||
|
||
// 启动同步
|
||
const syncResponse = await fetch('/sync', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
full_sync: true,
|
||
source_name: sourceName
|
||
})
|
||
});
|
||
|
||
if (syncResponse.ok) {
|
||
const syncResult = await syncResponse.json();
|
||
alert('同步已启动:' + syncResult.message);
|
||
} else {
|
||
throw new Error('同步启动失败');
|
||
}
|
||
} else {
|
||
// 现有配置(PUT):保存成功后,先删除chromadb中已有的知识,然后立刻启动数据源的同步操作
|
||
alert('配置更新成功,正在清理旧数据并启动同步...');
|
||
|
||
// 1. 删除旧数据
|
||
const deleteResponse = await fetch(`/documents/source/${encodeURIComponent(sourceName)}`, {
|
||
method: 'DELETE'
|
||
});
|
||
|
||
if (!deleteResponse.ok) {
|
||
throw new Error('删除旧数据失败');
|
||
}
|
||
|
||
// 2. 启动同步
|
||
const syncResponse = await fetch('/sync', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
full_sync: true,
|
||
source_name: sourceName
|
||
})
|
||
});
|
||
|
||
if (syncResponse.ok) {
|
||
const syncResult = await syncResponse.json();
|
||
alert('数据清理完成,同步已启动:' + syncResult.message);
|
||
} else {
|
||
throw new Error('同步启动失败');
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('同步操作失败:', error);
|
||
alert('配置保存成功,但同步操作失败:' + error.message);
|
||
}
|
||
|
||
// 重新渲染配置列表
|
||
renderConfigList();
|
||
|
||
} catch (error) {
|
||
console.error('Error saving config:', error);
|
||
alert('保存配置失败: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// 收集表单数据
|
||
function collectFormData() {
|
||
const formData = {};
|
||
|
||
// 基本信息
|
||
formData.name = document.getElementById('formName').value;
|
||
formData.type = document.getElementById('formType').value;
|
||
|
||
// 文件夹配置
|
||
if (formData.type === 'folder') {
|
||
formData.folder_path = document.getElementById('formFolderPath').value;
|
||
formData.recursive = document.getElementById('formRecursive').checked;
|
||
|
||
const ignorePatterns = document.getElementById('formIgnorePatterns').value;
|
||
formData.ignore_patterns = ignorePatterns ? ignorePatterns.split(',').map(p => p.trim()) : [];
|
||
|
||
// 文件夹的SSH配置
|
||
formData.host = document.getElementById('formHost').value;
|
||
formData.port = parseInt(document.getElementById('formPort').value);
|
||
formData.username = document.getElementById('formUsername').value;
|
||
formData.password = document.getElementById('formPassword').value;
|
||
}
|
||
|
||
// 数据库配置
|
||
if (formData.type === 'database') {
|
||
formData.database = document.getElementById('formDatabase').value;
|
||
formData.table_name = document.getElementById('formTableName').value;
|
||
formData.id_column = document.getElementById('formIdColumn').value;
|
||
|
||
// 收集多选的内容列
|
||
const contentGroup = document.getElementById('formContentColumn');
|
||
const checkedBoxes = Array.from(contentGroup.querySelectorAll('input[name="contentColumn"]:checked'));
|
||
formData.content_column = checkedBoxes.map(checkbox => checkbox.value).join(','); // 转换为逗号分隔的字符串
|
||
|
||
formData.title_column = document.getElementById('formTitleColumn').value;
|
||
formData.updated_at_column = document.getElementById('formUpdatedAtColumn').value;
|
||
|
||
// 数据库连接配置
|
||
formData.mysql_host = document.getElementById('formMysqlHost').value;
|
||
formData.mysql_port = parseInt(document.getElementById('formMysqlPort').value);
|
||
formData.mysql_user = document.getElementById('formMysqlUser').value;
|
||
formData.mysql_password = document.getElementById('formMysqlPassword').value;
|
||
formData.db_type = document.getElementById('formDbType').value;
|
||
|
||
|
||
|
||
}
|
||
|
||
// Git配置
|
||
if (formData.type === 'git') {
|
||
formData.git_url = document.getElementById('formGitUrl').value;
|
||
formData.branch = document.getElementById('formBranch').value;
|
||
formData.protocol = document.getElementById('formProtocol').value;
|
||
formData.https_token = document.getElementById('formHttpsToken').value;
|
||
formData.ssh_key = document.getElementById('formSshKey').value;
|
||
formData.poll_interval = parseInt(document.getElementById('formPollInterval').value);
|
||
}
|
||
|
||
return formData;
|
||
}
|
||
|
||
// 显示删除确认
|
||
function showDeleteConfirm() {
|
||
if (!currentConfig) {
|
||
alert('没有选中的配置');
|
||
return;
|
||
}
|
||
|
||
document.getElementById('deleteConfirmModal').classList.add('show');
|
||
}
|
||
|
||
// 隐藏删除确认模态框
|
||
function hideDeleteConfirmModal() {
|
||
document.getElementById('deleteConfirmModal').classList.remove('show');
|
||
}
|
||
|
||
// 确认删除配置
|
||
async function confirmDeleteConfig() {
|
||
if (!currentConfig) {
|
||
alert('没有选中的配置');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`/folder-configs/${currentConfig.id}`, {
|
||
method: 'DELETE'
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Failed to delete config');
|
||
}
|
||
|
||
// 隐藏模态框
|
||
hideDeleteConfirmModal();
|
||
|
||
// 刷新配置列表
|
||
loadConfigs();
|
||
|
||
// 显示欢迎界面
|
||
showWelcomeScreen();
|
||
|
||
alert('配置删除成功');
|
||
|
||
} catch (error) {
|
||
console.error('Error deleting config:', error);
|
||
alert('删除配置失败: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// Git事件绑定函数
|
||
function bindGitEvents() {
|
||
// 协议切换逻辑
|
||
const protocolSelect = document.getElementById('formProtocol');
|
||
const httpsTokenGroup = document.getElementById('httpsTokenGroup');
|
||
const sshKeyGroup = document.getElementById('sshKeyGroup');
|
||
|
||
protocolSelect.addEventListener('change', function() {
|
||
const protocol = this.value;
|
||
if (protocol === 'https') {
|
||
httpsTokenGroup.style.display = 'block';
|
||
sshKeyGroup.style.display = 'none';
|
||
} else if (protocol === 'ssh') {
|
||
httpsTokenGroup.style.display = 'none';
|
||
sshKeyGroup.style.display = 'block';
|
||
}
|
||
});
|
||
|
||
// 触发一次change事件,确保初始状态正确
|
||
protocolSelect.dispatchEvent(new Event('change'));
|
||
|
||
// 测试Git连接
|
||
document.getElementById('testGitConnectionBtn')?.addEventListener('click', async () => {
|
||
try {
|
||
const gitUrl = document.getElementById('formGitUrl').value;
|
||
const branch = document.getElementById('formBranch').value;
|
||
const protocol = document.getElementById('formProtocol').value;
|
||
const httpsToken = document.getElementById('formHttpsToken').value;
|
||
const sshKey = document.getElementById('formSshKey').value;
|
||
|
||
if (!gitUrl) {
|
||
alert('请填写Git仓库地址');
|
||
return;
|
||
}
|
||
|
||
// 禁用按钮
|
||
const btn = document.getElementById('testGitConnectionBtn');
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '🔌 测试中...';
|
||
btn.disabled = true;
|
||
|
||
// 发送请求
|
||
const response = await fetch('/git/test-connection', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
git_url: gitUrl,
|
||
branch: branch,
|
||
protocol: protocol,
|
||
https_token: httpsToken,
|
||
ssh_key: sshKey
|
||
})
|
||
});
|
||
|
||
if (response.ok) {
|
||
const result = await response.json();
|
||
alert('Git连接测试成功!');
|
||
} else {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || '连接失败');
|
||
}
|
||
|
||
} catch (error) {
|
||
alert('Git连接测试失败: ' + error.message);
|
||
} finally {
|
||
// 恢复按钮
|
||
const btn = document.getElementById('testGitConnectionBtn');
|
||
btn.textContent = '🔌 测试Git连接';
|
||
btn.disabled = false;
|
||
}
|
||
});
|
||
}
|
||
|
||
// 为文件夹配置添加事件绑定
|
||
function bindFolderEvents() {
|
||
// 测试SSH连接
|
||
const testBtn = document.getElementById('testSshConnectionBtn');
|
||
if (testBtn) {
|
||
testBtn.onclick = async () => {
|
||
try {
|
||
const host = document.getElementById('formHost').value;
|
||
const port = parseInt(document.getElementById('formPort').value);
|
||
const username = document.getElementById('formUsername').value;
|
||
const password = document.getElementById('formPassword').value;
|
||
|
||
if (!host || !username) {
|
||
alert('请填写主机地址和用户名');
|
||
return;
|
||
}
|
||
|
||
// 禁用按钮
|
||
const btn = document.getElementById('testSshConnectionBtn');
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '🔌 测试中...';
|
||
btn.disabled = true;
|
||
|
||
// 发送请求
|
||
const response = await fetch('/folder/test-connection', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
host,
|
||
port,
|
||
username,
|
||
password
|
||
})
|
||
});
|
||
|
||
if (response.ok) {
|
||
alert('SSH连接成功!');
|
||
} else {
|
||
const errorData = await response.json();
|
||
throw new Error(errorData.detail || '连接失败');
|
||
}
|
||
|
||
} catch (error) {
|
||
alert('SSH连接失败: ' + error.message);
|
||
} finally {
|
||
// 恢复按钮
|
||
const btn = document.getElementById('testSshConnectionBtn');
|
||
btn.textContent = '🔌 测试SSH连接';
|
||
btn.disabled = false;
|
||
}
|
||
};
|
||
}
|
||
}
|
||
|
||
// 添加CSS样式(确保模态框显示)
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
.modal {
|
||
display: none;
|
||
}
|
||
.modal.show {
|
||
display: block;
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
|
||
// 绑定事件
|
||
bindDatabaseEvents();
|
||
|
||
|