Mining/cmd/mining/cmd/serve.go

196 lines
5.9 KiB
Go

package cmd
import (
"bufio"
"context"
"fmt"
"net"
"os"
"os/signal"
"strings"
"syscall"
"github.com/Snider/Mining/pkg/mining"
"github.com/spf13/cobra"
)
var (
host string
port int
namespace string
)
// serveCmd represents the serve command
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the mining service and interactive shell",
Long: `Start the mining service, which provides a RESTful API for managing miners, and an interactive shell for CLI commands.`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
displayHost := host
if displayHost == "0.0.0.0" {
var err error
displayHost, err = getLocalIP()
if err != nil {
displayHost = "localhost"
}
}
displayAddr := fmt.Sprintf("%s:%d", displayHost, port)
listenAddr := fmt.Sprintf("%s:%d", host, port)
// Use the global manager instance
mgr := getManager() // This ensures we get the manager initialized by initManager
service, err := mining.NewService(mgr, listenAddr, displayAddr, namespace) // Pass the global manager
if err != nil {
return fmt.Errorf("failed to create new service: %w", err)
}
// Start the server in a goroutine
go func() {
if err := service.ServiceStartup(ctx); err != nil {
fmt.Fprintf(os.Stderr, "Failed to start service: %v\n", err)
cancel()
}
}()
// Handle graceful shutdown on Ctrl+C
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
// Start interactive shell in a goroutine
go func() {
fmt.Printf("Mining service started on http://%s:%d\n", displayHost, port)
fmt.Printf("Swagger documentation is available at http://%s:%d%s/index.html\n", displayHost, port, service.SwaggerUIPath)
fmt.Println("Entering interactive shell. Type 'exit' or 'quit' to stop.")
fmt.Print(">> ")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
fmt.Print(">> ")
continue
}
if strings.ToLower(line) == "exit" || strings.ToLower(line) == "quit" {
fmt.Println("Exiting...")
cancel()
return
}
parts := strings.Fields(line)
if len(parts) == 0 {
fmt.Print(">> ")
continue
}
command := strings.ToLower(parts[0])
cmdArgs := parts[1:]
switch command {
case "start":
if len(cmdArgs) < 1 {
fmt.Println("Error: start command requires miner type (e.g., 'start xmrig')")
} else {
minerType := cmdArgs[0]
// Use default pool and wallet for interactive shell for simplicity
config := &mining.Config{
Pool: "pool.hashvault.pro:443",
Wallet: "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H", // Corrected wallet address
LogOutput: true, // Enable logging for interactive shell
// Add other default config values if necessary
}
miner, err := mgr.StartMiner(minerType, config)
if err != nil {
fmt.Fprintf(os.Stderr, "Error starting miner: %v\n", err)
} else {
fmt.Printf("Miner %s started successfully.\n", miner.GetName())
}
}
case "status":
if len(cmdArgs) < 1 {
fmt.Println("Error: status command requires miner name (e.g., 'status xmrig')")
} else {
minerName := cmdArgs[0]
miner, err := mgr.GetMiner(minerName)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting miner status: %v\n", err)
} else {
stats, err := miner.GetStats()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting miner stats: %v\n", err)
} else {
fmt.Printf("Miner Status for %s:\n", strings.Title(minerName))
fmt.Printf(" Hash Rate: %d H/s\n", stats.Hashrate)
fmt.Printf(" Shares: %d\n", stats.Shares)
fmt.Printf(" Rejected: %d\n", stats.Rejected)
fmt.Printf(" Uptime: %d seconds\n", stats.Uptime)
fmt.Printf(" Algorithm: %s\n", stats.Algorithm)
}
}
}
case "stop":
if len(cmdArgs) < 1 {
fmt.Println("Error: stop command requires miner name (e.g., 'stop xmrig')")
} else {
minerName := cmdArgs[0]
err := mgr.StopMiner(minerName)
if err != nil {
fmt.Fprintf(os.Stderr, "Error stopping miner: %v\n", err)
} else {
fmt.Printf("Miner %s stopped successfully.\n", minerName)
}
}
case "list":
miners := mgr.ListMiners()
if len(miners) == 0 {
fmt.Println("No miners currently running.")
} else {
fmt.Println("Running Miners:")
for _, miner := range miners {
fmt.Printf(" - %s\n", miner.GetName())
}
}
default:
fmt.Fprintf(os.Stderr, "Unknown command: %s. Only 'start', 'status', 'stop', 'list' are directly supported in this shell.\n", command)
fmt.Fprintf(os.Stderr, "For other commands, please run them directly from your terminal (e.g., 'miner-cli doctor').\n")
}
fmt.Print(">> ")
}
}()
select {
case <-signalChan:
fmt.Println("\nReceived shutdown signal, stopping service...")
cancel()
case <-ctx.Done():
}
fmt.Println("Mining service stopped.")
return nil
},
}
func init() {
serveCmd.Flags().StringVar(&host, "host", "127.0.0.1", "Host to listen on")
serveCmd.Flags().IntVarP(&port, "port", "p", 9090, "Port to listen on")
serveCmd.Flags().StringVarP(&namespace, "namespace", "n", "/api/v1/mining", "API namespace for the swagger UI")
rootCmd.AddCommand(serveCmd)
}
func getLocalIP() (string, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "", err
}
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String(), nil
}
}
}
return "localhost", nil
}