package router import ( "fmt" "strings" "github.com/treytartt/honeydue-api/internal/monitoring" ) // CorsOrigins returns the CORS allowed origins based on debug mode. func CorsOrigins(debug bool, configuredOrigins []string) []string { if debug { return []string{ "http://localhost:3000", "http://localhost:3001", "http://localhost:8080", "http://localhost:8000", "http://127.0.0.1:3000", "http://127.0.0.1:3001", "http://127.0.0.1:8080", "http://127.0.0.1:8000", } } if len(configuredOrigins) > 0 { return configuredOrigins } return []string{ "https://api.myhoneydue.com", "https://myhoneydue.com", "https://admin.myhoneydue.com", } } // ShouldSkipTimeout returns true for paths that should bypass timeout middleware. func ShouldSkipTimeout(path, host, adminHost string) bool { return (adminHost != "" && host == adminHost) || strings.HasPrefix(path, "/_next") || strings.HasSuffix(path, "/ws") } // ShouldSkipBodyLimit returns true for webhook endpoints. func ShouldSkipBodyLimit(path string) bool { return strings.HasPrefix(path, "/api/subscription/webhook") } // ShouldSkipGzip returns true for media endpoints. func ShouldSkipGzip(path string) bool { return strings.HasPrefix(path, "/api/media/") } // ParseEndpoint splits "GET /api/foo" into method and path. func ParseEndpoint(endpoint string) (method, path string) { parts := strings.SplitN(endpoint, " ", 2) if len(parts) == 2 { return parts[0], parts[1] } return endpoint, "" } // AllowedProxyHosts builds the list of hosts allowed for admin proxy. func AllowedProxyHosts(adminHost string) []string { var hosts []string if adminHost != "" { hosts = append(hosts, adminHost) } hosts = append(hosts, "localhost:3001", "127.0.0.1:3001", "localhost:8000", "127.0.0.1:8000") return hosts } // DetermineAdminRoute decides how to route admin subdomain requests. func DetermineAdminRoute(path string) string { if strings.HasPrefix(path, "/admin") { return "redirect" } if strings.HasPrefix(path, "/api/") { return "passthrough" } return "proxy" } // FormatPrometheusMetrics converts HTTP stats to Prometheus text format. func FormatPrometheusMetrics(stats monitoring.HTTPStats) string { var b strings.Builder b.WriteString("# HELP http_requests_total Total number of HTTP requests.\n") b.WriteString("# TYPE http_requests_total counter\n") for statusCode, count := range stats.ByStatusCode { fmt.Fprintf(&b, "http_requests_total{status_code=\"%d\"} %d\n", statusCode, count) } b.WriteString("# HELP http_endpoint_requests_total Total requests per endpoint.\n") b.WriteString("# TYPE http_endpoint_requests_total counter\n") for endpoint, epStats := range stats.ByEndpoint { method, path := ParseEndpoint(endpoint) fmt.Fprintf(&b, "http_endpoint_requests_total{method=\"%s\",path=\"%s\"} %d\n", method, path, epStats.Count) } b.WriteString("# HELP http_request_duration_ms Average request duration in milliseconds per endpoint.\n") b.WriteString("# TYPE http_request_duration_ms gauge\n") for endpoint, epStats := range stats.ByEndpoint { method, path := ParseEndpoint(endpoint) fmt.Fprintf(&b, "http_request_duration_ms{method=\"%s\",path=\"%s\",quantile=\"avg\"} %.2f\n", method, path, epStats.AvgLatencyMs) fmt.Fprintf(&b, "http_request_duration_ms{method=\"%s\",path=\"%s\",quantile=\"p95\"} %.2f\n", method, path, epStats.P95LatencyMs) } b.WriteString("# HELP http_error_rate Overall error rate (4xx+5xx / total).\n") b.WriteString("# TYPE http_error_rate gauge\n") fmt.Fprintf(&b, "http_error_rate %.4f\n", stats.ErrorRate) b.WriteString("# HELP http_requests_per_minute Current request rate.\n") b.WriteString("# TYPE http_requests_per_minute gauge\n") fmt.Fprintf(&b, "http_requests_per_minute %.2f\n", stats.RequestsPerMinute) return b.String() }