Catalyst / admin/Synapse-Cortex 14.8 GB / 57.8 GB 40.0 GB free
Help Sign in

admin / Synapse-Cortex

public

Self Hosted ITSM Tool with RBAC/Tenanting and MFA

Code Issues Pull requests Pipelines Packages Security Insights Wiki Settings
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 %}