Add CARD Action Modal with full owner context
Replace inline CARD action form with a centered modal that: - Fetches and displays the full CARD owner record (confirmed, unconfirmed, candidates, declined teams with scores/sources) - Shows queue item info (hostname, IP, finding, CVEs) - Lets user switch between Confirm/Decline/Redirect actions - Pre-fills team dropdowns from the actual owner data - Shows CARD API errors inline with full detail Add GET /api/card/owner-lookup/:ip endpoint that resolves a bare IP to a CARD asset ID and returns the structured owner record.
This commit is contained in:
@@ -500,16 +500,69 @@ function createCardApiRouter() {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /owner-lookup/:ip
|
||||
*
|
||||
* Resolve an IP to a CARD asset ID and return the full owner record.
|
||||
* Used by the CARD Action Modal to display ownership state before
|
||||
* confirm/decline/redirect operations.
|
||||
*
|
||||
* @param {string} ip - IP address (path parameter)
|
||||
* @response 200 - { asset_id, ip, confirmed, unconfirmed, declined, candidate, update_token }
|
||||
* @response 404 - { error: string } — IP not found in CARD
|
||||
* @response 503 - { error: string } — CARD not configured
|
||||
*/
|
||||
router.get('/owner-lookup/:ip', requireAuth(), requireGroup('Admin', 'Standard_User'), async (req, res) => {
|
||||
if (!isConfigured) {
|
||||
return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
|
||||
}
|
||||
|
||||
const ip = req.params.ip;
|
||||
if (!ip || !ip.trim()) {
|
||||
return res.status(400).json({ error: 'IP address is required.' });
|
||||
}
|
||||
|
||||
// Resolve to full asset ID
|
||||
const assetId = await resolveAssetId(ip.trim());
|
||||
if (!assetId) {
|
||||
return res.status(404).json({ error: `Asset not found in CARD for IP: ${ip}` });
|
||||
}
|
||||
|
||||
// Fetch full owner record
|
||||
try {
|
||||
const ownerResult = await getOwner(assetId);
|
||||
if (!ownerResult.ok) {
|
||||
return res.status(ownerResult.status).json({ error: `Failed to fetch owner: HTTP ${ownerResult.status}` });
|
||||
}
|
||||
|
||||
const data = JSON.parse(ownerResult.body);
|
||||
const owner = data.owner || {};
|
||||
|
||||
res.json({
|
||||
asset_id: assetId,
|
||||
ip: ip.trim(),
|
||||
confirmed: owner.confirmed || null,
|
||||
unconfirmed: owner.unconfirmed || null,
|
||||
declined: owner.declined || [],
|
||||
candidate: owner.candidate || [],
|
||||
update_token: owner.update_token || null,
|
||||
});
|
||||
} catch (err) {
|
||||
return handleCardError(err, res);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /enrich-batch
|
||||
*
|
||||
* Batch lookup IPs in CARD to extract Granite loader fields. Tries each IP
|
||||
* with known asset ID suffixes (CTEC, NATL, CHTR, etc.) and falls back to
|
||||
* bare IP lookup. Returns enrichment results for each IP.
|
||||
* Batch lookup IPs in CARD to extract Granite loader fields. Fetches team
|
||||
* assets (paginated, across confirmed and unconfirmed dispositions) and
|
||||
* matches against the provided IPs. Returns enrichment results for each IP.
|
||||
*
|
||||
* @body {string[]} ips - Non-empty array of IP address strings (max 200)
|
||||
* @body {string} [team="NTS-AEO-STEAM"] - Team name to search assets under
|
||||
* @response 200 - { results: object[], enriched_count: number, not_found_count: number, total: number }
|
||||
* Each result: { ip: string, found: boolean, equip_inst_id: string|null, hostname: string|null, site_name?: string|null, mgmt_ip_asn?: string|null, responsible_team?: string|null, equipment_class?: string, equip_template?: null, equip_status?: string|null, error?: string }
|
||||
* Each result: { ip: string, found: boolean, equip_inst_id: string|null, hostname: string|null, site_name?: string|null, mgmt_ip_asn?: string|null, responsible_team?: string|null, equipment_class?: string, equip_template?: string|null, equip_status?: string|null, serial_number?: string|null, error?: string }
|
||||
* @response 400 - { error: string } — invalid or empty ips array, or exceeds 200
|
||||
* @response 503 - { error: string, missingVars: string[] } — CARD not configured
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user