server.go

 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		srv.SetLogger(slog.Default())
46		slog.Info("Starting Crush server...", "addr", srv.Addr)
47
48		errch := make(chan error, 1)
49		sigch := make(chan os.Signal, 1)
50		sigs := []os.Signal{os.Interrupt}
51		sigs = append(sigs, addSignals(sigs)...)
52		signal.Notify(sigch, sigs...)
53
54		go func() {
55			errch <- srv.ListenAndServe()
56		}()
57
58		select {
59		case <-sigch:
60			slog.Info("Received interrupt signal...")
61		case err := <-errch:
62			if err != nil && !errors.Is(err, server.ErrServerClosed) {
63				_ = srv.Close()
64				slog.Error("Server error", "error", err)
65				return fmt.Errorf("server error: %v", err)
66			}
67		}
68
69		ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second)
70		defer cancel()
71
72		slog.Info("Shutting down...")
73
74		if err := srv.Shutdown(ctx); err != nil {
75			slog.Error("Failed to shutdown server", "error", err)
76			return fmt.Errorf("failed to shutdown server: %v", err)
77		}
78
79		return nil
80	},
81}
82
83func init() {
84	rootCmd.AddCommand(serverCmd)
85}