Skip to content
This repository was archived by the owner on Nov 23, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .gitattributes
Empty file.
42 changes: 34 additions & 8 deletions cmd/gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Expand All @@ -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,
)
Comment on lines +106 to +111
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove sensitive header logging

Dumping the entire req.Header leaks bearer tokens and other sensitive credentials to the gateway logs, creating a serious security and compliance exposure. Please drop the header dump (or at least explicitly whitelist non-sensitive headers) before we ship.

-		logger.Info("proxying request",
-			"method", req.Method,
-			"original_path", req.URL.Path,
-			"target_host", target.Host,
-			"headers", req.Header,
-		)
+		logger.Info("proxying request",
+			"method", req.Method,
+			"original_path", req.URL.Path,
+			"target_host", target.Host,
+		)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.Info("proxying request",
"method", req.Method,
"original_path", req.URL.Path,
"target_host", target.Host,
"headers", req.Header,
)
logger.Info("proxying request",
"method", req.Method,
"original_path", req.URL.Path,
"target_host", target.Host,
)
🤖 Prompt for AI Agents
In cmd/gateway/main.go around lines 106 to 111, the logger currently dumps the
entire req.Header which can leak sensitive values (bearer tokens, cookies,
etc.); remove the req.Header dump and instead log only a safe, explicit
whitelist of headers (for example: Content-Type, Accept, User-Agent,
X-Request-ID) or build a filtered copy that redacts/omits any Authorization,
Cookie, Set-Cookie, and other sensitive headers before logging; implement the
whitelist/filtering when calling logger.Info so no raw headers are emitted.

}

proxy.ModifyResponse = func(resp *http.Response) error {
Expand All @@ -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
}

Expand Down Expand Up @@ -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))
Expand All @@ -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)
}
Expand Down
22 changes: 11 additions & 11 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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) ---
Expand All @@ -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
Loading