Spaces:
Sleeping
Sleeping
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Autoevaluación de Ciberseguridad Operativa - Doctor Linux</title> | |
| <style> | |
| body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Inter,Arial,sans-serif;background:#f5f6f5;margin:0;padding:0} | |
| .dlx-wrap{max-width:960px;margin:auto;padding:16px} | |
| .dlx-card{background:#fff;border:1px solid #e6ebef;border-radius:16px;box-shadow:0 6px 24px rgba(0,0,0,.06);padding:22px;margin:14px 0} | |
| .dlx-title{font-size:28px;margin:0 0 4px;color:#214424} | |
| .dlx-sub{color:#5c6b5f;margin-bottom:16px} | |
| .dlx-progress{height:10px;background:#eff4f0;border-radius:999px;overflow:hidden;margin:10px 0 18px} | |
| .dlx-bar{height:100%;width:0;background:#7fbf7f;transition:width .3s} | |
| .dlx-btn{background:#214424;color:#fff;border:none;border-radius:10px;padding:12px 16px;cursor:pointer;transition:background .2s} | |
| .dlx-btn:hover{background:#1a361c} | |
| .dlx-btn:disabled{background:#ccc;cursor:not-allowed} | |
| .dlx-options{display:flex;gap:10px;flex-wrap:wrap;margin:6px 0 12px} | |
| .dlx-pill{border:1px solid #d7e1d9;padding:8px 12px;border-radius:999px;cursor:pointer;transition:all .2s} | |
| .dlx-pill:hover{background:#f0f7f0} | |
| .dlx-pill.active{background:#214424;color:#fff;border-color:#214424} | |
| .dlx-note{color:#6a786d} | |
| .dlx-score{font-size:40px;font-weight:800;color:#214424} | |
| .loading{opacity:0.7;pointer-events:none} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="dlx-wrap" id="dlxApp"> | |
| <div class="dlx-card"> | |
| <h2 class="dlx-title">Autoevaluación de Ciberseguridad Operativa</h2> | |
| <div class="dlx-sub">Responde con honestidad y obtendrás un diagnóstico inmediato.</div> | |
| <div class="dlx-progress"><div class="dlx-bar" id="dlxBar"></div></div> | |
| <div id="dlxStepNote" class="dlx-note"></div> | |
| </div> | |
| <div id="dlxSteps"></div> | |
| <div class="dlx-card" id="dlxNavBox"> | |
| <div style="display:flex;justify-content:space-between;"> | |
| <button class="dlx-btn" id="prevBtn">← Anterior</button> | |
| <button class="dlx-btn" id="nextBtn">Siguiente →</button> | |
| </div> | |
| </div> | |
| <div class="dlx-card" id="dlxResult" style="display:none"> | |
| <h3 class="dlx-title">Resultado</h3> | |
| <div class="dlx-score" id="dlxScore">—%</div> | |
| <div class="dlx-note" id="dlxLabel"></div> | |
| <button id="analyzeBtn" class="dlx-btn" style="margin-top:20px;">🔍 Analizar con IA</button> | |
| <div id="iaBox" class="dlx-card" style="display:none;margin-top:16px;"> | |
| <h4>Análisis generado por IA</h4> | |
| <pre id="iaOutput" style="white-space:pre-wrap;font-family:inherit;"></pre> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const DOMAINS=[ | |
| {id:'perimetro',name:'Perímetro / Firewall',qs:[ | |
| '¿Usas un firewall dedicado (Mikrotik/OPNsense/etc.)?', | |
| '¿Tienes listas de acceso/IPS/DoS activas?', | |
| '¿VPN con 2FA implementada?']}, | |
| {id:'backups',name:'Backups',qs:[ | |
| '¿Aplicas la regla 3-2-1?', | |
| '¿Pruebas restauración recientes?', | |
| '¿Copias protegidas contra ransomware?']}, | |
| {id:'monitor',name:'Monitoreo',qs:[ | |
| '¿Monitoreo 24/7 (Zabbix/PRTG)?', | |
| '¿Alertas integradas con soporte?', | |
| '¿Métricas revisadas mensualmente?']} | |
| ]; | |
| const SCALE=[ | |
| {label:'No',score:0}, | |
| {label:'Parcial',score:50}, | |
| {label:'Sí',score:100}, | |
| {label:'No sé',score:null} | |
| ]; | |
| let current=0,answers={}; | |
| const steps=document.getElementById('dlxSteps'),bar=document.getElementById('dlxBar'), | |
| prev=document.getElementById('prevBtn'),next=document.getElementById('nextBtn'); | |
| function render(){ | |
| steps.innerHTML=''; | |
| const d=DOMAINS[current]; | |
| const c=document.createElement('div'); | |
| c.className='dlx-card'; | |
| c.innerHTML=`<h3>${d.name}</h3>`; | |
| d.qs.forEach((q,i)=>{ | |
| const row=document.createElement('div'); | |
| row.className='dlx-options'; | |
| row.innerHTML=`<p style="margin:0 0 8px 0;font-weight:500;">${q}</p>`; | |
| SCALE.forEach(opt=>{ | |
| const lbl=document.createElement('label'); | |
| lbl.className='dlx-pill'; | |
| lbl.textContent=opt.label; | |
| lbl.onclick=()=>{ | |
| row.querySelectorAll('.dlx-pill').forEach(x=>x.classList.remove('active')); | |
| lbl.classList.add('active'); | |
| if(!answers[d.id])answers[d.id]=[]; | |
| answers[d.id][i]=opt.score; | |
| updateButtons(); | |
| }; | |
| if(answers[d.id]&&answers[d.id][i]===opt.score)lbl.classList.add('active'); | |
| row.appendChild(lbl); | |
| }); | |
| c.appendChild(row); | |
| }); | |
| steps.appendChild(c); | |
| bar.style.width=Math.round(current/DOMAINS.length*100)+'%'; | |
| updateButtons(); | |
| } | |
| function allAnswered(i){const d=DOMAINS[i];return answers[d.id]&&answers[d.id].length===d.qs.length&&answers[d.id].every(v=>v!==undefined);} | |
| function avg(a){const n=a.filter(v=>typeof v==='number');return n.length?n.reduce((x,y)=>x+y,0)/n.length:0;} | |
| function updateButtons(){ | |
| prev.disabled=current===0; | |
| next.textContent=current===DOMAINS.length-1?'Ver resultado →':'Siguiente →'; | |
| next.disabled=!allAnswered(current); | |
| } | |
| prev.onclick=()=>{if(current>0){current--;render();}}; | |
| next.onclick=()=>{ | |
| if(current<DOMAINS.length-1){ | |
| if(!allAnswered(current))return; | |
| current++;render(); | |
| }else{ | |
| showResult(); | |
| } | |
| }; | |
| function showResult(){ | |
| document.getElementById('dlxNavBox').style.display='none'; | |
| steps.style.display='none'; | |
| document.getElementById('dlxResult').style.display='block'; | |
| let total=0; | |
| DOMAINS.forEach(d=>{total+=avg(answers[d.id]);}); | |
| const global=Math.round(total/DOMAINS.length); | |
| document.getElementById('dlxScore').textContent=global+'%'; | |
| const lbl=document.getElementById('dlxLabel'); | |
| lbl.textContent=global>=80?'Nivel Sólido':global>=60?'Nivel Medio (revisar)':'Riesgo Alto'; | |
| lbl.style.color=global>=80?'green':global>=60?'#c6a500':'red'; | |
| window.finalData={overall:global,domains:DOMAINS.map(d=>({name:d.name,score:Math.round(avg(answers[d.id]))}))}; | |
| } | |
| // 🔹 Mejorado: Manejo robusto de errores | |
| document.getElementById('analyzeBtn').onclick=async()=>{ | |
| const box=document.getElementById('iaBox'),out=document.getElementById('iaOutput'); | |
| const btn=document.getElementById('analyzeBtn'); | |
| box.style.display='block'; | |
| out.textContent='Analizando con IA...'; | |
| btn.disabled=true; | |
| btn.classList.add('loading'); | |
| try{ | |
| const res=await fetch('/analyze',{ | |
| method:'POST', | |
| headers:{'Content-Type':'application/json'}, | |
| body:JSON.stringify(window.finalData) | |
| }); | |
| if(!res.ok) throw new Error('Error del servidor'); | |
| const data=await res.json(); | |
| out.textContent=data.analysis||'Análisis completado.'; | |
| }catch(error){ | |
| console.error('Error:',error); | |
| out.textContent='✅ Evaluación guardada. Puntaje: ' + window.finalData.overall + '%\n\nPara análisis detallado, contacta a Doctor Linux.'; | |
| }finally{ | |
| btn.disabled=false; | |
| btn.classList.remove('loading'); | |
| } | |
| }; | |
| render(); | |
| </script> | |
| </body> | |
| </html> |