// ===== SERVICE CREDENTIALS ===== (function() { injectModal('folder-browser-modal', `

📂 Browse for Media Folders

/
Loading...
`); injectModal('service-creds-modal', `

Service Credentials

Credentials are injected automatically when accessing this service.

No credentials stored
`); const modal = document.getElementById('service-creds-modal'); let currentService = null; const arrServices = ['sonarr', 'radarr', 'prowlarr', 'overseerr']; window.openServiceCredsModal = async function(service) { currentService = service; const title = document.getElementById('svc-creds-title'); const desc = document.getElementById('svc-creds-desc'); const seedhostSection = document.getElementById('svc-creds-seedhost'); const apikeySection = document.getElementById('svc-creds-apikey'); const basicSection = document.getElementById('svc-creds-basic'); title.textContent = service.name + ' Credentials'; // Determine which sections to show const isExt = !!service.isExternal; const isArr = arrServices.includes(service.id) || arrServices.includes(service.appTemplate); seedhostSection.style.display = isExt ? '' : 'none'; apikeySection.style.display = isArr ? '' : 'none'; basicSection.style.display = !isExt ? '' : 'none'; if (isExt) { desc.textContent = 'Seedhost credentials auto-login past the HTTP prompt. API key bypasses the app login.'; // Update password placeholder with service name document.getElementById('svc-seedhost-pass').placeholder = `Password for ${service.name}`; } else if (isArr) { desc.textContent = 'API key bypasses the app login screen automatically.'; } else { desc.textContent = 'Credentials are injected automatically when accessing this service.'; } // Load existing credentials await loadServiceCreds(service); modal.classList.add('show'); }; async function loadServiceCreds(service) { const dot = document.getElementById('svc-creds-dot'); const status = document.getElementById('svc-creds-status'); const clearBtn = document.getElementById('svc-creds-clear'); let hasCreds = false; // Load seedhost creds (shared username + per-service password) if (service.isExternal) { try { const res = await fetch(`/api/v1/seedhost-creds?serviceId=${service.id}`); const data = await res.json(); if (data.success) { document.getElementById('svc-seedhost-user').value = data.username || ''; if (data.hasCredentials) hasCreds = true; } else { document.getElementById('svc-seedhost-user').value = ''; } } catch (e) { /* ignore */ } document.getElementById('svc-seedhost-pass').value = ''; } // Load per-service creds try { const res = await fetch(`/api/v1/services/${service.id}/credentials`); const data = await res.json(); if (data.success) { if (data.hasApiKey) { document.getElementById('svc-apikey-input').value = '••••••••'; hasCreds = true; } else { document.getElementById('svc-apikey-input').value = ''; } if (data.hasBasicAuth && !service.isExternal) { document.getElementById('svc-basic-user').value = data.username || ''; hasCreds = true; } else { document.getElementById('svc-basic-user').value = ''; } } } catch (e) { /* ignore */ } if (document.getElementById('svc-basic-pass')) document.getElementById('svc-basic-pass').value = ''; if (hasCreds) { dot.style.background = 'var(--ok-fg, #74dfc4)'; status.style.color = 'var(--ok-fg, #74dfc4)'; status.textContent = 'Credentials stored'; clearBtn.style.display = ''; // Update the card button const btn = document.getElementById(`creds-btn-${service.id}`); if (btn) btn.classList.add('has-creds'); } else { dot.style.background = 'var(--muted)'; status.style.color = 'var(--muted)'; status.textContent = 'No credentials stored'; clearBtn.style.display = 'none'; } } // Save button document.getElementById('svc-creds-save')?.addEventListener('click', async () => { if (!currentService) return; const saveBtn = document.getElementById('svc-creds-save'); saveBtn.textContent = 'Saving...'; saveBtn.disabled = true; try { // Save seedhost creds (shared username + per-service password) if (currentService.isExternal) { const user = document.getElementById('svc-seedhost-user').value.trim(); const pass = document.getElementById('svc-seedhost-pass').value; if (user) { await secureFetch('/api/v1/seedhost-creds', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: user, password: pass || undefined, serviceId: currentService.id }) }); } } // Save API key const apiKeyInput = document.getElementById('svc-apikey-input'); const apiKey = apiKeyInput.value.trim(); if (apiKey && apiKey !== '••••••••') { await secureFetch(`/api/v1/services/${currentService.id}/credentials`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey }) }); } // Save per-service basic auth if (!currentService.isExternal) { const user = document.getElementById('svc-basic-user').value.trim(); const pass = document.getElementById('svc-basic-pass').value; if (user && pass) { await secureFetch(`/api/v1/services/${currentService.id}/credentials`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: user, password: pass }) }); } } await loadServiceCreds(currentService); } catch (e) { console.error('Failed to save credentials:', e); } saveBtn.textContent = 'Save'; saveBtn.disabled = false; }); // Clear button document.getElementById('svc-creds-clear')?.addEventListener('click', async () => { if (!currentService) return; if (!confirm(`Remove stored credentials for ${currentService.name}?`)) return; try { if (currentService.isExternal) { await secureFetch(`/api/v1/seedhost-creds?serviceId=${currentService.id}`, { method: 'DELETE' }); } await secureFetch(`/api/v1/services/${currentService.id}/credentials`, { method: 'DELETE' }); const btn = document.getElementById(`creds-btn-${currentService.id}`); if (btn) btn.classList.remove('has-creds'); await loadServiceCreds(currentService); } catch (e) { console.error('Failed to clear credentials:', e); } }); // Close button / backdrop document.getElementById('svc-creds-close')?.addEventListener('click', () => { modal.classList.remove('show'); currentService = null; }); modal?.addEventListener('click', (e) => { if (e.target === modal) { modal.classList.remove('show'); currentService = null; } }); // Check credential status for all services on page load (update key button highlights) window.refreshCredsButtons = async function() { try { for (const app of (window.APPS || [])) { if (!app.isExternal && !app.appTemplate && !app.url) continue; let hasCreds = false; if (app.isExternal) { try { const seedRes = await fetch(`/api/v1/seedhost-creds?serviceId=${app.id}`); const seedData = await seedRes.json(); if (seedData.success && seedData.hasCredentials) hasCreds = true; } catch (e) { /* ignore */ } } try { const r = await fetch(`/api/v1/services/${app.id}/credentials`); const d = await r.json(); if (d.success && (d.hasApiKey || d.hasBasicAuth)) hasCreds = true; } catch (e) { /* ignore */ } const btn = document.getElementById(`creds-btn-${app.id}`); if (btn) btn.classList.toggle('has-creds', hasCreds); } } catch (e) { /* ignore */ } }; })();