123
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
this.currentContainer = '';
|
||||
this.logStream = null;
|
||||
this.logsBuffer = [];
|
||||
this.currentSettings = null;
|
||||
this.currentPage = 1;
|
||||
this.limit = 10;
|
||||
this.editingRecord = null;
|
||||
@@ -152,6 +153,7 @@
|
||||
document.getElementById('logsButton').classList.toggle('hidden', !this.getPermissions().canViewLogs);
|
||||
document.getElementById('usersButton').classList.toggle('hidden', !this.getPermissions().canManageUsers);
|
||||
document.getElementById('backupsButton').classList.toggle('hidden', !this.getPermissions().canManageUsers);
|
||||
document.getElementById('settingsButton').classList.toggle('hidden', !this.getPermissions().canManageUsers);
|
||||
document.getElementById('auditButton').classList.toggle('hidden', !this.getPermissions().canManageUsers);
|
||||
document.querySelector('button[onclick="app.showSQLPanel()"]').style.display = this.getPermissions().canRunSql ? '' : 'none';
|
||||
document.querySelector('button[onclick="app.showCreateTableModal()"]').style.display = this.getPermissions().canCreate ? '' : 'none';
|
||||
@@ -1226,6 +1228,11 @@
|
||||
document.getElementById('backupsModal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
async showSettingsModal() {
|
||||
await this.loadSettings();
|
||||
document.getElementById('settingsModal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
async loadBackups() {
|
||||
try {
|
||||
const response = await fetch('/api/backups');
|
||||
@@ -1239,7 +1246,7 @@
|
||||
<div class="border border-slate-200 rounded-xl p-4 flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<div class="font-medium text-slate-800">${backup.filename}</div>
|
||||
<div class="text-sm text-slate-500">${backup.createdAt} · ${backup.size} bytes</div>
|
||||
<div class="text-sm text-slate-500">${backup.createdAt} - ${backup.kind} - ${backup.size} bytes</div>
|
||||
</div>
|
||||
<a href="/api/backups/${encodeURIComponent(backup.filename)}/download" class="px-4 py-2 bg-slate-100 text-slate-700 rounded-lg">Download</a>
|
||||
</div>
|
||||
@@ -1267,6 +1274,66 @@
|
||||
}
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
try {
|
||||
const response = await fetch('/api/settings');
|
||||
const settings = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(settings.error || 'Failed to load settings');
|
||||
}
|
||||
|
||||
this.currentSettings = settings;
|
||||
document.getElementById('settingsBackupsEnabled').checked = Boolean(settings.backups?.enabled);
|
||||
document.getElementById('settingsBackupTime').value = `${String(settings.backups?.hour ?? 3).padStart(2, '0')}:${String(settings.backups?.minute ?? 0).padStart(2, '0')}`;
|
||||
document.getElementById('settingsKeepLast').value = settings.backups?.keepLast ?? 14;
|
||||
document.getElementById('settingsIncludeAppSnapshot').checked = settings.backups?.includeAppSnapshot !== false;
|
||||
document.getElementById('settingsTelegramEnabled').checked = Boolean(settings.telegram?.enabled);
|
||||
document.getElementById('settingsTelegramToken').value = settings.telegram?.botToken || '';
|
||||
document.getElementById('settingsTelegramChatId').value = settings.telegram?.chatId || '';
|
||||
} catch (err) {
|
||||
this.showToast(err.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
const [hour, minute] = (document.getElementById('settingsBackupTime').value || '03:00').split(':').map(Number);
|
||||
const payload = {
|
||||
backups: {
|
||||
enabled: document.getElementById('settingsBackupsEnabled').checked,
|
||||
hour,
|
||||
minute,
|
||||
keepLast: Number(document.getElementById('settingsKeepLast').value || 14),
|
||||
includeAppSnapshot: document.getElementById('settingsIncludeAppSnapshot').checked,
|
||||
},
|
||||
telegram: {
|
||||
enabled: document.getElementById('settingsTelegramEnabled').checked,
|
||||
botToken: document.getElementById('settingsTelegramToken').value.trim(),
|
||||
chatId: document.getElementById('settingsTelegramChatId').value.trim(),
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/settings', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Request-Source': 'WEB',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(result.error || 'Failed to save settings');
|
||||
}
|
||||
|
||||
this.currentSettings = result.settings;
|
||||
this.showToast('Settings saved', 'success');
|
||||
this.closeModal('settingsModal');
|
||||
} catch (err) {
|
||||
this.showToast(err.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async loadAuditLog() {
|
||||
try {
|
||||
const response = await fetch('/api/audit');
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
<i data-lucide="archive" class="w-4 h-4"></i>
|
||||
Backups
|
||||
</button>
|
||||
<button id="settingsButton" onclick="app.showSettingsModal()" class="hidden flex items-center gap-2 px-4 py-2 bg-slate-100 text-slate-700 rounded-lg hover:bg-slate-200 transition-colors text-sm font-medium">
|
||||
<i data-lucide="sliders-horizontal" class="w-4 h-4"></i>
|
||||
Settings
|
||||
</button>
|
||||
<button id="auditButton" onclick="app.showAuditModal()" class="hidden flex items-center gap-2 px-4 py-2 bg-slate-100 text-slate-700 rounded-lg hover:bg-slate-200 transition-colors text-sm font-medium">
|
||||
<i data-lucide="history" class="w-4 h-4"></i>
|
||||
Audit
|
||||
@@ -593,6 +597,69 @@ SELECT * FROM users LIMIT 10;"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="settingsModal" class="hidden fixed inset-0 z-50 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
|
||||
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-3xl max-h-[90vh] overflow-hidden flex flex-col fade-in">
|
||||
<div class="p-6 border-b border-slate-200 flex items-center justify-between">
|
||||
<h3 class="text-xl font-bold text-slate-800">System settings</h3>
|
||||
<button onclick="app.closeModal('settingsModal')" class="p-2 hover:bg-slate-100 rounded-lg transition-colors">
|
||||
<i data-lucide="x" class="w-5 h-5 text-slate-500"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 overflow-auto space-y-6">
|
||||
<section class="space-y-4">
|
||||
<div>
|
||||
<h4 class="text-lg font-semibold text-slate-800">Automatic backups</h4>
|
||||
<p class="text-sm text-slate-500">Daily SQL dump of PostgreSQL plus optional application snapshot.</p>
|
||||
</div>
|
||||
<label class="flex items-center gap-2 text-sm text-slate-700">
|
||||
<input type="checkbox" id="settingsBackupsEnabled" class="w-4 h-4">
|
||||
Enable automatic backups
|
||||
</label>
|
||||
<div class="grid sm:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Time</label>
|
||||
<input type="time" id="settingsBackupTime" class="w-full px-4 py-2 border border-slate-300 rounded-lg">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Keep last days</label>
|
||||
<input type="number" id="settingsKeepLast" min="1" max="90" class="w-full px-4 py-2 border border-slate-300 rounded-lg">
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<label class="flex items-center gap-2 text-sm text-slate-700">
|
||||
<input type="checkbox" id="settingsIncludeAppSnapshot" class="w-4 h-4">
|
||||
Include site settings and audit snapshot
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="space-y-4">
|
||||
<div>
|
||||
<h4 class="text-lg font-semibold text-slate-800">Telegram notifications</h4>
|
||||
<p class="text-sm text-slate-500">Used for backup errors and important service events.</p>
|
||||
</div>
|
||||
<label class="flex items-center gap-2 text-sm text-slate-700">
|
||||
<input type="checkbox" id="settingsTelegramEnabled" class="w-4 h-4">
|
||||
Enable Telegram notifications
|
||||
</label>
|
||||
<div class="grid sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Bot token</label>
|
||||
<input type="text" id="settingsTelegramToken" class="w-full px-4 py-2 border border-slate-300 rounded-lg" placeholder="123456:ABC...">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-slate-700 mb-1">Chat ID</label>
|
||||
<input type="text" id="settingsTelegramChatId" class="w-full px-4 py-2 border border-slate-300 rounded-lg" placeholder="-1001234567890">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="p-6 border-t border-slate-200 bg-slate-50 flex justify-end gap-3">
|
||||
<button onclick="app.closeModal('settingsModal')" class="px-4 py-2 text-slate-600 hover:bg-slate-200 rounded-lg transition-colors">Cancel</button>
|
||||
<button onclick="app.saveSettings()" class="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors">Save settings</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast Notifications -->
|
||||
<div id="toastContainer" class="fixed bottom-6 right-6 z-50 flex flex-col gap-2"></div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user