1package cmd
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "log/slog"
8 "os"
9 "os/signal"
10 "time"
11
12 "github.com/charmbracelet/crush/internal/config"
13 "github.com/charmbracelet/crush/internal/server"
14 "github.com/charmbracelet/log/v2"
15 "github.com/spf13/cobra"
16)
17
18var serverHost string
19
20var serverCmd = &cobra.Command{
21 Use: "server",
22 Short: "Start the Crush server",
23 RunE: func(cmd *cobra.Command, args []string) error {
24 dataDir, err := cmd.Flags().GetString("data-dir")
25 if err != nil {
26 return fmt.Errorf("failed to get data directory: %v", err)
27 }
28 debug, err := cmd.Flags().GetBool("debug")
29 if err != nil {
30 return fmt.Errorf("failed to get debug flag: %v", err)
31 }
32
33 cfg, err := config.Load("", dataDir, debug)
34 if err != nil {
35 return fmt.Errorf("failed to load configuration: %v", err)
36 }
37
38 logger := log.New(os.Stderr)
39 logger.SetReportTimestamp(true)
40 slog.SetDefault(slog.New(logger))
41 if debug {
42 logger.SetLevel(log.DebugLevel)
43 slog.SetLogLoggerLevel(slog.LevelDebug)
44 }
45
46 srv := server.NewServer(cfg, "unix", serverHost)
47 srv.SetLogger(slog.Default())
48 slog.Info("Starting Crush server...", "addr", srv.Addr)
49
50 errch := make(chan error, 1)
51 sigch := make(chan os.Signal, 1)
52 sigs := []os.Signal{os.Interrupt}
53 sigs = append(sigs, addSignals(sigs)...)
54 signal.Notify(sigch, sigs...)
55
56 go func() {
57 errch <- srv.ListenAndServe()
58 }()
59
60 select {
61 case <-sigch:
62 slog.Info("Received interrupt signal...")
63 case err = <-errch:
64 if err != nil && !errors.Is(err, server.ErrServerClosed) {
65 _ = srv.Close()
66 slog.Error("Server error", "error", err)
67 return fmt.Errorf("server error: %v", err)
68 }
69 }
70
71 if errors.Is(err, server.ErrServerClosed) {
72 return nil
73 }
74
75 ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second)
76 defer cancel()
77
78 slog.Info("Shutting down...")
79
80 if err := srv.Shutdown(ctx); err != nil {
81 slog.Error("Failed to shutdown server", "error", err)
82 return fmt.Errorf("failed to shutdown server: %v", err)
83 }
84
85 return nil
86 },
87}
88
89func init() {
90 serverCmd.Flags().StringVar(&serverHost, "host", server.DefaultHost(), "Server host (TCP or Unix socket)")
91 rootCmd.AddCommand(serverCmd)
92}