HEX
Server: Apache
System: Linux vpshost11508.publiccloud.com.br 5.15.179-grsec-vpshost-10.lc.el8.x86_64 #1 SMP Mon Apr 7 12:04:45 -03 2025 x86_64
User: wicomm2 (10002)
PHP: 8.3.0
Disabled: apache_child_terminate,dl,escapeshellarg,escapeshellcmd,exec,link,mail,openlog,passthru,pcntl_alarm,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getpriority,pcntl_setpriority,pcntl_signal,pcntl_signal_dispatch,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_wait,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig,php_check_syntax,php_strip_whitespace,popen,proc_close,proc_open,shell_exec,symlink,system
Upload Files
File: /home/wicomm2/public_html/blackfriday/lead.php
<?php
// lead.php — visualização interna do lead salvo na BF (VTEX MD)

require_once __DIR__ . '/config/config.php'; // ajuste o caminho se necessário
@date_default_timezone_set(defined('APP_TIMEZONE') ? APP_TIMEZONE : 'America/Sao_Paulo');

/* ---------- helpers ---------- */
function esc($s){ return htmlspecialchars((string)$s, ENT_QUOTES|ENT_SUBSTITUTE, 'UTF-8'); }
function clamp100($v){ $n=is_numeric($v)?floatval($v):0; return max(0,min(100,$n)); }
function maturity_to_score($m){
  $m = mb_strtolower(trim((string)$m),'UTF-8');
  if (strpos($m,'avan')===0) return 92;
  if (strpos($m,'inter')===0) return 65;
  if (strpos($m,'bás')===0 || strpos($m,'bas')===0) return 35;
  return 50;
}
function gauge_svg($score, $label=''){
  $score = clamp100($score);
  $color = ($score>=90)?'#0cce6b':(($score>=50)?'#ffa400':'#ff4e42');
  $R=45;$T=10;$C=2*pi()*$R;$off=$C*(1-$score/100);
  return '<svg width="120" height="120" viewBox="0 0 120 120" role="img" aria-label="'.esc($label).' '.$score.'">'.
         '<circle cx="60" cy="60" r="'.$R.'" fill="none" stroke="#1f2731" stroke-width="'.$T.'"/>'.
         '<circle cx="60" cy="60" r="'.$R.'" fill="none" stroke="'.$color.'" stroke-width="'.$T.
         '" stroke-dasharray="'.number_format($C,1,'.','').'" stroke-dashoffset="'.number_format($off,1,'.','').
         '" stroke-linecap="round" transform="rotate(-90 60 60)"/>'.
         '<text x="60" y="66" text-anchor="middle" font-size="28" font-weight="700" fill="'.$color.'">'.$score.'</text>'.
         '<text x="60" y="90" text-anchor="middle" font-size="12" fill="#8ea0b4">'.esc($label).'</text>'.
         '</svg>';
}
function vt_md_base(){ return 'https://'.VTEX_ACCOUNT.'.'.VTEX_ENVIRONMENT.'.com.br/api/dataentities/'.VTEX_DATA_ENTITY; }
function vt_lh_base(){ return 'https://'.VTEX_ACCOUNT.'.'.VTEX_ENVIRONMENT.'.com.br/api/dataentities/LH'; }

function http_get_json($url,$connect_ms=8000,$timeout_ms=15000){
  $h=[
    'Accept: application/json',
    'Content-Type: application/json',
    'X-VTEX-API-AppKey: '.VTEX_APPKEY,
    'X-VTEX-API-AppToken: '.VTEX_APPTOKEN,
  ];
  $ch=curl_init($url);
  curl_setopt_array($ch,[
    CURLOPT_RETURNTRANSFER=>true,
    CURLOPT_HTTPHEADER=>$h,
    CURLOPT_CONNECTTIMEOUT_MS=>$connect_ms,
    CURLOPT_TIMEOUT_MS=>$timeout_ms,
    CURLOPT_SSL_VERIFYPEER=>true,
    CURLOPT_SSL_VERIFYHOST=>2,
    CURLOPT_IPRESOLVE=>CURL_IPRESOLVE_V4
  ]);
  $body=curl_exec($ch); $err=curl_error($ch); $code=curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch);
  if ($code>=400||!$body) return [null,$body?:$err?:('HTTP '.$code)];
  $j=json_decode($body,true);
  return [$j,null];
}
function parse_json_field($raw){
  if (is_array($raw)) return $raw;
  if (!is_string($raw) || trim($raw)==='') return null;
  $j=json_decode($raw,true);
  return is_array($j)?$j:null;
}

