FloorVisualizer/internal/handler/router.go
dindang 94895cbc22 Initial commit: FloorVisualizer backend
- Go API server with PostgreSQL + Redis
- AI floor replacement (OpenRouter Gemini)
- Product database (10 brands, 3539 products)
- Recommendation engine, calculator, articles
- Redis async queue + worker pool
- Hot product caching, brand view tracking
- JWT auth, favorites, projects
- Docker deployment ready

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-16 09:47:11 +08:00

99 lines
3.1 KiB
Go

package handler
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"net/url"
"floorvisualizer/internal/middleware"
"floorvisualizer/internal/openrouter"
"floorvisualizer/internal/service"
)
func NewProxyClient(proxyURLStr string) *http.Client {
u, _ := url.Parse(proxyURLStr)
if u == nil {
return http.DefaultClient
}
return &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(u)}}
}
func RegisterAll(mux *http.ServeMux, client *openrouter.Client, db *sql.DB, authSvc *service.AuthService) {
requireAuth := middleware.RequireAuth(authSvc)
// Auth
mux.HandleFunc("/auth/register", handleRegister(authSvc, db))
mux.HandleFunc("/auth/login", handleLogin(authSvc, db))
// User (protected)
mux.HandleFunc("/user/me", requireAuth(handleMe(db)))
mux.HandleFunc("/user/avatar", requireAuth(handleUploadAvatar(db)))
mux.HandleFunc("/user/favorites", requireAuth(handleFavorites(db)))
mux.HandleFunc("/user/favorites/", requireAuth(handleFavorites(db)))
mux.HandleFunc("/user/projects", requireAuth(handleProjects(db)))
mux.HandleFunc("/user/projects/", requireAuth(handleProjects(db)))
mux.HandleFunc("/user/frequent-brands", requireAuth(handleFrequentBrands(db)))
// Upload
mux.HandleFunc("/healthz", handleHealthz)
mux.HandleFunc("/upload", handleUpload)
// Products
mux.HandleFunc("/products", handleProducts(db))
mux.HandleFunc("/products/", handleProducts(db))
mux.HandleFunc("/product-options", handleProductOptions(db))
mux.HandleFunc("/filter-options", handleFilterOptions(db))
// Recommend
mux.HandleFunc("/recommend/api", handleRecommend(db))
// Calculator
mux.HandleFunc("/calculator/calc", handleCalc)
// Floor AI
mux.HandleFunc("/floor/generate", handleFloorGenerate(client, db))
mux.HandleFunc("/floor/status", handleFloorStatus)
mux.HandleFunc("/floor/options", handleFloorOptions(db))
// Articles (recommend before /articles/ to avoid path conflict)
mux.HandleFunc("/articles/recommend", handleArticleRecommend(db))
mux.HandleFunc("/articles", handleArticles(db))
mux.HandleFunc("/articles/", handleArticles(db))
// Static
mux.Handle("/outputs/", http.StripPrefix("/outputs/", http.FileServer(http.Dir("./outputs"))))
mux.Handle("/uploads/", http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads"))))
mux.Handle("/knowledge_images/", http.StripPrefix("/knowledge_images/", http.FileServer(http.Dir("./knowledge_images"))))
mux.Handle("/images/knowledge/", http.StripPrefix("/images/knowledge/", http.FileServer(http.Dir("./knowledge_images"))))
}
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
if status >= 400 {
msg := "Unknown error"
switch x := v.(type) {
case string:
msg = x
case map[string]string:
if e, ok := x["error"]; ok {
msg = e
}
case map[string]any:
if e, ok := x["error"]; ok {
msg = fmt.Sprint(e)
}
}
w.WriteHeader(status)
json.NewEncoder(w).Encode(map[string]any{"code": status, "error": msg})
return
}
w.WriteHeader(status)
json.NewEncoder(w).Encode(map[string]any{"code": status, "data": v})
}
func writeErr(w http.ResponseWriter, status int, msg string) {
writeJSON(w, status, msg)
}