pediatric-ai-scribe-v3/public/js/nextcloud.js
Daniel Onyejesi 23d39dec65 Fix WebDAV selection, pdf-parse, prompt(), CSP, security issues 1/2/4/5
pdf-parse (was v2, broken API):
- Downgraded to v1.1.1 — default export is a function again
- pdfParse is not a function error fixed

WebDAV file selection (style.display bug, same cascade issue):
- lh-ai-webdav-selected had inline style="display:flex" always visible
- selectWebdavFile() / deselectWebdavFile() now use element.style.display
- On select: browser div hides, selected indicator shows with file name + X
- On deselect (X): indicator hides, browser shows again

Topic context for Upload and Nextcloud tabs:
- Both tabs now have optional "Topic / context" field
- Sent to backend as 'topic' param to help AI focus the generated content

Inline refine bar (replaces window.prompt):
- Refine Body button shows/hides an amber inline input bar below generate buttons
- Enter instructions, press Apply — no browser dialog, works in all contexts

CSP: remove unsafe-inline from scriptSrc (security issue #2):
- Converted all 26 onclick= handlers in component HTML files to
  data-action / data-target / data-label attributes
- Added delegated click handler in app.js for copy/speak/nc-export actions
- script-src now 'self' only; script-src-attr 'none'

WebDAV path endpoint (security issue #5):
- New POST /api/user/webdav-path (authMiddleware only, not moderator)
- nextcloud.js updated to use new endpoint

nodemailer: already at 6.10.1 (vulnerability fixed).
2026-03-24 02:28:43 -04:00

75 lines
3.2 KiB
JavaScript

(function() {
var _inited = false;
document.addEventListener('tabChanged', function(e) {
if (e.detail.tab !== 'settings' || _inited) return;
_inited = true;
function loadNextcloudStatus() {
fetch('/api/auth/me', { headers: getAuthHeaders() })
.then(function(r) { return r.json(); })
.then(function(data) {
var pathSection = document.getElementById('nc-webdav-path-section');
if (data.user && data.user.nextcloud_url) {
document.getElementById('nc-status').innerHTML = '✅ Connected to <strong>' + data.user.nextcloud_url + '</strong> as ' + data.user.nextcloud_user;
document.getElementById('nc-url').value = data.user.nextcloud_url;
document.getElementById('nc-user').value = data.user.nextcloud_user;
document.getElementById('btn-nc-disconnect').classList.remove('hidden');
if (pathSection) {
pathSection.classList.remove('hidden');
var pathEl = document.getElementById('nc-webdav-path');
if (pathEl) pathEl.value = data.user.webdav_learning_path || '';
}
} else {
document.getElementById('nc-status').textContent = 'Not connected';
document.getElementById('btn-nc-disconnect').classList.add('hidden');
if (pathSection) pathSection.classList.add('hidden');
}
});
}
window.loadNextcloudStatus = loadNextcloudStatus;
document.getElementById('btn-nc-connect').addEventListener('click', function() {
var url = document.getElementById('nc-url').value.trim().replace(/\/+$/, '');
var user = document.getElementById('nc-user').value.trim();
var pass = document.getElementById('nc-pass').value.trim();
if (!url || !user || !pass) { showToast('Fill all Nextcloud fields', 'error'); return; }
showLoading('Connecting to Nextcloud...');
fetch('/api/nextcloud/connect', {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ nextcloudUrl: url, username: user, appPassword: pass })
})
.then(function(r) { return r.json(); })
.then(function(data) {
hideLoading();
if (data.success) {
showToast(data.message, 'success');
loadNextcloudStatus();
document.getElementById('nc-pass').value = '';
} else {
showToast(data.error || 'Connection failed', 'error');
}
})
.catch(function(err) { hideLoading(); showToast(err.message, 'error'); });
});
document.getElementById('btn-nc-disconnect').addEventListener('click', function() {
fetch('/api/nextcloud/disconnect', { method: 'POST', headers: getAuthHeaders() })
.then(function() { showToast('Disconnected', 'info'); loadNextcloudStatus(); });
});
document.getElementById('btn-nc-save-path').addEventListener('click', function() {
var p = (document.getElementById('nc-webdav-path').value || '').trim();
fetch('/api/user/webdav-path', {
method: 'POST', headers: getAuthHeaders(), body: JSON.stringify({ path: p })
}).then(function(r) { return r.json(); })
.then(function(d) { d.success ? showToast('Path saved', 'success') : showToast(d.error || 'Failed', 'error'); })
.catch(function() { showToast('Failed to save', 'error'); });
});
console.log('✅ Nextcloud module loaded');
});
})();