function lh_extract_slides($raw){
  // Normalize into [{headline, conteudo}]
  $slides = [];

  // Helper: push headline/conteudo if present
  $push = function($obj) use (&$slides){
    if (!is_array($obj)) return;
    $h = trim((string)($obj['headline'] ?? ''));
    $c = trim((string)($obj['conteudo'] ?? ''));
    if ($h !== '' || $c !== '') $slides[] = ['headline'=>$h, 'conteudo'=>$c];
  };

  // If it's already JSON string, try decode directly
  if (is_string($raw)) {
    $try = json_decode($raw, true);
    if (is_array($try)) $raw = $try;
  }

  // Case A: already an array of slides (headline/conteudo)
  if (is_array($raw) && isset($raw[0]) && (isset($raw[0]['headline']) || isset($raw[0]['conteudo']))) {
    foreach ($raw as $it) $push($it);

    // Special: single "Análise indisponível" containing fenced ```json blocks
    if (count($slides) === 1
        && stripos($slides[0]['headline'], 'análise indisponível') !== false
        && strpos($slides[0]['conteudo'], '```') !== false) {
      $content = $slides[0]['conteudo'];
      $slides = []; // reset and re-extract
      if (preg_match_all('/```json\\s*([\\s\\S]*?)```/mi', $content, $blocks)) {
        foreach ($blocks[1] as $blk) {
          if (preg_match_all('/\\{[\\s\\S]*?\\}/m', $blk, $objs)) {
            foreach ($objs[0] as $atom) $push(json_decode($atom, true));
          }
        }
      }
    }
    return $slides;
  }

  // Case B: string with fenced ```json containing one or many JSON objects
  if (is_string($raw) && strpos($raw, '```') !== false) {
    if (preg_match_all('/```json\\s*([\\s\\S]*?)```/mi', $raw, $blocks)) {
      foreach ($blocks[1] as $blk) {
        // Extract EACH JSON object inside a single fence (they may be adjacent without commas)
        if (preg_match_all('/\\{[\\s\\S]*?\\}/m', $blk, $objs)) {
          foreach ($objs[0] as $atom) $push(json_decode($atom, true));
        }
      }
      if (count($slides)) return $slides;
    }
  }

  // Case C: raw string with multiple "{...}" JSON objects (no fences)
  if (is_string($raw)) {
    if (preg_match_all('/\\{[\\s\\S]*?\\}/m', $raw, $objs)) {
      foreach ($objs[0] as $atom) $push(json_decode($atom, true));
      if (count($slides)) return $slides;
    }
  }

  // Case D: classic Lighthouse opportunities (title/id/savingsMs)
  if (is_array($raw) && isset($raw[0]) && (isset($raw[0]['title']) || isset($raw[0]['id']))) {
    foreach ($raw as $op) {
      $t = trim((string)($op['title'] ?? ($op['id'] ?? 'Oportunidade')));
      $sv = (isset($op['savingsMs']) && is_numeric($op['savingsMs'])) ? (int)$op['savingsMs'] : null;
      $desc = $sv !== null ? ('Economia potencial ~'.$sv.'ms') : '';
      $slides[] = ['headline'=>$t, 'conteudo'=>$desc];
    }
    return $slides;
  }

  return $slides;
}

/**
 * Lê o payload bruto do Lighthouse (como salvo na entidade LH) e
 * devolve [score:int|null, opportunities:array]
 */
