diff --git a/nodejs/views/directory.ejs b/nodejs/views/directory.ejs index c7646a4..411b56f 100644 --- a/nodejs/views/directory.ejs +++ b/nodejs/views/directory.ejs @@ -3,7 +3,26 @@
-
+ +
+
+
Directory Management @@ -74,6 +93,148 @@
+ + +
+
+
+
+ Network Discovery Dashboard +
+
+ + +
+
+ +
+ Auto-discovered network resources. Promote unmanaged devices to track them in the Directory. + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
Name / SourceTypeIP AddressStatusActions
+
{{name}}
+
+ {{#metadata.source}}{{metadata.source}}{{/metadata.source}}{{^metadata.source}}Manual{{/metadata.source}} +
+
+ {{kind}} + {{#metadata.subType}} + {{metadata.subType}} + {{/metadata.subType}} + + {{#metadata.ip}}
{{metadata.ip}}
{{/metadata.ip}} + {{^metadata.ip}}Unknown IP{{/metadata.ip}} + {{#metadata.interfaces.length}} +
+ {{#metadata.interfaces}} +
{{mac}} {{#ip}}({{ip}}){{/ip}}
+ {{/metadata.interfaces}} +
+ {{/metadata.interfaces.length}} +
+ {{#metadata.managed}} + Managed + {{/metadata.managed}} + {{^metadata.managed}} + Unmanaged + {{/metadata.managed}} + + {{^metadata.managed}} + + {{/metadata.managed}} + {{#metadata.managed}} + + {{/metadata.managed}} +
+
+
+
+ + +
+
+
+
+ Plugins & Scheduler +
+
+
+ Manage background tasks and schedules. Learn how to make and use custom plugins. +
+
+ + + + + + + + + + + + + + + + + + + + + + +
Plugin NameCron ScheduleStatusActions
{{name}} + {{#enabled}}Enabled{{/enabled}} + {{^enabled}}Disabled{{/enabled}} + + + {{#enabled}}{{/enabled}} + {{^enabled}}{{/enabled}} +
+
+
+
+
@@ -413,14 +574,144 @@ const parentEdge = allEdges.find(e => e.childId === r.id); if (parentEdge) { r.parentId = parentEdge.parentId; - const parent = resourcesById[parentEdge.parentId]; + const parent = resourcesById[parentEdge.parentId]; if (parent) r.hostName = parent.name; } rawResources.push(r); } + function openAddModal(parent_id, kind) { + if(parent_id){ + $('#newResourceParent').val(parent_id); + $('#newResourceKind').val(kind); + var currentLabel = "Resource"; + if(kind === 'Host'){ currentLabel = 'Host'; } + else if(kind === 'Site'){ currentLabel = 'Site'; } + + $('#newResourceLabel').text('Add Child ' + currentLabel); + }else{ + $('#newResourceParent').val(''); + $('#newResourceKind').val('Host'); + $('#newResourceLabel').text('Add Resource'); + } + + // Clear input + $('#newResourceName').val(''); + $('#addResourceModal').modal('show'); + } + + // --- DISCOVERY SCRIPTS --- + let allDiscoveryResources = []; + + function loadDiscoveryResources() { + app.api.get('discovery/resources', function(err, res) { + if(err) { + $('.actionMessage').html('
' + (err.message || 'Error loading resources') + '
').show(); + return; + } + allDiscoveryResources = res.results || []; + renderDiscoveryTable(); + }); + } + + function renderDiscoveryTable() { + const search = $('#discovery-search-filter').val().toLowerCase(); + const managedFilter = $('#discovery-filter-managed').val(); + + const filtered = allDiscoveryResources.filter(r => { + if(search && !r.name.toLowerCase().includes(search) && !r.slug.toLowerCase().includes(search)) return false; + const isManaged = !!(r.metadata && r.metadata.managed); + if(managedFilter === 'managed' && !isManaged) return false; + if(managedFilter === 'unmanaged' && isManaged) return false; + return true; + }); + + $.scope.discoveryResources.empty(); + for(const r of filtered) { + $.scope.discoveryResources.push(r); + } + + if(filtered.length === 0) { + $('#discovery-list').hide(); + $('#discovery-empty-state').show(); + } else { + $('#discovery-list').show(); + $('#discovery-empty-state').hide(); + } + } + + function promoteResource(slug) { + if(!confirm("Are you sure you want to promote this resource? This will generate SSO LDAP groups for it.")) return; + app.api.post('discovery/promote/' + slug, {}, function(err, res) { + if(err) { + alert("Error promoting resource: " + (err.message || err)); + return; + } + const resource = allDiscoveryResources.find(r => r.slug === slug); + if(resource) { + resource.metadata = resource.metadata || {}; + resource.metadata.managed = true; + } + $('.actionMessage').html('
Successfully promoted! Created groups: ' + res.groups.join(', ') + '
').show(); + renderDiscoveryTable(); + renderTable(); // Also update directory tab + }); + } + + // --- PLUGINS SCRIPTS --- + function loadPlugins() { + app.api.get('plugins', function(err, res) { + if(err) { + alert("Error loading plugins: " + (err.message || err)); + return; + } + const plugins = res.results || {}; + const pluginNames = Object.keys(plugins); - renderTable(); + $.scope.plugins.empty(); + if(pluginNames.length === 0) { + $('#plugins-list').hide(); + $('#plugins-empty-state').show(); + } else { + pluginNames.forEach(name => { + const config = plugins[name]; + $.scope.plugins.push({ + name: name, + cron: config.cron || '', + enabled: config.enabled + }); + }); + $('#plugins-list').show(); + $('#plugins-empty-state').hide(); + } + }); + } + function updatePlugin(name) { + const cron = $('#cron-' + name).val(); + app.api.put('plugins/' + name, {cron: cron}, function(err, res) { + if(err) { alert("Failed to save: " + err.message); return; } + alert("Saved schedule successfully."); + }); + } + + function togglePlugin(name, enable) { + app.api.put('plugins/' + name, {enabled: enable}, function(err, res) { + if(err) { alert("Failed to toggle: " + err.message); return; } + loadPlugins(); + }); + } + + $(document).ready(function(){ + renderTable(); + loadDiscoveryResources(); + loadPlugins(); + + // Auto-open modal if hash is present + if(window.location.hash && window.location.hash.startsWith('#modal-')) { + const slug = window.location.hash.replace('#modal-', ''); + setTimeout(() => openEditModal(slug), 500); + } + }); // Type-ahead for the "what can this user reach" lookup. Non-blocking: the // input accepts a free-typed uid whether or not the list ever arrives. loadDirectoryUsers().then(function(users) {