Novidades do LigarIT

O que mudou na plataforma e no agente, versão a versão.

  1. Novo

    Tarefas SQL: avisar em cada sucesso, e tarefas de teste que não gastam SMS

    Duas coisas nos avisos por SMS das Tarefas SQL.

    «Incluir SMS nesses avisos», por tarefa, ligado por omissão. Numa tarefa de testes desliga-se: a falha continua a aparecer no ecrã e a ir por e-mail, mas não gasta SMS, não conta para o limite do plano e não acorda ninguém fora de horas. O notificar_falhas que já existia não servia para isto — desliga o aviso todo, e-mail incluído.

    Dava-se a volta pelos motivos de cada telemóvel, que se restringem a tarefas concretas, mas isso é uma lista de permissões: cada tarefa nova tinha de ser acrescentada a cada número, e quem se esquecesse ficava sem aviso de uma tarefa a sério. A informação é da tarefa.

    O guarda fica no funil (AvisosPorClosum::emitir) e não em cada chamador: a falha, o regresso e a falha crítica passam todos por lá com o tarefaSqlId, por isso um emissor novo fica coberto sem se lembrar disto. Uma tarefa que já não exista avisa — na dúvida manda-se, porque um aviso a menos é uma avaria que ninguém soube.

    Dois sítios que a leitura do código obrigou a corrigir de passagem:

    • aplicar uma correção da IA voltava a ligar o SMS. O detalhe manda ao PUT tudo o que o ecrã tinha, e sem o campo lá o controller punha-o a true — é o que o comentário do OBS-08 nesse ficheiro avisa. O campo passou a ir, e no controller «ausente» passou a ser «deixa como está»;
    • o campo acompanha o notificar_falhas em todos os sítios por onde ele viaja (criar, atualizar, duplicar para um grupo, exportar, importar), senão perdia-se numa duplicação em silêncio.

    Motivo novo «Correu bem» (tarefa_sql.sucesso): avisa em cada execução real com sucesso, agendada ou à mão, ao contrário do «Voltou a funcionar», que só avisa na primeira depois de uma falha. A chave do evento é por corrida, por isso dois sucessos seguidos são dois SMS e não uma repetição calada. O ecrã «Acrescentar telemóvel» mostra-o sozinho — monta-se do eventos().

    É o único aviso da lista que não é uma avaria, por isso: não vem marcado por omissão, tem alvo (escolhe-se tarefa a tarefa) e é só SMS — não se inventou notificação na plataforma nem e-mail de sucesso, que o histórico do ecrã já diz que correu bem e foi o volume de e-mail que bloqueou a conta de envio a 15/10. Não passa pelo notificar_falhas, que fala de falhas; quem o quiser calar numa tarefa desliga o «Incluir SMS».

    Atenção ao volume: uma tarefa de 5 em 5 minutos daria 288 SMS por dia, e o que o trava é o avisos_sms_hora do PLN-07 — 3 por hora por empresa, por omissão.

    Cada peça foi provada vermelha primeiro: sem o guarda falham os 4 testes de supressão (e os que provam que o SMS continua a sair passam); sem o controller e as vistas falham os 4 do formulário; sem o listener falham os 3 do sucesso.

    Suite: 3059 testes, 1 falha anterior a estas alterações (ClassesDoTailwind, CSS compilado desatualizado; pede npm run build). Falta correr a migração no servidor, e o CI é que prova o rollback em SQL Server.

  2. Novo

    Estado dos envios: separadores E-mail e SMS, com o histórico de cada SMS

    A página «Estado do e-mail» passa a «Estado dos envios» (só admin), com um separador SMS ao lado do do e-mail: números das últimas 24 h, o cartão de filtros das outras páginas (pesquisa, empresa, estado, tipo, período) e a lista, com o estado de cada SMS — enviado, falhou (com o porquê), pendente ou sem resposta (pode ter saído).

    Histórico próprio (sms_enviados): o closum_envios conta o limite do plano e apaga os que não saem. Cada aviso fica pendente ao ir para a fila e o EnviarSmsClosum escreve o resultado; os códigos de confirmação e os SMS de teste ficam com o resultado na hora. Número mascarado; um código nunca fica escrito.

  3. Novo

    Definições › Detalhes: a palavra-passe com lápis no cartão do perfil

    Sai o cartão «Palavra-passe» à parte. No cartão do perfil, a palavra-passe aparece escondida (••••••••••) com um lápis ao lado, como o nome; carregar abre por baixo os três campos (atual, nova, repetir), com Cancelar, que os limpa e fecha. Com erro, o formulário volta aberto com a mensagem.

  4. Novo

    Definições › Detalhes: primeiro separador, perfil novo e o nome com lápis

    • «Detalhes» passa a ser o primeiro separador e o que abre nas Definições (Detalhes, Notificações, SMS, Aparência, Plano, Segurança).
    • O perfil ganha um cabeçalho com faixa de cor, avatar, nome, e-mail e a função em destaque, e os dados em cartões com ícone; o 2FA mostra o estado e, por ativar, leva à Segurança.
    • O nome edita-se no sítio: um lápis ao lado troca-o pelo campo, com Guardar e Cancelar. Sai o cartão «Nome» à parte. Com erro, o campo volta aberto com a mensagem.
  5. Novo

    PHP 8.5: tirar as depreciacoes e um defeito latente que elas destaparam

    Validado nas duas versoes, 3014 testes e zero falhas em cada: 8.3.12 (a de producao hoje) e 8.5.12. As depreciacoes do 8.5 passaram de 42 a 0.

    53 chamadas a ReflectionMethod/Property::setAccessible() em 10 ficheiros de teste. O proprio PHP diz o motivo: «nao tem efeito desde o 8.1». Nao fazem nada ha quatro versoes, por isso saem sem substituto. Eram todas instrucoes isoladas — verificado antes de mexer, com contagem antes e depois.

    config/database.php, nos blocos mysql E mariadb (sao dois, nao um). O PDO::MYSQL_ATTR_SSL_CA ficou depreciado no 8.5, mas o Pdo\Mysql que o substitui so existe desde o 8.4: trocar sem guarda partia a producao enquanto ela estiver no 8.3. Fica a condicao por versao, e o ramo nao escolhido nunca e avaliado, por isso nenhuma das duas versoes toca no que nao tem.

    Nota para quem passar isto a uma linha so quando todos os servidores estiverem no 8.4+: a constante vale 1009 no 8.3 e 1008 no 8.5 — o bloco MYSQL_ATTR_SSL_* foi renumerado a montante. As duas formas concordam dentro de cada versao. E por isto que o numero nunca se escreve a mao.

    ClassificadorDeFalha:97 — a ultima depreciacao nao era ruido. O DIAGNOSTICOS[$diagnostico?->codigo] usava null como indice, e o PHP convertia-o para '', que nunca e chave do mapa: dava o resultado certo por acidente. Esta no codigo que decide se uma falha de mapeamento e infra ou dados, que e o que liga ou desliga os avisos ao cliente (OBS-20). A condicao passa a dizer o que se queria dizer desde o inicio.

  6. Novo

    Definições › Detalhes: o perfil da conta, com o nome e a palavra-passe

    Um separador novo com o perfil de quem está na sessão: nome, e-mail, empresa, função, data da conta e estado do 2FA. Só se mudam o nome (único, até 50 caracteres) e a palavra-passe; o e-mail, a empresa e a função ficam com quem gere os utilizadores. A palavra-passe pede a atual e a confirmação; trocada, as outras sessões e os «lembrar-me» deixam de valer (SessaoUnica) e sai o aviso de segurança por e-mail.

  7. Novo

    2FA nas Definições: separador «Segurança» em vez do ícone no menu

    A autenticação em dois passos tinha uma página à parte, aberta por um ícone no fundo do menu. Passa a ser o separador «Segurança» das Definições, com o mesmo conteúdo. O endereço é o mesmo (two-factor.show), que o ExigirDoisFactores deixa abrir a quem ainda o tem de configurar, e para onde voltam todas as ações. O ponto vermelho de «falta configurar» passa para o ícone das Definições e para o separador.

  8. Novo

    Agentes em baixo: o e-mail passa a ser uma opção, desligada por omissão

    O e-mail «agente sem sinal / voltou» ia para todos os supervisores, sem forma de o desligar no e-mail principal. Passa a ser a preferência «Agentes em baixo ou de volta» nas Definições › Notificações, só para supervisores e administradores, desligada por omissão (também para quem já existe): só a recebe quem é responsável pela monitorização. Respeita o interruptor geral. A notificação na plataforma continua para todos.

  9. Novo

    Estado do e-mail: todos os envios, com os filtros das outras páginas

    A lista só mostrava os que falharam. Passa a mostrar todos os e-mails — enviados, com falha e pendentes — com o cartão de filtros das Tarefas SQL, das Conexões e dos Mapeamentos: pesquisa (destinatário, assunto, erro), estado, tipo de aviso e período (24 h, 7, 30 ou 90 dias), paginada. «Reenviar» e «Suspender» só nos que falharam.

  10. Novo

    O detalhe diz o que cada lote fez, não só quantos lotes houve

    Uma execução de 353 linhas em três lotes mostrava «3 / 3 lotes» e mais nada. As linhas e a duração por lote já eram guardadas desde o SEG-19 e o ecrã nunca as mostrou: quem queria saber qual dos lotes tinha feito o que tinha de ir a base de dados. O total de linhas só existia na coluna da lista, não no detalhe.

    Agora: 3 / 3 lotes · 353 linhas na linha dos lotes, e uma tabela por lote — lote, linhas, duração — com as mensagens do PRINT debaixo da linha a que pertencem.

    As duas estruturas que o commit anterior criou (mensagens_do_script e os lotes) passam a ser uma só, lotes: as mensagens são sobre o mesmo lote que as linhas, e separa-las era um conceito a mais. Continua no bloco de quem gere, porque são texto do servidor — um PRINT 'Erro: ' + ERROR_MESSAGE() pode citar o valor de uma chave duplicada.

    A nota de que o agente ainda não reporta mensagens mudou de lugar: fica debaixo da tabela, pequena, e só quando nenhum lote trouxe mensagens. Em cima de tudo fazia parecer que não havia nada para ver, quando havia.

    6 testes, incluindo o corte por papel (um utilizador normal não recebe lotes) e os três estados do mensagens_suportadas: direto, agente sem a capacidade e agente com ela.

  11. Novo

    O rollback da coluna agent_secundario_id já não larga um índice inexistente

    No SQL Server o foreignId()->constrained() cria só a chave estrangeira, sem índice; o down() tentava largar connections_agent_secundario_id_index e o migrate:rollback do job sqlserver do CI falhava.

  12. Novo

    Não mandar atualizar o agente para uma captura que não existe

    A mensagem dizia «O agente instalado neste cliente ainda não reporta as mensagens do script. Atualize-o para as ver aqui.» — e manda atualizar uma coisa que não tem para onde ser atualizada: a capacidade tarefas_sql_mensagens e lida em dois sítios e declarada em zero, porque a metade em Go esta por fazer. O agente mais recente que exista nunca a declara.

    Culpava a instalação do cliente por uma falha que e nossa, e manda quem lê perder a manha a confirmar versões. Foi o próprio aviso que desenhei para evitar confusão a criar confusão.

    Passa a dizer o que e verdade: as execuções pelo agente ainda não trazem as mensagens, falta a captura do lado dele, e nas conexões diretas aparecem. Fica a nota de que quem fizer a metade em Go muda esta frase no mesmo commit em que o agente passa a declarar a capacidade — ai, e só ai, «atualize» passa a ser verdade para quem tiver uma versão antiga.

  13. Novo

    Endereços suspensos, teto de e-mails por endereço e «Estado do e-mail»

    • Endereços suspensos (emails_suspensos): o admin marca um endereço, por exemplo um que não existe e cujas devoluções bloqueiam a conta de envio, e o LigarIT deixa de lhe enviar. O 2FA sai sempre. Marca «E-mail suspenso» nos Utilizadores e na lista de e-mails da empresa.
    • Teto de 10 e-mails por endereço por hora; acima disso só a notificação na plataforma. Sem teto: 2FA e confirmações.
    • Os dois em EmailDispatch::destinatariosPermitidos, por onde passam os avisos e os dois resumos que mandam direto.
    • /admin/emails: pausa, enviados e falhas em 24 h, a conta em uso (da configuração em cache), e-mail de teste que passa a pausa e a retoma, falhas dos últimos 7 dias com «Reenviar» e «Suspender», e os suspensos.
  14. Novo

    ARQ-30: agente secundario por conexao, para bases de dados em cluster

    Numa base de dados em cluster o SQL responde sempre no endereço do cluster, mas o agente vive num no. Quando esse no cai, o trabalho tem de passar para o agente do outro no — e para um escolhido, não para «o que bateu o coração mais recentemente», que numa empresa com três agentes manda o trabalho para a maquina errada.

    A substituição já existia no EscolhaDeAgente e nunca disparava: estava presa ao is_ativo, que só e escrito a true — no registo e no batimento — e nada no código o volta a por a false. Um agente cuja maquina morreu continuava is_ativo = true para sempre, devolvia-se o agente morto, o AgentJob ficava pending a espera de quem não volta a perguntar por ele, e morria no agentes:expirar-jobs. O estaOnline() já existia e já era o critério do ecrã e do agentes:vigiar (60 s, dois sinais falhados).

    connections.agent_secundario_id, com a mesma regra de empresa do principal e recusa de ser igual a ele — um secundário igual ao principal diz no ecrã que há redundância e não há para onde ir quando o no cair.

    Não duplica trabalho, e a razão não esta na escolha: o AgentJob nasce com agent_id fixo e nada reavalia um job já criado; o nextJob filtra por agent_id, logo o secundário não levanta o que e do principal; e a atribuição e condicional. Se o principal cair com um job atribuído, esse job não migra — expira e a execução e repetida pelas tentativas, que criam um job novo. Fica escrito no cabeçalho da classe e tem teste próprio.

    O batimento dos fixtures passou a segundos: com minutos não havia como escrever «este esta vivo e aquele esta mais fresco», que e o caso do primeiro teste.

    12 testes. Verificado que falham: a desfazer o estaOnline(), quatro ficam vermelhos.

  15. Novo

    OBS-28: o que o script escreveu com PRINT chega ao detalhe da execucao

    Uma importação correu pelo agente, fechou com sucesso, mostrou «5 / 5 lotes» e 0 linhas, e o PRINT 'Inserção em bo2 concluída com sucesso.' do script não aparecia em sitio nenhum. Quem a pediu não tinha como saber o que acontecera.

    Eram dois defeitos somados, e o segundo e o que fechava o caminho do agente:

    1. O correrLote devolvia so um inteiro. O PRINT e o RAISERROR de severidade ate 10 chegam ao driver como mensagens informativas (classe de estado 01), ao lado do resultado e nunca como exceção — e por isso ninguém tropeçava neles. Lidos agora a cada nextRowset() e não só no fim: o errorInfo e por instrução, e o que as anteriores escreveram perdia-se ao chegar a seguinte.
    2. O ResultadoDeScript::deArray tem uma lista fechada de chaves por lote, e mensagens nao estava nela. O que o agente mandasse morria ali em silencio.

    As mensagens vao no bloco de admin e supervisor, ao lado do erro_tecnico: um PRINT 'Erro: ' + ERROR_MESSAGE() traz texto do servidor, que pode citar o valor de uma chave duplicada ou o nome de uma coluna do cliente — e pela regra do OBS-08 isso não vai para um utilizador normal.

    Uma capacidade do agente (tarefas_sql_mensagens) para o ecrã dizer «este agente ainda não reporta as mensagens» em vez de mostrar um painel vazio. Sem ela, uma execução de um agente antigo e indistinguível de um script que não imprimiu nada — que era exatamente a confusão de origem.

    Tetos no que vem de fora: 50 mensagens por lote, 1000 caracteres cada, sem repetidas. O detalhe e uma coluna JSON.

    Fica por fazer a metade em Go: o agente tem de capturar o PRINT e devolve-lo em lotes[].mensagens, que a cloud já aceita e já mostra. Conjuntos de resultados (um SELECT no script) não são capturados de propósito — punham linhas de dados do cliente na base da plataforma.

    O «0 linhas» do caso real não era defeito nosso: o script tinha SET NOCOUNT ON, que faz o servidor deixar de enviar a contagem. Fica registado num teste para a próxima pessoa não o procurar no código.

    9 testes em SQLite e 5 no grupo sqlserver, que e o único sitio onde um PRINT existe de verdade — o dialeto de testes devolve sempre lista vazia.

  16. Novo

    Perfil:relatorio: rotas sem nome pelo caminho e N+1 sem erro

    Com route:cache, as rotas sem nome apareciam como generated::… e as que caíam no caminho separavam-se por id do job. Agrupam-se agora pelo caminho, com os ids trocados por {id}.

    A secção das queries repetidas juntava a lista inteira de SQL com a lista cortada a 10 e rebentava com "Trying to access array offset on null".

  17. Novo

    Notificações: abrir o mapeamento e o agente; builds do agente só para administradores

    • As notificações de um mapeamento levam a /mapear?abrir={id}, que abre o mapeamento, e não só à lista. A migração corrige as já guardadas pelo mapeamento_id (e agent_id) do data.
    • O aviso de agente em baixo abre o detalhe do agente (/agent-status/{id}) em vez de /agent-builds.
    • /agent-builds/* passa a ter o middleware admin; o download do build no estado dos agentes só aparece a administradores.
    • Eliminar uma notificação deixa de falhar em silêncio: mostra o erro e o código HTTP.
  18. Novo

    ARQ-03: classificar AgentEvento como modelo que chega à empresa pelo agente

  19. Novo

    Tarefas SQL: avançar a corrida depois do commit do resultado

    Dentro da transação do submitResult, o fecho bloqueava a execução e depois a corrida, e quem avança uma corrida bloqueia a corrida e depois lê as execuções. A ordem cruzada dava deadlocks em série: 3 117 com 100 agentes no teste de carga de 19/10/2026, e 49 resultados perdidos.

    O AvancarCorridaDeTarefaSql passa a correr depois do commit (no mesmo pedido, sem fila). Se falhar, o resultado já está gravado e o varrimento avança a corrida (corridasSemDecisao).

    O varrimento feito ao pedir uma execução decide logo a corrida da execução que expira, para a tarefa ficar livre na mesma transação.

  20. Novo

    Recursos e disponibilidade dos agentes, e instalador com serviço

    • Heartbeat: guarda os recursos da máquina (agents.recursos) e uma amostra de 15 em 15 min (agent_amostras, 30 dias). Os valores são lidos à mão: um valor estranho não recusa o heartbeat.
    • Página do agente: disponibilidade a 7 e 30 dias (DisponibilidadeDoAgente, pelos eventos sem sinal / voltou), recursos com destaque para disco e memória baixos, evolução em SVG e jobs por tipo.
    • logs:cleanup apaga as amostras com mais de 30 dias e os jobs dos agentes terminados com mais de 90.
    • Estado dos agentes: aviso nos que não correm como serviço.
    • Instalador (BuildAgent): instala o serviço (-install), para o agente da pasta com o binário novo antes de copiar (-parar), desinstala só o desta pasta, e o ícone corre como o utilizador. check_interval de 1 h.