753 lines
22 KiB
Go
753 lines
22 KiB
Go
package mock
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"spore-gateway/internal/discovery"
|
|
"spore-gateway/pkg/client"
|
|
|
|
"github.com/gorilla/mux"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// MockHTTPServer represents the mock HTTP server
|
|
type MockHTTPServer struct {
|
|
port string
|
|
router *mux.Router
|
|
discovery *MockNodeDiscovery
|
|
wsServer *MockWebSocketServer
|
|
server *http.Server
|
|
enableWS bool
|
|
firmwareStore map[string][]byte // Simple in-memory firmware storage
|
|
}
|
|
|
|
// NewMockHTTPServer creates a new mock HTTP server instance
|
|
func NewMockHTTPServer(port string, discovery *MockNodeDiscovery, enableWS bool) *MockHTTPServer {
|
|
ms := &MockHTTPServer{
|
|
port: port,
|
|
router: mux.NewRouter(),
|
|
discovery: discovery,
|
|
enableWS: enableWS,
|
|
firmwareStore: make(map[string][]byte),
|
|
}
|
|
|
|
// Initialize WebSocket server if enabled
|
|
if enableWS {
|
|
ms.wsServer = NewMockWebSocketServer(discovery)
|
|
}
|
|
|
|
ms.setupRoutes()
|
|
ms.setupMiddleware()
|
|
|
|
ms.server = &http.Server{
|
|
Addr: ":" + port,
|
|
Handler: ms.router,
|
|
ReadTimeout: 30 * time.Second,
|
|
WriteTimeout: 30 * time.Second,
|
|
IdleTimeout: 60 * time.Second,
|
|
}
|
|
|
|
return ms
|
|
}
|
|
|
|
// setupMiddleware configures middleware for the server
|
|
func (ms *MockHTTPServer) setupMiddleware() {
|
|
ms.router.Use(ms.corsMiddleware)
|
|
ms.router.Use(ms.jsonMiddleware)
|
|
ms.router.Use(ms.loggingMiddleware)
|
|
}
|
|
|
|
// corsMiddleware handles CORS headers
|
|
func (ms *MockHTTPServer) corsMiddleware(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, Accept")
|
|
w.Header().Set("Access-Control-Expose-Headers", "Content-Type, Content-Length")
|
|
|
|
if r.Method == "OPTIONS" {
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// jsonMiddleware sets JSON content type
|
|
func (ms *MockHTTPServer) jsonMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// loggingMiddleware logs HTTP requests
|
|
func (ms *MockHTTPServer) loggingMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
start := time.Now()
|
|
next.ServeHTTP(w, r)
|
|
log.WithFields(log.Fields{
|
|
"method": r.Method,
|
|
"path": r.URL.Path,
|
|
"remote_addr": r.RemoteAddr,
|
|
"duration": time.Since(start),
|
|
}).Debug("HTTP request")
|
|
})
|
|
}
|
|
|
|
// setupRoutes configures all the API routes
|
|
func (ms *MockHTTPServer) setupRoutes() {
|
|
// API routes
|
|
api := ms.router.PathPrefix("/api").Subrouter()
|
|
api.Use(ms.corsMiddleware)
|
|
|
|
// Discovery endpoints
|
|
api.HandleFunc("/discovery/nodes", ms.getDiscoveryNodes).Methods("GET")
|
|
api.HandleFunc("/discovery/refresh", ms.refreshDiscovery).Methods("POST", "OPTIONS")
|
|
api.HandleFunc("/discovery/random-primary", ms.selectRandomPrimary).Methods("POST", "OPTIONS")
|
|
api.HandleFunc("/discovery/primary/{ip}", ms.setPrimaryNode).Methods("POST", "OPTIONS")
|
|
|
|
// Cluster endpoints
|
|
api.HandleFunc("/cluster/members", ms.getClusterMembers).Methods("GET")
|
|
api.HandleFunc("/cluster/refresh", ms.refreshCluster).Methods("POST", "OPTIONS")
|
|
api.HandleFunc("/cluster/node/versions", ms.getClusterNodeVersions).Methods("GET")
|
|
api.HandleFunc("/rollout", ms.startRollout).Methods("POST", "OPTIONS")
|
|
|
|
// Task endpoints
|
|
api.HandleFunc("/tasks/status", ms.getTaskStatus).Methods("GET")
|
|
|
|
// Node endpoints
|
|
api.HandleFunc("/node/status", ms.getNodeStatus).Methods("GET")
|
|
api.HandleFunc("/node/status/{ip}", ms.getNodeStatusByIP).Methods("GET")
|
|
api.HandleFunc("/node/endpoints", ms.getNodeEndpoints).Methods("GET")
|
|
api.HandleFunc("/node/update", ms.updateNodeFirmware).Methods("POST", "OPTIONS")
|
|
|
|
// Proxy endpoints
|
|
api.HandleFunc("/proxy-call", ms.proxyCall).Methods("POST", "OPTIONS")
|
|
|
|
// Registry proxy endpoints
|
|
api.HandleFunc("/registry/health", ms.getRegistryHealth).Methods("GET")
|
|
api.HandleFunc("/registry/firmware", ms.listRegistryFirmware).Methods("GET")
|
|
api.HandleFunc("/registry/firmware", ms.uploadRegistryFirmware).Methods("POST", "OPTIONS")
|
|
api.HandleFunc("/registry/firmware/{name}/{version}", ms.downloadRegistryFirmware).Methods("GET")
|
|
api.HandleFunc("/registry/firmware/{name}/{version}", ms.updateRegistryFirmware).Methods("PUT", "OPTIONS")
|
|
api.HandleFunc("/registry/firmware/{name}/{version}", ms.deleteRegistryFirmware).Methods("DELETE", "OPTIONS")
|
|
|
|
// Monitoring endpoints
|
|
api.HandleFunc("/monitoring/resources", ms.getMonitoringResources).Methods("GET")
|
|
|
|
// Test endpoints
|
|
api.HandleFunc("/test/websocket", ms.testWebSocket).Methods("POST", "OPTIONS")
|
|
|
|
// Health check
|
|
api.HandleFunc("/health", ms.healthCheck).Methods("GET")
|
|
|
|
// WebSocket endpoint
|
|
if ms.enableWS {
|
|
ms.router.HandleFunc("/ws", ms.corsMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if err := ms.wsServer.HandleWebSocket(w, r); err != nil {
|
|
log.WithError(err).Error("WebSocket connection failed")
|
|
http.Error(w, "WebSocket upgrade failed", http.StatusBadRequest)
|
|
}
|
|
})).ServeHTTP)
|
|
}
|
|
}
|
|
|
|
// Start starts the HTTP server
|
|
func (ms *MockHTTPServer) Start() error {
|
|
log.WithField("port", ms.port).Info("Starting mock HTTP server")
|
|
return ms.server.ListenAndServe()
|
|
}
|
|
|
|
// Shutdown gracefully shuts down the HTTP server
|
|
func (ms *MockHTTPServer) Shutdown(ctx context.Context) error {
|
|
log.Info("Shutting down mock HTTP server")
|
|
|
|
// Shutdown WebSocket server if enabled
|
|
if ms.enableWS && ms.wsServer != nil {
|
|
if err := ms.wsServer.Shutdown(ctx); err != nil {
|
|
log.WithError(err).Error("WebSocket server shutdown error")
|
|
}
|
|
}
|
|
|
|
return ms.server.Shutdown(ctx)
|
|
}
|
|
|
|
// API endpoint handlers
|
|
|
|
// GET /api/discovery/nodes
|
|
func (ms *MockHTTPServer) getDiscoveryNodes(w http.ResponseWriter, r *http.Request) {
|
|
nodes := ms.discovery.GetNodes()
|
|
primaryNode := ms.discovery.GetPrimaryNode()
|
|
clusterStatus := ms.discovery.GetClusterStatus()
|
|
|
|
type NodeResponse struct {
|
|
*discovery.NodeInfo
|
|
IsPrimary bool `json:"isPrimary"`
|
|
}
|
|
|
|
response := struct {
|
|
PrimaryNode string `json:"primaryNode"`
|
|
TotalNodes int `json:"totalNodes"`
|
|
Nodes []NodeResponse `json:"nodes"`
|
|
ClientInitialized bool `json:"clientInitialized"`
|
|
ClientBaseURL string `json:"clientBaseUrl"`
|
|
ClusterStatus discovery.ClusterStatus `json:"clusterStatus"`
|
|
}{
|
|
PrimaryNode: primaryNode,
|
|
TotalNodes: len(nodes),
|
|
Nodes: make([]NodeResponse, 0, len(nodes)),
|
|
ClientInitialized: primaryNode != "",
|
|
ClientBaseURL: fmt.Sprintf("http://%s", primaryNode),
|
|
ClusterStatus: clusterStatus,
|
|
}
|
|
|
|
for _, node := range nodes {
|
|
nodeResponse := NodeResponse{
|
|
NodeInfo: node,
|
|
IsPrimary: node.IP == primaryNode,
|
|
}
|
|
response.Nodes = append(response.Nodes, nodeResponse)
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// POST /api/discovery/refresh
|
|
func (ms *MockHTTPServer) refreshDiscovery(w http.ResponseWriter, r *http.Request) {
|
|
response := struct {
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
PrimaryNode string `json:"primaryNode"`
|
|
TotalNodes int `json:"totalNodes"`
|
|
ClientInitialized bool `json:"clientInitialized"`
|
|
}{
|
|
Success: true,
|
|
Message: "Mock cluster refresh completed",
|
|
PrimaryNode: ms.discovery.GetPrimaryNode(),
|
|
TotalNodes: len(ms.discovery.GetNodes()),
|
|
ClientInitialized: ms.discovery.GetPrimaryNode() != "",
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// POST /api/discovery/random-primary
|
|
func (ms *MockHTTPServer) selectRandomPrimary(w http.ResponseWriter, r *http.Request) {
|
|
nodes := ms.discovery.GetNodes()
|
|
if len(nodes) == 0 {
|
|
http.Error(w, `{"error": "No nodes available"}`, http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
newPrimary := ms.discovery.SelectRandomPrimaryNode()
|
|
if newPrimary == "" {
|
|
http.Error(w, `{"error": "Selection failed"}`, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
response := struct {
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
PrimaryNode string `json:"primaryNode"`
|
|
TotalNodes int `json:"totalNodes"`
|
|
ClientInitialized bool `json:"clientInitialized"`
|
|
Timestamp string `json:"timestamp"`
|
|
}{
|
|
Success: true,
|
|
Message: fmt.Sprintf("Randomly selected new primary node: %s", newPrimary),
|
|
PrimaryNode: newPrimary,
|
|
TotalNodes: len(nodes),
|
|
ClientInitialized: true,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// POST /api/discovery/primary/{ip}
|
|
func (ms *MockHTTPServer) setPrimaryNode(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
requestedIP := vars["ip"]
|
|
|
|
if err := ms.discovery.SetPrimaryNode(requestedIP); err != nil {
|
|
http.Error(w, fmt.Sprintf(`{"error": "Node not found", "message": "Node with IP %s not found"}`, requestedIP), http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
response := struct {
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
PrimaryNode string `json:"primaryNode"`
|
|
ClientInitialized bool `json:"clientInitialized"`
|
|
}{
|
|
Success: true,
|
|
Message: fmt.Sprintf("Primary node set to %s", requestedIP),
|
|
PrimaryNode: requestedIP,
|
|
ClientInitialized: true,
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/cluster/members
|
|
func (ms *MockHTTPServer) getClusterMembers(w http.ResponseWriter, r *http.Request) {
|
|
nodes := ms.discovery.GetNodes()
|
|
|
|
// Convert to mock format
|
|
mockNodes := make(map[string]*NodeInfo)
|
|
for ip, node := range nodes {
|
|
mockNodes[ip] = &NodeInfo{
|
|
IP: node.IP,
|
|
Hostname: node.Hostname,
|
|
Status: string(node.Status),
|
|
Latency: node.Latency,
|
|
LastSeen: node.LastSeen,
|
|
Labels: node.Labels,
|
|
}
|
|
}
|
|
|
|
members := GenerateMockClusterMembers(mockNodes)
|
|
|
|
response := &client.ClusterStatusResponse{
|
|
Members: members,
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// POST /api/cluster/refresh
|
|
func (ms *MockHTTPServer) refreshCluster(w http.ResponseWriter, r *http.Request) {
|
|
var requestBody struct {
|
|
Reason string `json:"reason"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil && err.Error() != "EOF" {
|
|
requestBody.Reason = "manual_refresh"
|
|
}
|
|
|
|
response := struct {
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
Reason string `json:"reason"`
|
|
WSclients int `json:"wsClients"`
|
|
}{
|
|
Success: true,
|
|
Message: "Mock cluster refresh triggered",
|
|
Reason: requestBody.Reason,
|
|
}
|
|
|
|
if ms.enableWS && ms.wsServer != nil {
|
|
response.WSclients = ms.wsServer.GetClientCount()
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/cluster/node/versions
|
|
func (ms *MockHTTPServer) getClusterNodeVersions(w http.ResponseWriter, r *http.Request) {
|
|
nodes := ms.discovery.GetNodes()
|
|
|
|
type NodeVersionInfo struct {
|
|
IP string `json:"ip"`
|
|
Version string `json:"version"`
|
|
Labels map[string]string `json:"labels"`
|
|
}
|
|
|
|
var nodeVersions []NodeVersionInfo
|
|
for _, node := range nodes {
|
|
version := "unknown"
|
|
if v, exists := node.Labels["version"]; exists {
|
|
version = v
|
|
}
|
|
|
|
nodeVersions = append(nodeVersions, NodeVersionInfo{
|
|
IP: node.IP,
|
|
Version: version,
|
|
Labels: node.Labels,
|
|
})
|
|
}
|
|
|
|
response := struct {
|
|
Nodes []NodeVersionInfo `json:"nodes"`
|
|
}{
|
|
Nodes: nodeVersions,
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// RolloutNode represents a node in a rollout request
|
|
type RolloutNode struct {
|
|
IP string `json:"ip"`
|
|
Version string `json:"version"`
|
|
Labels map[string]string `json:"labels"`
|
|
}
|
|
|
|
// POST /api/rollout
|
|
func (ms *MockHTTPServer) startRollout(w http.ResponseWriter, r *http.Request) {
|
|
var request struct {
|
|
Firmware struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
Labels map[string]string `json:"labels"`
|
|
} `json:"firmware"`
|
|
Nodes []RolloutNode `json:"nodes"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
|
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
rolloutID := fmt.Sprintf("rollout_%d", time.Now().Unix())
|
|
|
|
response := struct {
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
RolloutID string `json:"rolloutId"`
|
|
TotalNodes int `json:"totalNodes"`
|
|
FirmwareURL string `json:"firmwareUrl"`
|
|
}{
|
|
Success: true,
|
|
Message: fmt.Sprintf("Mock rollout started for %d nodes", len(request.Nodes)),
|
|
RolloutID: rolloutID,
|
|
TotalNodes: len(request.Nodes),
|
|
FirmwareURL: fmt.Sprintf("http://localhost:3002/firmware/%s/%s", request.Firmware.Name, request.Firmware.Version),
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
|
|
// Simulate rollout progress in background
|
|
if ms.enableWS && ms.wsServer != nil {
|
|
go ms.simulateRollout(rolloutID, request.Nodes, request.Firmware.Version)
|
|
}
|
|
}
|
|
|
|
// simulateRollout simulates a rollout process with progress updates
|
|
func (ms *MockHTTPServer) simulateRollout(rolloutID string, nodes []RolloutNode, newVersion string) {
|
|
for i, node := range nodes {
|
|
// Simulate updating labels
|
|
time.Sleep(500 * time.Millisecond)
|
|
if ms.wsServer != nil {
|
|
ms.wsServer.BroadcastRolloutProgress(rolloutID, node.IP, "updating_labels", i+1, len(nodes))
|
|
}
|
|
|
|
// Simulate uploading
|
|
time.Sleep(1 * time.Second)
|
|
if ms.wsServer != nil {
|
|
ms.wsServer.BroadcastRolloutProgress(rolloutID, node.IP, "uploading", i+1, len(nodes))
|
|
}
|
|
|
|
// Update node version in discovery
|
|
ms.discovery.UpdateNodeVersion(node.IP, strings.TrimPrefix(newVersion, "v"))
|
|
|
|
// Simulate completion
|
|
time.Sleep(500 * time.Millisecond)
|
|
if ms.wsServer != nil {
|
|
ms.wsServer.BroadcastRolloutProgress(rolloutID, node.IP, "completed", i+1, len(nodes))
|
|
}
|
|
}
|
|
|
|
log.WithField("rollout_id", rolloutID).Info("Mock rollout completed")
|
|
}
|
|
|
|
// GET /api/tasks/status
|
|
func (ms *MockHTTPServer) getTaskStatus(w http.ResponseWriter, r *http.Request) {
|
|
response := GenerateMockTaskStatus()
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/node/status
|
|
func (ms *MockHTTPServer) getNodeStatus(w http.ResponseWriter, r *http.Request) {
|
|
primaryNode := ms.discovery.GetPrimaryNode()
|
|
nodes := ms.discovery.GetNodes()
|
|
|
|
var labels map[string]string
|
|
if primaryNode != "" && nodes[primaryNode] != nil {
|
|
labels = nodes[primaryNode].Labels
|
|
} else {
|
|
labels = make(map[string]string)
|
|
}
|
|
|
|
response := GenerateMockSystemStatus(labels)
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/node/status/{ip}
|
|
func (ms *MockHTTPServer) getNodeStatusByIP(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
nodeIP := vars["ip"]
|
|
|
|
nodes := ms.discovery.GetNodes()
|
|
node, exists := nodes[nodeIP]
|
|
if !exists {
|
|
http.Error(w, fmt.Sprintf(`{"error": "Node not found", "message": "Node with IP %s not found"}`, nodeIP), http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
response := GenerateMockSystemStatus(node.Labels)
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/node/endpoints
|
|
func (ms *MockHTTPServer) getNodeEndpoints(w http.ResponseWriter, r *http.Request) {
|
|
response := GenerateMockCapabilities()
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// POST /api/node/update
|
|
func (ms *MockHTTPServer) updateNodeFirmware(w http.ResponseWriter, r *http.Request) {
|
|
nodeIP := r.URL.Query().Get("ip")
|
|
if nodeIP == "" {
|
|
nodeIP = r.Header.Get("X-Node-IP")
|
|
}
|
|
|
|
if nodeIP == "" {
|
|
http.Error(w, `{"error": "Node IP required"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Parse multipart form
|
|
err := r.ParseMultipartForm(50 << 20) // 50MB limit
|
|
if err != nil {
|
|
http.Error(w, `{"error": "Failed to parse form"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
file, fileHeader, err := r.FormFile("file")
|
|
if err != nil {
|
|
http.Error(w, `{"error": "No file received"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
filename := fileHeader.Filename
|
|
if filename == "" {
|
|
filename = "firmware.bin"
|
|
}
|
|
|
|
// Read file data
|
|
fileData, err := io.ReadAll(file)
|
|
if err != nil {
|
|
http.Error(w, `{"error": "Failed to read file"}`, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
log.WithFields(log.Fields{
|
|
"node_ip": nodeIP,
|
|
"file_size": len(fileData),
|
|
"filename": filename,
|
|
}).Info("Mock firmware upload received")
|
|
|
|
// Store firmware in memory
|
|
ms.firmwareStore[nodeIP] = fileData
|
|
|
|
// Broadcast status if WebSocket enabled
|
|
if ms.enableWS && ms.wsServer != nil {
|
|
ms.wsServer.BroadcastFirmwareUploadStatus(nodeIP, "uploading", filename, len(fileData))
|
|
}
|
|
|
|
// Send immediate response
|
|
response := struct {
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
NodeIP string `json:"nodeIp"`
|
|
FileSize int `json:"fileSize"`
|
|
Filename string `json:"filename"`
|
|
Status string `json:"status"`
|
|
}{
|
|
Success: true,
|
|
Message: "Mock firmware upload received",
|
|
NodeIP: nodeIP,
|
|
FileSize: len(fileData),
|
|
Filename: filename,
|
|
Status: "processing",
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
|
|
// Simulate firmware update in background
|
|
go func() {
|
|
time.Sleep(2 * time.Second)
|
|
if ms.enableWS && ms.wsServer != nil {
|
|
// Randomly succeed or fail (90% success rate)
|
|
if rand.Float32() < 0.9 {
|
|
ms.wsServer.BroadcastFirmwareUploadStatus(nodeIP, "completed", filename, len(fileData))
|
|
} else {
|
|
ms.wsServer.BroadcastFirmwareUploadStatus(nodeIP, "failed", filename, len(fileData))
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// POST /api/proxy-call
|
|
func (ms *MockHTTPServer) proxyCall(w http.ResponseWriter, r *http.Request) {
|
|
var requestBody struct {
|
|
IP string `json:"ip"`
|
|
Method string `json:"method"`
|
|
URI string `json:"uri"`
|
|
Params []map[string]interface{} `json:"params"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
|
|
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Generate mock response based on URI
|
|
mockResponse := GenerateMockProxyResponse(requestBody.Method, requestBody.URI)
|
|
|
|
// Wrap in data field for consistency
|
|
response := map[string]interface{}{
|
|
"data": mockResponse,
|
|
"status": http.StatusOK,
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/registry/health
|
|
func (ms *MockHTTPServer) getRegistryHealth(w http.ResponseWriter, r *http.Request) {
|
|
response := map[string]interface{}{
|
|
"status": "healthy",
|
|
"timestamp": time.Now().Format(time.RFC3339),
|
|
"service": "mock-registry",
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/registry/firmware
|
|
func (ms *MockHTTPServer) listRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
|
firmwareList := GenerateMockFirmwareList()
|
|
json.NewEncoder(w).Encode(firmwareList)
|
|
}
|
|
|
|
// POST /api/registry/firmware
|
|
func (ms *MockHTTPServer) uploadRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
|
response := map[string]interface{}{
|
|
"success": true,
|
|
"message": "Mock firmware uploaded successfully",
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/registry/firmware/{name}/{version}
|
|
func (ms *MockHTTPServer) downloadRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
|
vars := mux.Vars(r)
|
|
name := vars["name"]
|
|
version := vars["version"]
|
|
|
|
// Generate mock firmware binary
|
|
firmwareData := GenerateMockFirmwareBinary(524288) // 512KB
|
|
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s-%s.bin\"", name, version))
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(firmwareData)))
|
|
|
|
w.Write(firmwareData)
|
|
}
|
|
|
|
// PUT /api/registry/firmware/{name}/{version}
|
|
func (ms *MockHTTPServer) updateRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
|
response := map[string]interface{}{
|
|
"success": true,
|
|
"message": "Mock firmware metadata updated",
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// DELETE /api/registry/firmware/{name}/{version}
|
|
func (ms *MockHTTPServer) deleteRegistryFirmware(w http.ResponseWriter, r *http.Request) {
|
|
response := map[string]interface{}{
|
|
"success": true,
|
|
"message": "Mock firmware deleted",
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// POST /api/test/websocket
|
|
func (ms *MockHTTPServer) testWebSocket(w http.ResponseWriter, r *http.Request) {
|
|
response := struct {
|
|
Success bool `json:"success"`
|
|
Message string `json:"message"`
|
|
WSclients int `json:"websocketClients"`
|
|
TotalNodes int `json:"totalNodes"`
|
|
}{
|
|
Success: true,
|
|
Message: "Mock WebSocket test broadcast sent",
|
|
TotalNodes: len(ms.discovery.GetNodes()),
|
|
}
|
|
|
|
if ms.enableWS && ms.wsServer != nil {
|
|
response.WSclients = ms.wsServer.GetClientCount()
|
|
// Trigger a test broadcast
|
|
ms.wsServer.BroadcastClusterUpdate()
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/monitoring/resources
|
|
func (ms *MockHTTPServer) getMonitoringResources(w http.ResponseWriter, r *http.Request) {
|
|
nodes := ms.discovery.GetNodes()
|
|
|
|
// Convert to mock format
|
|
mockNodes := make(map[string]*NodeInfo)
|
|
for ip, node := range nodes {
|
|
mockNodes[ip] = &NodeInfo{
|
|
IP: node.IP,
|
|
Hostname: node.Hostname,
|
|
Status: string(node.Status),
|
|
Latency: node.Latency,
|
|
LastSeen: node.LastSeen,
|
|
Labels: node.Labels,
|
|
}
|
|
}
|
|
|
|
response := GenerateMockMonitoringResources(mockNodes)
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
// GET /api/health
|
|
func (ms *MockHTTPServer) healthCheck(w http.ResponseWriter, r *http.Request) {
|
|
nodes := ms.discovery.GetNodes()
|
|
primaryNode := ms.discovery.GetPrimaryNode()
|
|
|
|
health := struct {
|
|
Status string `json:"status"`
|
|
Timestamp string `json:"timestamp"`
|
|
Services map[string]bool `json:"services"`
|
|
Cluster map[string]interface{} `json:"cluster"`
|
|
Mock bool `json:"mock"`
|
|
}{
|
|
Status: "healthy",
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
Services: map[string]bool{
|
|
"http": true,
|
|
"websocket": ms.enableWS,
|
|
"discovery": true,
|
|
"mockClient": primaryNode != "",
|
|
},
|
|
Cluster: map[string]interface{}{
|
|
"totalNodes": len(nodes),
|
|
"primaryNode": primaryNode,
|
|
},
|
|
Mock: true,
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(health)
|
|
}
|