This comprehensive refactoring addresses critical architectural issues: IMPORT CYCLE RESOLUTION: • pkg/crypto ↔ pkg/slurp/roles: Created pkg/security/access_levels.go • pkg/ucxl → pkg/dht: Created pkg/storage/interfaces.go • pkg/slurp/leader → pkg/election → pkg/slurp/storage: Moved types to pkg/election/interfaces.go MODULE PATH MIGRATION: • Changed from github.com/anthonyrawlins/bzzz to chorus.services/bzzz • Updated all import statements across 115+ files • Maintains compatibility while removing personal GitHub account dependency TYPE SYSTEM IMPROVEMENTS: • Resolved duplicate type declarations in crypto package • Added missing type definitions (RoleStatus, TimeRestrictions, KeyStatus, KeyRotationResult) • Proper interface segregation to prevent future cycles ARCHITECTURAL BENEFITS: • Build now progresses past structural issues to normal dependency resolution • Cleaner separation of concerns between packages • Eliminates circular dependencies that prevented compilation • Establishes foundation for scalable codebase growth 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
243 lines
6.2 KiB
Go
243 lines
6.2 KiB
Go
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)
|
|
} |