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 serverCmd = &cobra.Command{
19	Use:   "server",
20	Short: "Start the Crush server",
21	RunE: func(cmd *cobra.Command, args []string) error {
22		dataDir, err := cmd.Flags().GetString("data-dir")
23		if err != nil {
24			return fmt.Errorf("failed to get data directory: %v", err)
25		}
26		debug, err := cmd.Flags().GetBool("debug")
27		if err != nil {
28			return fmt.Errorf("failed to get debug flag: %v", err)
29		}
30
31		cfg, err := config.Load("", dataDir, debug)
32		if err != nil {
33			return fmt.Errorf("failed to load configuration: %v", err)
34		}
35
36		logger := log.New(os.Stderr)
37		logger.SetReportTimestamp(true)
38		slog.SetDefault(slog.New(logger))
39		if debug {
40			logger.SetLevel(log.DebugLevel)
41			slog.SetLogLoggerLevel(slog.LevelDebug)
42		}
43
44		srv := server.NewServer(cfg, "unix", server.DefaultAddr())
45		slog.Info("Starting Crush server...", "addr", srv.Addr)
46
47		errch := make(chan error, 1)
48		sigch := make(chan os.Signal, 1)
49		sigs := []os.Signal{os.Interrupt}
50		sigs = append(sigs, addSignals(sigs)...)
51		signal.Notify(sigch, sigs...)
52
53		go func() {
54			errch <- srv.ListenAndServe()
55		}()
56
57		select {
58		case <-sigch:
59			slog.Info("Received interrupt signal...")
60		case err := <-errch:
61			if err != nil && !errors.Is(err, server.ErrServerClosed) {
62				_ = srv.Close()
63				slog.Error("Server error", "error", err)
64				return fmt.Errorf("server error: %v", err)
65			}
66		}
67
68		ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second)
69		defer cancel()
70
71		slog.Info("Shutting down...")
72
73		if err := srv.Shutdown(ctx); err != nil {
74			slog.Error("Failed to shutdown server", "error", err)
75			return fmt.Errorf("failed to shutdown server: %v", err)
76		}
77
78		return nil
79	},
80}
81
82func init() {
83	rootCmd.AddCommand(serverCmd)
84}