feat: add Ivanti FP workflow submission from Queue
- Add shared ivantiApi.js helper (ivantiPost + ivantiMultipartPost) - Add ivantiFpWorkflow.js backend route with validation, Ivanti API workflow creation, attachment uploads, submission tracking, and audit - Add add_fp_submissions_table.js migration - Wire route into server.js at /api/ivanti/fp-workflow - Add FpWorkflowModal component in ReportingPage.js with form fields, drag-and-drop file upload, progress indicator, and result views - Add Create FP Workflow button to QueuePanel footer (editor/admin only) - Refactor ivantiWorkflows.js and ivantiFindings.js to use shared helper
This commit is contained in:
@@ -4,49 +4,11 @@
|
||||
// Error codes: 401 bad key, 419 insufficient privileges, 429 rate limited
|
||||
|
||||
const express = require('express');
|
||||
const https = require('https');
|
||||
const { requireGroup } = require('../middleware/auth');
|
||||
const { ivantiPost } = require('../helpers/ivantiApi');
|
||||
|
||||
const IVANTI_URL_BASE = 'https://platform4.risksense.com/api/v1';
|
||||
const SYNC_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HTTP helper — uses Node's https module directly so we can toggle
|
||||
// rejectUnauthorized for Charter's SSL inspection proxy (IVANTI_SKIP_TLS=true)
|
||||
// ---------------------------------------------------------------------------
|
||||
function ivantiPost(urlPath, body, apiKey, skipTls) {
|
||||
const bodyStr = JSON.stringify(body);
|
||||
const fullUrl = new URL(IVANTI_URL_BASE + urlPath);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: fullUrl.hostname,
|
||||
path: fullUrl.pathname + fullUrl.search,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': '*/*',
|
||||
'content-type': 'application/json',
|
||||
'x-api-key': apiKey,
|
||||
'x-http-client-type': 'browser',
|
||||
'content-length': Buffer.byteLength(bodyStr)
|
||||
},
|
||||
rejectUnauthorized: !skipTls,
|
||||
timeout: 15000
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => { data += chunk; });
|
||||
res.on('end', () => resolve({ status: res.statusCode, body: data }));
|
||||
});
|
||||
|
||||
req.on('timeout', () => req.destroy(new Error('Request timed out')));
|
||||
req.on('error', reject);
|
||||
req.write(bodyStr);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ensure the sync state table exists (idempotent — safe to call on every start)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user