Files
carvalho-finances/apps/api/cmd/server/main.go
T
Mlcavalho1andClaude Sonnet 4.6 d6782d51a5 feat(#17): CRUD de categorias — API Go + tela Vue + testes unitários
API REST /api/categories (GET/POST/PUT/DELETE) com regras de negócio:
categorias padrão não podem ser excluídas; categorias com transações vinculadas
também bloqueadas. Tela Vue com formulário inline de criação/edição, indicador
visual de categorias padrão e botão de exclusão condicional.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-26 20:11:54 -03:00

90 lines
2.1 KiB
Go

package main
import (
"context"
"fmt"
"io/fs"
"log"
"net/http"
"os"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/joho/godotenv"
"financeiro-carvalho/internal/db"
"financeiro-carvalho/internal/handler"
"financeiro-carvalho/internal/migration"
"financeiro-carvalho/internal/repository"
"financeiro-carvalho/internal/service"
"financeiro-carvalho/static"
)
func main() {
_ = godotenv.Load()
dsn := os.Getenv("DATABASE_URL")
if dsn == "" {
log.Fatal("DATABASE_URL is required")
}
ctx := context.Background()
pool, err := db.Connect(ctx, dsn)
if err != nil {
log.Fatalf("db connect: %v", err)
}
defer pool.Close()
if err := migration.Run(ctx, pool); err != nil {
log.Fatalf("migration: %v", err)
}
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
categoryRepo := repository.NewCategoryRepository(pool)
categorySvc := service.NewCategoryService(categoryRepo)
categoryHandler := handler.NewCategoryHandler(categorySvc)
r.Get("/health", handler.Health)
r.Route("/api", func(r chi.Router) {
r.Get("/categories", categoryHandler.List)
r.Post("/categories", categoryHandler.Create)
r.Put("/categories/{id}", categoryHandler.Update)
r.Delete("/categories/{id}", categoryHandler.Delete)
})
// Serve Vue SPA — non-API routes fall through to index.html
distFS, err := fs.Sub(static.FS, "dist")
if err != nil {
log.Fatalf("static embed sub: %v", err)
}
fileServer := http.FileServer(http.FS(distFS))
r.Get("/*", func(w http.ResponseWriter, req *http.Request) {
path := req.URL.Path[1:]
if _, statErr := fs.Stat(distFS, path); statErr != nil {
index, readErr := fs.ReadFile(distFS, "index.html")
if readErr != nil {
http.NotFound(w, req)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write(index)
return
}
fileServer.ServeHTTP(w, req)
})
fmt.Printf("server listening on :%s\n", port)
if err := http.ListenAndServe(":"+port, r); err != nil {
log.Fatal(err)
}
}