refatoração de segurança e logs
This commit is contained in:
parent
6873b87a85
commit
663a8d5bf2
12 changed files with 362 additions and 37 deletions
|
|
@ -3,6 +3,7 @@ package elinps
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
|
@ -38,21 +39,22 @@ func (h *Handlers) PostPedido(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Ensure per-product table exists (also normalizes produto).
|
||||
// Garante a tabela do produto (e normaliza o identificador técnico).
|
||||
table, err := h.store.EnsureTableForProduto(ctx, in.ProdutoNome)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": "produto_invalido"})
|
||||
return
|
||||
}
|
||||
|
||||
// Keep normalized form for the widget to build URLs safely.
|
||||
// Mantemos a forma normalizada para o widget montar URLs com segurança.
|
||||
// table = "nps_" + produto_normalizado
|
||||
produtoNormalizado := strings.TrimPrefix(table, "nps_")
|
||||
|
||||
// Rules
|
||||
// Regras.
|
||||
respRecente, err := h.store.HasRespostaValidaRecente(ctx, table, in.InquilinoCodigo, in.UsuarioCodigo)
|
||||
if err != nil {
|
||||
// Fail-closed
|
||||
slog.Error("erro ao checar resposta recente", "err", err)
|
||||
// Fail-closed.
|
||||
writeJSON(w, http.StatusOK, PedidoResponse{PodeAbrir: false, Motivo: "erro"})
|
||||
return
|
||||
}
|
||||
|
|
@ -63,6 +65,7 @@ func (h *Handlers) PostPedido(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
pedidoAberto, err := h.store.HasPedidoEmAbertoRecente(ctx, table, in.InquilinoCodigo, in.UsuarioCodigo)
|
||||
if err != nil {
|
||||
slog.Error("erro ao checar pedido em aberto", "err", err)
|
||||
writeJSON(w, http.StatusOK, PedidoResponse{PodeAbrir: false, Motivo: "erro"})
|
||||
return
|
||||
}
|
||||
|
|
@ -73,6 +76,7 @@ func (h *Handlers) PostPedido(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
id, err := h.store.CreatePedido(ctx, table, in, r)
|
||||
if err != nil {
|
||||
slog.Error("erro ao criar pedido", "err", err)
|
||||
writeJSON(w, http.StatusOK, PedidoResponse{PodeAbrir: false, Motivo: "erro"})
|
||||
return
|
||||
}
|
||||
|
|
@ -85,7 +89,7 @@ func (h *Handlers) PatchResposta(w http.ResponseWriter, r *http.Request) {
|
|||
produtoParam := chi.URLParam(r, "produto")
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
// produtoParam already in path; sanitize again.
|
||||
// produtoParam já está no path; sanitizamos novamente por segurança.
|
||||
prod, err := db.NormalizeProduto(produtoParam)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"error": "produto_invalido"})
|
||||
|
|
@ -93,6 +97,7 @@ func (h *Handlers) PatchResposta(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
table := db.TableNameForProduto(prod)
|
||||
if err := db.EnsureNPSTable(ctx, h.store.pool, table); err != nil {
|
||||
slog.Error("erro ao garantir tabela", "err", err)
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "db"})
|
||||
return
|
||||
}
|
||||
|
|
@ -113,14 +118,16 @@ func (h *Handlers) PatchResposta(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if err := h.store.PatchRegistro(ctx, table, id, in); err != nil {
|
||||
slog.Error("erro ao atualizar registro", "err", err)
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "db"})
|
||||
return
|
||||
}
|
||||
|
||||
// If called via HTMX, respond with refreshed HTML fragment.
|
||||
// Se chamado via HTMX, respondemos com fragmento HTML atualizado.
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
reg, err := h.store.GetRegistro(ctx, table, id)
|
||||
if err != nil {
|
||||
slog.Error("erro ao buscar registro", "err", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("db"))
|
||||
return
|
||||
|
|
@ -147,6 +154,7 @@ func (h *Handlers) GetForm(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
table := db.TableNameForProduto(prod)
|
||||
if err := db.EnsureNPSTable(ctx, h.store.pool, table); err != nil {
|
||||
slog.Error("erro ao garantir tabela", "err", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("db"))
|
||||
return
|
||||
|
|
@ -159,6 +167,7 @@ func (h *Handlers) GetForm(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write([]byte("nao encontrado"))
|
||||
return
|
||||
}
|
||||
slog.Error("erro ao buscar registro", "err", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("db"))
|
||||
return
|
||||
|
|
@ -170,8 +179,8 @@ func (h *Handlers) GetForm(w http.ResponseWriter, r *http.Request) {
|
|||
Reg: reg,
|
||||
}
|
||||
|
||||
// Always return a standalone HTML page so the widget can use iframe.
|
||||
// But the inner container is also HTMX-friendly (it swaps itself).
|
||||
// Sempre retornamos uma página HTML completa para o widget usar iframe.
|
||||
// Porém o container interno também é compatível com HTMX (swap de si mesmo).
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
h.tpl.Render(w, "form_page.html", data)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue