Files
SimpleFinanceDash/backend/internal/api/router.go
Jean-Luc Makiola b42f7b13bb feat(07-01): add quick-add HTTP handlers and route registration
- Add ListQuickAddItems, CreateQuickAddItem, UpdateQuickAddItem, DeleteQuickAddItem handler methods
- Register GET/POST /api/quick-add and PUT/DELETE /api/quick-add/{itemId} in authenticated route group
- Validate name non-empty on create and update; return 404 on update when item not found
2026-03-12 13:34:19 +01:00

101 lines
2.7 KiB
Go

package api
import (
"io/fs"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"simplefinancedash/backend/internal/auth"
"simplefinancedash/backend/internal/db"
)
func NewRouter(queries *db.Queries, sessionSecret string, frontendFS fs.FS) http.Handler {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Compress(5))
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"http://localhost:5173", "http://localhost:8080"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Content-Type"},
AllowCredentials: true,
}))
h := NewHandlers(queries, sessionSecret)
// Auth routes (no auth required)
r.Route("/api/auth", func(r chi.Router) {
r.Post("/register", h.Register)
r.Post("/login", h.Login)
r.Post("/logout", h.Logout)
r.Get("/me", h.Me)
r.Get("/oidc", h.OIDCStart)
r.Get("/oidc/callback", h.OIDCCallback)
})
// Protected routes
r.Group(func(r chi.Router) {
r.Use(auth.Middleware(sessionSecret))
r.Route("/api/categories", func(r chi.Router) {
r.Get("/", h.ListCategories)
r.Post("/", h.CreateCategory)
r.Put("/{id}", h.UpdateCategory)
r.Delete("/{id}", h.DeleteCategory)
})
r.Route("/api/budgets", func(r chi.Router) {
r.Get("/", h.ListBudgets)
r.Post("/", h.CreateBudget)
r.Post("/generate", h.GenerateBudget)
r.Get("/{id}", h.GetBudget)
r.Put("/{id}", h.UpdateBudget)
r.Delete("/{id}", h.DeleteBudget)
r.Post("/{id}/copy-from/{srcId}", h.CopyBudgetItems)
r.Post("/{id}/items", h.CreateBudgetItem)
r.Put("/{id}/items/{itemId}", h.UpdateBudgetItem)
r.Delete("/{id}/items/{itemId}", h.DeleteBudgetItem)
})
r.Route("/api/template", func(r chi.Router) {
r.Get("/", h.GetTemplate)
r.Put("/", h.UpdateTemplateName)
r.Post("/items", h.CreateTemplateItem)
r.Put("/items/reorder", h.ReorderTemplateItems)
r.Put("/items/{itemId}", h.UpdateTemplateItem)
r.Delete("/items/{itemId}", h.DeleteTemplateItem)
})
r.Route("/api/quick-add", func(r chi.Router) {
r.Get("/", h.ListQuickAddItems)
r.Post("/", h.CreateQuickAddItem)
r.Put("/{itemId}", h.UpdateQuickAddItem)
r.Delete("/{itemId}", h.DeleteQuickAddItem)
})
r.Get("/api/settings", h.GetSettings)
r.Put("/api/settings", h.UpdateSettings)
})
// Serve SPA for all non-API routes
spaHandler := http.FileServer(http.FS(frontendFS))
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
// Try to serve the file directly first
f, err := frontendFS.Open(r.URL.Path[1:]) // strip leading /
if err == nil {
f.Close()
spaHandler.ServeHTTP(w, r)
return
}
// Fall back to index.html for SPA routing
r.URL.Path = "/"
spaHandler.ServeHTTP(w, r)
})
return r
}