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)