package api import ( "encoding/json" "fmt" "net/http" "strconv" "time" "chorus.services/bzzz/logging" "chorus.services/bzzz/pubsub" "github.com/gorilla/mux" ) // HTTPServer provides HTTP API endpoints for Bzzz type HTTPServer struct { port int hypercoreLog *logging.HypercoreLog pubsub *pubsub.PubSub server *http.Server } // NewHTTPServer creates a new HTTP server for Bzzz API func NewHTTPServer(port int, hlog *logging.HypercoreLog, ps *pubsub.PubSub) *HTTPServer { return &HTTPServer{ port: port, hypercoreLog: hlog, pubsub: ps, } } // Start starts the HTTP server func (h *HTTPServer) Start() error { router := mux.NewRouter() // Enable CORS for all routes router.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return } next.ServeHTTP(w, r) }) }) // API routes api := router.PathPrefix("/api").Subrouter() // Hypercore log endpoints api.HandleFunc("/hypercore/logs", h.handleGetLogs).Methods("GET") api.HandleFunc("/hypercore/logs/recent", h.handleGetRecentLogs).Methods("GET") api.HandleFunc("/hypercore/logs/stats", h.handleGetLogStats).Methods("GET") api.HandleFunc("/hypercore/logs/since/{index}", h.handleGetLogsSince).Methods("GET") // Health check api.HandleFunc("/health", h.handleHealth).Methods("GET") // Status endpoint api.HandleFunc("/status", h.handleStatus).Methods("GET") h.server = &http.Server{ Addr: fmt.Sprintf(":%d", h.port), Handler: router, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } fmt.Printf("🌐 Starting HTTP API server on port %d\n", h.port) return h.server.ListenAndServe() } // Stop stops the HTTP server func (h *HTTPServer) Stop() error { if h.server != nil { return h.server.Close() } return nil } // handleGetLogs returns hypercore log entries func (h *HTTPServer) handleGetLogs(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") // Parse query parameters query := r.URL.Query() startStr := query.Get("start") endStr := query.Get("end") limitStr := query.Get("limit") var start, end uint64 var err error if startStr != "" { start, err = strconv.ParseUint(startStr, 10, 64) if err != nil { http.Error(w, "Invalid start parameter", http.StatusBadRequest) return } } if endStr != "" { end, err = strconv.ParseUint(endStr, 10, 64) if err != nil { http.Error(w, "Invalid end parameter", http.StatusBadRequest) return } } else { end = h.hypercoreLog.Length() } var limit int = 100 // Default limit if limitStr != "" { limit, err = strconv.Atoi(limitStr) if err != nil || limit <= 0 || limit > 1000 { limit = 100 } } // Get log entries var entries []logging.LogEntry if endStr != "" || startStr != "" { entries, err = h.hypercoreLog.GetRange(start, end) } else { entries, err = h.hypercoreLog.GetRecentEntries(limit) } if err != nil { http.Error(w, fmt.Sprintf("Failed to get log entries: %v", err), http.StatusInternalServerError) return } response := map[string]interface{}{ "entries": entries, "count": len(entries), "timestamp": time.Now().Unix(), "total": h.hypercoreLog.Length(), } json.NewEncoder(w).Encode(response) } // handleGetRecentLogs returns the most recent log entries func (h *HTTPServer) handleGetRecentLogs(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") // Parse limit parameter query := r.URL.Query() limitStr := query.Get("limit") limit := 50 // Default if limitStr != "" { if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 1000 { limit = l } } entries, err := h.hypercoreLog.GetRecentEntries(limit) if err != nil { http.Error(w, fmt.Sprintf("Failed to get recent entries: %v", err), http.StatusInternalServerError) return } response := map[string]interface{}{ "entries": entries, "count": len(entries), "timestamp": time.Now().Unix(), "total": h.hypercoreLog.Length(), } json.NewEncoder(w).Encode(response) } // handleGetLogsSince returns log entries since a given index func (h *HTTPServer) handleGetLogsSince(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") vars := mux.Vars(r) indexStr := vars["index"] index, err := strconv.ParseUint(indexStr, 10, 64) if err != nil { http.Error(w, "Invalid index parameter", http.StatusBadRequest) return } entries, err := h.hypercoreLog.GetEntriesSince(index) if err != nil { http.Error(w, fmt.Sprintf("Failed to get entries since index: %v", err), http.StatusInternalServerError) return } response := map[string]interface{}{ "entries": entries, "count": len(entries), "since_index": index, "timestamp": time.Now().Unix(), "total": h.hypercoreLog.Length(), } json.NewEncoder(w).Encode(response) } // handleGetLogStats returns statistics about the hypercore log func (h *HTTPServer) handleGetLogStats(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") stats := h.hypercoreLog.GetStats() json.NewEncoder(w).Encode(stats) } // handleHealth returns health status func (h *HTTPServer) handleHealth(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") health := map[string]interface{}{ "status": "healthy", "timestamp": time.Now().Unix(), "log_entries": h.hypercoreLog.Length(), } json.NewEncoder(w).Encode(health) } // handleStatus returns detailed status information func (h *HTTPServer) handleStatus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") status := map[string]interface{}{ "status": "running", "timestamp": time.Now().Unix(), "hypercore": h.hypercoreLog.GetStats(), "api_version": "1.0.0", } json.NewEncoder(w).Encode(status) }