// ============================================================ // Callbacks: CRUD management // ============================================================ let callbackFormDirty = false; let _loadCallbacksPromise = null; async function loadCallbacksTable() { if (_loadCallbacksPromise) return _loadCallbacksPromise; _loadCallbacksPromise = _loadCallbacksTableImpl(); _loadCallbacksPromise.finally(() => { _loadCallbacksPromise = null; }); return _loadCallbacksPromise; } async function _loadCallbacksTableImpl() { const token = localStorage.getItem('media_server_token'); const tbody = document.getElementById('callbacksTableBody'); try { const response = await fetch('/api/callbacks/list', { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('Failed to fetch callbacks'); } const callbacksList = await response.json(); if (callbacksList.length === 0) { tbody.innerHTML = '

' + t('callbacks.empty') + '

'; return; } tbody.innerHTML = callbacksList.map(callback => ` ${escapeHtml(callback.name)} ${escapeHtml(callback.command)} ${callback.timeout}s
`).join(''); } catch (error) { console.error('Error loading callbacks:', error); tbody.innerHTML = 'Failed to load callbacks'; } } function showAddCallbackDialog() { const dialog = document.getElementById('callbackDialog'); const form = document.getElementById('callbackForm'); const title = document.getElementById('callbackDialogTitle'); form.reset(); document.getElementById('callbackIsEdit').value = 'false'; document.getElementById('callbackName').disabled = false; title.textContent = t('callbacks.dialog.add'); callbackFormDirty = false; document.body.classList.add('dialog-open'); dialog.showModal(); } async function showEditCallbackDialog(callbackName) { const token = localStorage.getItem('media_server_token'); const dialog = document.getElementById('callbackDialog'); const title = document.getElementById('callbackDialogTitle'); try { const response = await fetch('/api/callbacks/list', { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('Failed to fetch callback details'); } const callbacksList = await response.json(); const callback = callbacksList.find(c => c.name === callbackName); if (!callback) { showToast('Callback not found', 'error'); return; } document.getElementById('callbackIsEdit').value = 'true'; document.getElementById('callbackName').value = callbackName; document.getElementById('callbackName').disabled = true; document.getElementById('callbackCommand').value = callback.command; document.getElementById('callbackTimeout').value = callback.timeout; document.getElementById('callbackWorkingDir').value = callback.working_dir || ''; title.textContent = t('callbacks.dialog.edit'); callbackFormDirty = false; document.body.classList.add('dialog-open'); dialog.showModal(); } catch (error) { console.error('Error loading callback for edit:', error); showToast('Failed to load callback details', 'error'); } } async function closeCallbackDialog() { if (callbackFormDirty) { if (!await showConfirm(t('callbacks.confirm.unsaved'))) { return; } } const dialog = document.getElementById('callbackDialog'); callbackFormDirty = false; closeDialog(dialog); document.body.classList.remove('dialog-open'); } async function saveCallback(event) { event.preventDefault(); const submitBtn = event.target.querySelector('button[type="submit"]'); if (submitBtn) submitBtn.disabled = true; const token = localStorage.getItem('media_server_token'); const isEdit = document.getElementById('callbackIsEdit').value === 'true'; const callbackName = document.getElementById('callbackName').value; const data = { command: document.getElementById('callbackCommand').value, timeout: parseInt(document.getElementById('callbackTimeout').value) || 30, working_dir: document.getElementById('callbackWorkingDir').value || null, shell: true }; const endpoint = isEdit ? `/api/callbacks/update/${callbackName}` : `/api/callbacks/create/${callbackName}`; const method = isEdit ? 'PUT' : 'POST'; try { const response = await fetch(endpoint, { method, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (response.ok && result.success) { showToast(`Callback ${isEdit ? 'updated' : 'created'} successfully`, 'success'); callbackFormDirty = false; closeCallbackDialog(); loadCallbacksTable(); } else { showToast(result.detail || `Failed to ${isEdit ? 'update' : 'create'} callback`, 'error'); } } catch (error) { console.error('Error saving callback:', error); showToast(`Error ${isEdit ? 'updating' : 'creating'} callback`, 'error'); } finally { if (submitBtn) submitBtn.disabled = false; } } async function deleteCallbackConfirm(callbackName) { if (!await showConfirm(t('callbacks.confirm.delete').replace('{name}', callbackName))) { return; } const token = localStorage.getItem('media_server_token'); try { const response = await fetch(`/api/callbacks/delete/${callbackName}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); const result = await response.json(); if (response.ok && result.success) { showToast('Callback deleted successfully', 'success'); loadCallbacksTable(); } else { showToast(result.detail || 'Failed to delete callback', 'error'); } } catch (error) { console.error('Error deleting callback:', error); showToast('Error deleting callback', 'error'); } }