fix: bug sistêmico no cliente HTTP quebrava toda ação sem payload

Causa raiz do 'body is empty' relatado pelo usuário ao clicar em
redefinir senha do ramal: o cliente sempre mandava Content-Type:
application/json mesmo em requisições sem corpo (POST/PATCH de ação, ex.:
reset-password, agent-console available/pause/unpause/logout, dialplan
publish/rollback, campanha start/pause/stop/drain). O Fastify rejeita
isso com 400 'Body cannot be empty when content-type is set to
application/json' — nunca detectado antes porque todo teste anterior foi
via curl sem fixar esse header, não reproduzindo o comportamento real do
fetch() do navegador. Corrigido na única função request() central
(apps/frontend/src/lib/api-client.ts): só envia o header quando há de
fato um corpo.

Bug relacionado encontrado na mesma revisão: DELETE /api/suppression/:id
exige removalReason no corpo, mas api.delete() nem aceitava um argumento
de corpo — o botão 'Desbloquear' da lista de bloqueio sempre falhava com
403. api.delete() agora aceita body opcional; a tela pede o motivo via
prompt antes de remover.
This commit is contained in:
2026-08-27 20:48:00 -03:00
parent 71555af5da
commit fa8339003d
3 changed files with 16 additions and 5 deletions

View File

@@ -95,7 +95,8 @@ function SuppressionContent() {
});
const remove = useMutation({
mutationFn: (id: string) => suppressionService.remove(id),
mutationFn: ({ id, removalReason }: { id: string; removalReason: string }) =>
suppressionService.remove(id, removalReason),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['suppression'] });
toast({ title: 'Número desbloqueado', variant: 'success' });
@@ -131,7 +132,12 @@ function SuppressionContent() {
variant="ghost"
className="text-destructive"
onClick={() => {
if (confirm(`Desbloquear ${r.phoneNormalized}?`)) remove.mutate(r.id);
const removalReason = window.prompt(
`Motivo para desbloquear ${r.phoneNormalized} (obrigatório):`,
);
if (removalReason && removalReason.trim().length > 0) {
remove.mutate({ id: r.id, removalReason: removalReason.trim() });
}
}}
>
<Trash2 className="size-4" />