admin / Synapse-Cortex
publicSelf Hosted ITSM Tool with RBAC/Tenanting and MFA
Synapse-Cortex / synapse-cortex / app / templates / admin_integrations.html
6000 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | {% extends "base.html" %} {% block title %}Integrations // Synapse-Cortex{% endblock %} {% block header %}Admin // Integrations{% endblock %} {% block content %} <div class="max-w-2xl border border-cortex-border bg-cortex-panel rounded-md p-6"> <div class="text-[10px] text-cortex-muted uppercase tracking-wider font-display mb-2">NetscanXi Integration</div> <p class="text-xs text-cortex-muted mb-4"> Generate a key here, then paste the API URL and key into NetscanXi's own <b class="text-chrome">Synapse Cortex</b> integration panel. The same key covers two things: </p> <ul class="text-xs text-cortex-muted mb-4 pl-4 list-disc space-y-1"> <li><b class="text-chrome">Asset import</b> — discovered inventory (asset ID, MAC, IP, type, OS, software) syncs into this tenant's CMDB. The asset ID stays identical in both systems.</li> <li><b class="text-chrome">Ticket auto-creation</b> — items from NetscanXi's Remediation Tracking are pushed in as Cortex tickets, linked to their asset when one was already imported. A ticket is created once per NetscanXi remediation item and then left alone on later pushes, so work already done on it in Cortex is never overwritten.</li> </ul> <div id="int-msg" class="hidden mb-4 text-xs px-3 py-2 rounded"></div> <div class="space-y-3 text-sm"> <div> <label class="block text-[10px] text-cortex-muted mb-1 uppercase tracking-wider">API URL (paste into NetscanXi's "API URL" field)</label> <div class="flex gap-2"> <input id="endpoint-url" type="text" readonly onfocus="this.select()" class="flex-1 bg-cortex-panel2 border border-cortex-border rounded px-3 py-2 font-mono text-xs" /> <button onclick="copyField('endpoint-url')" class="text-[10px] uppercase px-3 rounded border border-cortex-border text-cortex-muted hover:text-accent-blue">Copy</button> </div> <div class="text-[10px] text-cortex-muted mt-1">This is the base address of this Cortex instance — NetscanXi appends its own ingest path automatically. Don't add a path to it.</div> </div> <div> <label class="block text-[10px] text-cortex-muted mb-1 uppercase tracking-wider"> API Key <span id="key-state" class="text-cortex-muted normal-case">{{ "· configured" if key_set else "· not generated" }}</span> </label> <div class="flex gap-2"> <input id="api-key" type="text" readonly onfocus="this.select()" placeholder="Generate a key to reveal it once" class="flex-1 bg-cortex-panel2 border border-cortex-border rounded px-3 py-2 font-mono text-xs" /> <button onclick="copyField('api-key')" class="text-[10px] uppercase px-3 rounded border border-cortex-border text-cortex-muted hover:text-accent-blue">Copy</button> </div> <div class="text-[10px] text-cortex-muted mt-1">Shown once at generation. It can't be retrieved later — only revoked and reissued.</div> </div> <label class="flex items-center gap-2 text-xs"> <input type="checkbox" id="enabled-toggle" {% if enabled %}checked{% endif %} {% if not key_set %}disabled{% endif %} onchange="toggleEnabled(this.checked)" /> Enable asset ingestion from NetscanXi </label> {% if last_used_at %} <div class="text-[10px] text-cortex-muted">Last used: {{ last_used_at.strftime('%Y-%m-%d %H:%M:%S') }}</div> {% endif %} </div> <div class="flex gap-2 mt-5"> <button onclick="generateKey()" class="bg-accent-blue text-cortex-bg font-display font-bold uppercase tracking-wider text-xs px-4 py-2 rounded hover:bg-accent-blue-bright transition"> {{ "Regenerate API Key" if key_set else "Generate API Key" }} </button> {% if key_set %} <button onclick="revokeKey()" class="text-[10px] uppercase text-accent-red border border-accent-red/40 rounded px-4 hover:bg-accent-red/10 transition">Revoke</button> {% endif %} </div> </div> <script> // Base URL only - NetscanXi's cortex.py client appends its own ingest // path ("/api/ingest/assets") onto whatever URL is configured here, the // same way it already does for the proven Synapse Sonar integration. // Pasting the full ingest path here would double up and 404. document.getElementById('endpoint-url').value = window.location.origin; function intMsg(text, isError) { const el = document.getElementById('int-msg'); el.textContent = text; el.className = 'mb-4 text-xs px-3 py-2 rounded border ' + (isError ? 'border-accent-red/40 bg-accent-red/10 text-accent-red' : 'border-accent-green/40 bg-accent-green/10 text-accent-green'); } function copyField(id) { const el = document.getElementById(id); if (!el.value) return; el.select(); navigator.clipboard && navigator.clipboard.writeText(el.value).catch(() => {}); } async function generateKey() { if (!confirm('Generate a new API key? Any existing key stops working immediately.')) return; const res = await fetch('/api/v1/admin/integrations/key', { method: 'POST' }); const data = await res.json(); if (!res.ok) { intMsg(data.detail || 'Failed to generate key.', true); return; } document.getElementById('api-key').value = data.api_key; intMsg('Key generated. Copy it now and paste the URL + key into NetscanXi — it will not be shown again.'); setTimeout(() => window.location.reload(), 2500); } async function toggleEnabled(checked) { const res = await fetch('/api/v1/admin/integrations/toggle?enabled=' + checked, { method: 'POST' }); const data = await res.json(); if (!res.ok) { intMsg(data.detail || 'Failed to update.', true); return; } intMsg(checked ? 'Ingestion enabled.' : 'Ingestion disabled.'); } async function revokeKey() { if (!confirm('Revoke the API key? NetscanXi will stop being able to push assets here.')) return; const res = await fetch('/api/v1/admin/integrations/key', { method: 'DELETE' }); if (res.ok) window.location.reload(); } </script> {% endblock %} |