|
@@ -102,6 +102,7 @@
|
|
|
aitableSecretInput.value = '';
|
|
aitableSecretInput.value = '';
|
|
|
aitableSecretInput.placeholder = settings.aitableSecretSet ? '已设置,留空保持不变(输入 - 清除)' : '未设置';
|
|
aitableSecretInput.placeholder = settings.aitableSecretSet ? '已设置,留空保持不变(输入 - 清除)' : '未设置';
|
|
|
await loadModels(settings.model);
|
|
await loadModels(settings.model);
|
|
|
|
|
+ await loadStorageSettings();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function setPanelStatus(badgeId, message, ok) {
|
|
function setPanelStatus(badgeId, message, ok) {
|
|
@@ -292,6 +293,83 @@
|
|
|
}
|
|
}
|
|
|
window.shanAitableAction = aitableAction;
|
|
window.shanAitableAction = aitableAction;
|
|
|
|
|
|
|
|
|
|
+ async function loadStorageSettings() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch('/api/settings/storage');
|
|
|
|
|
+ if (!response.ok) return;
|
|
|
|
|
+ const st = await response.json();
|
|
|
|
|
+ document.getElementById('settingsStorageDriver').value = st.driver || 'sqlite';
|
|
|
|
|
+ document.getElementById('settingsPgHost').value = st.postgres.host || '';
|
|
|
|
|
+ document.getElementById('settingsPgPort').value = st.postgres.port || 5432;
|
|
|
|
|
+ document.getElementById('settingsPgDatabase').value = st.postgres.database || '';
|
|
|
|
|
+ document.getElementById('settingsPgUser').value = st.postgres.user || '';
|
|
|
|
|
+ document.getElementById('settingsPgSsl').checked = !!st.postgres.ssl;
|
|
|
|
|
+ const pw = document.getElementById('settingsPgPassword');
|
|
|
|
|
+ pw.value = '';
|
|
|
|
|
+ pw.placeholder = st.postgres.passwordSet ? '已设置,留空保持不变' : '数据库密码';
|
|
|
|
|
+ toggleStorageFields();
|
|
|
|
|
+ if (!st.pgInstalled) setPanelStatus('storageTestStatus', '未安装 pg 驱动', false);
|
|
|
|
|
+ } catch { /* 读取失败不阻塞设置页 */ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function toggleStorageFields() {
|
|
|
|
|
+ const isPg = document.getElementById('settingsStorageDriver').value === 'postgres';
|
|
|
|
|
+ document.getElementById('settingsPgFields').hidden = !isPg;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function storagePayload() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ driver: document.getElementById('settingsStorageDriver').value,
|
|
|
|
|
+ pgHost: val('settingsPgHost'),
|
|
|
|
|
+ pgPort: Number(val('settingsPgPort')) || 5432,
|
|
|
|
|
+ pgDatabase: val('settingsPgDatabase'),
|
|
|
|
|
+ pgUser: val('settingsPgUser'),
|
|
|
|
|
+ pgPassword: document.getElementById('settingsPgPassword').value,
|
|
|
|
|
+ pgSsl: document.getElementById('settingsPgSsl').checked,
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async function testStorage() {
|
|
|
|
|
+ const btn = document.getElementById('btnTestStorage');
|
|
|
|
|
+ btn.disabled = true;
|
|
|
|
|
+ setPanelStatus('storageTestStatus', '连接中…');
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch('/api/settings/storage/test', {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
+ body: JSON.stringify(storagePayload()),
|
|
|
|
|
+ });
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+ if (data.ok) setPanelStatus('storageTestStatus', `连接成功 · ${data.version || ''}`, true);
|
|
|
|
|
+ else setPanelStatus('storageTestStatus', data.error || '连接失败', false);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ setPanelStatus('storageTestStatus', error.message, false);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ btn.disabled = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async function saveStorageSettings() {
|
|
|
|
|
+ const btn = document.getElementById('btnSaveStorage');
|
|
|
|
|
+ btn.disabled = true;
|
|
|
|
|
+ setPanelStatus('storageSaveStatus', '保存中…');
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch('/api/settings/storage', {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
+ body: JSON.stringify(storagePayload()),
|
|
|
|
|
+ });
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+ if (!response.ok) throw new Error(data.error || `HTTP ${response.status}`);
|
|
|
|
|
+ setPanelStatus('storageSaveStatus', data.restartRequired ? '已保存,重启服务后生效' : '已保存', true);
|
|
|
|
|
+ await loadStorageSettings();
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ setPanelStatus('storageSaveStatus', error.message, false);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ btn.disabled = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
async function initSettings() {
|
|
async function initSettings() {
|
|
|
// 事件绑定与数据加载分离:加载失败重进设置页时不会重复 addEventListener
|
|
// 事件绑定与数据加载分离:加载失败重进设置页时不会重复 addEventListener
|
|
|
if (!settingsBound) {
|
|
if (!settingsBound) {
|
|
@@ -307,6 +385,9 @@
|
|
|
document.getElementById('btnTestImage').addEventListener('click', testImageApi);
|
|
document.getElementById('btnTestImage').addEventListener('click', testImageApi);
|
|
|
document.getElementById('btnTestChat').addEventListener('click', testChatModel);
|
|
document.getElementById('btnTestChat').addEventListener('click', testChatModel);
|
|
|
document.getElementById('btnTestAitable').addEventListener('click', () => aitableAction('test'));
|
|
document.getElementById('btnTestAitable').addEventListener('click', () => aitableAction('test'));
|
|
|
|
|
+ document.getElementById('btnSaveStorage').addEventListener('click', saveStorageSettings);
|
|
|
|
|
+ document.getElementById('btnTestStorage').addEventListener('click', testStorage);
|
|
|
|
|
+ document.getElementById('settingsStorageDriver').addEventListener('change', toggleStorageFields);
|
|
|
const bindHelpPopover = (btnId, popId) => {
|
|
const bindHelpPopover = (btnId, popId) => {
|
|
|
const btn = document.getElementById(btnId);
|
|
const btn = document.getElementById(btnId);
|
|
|
const pop = document.getElementById(popId);
|
|
const pop = document.getElementById(popId);
|