package main import ( "bytes" "fmt" "net/url" "os" "path/filepath" "strings" "text/template" ) const nginxTemplate = `server { listen {{.ListenPort}}; listen [::]:{{.ListenPort}}; # Replace with your actual domain(s) server_name {{.MonitoringHost}}; # Document root for static files root {{.LocalDirectory}}; location = / { try_files /index.html =404; add_header Content-Type "text/html; charset=utf-8"; add_header Access-Control-Allow-Origin "*"; } # Checkpoint endpoint - no caching location = /checkpoint { try_files /checkpoint =404; add_header Content-Type "text/plain; charset=utf-8"; add_header Access-Control-Allow-Origin "*"; add_header Cache-Control "no-store"; } # Log info endpoint location = /log.v3.json { try_files /log.v3.json =404; add_header Content-Type "application/json"; add_header Access-Control-Allow-Origin "*"; add_header Cache-Control "public, max-age=3600, immutable"; } # Issuer certificate endpoint - long cache location ~ ^/issuer/(.+)$ { try_files /issuer/$1 =404; add_header Content-Type "application/pkix-cert"; add_header Access-Control-Allow-Origin "*"; add_header Cache-Control "public, max-age=604800, immutable"; } # Tile data endpoint - long cache, may have gzip location ~ ^/tile/(.+)$ { try_files /tile/$1 =404; add_header Content-Type "application/octet-stream"; add_header Access-Control-Allow-Origin "*"; add_header Cache-Control "public, max-age=604800, immutable"; # Gzip encoding for .gz files location ~ \.gz$ { add_header Content-Encoding "gzip"; } } } ` // NginxTemplateData contains the data needed to generate nginx configuration files type NginxTemplateData struct { MonitoringHost string LocalDirectory string ListenPort string } func generateNginx(yamlFile string, wantDiff bool, allowWrite bool, useColor bool) { config := loadConfig(yamlFile) // Extract port from first listen address listenPort := "8080" // fallback default if len(config.Listen) > 0 { port := extractPort(config.Listen[0]) if port != "" { listenPort = port } } for _, log := range config.Logs { // Extract hostname from monitoring prefix hostname, err := extractHostname(log.MonitoringPrefix) if err != nil { fmt.Fprintf(os.Stderr, "Failed to extract hostname from %s: %v\n", log.MonitoringPrefix, err) continue } // Create template data data := NginxTemplateData{ MonitoringHost: hostname, LocalDirectory: log.LocalDirectory, ListenPort: listenPort, } // Parse and execute template tmpl, err := template.New("nginx").Parse(nginxTemplate) if err != nil { fmt.Fprintf(os.Stderr, "Failed to parse nginx template: %v\n", err) continue } // Generate output filename using only hostname part outputFilename := fmt.Sprintf("%s.conf", hostname) outputPath := filepath.Join(log.LocalDirectory, outputFilename) // Execute template to buffer var buf bytes.Buffer err = tmpl.Execute(&buf, data) if err != nil { fmt.Fprintf(os.Stderr, "Failed to execute nginx template for %s: %v\n", outputPath, err) continue } // Write file with status err = writeFileWithStatus(outputPath, buf.Bytes(), wantDiff, allowWrite, useColor) if err != nil { fmt.Fprintf(os.Stderr, "Failed to write nginx config file %s: %v\n", outputPath, err) continue } } } func extractPort(listenAddr string) string { // Handle common listen address formats: // ":8080" -> "8080" // "localhost:8080" -> "8080" // "[::]:8080" -> "8080" if strings.HasPrefix(listenAddr, ":") { return listenAddr[1:] // Remove the leading ":" } // For addresses with host:port format if strings.Contains(listenAddr, ":") { parts := strings.Split(listenAddr, ":") return parts[len(parts)-1] // Return the last part (port) } return "" } func extractHostname(urlStr string) (string, error) { parsedURL, err := url.Parse(urlStr) if err != nil { return "", err } return parsedURL.Host, nil }