fix config
This commit is contained in:
@@ -215,6 +215,8 @@ const tableSort = {};
|
||||
const uiCache = {correlations: [], fieldRows: [], correlationHitboxes: [], selectedCorrelationKey: '', entityLabels: {}};
|
||||
window.availableStreams = [];
|
||||
window.streamSelection = {};
|
||||
window.configuredStreams = [];
|
||||
window.streamProfiles = [];
|
||||
window.currentCorrelations = [];
|
||||
window.currentConfiguration = {};
|
||||
function table(rows, columns, id = '') {
|
||||
@@ -815,6 +817,7 @@ async function loadSettings() {
|
||||
const vtKey = form.elements.namedItem('virustotal_api_key');
|
||||
vtKey.placeholder = config.virustotal_api_key_configured ? 'Key configured; leave blank to keep it' : 'Paste VirusTotal API key';
|
||||
window.streamProfiles = config.graylog_stream_profiles || [];
|
||||
window.configuredStreams = config.graylog_streams || [];
|
||||
if (config.graylog_mcp_token_configured && config.graylog_mcp_url) loadStreams();
|
||||
loadOllamaModels();
|
||||
}
|
||||
@@ -842,10 +845,16 @@ async function loadStreams() {
|
||||
renderStreamPicker(payload.error || `Could not load streams: ${response.status}`);
|
||||
return;
|
||||
}
|
||||
const selected = new Set((payload.selected || []).filter(item => item.enabled).map(item => item.id));
|
||||
const configuredStreams = payload.selected || window.configuredStreams || [];
|
||||
let selected = new Set(configuredStreams.filter(item => item.enabled).map(item => item.id));
|
||||
let recoveryText = '';
|
||||
if (!selected.size && (window.streamProfiles || []).length) {
|
||||
selected = new Set((window.streamProfiles || []).map(item => item.stream_id).filter(Boolean));
|
||||
recoveryText = 'No enabled streams were saved; streams with existing profiles are selected for recovery. Click Save streams to keep this selection.';
|
||||
}
|
||||
window.availableStreams = payload.streams || [];
|
||||
window.streamSelection = Object.fromEntries(window.availableStreams.map(stream => [stream.id, {id:stream.id, title:stream.title, enabled:selected.has(stream.id)}]));
|
||||
renderStreamPicker(payload.error || '');
|
||||
renderStreamPicker(recoveryText || payload.error || '');
|
||||
}
|
||||
document.getElementById('loadStreams').addEventListener('click', loadStreams);
|
||||
async function saveStreamSelection() {
|
||||
@@ -879,6 +888,7 @@ function renderStreamPicker(errorText='') {
|
||||
.filter(stream => !previousEnabledOnly || window.streamSelection[stream.id]?.enabled);
|
||||
const enabledCount = Object.values(window.streamSelection).filter(item => item.enabled).length;
|
||||
target.innerHTML = `
|
||||
${errorText ? `<div class="notice warn">${esc(errorText)}</div>` : ''}
|
||||
<div class="stream-tools">
|
||||
<input id="streamFilter" type="search" placeholder="Search streams by name or id" value="${esc(previousFilter)}">
|
||||
<label><input id="streamEnabledOnly" type="checkbox" ${previousEnabledOnly ? 'checked' : ''}> enabled only</label>
|
||||
@@ -1027,7 +1037,10 @@ document.getElementById('settingsForm').addEventListener('submit', async event =
|
||||
values.profile_advisor_enabled = form.elements.profile_advisor_enabled.checked;
|
||||
values.threat_intel_enabled = form.elements.threat_intel_enabled.checked;
|
||||
values.graylog_tls_verify = form.elements.graylog_tls_verify.checked;
|
||||
values.graylog_streams = Object.values(window.streamSelection || {}).length ? Object.values(window.streamSelection) : [...document.querySelectorAll('.graylog-stream')].map(item => ({id:item.dataset.id, title:item.dataset.title, enabled:item.checked}));
|
||||
const streamValues = Object.values(window.streamSelection || {});
|
||||
if (streamValues.length) {
|
||||
values.graylog_streams = streamValues;
|
||||
}
|
||||
if (window.activeProfileStream) { let detectors={},fieldWeights={},relationshipFields=[]; try { detectors=form.elements.profile_detectors.value.trim() ? JSON.parse(form.elements.profile_detectors.value) : {}; } catch { document.getElementById('settingsResult').textContent='Detector thresholds must be valid JSON.'; return; } try { fieldWeights=form.elements.profile_field_weights.value.trim() ? JSON.parse(form.elements.profile_field_weights.value) : {}; } catch { document.getElementById('settingsResult').textContent='Field weights must be valid JSON.'; return; } try { relationshipFields=form.elements.profile_relationship_fields.value.trim() ? JSON.parse(form.elements.profile_relationship_fields.value) : []; } catch { document.getElementById('settingsResult').textContent='Behavior relationships must be valid JSON.'; return; } if (!Array.isArray(relationshipFields)) { document.getElementById('settingsResult').textContent='Behavior relationships must be a JSON list.'; return; } const entityFields=[...form.querySelectorAll('.profile-entity:checked')].map(item=>item.value); if (!entityFields.length) { document.getElementById('settingsResult').textContent='Select at least one Entity field for the active profile.'; return; } const profileTitle=window.activeProfileTitle || window.activeProfileStream; const profile={stream_id:window.activeProfileStream,name:form.elements.profile_name.value.trim() || `${profileTitle} profile`,entity_field:entityFields[0]||'',entity_fields:entityFields,timestamp_field:form.querySelector('[name="profile_timestamp"]:checked')?.value||'timestamp',categorical_fields:[...form.querySelectorAll('.profile-categorical:checked')].map(item=>item.value),numeric_fields:[...form.querySelectorAll('.profile-numeric:checked')].map(item=>item.value),detectors,field_weights:fieldWeights,relationship_fields:relationshipFields}; values.graylog_stream_profiles=[...(window.streamProfiles||[]).filter(item=>item.stream_id!==profile.stream_id),profile]; }
|
||||
const savedProfile = window.activeProfileStream ? ` Profile saved for ${window.activeProfileTitle || window.activeProfileStream}.` : ' No profile editor active, so profiles were not changed.';
|
||||
const response = await fetch('/api/config', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(values)});
|
||||
|
||||
@@ -57,6 +57,15 @@ class DashboardTests(unittest.TestCase):
|
||||
self.assertIn("incidentNotice", HTML)
|
||||
self.assertIn("Incident marked", HTML)
|
||||
|
||||
def test_dashboard_does_not_clear_streams_when_picker_is_unloaded(self):
|
||||
self.assertIn("const streamValues = Object.values(window.streamSelection || {})", HTML)
|
||||
self.assertIn("if (streamValues.length)", HTML)
|
||||
self.assertIn("values.graylog_streams = streamValues", HTML)
|
||||
|
||||
def test_dashboard_can_recover_stream_selection_from_profiles(self):
|
||||
self.assertIn("No enabled streams were saved", HTML)
|
||||
self.assertIn("streams with existing profiles are selected for recovery", HTML)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user