function lighthouse_from_payload($payloadRaw){
  $score = null;
  $ops = [];

  $p = is_string($payloadRaw) ? json_decode($payloadRaw, true) : (is_array($payloadRaw) ? $payloadRaw : null);
  if (!is_array($p)) return [$score, $ops];

  // payload pode já vir como o JSON do PSI completo ou um wrapper
  $root = $p;
  if (isset($p['lighthouseResult'])) {
    $root = $p;
  } elseif (isset($p['payload']) && is_string($p['payload'])) {
    $pp = json_decode($p['payload'], true);
    if (isset($pp['lighthouseResult'])) $root = $pp;
  }

  if (!isset($root['lighthouseResult'])) return [$score, $ops];

  $lh   = $root['lighthouseResult'];
  $aud  = $lh['audits'] ?? [];
  $cats = $lh['categories'] ?? [];
  if (isset($cats['performance']['score'])) {
    $score = (int) round(($cats['performance']['score'] ?? 0) * 100);
  }

  foreach ($aud as $id => $a) {
    if (isset($a['details']['type']) && $a['details']['type'] === 'opportunity') {
      $ops[] = [
        'id'        => $id,
        'title'     => $a['title'] ?? $id,
        'savingsMs' => $a['details']['overallSavingsMs'] ?? 0
      ];
    }
  }
  usort($ops, fn($A,$B) => ($B['savingsMs'] ?? 0) <=> ($A['savingsMs'] ?? 0));
  $ops = array_slice($ops, 0, 10);

  return [$score, $ops];
}

/**
 * Busca fallback na entidade LH:
 *  - Se tiver scanId, procura por ele;
 *  - Senão, pega o mais recente (_creationDate desc).
 * Retorna [score:int|null, opportunities:array] se conseguir extrair.
 */
function try_fetch_lighthouse_from_LH($scanId = ''){
  $base = vt_lh_base();

  // 1) com scanId
  if ($scanId !== '') {
    $qs = http_build_query([
      '_where'  => 'scanId='.$scanId,
      '_fields' => 'scanId,payload',
      '_size'   => 1
    ]);
    [$res, $err] = http_get_json($base.'/search?'.$qs);
    if (!$err && is_array($res) && count($res)) {
      $row = $res[0];
      return lighthouse_from_payload($row['payload'] ?? '');
    }
  }

  // 2) último registro
  $qs = http_build_query([
    '_fields' => 'scanId,payload',
    '_size'   => 1,
    '_sort'   => '_creationDate desc'
  ]);
  [$res2, $err2] = http_get_json($base.'/search?'.$qs);
  if (!$err2 && is_array($res2) && count($res2)) {
    $row = $res2[0];
    return lighthouse_from_payload($row['payload'] ?? '');
  }

  return [null, []];
}

/* ---------- entrada ---------- */
$docId = trim((string)($_GET['id'] ?? ''));
if ($docId===''){ http_response_code(400); echo 'Parâmetro "id" é obrigatório (lead.php?id=DOCUMENT_ID).'; exit; }

/* ---------- busca no MD: documents -> fallback search ---------- */
$base = vt_md_base();
list($doc,$err) = http_get_json($base.'/documents/'.$docId);

$needFallback = false;
if ($err) { $needFallback = true; }
else {
  $keys = is_array($doc) ? array_keys($doc) : [];
  if (count($keys) <= 4 && isset($doc['id'])) $needFallback = true;
}

if ($needFallback){
  $fields = [
    'email','site','maturidade','explanation',
    'lighthouse_score','lighthouse_sugestoes','questions','roadmap_ai','scan_id','scanId'
  ];
  $qs = http_build_query([
    '_where'  => 'id='.$docId,
    '_fields' => implode(',', $fields)
  ]);
  list($res,$err2) = http_get_json($base.'/search?'.$qs);
  if ($err2 || !is_array($res) || !count($res)){
    http_response_code(502);
    echo 'Falha ao buscar documento: '.esc($err2 ?: 'sem resultados');
    exit;
  }
  $doc = $res[0];
}

/* ---------- campos ---------- */
$email       = $doc['email'] ?? '';
$site        = $doc['site'] ?? '';
$maturidade  = $doc['maturidade'] ?? '';
$explanation = $doc['explanation'] ?? '';

