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