Add SVP org hierarchy discovery to BU Lookup panel
Backend: - GET /api/ivanti/findings/org-hierarchy — returns all SVP names and all BU names from Ivanti suggest API (Admin only) - GET /api/ivanti/findings/bus-by-svp?svp=<name> — returns BUs under a specific SVP with finding counts (Admin only) Frontend (Admin > BU Lookup tab): - 'Load Org Hierarchy' button fetches all SVPs and BUs - SVP dropdown (sorted by finding count) to filter BUs by org leader - Results table shows BU name, finding count, and configured status (green badge for BUs already in the dashboard, grey for unconfigured) - Enables admin to discover which BUs roll up under an SVP for correct team assignment when onboarding new users
This commit is contained in:
@@ -1766,6 +1766,84 @@ function createIvantiFindingsRouter(db, requireAuth) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/ivanti/findings/org-hierarchy
|
||||
*
|
||||
* Return the full SVP list and BU list from the Ivanti suggest API.
|
||||
* Used by admin tooling to display the organizational hierarchy for
|
||||
* BU filter configuration and team assignment verification.
|
||||
* Requires Admin group.
|
||||
*
|
||||
* @returns {Object} 200 - { svps: string[], bus: string[] }
|
||||
* @returns {Object} 503 - { error: string } when Ivanti API key is not configured
|
||||
* @returns {Object} 500 - { error: string } on API failure
|
||||
*/
|
||||
router.get('/org-hierarchy', requireGroup('Admin'), async (req, res) => {
|
||||
try {
|
||||
const clientId = process.env.IVANTI_CLIENT_ID || '1550';
|
||||
const apiKey = process.env.IVANTI_API_KEY;
|
||||
const skipTls = process.env.IVANTI_SKIP_TLS === 'true';
|
||||
if (!apiKey) return res.status(503).json({ error: 'Ivanti API key not configured' });
|
||||
|
||||
// Fetch SVP values
|
||||
const svpResult = await ivantiPost(`/client/${clientId}/hostFinding/suggest`, {
|
||||
filter: { field: 'assetCustomAttributes.1550_host_10.value', operator: 'WILDCARD', value: '*', exclusive: false }
|
||||
}, apiKey, skipTls);
|
||||
|
||||
// Fetch all BU values
|
||||
const buResult = await ivantiPost(`/client/${clientId}/hostFinding/suggest`, {
|
||||
filter: { field: 'assetCustomAttributes.1550_host_1.value', operator: 'WILDCARD', value: '*', exclusive: false }
|
||||
}, apiKey, skipTls);
|
||||
|
||||
const svps = svpResult.status === 200 ? JSON.parse(svpResult.body) : [];
|
||||
const bus = buResult.status === 200 ? JSON.parse(buResult.body) : [];
|
||||
|
||||
res.json({ svps, bus });
|
||||
} catch (err) {
|
||||
console.error('[Ivanti] Org hierarchy error:', err.message);
|
||||
res.status(500).json({ error: 'Failed to fetch org hierarchy' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/ivanti/findings/bus-by-svp
|
||||
*
|
||||
* Return BU names that belong to a specific SVP, queried from the Ivanti
|
||||
* suggest API with the SVP as a filter. Used for cascading dropdowns in
|
||||
* admin configuration UIs.
|
||||
* Requires Admin group.
|
||||
*
|
||||
* @query {string} svp - The SVP name to filter BUs by (required)
|
||||
*
|
||||
* @returns {Object} 200 - { svp: string, bus: string[] }
|
||||
* @returns {Object} 400 - { error: string } when svp query parameter is missing
|
||||
* @returns {Object} 503 - { error: string } when Ivanti API key is not configured
|
||||
* @returns {Object} 500 - { error: string } on API failure
|
||||
*/
|
||||
router.get('/bus-by-svp', requireGroup('Admin'), async (req, res) => {
|
||||
const svp = (req.query.svp || '').trim();
|
||||
if (!svp) return res.status(400).json({ error: 'svp query parameter is required' });
|
||||
|
||||
try {
|
||||
const clientId = process.env.IVANTI_CLIENT_ID || '1550';
|
||||
const apiKey = process.env.IVANTI_API_KEY;
|
||||
const skipTls = process.env.IVANTI_SKIP_TLS === 'true';
|
||||
if (!apiKey) return res.status(503).json({ error: 'Ivanti API key not configured' });
|
||||
|
||||
// Get BUs filtered by the selected SVP
|
||||
const result = await ivantiPost(`/client/${clientId}/hostFinding/suggest`, {
|
||||
filter: { field: 'assetCustomAttributes.1550_host_1.value', operator: 'WILDCARD', value: '*', exclusive: false },
|
||||
filters: [{ field: 'assetCustomAttributes.1550_host_10.value', operator: 'EXACT', value: svp, exclusive: false }]
|
||||
}, apiKey, skipTls);
|
||||
|
||||
const bus = result.status === 200 ? JSON.parse(result.body) : [];
|
||||
res.json({ svp, bus });
|
||||
} catch (err) {
|
||||
console.error('[Ivanti] BUs by SVP error:', err.message);
|
||||
res.status(500).json({ error: 'Failed to fetch BUs for SVP' });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user