17 Commits

Author SHA1 Message Date
Jordan Ramos
e9d6038636 Add Granite Loader to AEO Compliance page with CARD enrichment and pagination
- Add checkbox selection + Granite Loader button to compliance device table
- Integrate LoaderModal for generating loader sheets from compliance devices
- Add direct IP resolve path (resolveAssetId + searchByAssetId) for CARD
  enrichment on compliance devices without Ivanti host IDs
- Add searchByAssetId helper for full enriched record via asset-search endpoint
- Include NTS-AEO-ACCESS-OPS in default enrich-batch team search
- Increase CARD quick-mode timeout from 15s to 30s
- Add timeout vs not-found distinction in enrichment error reporting
- Fix LoaderModal enriching state not resetting on modal reopen
- Add pagination to compliance device table (25/50/100/200 per page)
- Page resets on team, tab, filter, or search change
2026-06-19 13:49:26 -06:00
Jordan Ramos
032a8df403 Merge update_token from getOwner when asset-search omits it
When the hostId fast path resolves via asset-search but the response lacks
an update_token, do a follow-up getOwner() call using the resolved _id to
fetch the token. Returns the rich owner data from asset-search merged with
the update_token from the owner endpoint.
2026-06-09 14:23:56 -06:00
Jordan Ramos
32ed65eb79 Fix owner-lookup hostId fast path — use asset-search owner data directly
The asset-search response wraps in { assets: [...] } and includes the full
owner record. Previously we tried to extract just an _id from the top level
(which didn't exist) and then made a separate getOwner() call that returned
empty data for IPv6 assets.

Now when hostId resolves via asset-search, we return the owner data directly
from the search response — no second API call needed. This fixes the tooltip
showing empty confirmed/unconfirmed for IPv6-only findings.
2026-06-09 14:03:31 -06:00
Jordan Ramos
23ea3983c8 Fix Enrich from CARD for items without IP — use host_id lookup
The enrich-batch endpoint now accepts a host_ids array alongside ips.
When queue items have no IP address but have a host_id (from ivanti_findings),
the frontend sends host_ids and the backend resolves them via CARD asset-search.

Results include the resolved IP so it populates the IPV4_ADDRESS column.
The LoaderModal now carries _host_id from initialDevices through to the
enrich call.
2026-06-09 13:00:02 -06:00
Jordan Ramos
54d6e49cb1 Use asset-search fast path in enrich-batch for Granite lookups
The CARD asset-search endpoint returns the full enriched record (card_flags,
ivanti_assets, ncim_discovery, etc.) — same shape as team-assets. Before
falling back to the slow paginated team-assets loop, try each IP's host_id
via asset-search for direct single-call resolution.

Also registers the notifications table migration in run-all.js.
2026-06-09 12:48:08 -06:00
Jordan Ramos
a8d3909798 Add CARD asset-search by Ivanti Host ID for faster lookups
Integrate CARD's new v2 asset-search endpoint that accepts Ivanti Asset ID
integers directly, eliminating the slow suffix-guessing resolution flow.

Changes:
- Add searchByIvantiHostId() helper to cardApi.js
- Add GET /api/card/asset-search/:hostId endpoint
- Update CARD queue confirm/decline/redirect to try host_id fast path first
- Update owner-lookup to accept optional hostId query param for fast resolution
- Pass hostId through CardOwnerTooltip and ReportingPage for tooltip lookups
- Join ivanti_findings in todo queue GET to expose host_id on queue items
- Update CardActionModal to pass host_id for faster owner-lookup
2026-06-09 11:57:13 -06:00
Jordan Ramos
e887fa8946 Add CARD ownership tooltip and direct action modal on IP hover
Hover over any IP address in the findings table to see CARD ownership data
(confirmed/unconfirmed/candidate teams) in an interactive tooltip. Click
'Actions' to open a full modal for confirm/decline/redirect — no queue
item required.

Backend:
- Add direct /api/card/owner/:assetId/confirm|decline|redirect endpoints
- Add quick mode to resolveAssetId (CTEC only, 15s timeout) for tooltip use
- owner-lookup supports ?quick=1 query param with 504 on timeout
- getOwner accepts options for custom timeout

Frontend:
- New CardOwnerTooltip component (portal, hover bridge, cached results)
- New CardDetailModal for confirm/decline/redirect from tooltip
- IP cells show help cursor, trigger tooltip on 400ms hover
- Timeouts (504) not cached — retry on re-hover
- Teams fetch retries silently up to 3x on failure
- Redirect dropdowns show owner-data teams as fallback when teams API fails
2026-06-04 11:15:13 -06:00
Jordan Ramos
1cc8bd5a4c Improve CARD decline error diagnostics and prevent accidental modal dismiss
- Log the full owner response in audit when update_token is missing so
  we can see what CARD actually returned
- Improve error message to suggest the asset may have already been actioned
- Remove backdrop-click-to-close on TemplateFormModal to prevent
  accidental data loss while filling in template content
2026-06-03 13:33:24 -06:00
Jordan Ramos
b23a49a78d Sync enrich-batch JSDoc with both-team and candidate search behavior
The enrich-batch handler was updated in df62e13 to search both
NTS-AEO-STEAM and NTS-AEO-ACCESS-ENG across confirmed, unconfirmed, and
candidate dispositions, but the JSDoc comment still described the old
single-team, two-disposition behavior. Update the comment to match.
2026-05-29 12:27:25 -06:00
Jordan Ramos
df62e13627 Search both teams and all dispositions in enrich-batch
Search NTS-AEO-STEAM and NTS-AEO-ACCESS-ENG across confirmed,
unconfirmed, and candidate dispositions. Assets that are only
candidates (not yet confirmed) were previously missed.
2026-05-28 16:10:59 -06:00
Jordan Ramos
8224183679 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.
2026-05-28 14:58:27 -06:00
Jordan Ramos
1256c7510f Rewrite enrich-batch to use team assets endpoint for full data
The owner endpoint only returns ownership info (no card_flags,
ncim_discovery, or netops_granite_allips). Switch to fetching
team assets (paginated) which returns the full enriched record
with EQUIP_INST_ID, CARD_HOSTNAME, CARD_ASN, CARD_DEVICE_ID,
CARD_VENDOR_MODEL, CARD_CLLI, and ncim_discovery data.

Accepts optional 'team' parameter (defaults to NTS-AEO-STEAM).
Paginates through confirmed and unconfirmed dispositions until
all target IPs are found or pages are exhausted.
2026-05-27 19:50:01 -06:00
Jordan Ramos
3310f7fa22 Improve CARD enrichment to extract fields from card_flags
The owner endpoint doesn't return ncim_discovery (so EQUIP_INST_ID
is unavailable from that endpoint). Update extractGraniteFields to
pull hostname from CARD_HOSTNAME, ASN from CARD_ASN, CLLI from
CARD_CLLI, serial from CARD_DEVICE_ID, and vendor/model from
CARD_VENDOR_MODEL in the card_flags array.