$lighthouseScore = $doc['lighthouse_score'] ?? null;
$lighthouseScore = is_numeric($lighthouseScore) ? intval($lighthouseScore) : null;
$lighthouseOps = parse_json_field($doc['lighthouse_sugestoes'] ?? '');

$questions = parse_json_field($doc['questions'] ?? '');
$roadmapAI = parse_json_field($doc['roadmap_ai'] ?? '');

$mScore = maturity_to_score($maturidade);

/* ---------- Fallback Lighthouse pela entidade LH (se vazio) ---------- */
if (!is_numeric($lighthouseScore) || !is_array($lighthouseOps) || !count($lighthouseOps)) {
  $scanIdDoc = trim((string)($doc['scan_id'] ?? ($doc['scanId'] ?? '')));
  [$sc, $ops] = try_fetch_lighthouse_from_LH($scanIdDoc);
  if (is_numeric($sc))   $lighthouseScore = intval($sc);
  if (is_array($ops) && count($ops)) $lighthouseOps = $ops;
}

/* ---------- html ---------- */
?><!doctype html>
<html lang="pt-br">
<head>
  <meta charset="utf-8" />
  <title>Check Up | Prepare seu site para Black Friday | Grupo W</title>
  <meta name="robots" content="noindex, nofollow">
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    :root{ --bg:#0b0d10; --card:#12161b; --muted:#8ea0b4; --text:#e6eef7; --bar:#1f2731; --brand:#6aa6ff; }
    *{box-sizing:border-box}
    body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 ui-sans-serif,system-ui,Segoe UI,Roboto}
    header{padding:20px 24px;border-bottom:1px solid var(--bar);position:sticky;top:0;background:#0b0d10cc;backdrop-filter:saturate(1.2) blur(2px)}
    h1{margin:0 0 6px;font-size:20px}
    .muted{color:var(--muted)}
    main{padding:24px;display:grid;gap:20px}
    .grid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
    .card{background:var(--card);border:1px solid var(--bar);border-radius:14px;padding:16px}
    .flex{display:flex;gap:16px;align-items:center}
    .gauge{min-width:120px}
    .kv{display:grid;grid-template-columns:120px 1fr;gap:6px;align-items:start}
    .pill{display:inline-block;padding:2px 8px;border-radius:999px;background:#1a2330;border:1px solid #293241;color:#cfe3ff;font-size:12px}
    .h3{font-size:16px;font-weight:700;margin:0 0 6px}
    .small{font-size:12px}
    ul{margin:8px 0 0 18px}
    .opps li span{color:var(--muted)}
    .gridCol{display:grid;gap:12px}
    .qa-item{border-top:1px dashed #243042;padding-top:10px;margin-top:10px}
    a{color:#cfe3ff;text-decoration:none}
  </style>
</head>
<body>
  <header>
    <h1>Lead — Black Friday</h1>
    <div class="muted">
      ID: <?= esc($docId) ?> • Email:
      <?php if ($email): ?><a href="mailto:<?= esc($email) ?>"><?= esc($email) ?></a><?php else: ?>—<?php endif; ?>
      • Site:
      <?php if ($site): ?><a href="<?= esc($site) ?>" target="_blank" rel="noopener"><?= esc($site) ?></a><?php else: ?>—<?php endif; ?>
    </div>
  </header>

  <main>
    <div class="grid2">
      <!-- Maturidade -->
      <section class="card">
        <div class="h3">Maturidade</div>
        <div class="flex">
          <div class="gauge"><?= gauge_svg($mScore, 'Nível') ?></div>
          <div class="gridCol">
            <div><span class="pill">Nível</span> <?= esc($maturidade ?: '—') ?></div>
            <div>
              <div class="muted small">Resumo</div>
              <div><?= nl2br(esc($explanation ?: '—')) ?></div>
            </div>
          </div>
        </div>
      </section>

      <!-- Lighthouse -->
      <section class="card">
        <div class="h3">Lighthouse (PageSpeed)</div>
        <div class="flex">
          <div class="gauge">
            <?php
              if (is_numeric($lighthouseScore)) {
                echo gauge_svg(intval($lighthouseScore), 'Performance');
              } else {
                echo '<div class="muted">Sem resultado salvo.</div>';
              }
            ?>
          </div>
          <div>
            <div class="muted small">Oportunidades principais</div>
            <?php
              $lhSlides = lh_extract_slides($lighthouseOps);
            ?>
            <?php if (is_array($lhSlides) && count($lhSlides)): ?>
              <ul class="opps">
                <?php foreach (array_slice($lhSlides, 0, 8) as $sl): ?>
                  <li>
                    <div><b><?= esc($sl['headline'] ?? '') ?></b></div>
                    <?php if (!empty($sl['conteudo'])): ?>
                      <div class="muted small"><?= esc($sl['conteudo']) ?></div>
                    <?php endif; ?>
                  </li>
                <?php endforeach; ?>
              </ul>
            <?php elseif (is_array($lighthouseOps) && count($lighthouseOps)): ?>
              <ul class="opps">
                <?php foreach (array_slice($lighthouseOps, 0, 8) as $op):
                  $t = $op['title'] ?? ($op['id'] ?? '');
                  $sv = isset($op['savingsMs']) ? (' (~'.round(floatval($op['savingsMs'])).'ms)') : '';
                ?>
                  <li><?= esc($t.$sv) ?></li>
                <?php endforeach; ?>
              </ul>
            <?php else: ?>
              <div class="muted">—</div>
            <?php endif; ?>
          </div>
        </div>
      </section>
    </div>

    <div class="grid2">
      <!-- Perguntas & respostas -->
      <section class="card">
        <div class="h3">Perguntas & respostas (quiz)</div>
        <?php if (is_array($questions) && count($questions)): ?>
          <?php foreach ($questions as $qa): ?>
            <div class="qa-item">
              <div class="muted small">Pergunta</div>
              <div><?= esc($qa['pergunta'] ?? $qa['question'] ?? '') ?></div>
              <div class="muted small" style="margin-top:6px">Resposta</div>
              <div><b><?= esc($qa['resposta'] ?? $qa['answer'] ?? '') ?></b></div>
            </div>
          <?php endforeach; ?>
        <?php else: ?>
          <div class="muted">Sem respostas salvas.</div>
        <?php endif; ?>
      </section>

      <!-- Estratégias IA -->
      <section class="card">
        <div class="h3">Estratégias sugeridas (IA)</div>
        <?php if (is_array($roadmapAI) && count($roadmapAI)): ?>
          <?php foreach ($roadmapAI as $i => $it): ?>
            <div class="qa-item">
              <div class="h3" style="font-size:15px"><?= esc($it['titulo'] ?? ('Item '.($i+1))) ?></div>
              <?php if (!empty($it['area'])): ?>
                <div class="muted small">Área: <b><?= esc($it['area']) ?></b></div>
              <?php endif; ?>
              <?php if (!empty($it['kpi'])): ?>
                <div class="muted small">KPI: <b><?= esc($it['kpi']) ?></b></div>
              <?php endif; ?>
              <?php if (!empty($it['impacto']) || !empty($it['prazo'])): ?>
                <div class="muted small">
                  <?php if (!empty($it['impacto'])): ?>Impacto: <b><?= esc($it['impacto']) ?></b><?php endif; ?>
                  <?php if (!empty($it['prazo'])): ?> • Prazo: <b><?= esc($it['prazo']) ?></b><?php endif; ?>
                </div>
              <?php endif; ?>
              <?php if (!empty($it['por_que'])): ?>
                <div class="muted small" style="margin-top:6px">Por quê</div>
                <div><?= esc($it['por_que']) ?></div>
              <?php endif; ?>
              <?php if (!empty($it['acao_estrategica'])): ?>
                <div class="muted small" style="margin-top:6px">Ação estratégica</div>
                <div><?= esc($it['acao_estrategica']) ?></div>
              <?php endif; ?>
            </div>
          <?php endforeach; ?>
        <?php else: ?>
          <div class="muted">Sem estratégias salvas.</div>
        <?php endif; ?>
      </section>
    </div>
  </main>
</body>
</html>