package main import ( "fmt" "io" "net/http" "os" "path" "strings" "git.secnex.io/secnex/masterlog" ) type Config struct { Port string Url string } func main() { config := loadConfig() http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { proxyHandler(w, r, config) }) masterlog.Info("Starting server", map[string]interface{}{ "port": config.Port, "url": config.Url, }) if err := http.ListenAndServe(config.Port, nil); err != nil { masterlog.Error("Error starting server", map[string]interface{}{"error": err}) os.Exit(1) } } func loadConfig() *Config { config := &Config{ Url: getEnv("URL", "https://git.secnex.io/secnex/%s/raw/branch/main/%s"), Port: getEnv("PORT", ":8080"), } return config } func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } func proxyHandler(w http.ResponseWriter, r *http.Request, config *Config) { // Only handle GET requests if r.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // Extract repository and file path from URL // Expected format: /repositoryName/path/to/file cleanPath := path.Clean(r.URL.Path) if cleanPath == "/" || cleanPath == "" { http.Error(w, "Repository name required", http.StatusBadRequest) return } pathParts := strings.Split(strings.TrimPrefix(cleanPath, "/"), "/") if len(pathParts) < 1 { http.Error(w, "Invalid path format", http.StatusBadRequest) return } repository := pathParts[0] filePath := strings.Join(pathParts[1:], "/") // Log the incoming request masterlog.Info("Incoming request", map[string]interface{}{ "method": r.Method, "repository": repository, "file_path": filePath, "original_path": r.URL.Path, }) // Build target URL based on server type var targetURL string targetURL = fmt.Sprintf(config.Url, repository, filePath) // Create HTTP client with timeout client := &http.Client{} req, err := http.NewRequestWithContext(r.Context(), http.MethodGet, targetURL, nil) if err != nil { masterlog.Error("Error creating request", map[string]interface{}{"error": err}) http.Error(w, "Failed to create request", http.StatusInternalServerError) return } // Copy headers from original request for key, values := range r.Header { // Skip certain headers that shouldn't be forwarded if strings.ToLower(key) == "host" { continue } for _, value := range values { req.Header.Add(key, value) } } resp, err := client.Do(req) if err != nil { masterlog.Error("Error fetching target URL", map[string]interface{}{"error": err, "url": targetURL}) http.Error(w, "Not Found", http.StatusNotFound) return } defer resp.Body.Close() // Check if response is successful if resp.StatusCode != http.StatusOK { masterlog.Info("Target returned non-200 status", map[string]interface{}{ "url": targetURL, "status_code": resp.StatusCode, }) http.Error(w, "Not Found", http.StatusNotFound) return } // Copy headers from target response for key, values := range resp.Header { for _, value := range values { w.Header().Add(key, value) } } // Set status code to 200 w.WriteHeader(http.StatusOK) // Copy the response body _, err = io.Copy(w, resp.Body) if err != nil { masterlog.Error("Error copying response", map[string]interface{}{"error": err}) // Can't send error response after headers have been written return } masterlog.Info("Successfully proxied request", map[string]interface{}{ "url": targetURL, "status_code": http.StatusOK, "content_length": resp.ContentLength, }) }