fix(cliente): exibir qrcode e acoes de whatsapp apenas apos aprovacao da conexao

This commit is contained in:
Tiago
2026-10-13 10:10:14 -03:00
parent 83d5b68459
commit 40d5a0f3fc
2 changed files with 43 additions and 15 deletions
@@ -457,7 +457,7 @@ public final class ClienteHandlers implements HttpHandler {
}
private boolean adminEmpresa(String cargo){return "admin_empresa".equals(cargo);}
private void conectarEvolution(HttpExchange ex,long empresaId)throws Exception{Map<String,String>b=body(ex);String url=b.getOrDefault("instance_url","");String numero=EvolutionApiClient.onlyDigits(req(b,"numero_whatsapp"));if(numero.length()<10){HttpResponses.json(ex,400,Map.of("erro","numero_whatsapp_invalido","mensagem","Informe o número com DDD e código do país."));return;}try(Connection m=masterDs.getMasterConnection()){liberarCadastrosEvolutionOrfaos(m,numero);Map<String,Object> existente=one(m,"SELECT id,empresa_id,status FROM evolution_instancias WHERE numero_whatsapp=? ORDER BY id DESC LIMIT 1",Arrays.asList(numero));if(existente!=null){HttpResponses.json(ex,409,Map.of("erro","numero_whatsapp_ja_cadastrado","mensagem","Este número de WhatsApp já está cadastrado em uma conexão."));return;}}String instance="empresa"+empresaId+"_"+System.currentTimeMillis();String token=UUID.randomUUID().toString().replace("-","");var criada=evolutionApiClient.criarInstancia(url,instance,token,numero);try(Connection m=masterDs.getMasterConnection();PreparedStatement ps=m.prepareStatement("INSERT INTO evolution_instancias (empresa_id,instance_url,instance_token_enc,numero_whatsapp,status) VALUES (?,?,?,?, 'pendente') RETURNING id")){ps.setLong(1,empresaId);ps.setString(2,url);ps.setString(3,criada.instanceName()+":"+criada.token());ps.setString(4,numero);try(ResultSet rs=ps.executeQuery()){rs.next();Map<String,Object> resp=new LinkedHashMap<>();resp.put("id",rs.getLong(1));resp.put("instance_name",criada.instanceName());resp.put("status","pendente");resp.put("qrcode_base64",criada.qrcodeBase64());resp.put("mock",criada.mock());HttpResponses.json(ex,201,resp);}}}
private void conectarEvolution(HttpExchange ex,long empresaId)throws Exception{Map<String,String>b=body(ex);String url=b.getOrDefault("instance_url","");String numero=EvolutionApiClient.onlyDigits(req(b,"numero_whatsapp"));if(numero.length()<10){HttpResponses.json(ex,400,Map.of("erro","numero_whatsapp_invalido","mensagem","Informe o número com DDD e código do país."));return;}try(Connection m=masterDs.getMasterConnection()){liberarCadastrosEvolutionOrfaos(m,numero);Map<String,Object> existente=one(m,"SELECT id,empresa_id,status FROM evolution_instancias WHERE numero_whatsapp=? ORDER BY id DESC LIMIT 1",Arrays.asList(numero));if(existente!=null){HttpResponses.json(ex,409,Map.of("erro","numero_whatsapp_ja_cadastrado","mensagem","Este número de WhatsApp já está cadastrado em uma conexão."));return;}}String instance="empresa"+empresaId+"_"+System.currentTimeMillis();String token=UUID.randomUUID().toString().replace("-","");var criada=evolutionApiClient.criarInstancia(url,instance,token,numero);try(Connection m=masterDs.getMasterConnection();PreparedStatement ps=m.prepareStatement("INSERT INTO evolution_instancias (empresa_id,instance_url,instance_token_enc,numero_whatsapp,status) VALUES (?,?,?,?, 'pendente') RETURNING id")){ps.setLong(1,empresaId);ps.setString(2,url);ps.setString(3,criada.instanceName()+":"+criada.token());ps.setString(4,numero);try(ResultSet rs=ps.executeQuery()){rs.next();Map<String,Object> resp=new LinkedHashMap<>();resp.put("id",rs.getLong(1));resp.put("instance_name",criada.instanceName());resp.put("status","pendente");resp.put("mock",criada.mock());HttpResponses.json(ex,201,resp);}}}
private void liberarCadastrosEvolutionOrfaos(Connection m,String numero)throws Exception{List<Map<String,Object>> existentes=rows(m,"SELECT id,instance_url,instance_token_enc FROM evolution_instancias WHERE numero_whatsapp=? ORDER BY id",Arrays.asList(numero));for(Map<String,Object> inst:existentes){String token=str(inst.get("instance_token_enc"));String instanceName=token.split(":",2)[0];if(instanceName.isBlank())continue;try{evolutionApiClient.estado(str(inst.get("instance_url")),instanceName);}catch(Exception err){if(!evolutionNaoEncontrada(err))throw err;try(PreparedStatement ps=m.prepareStatement("DELETE FROM evolution_instancias WHERE id=?")){ps.setLong(1,((Number)inst.get("id")).longValue());ps.executeUpdate();}}}}
private boolean evolutionNaoEncontrada(Exception err){String msg=err.getMessage()==null?"":err.getMessage().toLowerCase(Locale.ROOT);return msg.contains("404")||msg.contains("not found")||msg.contains("instance not found")||msg.contains("nao encontrada")||msg.contains("não encontrada");}
private void statusEvolution(HttpExchange ex,long empresaId)throws Exception{
@@ -494,6 +494,10 @@ public final class ClienteHandlers implements HttpHandler {
Long id=optLong(b.get("id"));
Map<String,Object> inst=id!=null?evolutionInstanciaPorId(empresaId,id):evolutionInstancia(empresaId);
if(inst==null){HttpResponses.json(ex,404,Map.of("erro","instancia_nao_configurada"));return;}
if(!"aprovada".equalsIgnoreCase(str(inst.get("status")))){
HttpResponses.json(ex,403,Map.of("erro","instancia_pendente_aprovacao","mensagem","Esta conexão está pendente de aprovação pelo administrador."));
return;
}
String[] parts=String.valueOf(inst.get("instance_token_enc")).split(":",2);
Map<String,Object> qr=evolutionApiClient.conectar(str(inst.get("instance_url")),parts[0],str(inst.get("numero_whatsapp")));
HttpResponses.json(ex,200,qr);
@@ -503,6 +507,10 @@ public final class ClienteHandlers implements HttpHandler {
Long id=optLong(b.get("id"));
Map<String,Object> inst=id!=null?evolutionInstanciaPorId(empresaId,id):evolutionInstancia(empresaId);
if(inst==null){HttpResponses.json(ex,404,Map.of("erro","instancia_nao_configurada"));return;}
if(!"aprovada".equalsIgnoreCase(str(inst.get("status")))){
HttpResponses.json(ex,403,Map.of("erro","instancia_pendente_aprovacao","mensagem","Esta conexão está pendente de aprovação pelo administrador."));
return;
}
String[] parts=String.valueOf(inst.get("instance_token_enc")).split(":",2);
HttpResponses.json(ex,200,evolutionApiClient.logout(str(inst.get("instance_url")),parts[0]));
}
@@ -511,6 +519,10 @@ public final class ClienteHandlers implements HttpHandler {
Long id=optLong(b.get("id"));
Map<String,Object> inst=id!=null?evolutionInstanciaPorId(empresaId,id):evolutionInstancia(empresaId);
if(inst==null){HttpResponses.json(ex,404,Map.of("erro","instancia_nao_configurada"));return;}
if(!"aprovada".equalsIgnoreCase(str(inst.get("status")))){
HttpResponses.json(ex,403,Map.of("erro","instancia_pendente_aprovacao","mensagem","Esta conexão está pendente de aprovação pelo administrador."));
return;
}
String[] parts=String.valueOf(inst.get("instance_token_enc")).split(":",2);
HttpResponses.json(ex,200,evolutionApiClient.reiniciar(str(inst.get("instance_url")),parts[0]));
}
+30 -14
View File
@@ -90,6 +90,7 @@ function renderCards(list) {
const evoState = evo.state || 'close';
const instName = inst.instance_name || ('instancia_' + inst.id);
const phoneFormatted = formatPhone(inst.numero_whatsapp);
const isAprovada = String(inst.status || '').toLowerCase() === 'aprovada';
return `
<article class="wa-card" data-id="${inst.id}">
@@ -129,15 +130,19 @@ function renderCards(list) {
<footer class="wa-card-footer">
<span style="font-size: 12px; font-weight: 600; color: var(--muted-strong);">Ações:</span>
<div class="table-actions">
<button class="table-icon-btn" type="button" title="Conectar via QR Code" aria-label="Conectar via QR Code" onclick="abrirModalQr(${inst.id})">
${Icons.qrCode}
</button>
<button class="table-icon-btn" type="button" title="Reconectar WhatsApp" aria-label="Reconectar WhatsApp" onclick="abrirModalReconectar(${inst.id})">
${Icons.restart}
</button>
<button class="table-icon-btn danger" type="button" title="Desconectar WhatsApp" aria-label="Desconectar WhatsApp" onclick="abrirModalDesconectar(${inst.id})">
${Icons.power}
</button>
${isAprovada ? `
<button class="table-icon-btn" type="button" title="Conectar via QR Code" aria-label="Conectar via QR Code" onclick="abrirModalQr(${inst.id})">
${Icons.qrCode}
</button>
<button class="table-icon-btn" type="button" title="Reconectar WhatsApp" aria-label="Reconectar WhatsApp" onclick="abrirModalReconectar(${inst.id})">
${Icons.restart}
</button>
<button class="table-icon-btn danger" type="button" title="Desconectar WhatsApp" aria-label="Desconectar WhatsApp" onclick="abrirModalDesconectar(${inst.id})">
${Icons.power}
</button>
` : `
<span class="badge warn" style="font-size: 11px; padding: 4px 8px; font-weight: 600;">Aguardando aprovação</span>
`}
</div>
</footer>
</article>
@@ -200,6 +205,10 @@ async function requisitarQrCode() {
function abrirModalQr(id, initialData = null) {
selectedInstanciaId = id;
const inst = conexoes.find(c => c.id === id);
if (inst && String(inst.status || '').toLowerCase() !== 'aprovada') {
Util.toast('Esta conexão ainda está pendente de aprovação pelo administrador.', true);
return;
}
if (inst && inst.numero_whatsapp) {
qrModalTitle.textContent = `Conectar ${formatPhonePlain(inst.numero_whatsapp)}`;
} else {
@@ -220,6 +229,11 @@ function fecharModalQr() {
function abrirModalReconectar(id) {
selectedInstanciaId = id;
const inst = conexoes.find(c => c.id === id);
if (inst && String(inst.status || '').toLowerCase() !== 'aprovada') {
Util.toast('Esta conexão ainda está pendente de aprovação pelo administrador.', true);
return;
}
reconectarStepConfirm.classList.remove('hidden');
reconectarStepLoading.classList.add('hidden');
reconectarModal.classList.remove('hidden');
@@ -251,6 +265,11 @@ async function executarReconectar() {
function abrirModalDesconectar(id) {
selectedInstanciaId = id;
const inst = conexoes.find(c => c.id === id);
if (inst && String(inst.status || '').toLowerCase() !== 'aprovada') {
Util.toast('Esta conexão ainda está pendente de aprovação pelo administrador.', true);
return;
}
desconectarStepConfirm.classList.remove('hidden');
desconectarStepLoading.classList.add('hidden');
desconectarModal.classList.remove('hidden');
@@ -286,13 +305,10 @@ form.onsubmit = async (e) => {
try {
if (submitBtn) submitBtn.disabled = true;
const body = Util.formData(form);
const d = await Api.post('/api/config/evolution-instancia/conectar', body);
await Api.post('/api/config/evolution-instancia/conectar', body);
form.reset();
Util.toast('WhatsApp cadastrado com sucesso!');
Util.toast('WhatsApp cadastrado com sucesso! Aguarde a aprovação do administrador para conectar via QR Code.');
await load();
if (d.id) {
abrirModalQr(d.id, d);
}
} catch (err) {
Util.toast(err.message, true);
} finally {