refatoração de segurança e logs

This commit is contained in:
Luiz Silva 2026-01-01 19:13:24 -03:00
parent 6873b87a85
commit 663a8d5bf2
12 changed files with 362 additions and 37 deletions

View file

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"log/slog"
"net/http"
"os"
"os/signal"
@ -20,8 +21,13 @@ import (
)
func main() {
// Load .env if present (convenience for local dev). Environment variables
// explicitly set in the OS take precedence.
// Logger estruturado (stdout). Mantemos log.Printf apenas para logs muito
// iniciais/críticos e para compatibilidade; o restante deve usar slog.
logger := elinps.LoggerPadrao()
slog.SetDefault(logger)
// Carrega .env se existir (conveniência para dev local).
// Variáveis definidas no SO têm precedência.
_ = godotenv.Load()
cfg := mustLoadConfig()
@ -35,7 +41,7 @@ func main() {
}
defer pool.Close()
// Ensures required extensions exist.
// Garante extensões necessárias.
if err := db.EnsurePgcrypto(ctx, pool); err != nil {
log.Fatalf("ensure pgcrypto: %v", err)
}
@ -44,23 +50,24 @@ func main() {
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Recoverer)
r.Use(elinps.MiddlewareLogRequisicao(logger))
r.Use(middleware.Timeout(15 * time.Second))
r.Use(middleware.Compress(5))
// CORS wildcard + preflight
// CORS liberado + preflight.
r.Use(elinps.CORSMiddleware())
// Basic limits
// Limites básicos.
r.Use(elinps.MaxBodyBytesMiddleware(64 * 1024))
// Health
// Health.
r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
// Home: renderiza README.md
// Público (sem senha), para facilitar documentação do serviço.
r.Get("/", elinps.NewReadmePage("README.md").ServeHTTP)
// Static widget
// Widget estático.
fileServer := http.FileServer(http.Dir("web/static"))
// Versão do widget para controle de cache.
//
@ -87,12 +94,12 @@ func main() {
})
r.Handle("/*", http.StripPrefix("/static/", fileServer))
})
// Convenience: allow /teste.html
// Conveniência: permitir /teste.html
r.Get("/teste.html", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "web/static/teste.html")
})
// NPS routes
// Rotas NPS.
h := elinps.NewHandlers(pool)
r.Route("/api/e-li.nps", func(r chi.Router) {
r.Post("/pedido", h.PostPedido)
@ -103,7 +110,7 @@ func main() {
r.Get("/{produto}/{id}/form", h.GetForm)
})
// Painel (dashboard)
// Painel.
// Protegido por SENHA_PAINEL.
// Se SENHA_PAINEL estiver vazia, o painel fica desabilitado.
painel := elinps.NewPainelHandlers(pool, cfg.SenhaPainel)
@ -116,7 +123,7 @@ func main() {
}
go func() {
log.Printf("listening on %s", cfg.Addr)
logger.Info("servidor_iniciado", "addr", cfg.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %v", err)
}
@ -126,9 +133,9 @@ func main() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
log.Printf("shutdown: %v", err)
logger.Error("erro_no_shutdown", "err", err)
}
log.Printf("bye")
logger.Info("servidor_finalizado")
}
type config struct {