package api import ( "context" "crypto/tls" "crypto/x509" "fmt" "log" "net/http" "os" "strings" "time" ) // loggingResponseWriter is a wrapper for http.ResponseWriter to capture status code type loggingResponseWriter struct { http.ResponseWriter statusCode int } // WriteHeader captures the status code before passing to the underlying ResponseWriter func (lrw *loggingResponseWriter) WriteHeader(code int) { lrw.statusCode = code lrw.ResponseWriter.WriteHeader(code) } // LoggingMiddleware logs information about each request func LoggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() // Create a response writer wrapper to capture status code lrw := &loggingResponseWriter{ ResponseWriter: w, statusCode: http.StatusOK, // Default status } // Process the request next.ServeHTTP(lrw, r) // Calculate duration duration := time.Since(start) // Log the request details log.Printf("REQUEST: %s %s - %d %s - %s - %v", r.Method, r.URL.Path, lrw.statusCode, http.StatusText(lrw.statusCode), r.RemoteAddr, duration, ) }) } // Server represents the API server for KAT type Server struct { httpServer *http.Server router *Router certFile string keyFile string caFile string } // NewServer creates a new API server instance func NewServer(addr string, certFile, keyFile, caFile string) (*Server, error) { router := NewRouter() server := &Server{ router: router, certFile: certFile, keyFile: keyFile, caFile: caFile, } // Create the HTTP server with TLS config server.httpServer = &http.Server{ Addr: addr, Handler: LoggingMiddleware(router), // Add logging middleware ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 120 * time.Second, } return server, nil } // Start begins listening for requests func (s *Server) Start() error { log.Printf("Starting server on %s", s.httpServer.Addr) // Load server certificate and key cert, err := tls.LoadX509KeyPair(s.certFile, s.keyFile) if err != nil { return fmt.Errorf("failed to load server certificate and key: %w", err) } // Load CA certificate for client verification caCert, err := os.ReadFile(s.caFile) if err != nil { return fmt.Errorf("failed to read CA certificate: %w", err) } caCertPool := x509.NewCertPool() if !caCertPool.AppendCertsFromPEM(caCert) { return fmt.Errorf("failed to append CA certificate to pool") } // For Phase 2, we'll use a simpler approach - don't require client certs at all // This is a temporary solution until we implement proper authentication s.httpServer.TLSConfig = &tls.Config{ Certificates: []tls.Certificate{cert}, ClientAuth: tls.NoClientCert, // Don't require client certs for now MinVersion: tls.VersionTLS12, } log.Printf("WARNING: TLS configured without client certificate verification for Phase 2") log.Printf("This is a temporary development configuration and should be secured in production") log.Printf("Server configured with TLS, starting to listen for requests") // Start the server return s.httpServer.ListenAndServeTLS("", "") } // Stop gracefully shuts down the server func (s *Server) Stop(ctx context.Context) error { log.Printf("Shutting down server on %s", s.httpServer.Addr) err := s.httpServer.Shutdown(ctx) if err != nil { log.Printf("Error during server shutdown: %v", err) return err } log.Printf("Server shutdown complete") return nil } // RegisterJoinHandler registers the handler for agent join requests func (s *Server) RegisterJoinHandler(handler http.HandlerFunc) { s.router.HandleFunc("POST", "/internal/v1alpha1/join", handler) } // RegisterNodeStatusHandler registers the handler for node status updates func (s *Server) RegisterNodeStatusHandler(handler http.HandlerFunc) { s.router.HandleFunc("POST", "/v1alpha1/nodes/{nodeName}/status", handler) }