1/** 2 * Portão: config pública (API ou JSON estático). Validação da senha só no navegador (nada de senha em claro ao servidor). 3 * Sessão: localStorage com horário de inÃcio. 4 */ 5(function () { 6 "use strict"; 7 8 var STORAGE_KEY = "nr_site_gate_v1"; 9 var SESSION_MS = 30 * 60 * 1000; 10 var SESSION_GAME_MS = 2 * 60 * 1000; 11 var SESSION_RIBBON_ID = "nr-site-gate-session"; 12 var CONFIG_URLS = ["/api/site-admin/public/site-gate-public.json", "assets/data/site-gate-public.json"]; 13 var GAME_STATS_GET = "/api/site-admin/public/site-gate-game-stats.json"; 14 var GAME_STATS_POST = "/api/site-admin/public/site-gate-game-stats"; 15 /** Graus de tolerância no alinhamento do logo (desafio âdifÃcilâ). */ 16 var GAME_ANGLE_TOL = 3; 17 18 var JOKES = [ 19 "Sem a senha certa não rola.", 20 "Só entra quem a equipe liberou.", 21 "Fibra é sério. Senha também.", 22 "Chute óbvio não cola.", 23 "Rafael treina pesado e o site também pede senha.", 24 ]; 25 26 /** Mensagem principal ao errar a senha (sem detalhes técnicos). */ 27 var WRONG_PRIMARY = [ 28 "Senha incorreta. Tente de novo.", 29 "Não confere. Revise e tente outra vez.", 30 "Ainda não. Conferência não passou.", 31 "Não foi dessa vez. Tente novamente.", 32 "Acesso negado para essa tentativa.", 33 ]; 34 35 var WRONG_EXTRA = [ 36 "Fibra no trabalho, caractere certo no teclado.", 37 "Creatina no copo, caps lock no lugar certo.", 38 "Agacha o ego, sobe a atenção nas letras.", 39 "Terra pesada, senha leve (na cabeça).", 40 "Char certo antes do Enter.", 41 "Relaxa o ombro e revisa o que digitou.", 42 "Teimosia boa; senha ainda não.", 43 "Quem tem a credencial certa é a equipe.", 44 "Respira, tenta de novo.", 45 ]; 46 47 /** Terminal decorativo: sem nomes de produtos, portas ou stack. */ 48 var TERMINAL_FAIL_SNIPPETS = [ 49 "$ verificar-acesso\n> negado: tente novamente", 50 "$ checagem\n> não confere", 51 "$ porta-virtual\n> fechada para esta tentativa", 52 "$ fila-de-entrada\n> aguardando credencial válida", 53 "$ revisão\n> tentativa não aceita", 54 "$ sinal\n> sem autorização neste momento", 55 ]; 56 57 var KONAMI = "38,38,40,40,37,39,37,39,66,65"; 58 var konamiBuf = []; 59 60 /** Sem portão: só prévia CMS pedida (não todo o host preview) + caminhos /api/site-admin */ 61 function shouldSkipSiteGate() { 62 try { 63 var pathname = window.location.pathname || ""; 64 if (pathname.indexOf("/api/site-admin") !== -1) { 65 return true; 66 } 67 var file = ((pathname.split("/").pop() || "").split("?")[0] || "").toLowerCase(); 68 if ( 69 file === "preview.html" || 70 file === "preview-fatura.html" || 71 file === "preview-cookies.html" || 72 file === "preview-trabalhe.html" || 73 file === "preview-empresa-sustentavel.html" 74 ) { 75 return true; 76 } 77 } catch (e) {} 78 return false; 79 } 80 81 function pick(arr) { 82 return arr[Math.floor(Math.random() * arr.length)]; 83 } 84 85 function sessionDurationMs(j) { 86 if (!j || typeof j.t !== "number") { 87 return SESSION_MS; 88 } 89 return j.mode === "game" ? SESSION_GAME_MS : SESSION_MS; 90 } 91 92 function getSessionPayload() { 93 try { 94 var raw = localStorage.getItem(STORAGE_KEY); 95 if (!raw || raw === "1") { 96 return null; 97 } 98 var j = JSON.parse(raw); 99 if (!j || typeof j.t !== "number") { 100 return null; 101 } 102 return j; 103 } catch (e) { 104 return null; 105 } 106 } 107 108 function gateSessionOk() { 109 try { 110 var raw = localStorage.getItem(STORAGE_KEY); 111 if (!raw) return false; 112 if (raw === "1") { 113 localStorage.removeItem(STORAGE_KEY); 114 return false; 115 } 116 var j = JSON.parse(raw); 117 if (!j || typeof j.t !== "number") return false; 118 if (Date.now() - j.t > sessionDurationMs(j)) { 119 localStorage.removeItem(STORAGE_KEY); 120 return false; 121 } 122 return true; 123 } catch (e) { 124 try { 125 localStorage.removeItem(STORAGE_KEY); 126 } catch (x) {} 127 return false; 128 } 129 } 130 131 function loadPublicConfig() { 132 for (var i = 0; i < CONFIG_URLS.length; i++) {
133 try { 134 var xhr = new XMLHttpRequest(); 135 var u = CONFIG_URLS[i]; 136 xhr.open("GET", u + "?t=" + String(Date.now()), false); 137 xhr.send(null); 138 var st = xhr.status; 139 if (st !== 200 && st !== 0) { 140 continue; 141 } 142 var j = JSON.parse(xhr.responseText); 143 if (j && typeof j === "object") { 144 return j; 145 } 146 } catch (e) { 147 /* próxima URL */ 148 } 149 } 150 return null; 151 } 152 153 function gateEnabledFromConfig(cfg) { 154 if (!cfg || typeof cfg !== "object") { 155 return false; 156 } 157 var v = cfg.gate_enabled; 158 return v === true || v === 1 || v === "1" || v === "true"; 159 } 160 161 function hasPasswordFromConfig(cfg) { 162 if (!cfg || typeof cfg !== "object") { 163 return false; 164 } 165 if (cfg.has_password === true || cfg.has_password === 1 || cfg.has_password === "1") { 166 return true; 167 } 168 // Compatibilidade com deploys antigos que ainda emitiam o hash no JSON público 169 var legacyHex = String(cfg.password_sha256_hex || "").trim(); 170 return /^[a-f0-9]{64}$/i.test(legacyHex); 171 } 172 173 function openAccess() { 174 document.documentElement.classList.remove("site-gate-pending"); 175 document.documentElement.classList.add("site-gate-booted"); 176 } 177 178 /** Portão desligado no painel: limpa sessão do navegador e remove faixa (contador / IP). */ 179 function clearGateSessionAndRibbon() { 180 try { 181 localStorage.removeItem(STORAGE_KEY); 182 } catch (e) {} 183 try { 184 document.documentElement.classList.remove("has-site-gate-session"); 185 } catch (e2) {} 186 var rib = document.getElementById(SESSION_RIBBON_ID); 187 if (rib) { 188 try { 189 rib.remove(); 190 } catch (e3) {} 191 } 192 } 193 194 function sessionDeadlineMs() { 195 try { 196 var raw = localStorage.getItem(STORAGE_KEY); 197 if (!raw || raw === "1") return 0; 198 var j = JSON.parse(raw); 199 if (!j || typeof j.t !== "number") return 0; 200 return j.t + sessionDurationMs(j); 201 } catch (e) { 202 return 0; 203 } 204 } 205 206 function formatRemaining(ms) { 207 if (ms <= 0) return "00:00"; 208 var sec = Math.ceil(ms / 1000); 209 var m = Math.floor(sec / 60); 210 var s = sec % 60; 211 return (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s; 212 } 213 214 function installSessionRibbonIfNeeded() { 215 if (shouldSkipSiteGate()) return; 216 if (!gateSessionOk()) return; 217 if (document.getElementById(SESSION_RIBBON_ID)) return; 218 var cfgRibbon = loadPublicConfig(); 219 if (!gateEnabledFromConfig(cfgRibbon) || !hasPasswordFromConfig(cfgRibbon)) { 220 return; 221 } 222 223 var sessRibbon = getSessionPayload(); 224 var ribbonGame = sessRibbon && sessRibbon.mode === "game";
225 var ribbonTotalMs = ribbonGame ? SESSION_GAME_MS : SESSION_MS; 226 var kickerTxt = ribbonGame 227 ? "Acesso por desafio · janela menor" 228 : "Sessão liberada · ainda rola"; 229 var unitTxt = ribbonGame 230 ? "Expira em breve. O portão volta." 231 : "Até pedir senha de novo"; 232 233 var deadline = sessionDeadlineMs(); 234 if (deadline <= 0) return; 235 236 document.documentElement.classList.add("has-site-gate-session"); 237 238 var bar = document.createElement("div"); 239 bar.id = SESSION_RIBBON_ID; 240 bar.className = "site-gate-session" + (ribbonGame ? " site-gate-session--turbo" : ""); 241 bar.setAttribute("role", "complementary"); 242 bar.setAttribute("aria-label", "Tempo restante da sessão do portão NR Conexões"); 243 bar.innerHTML = 244 '<div class="site-gate-session__card">' + 245 '<div class="site-gate-session__glow" aria-hidden="true"></div>' + 246 '<div class="site-gate-session__row">' + 247 '<img class="site-gate-session__logo" src="assets/img/logo-branco.png" alt="" width="120" height="40" decoding="async" />' + 248 '<div class="site-gate-session__meta">' + 249 '<span class="site-gate-session__kicker">' + 250 kickerTxt + 251 "</span>" + 252 '<div class="site-gate-session__time-row">' + 253 '<span class="site-gate-session__time" id="site-gate-session-remaining">30:00</span>' + 254 '<span class="site-gate-session__unit">' + 255 unitTxt + 256 "</span>" + 257 "</div>" + 258 '<div class="site-gate-session__track" aria-hidden="true"><div class="site-gate-session__fill" id="site-gate-session-fill"></div></div>' + 259 "</div></div>" + 260 '<p class="site-gate-session__ip">' + 261 'Seu IP: <strong id="site-gate-session-ip">â¦</strong> · ' + 262 "Este acesso é monitorado e registrado.</p>" + 263 "</div>"; 264 265 document.body.appendChild(bar); 266 267 var elTime = document.getElementById("site-gate-session-remaining"); 268 var elFill = document.getElementById("site-gate-session-fill"); 269 var elIp = document.getElementById("site-gate-session-ip"); 270 271 function tickRibbon() { 272 var left = deadline - Date.now(); 273 if (left <= 0) { 274 try { 275 localStorage.removeItem(STORAGE_KEY); 276 } catch (e) {} 277 document.documentElement.classList.remove("has-site-gate-session"); 278 bar.remove(); 279 window.location.reload(); 280 return; 281 } 282 if (elTime) elTime.textContent = formatRemaining(left); 283 if (elFill) { 284 elFill.style.width = Math.min(100, Math.max(0, (left / ribbonTotalMs) * 100)) + "%"; 285 } 286 } 287 tickRibbon(); 288 var iv = window.setInterval(tickRibbon, 1000); 289 290 window.addEventListener( 291 "beforeunload", 292 function () { 293 window.clearInterval(iv); 294 }, 295 { once: true } 296 ); 297 298 if (elIp) { 299 function setIpFromJson(j) { 300 if (j && typeof j.ip === "string" && j.ip.trim() !== "") { 301 elIp.textContent = j.ip.trim(); 302 return true; 303 } 304 return false; 305 } 306 fetch("/api/site-admin/public/client-ip.json?t=" + String(Date.now()), { 307 cache: "no-store", 308 credentials: "same-origin", 309 }) 310 .then(function (r) { 311 if (r.status === 429) return null; 312 return r.ok ? r.json() : null; 313 }) 314 .then(function (j) { 315 if (setIpFromJson(j)) return null; 316 return fetch("https://api.ipify.org?format=json", { 317 cache: "no-store", 318 credentials: "omit", 319 }); 320 }) 321 .then(function (r) { 322 if (!r || typeof r.json !== "function") return null; 323 return r.ok ? r.json() : null; 324 }) 325 .then(function (j) { 326 if (j && setIpFromJson(j)) return; 327 if (elIp.textContent === "â¦") elIp.textContent = "-"; 328 }) 329 .catch(function () { 330 if (elIp.textContent === "â¦") elIp.textContent = "-"; 331 }); 332 } 333 } 334 335 function unlock() { 336 try { 337 localStorage.setItem(STORAGE_KEY, JSON.stringify({ t: Date.now() })); 338 } catch (e) {} 339 openAccess(); 340 var g = document.getElementById("nr-site-gate"); 341 if (g) { 342 g.remove(); 343 } 344 installSessionRibbonIfNeeded(); 345 } 346 347 function unlockGame() { 348 try { 349 localStorage.setItem( 350 STORAGE_KEY, 351 JSON.stringify({ t: Date.now(), mode: "game" }) 352 ); 353 } catch (e) {} 354 openAccess(); 355 var g2 = document.getElementById("nr-site-gate"); 356 if (g2) { 357 g2.remove(); 358 } 359 installSessionRibbonIfNeeded(); 360 } 361 362 function angularDiffDeg(a, b) { 363 var d = Math.abs(a - b) % 360; 364 return Math.min(d, 360 - d); 365 } 366 367 function setArenaStats(elOk, elFail, ok, fail) { 368 if (elOk) elOk.textContent = String(ok); 369 if (elFail) elFail.textContent = String(fail); 370 } 371 372 function fetchArenaStats(elOk, elFail) { 373 try { 374 fetch(GAME_STATS_GET + "?t=" + String(Date.now()), { 375 cache: "no-store", 376 credentials: "same-origin", 377 }) 378 .then(function (r) { 379 if (r.status === 429) return null; 380 return r.ok ? r.json() : null; 381 }) 382 .then(function (j) { 383 if ( 384 j && 385 typeof j.ok === "number" && 386 typeof j.fail === "number" 387 ) { 388 setArenaStats(elOk, elFail, j.ok, j.fail); 389 } 390 }) 391 .catch(function () {}); 392 } catch (e) {} 393 } 394 395 function postArenaResult(which, elOk, elFail) { 396 return fetch(GAME_STATS_POST, { 397 method: "POST", 398 headers: { "Content-Type": "application/json" }, 399 cache: "no-store", 400 credentials: "same-origin", 401 body: JSON.stringify({ result: which }), 402 }) 403 .then(function (r) { 404 if (r.status === 429 || r.status === 422) return null; 405 return r.ok ? r.json() : null; 406 }) 407 .then(function (j) { 408 if ( 409 j && 410 j.ok === true && 411 j.stats && 412 typeof j.stats.ok === "number" && 413 typeof j.stats.fail === "number" 414 ) { 415 setArenaStats(elOk, elFail, j.stats.ok, j.stats.fail); 416 } 417 }) 418 .catch(function () {}); 419 } 420 421 function initSiteGateMiniGame(wrap) { 422 var elOk = wrap.querySelector("#site-gate-st-ok"); 423 var elFail = wrap.querySelector("#site-gate-st-fail"); 424 fetchArenaStats(elOk, elFail); 425 426 var btnToggle = wrap.querySelector("#site-gate-game-toggle"); 427 var panel = wrap.querySelector("#site-gate-game-panel"); 428 var targetImg = wrap.querySelector("#site-gate-game-target-img"); 429 var userImg = wrap.querySelector("#site-gate-game-user-img"); 430 var spin = wrap.querySelector("#site-gate-game-spin"); 431 var degRead = wrap.querySelector("#site-gate-game-deg-readout"); 432 var btnCheck = wrap.querySelector("#site-gate-game-check"); 433 var toast = wrap.querySelector("#site-gate-game-toast"); 434 if (!btnToggle || !panel || !targetImg || !userImg || !spin || !btnCheck) { 435 return; 436 } 437 438 var targetDeg = 0; 439 440 function applyRot(el, deg) { 441 el.style.transform = "rotate(" + String(deg) + "deg)"; 442 } 443 444 function prepRound() { 445 targetDeg = Math.floor(Math.random() * 360); 446 applyRot(targetImg, targetDeg); 447 spin.value = "0"; 448 applyRot(userImg, 0); 449 if (degRead) degRead.textContent = "0°"; 450 if (toast) toast.textContent = ""; 451 } 452 453 btnToggle.addEventListener("click", function () { 454 var willOpen = panel.hidden; 455 panel.hidden = !willOpen; 456 btnToggle.setAttribute("aria-expanded", willOpen ? "true" : "false"); 457 if (willOpen) { 458 prepRound(); 459 } 460 }); 461 462 spin.addEventListener("input", function () { 463 var v = Number(spin.value) || 0; 464 applyRot(userImg, v); 465 if (degRead) degRead.textContent = String(v) + "°"; 466 }); 467 468 btnCheck.addEventListener("click", function () { 469 var userDeg = Number(spin.value) || 0; 470 if (angularDiffDeg(userDeg, targetDeg) <= GAME_ANGLE_TOL) { 471 btnCheck.disabled = true; 472 if (toast) toast.textContent = "Encaixou! Liberando acessoâ¦"; 473 postArenaResult("ok", elOk, elFail).finally(function () { 474 window.setTimeout(unlockGame, 380); 475 }); 476 } else { 477 var miss = angularDiffDeg(userDeg, targetDeg); 478 if (toast) { 479 toast.textContent = 480 "Quase⦠falta uns " + String(Math.max(1, Math.round(miss))) + "° de precisão.";
481 } 482 postArenaResult("fail", elOk, elFail); 483 panel.classList.remove("site-gate__game-panel--nudge"); 484 void panel.offsetWidth; 485 panel.classList.add("site-gate__game-panel--nudge"); 486 window.setTimeout(function () { 487 panel.classList.remove("site-gate__game-panel--nudge"); 488 }, 420); 489 } 490 }); 491 } 492 493 function startGateCounters() { 494 var reduceMotion = false; 495 try { 496 reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; 497 } catch (e) {} 498 var t0 = Date.now(); 499 var elElapsed = document.getElementById("site-gate-ct-elapsed"); 500 var elHype = document.getElementById("site-gate-ct-hype"); 501 var mental = 0; 502 function tickElapsed() { 503 var sec = Math.floor((Date.now() - t0) / 1000); 504 var m = Math.floor(sec / 60); 505 var s = sec % 60; 506 if (elElapsed) { 507 elElapsed.textContent = (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s; 508 } 509 } 510 tickElapsed(); 511 if (reduceMotion) { 512 setInterval(tickElapsed, 10000); 513 } else { 514 setInterval(tickElapsed, 400); 515 } 516 if (elHype) { 517 elHype.textContent = "0"; 518 } 519 if (!reduceMotion) { 520 setInterval(function () { 521 mental += 1 + Math.floor(Math.random() * 3); 522 if (elHype) { 523 elHype.textContent = String(mental); 524 } 525 }, 1400); 526 } 527 } 528 529 function bootLocked() { 530 var joke = pick(JOKES); 531 var termLine = "$ liberar-site.sh\n> esperando senha da equipe..."; 532 533 var wrap = document.createElement("div"); 534 wrap.id = "nr-site-gate"; 535 wrap.setAttribute("role", "dialog"); 536 wrap.setAttribute("aria-modal", "true"); 537 wrap.setAttribute("aria-labelledby", "site-gate-title"); 538 wrap.innerHTML = 539 '<div class="site-gate__panel site-gate__panel--wide">' + 540 '<div class="site-gate__hero">' + 541 '<div class="site-gate__hero-glow" aria-hidden="true"></div>' + 542 '<div class="site-gate__brand">' + 543 '<img class="site-gate__brand-logo" src="assets/img/logo-branco.png" alt="" width="200" height="67" decoding="async" loading="eager" />' + 544 '<div class="site-gate__brand-orbit" aria-hidden="true"></div>' + 545 "</div></div>" + 546 '<div class="site-gate__layout">' + 547 '<div class="site-gate__col site-gate__col--form">' + 548 '<p class="site-gate__pretitle">Acesso NR</p>' + 549 '<h1 class="site-gate__title" id="site-gate-title">Antes de entrar, coloca a senha</h1>' + 550 '<p class="site-gate__joke"></p>' + 551 '<div class="site-gate__terminal" aria-hidden="true"></div>' + 552 '<form class="site-gate__form" autocomplete="off">' + 553 '<label class="site-gate__label" for="site-gate-pass">Senha</label>' + 554 '<input class="site-gate__input" id="site-gate-pass" name="nr_gate_p" type="password" ' + 555 'placeholder="Não é 123456" required maxlength="200" />' + 556 '<button class="site-gate__btn" type="submit">Entrar no site</button>' + 557 '<p class="site-gate__msg" role="status" aria-live="polite"></p>' + 558 '<p class="site-gate__msg-extra" aria-live="polite" aria-hidden="true" hidden></p>' + 559 "</form>" + 560 '<div class="site-gate__game-arena">' + 561 '<button type="button" class="site-gate__game-trigger" id="site-gate-game-toggle" aria-expanded="false" aria-controls="site-gate-game-panel">' + 562 '<span class="site-gate__game-trigger-icon" aria-hidden="true">â</span>' + 563 '<span class="site-gate__game-trigger-label">Sem credencial? Use o <strong>desafio opcional</strong> do logo para ganhar acesso curto ao alinhar o ângulo.</span>' + 564 "</button>" + 565 '<div class="site-gate__game-panel" id="site-gate-game-panel" hidden>' + 566 '<div class="site-gate__arena-board site-gate__arena-board--panel" role="status" aria-live="polite" aria-atomic="true">' + 567 '<span class="site-gate__arena-chip site-gate__arena-chip--hit">' + 568 '<span class="site-gate__arena-glyph" aria-hidden="true">â</span>' + 569 '<span id="site-gate-st-ok">0</span></span>' + 570 '<span class="site-gate__arena-chip site-gate__arena-chip--miss">' + 571 '<span class="site-gate__arena-glyph" aria-hidden="true">â</span>' + 572 '<span id="site-gate-st-fail">0</span></span>' + 573 "</div>" + 574 '<div class="site-gate__game-duel">' + 575 '<div class="site-gate__game-pillar">' + 576 '<span class="site-gate__game-pillar-cap">Alvo fantasma</span>' + 577 '<div class="site-gate__game-stage">' + 578 '<img class="site-gate__game-img site-gate__game-img--target" src="assets/img/logo-branco.png" alt="" width="130" height="44" id="site-gate-game-target-img" />' + 579 "</div></div>" + 580 '<div class="site-gate__game-pillar">' + 581 '<span class="site-gate__game-pillar-cap">Teu giro</span>' + 582 '<div class="site-gate__game-stage">' + 583 '<img class="site-gate__game-img" src="assets/img/logo-branco.png" alt="" width="130" height="44" id="site-gate-game-user-img" />' + 584 "</div></div></div>" + 585 '<div class="site-gate__game-slider-row">' + 586 '<label class="site-gate__game-slider-lbl" for="site-gate-game-spin">Giro (°)</label>' + 587 '<input type="range" id="site-gate-game-spin" class="site-gate__game-slider" min="0" max="359" value="
5870" />' + 588 '<span class="site-gate__game-deg" id="site-gate-game-deg-readout" aria-hidden="true">0°</span>' + 589 "</div>" + 590 '<button type="button" class="site-gate__game-fire" id="site-gate-game-check">Conferir alinhamento</button>' + 591 '<p class="site-gate__game-toast" id="site-gate-game-toast" role="status" aria-live="polite"></p>' + 592 "</div></div>" + 593 '<p class="site-gate__foot">Site em construção e validação. Só quem tem a senha vê o resto. Senha errada não queima cabo óptico. Errou? Tenta outra vez, de boa.</p>' + 594 "</div>" + 595 '<aside class="site-gate__col site-gate__col--show" aria-label="Informações">' + 596 '<p class="site-gate__dev-kicker">NR Conexões</p>' + 597 '<p class="site-gate__dev-name">Acesso restrito.</p>' + 598 '<div class="site-gate__counter-grid" aria-live="polite">' + 599 '<div class="site-gate__counter-card">' + 600 '<span class="site-gate__counter-label">Tempo parado nesta tela</span>' + 601 '<span class="site-gate__counter-val" id="site-gate-ct-elapsed">00:00</span>' + 602 '<span class="site-gate__counter-sub">Quanto mais sobe, mais você tá curioso.</span>' + 603 "</div>" + 604 '<div class="site-gate__counter-card">' + 605 '<span class="site-gate__counter-label">Contador bobo de "ganho"</span>' + 606 '<span class="site-gate__counter-val" id="site-gate-ct-hype">0</span>' + 607 '<span class="site-gate__counter-sub">Brincadeira, não é ciência.</span>' + 608 "</div></div>" + 609 '<div class="site-gate__embed-section">' + 610 '<p class="site-gate__embed-cap">Três vÃdeos curtos antes da senha</p>' + 611 '<div class="site-gate__embed-row">' + 612 '<div class="site-gate__embed-wrap">' + 613 '<iframe class="site-gate__iframe" src="https://www.youtube.com/embed/M7cPMJYEXbQ?autoplay=1&mute=1&playsinline=1&rel=0" ' + 614 'title="Rafael Vieira, treino, vÃdeo 1" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>' + 615 "</div>" + 616 '<div class="site-gate__embed-wrap">' + 617 '<iframe class="site-gate__iframe" src="https://www.youtube.com/embed/Kzr9C4UoCoU?autoplay=1&mute=1&playsinline=1&rel=0" ' + 618 'title="Rafael Vieira, treino, vÃdeo 2" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>' + 619 "</div>" + 620 '<div class="site-gate__embed-wrap">' + 621 '<iframe class="site-gate__iframe" src="https://www.youtube.com/embed/KBBlBIu0DK8?autoplay=1&mute=1&playsinline=1&rel=0" ' + 622 'title="Rafael Vieira, treino, vÃdeo 3" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>' + 623 "</div></div>" + 624 '<p class="site-gate__embed-tagline">Treino, vibe e carreira</p>' + 625 "</div>" + 626 "</aside></div></div>"; 627 628 wrap.querySelector(".site-gate__joke").textContent = joke; 629 var term = wrap.querySelector(".site-gate__terminal"); 630 term.textContent = termLine; 631 var cursor = document.createElement("span"); 632 cursor.className = "site-gate__cursor"; 633 cursor.setAttribute("aria-hidden", "true"); 634 term.appendChild(cursor); 635 636 var form = wrap.querySelector(".site-gate__form"); 637 var input = wrap.querySelector("#site-gate-pass"); 638 var msg = wrap.querySelector(".site-gate__msg"); 639 var msgExtra = wrap.querySelector(".site-gate__msg-extra"); 640 var btn = wrap.querySelector(".site-gate__btn"); 641 642 function clearExtraMsg() { 643 msgExtra.textContent = ""; 644 msgExtra.hidden = true; 645 msgExtra.setAttribute("aria-hidden", "true"); 646 } 647 648 function showWrongPassword() { 649 msg.textContent = pick(WRONG_PRIMARY); 650 msg.classList.remove("site-gate__msg--ok"); 651 msgExtra.textContent = pick(WRONG_EXTRA); 652 msgExtra.hidden = false; 653 msgExtra.setAttribute("aria-hidden", "false"); 654 term.textContent = pick(TERMINAL_FAIL_SNIPPETS); 655 term.appendChild(cursor); 656 form.classList.remove("site-gate__form--shake"); 657 void form.offsetWidth; 658 form.classList.add("site-gate__form--shake"); 659 window.setTimeout(function () { 660 form.classList.remove("site-gate__form--shake"); 661 }, 500); 662 } 663 664 form.addEventListener("submit", function (ev) { 665 ev.preventDefault(); 666 var raw = (input.value || "").trim(); 667 msg.textContent = ""; 668 clearExtraMsg(); 669 msg.classList.remove("site-gate__msg--ok"); 670 if (!raw) { 671 msg.textContent = "Coloca a senha aÃ."; 672 return; 673 } 674 btn.disabled = true; 675 // A verificação é feita no servidor â o navegador nunca vê o hash da senha. 676 // O endpoint tem rate limit por IP para evitar tentativa exaustiva. 677 fetch("/api/site-admin/public/site-gate/check", { 678 method: "POST", 679 credentials: "same-origin", 680 headers: { "Content-Type": "application/json" }, 681 body: JSON.stringify({ password: raw }), 682 }) 683 .then(function (r) { 684 if (r.status === 429) { 685 return { ok: false, rate: true }; 686 } 687 if (r.status === 410) { 688 return { ok: true, gateOff: true }; 689 } 690 return r.json().catch(function () { 691 return { ok: false }; 692 }); 693 }) 694 .then(function (j) { 695 if (j && j.gateOff) { 696 // Admin desligou o portão durante a sessão; libera o site direto. 697 clearExtraMsg(); 698 msg.classList.add("site-gate__msg--ok"); 699 msg.textContent = "Portão liberado."; 700 setTimeout(unlock, 320); 701 return; 702 } 703 if (j && j.ok) { 704 clearExtraMsg(); 705 msg.classList.add("site-gate__msg--ok"); 706 msg.textContent = "Acesso confirmado."; 707 setTimeout(unlock, 420); 708 return; 709 } 710 if (j && j.rate) { 711 clearExtraMsg(); 712 msg.textContent = "Muitas tentativas seguidas; tenta de novo em alguns minutos."; 713 btn.disabled = false;
714 return; 715 } 716 showWrongPassword(); 717 btn.disabled = false; 718 input.select(); 719 }) 720 .catch(function () { 721 clearExtraMsg(); 722 msg.textContent = "Não foi possÃvel verificar agora. Tenta de novo em instantes."; 723 btn.disabled = false; 724 }); 725 }); 726 727 document.addEventListener( 728 "keydown", 729 function (ev) { 730 konamiBuf.push(ev.keyCode); 731 if (konamiBuf.length > 10) { 732 konamiBuf.shift(); 733 } 734 if (konamiBuf.join(",") === KONAMI) { 735 konamiBuf.length = 0; 736 msg.classList.remove("site-gate__msg--ok"); 737 msg.textContent = "Atalho registrado; o acesso continua protegido."; 738 clearExtraMsg(); 739 } 740 }, 741 true 742 ); 743 744 document.body.insertBefore(wrap, document.body.firstChild); 745 startGateCounters(); 746 initSiteGateMiniGame(wrap); 747 setTimeout(function () { 748 input.focus(); 749 }, 50); 750 } 751 752 try { 753 if (shouldSkipSiteGate()) { 754 openAccess(); 755 return; 756 } 757 } catch (e) { 758 openAccess(); 759 return; 760 } 761 762 var cfg = loadPublicConfig(); 763 // O backend novo só publica gate_enabled + has_password. Em deploys antigos vinha junto 764 // o hash da senha; aceitamos os dois formatos para a transição não quebrar caches. 765 var gateOn = gateEnabledFromConfig(cfg) && hasPasswordFromConfig(cfg); 766 767 if (!gateOn) { 768 clearGateSessionAndRibbon(); 769 openAccess(); 770 return; 771 } 772 773 try { 774 if (gateSessionOk()) { 775 openAccess(); 776 installSessionRibbonIfNeeded(); 777 return; 778 } 779 } catch (e2) { 780 openAccess(); 781 return; 782 } 783 784 bootLocked(); 785})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.