package main import ( "context" "fmt" "log" "net/http" "net/url" "os" "os/signal" "slices" "strings" "syscall" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" "golang.org/x/mod/semver" ) const ( forgeDomain = "code.fcuny.net" forgeUser = "fcuny" goPkgDomain = "go.fcuny.net" ) var ( goGetReqs = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "goget_requests_total", Help: "go get requests processed, by repository name.", }, []string{"name"}) modules = []string{"x"} ) func main() { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() metricsMux := http.NewServeMux() metricsMux.Handle("/metrics", promhttp.Handler()) metricsServer := &http.Server{ Addr: ":9091", Handler: metricsMux, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } go func() { log.Println("Starting metrics server on :9091") if err := metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Printf("Metrics server error: %v", err) } }() s := &http.Server{ Addr: ":8080", Handler: handler(), ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 1 * time.Minute, } go func() { log.Println("Starting main server on :8080") if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Printf("Main server error: %v", err) } }() <-ctx.Done() log.Println("Shutdown signal received, starting graceful shutdown...") shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) defer shutdownCancel() var shutdownErr error log.Println("Shutting down main server...") if err := s.Shutdown(shutdownCtx); err != nil { log.Printf("Main server shutdown error: %v", err) shutdownErr = err } log.Println("Shutting down metrics server...") if err := metricsServer.Shutdown(shutdownCtx); err != nil { log.Printf("Metrics server shutdown error: %v", err) if shutdownErr == nil { shutdownErr = err } } if shutdownErr != nil { log.Printf("Shutdown completed with errors") os.Exit(1) } log.Println("Shutdown completed successfully") } func handler() http.Handler { mux := http.NewServeMux() mux.Handle(goPkgDomain+"/{name}", siteHandler(modules)) mux.Handle(goPkgDomain+"/{name}/", siteHandler(modules)) goGetMux := http.NewServeMux() for _, name := range modules { module := goPkgDomain + "/" + name goGetMux.Handle( module+"/", goImportHandler(module, "https://"+forgeDomain+"/"+forgeUser+"/"+name), ) } return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { status := http.StatusMethodNotAllowed http.Error(rw, http.StatusText(status), status) return } if r.URL.Query().Get("go-get") == "1" { goGetMux.ServeHTTP(rw, r) return } mux.ServeHTTP(rw, r) }) } func goImportHandler(module, repo string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { goGetReqs.WithLabelValues(module).Inc() w.Header().Set("Content-Type", "text/html; charset=UTF-8") if _, err := fmt.Fprintf(w, `
`, module, repo); err != nil { log.Printf("Error writing response: %v", err) } }) } func siteHandler(names []string) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { name := r.PathValue("name") name, version, hasV := strings.Cut(name, "@") if !hasV { name, _, _ = strings.Cut(name, ".") } if hasV && !semver.IsValid(version) || !slices.Contains(names, name) { http.NotFound(rw, r) return } u := &url.URL{ Scheme: "https", Host: forgeDomain, Path: forgeUser + r.URL.Path, } if !hasV { path, symbol, hasSymbol := strings.Cut(r.URL.Path, ".") if hasSymbol { u.Path = forgeUser + "/" + path u.Fragment = symbol } } http.Redirect(rw, r, u.String(), http.StatusFound) }) }