O algoritmo (CHAVE_INTERNA + funcoes gerarReativacao/gerarInstalacao) ficava 100% legivel no codigo-fonte da pagina — qualquer pessoa com acesso ao painel conseguia abrir o DevTools e extrair a logica pra gerar chaves de licenca fora do sistema. Move tudo pra app/gerador_chave.py (roda so no servidor) e expoe via POST /gerador-chave/gerar (autenticado, protegido por CSRF). A pagina agora so manda os dados do formulario e mostra o resultado que volta do backend. Algoritmo verificado byte-a-byte contra os 3 exemplos reais conhecidos (cliente 000001, ALUGUEL). Bonus: o byte inicial agora usa secrets.randbelow (CSPRNG) em vez de Math.random() do JS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
113 lines
4.1 KiB
HTML
113 lines
4.1 KiB
HTML
{% extends "base.html" %}
|
|
{% block titulo %}Gerador de Chave{% endblock %}
|
|
{% block conteudo %}
|
|
<div class="cartao" style="max-width:420px;margin:0 auto;">
|
|
<h1 style="text-align:center;">Gerador de Chave</h1>
|
|
|
|
<label for="tipo">Tipo de chave</label>
|
|
<select id="tipo">
|
|
<option value="reativacao">Reativação (mês/ano)</option>
|
|
<option value="instalacao">Instalação (aluguel/compra)</option>
|
|
</select>
|
|
|
|
<label for="codigo">Código do cliente</label>
|
|
<input type="text" id="codigo" placeholder="Ex: 000123" maxlength="6" inputmode="numeric">
|
|
|
|
<div id="campos-reativacao">
|
|
<label for="referencia">Mês/ano de referência</label>
|
|
<input type="month" id="referencia" min="2000-01" max="2100-12">
|
|
</div>
|
|
|
|
<div id="campos-instalacao" style="display:none;">
|
|
<label for="modalidade">Modalidade</label>
|
|
<select id="modalidade">
|
|
<option value="ALUGUEL">Aluguel</option>
|
|
<option value="COMPRA">Compra</option>
|
|
</select>
|
|
</div>
|
|
|
|
<button id="gerar">Gerar chave</button>
|
|
|
|
<div id="erro" style="color:var(--cor-perigo);margin-top:14px;display:none;"></div>
|
|
<div id="resultado" style="margin-top:20px;padding:14px;border-radius:8px;background:var(--cor-superficie-alt);border:1px solid var(--cor-borda);border-left:3px solid var(--cor-acento);word-break:break-all;font-family:'Courier New',monospace;color:var(--cor-acento-texto);display:none;"></div>
|
|
<button id="copiar" style="display:none;background:transparent;border:1px solid var(--cor-borda);color:var(--cor-texto-secundario);width:auto;padding:6px 10px;">Copiar</button>
|
|
</div>
|
|
|
|
<script>
|
|
// O algoritmo de geração de chave roda só no backend (app/gerador_chave.py)
|
|
// — esta página só manda os dados do formulário pro endpoint e mostra o
|
|
// resultado. Nunca reintroduza a lógica de cifra aqui: qualquer pessoa com
|
|
// acesso à página consegue ler o código-fonte JS pelo navegador.
|
|
const CSRF_TOKEN = "{{ csrf_token }}";
|
|
|
|
const $tipo = document.getElementById('tipo');
|
|
const $codigo = document.getElementById('codigo');
|
|
const $referencia = document.getElementById('referencia');
|
|
const $modalidade = document.getElementById('modalidade');
|
|
const $camposReativacao = document.getElementById('campos-reativacao');
|
|
const $camposInstalacao = document.getElementById('campos-instalacao');
|
|
const $erro = document.getElementById('erro');
|
|
const $resultado = document.getElementById('resultado');
|
|
const $copiar = document.getElementById('copiar');
|
|
|
|
$codigo.addEventListener('input', () => {
|
|
$codigo.value = $codigo.value.replace(/\D/g, '');
|
|
});
|
|
|
|
(function preencherMesAtual() {
|
|
const hoje = new Date();
|
|
$referencia.value = `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}`;
|
|
})();
|
|
|
|
$tipo.addEventListener('change', () => {
|
|
const ehReativacao = $tipo.value === 'reativacao';
|
|
$camposReativacao.style.display = ehReativacao ? 'block' : 'none';
|
|
$camposInstalacao.style.display = ehReativacao ? 'none' : 'block';
|
|
});
|
|
|
|
function mostrarErro(msg) {
|
|
$erro.textContent = msg;
|
|
$erro.style.display = 'block';
|
|
$resultado.style.display = 'none';
|
|
$copiar.style.display = 'none';
|
|
}
|
|
|
|
document.getElementById('gerar').addEventListener('click', async () => {
|
|
$erro.style.display = 'none';
|
|
|
|
const corpo = new URLSearchParams({
|
|
csrf_token: CSRF_TOKEN,
|
|
tipo: $tipo.value,
|
|
codigo: $codigo.value,
|
|
referencia: $referencia.value,
|
|
modalidade: $modalidade.value,
|
|
});
|
|
|
|
let resposta, dados;
|
|
try {
|
|
resposta = await fetch('/gerador-chave/gerar', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
body: corpo,
|
|
});
|
|
dados = await resposta.json();
|
|
} catch (e) {
|
|
return mostrarErro('Não foi possível falar com o servidor — tente de novo.');
|
|
}
|
|
|
|
if (!resposta.ok) return mostrarErro(dados.erro || 'Não foi possível gerar a chave.');
|
|
|
|
$resultado.textContent = dados.chave;
|
|
$resultado.style.display = 'block';
|
|
$copiar.style.display = 'inline-block';
|
|
});
|
|
|
|
$copiar.addEventListener('click', () => {
|
|
navigator.clipboard.writeText($resultado.textContent).then(() => {
|
|
$copiar.textContent = 'Copiado!';
|
|
setTimeout(() => { $copiar.textContent = 'Copiar'; }, 1500);
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|