package system import ( "net/http" "strconv" "github.com/atlasos/calypso/internal/common/logger" "github.com/atlasos/calypso/internal/tasks" "github.com/gin-gonic/gin" ) // Handler handles system management API requests type Handler struct { service *Service taskEngine *tasks.Engine logger *logger.Logger } // NewHandler creates a new system handler func NewHandler(log *logger.Logger, taskEngine *tasks.Engine) *Handler { return &Handler{ service: NewService(log), taskEngine: taskEngine, logger: log, } } // ListServices lists all system services func (h *Handler) ListServices(c *gin.Context) { services, err := h.service.ListServices(c.Request.Context()) if err != nil { h.logger.Error("Failed to list services", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list services"}) return } c.JSON(http.StatusOK, gin.H{"services": services}) } // GetServiceStatus retrieves status of a specific service func (h *Handler) GetServiceStatus(c *gin.Context) { serviceName := c.Param("name") status, err := h.service.GetServiceStatus(c.Request.Context(), serviceName) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "service not found"}) return } c.JSON(http.StatusOK, status) } // RestartService restarts a system service func (h *Handler) RestartService(c *gin.Context) { serviceName := c.Param("name") if err := h.service.RestartService(c.Request.Context(), serviceName); err != nil { h.logger.Error("Failed to restart service", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "service restarted successfully"}) } // GetServiceLogs retrieves journald logs for a service func (h *Handler) GetServiceLogs(c *gin.Context) { serviceName := c.Param("name") linesStr := c.DefaultQuery("lines", "100") lines, err := strconv.Atoi(linesStr) if err != nil { lines = 100 } logs, err := h.service.GetJournalLogs(c.Request.Context(), serviceName, lines) if err != nil { h.logger.Error("Failed to get logs", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get logs"}) return } c.JSON(http.StatusOK, gin.H{"logs": logs}) } // GenerateSupportBundle generates a diagnostic support bundle func (h *Handler) GenerateSupportBundle(c *gin.Context) { userID, _ := c.Get("user_id") // Create async task taskID, err := h.taskEngine.CreateTask(c.Request.Context(), tasks.TaskTypeSupportBundle, userID.(string), map[string]interface{}{ "operation": "generate_support_bundle", }) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create task"}) return } // Run bundle generation in background go func() { ctx := c.Request.Context() h.taskEngine.StartTask(ctx, taskID) h.taskEngine.UpdateProgress(ctx, taskID, 50, "Collecting system information...") outputPath := "/tmp/calypso-support-bundle-" + taskID if err := h.service.GenerateSupportBundle(ctx, outputPath); err != nil { h.taskEngine.FailTask(ctx, taskID, err.Error()) return } h.taskEngine.UpdateProgress(ctx, taskID, 100, "Support bundle generated") h.taskEngine.CompleteTask(ctx, taskID, "Support bundle generated successfully") }() c.JSON(http.StatusAccepted, gin.H{"task_id": taskID}) } // ListNetworkInterfaces lists all network interfaces func (h *Handler) ListNetworkInterfaces(c *gin.Context) { interfaces, err := h.service.ListNetworkInterfaces(c.Request.Context()) if err != nil { h.logger.Error("Failed to list network interfaces", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list network interfaces"}) return } // Ensure we return an empty array instead of null if interfaces == nil { interfaces = []NetworkInterface{} } c.JSON(http.StatusOK, gin.H{"interfaces": interfaces}) } // SaveNTPSettings saves NTP configuration to the OS func (h *Handler) SaveNTPSettings(c *gin.Context) { var settings NTPSettings if err := c.ShouldBindJSON(&settings); err != nil { h.logger.Error("Invalid request body", "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) return } // Validate timezone if settings.Timezone == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "timezone is required"}) return } // Validate NTP servers if len(settings.NTPServers) == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "at least one NTP server is required"}) return } if err := h.service.SaveNTPSettings(c.Request.Context(), settings); err != nil { h.logger.Error("Failed to save NTP settings", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "NTP settings saved successfully"}) } // GetNTPSettings retrieves current NTP configuration func (h *Handler) GetNTPSettings(c *gin.Context) { settings, err := h.service.GetNTPSettings(c.Request.Context()) if err != nil { h.logger.Error("Failed to get NTP settings", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get NTP settings"}) return } c.JSON(http.StatusOK, gin.H{"settings": settings}) } // UpdateNetworkInterface updates a network interface configuration func (h *Handler) UpdateNetworkInterface(c *gin.Context) { ifaceName := c.Param("name") if ifaceName == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "interface name is required"}) return } var req struct { IPAddress string `json:"ip_address" binding:"required"` Subnet string `json:"subnet" binding:"required"` Gateway string `json:"gateway,omitempty"` DNS1 string `json:"dns1,omitempty"` DNS2 string `json:"dns2,omitempty"` Role string `json:"role,omitempty"` } if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Invalid request body", "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) return } // Convert to service request serviceReq := UpdateNetworkInterfaceRequest{ IPAddress: req.IPAddress, Subnet: req.Subnet, Gateway: req.Gateway, DNS1: req.DNS1, DNS2: req.DNS2, Role: req.Role, } updatedIface, err := h.service.UpdateNetworkInterface(c.Request.Context(), ifaceName, serviceReq) if err != nil { h.logger.Error("Failed to update network interface", "interface", ifaceName, "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"interface": updatedIface}) } // GetSystemLogs retrieves recent system logs func (h *Handler) GetSystemLogs(c *gin.Context) { limitStr := c.DefaultQuery("limit", "30") limit, err := strconv.Atoi(limitStr) if err != nil || limit <= 0 || limit > 100 { limit = 30 } logs, err := h.service.GetSystemLogs(c.Request.Context(), limit) if err != nil { h.logger.Error("Failed to get system logs", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get system logs"}) return } c.JSON(http.StatusOK, gin.H{"logs": logs}) }