Assets without EQUIP_INST_ID are not in Granite and should use
the Add operation in the loader sheet instead of Change.
2026-05-27 19:40:21 -06:00
Jordan Ramos
8fc7c33cff Auto-resolve bare IP to CARD asset ID with suffix lookup
The CARD API requires asset IDs in the format {IP}-{SUFFIX} (e.g.,
10.240.78.110-CTEC) but the frontend only has the bare IP. Add
resolveAssetId() helper that tries known suffixes (CTEC, NATL,
CHTR, COML, RESI, WIFI, VOIP) via owner lookup until one succeeds.

Apply resolution to confirm, decline, and redirect handlers so
they accept bare IPs from the frontend and resolve them
automatically before calling the CARD mutation APIs.
2026-05-27 18:56:40 -06:00
Jordan Ramos
fe82362afa Add Granite Loader Sheet generator with CARD enrichment
Implement the Granite Team_Device Loader xlsx export feature:
- Add graniteLoaderConfig.js with all 41 columns, groupings, and
  operation-type requirements (Change/Add/Delete/Move)
- Add graniteLoaderExport.js for client-side xlsx generation using
  the xlsx library
- Add LoaderModal component with operation type selection, column
  checkboxes, bulk defaults with per-row overrides, editable preview
  table, CARD enrichment integration, and standalone paste-IPs mode
- Add POST /api/card/enrich-batch endpoint for batch IP lookup in
  CARD returning EQUIP_INST_ID, hostname, site, ASN, team
- Integrate 'Generate Loader Sheet' button in Ivanti Queue floating
  action bar (visible when CARD/GRANITE/DECOM items selected)
- Add card-connectivity-test.js script for verifying CARD API access
2026-05-27 17:18:36 -06:00
Jordan Ramos
33927b150b feat(postgres): migrate all route files from SQLite to pg pool
- All 16 route files now import pool from ../db directly
- Removed db parameter from all factory functions
- All callbacks replaced with async/await pool.query()
- All ? placeholders converted to $1, $2... numbered params
- datetime('now') → NOW(), INSERT OR IGNORE → ON CONFLICT DO NOTHING
- LIKE → ILIKE for case-insensitive searches
- Error detection: err.code === '23505' for unique violations
- server.js no longer passes pool/db/requireAuth to route factories
- Only ivantiFindings.js still receives pool (pending task 8 rewrite)
2026-05-06 11:44:17 -06:00
root
15abf8bae4 feat: add return classification for archive chart, CARD API integration, compliance charts, systemd services 2026-05-01 17:15:41 +00:00