package supervise import ( "context" "log" "os" "os/exec" "os/signal" "strings" "sync" "syscall" "time" "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/tools/router" ) func ServeSubprocessSupervisor(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] { command, _ := app.RootCmd.PersistentFlags().GetString("subcommand") if len(command) > 0 { cmdsub := strings.Split(command, " ") if len(cmdsub) > 0 { startNodeProcessSupervised(cmdsub[0], strings.Join(cmdsub[1:], " ")) } } return nil } func startNodeProcessSupervised(command string, args ...string) { const maxRetries = 3 const retryDelay = 30 * time.Second var ( cmdMu sync.Mutex cmd *exec.Cmd ) stopChan := make(chan os.Signal, 1) signal.Notify(stopChan, os.Interrupt, syscall.SIGTERM) go func() { retries := 0 for { select { case <-stopChan: log.Println("Received shutdown signal. Terminating subprocess...") cmdMu.Lock() if cmd != nil && cmd.Process != nil { _ = cmd.Process.Signal(syscall.SIGTERM) } cmdMu.Unlock() return default: ctx, cancel := context.WithCancel(context.Background()) cmdMu.Lock() cmd = exec.CommandContext(ctx, command, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmdMu.Unlock() log.Printf("Starting Process: %s %v\n", command, args) err := cmd.Run() cancel() // cancel the context when done if err != nil { log.Printf("Process exited with error: %v\n", err) retries++ if retries >= maxRetries { log.Printf("Process failed %d times. Shutting down application...", retries) // _ = app.ResetBootstrapState() os.Exit(1) } log.Printf("Retrying in %s (%d/%d)...", retryDelay, retries, maxRetries) time.Sleep(retryDelay) } else { log.Printf("Process exited normally. Resetting retry count.") retries = 0 } } } }() }