1package main
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "os/signal"
8 "path/filepath"
9 "syscall"
10 "time"
11
12 "github.com/charmbracelet/soft-serve/server"
13 "github.com/charmbracelet/soft-serve/server/config"
14 "github.com/spf13/cobra"
15)
16
17var (
18 serveCmd = &cobra.Command{
19 Use: "serve",
20 Short: "Start the server",
21 Long: "Start the server",
22 Args: cobra.NoArgs,
23 RunE: func(cmd *cobra.Command, _ []string) error {
24 ctx := cmd.Context()
25 cfg := config.DefaultConfig()
26 ctx = config.WithContext(ctx, cfg)
27 cmd.SetContext(ctx)
28
29 // Create custom hooks directory if it doesn't exist
30 customHooksPath := filepath.Join(cfg.DataPath, "hooks")
31 if _, err := os.Stat(customHooksPath); err != nil && os.IsNotExist(err) {
32 os.MkdirAll(customHooksPath, os.ModePerm) // nolint: errcheck
33 // Generate update hook example without executable permissions
34 hookPath := filepath.Join(customHooksPath, "update.sample")
35 // nolint: gosec
36 if err := os.WriteFile(hookPath, []byte(updateHookExample), 0744); err != nil {
37 return fmt.Errorf("failed to generate update hook example: %w", err)
38 }
39 }
40
41 // Create log directory if it doesn't exist
42 logPath := filepath.Join(cfg.DataPath, "log")
43 if _, err := os.Stat(logPath); err != nil && os.IsNotExist(err) {
44 os.MkdirAll(logPath, os.ModePerm) // nolint: errcheck
45 }
46
47 s, err := server.NewServer(ctx)
48 if err != nil {
49 return fmt.Errorf("start server: %w", err)
50 }
51
52 done := make(chan os.Signal, 1)
53 lch := make(chan error, 1)
54 go func() {
55 defer close(lch)
56 defer close(done)
57 lch <- s.Start()
58 }()
59
60 signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
61 <-done
62
63 ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
64 defer cancel()
65 if err := s.Shutdown(ctx); err != nil {
66 return err
67 }
68
69 // wait for serve to finish
70 return <-lch
71 },
72 }
73)