HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //tmp/ezdns_index_runtime_scripts.js

      const apiBase = '';
      const scansTbody = document.getElementById('scans');
      const scanBtn = document.getElementById('scan');
      const domainInput = document.getElementById('domain');
      const scanOutcome = document.getElementById('scanOutcome');
      let lastScanOutcomeDomain = '';
      const API_KEY = 'dev-key';
      const SUPPORT_URL = 'https://support.panomity.com/tickets/new/';
      const quickRequestForm = document.getElementById('quickRequestForm');
      const requestDomain = document.getElementById('requestDomain');
      const requestEmail = document.getElementById('requestEmail');
      const requestMessage = document.getElementById('requestMessage');
      const requestPlan = document.getElementById('requestPlan');
      const requestStatus = document.getElementById('requestStatus');
      const ORDER_URLS = {
        priority_audit: 'https://support.panomity.com/cart/priority-support/DNS-Priority-Audit/&step=0',
        quick_fix: 'https://support.panomity.com/cart/priority-support/DNS-Quick-Fix/&step=0',
        cleanup: 'https://support.panomity.com/cart/priority-support/DNS-Mail-Security-Cleanup/&step=0',
        managed_monitoring: 'https://support.panomity.com/?cmd=cart&action=add&id=856&cycle=m',
        pro_monthly: 'https://support.panomity.com/?cmd=cart&action=add&id=854&cycle=m',
        pro_yearly: 'https://support.panomity.com/?cmd=cart&action=add&id=854&cycle=a',
        business_monthly: 'https://support.panomity.com/?cmd=cart&action=add&id=855&cycle=m',
        business_yearly: 'https://support.panomity.com/?cmd=cart&action=add&id=855&cycle=a',
        agency_monthly: 'https://support.panomity.com/?cmd=cart&action=add&id=856&cycle=m',
        agency_yearly: 'https://support.panomity.com/?cmd=cart&action=add&id=856&cycle=a'
      };

      const ORDER_POSTS = {
        priority_audit: { action: "https://support.panomity.com/cart/priority-support/DNS-Priority-Audit/&step=0", id: "879", cycle: "Once" },
        quick_fix: { action: "https://support.panomity.com/cart/priority-support/DNS-Quick-Fix/&step=0", id: "867", cycle: "Once" },
        cleanup: { action: "https://support.panomity.com/cart/priority-support/DNS-Mail-Security-Cleanup/&step=0", id: "868", cycle: "Once" }
      };

      function trackEvent(category, action, name) {
        if (window._paq) window._paq.push(['trackEvent', category, action, name || '']);
      }

      function currentDomainValue() {
        const typed = domainInput && domainInput.value ? domainInput.value.trim().toLowerCase() : '';
        return typed || lastScanOutcomeDomain;
      }

      async function recordLead(intent, plan, extra = {}) {
        try {
          const payload = Object.assign({
            source: 'website',
            intent,
            plan,
            domain: currentDomainValue(),
            page: window.location.pathname
          }, extra || {});
          await fetch('/v1/leads', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload)
          });
        } catch (e) { /* lead tracking must never block sales */ }
      }

      function syncRequestDomain() {
        if (requestDomain && !requestDomain.value && currentDomainValue()) {
          requestDomain.value = currentDomainValue();
        }
      }

      async function submitQualifiedRequest(event) {
        event.preventDefault();
        const email = requestEmail ? requestEmail.value.trim() : '';
        const message = requestMessage ? requestMessage.value.trim() : '';
        const domain = requestDomain && requestDomain.value.trim() ? requestDomain.value.trim().toLowerCase() : currentDomainValue();
        const plan = requestPlan && requestPlan.value ? requestPlan.value : 'cleanup';
        if (!email || !message) {
          if (requestStatus) requestStatus.textContent = 'Bitte E-Mail und Problem ergänzen.';
          return;
        }
        if (requestStatus) requestStatus.textContent = 'Anfrage wird gespeichert…';
        trackEvent('Revenue', 'qualified_fix_request', plan);
        await recordLead('qualified_fix_request', plan, {
          domain,
          contact_email: email,
          message,
          page: window.location.pathname + '#fix-request'
        });
        if (requestStatus) requestStatus.textContent = 'Gespeichert. Wir haben jetzt Domain, Kontakt und Problem im Revenue-Report.';
        toast('Fix-Anfrage gespeichert. Nächster Schritt: passendes Paket buchen.', 'success', {
          actionHref: orderUrlFor(plan) || supportTicketUrl('qualified_fix_request', plan),
          actionLabel: 'Paket öffnen',
          timeoutMs: 9000
        });
      }

      function supportTicketUrl(intent, plan) {
        const params = new URLSearchParams();
        params.set('subject', `ezdns.support ${intent}: ${plan}`);
        if (currentDomainValue()) params.set('domain', currentDomainValue());
        params.set('source', 'ezdns.support');
        return `${SUPPORT_URL}?${params.toString()}`;
      }

      function orderUrlFor(plan) {
        return ORDER_URLS[plan] || '';
      }


      function submitHostBillOrder(plan) {
        const cfg = ORDER_POSTS[plan];
        if (!cfg) return false;
        const form = document.createElement("form");
        form.method = "post";
        form.action = cfg.action;
        for (const [name, value] of Object.entries({ action: "add", id: cfg.id, cycle: cfg.cycle })) {
          const input = document.createElement("input");
          input.type = "hidden";
          input.name = name;
          input.value = value;
          form.appendChild(input);
        }
        document.body.appendChild(form);
        form.submit();
        return true;
      }

      async function requestService(plan, intent = 'service_lead') {
        trackEvent('Revenue', intent, plan);
        await recordLead(intent, plan);
        const orderUrl = orderUrlFor(plan);
        toast(orderUrl ? 'Warenkorb wird geöffnet…' : 'Anfrage wird geöffnet…', 'info', { timeoutMs: 1500 });
        setTimeout(() => {
          if (submitHostBillOrder(plan)) return;
          window.location.href = orderUrl || supportTicketUrl(intent, plan);
        }, 250);
      }

      async function fetchJSON(url, opts={}) {
        const headers = Object.assign({ 'x-api-key': API_KEY }, opts.headers||{});
        const res = await fetch(url, Object.assign({}, opts, { headers }));
        if (!res.ok) throw new Error('HTTP '+res.status);
        return await res.json();
      }

      function sleep(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
      }

      function toast(message, variant = 'info', opts = {}) {
        const hostId = 'toastHost';
        let host = document.getElementById(hostId);
        if (!host) {
          host = document.createElement('div');
          host.id = hostId;
          host.className = 'fixed top-4 right-4 z-50 flex flex-col gap-2';
          document.body.appendChild(host);
        }

        const t = document.createElement('div');
        const cls = variant === 'success'
          ? 'bg-emerald-900 text-emerald-100 border border-emerald-700'
          : variant === 'error'
            ? 'bg-red-900 text-red-100 border border-red-700'
            : 'bg-slate-900 text-slate-100 border border-slate-700';
        t.className = `px-3 py-2 rounded shadow max-w-sm ${cls}`;

        const row = document.createElement('div');
        row.className = 'flex items-center gap-3';
        const msg = document.createElement('div');
        msg.className = 'text-sm';
        msg.textContent = message;
        row.appendChild(msg);

        if (opts.actionHref && opts.actionLabel) {
          const a = document.createElement('a');
          a.href = opts.actionHref;
          a.className = 'text-sm text-blue-200 hover:underline whitespace-nowrap';
          a.textContent = opts.actionLabel;
          row.appendChild(a);
        }

        t.appendChild(row);
        host.appendChild(t);

        const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : 3500;
        if (timeoutMs > 0) {
          setTimeout(() => {
            t.remove();
            if (host && host.childElementCount === 0) host.remove();
          }, timeoutMs);
        }

        return t;
      }

      const scanPollers = new Map();

      async function pollScanUntilDone(scanId, { maxMs = 120000, intervalMs = 2000 } = {}) {
        const start = Date.now();
        while (Date.now() - start < maxMs) {
          await sleep(intervalMs);
          const scan = await fetchJSON(`${apiBase}/v1/scan/${scanId}`);
          if (scan && scan.status === 'done') return scan;
        }
        throw new Error('timeout');
      }

      function row(status) {
        const cls = status === 'done' ? 'bg-emerald-900 text-emerald-200' : 'bg-amber-900 text-amber-200';
        return `<span class="px-2 py-0.5 rounded ${cls}">${status}</span>`;
      }

      function escapeHTML(value) {
        return String(value ?? '').replace(/[&<>"']/g, (ch) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[ch]));
      }

      function scanFindings(scan) {
        const result = scan && scan.result ? scan.result : {};
        return Array.isArray(result.findings) ? result.findings : [];
      }

      function renderScanOutcome(scan) {
        if (!scanOutcome || !scan) return;
        const result = scan.result || {};
        const domain = result.domain || currentDomainValue() || 'deine Domain';
        lastScanOutcomeDomain = String(domain || '').trim().toLowerCase();
        syncRequestDomain();
        const findings = scanFindings(scan);
        const critical = findings.filter((f) => f.severity === 'error').length;
        const warnings = findings.filter((f) => f.severity === 'warning').length;
        const top = findings.find((f) => f.severity === 'error') || findings.find((f) => f.severity === 'warning') || findings[0];
        const recommendedPlan = critical > 0 || findings.length > 1 ? 'cleanup' : 'quick_fix';
        const recommendedLabel = recommendedPlan === 'cleanup' ? 'Cleanup €499 buchen' : 'Quick Fix €149 buchen';
        const scoreLabel = critical > 0 ? 'kritisch' : warnings > 0 ? 'fixbar' : 'stabil';
        const summaryText = findings.length
          ? `${critical} kritisch · ${warnings} Warnungen · ${findings.length} Findings`
          : 'Keine dringenden Findings gefunden';
        const findingText = top
          ? `${escapeHTML(top.category || 'dns')} – ${escapeHTML(top.message || 'Empfohlene DNS-Korrektur')}`
          : 'Monitoring aktivieren, damit DNS- und Mail-Änderungen nicht unbemerkt bleiben.';
        scanOutcome.classList.remove('hidden');
        scanOutcome.innerHTML = `
          <div class="ez-scan-outcome-head">
            <div>
              <span>Scan-Ergebnis</span>
              <strong>${escapeHTML(domain)}</strong>
            </div>
            <b>${escapeHTML(scoreLabel)}</b>
          </div>
          <div class="ez-scan-outcome-body">
            <div>
              <small>${escapeHTML(summaryText)}</small>
              <p>${findingText}</p>
            </div>
            <div class="ez-scan-outcome-actions">
              <a href="/app/detail.html?id=${encodeURIComponent(scan.id)}">Report öffnen</a>
              <button data-scan-service-lead="priority_audit">Audit €49</button>
              <button data-scan-service-lead="${recommendedPlan}">${recommendedLabel}</button>
              ${recommendedPlan === 'cleanup' ? '<button data-scan-service-lead="quick_fix">Quick Fix €149</button>' : '<button data-scan-service-lead="cleanup">Cleanup €499</button>'}
            </div>
          </div>
        `;
      }

      function setActiveTab(tab) {
        const buttons = document.querySelectorAll('.tab-btn');
        const panels = document.querySelectorAll('.tab-panel');
        for (const b of buttons) {
          const isActive = b.dataset.tab === tab;
          b.className = isActive
            ? 'tab-btn px-3 py-1.5 rounded-lg bg-slate-800 text-slate-100'
            : 'tab-btn px-3 py-1.5 rounded-lg border border-slate-700 text-slate-200';
        }
        for (const p of panels) {
          p.classList.toggle('hidden', p.id !== `tab-${tab}`);
        }
      }

      async function loadScans() {
        const data = await fetchJSON(`${apiBase}/v1/scans?limit=5`);
        scansTbody.innerHTML = '';
        for (const s of data.items) {
          const tr = document.createElement('tr');
          const domain = s.domain || s.domain_id;
          tr.innerHTML = `
            <td class="py-2 text-blue-300">${domain}</td>
            <td>${row(s.status)}</td>
            <td><a href="/app/detail.html?id=${s.id}" class="text-sm text-blue-400 hover:underline">Open</a></td>
          `;
          scansTbody.appendChild(tr);
        }
      }

      // kein separates Detail-Panel mehr
      const listedCheckbox = document.getElementById('listed');
      scanBtn.addEventListener('click', async () => {
        const d = domainInput.value.trim();
        if (!d) return;
        try {
          scanBtn.disabled = true; scanBtn.textContent = 'Starting…';
          const created = await fetchJSON(`${apiBase}/v1/scan`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ domain: d, listed: !!listedCheckbox.checked }) });
          const scanId = created && created.id;
          toast('Scan läuft… wir sagen Bescheid, sobald er fertig ist.', 'info', { timeoutMs: 4000 });
          setActiveTab('scans');
          await loadScans();

          if (scanId && !scanPollers.has(scanId)) {
            const p = pollScanUntilDone(scanId)
              .then(async (finishedScan) => {
                renderScanOutcome(finishedScan);
                toast('Scan fertig. Ergebnis und Fix-Optionen sind sichtbar.', 'success', { actionLabel: 'Report öffnen', actionHref: `/app/detail.html?id=${scanId}`, timeoutMs: 9000 });
                await loadScans();
              })
              .catch(() => {
                toast('Scan läuft noch – bitte in „Letzte Scans“ prüfen.', 'info', { timeoutMs: 6000 });
              })
              .finally(() => {
                scanPollers.delete(scanId);
              });
            scanPollers.set(scanId, p);
          }
        } catch (e) {
          toast('Scan konnte nicht gestartet werden.', 'error', { timeoutMs: 5000 });
          } finally {
          scanBtn.disabled = false; scanBtn.textContent = 'Scan';
          }
      });

      // Checkout-Funktion für den kostenlosen Scan. Bezahlte CTAs nutzen requestService() und HostBill direkt.
      function checkout(plan) {
        if (plan === 'free') {
          trackEvent('Revenue', 'free_plan_clicked', 'free');
          domainInput.focus();
          setActiveTab('scans');
          return;
        }
        requestService(plan, 'plan_inquiry');
      }
      
      // Tabs + initial state
      document.querySelectorAll('.tab-btn').forEach((b) => {
        b.addEventListener('click', () => setActiveTab(b.dataset.tab));
      });
      setActiveTab('why');

      const cta = document.getElementById('ctaFocus');
      if (cta) {
        cta.addEventListener('click', () => {
          trackEvent('Acquisition', 'scan_cta_clicked', 'hero');
          domainInput.focus();
          setActiveTab('scans');
        });
      }
      const serviceCta = document.getElementById('serviceCta');
      if (serviceCta) {
        serviceCta.addEventListener('click', () => requestService(serviceCta.dataset.serviceLead || 'priority_audit', 'service_lead'));
      }
      if (quickRequestForm) {
        syncRequestDomain();
        quickRequestForm.addEventListener('submit', submitQualifiedRequest);
      }
      document.body.addEventListener('click', (event) => {
        const scanServiceBtn = event.target.closest('[data-scan-service-lead]');
        if (!scanServiceBtn) return;
        event.preventDefault();
        requestService(scanServiceBtn.dataset.scanServiceLead, 'scan_result_fix');
      });

      loadScans();
      // Footer year
      document.getElementById('year').textContent = new Date().getFullYear();
      // Matomo config + tracking
      (async () => {
        try {
          await import('/config.js');
          const cfg = window.__APP_CONFIG__ || {};
          if (cfg.MATOMO_URL && cfg.MATOMO_SITE_ID) {
            var _paq = (window._paq = window._paq || []);
            _paq.push(['trackPageView']);
            _paq.push(['enableLinkTracking']);
            (function() {
              var u = cfg.MATOMO_URL.endsWith('/') ? cfg.MATOMO_URL : cfg.MATOMO_URL + '/';
              _paq.push(['setTrackerUrl', u + 'matomo.php']);
              _paq.push(['setSiteId', cfg.MATOMO_SITE_ID]);
              var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
              g.async = true; g.src = u + 'matomo.js'; s.parentNode.insertBefore(g, s);
            })();
          }
        } catch (e) { /* ignore */ }
      })();