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/storage/5/78/dd/wicomm2/public_html/blackfriday/api/start_scan.php
<?php
// ===== start_scan.php =====
// Executa PSI/Lighthouse, devolve resumo e persiste payload inteiro em VTEX MD (LH)

require_once __DIR__ . '/../config/config.php'; // usa as CONSTANTES informadas pelo usuário

@date_default_timezone_set(defined('APP_TIMEZONE') ? APP_TIMEZONE : 'America/Sao_Paulo');
if (!is_dir(STORAGE_DIR))
  @mkdir(STORAGE_DIR, 0775, true);

// ---------- Helpers ----------
function json_out($data, $code = 200)
{
  if (!headers_sent()) {
    http_response_code($code);
    header('Content-Type: application/json; charset=utf-8');
  }
  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  exit;
}
function require_post_json()
{
  $raw = file_get_contents('php://input');
  $j = json_decode($raw, true);
  if (!is_array($j))
    json_out(['ok' => false, 'error' => 'Invalid JSON body'], 400);
  return $j;
}
function http_get($url, $timeout_ms = 15000, $connect_ms = 6000)
{
  $ch = curl_init($url);
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_CONNECTTIMEOUT_MS => $connect_ms,
    CURLOPT_TIMEOUT_MS => $timeout_ms,
    CURLOPT_USERAGENT => 'FridayUpBot/1.0',
    CURLOPT_ENCODING => '',
    CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
  ]);
  $body = curl_exec($ch);
  $err = curl_error($ch);
  $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);
  return [$code, $body, $err];
}
function clamp100($v)
{
  $n = is_numeric($v) ? floatval($v) : 0;
  return max(0, min(100, $n));
}

// ---------- VTEX Master Data (LH) ----------
function vtex_md_upsert_LH($scanId, $payloadJson)
{
  if (!defined('VTEX_ACCOUNT') || !VTEX_ACCOUNT)
    return [false, 'missing account'];
  $host = 'https://' . VTEX_ACCOUNT . '.' . (defined('VTEX_ENVIRONMENT') ? VTEX_ENVIRONMENT : 'vtexcommercestable') . '.com.br';
  $url = $host . '/api/dataentities/LH/documents';

  $doc = ['scanId' => $scanId, 'payload' => $payloadJson];

  $ch = curl_init($url);
  curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
      'Content-Type: application/json',
      'X-VTEX-API-AppKey: ' . VTEX_APPKEY,
      'X-VTEX-API-AppToken: ' . VTEX_APPTOKEN,
    ],
    CURLOPT_POSTFIELDS => json_encode($doc, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
    CURLOPT_TIMEOUT => 15,
    CURLOPT_CONNECTTIMEOUT => 8,
  ]);
  $body = curl_exec($ch);
  $err = curl_error($ch);
  $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  if ($code >= 400 || !$body)
    return [false, $body ?: $err ?: ("HTTP " . $code)];
  return [true, json_decode($body, true)];
}

// ---------- PSI / Lighthouse ----------
function run_psi_once($url, $strategy)
{
  $u = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=' . rawurlencode($url) .
    '&locale=pt_BR' .
    '&strategy=' . rawurlencode($strategy) .
    '&key=' . rawurlencode(PSI_API_KEY);

  [$code, $body, $err] = http_get($u, PSI_TOTAL_MS, PSI_CONNECT_MS);
  if ($code >= 400 || !$body) {
    return [null, $body ?: ($err ?: ('HTTP ' . $code))];
  }
  $json = json_decode($body, true);
  if (!is_array($json) || !isset($json['lighthouseResult'])) {
    $msg = $json['error']['message'] ?? 'Sem lighthouseResult';
    return [null, $msg];
  }
  return [$json, null];
}

function summarize_lighthouse($json)
{
  $lh = $json['lighthouseResult'] ?? [];
  $aud = $lh['audits'] ?? [];
  $cats = $lh['categories'] ?? [];

  $score = (int) round((($cats['performance']['score'] ?? 0) * 100));
  $disp = function ($id) use ($aud) {
    return $aud[$id]['displayValue'] ?? ''; };

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

  return [
    'score' => clamp100($score),
    'metrics' => [
      'lcp' => $disp('largest-contentful-paint'),
      'tbt' => $disp('total-blocking-time'),
      'cls' => $disp('cumulative-layout-shift'),
      'interactive' => $disp('interactive'),
    ],
    'opportunidades' => $opps
  ];
}

// ---------- Main ----------
try {
  $data = require_post_json();
  $site = trim((string) ($data['site'] ?? ''));
  $strategy = trim((string) ($data['strategy'] ?? 'mobile'));
  if ($strategy === '')
    $strategy = 'mobile';
  $strategy = strtolower($strategy) === 'desktop' ? 'desktop' : 'mobile';

  if ($site === '' || !defined('PSI_API_KEY') || PSI_API_KEY === '') {
    json_out(['ok' => false, 'error' => 'site e/ou PSI_API_KEY ausentes'], 400);
  }

  // Re-tentativas leves
  $json = null;
  $err = null;
  for ($i = 0; $i < (defined('PSI_RETRIES') ? PSI_RETRIES : 3); $i++) {
    [$json, $err] = run_psi_once($site, $strategy);
    if ($json)
      break;
    usleep(200000); // 200ms
  }
  if (!$json)
    json_out(['ok' => false, 'error' => $err ?: 'Falha PSI'], 502);

  // Gera um scan_id e salva storage local
  $scanId = bin2hex(random_bytes(8));
  $storagePath = rtrim(STORAGE_DIR, '/') . '/scan_' . $scanId . '.json';
  @file_put_contents($storagePath, json_encode($json, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

  // Salva no Master Data (LH): scanId + payload (JSON puro)
  [$okSave, $saveRes] = vtex_md_upsert_LH($scanId, json_encode($json, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
  // (não quebra fluxo se falhar)

  // Resumo + cookie (fallback)
  $summary = summarize_lighthouse($json);
  $summaryCookie = json_encode($summary, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  // Cookie por 30 min para o mesmo host
  setcookie('fridayup_lighthouse', $summaryCookie, [
    'expires' => time() + 1800,
    'path' => '/',
    'secure' => false,
    'httponly' => false,
    'samesite' => 'Lax'
  ]);

  json_out([
    'ok' => true,
    'scan_id' => $scanId,
    'url' => $site,
    'strategy' => $strategy,
    'score' => $summary['score'],
    'metrics' => $summary['metrics'],
    'oportunidades' => $summary['opportunidades'],
    'md_saved' => $okSave ? true : false
  ]);

} catch (Throwable $e) {
  json_out(['ok' => false, 'error' => $e->getMessage()], 500);
}