diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e69de29 diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go index 00b9c0e..5414555 100644 --- a/cmd/gateway/main.go +++ b/cmd/gateway/main.go @@ -78,8 +78,9 @@ func loadConfig(path string) (*Config, error) { for i := range config.Services { if envVar, ok := serviceURLEnvMap[config.Services[i].Name]; ok { - if url := os.Getenv(envVar); url != "" { - config.Services[i].TargetURL = url + // avoid shadowing the imported "url" package by naming the env var value envURL + if envURL := os.Getenv(envVar); envURL != "" { + config.Services[i].TargetURL = envURL } } } @@ -102,6 +103,12 @@ func newProxy(targetURL, stripPrefix string) (*httputil.ReverseProxy, error) { if stripPrefix != "" { req.URL.Path = strings.TrimPrefix(req.URL.Path, stripPrefix) } + logger.Info("proxying request", + "method", req.Method, + "original_path", req.URL.Path, + "target_host", target.Host, + "headers", req.Header, + ) } proxy.ModifyResponse = func(resp *http.Response) error { @@ -113,6 +120,16 @@ func newProxy(targetURL, stripPrefix string) (*httputil.ReverseProxy, error) { return nil } + proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { + logger.Error("proxy error", + "target", targetURL, + "error", err, + "path", r.URL.Path, + "method", r.Method, + ) + http.Error(w, "Bad Gateway: "+err.Error(), http.StatusBadGateway) + } + return proxy, nil } @@ -213,7 +230,9 @@ func main() { router.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte("OK")) + if _, err := w.Write([]byte("OK")); err != nil { + logger.Warn("Failed to write health response", "error", err) + } }) authMW := authMiddleware([]byte(config.JWTSecret)) @@ -227,13 +246,20 @@ func main() { handler := http.Handler(proxy) - router.Group(func(r chi.Router) { - if service.AuthRequired { + // Create a sub-router for each service with proper authentication + pathPrefix := strings.TrimSuffix(service.PathPrefix, "/") + + if service.AuthRequired { + router.Group(func(r chi.Router) { r.Use(authMW) r.Use(injectUserInfo) - } - r.Handle(service.PathPrefix+"*", handler) - }) + r.Handle(pathPrefix+"/*", handler) + r.Handle(pathPrefix, handler) + }) + } else { + router.Handle(pathPrefix+"/*", handler) + router.Handle(pathPrefix, handler) + } logger.Info("Registered service", "name", service.Name, "prefix", service.PathPrefix, "target", service.TargetURL) } diff --git a/config.yaml b/config.yaml index 6063dd4..f0c779b 100644 --- a/config.yaml +++ b/config.yaml @@ -10,67 +10,67 @@ services: - name: "auth" path_prefix: "/api/v1/auth/" target_url: "http://localhost:8081" - strip_prefix: "/api/v1/auth" + strip_prefix: "/api/v1" auth_required: false # Login/Register must be public - name: "users" path_prefix: "/api/v1/users/" target_url: "http://localhost:8081" # Also points to the auth service - strip_prefix: "/api/v1/users" + strip_prefix: "/api/v1" auth_required: true # --- Vehicle Service (Port 8082) --- - name: "vehicles" path_prefix: "/api/v1/vehicles/" target_url: "http://localhost:8082" - strip_prefix: "/api/v1/vehicles" + strip_prefix: "/api/v1" auth_required: true # --- Appointment & Scheduling Service (Port 8083) --- - name: "appointments" path_prefix: "/api/v1/appointments/" target_url: "http://localhost:8083" - strip_prefix: "/api/v1/appointments" + strip_prefix: "/api/v1" auth_required: true # --- Service & Project Management Service (Port 8084) --- - name: "services" path_prefix: "/api/v1/services/" target_url: "http://localhost:8084" - strip_prefix: "/api/v1/services" + strip_prefix: "/api/v1" auth_required: true - name: "projects" path_prefix: "/api/v1/projects/" target_url: "http://localhost:8084" # Also points to the project service - strip_prefix: "/api/v1/projects" + strip_prefix: "/api/v1" auth_required: true # --- Time Logging Service (Port 8085) --- - name: "time-logs" path_prefix: "/api/v1/time-logs/" target_url: "http://localhost:8085" - strip_prefix: "/api/v1/time-logs" + strip_prefix: "/api/v1" auth_required: true # --- Payment & Billing Service (Port 8086) --- - name: "payments" path_prefix: "/api/v1/payments/" target_url: "http://localhost:8086" - strip_prefix: "/api/v1/payments" + strip_prefix: "/api/v1" auth_required: true - name: "invoices" path_prefix: "/api/v1/invoices/" target_url: "http://localhost:8086" # Also points to the payment service - strip_prefix: "/api/v1/invoices" + strip_prefix: "/api/v1" auth_required: true # --- Admin & Reporting Service (Port 8087) --- - name: "admin" path_prefix: "/api/v1/admin/" target_url: "http://localhost:8087" - strip_prefix: "/api/v1/admin" + strip_prefix: "/api/v1" auth_required: true # --- Bonus Features (For Later Implementation) --- @@ -83,5 +83,5 @@ services: - name: "ai" path_prefix: "/api/v1/ai/" target_url: "http://localhost:8089" # Assuming a future port for the FastAPI service - strip_prefix: "/api/v1/ai" + strip_prefix: "/api/v1" auth_required: true \ No newline at end of file