Key / index.html
Isaac194's picture
Upload 4 files
85795a6 verified
Raw
History Blame Contribute Delete
7.63 kB
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Panel de Keys</title>
<style>
:root { --bg:#0f172a; --card:#1e293b; --accent:#22d3ee; --text:#e2e8f0; --muted:#94a3b8; --ok:#34d399; --bad:#f87171; }
* { box-sizing: border-box; }
body { margin:0; font-family: system-ui, sans-serif; background:var(--bg); color:var(--text); padding:20px; }
h1 { font-size: 1.3rem; margin-bottom: 4px; }
p.sub { color: var(--muted); margin-top:0; font-size:.85rem; }
.card { background:var(--card); border-radius:12px; padding:16px; margin-bottom:16px; }
label { display:block; font-size:.8rem; color:var(--muted); margin-bottom:4px; margin-top:10px; }
input, select { width:100%; padding:8px; border-radius:8px; border:1px solid #334155; background:#0f172a; color:var(--text); font-size:.9rem; }
button { margin-top:14px; width:100%; padding:10px; border:none; border-radius:8px; background:var(--accent); color:#0f172a; font-weight:600; cursor:pointer; }
button.secondary { background:#334155; color:var(--text); }
.key-box { font-family: monospace; font-size:1.1rem; background:#0f172a; border:1px dashed var(--accent); padding:10px; border-radius:8px; margin-top:10px; text-align:center; letter-spacing:1px; }
.row { display:flex; justify-content:space-between; font-size:.85rem; padding:4px 0; border-bottom:1px solid #334155; }
.badge { padding:2px 8px; border-radius:20px; font-size:.75rem; }
.badge.active { background:rgba(52,211,153,.15); color:var(--ok); }
.badge.used { background:rgba(34,211,238,.15); color:var(--accent); }
.badge.expired { background:rgba(248,113,113,.15); color:var(--bad); }
#countdown { font-size:1.5rem; text-align:center; margin-top:8px; }
.log-item { font-size:.78rem; color:var(--muted); padding:4px 0; border-bottom:1px solid #263449; }
</style>
</head>
<body>
<h1>馃攽 Generador de Keys con vencimiento</h1>
<p class="sub">Tienda de descuentos 路 Apps 路 Juegos</p>
<div class="card">
<strong>1. Generar key</strong>
<label>Plataforma</label>
<select id="platform">
<option>Tienda de descuentos</option>
<option>Apps</option>
<option>Juegos</option>
</select>
<label>Apartado / secci贸n</label>
<input id="section" placeholder="ej: Descuento 20%, Nivel 3, C贸digo de acceso...">
<label>Duraci贸n</label>
<div style="display:flex; gap:8px;">
<input id="durationValue" type="number" value="60" min="1" style="flex:1;">
<select id="durationUnit" style="flex:1;">
<option value="minutes">Minutos</option>
<option value="hours">Horas</option>
<option value="days">D铆as</option>
<option value="weeks">Semanas</option>
<option value="months">Meses</option>
</select>
</div>
<label>Gmail del usuario (opcional)</label>
<input id="gmail" type="email" placeholder="usuario@gmail.com">
<button onclick="generateKey()">Generar key</button>
<div id="genResult" style="display:none">
<div class="key-box" id="genKey"></div>
<div class="row"><span>Vence</span><span id="genExpires"></span></div>
<button class="secondary" onclick="downloadReceipt()">Descargar comprobante PDF</button>
</div>
</div>
<div class="card">
<strong>2. Consultar / usar una key</strong>
<label>Key</label>
<input id="checkKey" placeholder="XXXXX-XXXXX-XXXXX-XXXXX">
<label>Nombre del dispositivo</label>
<input id="deviceName" placeholder="ej: iPhone de Juan, PC-Oficina">
<label>Plataforma donde se inicia sesi贸n</label>
<select id="loginPlatform">
<option>Tienda de descuentos</option>
<option>Apps</option>
<option>Juegos</option>
</select>
<button onclick="checkStatus()">Consultar estado</button>
<button class="secondary" onclick="useKey()">Registrar uso (login)</button>
<div id="statusResult" style="display:none">
<div id="countdown"></div>
<div class="row"><span>Estado</span><span id="statusBadge"></span></div>
<div class="row"><span>Plataforma</span><span id="statusPlatform"></span></div>
<div class="row"><span>Apartado</span><span id="statusSection"></span></div>
<h4>Historial</h4>
<div id="history"></div>
</div>
</div>
<script>
let currentKey = null;
let countdownTimer = null;
async function generateKey() {
const body = {
platform: document.getElementById('platform').value,
section: document.getElementById('section').value,
duration_value: document.getElementById('durationValue').value,
duration_unit: document.getElementById('durationUnit').value,
gmail: document.getElementById('gmail').value
};
const res = await fetch('/api/keys/generate', {
method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(body)
});
const data = await res.json();
if (data.error) { alert(data.error); return; }
currentKey = data.key;
document.getElementById('genResult').style.display = 'block';
document.getElementById('genKey').textContent = data.key;
document.getElementById('genExpires').textContent = new Date(data.expires_at).toLocaleString();
document.getElementById('checkKey').value = data.key;
}
function downloadReceipt() {
if (!currentKey) return;
window.open(`/api/keys/${currentKey}/receipt.pdf`, '_blank');
}
async function fetchStatus(key) {
const res = await fetch(`/api/keys/${key}/status`);
return res.json();
}
function renderStatus(data) {
document.getElementById('statusResult').style.display = 'block';
const badge = document.getElementById('statusBadge');
badge.textContent = data.status;
badge.className = 'badge ' + data.status;
document.getElementById('statusPlatform').textContent = data.platform;
document.getElementById('statusSection').textContent = data.section;
const hist = document.getElementById('history');
hist.innerHTML = '';
data.history.forEach(l => {
const div = document.createElement('div');
div.className = 'log-item';
div.textContent = `${l.event_type.toUpperCase()}${l.device_name || '-'}${l.event_date} ${l.event_time}${l.platform}`;
hist.appendChild(div);
});
if (countdownTimer) clearInterval(countdownTimer);
if (data.status === 'expired' || data.seconds_remaining === null) {
document.getElementById('countdown').textContent = 'VENCIDA';
return;
}
let remaining = data.seconds_remaining;
const update = () => {
const h = Math.floor(remaining/3600), m = Math.floor((remaining%3600)/60), s = remaining%60;
document.getElementById('countdown').textContent = `${h}h ${m}m ${s}s restantes`;
if (remaining <= 0) { clearInterval(countdownTimer); document.getElementById('countdown').textContent = 'VENCIDA'; }
remaining--;
};
update();
countdownTimer = setInterval(update, 1000);
}
async function checkStatus() {
const key = document.getElementById('checkKey').value.trim();
if (!key) return;
const data = await fetchStatus(key);
if (data.error) { alert(data.error); return; }
renderStatus(data);
}
async function useKey() {
const key = document.getElementById('checkKey').value.trim();
const device_name = document.getElementById('deviceName').value.trim();
const platform = document.getElementById('loginPlatform').value;
if (!key) return;
const res = await fetch('/api/keys/validate', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({ key, device_name, platform })
});
const data = await res.json();
if (!data.valid) { alert(data.reason || 'Key inv谩lida'); }
const status = await fetchStatus(key);
renderStatus(status);
}
</script>
</body>
</html>