fix
This commit is contained in:
562
internal/server/templates/static/admin.js
Normal file
562
internal/server/templates/static/admin.js
Normal file
@@ -0,0 +1,562 @@
|
||||
let currentTab = 'clients';
|
||||
|
||||
// Check authentication on load
|
||||
async function checkAuth() {
|
||||
try {
|
||||
const res = await fetch('/admin/check');
|
||||
const data = await res.json();
|
||||
if (!data.authenticated) {
|
||||
window.location.href = '/admin/';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Auth check failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Logout
|
||||
async function logout() {
|
||||
await fetch('/admin/logout', { method: 'POST' });
|
||||
location.reload();
|
||||
}
|
||||
|
||||
// Load stats
|
||||
async function loadStats() {
|
||||
try {
|
||||
const res = await fetch('/admin/stats');
|
||||
const data = await res.json();
|
||||
document.getElementById('stats-grid').innerHTML =
|
||||
'<div class="bg-white p-4 rounded-lg shadow text-center"><h4 class="text-gray-500 text-sm mb-2">Clients</h4><div class="text-2xl font-bold text-slate-800">' + data.client_count + '</div></div>' +
|
||||
'<div class="bg-white p-4 rounded-lg shadow text-center"><h4 class="text-gray-500 text-sm mb-2">Total Snapshots</h4><div class="text-2xl font-bold text-slate-800">' + data.total_snapshots + '</div></div>' +
|
||||
'<div class="bg-white p-4 rounded-lg shadow text-center"><h4 class="text-gray-500 text-sm mb-2">Total Storage</h4><div class="text-2xl font-bold text-slate-800">' + data.total_storage_gb.toFixed(2) + ' GB</div></div>';
|
||||
} catch (e) {
|
||||
console.error('Failed to load stats:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load clients
|
||||
async function loadClients() {
|
||||
try {
|
||||
const res = await fetch('/admin/clients');
|
||||
const clients = await res.json();
|
||||
|
||||
const tbody = document.getElementById('clients-table');
|
||||
tbody.innerHTML = clients.map(c => {
|
||||
const usedPercent = c.max_size_bytes > 0 ? (c.current_usage / c.max_size_bytes * 100).toFixed(1) : 0;
|
||||
const usedGB = (c.current_usage / (1024*1024*1024)).toFixed(2);
|
||||
const maxGB = (c.max_size_bytes / (1024*1024*1024)).toFixed(0);
|
||||
return '<tr class="border-b hover:bg-gray-50">' +
|
||||
'<td class="py-2 px-2 font-semibold">' + c.client_id + '</td>' +
|
||||
'<td class="py-2 px-2"><span class="px-2 py-0.5 rounded text-xs font-semibold bg-blue-100 text-blue-800">' + c.storage_type + '</span></td>' +
|
||||
'<td class="py-2 px-2">' + maxGB + ' GB</td>' +
|
||||
'<td class="py-2 px-2">' +
|
||||
'<div>' + usedGB + ' GB (' + usedPercent + '%)</div>' +
|
||||
'<div class="w-24 h-2 bg-gray-200 rounded overflow-hidden mt-1"><div class="h-full bg-primary transition-all" style="width: ' + Math.min(usedPercent, 100) + '%"></div></div>' +
|
||||
'</td>' +
|
||||
'<td class="py-2 px-2">' + c.snapshot_count + '</td>' +
|
||||
'<td class="py-2 px-2">' + (c.enabled ? '<span class="px-2 py-0.5 rounded text-xs font-semibold bg-green-100 text-green-800">Enabled</span>' : '<span class="px-2 py-0.5 rounded text-xs font-semibold bg-red-100 text-red-800">Disabled</span>') + '</td>' +
|
||||
'<td class="py-2 px-2 whitespace-nowrap">' +
|
||||
'<button class="px-2 py-1 text-xs rounded bg-warning hover:bg-yellow-600 text-white mr-1" data-action="edit-client" data-client-id="' + c.client_id + '">Edit</button>' +
|
||||
'<button class="px-2 py-1 text-xs rounded bg-purple-500 hover:bg-purple-600 text-white mr-1" data-action="set-client-key" data-client-id="' + c.client_id + '">Set Key</button>' +
|
||||
'<button class="px-2 py-1 text-xs rounded bg-orange-500 hover:bg-orange-600 text-white mr-1" data-action="reset-client-key" data-client-id="' + c.client_id + '">Reset Key</button>' +
|
||||
'<button class="px-2 py-1 text-xs rounded bg-danger hover:bg-red-600 text-white" data-action="delete-client" data-client-id="' + c.client_id + '">Delete</button>' +
|
||||
'</td>' +
|
||||
'</tr>';
|
||||
}).join('');
|
||||
|
||||
// Update snapshot filter
|
||||
const filter = document.getElementById('snapshot-client-filter');
|
||||
filter.innerHTML = '<option value="">All Clients</option>' +
|
||||
clients.map(c => '<option value="' + c.client_id + '">' + c.client_id + '</option>').join('');
|
||||
} catch (e) {
|
||||
console.error('Failed to load clients:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load snapshots
|
||||
async function loadSnapshots() {
|
||||
const clientId = document.getElementById('snapshot-client-filter').value;
|
||||
const url = '/admin/snapshots' + (clientId ? '?client_id=' + clientId : '');
|
||||
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const snapshots = await res.json();
|
||||
|
||||
const tbody = document.getElementById('snapshots-table');
|
||||
tbody.innerHTML = snapshots.map(s => {
|
||||
const sizeGB = (s.size_bytes / (1024*1024*1024)).toFixed(2);
|
||||
return '<tr class="border-b hover:bg-gray-50">' +
|
||||
'<td class="py-2 px-2">' + s.client_id + '</td>' +
|
||||
'<td class="py-2 px-2">' + s.snapshot_id + '</td>' +
|
||||
'<td class="py-2 px-2">' + new Date(s.timestamp).toLocaleString() + '</td>' +
|
||||
'<td class="py-2 px-2">' + sizeGB + ' GB</td>' +
|
||||
'<td class="py-2 px-2">' +
|
||||
(s.incremental ? '<span class="px-2 py-0.5 rounded text-xs font-semibold bg-blue-100 text-blue-800">Incremental</span>' : '<span class="px-2 py-0.5 rounded text-xs font-semibold bg-green-100 text-green-800">Full</span>') +
|
||||
(s.compressed ? ' <span class="px-2 py-0.5 rounded text-xs font-semibold bg-blue-100 text-blue-800">LZ4</span>' : '') +
|
||||
'</td>' +
|
||||
'<td class="py-2 px-2"><button class="px-2 py-1 text-xs rounded bg-danger hover:bg-red-600 text-white" data-action="delete-snapshot" data-client-id="' + s.client_id + '" data-snapshot-id="' + s.snapshot_id + '">Delete</button></td>' +
|
||||
'</tr>';
|
||||
}).join('');
|
||||
} catch (e) {
|
||||
console.error('Failed to load snapshots:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load admins
|
||||
async function loadAdmins() {
|
||||
try {
|
||||
const res = await fetch('/admin/admins');
|
||||
const admins = await res.json();
|
||||
|
||||
const tbody = document.getElementById('admins-table');
|
||||
tbody.innerHTML = admins.map(a =>
|
||||
'<tr class="border-b hover:bg-gray-50">' +
|
||||
'<td class="py-2 px-2">' + a.id + '</td>' +
|
||||
'<td class="py-2 px-2 font-semibold">' + a.username + '</td>' +
|
||||
'<td class="py-2 px-2"><span class="px-2 py-0.5 rounded text-xs font-semibold bg-blue-100 text-blue-800">' + a.role + '</span></td>' +
|
||||
'<td class="py-2 px-2">' + new Date(a.created_at).toLocaleDateString() + '</td>' +
|
||||
'<td class="py-2 px-2 whitespace-nowrap">' +
|
||||
'<button class="px-2 py-1 text-xs rounded bg-warning hover:bg-yellow-600 text-white mr-1" data-action="change-admin-password" data-admin-id="' + a.id + '" data-admin-username="' + a.username + '">Change Password</button>' +
|
||||
'<button class="px-2 py-1 text-xs rounded bg-danger hover:bg-red-600 text-white" data-action="delete-admin" data-admin-id="' + a.id + '">Delete</button>' +
|
||||
'</td>' +
|
||||
'</tr>'
|
||||
).join('');
|
||||
} catch (e) {
|
||||
console.error('Failed to load admins:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Tab switching
|
||||
function showTab(tab) {
|
||||
currentTab = tab;
|
||||
document.querySelectorAll('[data-tab]').forEach(t => {
|
||||
t.classList.remove('bg-primary', 'text-white');
|
||||
t.classList.add('bg-white', 'text-gray-600');
|
||||
});
|
||||
document.querySelector('[data-tab="' + tab + '"]').classList.remove('bg-white', 'text-gray-600');
|
||||
document.querySelector('[data-tab="' + tab + '"]').classList.add('bg-primary', 'text-white');
|
||||
|
||||
document.getElementById('clients-tab').classList.add('hidden');
|
||||
document.getElementById('snapshots-tab').classList.add('hidden');
|
||||
document.getElementById('admins-tab').classList.add('hidden');
|
||||
document.getElementById(tab + '-tab').classList.remove('hidden');
|
||||
|
||||
if (tab === 'snapshots') loadSnapshots();
|
||||
if (tab === 'admins') loadAdmins();
|
||||
}
|
||||
|
||||
// Modal functions
|
||||
function showModal(id) {
|
||||
const modal = document.getElementById(id);
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
}
|
||||
|
||||
function closeModal(id) {
|
||||
const modal = document.getElementById(id);
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
}
|
||||
|
||||
// Edit client
|
||||
async function editClient(clientId) {
|
||||
try {
|
||||
const res = await fetch('/admin/client?client_id=' + clientId);
|
||||
const data = await res.json();
|
||||
const c = data.client;
|
||||
|
||||
document.getElementById('edit-client-id').value = c.client_id;
|
||||
document.getElementById('edit-client-apikey').value = '';
|
||||
document.getElementById('edit-client-storage').value = c.storage_type;
|
||||
document.getElementById('edit-client-dataset').value = c.dataset;
|
||||
document.getElementById('edit-client-quota').value = Math.round(c.max_size_bytes / (1024*1024*1024));
|
||||
document.getElementById('edit-client-enabled').checked = c.enabled;
|
||||
|
||||
if (c.rotation_policy) {
|
||||
document.getElementById('edit-client-hourly').value = c.rotation_policy.keep_hourly || 0;
|
||||
document.getElementById('edit-client-daily').value = c.rotation_policy.keep_daily || 0;
|
||||
document.getElementById('edit-client-weekly').value = c.rotation_policy.keep_weekly || 0;
|
||||
document.getElementById('edit-client-monthly').value = c.rotation_policy.keep_monthly || 0;
|
||||
}
|
||||
|
||||
showModal('edit-client-modal');
|
||||
} catch (e) {
|
||||
alert('Failed to load client data');
|
||||
}
|
||||
}
|
||||
|
||||
// Delete client
|
||||
async function deleteClient(clientId) {
|
||||
if (!confirm('Are you sure you want to delete client "' + clientId + '"? This will also delete all snapshots.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/client/delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ client_id: clientId })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
loadClients();
|
||||
loadStats();
|
||||
} else {
|
||||
alert(data.message || 'Failed to delete client');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to delete client');
|
||||
}
|
||||
}
|
||||
|
||||
// Delete snapshot
|
||||
async function deleteSnapshot(clientId, snapshotId) {
|
||||
if (!confirm('Are you sure you want to delete snapshot "' + snapshotId + '"?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/snapshot/delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ client_id: clientId, snapshot_id: snapshotId })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
loadSnapshots();
|
||||
loadStats();
|
||||
} else {
|
||||
alert(data.message || 'Failed to delete snapshot');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to delete snapshot');
|
||||
}
|
||||
}
|
||||
|
||||
// Delete admin
|
||||
async function deleteAdmin(adminId) {
|
||||
if (!confirm('Are you sure you want to delete this admin?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/admin/delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ admin_id: adminId })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
loadAdmins();
|
||||
} else {
|
||||
alert(data.message || 'Failed to delete admin');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to delete admin');
|
||||
}
|
||||
}
|
||||
|
||||
// Show change password modal
|
||||
function showChangePassword(adminId, username) {
|
||||
document.getElementById('change-password-admin-id').value = adminId;
|
||||
document.getElementById('change-password-username').value = username;
|
||||
document.getElementById('change-password-new').value = '';
|
||||
document.getElementById('change-password-confirm').value = '';
|
||||
showModal('change-password-modal');
|
||||
}
|
||||
|
||||
// Show client change password modal
|
||||
function showClientChangePassword(clientId) {
|
||||
document.getElementById('client-password-client-id').value = clientId;
|
||||
document.getElementById('client-password-client-name').value = clientId;
|
||||
document.getElementById('client-password-new').value = '';
|
||||
document.getElementById('client-password-confirm').value = '';
|
||||
showModal('client-password-modal');
|
||||
}
|
||||
|
||||
// Reset client password
|
||||
async function resetClientPassword(clientId) {
|
||||
if (!confirm('Are you sure you want to reset the API key for "' + clientId + '"? A new random key will be generated.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/client/reset-password', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ client_id: clientId })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
alert('New API key for ' + clientId + ': ' + data.api_key + '\n\nPlease save this key as it will not be shown again.');
|
||||
} else {
|
||||
alert(data.message || 'Failed to reset API key');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to reset API key');
|
||||
}
|
||||
}
|
||||
|
||||
// Event delegation for click handlers
|
||||
document.addEventListener('click', function(e) {
|
||||
const target = e.target;
|
||||
|
||||
// Handle modal content clicks - stop propagation
|
||||
if (target.classList.contains('modal-content') || target.closest('.modal-content')) {
|
||||
// Don't process if clicking on close button
|
||||
if (!target.classList.contains('modal-close')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle modal close buttons
|
||||
if (target.classList.contains('modal-close')) {
|
||||
const modalId = target.getAttribute('data-modal-id');
|
||||
if (modalId) {
|
||||
closeModal(modalId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle modal overlay clicks (close on backdrop click)
|
||||
if (target.classList.contains('fixed') && target.hasAttribute('data-modal-id')) {
|
||||
closeModal(target.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle data-action buttons
|
||||
const action = target.getAttribute('data-action');
|
||||
if (!action) return;
|
||||
|
||||
switch (action) {
|
||||
case 'show-modal':
|
||||
const modalId = target.getAttribute('data-modal');
|
||||
if (modalId) showModal(modalId);
|
||||
break;
|
||||
case 'edit-client':
|
||||
const clientId = target.getAttribute('data-client-id');
|
||||
if (clientId) editClient(clientId);
|
||||
break;
|
||||
case 'delete-client':
|
||||
const delClientId = target.getAttribute('data-client-id');
|
||||
if (delClientId) deleteClient(delClientId);
|
||||
break;
|
||||
case 'set-client-key':
|
||||
const setKeyClientId = target.getAttribute('data-client-id');
|
||||
if (setKeyClientId) showClientChangePassword(setKeyClientId);
|
||||
break;
|
||||
case 'reset-client-key':
|
||||
const resetKeyClientId = target.getAttribute('data-client-id');
|
||||
if (resetKeyClientId) resetClientPassword(resetKeyClientId);
|
||||
break;
|
||||
case 'delete-snapshot':
|
||||
const snapClientId = target.getAttribute('data-client-id');
|
||||
const snapshotId = target.getAttribute('data-snapshot-id');
|
||||
if (snapClientId && snapshotId) deleteSnapshot(snapClientId, snapshotId);
|
||||
break;
|
||||
case 'change-admin-password':
|
||||
const adminId = target.getAttribute('data-admin-id');
|
||||
const adminUsername = target.getAttribute('data-admin-username');
|
||||
if (adminId && adminUsername) showChangePassword(adminId, adminUsername);
|
||||
break;
|
||||
case 'delete-admin':
|
||||
const delAdminId = target.getAttribute('data-admin-id');
|
||||
if (delAdminId) deleteAdmin(parseInt(delAdminId));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Tab click handler
|
||||
document.addEventListener('click', function(e) {
|
||||
const tab = e.target.getAttribute('data-tab');
|
||||
if (tab) {
|
||||
showTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
// Snapshot filter change handler
|
||||
document.addEventListener('change', function(e) {
|
||||
if (e.target.id === 'snapshot-client-filter') {
|
||||
loadSnapshots();
|
||||
}
|
||||
});
|
||||
|
||||
// Add client form
|
||||
document.getElementById('add-client-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const client = {
|
||||
client_id: document.getElementById('new-client-id').value,
|
||||
api_key: document.getElementById('new-client-apikey').value,
|
||||
storage_type: document.getElementById('new-client-storage').value,
|
||||
dataset: document.getElementById('new-client-dataset').value,
|
||||
max_size_bytes: parseInt(document.getElementById('new-client-quota').value) * 1024 * 1024 * 1024,
|
||||
enabled: document.getElementById('new-client-enabled').checked,
|
||||
rotation_policy: {
|
||||
keep_hourly: parseInt(document.getElementById('new-client-hourly').value) || 0,
|
||||
keep_daily: parseInt(document.getElementById('new-client-daily').value) || 0,
|
||||
keep_weekly: parseInt(document.getElementById('new-client-weekly').value) || 0,
|
||||
keep_monthly: parseInt(document.getElementById('new-client-monthly').value) || 0
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/client/create', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(client)
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
closeModal('add-client-modal');
|
||||
document.getElementById('add-client-form').reset();
|
||||
loadClients();
|
||||
loadStats();
|
||||
} else {
|
||||
alert(data.message || 'Failed to create client');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to create client');
|
||||
}
|
||||
});
|
||||
|
||||
// Edit client form
|
||||
document.getElementById('edit-client-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const client = {
|
||||
client_id: document.getElementById('edit-client-id').value,
|
||||
api_key: document.getElementById('edit-client-apikey').value,
|
||||
storage_type: document.getElementById('edit-client-storage').value,
|
||||
dataset: document.getElementById('edit-client-dataset').value,
|
||||
max_size_bytes: parseInt(document.getElementById('edit-client-quota').value) * 1024 * 1024 * 1024,
|
||||
enabled: document.getElementById('edit-client-enabled').checked,
|
||||
rotation_policy: {
|
||||
keep_hourly: parseInt(document.getElementById('edit-client-hourly').value) || 0,
|
||||
keep_daily: parseInt(document.getElementById('edit-client-daily').value) || 0,
|
||||
keep_weekly: parseInt(document.getElementById('edit-client-weekly').value) || 0,
|
||||
keep_monthly: parseInt(document.getElementById('edit-client-monthly').value) || 0
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/client/update', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(client)
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
closeModal('edit-client-modal');
|
||||
loadClients();
|
||||
loadStats();
|
||||
} else {
|
||||
alert(data.message || 'Failed to update client');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to update client');
|
||||
}
|
||||
});
|
||||
|
||||
// Add admin form
|
||||
document.getElementById('add-admin-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const admin = {
|
||||
username: document.getElementById('new-admin-username').value,
|
||||
password: document.getElementById('new-admin-password').value,
|
||||
role: document.getElementById('new-admin-role').value
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/admin/create', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(admin)
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
closeModal('add-admin-modal');
|
||||
document.getElementById('add-admin-form').reset();
|
||||
loadAdmins();
|
||||
} else {
|
||||
alert(data.message || 'Failed to create admin');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to create admin');
|
||||
}
|
||||
});
|
||||
|
||||
// Change password form
|
||||
document.getElementById('change-password-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const adminId = document.getElementById('change-password-admin-id').value;
|
||||
const newPassword = document.getElementById('change-password-new').value;
|
||||
const confirmPassword = document.getElementById('change-password-confirm').value;
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
alert('Passwords do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/admin/password', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ admin_id: parseInt(adminId), new_password: newPassword })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
closeModal('change-password-modal');
|
||||
alert('Password changed successfully');
|
||||
} else {
|
||||
alert(data.message || 'Failed to change password');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to change password');
|
||||
}
|
||||
});
|
||||
|
||||
// Client password form
|
||||
document.getElementById('client-password-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const clientId = document.getElementById('client-password-client-id').value;
|
||||
const newKey = document.getElementById('client-password-new').value;
|
||||
const confirmKey = document.getElementById('client-password-confirm').value;
|
||||
|
||||
if (newKey !== confirmKey) {
|
||||
alert('API keys do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/admin/client/password', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ client_id: clientId, api_key: newKey })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
closeModal('client-password-modal');
|
||||
alert('API key changed successfully for ' + clientId);
|
||||
} else {
|
||||
alert(data.message || 'Failed to change API key');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Failed to change API key');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize
|
||||
loadStats();
|
||||
loadClients();
|
||||
Reference in New Issue
Block a user